From 1537544f6aaf30baf658fbe279b80b76c8bef664 Mon Sep 17 00:00:00 2001 From: zhugengyu <85654684+zhugengyu@users.noreply.github.com> Date: Thu, 10 Nov 2022 22:22:48 +0800 Subject: [PATCH] [bsp/phytium] add phytium bsp to support e2000 bootup with smp (#6566) add phytium board (E2000) bsp support usart support SMP with demo --- bsp/phytium/.gitignore | 1 + bsp/phytium/README.md | 171 ++ bsp/phytium/aarch32/.config | 927 +++++++++++ bsp/phytium/aarch32/Kconfig | 54 + bsp/phytium/aarch32/README.md | 108 ++ bsp/phytium/aarch32/SConscript | 15 + bsp/phytium/aarch32/SConstruct | 58 + bsp/phytium/aarch32/applications/SConscript | 11 + bsp/phytium/aarch32/applications/main.c | 77 + bsp/phytium/aarch32/boot/SConscript | 10 + bsp/phytium/aarch32/boot/aarch32_boot.S | 123 ++ bsp/phytium/aarch32/export_project.py | 37 + bsp/phytium/aarch32/figures/chip_select.png | Bin 0 -> 45417 bytes .../aarch32/figures/export_project.png | Bin 0 -> 30495 bytes .../aarch32/figures/import_project.png | Bin 0 -> 22542 bytes .../aarch32/figures/phytium_cpu_select.png | Bin 0 -> 45943 bytes .../aarch32/figures/select_debug_info.png | Bin 0 -> 32841 bytes bsp/phytium/aarch32/figures/select_driver.png | Bin 0 -> 35393 bytes bsp/phytium/aarch32/link.lds | 116 ++ bsp/phytium/aarch32/rtconfig.h | 261 +++ bsp/phytium/aarch32/rtconfig.py | 68 + bsp/phytium/aarch32/sdkconfig.h | 75 + bsp/phytium/aarch64/.config | 922 +++++++++++ bsp/phytium/aarch64/Kconfig | 55 + bsp/phytium/aarch64/README.md | 121 ++ bsp/phytium/aarch64/SConscript | 15 + bsp/phytium/aarch64/SConstruct | 58 + bsp/phytium/aarch64/applications/SConscript | 11 + bsp/phytium/aarch64/applications/main.c | 77 + bsp/phytium/aarch64/export_project.py | 37 + bsp/phytium/aarch64/figures/chip_select.png | Bin 0 -> 45417 bytes .../aarch64/figures/export_project.png | Bin 0 -> 30495 bytes .../aarch64/figures/import_project.png | Bin 0 -> 22542 bytes .../aarch64/figures/phytium_cpu_select.png | Bin 0 -> 45943 bytes .../aarch64/figures/select_debug_info.png | Bin 0 -> 32841 bytes bsp/phytium/aarch64/figures/select_driver.png | Bin 0 -> 35393 bytes bsp/phytium/aarch64/link.lds | 150 ++ bsp/phytium/aarch64/rtconfig.h | 260 +++ bsp/phytium/aarch64/rtconfig.py | 52 + bsp/phytium/aarch64/sdkconfig.h | 71 + bsp/phytium/board/SConscript | 25 + bsp/phytium/board/board.c | 305 ++++ bsp/phytium/board/board.h | 32 + bsp/phytium/board/d2000/parameters.c | 115 ++ bsp/phytium/board/e2000/d/parameters.c | 54 + bsp/phytium/board/e2000/q/parameters.c | 75 + bsp/phytium/board/e2000/s/parameters.c | 43 + bsp/phytium/board/ft2004/parameters.c | 71 + bsp/phytium/board/phytium_cpu.c | 113 ++ bsp/phytium/board/phytium_cpu.h | 80 + bsp/phytium/board/secondary_cpu.c | 131 ++ bsp/phytium/figures/config_tftp32.png | Bin 0 -> 11098 bytes bsp/phytium/figures/result.png | Bin 0 -> 12890 bytes bsp/phytium/figures/sdk_setup.png | Bin 0 -> 38267 bytes bsp/phytium/figures/tftp32_srv.png | Bin 0 -> 39206 bytes bsp/phytium/libraries/SConscript | 76 + bsp/phytium/libraries/drivers/Kconfig | 29 + bsp/phytium/libraries/drivers/drv_usart.c | 233 +++ bsp/phytium/libraries/drivers/drv_usart.h | 47 + bsp/phytium/libraries/standalone/LICENSE | 28 + bsp/phytium/libraries/standalone/README.md | 291 ++++ .../standalone/arch/armv8/aarch32/faarch32.h | 416 +++++ .../arch/armv8/aarch32/fgeneric_timer.c | 154 ++ .../arch/armv8/aarch32/fgeneric_timer.h | 51 + .../standalone/arch/armv8/aarch32/fpsci.c | 75 + .../standalone/arch/armv8/aarch32/fpsci.h | 43 + .../standalone/arch/armv8/aarch32/fsmc.h | 53 + .../arch/armv8/aarch32/gcc/fsmccc_call.S | 58 + .../arch/armv8/aarch64/farm_smccc.h | 125 ++ .../standalone/arch/armv8/aarch64/fpsci.c | 47 + .../standalone/arch/armv8/aarch64/fpsci.h | 41 + .../arch/armv8/aarch64/gcc/fsmccc_call.S | 49 + .../standalone/arch/common/fkernel.h | 237 +++ .../libraries/standalone/board/Kconfig | 50 + .../standalone/board/common/fcpu_asm.S | 63 + .../standalone/board/common/fcpu_info.c | 264 +++ .../standalone/board/common/fcpu_info.h | 44 + .../libraries/standalone/board/common/fsmp.h | 34 + .../standalone/board/e2000/d/fiopad.h | 269 ++++ .../standalone/board/e2000/d/fiopad_config.c | 562 +++++++ .../standalone/board/e2000/d/fparameters.h | 55 + .../standalone/board/e2000/fearly_uart.c | 59 + .../standalone/board/e2000/fearly_uart.h | 83 + .../standalone/board/e2000/fiopad_comm.c | 572 +++++++ .../standalone/board/e2000/fiopad_comm.h | 310 ++++ .../standalone/board/e2000/fparameters_comm.h | 624 +++++++ .../standalone/board/e2000/q/fiopad.h | 266 +++ .../standalone/board/e2000/q/fiopad_config.c | 619 +++++++ .../standalone/board/e2000/q/fparameters.h | 50 + .../standalone/board/e2000/s/fiopad.h | 270 ++++ .../standalone/board/e2000/s/fiopad_config.c | 291 ++++ .../standalone/board/e2000/s/fparameters.h | 53 + .../libraries/standalone/common/Kconfig | 64 + .../libraries/standalone/common/fassert.c | 118 ++ .../libraries/standalone/common/fassert.h | 102 ++ .../libraries/standalone/common/fdebug.c | 106 ++ .../libraries/standalone/common/fdebug.h | 127 ++ .../libraries/standalone/common/ferror_code.h | 106 ++ bsp/phytium/libraries/standalone/common/fio.h | 113 ++ .../libraries/standalone/common/fpinctrl.h | 211 +++ .../libraries/standalone/common/fprintf.c | 265 +++ .../libraries/standalone/common/fprintf.h | 38 + .../libraries/standalone/common/fprintk.c | 418 +++++ .../libraries/standalone/common/fprintk.h | 38 + .../libraries/standalone/common/fsleep.h | 34 + .../libraries/standalone/common/ftypes.h | 95 ++ .../libraries/standalone/doc/ChangeLog.md | 1431 +++++++++++++++++ .../standalone/doc/design/baremetal.dio | 109 ++ .../libraries/standalone/doc/design/build.dio | 62 + .../standalone/doc/design/driver.dio | 75 + .../standalone/doc/design/framework.dio | 99 ++ .../standalone/doc/design/freertos_system.dio | 175 ++ .../standalone/doc/design/freertos_system.png | Bin 0 -> 185751 bytes .../doc/design/freertos_system_deps.dio | 202 +++ .../doc/design/freertos_system_deps.png | Bin 0 -> 177601 bytes .../standalone/doc/design/middleware.dio | 67 + .../standalone/doc/design/platform_test.dio | 64 + .../standalone/doc/design/system.dio | 253 +++ .../standalone/doc/design/system.png | Bin 0 -> 314383 bytes .../standalone/doc/design/system_2.dio | 181 +++ .../standalone/doc/design/system_2.png | Bin 0 -> 178111 bytes .../standalone/doc/design/system_2_deps.dio | 229 +++ .../standalone/doc/design/system_2_deps.png | Bin 0 -> 207455 bytes .../standalone/doc/driver_template/foox_hw.c | 54 + .../standalone/doc/driver_template/foox_hw.h | 84 + .../doc/driver_template/foox_options.c | 73 + .../doc/driver_template/foox_role.c | 50 + .../standalone/doc/driver_template/fooxx.c | 67 + .../standalone/doc/driver_template/fooxx.h | 106 ++ .../standalone/doc/driver_template/fooxx_g.c | 53 + .../doc/driver_template/fooxx_sinit.c | 61 + .../standalone/doc/fig/Serial_inform.png | Bin 0 -> 115462 bytes .../standalone/doc/fig/add_path_for_win.png | Bin 0 -> 44866 bytes .../standalone/doc/fig/admin_rights.png | Bin 0 -> 3648 bytes .../libraries/standalone/doc/fig/bin_show.png | Bin 0 -> 46893 bytes .../doc/fig/check_env_for_aarch64.png | Bin 0 -> 49014 bytes .../standalone/doc/fig/check_env_for_win.png | Bin 0 -> 9594 bytes .../standalone/doc/fig/check_env_for_x86.png | Bin 0 -> 62404 bytes .../standalone/doc/fig/compiling.png | Bin 0 -> 84126 bytes .../standalone/doc/fig/config_tftp32.png | Bin 0 -> 11098 bytes .../libraries/standalone/doc/fig/git_url.png | Bin 0 -> 24688 bytes .../doc/fig/install_for_aarch64.png | Bin 0 -> 36086 bytes .../standalone/doc/fig/install_for_mingw.png | Bin 0 -> 37836 bytes .../standalone/doc/fig/install_for_x86.png | Bin 0 -> 114606 bytes .../standalone/doc/fig/install_msys2.png | Bin 0 -> 7564 bytes .../standalone/doc/fig/installing_msys2.png | Bin 0 -> 10704 bytes .../standalone/doc/fig/ipv4_setting.png | Bin 0 -> 127004 bytes .../standalone/doc/fig/is_aarch64.png | Bin 0 -> 12778 bytes .../standalone/doc/fig/is_x86_64.png | Bin 0 -> 13254 bytes .../libraries/standalone/doc/fig/kylin.png | Bin 0 -> 4240 bytes .../standalone/doc/fig/letter_shell.png | Bin 0 -> 14042 bytes .../libraries/standalone/doc/fig/linux.png | Bin 0 -> 2504 bytes .../standalone/doc/fig/load_image.png | Bin 0 -> 39950 bytes .../standalone/doc/fig/setup_aarch64_dev.png | Bin 0 -> 25260 bytes .../standalone/doc/fig/setup_win.png | Bin 0 -> 47170 bytes .../standalone/doc/fig/setup_x86_dev.png | Bin 0 -> 25260 bytes .../standalone/doc/fig/tftp32_srv.png | Bin 0 -> 39206 bytes .../standalone/doc/fig/uncompress_for_x86.png | Bin 0 -> 63050 bytes .../standalone/doc/fig/update_packman.png | Bin 0 -> 40854 bytes .../libraries/standalone/doc/fig/windows.png | Bin 0 -> 1310 bytes .../libraries/standalone/doc/fig/wsl_l_v.png | Bin 0 -> 12553 bytes .../standalone/doc/fig/wsl_make_success.png | Bin 0 -> 39692 bytes .../standalone/doc/fig/wsl_teiminal.png | Bin 0 -> 208242 bytes .../cpu/figs/NotUseDefaultConfig.png | Bin 0 -> 61012 bytes .../reference/cpu/figs/UseDefaultConfig.png | Bin 0 -> 60817 bytes .../standalone/doc/reference/cpu/interrupt.md | 442 +++++ .../standalone/doc/reference/cpu/mmu.md | 98 ++ .../standalone/doc/reference/driver/fadc.md | 327 ++++ .../standalone/doc/reference/driver/fcan.md | 248 +++ .../standalone/doc/reference/driver/fddma.md | 288 ++++ .../standalone/doc/reference/driver/fgdma.md | 372 +++++ .../standalone/doc/reference/driver/fgic.md | 441 +++++ .../standalone/doc/reference/driver/fgmac.md | 466 ++++++ .../standalone/doc/reference/driver/fgpio.md | 454 ++++++ .../standalone/doc/reference/driver/fi2c.md | 340 ++++ .../doc/reference/driver/figs/pwm_duty.png | Bin 0 -> 24636 bytes .../doc/reference/driver/figs/spi_app.dio | 58 + .../doc/reference/driver/figs/spi_app.png | Bin 0 -> 40146 bytes .../standalone/doc/reference/driver/fmio.md | 180 +++ .../standalone/doc/reference/driver/fnand.md | 447 +++++ .../standalone/doc/reference/driver/fpcie.md | 450 ++++++ .../standalone/doc/reference/driver/fpl011.md | 368 +++++ .../standalone/doc/reference/driver/fpwm.md | 313 ++++ .../standalone/doc/reference/driver/fqspi.md | 409 +++++ .../standalone/doc/reference/driver/frtc.md | 196 +++ .../standalone/doc/reference/driver/fsata.md | 310 ++++ .../standalone/doc/reference/driver/fsdio.md | 436 +++++ .../standalone/doc/reference/driver/fsdmmc.md | 383 +++++ .../doc/reference/driver/fsemaphore.md | 254 +++ .../standalone/doc/reference/driver/fspim.md | 374 +++++ .../standalone/doc/reference/driver/fusb.md | 535 ++++++ .../standalone/doc/reference/driver/fwdt.md | 171 ++ .../standalone/doc/reference/driver/fxhci.md | 104 ++ .../standalone/doc/reference/driver/fxmac.md | 496 ++++++ .../standalone/doc/reference/driver/sfud.md | 42 + .../doc/reference/driver/template.md | 22 + .../doc/reference/driver/timer_tacho.md | 454 ++++++ .../standalone/doc/reference/hw/template.md | 0 .../doc/reference/sdk/fmemory_pool.md | 279 ++++ .../standalone/doc/reference/sdk/fpinctrl.md | 266 +++ .../standalone/doc/reference/sdk_reference.md | 42 + .../reference/usr/install_linux_aarch64.md | 77 + .../doc/reference/usr/install_linux_x86_64.md | 75 + .../doc/reference/usr/install_windos_wsl.md | 113 ++ .../doc/reference/usr/install_windows.md | 142 ++ .../standalone/doc/reference/usr/usage.md | 224 +++ .../libraries/standalone/drivers/Kconfig | 237 +++ .../libraries/standalone/drivers/adc/Kconfig | 10 + .../standalone/drivers/adc/fadc/fadc.c | 535 ++++++ .../standalone/drivers/adc/fadc/fadc.h | 179 +++ .../standalone/drivers/adc/fadc/fadc_g.c | 45 + .../standalone/drivers/adc/fadc/fadc_hw.c | 48 + .../standalone/drivers/adc/fadc/fadc_hw.h | 121 ++ .../standalone/drivers/adc/fadc/fadc_intr.c | 133 ++ .../standalone/drivers/adc/fadc/fadc_sinit.c | 67 + .../libraries/standalone/drivers/can/Kconfig | 18 + .../standalone/drivers/can/fcan/fcan.c | 1060 ++++++++++++ .../standalone/drivers/can/fcan/fcan.h | 249 +++ .../standalone/drivers/can/fcan/fcan_g.c | 48 + .../standalone/drivers/can/fcan/fcan_hw.c | 67 + .../standalone/drivers/can/fcan/fcan_hw.h | 246 +++ .../standalone/drivers/can/fcan/fcan_intr.c | 128 ++ .../standalone/drivers/can/fcan/fcan_sinit.c | 52 + .../libraries/standalone/drivers/dma/Kconfig | 10 + .../standalone/drivers/dma/fddma/fddma.c | 380 +++++ .../standalone/drivers/dma/fddma/fddma.h | 169 ++ .../standalone/drivers/dma/fddma/fddma_g.c | 57 + .../standalone/drivers/dma/fddma/fddma_hw.c | 372 +++++ .../standalone/drivers/dma/fddma/fddma_hw.h | 223 +++ .../standalone/drivers/dma/fddma/fddma_intr.c | 141 ++ .../drivers/dma/fddma/fddma_selftest.c | 85 + .../drivers/dma/fddma/fddma_sinit.c | 63 + .../standalone/drivers/dma/fgdma/fgdma.c | 652 ++++++++ .../standalone/drivers/dma/fgdma/fgdma.h | 265 +++ .../standalone/drivers/dma/fgdma/fgdma_g.c | 58 + .../standalone/drivers/dma/fgdma/fgdma_hw.h | 447 +++++ .../standalone/drivers/dma/fgdma/fgdma_intr.c | 156 ++ .../drivers/dma/fgdma/fgdma_selftest.c | 87 + .../drivers/dma/fgdma/fgdma_sinit.c | 64 + .../libraries/standalone/drivers/eth/Kconfig | 22 + .../standalone/drivers/eth/fgmac/Kconfig | 13 + .../standalone/drivers/eth/fgmac/fgmac.c | 497 ++++++ .../standalone/drivers/eth/fgmac/fgmac.h | 259 +++ .../standalone/drivers/eth/fgmac/fgmac_dma.c | 342 ++++ .../standalone/drivers/eth/fgmac/fgmac_g.c | 65 + .../standalone/drivers/eth/fgmac/fgmac_hw.c | 166 ++ .../standalone/drivers/eth/fgmac/fgmac_hw.h | 554 +++++++ .../standalone/drivers/eth/fgmac/fgmac_intr.c | 177 ++ .../drivers/eth/fgmac/fgmac_sinit.c | 68 + .../eth/fgmac/phy/ar803x/fgmac_ar803x.c | 198 +++ .../eth/fgmac/phy/ar803x/fgmac_ar803x.h | 93 ++ .../drivers/eth/fgmac/phy/fgmac_phy.c | 523 ++++++ .../drivers/eth/fgmac/phy/fgmac_phy.h | 213 +++ .../standalone/drivers/eth/fxmac/Kconfig | 13 + .../standalone/drivers/eth/fxmac/fxmac.c | 874 ++++++++++ .../standalone/drivers/eth/fxmac/fxmac.h | 332 ++++ .../standalone/drivers/eth/fxmac/fxmac_bd.h | 272 ++++ .../drivers/eth/fxmac/fxmac_bdring.c | 964 +++++++++++ .../drivers/eth/fxmac/fxmac_bdring.h | 160 ++ .../standalone/drivers/eth/fxmac/fxmac_g.c | 152 ++ .../standalone/drivers/eth/fxmac/fxmac_hw.h | 701 ++++++++ .../standalone/drivers/eth/fxmac/fxmac_intr.c | 403 +++++ .../drivers/eth/fxmac/fxmac_options.c | 745 +++++++++ .../standalone/drivers/eth/fxmac/fxmac_phy.c | 371 +++++ .../standalone/drivers/eth/fxmac/fxmac_phy.h | 36 + .../drivers/eth/fxmac/fxmac_sinit.c | 44 + .../drivers/eth/fxmac/phy/eth_ieee_reg.h | 104 ++ .../drivers/eth/fxmac/phy/yt/phy_yt.c | 224 +++ .../drivers/eth/fxmac/phy/yt/phy_yt.h | 45 + .../libraries/standalone/drivers/gic/Kconfig | 6 + .../standalone/drivers/gic/fgic/fgic.c | 674 ++++++++ .../standalone/drivers/gic/fgic/fgic.h | 109 ++ .../drivers/gic/fgic/fgic_cpu_interface.S | 494 ++++++ .../drivers/gic/fgic/fgic_cpu_interface.h | 547 +++++++ .../drivers/gic/fgic/fgic_distributor.h | 165 ++ .../standalone/drivers/gic/fgic/fgic_g.c | 32 + .../standalone/drivers/gic/fgic/fgic_hw.h | 315 ++++ .../drivers/gic/fgic/fgic_redistributor.h | 173 ++ .../standalone/drivers/gic/fgic/fgic_sinit.c | 49 + .../libraries/standalone/drivers/i2c/Kconfig | 8 + .../standalone/drivers/i2c/fi2c/fi2c.c | 198 +++ .../standalone/drivers/i2c/fi2c/fi2c.h | 201 +++ .../standalone/drivers/i2c/fi2c/fi2c_g.c | 143 ++ .../standalone/drivers/i2c/fi2c/fi2c_hw.c | 433 +++++ .../standalone/drivers/i2c/fi2c/fi2c_hw.h | 271 ++++ .../standalone/drivers/i2c/fi2c/fi2c_intr.c | 448 ++++++ .../standalone/drivers/i2c/fi2c/fi2c_master.c | 411 +++++ .../standalone/drivers/i2c/fi2c/fi2c_sinit.c | 66 + .../libraries/standalone/drivers/ipc/Kconfig | 9 + .../drivers/ipc/fsemaphore/fsemaphore.c | 337 ++++ .../drivers/ipc/fsemaphore/fsemaphore.h | 109 ++ .../drivers/ipc/fsemaphore/fsemaphore_g.c | 50 + .../drivers/ipc/fsemaphore/fsemaphore_hw.h | 153 ++ .../drivers/ipc/fsemaphore/fsemaphore_sinit.c | 63 + .../libraries/standalone/drivers/mio/Kconfig | 9 + .../standalone/drivers/mio/fmio/fmio.c | 137 ++ .../standalone/drivers/mio/fmio/fmio.h | 82 + .../standalone/drivers/mio/fmio/fmio_g.c | 130 ++ .../standalone/drivers/mio/fmio/fmio_hw.c | 89 + .../standalone/drivers/mio/fmio/fmio_hw.h | 134 ++ .../standalone/drivers/mio/fmio/fmio_sinit.c | 60 + .../libraries/standalone/drivers/mmc/Kconfig | 19 + .../standalone/drivers/mmc/fsdio/fsdio.c | 482 ++++++ .../standalone/drivers/mmc/fsdio/fsdio.h | 227 +++ .../standalone/drivers/mmc/fsdio/fsdio_cmd.c | 210 +++ .../standalone/drivers/mmc/fsdio/fsdio_dma.c | 365 +++++ .../standalone/drivers/mmc/fsdio/fsdio_g.c | 67 + .../standalone/drivers/mmc/fsdio/fsdio_hw.h | 625 +++++++ .../standalone/drivers/mmc/fsdio/fsdio_intr.c | 225 +++ .../standalone/drivers/mmc/fsdio/fsdio_pio.c | 266 +++ .../drivers/mmc/fsdio/fsdio_selftest.c | 103 ++ .../drivers/mmc/fsdio/fsdio_sinit.c | 64 + .../standalone/drivers/mmc/fsdmmc/fsdmmc.c | 389 +++++ .../standalone/drivers/mmc/fsdmmc/fsdmmc.h | 173 ++ .../drivers/mmc/fsdmmc/fsdmmc_dma.c | 142 ++ .../drivers/mmc/fsdmmc/fsdmmc_dma.h | 54 + .../standalone/drivers/mmc/fsdmmc/fsdmmc_g.c | 62 + .../standalone/drivers/mmc/fsdmmc/fsdmmc_hw.c | 258 +++ .../standalone/drivers/mmc/fsdmmc/fsdmmc_hw.h | 346 ++++ .../drivers/mmc/fsdmmc/fsdmmc_intr.c | 318 ++++ .../drivers/mmc/fsdmmc/fsdmmc_sinit.c | 69 + .../libraries/standalone/drivers/nand/Kconfig | 34 + .../standalone/drivers/nand/fnand/fnand.c | 567 +++++++ .../standalone/drivers/nand/fnand/fnand.h | 302 ++++ .../standalone/drivers/nand/fnand/fnand_bbm.c | 882 ++++++++++ .../standalone/drivers/nand/fnand/fnand_bbm.h | 106 ++ .../drivers/nand/fnand/fnand_common_cmd.c | 731 +++++++++ .../drivers/nand/fnand/fnand_common_cmd.h | 74 + .../standalone/drivers/nand/fnand/fnand_dma.c | 153 ++ .../standalone/drivers/nand/fnand/fnand_dma.h | 111 ++ .../standalone/drivers/nand/fnand/fnand_ecc.c | 266 +++ .../standalone/drivers/nand/fnand/fnand_ecc.h | 45 + .../standalone/drivers/nand/fnand/fnand_g.c | 40 + .../standalone/drivers/nand/fnand/fnand_hw.c | 58 + .../standalone/drivers/nand/fnand/fnand_hw.h | 320 ++++ .../standalone/drivers/nand/fnand/fnand_id.c | 264 +++ .../standalone/drivers/nand/fnand/fnand_id.h | 53 + .../drivers/nand/fnand/fnand_intr.c | 203 +++ .../drivers/nand/fnand/fnand_option.c | 59 + .../drivers/nand/fnand/fnand_sinit.c | 51 + .../drivers/nand/fnand/fnand_timing.c | 467 ++++++ .../drivers/nand/fnand/fnand_timing.h | 124 ++ .../nand/fnand/manufacturer/fnand_onfi.c | 284 ++++ .../nand/fnand/manufacturer/fnand_onfi.h | 107 ++ .../nand/fnand/manufacturer/fnand_toggle.c | 294 ++++ .../nand/fnand/manufacturer/fnand_toggle.h | 157 ++ .../nand/fnand/manufacturer/fnand_toshiba.c | 110 ++ .../libraries/standalone/drivers/pcie/Kconfig | 9 + .../standalone/drivers/pcie/fpcie/fpcie.c | 863 ++++++++++ .../standalone/drivers/pcie/fpcie/fpcie.h | 303 ++++ .../drivers/pcie/fpcie/fpcie_common.h | 575 +++++++ .../drivers/pcie/fpcie/fpcie_config.c | 98 ++ .../standalone/drivers/pcie/fpcie/fpcie_dma.c | 167 ++ .../standalone/drivers/pcie/fpcie/fpcie_dma.h | 77 + .../standalone/drivers/pcie/fpcie/fpcie_ep.c | 216 +++ .../standalone/drivers/pcie/fpcie/fpcie_g.c | 74 + .../standalone/drivers/pcie/fpcie/fpcie_hw.c | 312 ++++ .../standalone/drivers/pcie/fpcie/fpcie_hw.h | 327 ++++ .../drivers/pcie/fpcie/fpcie_misc.c | 162 ++ .../drivers/pcie/fpcie/fpcie_sinit.c | 46 + .../drivers/pcie/fpcie/fpcir_intx.c | 224 +++ .../drivers/pcie/fpcie/fpcir_intx.h | 47 + .../standalone/drivers/pin/fgpio/Kconfig | 7 + .../standalone/drivers/pin/fgpio/fgpio.c | 460 ++++++ .../standalone/drivers/pin/fgpio/fgpio.h | 239 +++ .../standalone/drivers/pin/fgpio/fgpio_g.c | 101 ++ .../standalone/drivers/pin/fgpio/fgpio_hw.h | 166 ++ .../standalone/drivers/pin/fgpio/fgpio_intr.c | 328 ++++ .../drivers/pin/fgpio/fgpio_selftest.c | 79 + .../drivers/pin/fgpio/fgpio_sinit.c | 150 ++ .../libraries/standalone/drivers/pwm/Kconfig | 9 + .../standalone/drivers/pwm/fpwm/fpwm.c | 808 ++++++++++ .../standalone/drivers/pwm/fpwm/fpwm.h | 220 +++ .../standalone/drivers/pwm/fpwm/fpwm_g.c | 129 ++ .../standalone/drivers/pwm/fpwm/fpwm_hw.c | 103 ++ .../standalone/drivers/pwm/fpwm/fpwm_hw.h | 143 ++ .../standalone/drivers/pwm/fpwm/fpwm_intr.c | 103 ++ .../standalone/drivers/pwm/fpwm/fpwm_sinit.c | 66 + .../libraries/standalone/drivers/qspi/Kconfig | 8 + .../standalone/drivers/qspi/fqspi/fqspi.c | 312 ++++ .../standalone/drivers/qspi/fqspi/fqspi.h | 238 +++ .../drivers/qspi/fqspi/fqspi_flash.c | 1039 ++++++++++++ .../drivers/qspi/fqspi/fqspi_flash.h | 147 ++ .../standalone/drivers/qspi/fqspi/fqspi_g.c | 42 + .../standalone/drivers/qspi/fqspi/fqspi_hw.c | 159 ++ .../standalone/drivers/qspi/fqspi/fqspi_hw.h | 189 +++ .../drivers/qspi/fqspi/fqspi_sinit.c | 47 + .../libraries/standalone/drivers/rtc/Kconfig | 9 + .../standalone/drivers/rtc/frtc/frtc.c | 231 +++ .../standalone/drivers/rtc/frtc/frtc.h | 92 ++ .../standalone/drivers/rtc/frtc/frtc_g.c | 34 + .../standalone/drivers/rtc/frtc/frtc_hw.c | 36 + .../standalone/drivers/rtc/frtc/frtc_hw.h | 99 ++ .../standalone/drivers/rtc/frtc/frtc_intr.c | 30 + .../standalone/drivers/rtc/frtc/frtc_sinit.c | 51 + .../libraries/standalone/drivers/sata/Kconfig | 9 + .../standalone/drivers/sata/fsata/fsata.c | 916 +++++++++++ .../standalone/drivers/sata/fsata/fsata.h | 277 ++++ .../standalone/drivers/sata/fsata/fsata_g.c | 103 ++ .../standalone/drivers/sata/fsata/fsata_hw.c | 36 + .../standalone/drivers/sata/fsata/fsata_hw.h | 164 ++ .../drivers/sata/fsata/fsata_intr.c | 223 +++ .../drivers/sata/fsata/fsata_sinit.c | 72 + .../standalone/drivers/serial/Kconfig | 10 + .../standalone/drivers/serial/fpl011/fpl011.c | 287 ++++ .../standalone/drivers/serial/fpl011/fpl011.h | 182 +++ .../drivers/serial/fpl011/fpl011_g.c | 70 + .../drivers/serial/fpl011/fpl011_hw.c | 73 + .../drivers/serial/fpl011/fpl011_hw.h | 255 +++ .../drivers/serial/fpl011/fpl011_intr.c | 234 +++ .../drivers/serial/fpl011/fpl011_options.c | 482 ++++++ .../drivers/serial/fpl011/fpl011_sinit.c | 62 + .../libraries/standalone/drivers/spi/Kconfig | 11 + .../standalone/drivers/spi/fspim/fspim.c | 620 +++++++ .../standalone/drivers/spi/fspim/fspim.h | 214 +++ .../standalone/drivers/spi/fspim/fspim_g.c | 100 ++ .../standalone/drivers/spi/fspim/fspim_hw.c | 256 +++ .../standalone/drivers/spi/fspim/fspim_hw.h | 588 +++++++ .../standalone/drivers/spi/fspim/fspim_intr.c | 133 ++ .../drivers/spi/fspim/fspim_selftest.c | 73 + .../drivers/spi/fspim/fspim_sinit.c | 64 + .../standalone/drivers/timer/Kconfig | 9 + .../drivers/timer/ftimer_tacho/ftacho.c | 298 ++++ .../drivers/timer/ftimer_tacho/ftimer.c | 459 ++++++ .../drivers/timer/ftimer_tacho/ftimer_tacho.h | 227 +++ .../timer/ftimer_tacho/ftimer_tacho_g.c | 62 + .../timer/ftimer_tacho/ftimer_tacho_hw.h | 163 ++ .../timer/ftimer_tacho/ftimer_tacho_intr.c | 211 +++ .../libraries/standalone/drivers/usb/Kconfig | 9 + .../libraries/standalone/drivers/usb/fusb.c | 1024 ++++++++++++ .../libraries/standalone/drivers/usb/fusb.h | 398 +++++ .../standalone/drivers/usb/fusb_debug.c | 230 +++ .../standalone/drivers/usb/fusb_def.h | 288 ++++ .../standalone/drivers/usb/fusb_dev.c | 439 +++++ .../libraries/standalone/drivers/usb/fusb_g.c | 60 + .../standalone/drivers/usb/fusb_generic_hub.c | 295 ++++ .../standalone/drivers/usb/fusb_generic_hub.h | 93 ++ .../standalone/drivers/usb/fusb_hid.c | 554 +++++++ .../standalone/drivers/usb/fusb_hid.h | 328 ++++ .../standalone/drivers/usb/fusb_hub.c | 493 ++++++ .../standalone/drivers/usb/fusb_hub.h | 65 + .../standalone/drivers/usb/fusb_msc.c | 859 ++++++++++ .../standalone/drivers/usb/fusb_msc.h | 163 ++ .../standalone/drivers/usb/fusb_private.h | 104 ++ .../standalone/drivers/usb/fusb_sinit.c | 60 + .../standalone/drivers/usb/fxhci/fxhci.c | 1240 ++++++++++++++ .../standalone/drivers/usb/fxhci/fxhci.h | 94 ++ .../standalone/drivers/usb/fxhci/fxhci_cmd.c | 243 +++ .../drivers/usb/fxhci/fxhci_debug.c | 131 ++ .../standalone/drivers/usb/fxhci/fxhci_dev.c | 597 +++++++ .../standalone/drivers/usb/fxhci/fxhci_evt.c | 371 +++++ .../standalone/drivers/usb/fxhci/fxhci_hw.c | 217 +++ .../standalone/drivers/usb/fxhci/fxhci_hw.h | 589 +++++++ .../drivers/usb/fxhci/fxhci_private.h | 475 ++++++ .../drivers/usb/fxhci/fxhci_roothub.c | 193 +++ .../standalone/drivers/watchdog/Kconfig | 10 + .../standalone/drivers/watchdog/fwdt/fwdt.c | 255 +++ .../standalone/drivers/watchdog/fwdt/fwdt.h | 103 ++ .../standalone/drivers/watchdog/fwdt/fwdt_g.c | 48 + .../drivers/watchdog/fwdt/fwdt_hw.c | 37 + .../drivers/watchdog/fwdt/fwdt_hw.h | 131 ++ .../drivers/watchdog/fwdt/fwdt_intr.c | 22 + .../drivers/watchdog/fwdt/fwdt_sinit.c | 75 + bsp/phytium/libraries/standalone/gitinfo | 4 + 464 files changed, 85749 insertions(+) create mode 100644 bsp/phytium/.gitignore create mode 100644 bsp/phytium/README.md create mode 100644 bsp/phytium/aarch32/.config create mode 100644 bsp/phytium/aarch32/Kconfig create mode 100644 bsp/phytium/aarch32/README.md create mode 100644 bsp/phytium/aarch32/SConscript create mode 100644 bsp/phytium/aarch32/SConstruct create mode 100644 bsp/phytium/aarch32/applications/SConscript create mode 100644 bsp/phytium/aarch32/applications/main.c create mode 100644 bsp/phytium/aarch32/boot/SConscript create mode 100644 bsp/phytium/aarch32/boot/aarch32_boot.S create mode 100644 bsp/phytium/aarch32/export_project.py create mode 100644 bsp/phytium/aarch32/figures/chip_select.png create mode 100644 bsp/phytium/aarch32/figures/export_project.png create mode 100644 bsp/phytium/aarch32/figures/import_project.png create mode 100644 bsp/phytium/aarch32/figures/phytium_cpu_select.png create mode 100644 bsp/phytium/aarch32/figures/select_debug_info.png create mode 100644 bsp/phytium/aarch32/figures/select_driver.png create mode 100644 bsp/phytium/aarch32/link.lds create mode 100644 bsp/phytium/aarch32/rtconfig.h create mode 100644 bsp/phytium/aarch32/rtconfig.py create mode 100644 bsp/phytium/aarch32/sdkconfig.h create mode 100644 bsp/phytium/aarch64/.config create mode 100644 bsp/phytium/aarch64/Kconfig create mode 100644 bsp/phytium/aarch64/README.md create mode 100644 bsp/phytium/aarch64/SConscript create mode 100644 bsp/phytium/aarch64/SConstruct create mode 100644 bsp/phytium/aarch64/applications/SConscript create mode 100644 bsp/phytium/aarch64/applications/main.c create mode 100644 bsp/phytium/aarch64/export_project.py create mode 100644 bsp/phytium/aarch64/figures/chip_select.png create mode 100644 bsp/phytium/aarch64/figures/export_project.png create mode 100644 bsp/phytium/aarch64/figures/import_project.png create mode 100644 bsp/phytium/aarch64/figures/phytium_cpu_select.png create mode 100644 bsp/phytium/aarch64/figures/select_debug_info.png create mode 100644 bsp/phytium/aarch64/figures/select_driver.png create mode 100644 bsp/phytium/aarch64/link.lds create mode 100644 bsp/phytium/aarch64/rtconfig.h create mode 100644 bsp/phytium/aarch64/rtconfig.py create mode 100644 bsp/phytium/aarch64/sdkconfig.h create mode 100644 bsp/phytium/board/SConscript create mode 100644 bsp/phytium/board/board.c create mode 100644 bsp/phytium/board/board.h create mode 100644 bsp/phytium/board/d2000/parameters.c create mode 100644 bsp/phytium/board/e2000/d/parameters.c create mode 100644 bsp/phytium/board/e2000/q/parameters.c create mode 100644 bsp/phytium/board/e2000/s/parameters.c create mode 100644 bsp/phytium/board/ft2004/parameters.c create mode 100644 bsp/phytium/board/phytium_cpu.c create mode 100644 bsp/phytium/board/phytium_cpu.h create mode 100644 bsp/phytium/board/secondary_cpu.c create mode 100644 bsp/phytium/figures/config_tftp32.png create mode 100644 bsp/phytium/figures/result.png create mode 100644 bsp/phytium/figures/sdk_setup.png create mode 100644 bsp/phytium/figures/tftp32_srv.png create mode 100644 bsp/phytium/libraries/SConscript create mode 100644 bsp/phytium/libraries/drivers/Kconfig create mode 100644 bsp/phytium/libraries/drivers/drv_usart.c create mode 100644 bsp/phytium/libraries/drivers/drv_usart.h create mode 100644 bsp/phytium/libraries/standalone/LICENSE create mode 100644 bsp/phytium/libraries/standalone/README.md create mode 100644 bsp/phytium/libraries/standalone/arch/armv8/aarch32/faarch32.h create mode 100644 bsp/phytium/libraries/standalone/arch/armv8/aarch32/fgeneric_timer.c create mode 100644 bsp/phytium/libraries/standalone/arch/armv8/aarch32/fgeneric_timer.h create mode 100644 bsp/phytium/libraries/standalone/arch/armv8/aarch32/fpsci.c create mode 100644 bsp/phytium/libraries/standalone/arch/armv8/aarch32/fpsci.h create mode 100644 bsp/phytium/libraries/standalone/arch/armv8/aarch32/fsmc.h create mode 100644 bsp/phytium/libraries/standalone/arch/armv8/aarch32/gcc/fsmccc_call.S create mode 100644 bsp/phytium/libraries/standalone/arch/armv8/aarch64/farm_smccc.h create mode 100644 bsp/phytium/libraries/standalone/arch/armv8/aarch64/fpsci.c create mode 100644 bsp/phytium/libraries/standalone/arch/armv8/aarch64/fpsci.h create mode 100644 bsp/phytium/libraries/standalone/arch/armv8/aarch64/gcc/fsmccc_call.S create mode 100644 bsp/phytium/libraries/standalone/arch/common/fkernel.h create mode 100644 bsp/phytium/libraries/standalone/board/Kconfig create mode 100644 bsp/phytium/libraries/standalone/board/common/fcpu_asm.S create mode 100644 bsp/phytium/libraries/standalone/board/common/fcpu_info.c create mode 100644 bsp/phytium/libraries/standalone/board/common/fcpu_info.h create mode 100644 bsp/phytium/libraries/standalone/board/common/fsmp.h create mode 100644 bsp/phytium/libraries/standalone/board/e2000/d/fiopad.h create mode 100644 bsp/phytium/libraries/standalone/board/e2000/d/fiopad_config.c create mode 100644 bsp/phytium/libraries/standalone/board/e2000/d/fparameters.h create mode 100644 bsp/phytium/libraries/standalone/board/e2000/fearly_uart.c create mode 100644 bsp/phytium/libraries/standalone/board/e2000/fearly_uart.h create mode 100644 bsp/phytium/libraries/standalone/board/e2000/fiopad_comm.c create mode 100644 bsp/phytium/libraries/standalone/board/e2000/fiopad_comm.h create mode 100644 bsp/phytium/libraries/standalone/board/e2000/fparameters_comm.h create mode 100644 bsp/phytium/libraries/standalone/board/e2000/q/fiopad.h create mode 100644 bsp/phytium/libraries/standalone/board/e2000/q/fiopad_config.c create mode 100644 bsp/phytium/libraries/standalone/board/e2000/q/fparameters.h create mode 100644 bsp/phytium/libraries/standalone/board/e2000/s/fiopad.h create mode 100644 bsp/phytium/libraries/standalone/board/e2000/s/fiopad_config.c create mode 100644 bsp/phytium/libraries/standalone/board/e2000/s/fparameters.h create mode 100644 bsp/phytium/libraries/standalone/common/Kconfig create mode 100644 bsp/phytium/libraries/standalone/common/fassert.c create mode 100644 bsp/phytium/libraries/standalone/common/fassert.h create mode 100644 bsp/phytium/libraries/standalone/common/fdebug.c create mode 100644 bsp/phytium/libraries/standalone/common/fdebug.h create mode 100644 bsp/phytium/libraries/standalone/common/ferror_code.h create mode 100644 bsp/phytium/libraries/standalone/common/fio.h create mode 100644 bsp/phytium/libraries/standalone/common/fpinctrl.h create mode 100644 bsp/phytium/libraries/standalone/common/fprintf.c create mode 100644 bsp/phytium/libraries/standalone/common/fprintf.h create mode 100644 bsp/phytium/libraries/standalone/common/fprintk.c create mode 100644 bsp/phytium/libraries/standalone/common/fprintk.h create mode 100644 bsp/phytium/libraries/standalone/common/fsleep.h create mode 100644 bsp/phytium/libraries/standalone/common/ftypes.h create mode 100644 bsp/phytium/libraries/standalone/doc/ChangeLog.md create mode 100644 bsp/phytium/libraries/standalone/doc/design/baremetal.dio create mode 100644 bsp/phytium/libraries/standalone/doc/design/build.dio create mode 100644 bsp/phytium/libraries/standalone/doc/design/driver.dio create mode 100644 bsp/phytium/libraries/standalone/doc/design/framework.dio create mode 100644 bsp/phytium/libraries/standalone/doc/design/freertos_system.dio create mode 100644 bsp/phytium/libraries/standalone/doc/design/freertos_system.png create mode 100644 bsp/phytium/libraries/standalone/doc/design/freertos_system_deps.dio create mode 100644 bsp/phytium/libraries/standalone/doc/design/freertos_system_deps.png create mode 100644 bsp/phytium/libraries/standalone/doc/design/middleware.dio create mode 100644 bsp/phytium/libraries/standalone/doc/design/platform_test.dio create mode 100644 bsp/phytium/libraries/standalone/doc/design/system.dio create mode 100644 bsp/phytium/libraries/standalone/doc/design/system.png create mode 100644 bsp/phytium/libraries/standalone/doc/design/system_2.dio create mode 100644 bsp/phytium/libraries/standalone/doc/design/system_2.png create mode 100644 bsp/phytium/libraries/standalone/doc/design/system_2_deps.dio create mode 100644 bsp/phytium/libraries/standalone/doc/design/system_2_deps.png create mode 100644 bsp/phytium/libraries/standalone/doc/driver_template/foox_hw.c create mode 100644 bsp/phytium/libraries/standalone/doc/driver_template/foox_hw.h create mode 100644 bsp/phytium/libraries/standalone/doc/driver_template/foox_options.c create mode 100644 bsp/phytium/libraries/standalone/doc/driver_template/foox_role.c create mode 100644 bsp/phytium/libraries/standalone/doc/driver_template/fooxx.c create mode 100644 bsp/phytium/libraries/standalone/doc/driver_template/fooxx.h create mode 100644 bsp/phytium/libraries/standalone/doc/driver_template/fooxx_g.c create mode 100644 bsp/phytium/libraries/standalone/doc/driver_template/fooxx_sinit.c create mode 100644 bsp/phytium/libraries/standalone/doc/fig/Serial_inform.png create mode 100644 bsp/phytium/libraries/standalone/doc/fig/add_path_for_win.png create mode 100644 bsp/phytium/libraries/standalone/doc/fig/admin_rights.png create mode 100644 bsp/phytium/libraries/standalone/doc/fig/bin_show.png create mode 100644 bsp/phytium/libraries/standalone/doc/fig/check_env_for_aarch64.png create mode 100644 bsp/phytium/libraries/standalone/doc/fig/check_env_for_win.png create mode 100644 bsp/phytium/libraries/standalone/doc/fig/check_env_for_x86.png create mode 100644 bsp/phytium/libraries/standalone/doc/fig/compiling.png create mode 100644 bsp/phytium/libraries/standalone/doc/fig/config_tftp32.png create mode 100644 bsp/phytium/libraries/standalone/doc/fig/git_url.png create mode 100644 bsp/phytium/libraries/standalone/doc/fig/install_for_aarch64.png create mode 100644 bsp/phytium/libraries/standalone/doc/fig/install_for_mingw.png create mode 100644 bsp/phytium/libraries/standalone/doc/fig/install_for_x86.png create mode 100644 bsp/phytium/libraries/standalone/doc/fig/install_msys2.png create mode 100644 bsp/phytium/libraries/standalone/doc/fig/installing_msys2.png create mode 100644 bsp/phytium/libraries/standalone/doc/fig/ipv4_setting.png create mode 100644 bsp/phytium/libraries/standalone/doc/fig/is_aarch64.png create mode 100644 bsp/phytium/libraries/standalone/doc/fig/is_x86_64.png create mode 100644 bsp/phytium/libraries/standalone/doc/fig/kylin.png create mode 100644 bsp/phytium/libraries/standalone/doc/fig/letter_shell.png create mode 100644 bsp/phytium/libraries/standalone/doc/fig/linux.png create mode 100644 bsp/phytium/libraries/standalone/doc/fig/load_image.png create mode 100644 bsp/phytium/libraries/standalone/doc/fig/setup_aarch64_dev.png create mode 100644 bsp/phytium/libraries/standalone/doc/fig/setup_win.png create mode 100644 bsp/phytium/libraries/standalone/doc/fig/setup_x86_dev.png create mode 100644 bsp/phytium/libraries/standalone/doc/fig/tftp32_srv.png create mode 100644 bsp/phytium/libraries/standalone/doc/fig/uncompress_for_x86.png create mode 100644 bsp/phytium/libraries/standalone/doc/fig/update_packman.png create mode 100644 bsp/phytium/libraries/standalone/doc/fig/windows.png create mode 100644 bsp/phytium/libraries/standalone/doc/fig/wsl_l_v.png create mode 100644 bsp/phytium/libraries/standalone/doc/fig/wsl_make_success.png create mode 100644 bsp/phytium/libraries/standalone/doc/fig/wsl_teiminal.png create mode 100644 bsp/phytium/libraries/standalone/doc/reference/cpu/figs/NotUseDefaultConfig.png create mode 100644 bsp/phytium/libraries/standalone/doc/reference/cpu/figs/UseDefaultConfig.png create mode 100644 bsp/phytium/libraries/standalone/doc/reference/cpu/interrupt.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/cpu/mmu.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/driver/fadc.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/driver/fcan.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/driver/fddma.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/driver/fgdma.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/driver/fgic.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/driver/fgmac.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/driver/fgpio.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/driver/fi2c.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/driver/figs/pwm_duty.png create mode 100644 bsp/phytium/libraries/standalone/doc/reference/driver/figs/spi_app.dio create mode 100644 bsp/phytium/libraries/standalone/doc/reference/driver/figs/spi_app.png create mode 100644 bsp/phytium/libraries/standalone/doc/reference/driver/fmio.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/driver/fnand.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/driver/fpcie.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/driver/fpl011.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/driver/fpwm.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/driver/fqspi.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/driver/frtc.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/driver/fsata.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/driver/fsdio.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/driver/fsdmmc.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/driver/fsemaphore.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/driver/fspim.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/driver/fusb.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/driver/fwdt.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/driver/fxhci.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/driver/fxmac.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/driver/sfud.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/driver/template.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/driver/timer_tacho.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/hw/template.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/sdk/fmemory_pool.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/sdk/fpinctrl.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/sdk_reference.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/usr/install_linux_aarch64.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/usr/install_linux_x86_64.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/usr/install_windos_wsl.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/usr/install_windows.md create mode 100644 bsp/phytium/libraries/standalone/doc/reference/usr/usage.md create mode 100644 bsp/phytium/libraries/standalone/drivers/Kconfig create mode 100644 bsp/phytium/libraries/standalone/drivers/adc/Kconfig create mode 100644 bsp/phytium/libraries/standalone/drivers/adc/fadc/fadc.c create mode 100644 bsp/phytium/libraries/standalone/drivers/adc/fadc/fadc.h create mode 100644 bsp/phytium/libraries/standalone/drivers/adc/fadc/fadc_g.c create mode 100644 bsp/phytium/libraries/standalone/drivers/adc/fadc/fadc_hw.c create mode 100644 bsp/phytium/libraries/standalone/drivers/adc/fadc/fadc_hw.h create mode 100644 bsp/phytium/libraries/standalone/drivers/adc/fadc/fadc_intr.c create mode 100644 bsp/phytium/libraries/standalone/drivers/adc/fadc/fadc_sinit.c create mode 100644 bsp/phytium/libraries/standalone/drivers/can/Kconfig create mode 100644 bsp/phytium/libraries/standalone/drivers/can/fcan/fcan.c create mode 100644 bsp/phytium/libraries/standalone/drivers/can/fcan/fcan.h create mode 100644 bsp/phytium/libraries/standalone/drivers/can/fcan/fcan_g.c create mode 100644 bsp/phytium/libraries/standalone/drivers/can/fcan/fcan_hw.c create mode 100644 bsp/phytium/libraries/standalone/drivers/can/fcan/fcan_hw.h create mode 100644 bsp/phytium/libraries/standalone/drivers/can/fcan/fcan_intr.c create mode 100644 bsp/phytium/libraries/standalone/drivers/can/fcan/fcan_sinit.c create mode 100644 bsp/phytium/libraries/standalone/drivers/dma/Kconfig create mode 100644 bsp/phytium/libraries/standalone/drivers/dma/fddma/fddma.c create mode 100644 bsp/phytium/libraries/standalone/drivers/dma/fddma/fddma.h create mode 100644 bsp/phytium/libraries/standalone/drivers/dma/fddma/fddma_g.c create mode 100644 bsp/phytium/libraries/standalone/drivers/dma/fddma/fddma_hw.c create mode 100644 bsp/phytium/libraries/standalone/drivers/dma/fddma/fddma_hw.h create mode 100644 bsp/phytium/libraries/standalone/drivers/dma/fddma/fddma_intr.c create mode 100644 bsp/phytium/libraries/standalone/drivers/dma/fddma/fddma_selftest.c create mode 100644 bsp/phytium/libraries/standalone/drivers/dma/fddma/fddma_sinit.c create mode 100644 bsp/phytium/libraries/standalone/drivers/dma/fgdma/fgdma.c create mode 100644 bsp/phytium/libraries/standalone/drivers/dma/fgdma/fgdma.h create mode 100644 bsp/phytium/libraries/standalone/drivers/dma/fgdma/fgdma_g.c create mode 100644 bsp/phytium/libraries/standalone/drivers/dma/fgdma/fgdma_hw.h create mode 100644 bsp/phytium/libraries/standalone/drivers/dma/fgdma/fgdma_intr.c create mode 100644 bsp/phytium/libraries/standalone/drivers/dma/fgdma/fgdma_selftest.c create mode 100644 bsp/phytium/libraries/standalone/drivers/dma/fgdma/fgdma_sinit.c create mode 100644 bsp/phytium/libraries/standalone/drivers/eth/Kconfig create mode 100644 bsp/phytium/libraries/standalone/drivers/eth/fgmac/Kconfig create mode 100644 bsp/phytium/libraries/standalone/drivers/eth/fgmac/fgmac.c create mode 100644 bsp/phytium/libraries/standalone/drivers/eth/fgmac/fgmac.h create mode 100644 bsp/phytium/libraries/standalone/drivers/eth/fgmac/fgmac_dma.c create mode 100644 bsp/phytium/libraries/standalone/drivers/eth/fgmac/fgmac_g.c create mode 100644 bsp/phytium/libraries/standalone/drivers/eth/fgmac/fgmac_hw.c create mode 100644 bsp/phytium/libraries/standalone/drivers/eth/fgmac/fgmac_hw.h create mode 100644 bsp/phytium/libraries/standalone/drivers/eth/fgmac/fgmac_intr.c create mode 100644 bsp/phytium/libraries/standalone/drivers/eth/fgmac/fgmac_sinit.c create mode 100644 bsp/phytium/libraries/standalone/drivers/eth/fgmac/phy/ar803x/fgmac_ar803x.c create mode 100644 bsp/phytium/libraries/standalone/drivers/eth/fgmac/phy/ar803x/fgmac_ar803x.h create mode 100644 bsp/phytium/libraries/standalone/drivers/eth/fgmac/phy/fgmac_phy.c create mode 100644 bsp/phytium/libraries/standalone/drivers/eth/fgmac/phy/fgmac_phy.h create mode 100644 bsp/phytium/libraries/standalone/drivers/eth/fxmac/Kconfig create mode 100644 bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac.c create mode 100644 bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac.h create mode 100644 bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_bd.h create mode 100644 bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_bdring.c create mode 100644 bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_bdring.h create mode 100644 bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_g.c create mode 100644 bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_hw.h create mode 100644 bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_intr.c create mode 100644 bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_options.c create mode 100644 bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_phy.c create mode 100644 bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_phy.h create mode 100644 bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_sinit.c create mode 100644 bsp/phytium/libraries/standalone/drivers/eth/fxmac/phy/eth_ieee_reg.h create mode 100644 bsp/phytium/libraries/standalone/drivers/eth/fxmac/phy/yt/phy_yt.c create mode 100644 bsp/phytium/libraries/standalone/drivers/eth/fxmac/phy/yt/phy_yt.h create mode 100644 bsp/phytium/libraries/standalone/drivers/gic/Kconfig create mode 100644 bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic.c create mode 100644 bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic.h create mode 100644 bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic_cpu_interface.S create mode 100644 bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic_cpu_interface.h create mode 100644 bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic_distributor.h create mode 100644 bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic_g.c create mode 100644 bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic_hw.h create mode 100644 bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic_redistributor.h create mode 100644 bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic_sinit.c create mode 100644 bsp/phytium/libraries/standalone/drivers/i2c/Kconfig create mode 100644 bsp/phytium/libraries/standalone/drivers/i2c/fi2c/fi2c.c create mode 100644 bsp/phytium/libraries/standalone/drivers/i2c/fi2c/fi2c.h create mode 100644 bsp/phytium/libraries/standalone/drivers/i2c/fi2c/fi2c_g.c create mode 100644 bsp/phytium/libraries/standalone/drivers/i2c/fi2c/fi2c_hw.c create mode 100644 bsp/phytium/libraries/standalone/drivers/i2c/fi2c/fi2c_hw.h create mode 100644 bsp/phytium/libraries/standalone/drivers/i2c/fi2c/fi2c_intr.c create mode 100644 bsp/phytium/libraries/standalone/drivers/i2c/fi2c/fi2c_master.c create mode 100644 bsp/phytium/libraries/standalone/drivers/i2c/fi2c/fi2c_sinit.c create mode 100644 bsp/phytium/libraries/standalone/drivers/ipc/Kconfig create mode 100644 bsp/phytium/libraries/standalone/drivers/ipc/fsemaphore/fsemaphore.c create mode 100644 bsp/phytium/libraries/standalone/drivers/ipc/fsemaphore/fsemaphore.h create mode 100644 bsp/phytium/libraries/standalone/drivers/ipc/fsemaphore/fsemaphore_g.c create mode 100644 bsp/phytium/libraries/standalone/drivers/ipc/fsemaphore/fsemaphore_hw.h create mode 100644 bsp/phytium/libraries/standalone/drivers/ipc/fsemaphore/fsemaphore_sinit.c create mode 100644 bsp/phytium/libraries/standalone/drivers/mio/Kconfig create mode 100644 bsp/phytium/libraries/standalone/drivers/mio/fmio/fmio.c create mode 100644 bsp/phytium/libraries/standalone/drivers/mio/fmio/fmio.h create mode 100644 bsp/phytium/libraries/standalone/drivers/mio/fmio/fmio_g.c create mode 100644 bsp/phytium/libraries/standalone/drivers/mio/fmio/fmio_hw.c create mode 100644 bsp/phytium/libraries/standalone/drivers/mio/fmio/fmio_hw.h create mode 100644 bsp/phytium/libraries/standalone/drivers/mio/fmio/fmio_sinit.c create mode 100644 bsp/phytium/libraries/standalone/drivers/mmc/Kconfig create mode 100644 bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio.c create mode 100644 bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio.h create mode 100644 bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio_cmd.c create mode 100644 bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio_dma.c create mode 100644 bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio_g.c create mode 100644 bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio_hw.h create mode 100644 bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio_intr.c create mode 100644 bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio_pio.c create mode 100644 bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio_selftest.c create mode 100644 bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio_sinit.c create mode 100644 bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc.c create mode 100644 bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc.h create mode 100644 bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc_dma.c create mode 100644 bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc_dma.h create mode 100644 bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc_g.c create mode 100644 bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc_hw.c create mode 100644 bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc_hw.h create mode 100644 bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc_intr.c create mode 100644 bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc_sinit.c create mode 100644 bsp/phytium/libraries/standalone/drivers/nand/Kconfig create mode 100644 bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand.c create mode 100644 bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand.h create mode 100644 bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_bbm.c create mode 100644 bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_bbm.h create mode 100644 bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_common_cmd.c create mode 100644 bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_common_cmd.h create mode 100644 bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_dma.c create mode 100644 bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_dma.h create mode 100644 bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_ecc.c create mode 100644 bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_ecc.h create mode 100644 bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_g.c create mode 100644 bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_hw.c create mode 100644 bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_hw.h create mode 100644 bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_id.c create mode 100644 bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_id.h create mode 100644 bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_intr.c create mode 100644 bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_option.c create mode 100644 bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_sinit.c create mode 100644 bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_timing.c create mode 100644 bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_timing.h create mode 100644 bsp/phytium/libraries/standalone/drivers/nand/fnand/manufacturer/fnand_onfi.c create mode 100644 bsp/phytium/libraries/standalone/drivers/nand/fnand/manufacturer/fnand_onfi.h create mode 100644 bsp/phytium/libraries/standalone/drivers/nand/fnand/manufacturer/fnand_toggle.c create mode 100644 bsp/phytium/libraries/standalone/drivers/nand/fnand/manufacturer/fnand_toggle.h create mode 100644 bsp/phytium/libraries/standalone/drivers/nand/fnand/manufacturer/fnand_toshiba.c create mode 100644 bsp/phytium/libraries/standalone/drivers/pcie/Kconfig create mode 100644 bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie.c create mode 100644 bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie.h create mode 100644 bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_common.h create mode 100644 bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_config.c create mode 100644 bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_dma.c create mode 100644 bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_dma.h create mode 100644 bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_ep.c create mode 100644 bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_g.c create mode 100644 bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_hw.c create mode 100644 bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_hw.h create mode 100644 bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_misc.c create mode 100644 bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_sinit.c create mode 100644 bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcir_intx.c create mode 100644 bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcir_intx.h create mode 100644 bsp/phytium/libraries/standalone/drivers/pin/fgpio/Kconfig create mode 100644 bsp/phytium/libraries/standalone/drivers/pin/fgpio/fgpio.c create mode 100644 bsp/phytium/libraries/standalone/drivers/pin/fgpio/fgpio.h create mode 100644 bsp/phytium/libraries/standalone/drivers/pin/fgpio/fgpio_g.c create mode 100644 bsp/phytium/libraries/standalone/drivers/pin/fgpio/fgpio_hw.h create mode 100644 bsp/phytium/libraries/standalone/drivers/pin/fgpio/fgpio_intr.c create mode 100644 bsp/phytium/libraries/standalone/drivers/pin/fgpio/fgpio_selftest.c create mode 100644 bsp/phytium/libraries/standalone/drivers/pin/fgpio/fgpio_sinit.c create mode 100644 bsp/phytium/libraries/standalone/drivers/pwm/Kconfig create mode 100644 bsp/phytium/libraries/standalone/drivers/pwm/fpwm/fpwm.c create mode 100644 bsp/phytium/libraries/standalone/drivers/pwm/fpwm/fpwm.h create mode 100644 bsp/phytium/libraries/standalone/drivers/pwm/fpwm/fpwm_g.c create mode 100644 bsp/phytium/libraries/standalone/drivers/pwm/fpwm/fpwm_hw.c create mode 100644 bsp/phytium/libraries/standalone/drivers/pwm/fpwm/fpwm_hw.h create mode 100644 bsp/phytium/libraries/standalone/drivers/pwm/fpwm/fpwm_intr.c create mode 100644 bsp/phytium/libraries/standalone/drivers/pwm/fpwm/fpwm_sinit.c create mode 100644 bsp/phytium/libraries/standalone/drivers/qspi/Kconfig create mode 100644 bsp/phytium/libraries/standalone/drivers/qspi/fqspi/fqspi.c create mode 100644 bsp/phytium/libraries/standalone/drivers/qspi/fqspi/fqspi.h create mode 100644 bsp/phytium/libraries/standalone/drivers/qspi/fqspi/fqspi_flash.c create mode 100644 bsp/phytium/libraries/standalone/drivers/qspi/fqspi/fqspi_flash.h create mode 100644 bsp/phytium/libraries/standalone/drivers/qspi/fqspi/fqspi_g.c create mode 100644 bsp/phytium/libraries/standalone/drivers/qspi/fqspi/fqspi_hw.c create mode 100644 bsp/phytium/libraries/standalone/drivers/qspi/fqspi/fqspi_hw.h create mode 100644 bsp/phytium/libraries/standalone/drivers/qspi/fqspi/fqspi_sinit.c create mode 100644 bsp/phytium/libraries/standalone/drivers/rtc/Kconfig create mode 100644 bsp/phytium/libraries/standalone/drivers/rtc/frtc/frtc.c create mode 100644 bsp/phytium/libraries/standalone/drivers/rtc/frtc/frtc.h create mode 100644 bsp/phytium/libraries/standalone/drivers/rtc/frtc/frtc_g.c create mode 100644 bsp/phytium/libraries/standalone/drivers/rtc/frtc/frtc_hw.c create mode 100644 bsp/phytium/libraries/standalone/drivers/rtc/frtc/frtc_hw.h create mode 100644 bsp/phytium/libraries/standalone/drivers/rtc/frtc/frtc_intr.c create mode 100644 bsp/phytium/libraries/standalone/drivers/rtc/frtc/frtc_sinit.c create mode 100644 bsp/phytium/libraries/standalone/drivers/sata/Kconfig create mode 100644 bsp/phytium/libraries/standalone/drivers/sata/fsata/fsata.c create mode 100644 bsp/phytium/libraries/standalone/drivers/sata/fsata/fsata.h create mode 100644 bsp/phytium/libraries/standalone/drivers/sata/fsata/fsata_g.c create mode 100644 bsp/phytium/libraries/standalone/drivers/sata/fsata/fsata_hw.c create mode 100644 bsp/phytium/libraries/standalone/drivers/sata/fsata/fsata_hw.h create mode 100644 bsp/phytium/libraries/standalone/drivers/sata/fsata/fsata_intr.c create mode 100644 bsp/phytium/libraries/standalone/drivers/sata/fsata/fsata_sinit.c create mode 100644 bsp/phytium/libraries/standalone/drivers/serial/Kconfig create mode 100644 bsp/phytium/libraries/standalone/drivers/serial/fpl011/fpl011.c create mode 100644 bsp/phytium/libraries/standalone/drivers/serial/fpl011/fpl011.h create mode 100644 bsp/phytium/libraries/standalone/drivers/serial/fpl011/fpl011_g.c create mode 100644 bsp/phytium/libraries/standalone/drivers/serial/fpl011/fpl011_hw.c create mode 100644 bsp/phytium/libraries/standalone/drivers/serial/fpl011/fpl011_hw.h create mode 100644 bsp/phytium/libraries/standalone/drivers/serial/fpl011/fpl011_intr.c create mode 100644 bsp/phytium/libraries/standalone/drivers/serial/fpl011/fpl011_options.c create mode 100644 bsp/phytium/libraries/standalone/drivers/serial/fpl011/fpl011_sinit.c create mode 100644 bsp/phytium/libraries/standalone/drivers/spi/Kconfig create mode 100644 bsp/phytium/libraries/standalone/drivers/spi/fspim/fspim.c create mode 100644 bsp/phytium/libraries/standalone/drivers/spi/fspim/fspim.h create mode 100644 bsp/phytium/libraries/standalone/drivers/spi/fspim/fspim_g.c create mode 100644 bsp/phytium/libraries/standalone/drivers/spi/fspim/fspim_hw.c create mode 100644 bsp/phytium/libraries/standalone/drivers/spi/fspim/fspim_hw.h create mode 100644 bsp/phytium/libraries/standalone/drivers/spi/fspim/fspim_intr.c create mode 100644 bsp/phytium/libraries/standalone/drivers/spi/fspim/fspim_selftest.c create mode 100644 bsp/phytium/libraries/standalone/drivers/spi/fspim/fspim_sinit.c create mode 100644 bsp/phytium/libraries/standalone/drivers/timer/Kconfig create mode 100644 bsp/phytium/libraries/standalone/drivers/timer/ftimer_tacho/ftacho.c create mode 100644 bsp/phytium/libraries/standalone/drivers/timer/ftimer_tacho/ftimer.c create mode 100644 bsp/phytium/libraries/standalone/drivers/timer/ftimer_tacho/ftimer_tacho.h create mode 100644 bsp/phytium/libraries/standalone/drivers/timer/ftimer_tacho/ftimer_tacho_g.c create mode 100644 bsp/phytium/libraries/standalone/drivers/timer/ftimer_tacho/ftimer_tacho_hw.h create mode 100644 bsp/phytium/libraries/standalone/drivers/timer/ftimer_tacho/ftimer_tacho_intr.c create mode 100644 bsp/phytium/libraries/standalone/drivers/usb/Kconfig create mode 100644 bsp/phytium/libraries/standalone/drivers/usb/fusb.c create mode 100644 bsp/phytium/libraries/standalone/drivers/usb/fusb.h create mode 100644 bsp/phytium/libraries/standalone/drivers/usb/fusb_debug.c create mode 100644 bsp/phytium/libraries/standalone/drivers/usb/fusb_def.h create mode 100644 bsp/phytium/libraries/standalone/drivers/usb/fusb_dev.c create mode 100644 bsp/phytium/libraries/standalone/drivers/usb/fusb_g.c create mode 100644 bsp/phytium/libraries/standalone/drivers/usb/fusb_generic_hub.c create mode 100644 bsp/phytium/libraries/standalone/drivers/usb/fusb_generic_hub.h create mode 100644 bsp/phytium/libraries/standalone/drivers/usb/fusb_hid.c create mode 100644 bsp/phytium/libraries/standalone/drivers/usb/fusb_hid.h create mode 100644 bsp/phytium/libraries/standalone/drivers/usb/fusb_hub.c create mode 100644 bsp/phytium/libraries/standalone/drivers/usb/fusb_hub.h create mode 100644 bsp/phytium/libraries/standalone/drivers/usb/fusb_msc.c create mode 100644 bsp/phytium/libraries/standalone/drivers/usb/fusb_msc.h create mode 100644 bsp/phytium/libraries/standalone/drivers/usb/fusb_private.h create mode 100644 bsp/phytium/libraries/standalone/drivers/usb/fusb_sinit.c create mode 100644 bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci.c create mode 100644 bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci.h create mode 100644 bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci_cmd.c create mode 100644 bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci_debug.c create mode 100644 bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci_dev.c create mode 100644 bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci_evt.c create mode 100644 bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci_hw.c create mode 100644 bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci_hw.h create mode 100644 bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci_private.h create mode 100644 bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci_roothub.c create mode 100644 bsp/phytium/libraries/standalone/drivers/watchdog/Kconfig create mode 100644 bsp/phytium/libraries/standalone/drivers/watchdog/fwdt/fwdt.c create mode 100644 bsp/phytium/libraries/standalone/drivers/watchdog/fwdt/fwdt.h create mode 100644 bsp/phytium/libraries/standalone/drivers/watchdog/fwdt/fwdt_g.c create mode 100644 bsp/phytium/libraries/standalone/drivers/watchdog/fwdt/fwdt_hw.c create mode 100644 bsp/phytium/libraries/standalone/drivers/watchdog/fwdt/fwdt_hw.h create mode 100644 bsp/phytium/libraries/standalone/drivers/watchdog/fwdt/fwdt_intr.c create mode 100644 bsp/phytium/libraries/standalone/drivers/watchdog/fwdt/fwdt_sinit.c create mode 100644 bsp/phytium/libraries/standalone/gitinfo diff --git a/bsp/phytium/.gitignore b/bsp/phytium/.gitignore new file mode 100644 index 0000000000..f577e88ffe --- /dev/null +++ b/bsp/phytium/.gitignore @@ -0,0 +1 @@ +*.dis \ No newline at end of file diff --git a/bsp/phytium/README.md b/bsp/phytium/README.md new file mode 100644 index 0000000000..74a251aabc --- /dev/null +++ b/bsp/phytium/README.md @@ -0,0 +1,171 @@ + +# PHYTIUM BSP 说明 + +## 简介 + +本文档为基于 RT-THREAD 的 Phytium 系列 CPU 相关 BSP 说明。 + +本文的主要内容如下: + +- BSP 简介 +- 移植支持情况 +- 如何在 Ubuntu/Windows 环境下使用此 BSP +- 如何进行编译与实验 +- 维护人信息 +- 注意事项 + + +## BSP简介 + +- 飞腾芯片产品具有谱系全、性能高、生态完善、自主化程度高等特点,目前主要包括高性能服务器CPU(飞腾腾云S系列)、高效能桌面CPU(飞腾腾锐D系列)、高端嵌入式CPU(飞腾腾珑E系列)和飞腾套片四大系列,为从端到云的各型设备提供核心算力支撑。 + + + +- 本BSP目前支持飞腾腾锐D系列、飞腾腾珑E系列 相关CPU,基于 Phytium-Standalone-SDK 进行开发。开发者能够使用 + +- 本BSP 支持Phytium系列CPU 工作在 aarch32/aarch64 两种执行状态 ,开发者能够根据自己的应用场景灵活选择CPU 工作状态。 + + + +## 移植支持情况 + +| **片上外设** | **支持情况** | **备注** | +| :----------------- | :----------: | :------------------------------------- | +| UART | 支持 | uart1 打印输出| + +| **芯片** | **支持情况** | **备注** | +| :----------------- | :----------: | :------------------------------------- | +| E2000D | 支持 | 支持SMP | +| E2000Q | 支持 | 支持SMP | +| E2000S | 支持 | | + + +## 如何在Ubuntu/Windows 环境下使用此BSP + + +### Ubuntu 环境 + +1. 根据 [Linux x86_64 SDK安装方法](https://gitee.com/phytium_embedded/phytium-standalone-sdk/blob/release/doc/reference/usr/install_linux_x86_64.md) 中1.1 - 1.2 节中介绍,先安装 SDK 编译环境 +2. 参考[RT-Thread/env](https://github.com/RT-Thread/env) 中Tutorial 在ubuntu 环境下安装 env 环境 +3. 在编译环境下执行 ```source ~/.env/env.sh``` +4. 以aarch32 执行状态为例,```cd bsp/phytium/aarch32``` + + +### Windows 环境 + +1. 根据[Windows 10 SDK安装方法](https://gitee.com/phytium_embedded/phytium-standalone-sdk/blob/release/doc/reference/usr/install_windows.md),安装 SDK 编译环境,编辑新建 Windows 环境变量 AARCH32_CROSS_PATH 和 AARCH64_CROSS_PATH +2. 参考[RT-Thread/env](https://github.com/RT-Thread/env) 中Tutorial 在 Windows 环境下解压 env 压缩包 +3. 以aarch32 执行状态为例,```cd bsp/phytium/aarch32``` +4. 使用 export_project.py 导出 BSP 工程到其他目录进行开发 +5. 使用 RT-Studio 导入 BSP 工程进行开发 + + +## 如何进行编译与实验 + +### 编译说明 + +- [AARCH32](./aarch32/README.md) +- [AARCH64](./aarch64/README.md) + +### 烧写及执行 + +#### Ubuntu 环境配置 tftp 服务 + +- 在开发环境`host`侧安装`tftp`服务 + +``` +sudo apt-get install tftp-hpa tftpd-hpa +sudo apt-get install xinetd +``` + +- 新建 tftboot 目录, 以`/mnt/d/tftboot`为例, 此目录应与项目编译脚本makefile中的USR_BOOT_DIR一致, 并确保 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`配置,指向`tftboot`目录 + 修改/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`服务,获取`tftboot`目录下的一个文件 + +``` +$ tftp 192.168.4.50 +tftp> get test1234 +tftp> q +``` + +#### Windows环境下配置 tftp 服务 + +- 下载Tftpd64 工具 ,并安装Tftpd64 工具 + +![输入图片说明](./figures/tftp32_srv.png) + +- 之后每次使用前,进入Windows服务,手动将一下服务打开 + +![输入图片说明](./figures/config_tftp32.png) + +#### 利用uboot 上tftp 服务加载镜像 + +- 进入`u-boot`界面,输入如下指令,配置开发板ip,`host`侧ip和网关地址 + ``` + setenv ipaddr 192.168.4.20 + setenv serverip 192.168.4.50 + setenv gatewayip 192.168.4.1 + ``` +- 将编译好的elf 或者bin 文件拷贝至Tftpd64所设置文件夹下 +- 随后烧录的文件到开发板,输入以下指令 + + ``` + tftpboot 0x90100000 rtthread.elf + bootelf -p 0x90100000 + ``` + +### 运行结果 + +![运行结果](./figures/result.png) + + +## 维护人信息 + +- huanghe: huanghe@phytium.com.cn +- zhugengyu: zhugengyu@phytium.com.cn \ No newline at end of file diff --git a/bsp/phytium/aarch32/.config b/bsp/phytium/aarch32/.config new file mode 100644 index 0000000000..86c8e62a6a --- /dev/null +++ b/bsp/phytium/aarch32/.config @@ -0,0 +1,927 @@ +# +# Automatically generated file; DO NOT EDIT. +# RT-Thread Project Configuration +# + +# +# RT-Thread Kernel +# +CONFIG_RT_NAME_MAX=8 +# CONFIG_RT_USING_ARCH_DATA_TYPE is not set +CONFIG_RT_USING_SMP=y +CONFIG_RT_CPUS_NR=4 +CONFIG_RT_ALIGN_SIZE=4 +# 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=1000 +CONFIG_RT_USING_OVERFLOW_CHECK=y +CONFIG_RT_USING_HOOK=y +CONFIG_RT_HOOK_USING_FUNC_PTR=y +CONFIG_RT_USING_IDLE_HOOK=y +CONFIG_RT_IDLE_HOOK_LIST_SIZE=4 +CONFIG_IDLE_THREAD_STACK_SIZE=256 +CONFIG_SYSTEM_THREAD_STACK_SIZE=256 +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=y +# CONFIG_RT_KSERVICE_USING_STDLIB_MEMORY is not set +# CONFIG_RT_KSERVICE_USING_TINY_SIZE is not set +# CONFIG_RT_USING_TINY_FFS is not set +# CONFIG_RT_KPRINTF_USING_LONGLONG is not set +CONFIG_RT_DEBUG=y +# CONFIG_RT_DEBUG_COLOR is not set +# 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=y +CONFIG_RT_USING_SMALL_MEM=y +# CONFIG_RT_USING_SLAB is not set +# CONFIG_RT_USING_MEMHEAP is not set +CONFIG_RT_USING_SMALL_MEM_AS_HEAP=y +# CONFIG_RT_USING_MEMHEAP_AS_HEAP is not set +# CONFIG_RT_USING_SLAB_AS_HEAP is not set +# CONFIG_RT_USING_USERHEAP is not set +# CONFIG_RT_USING_NOHEAP is not set +# CONFIG_RT_USING_MEMTRACE is not set +# CONFIG_RT_USING_HEAP_ISR 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 is not set +CONFIG_RT_USING_CONSOLE=y +CONFIG_RT_CONSOLEBUF_SIZE=128 +CONFIG_RT_CONSOLE_DEVICE_NAME="uart1" +CONFIG_RT_VER_NUM=0x50000 +CONFIG_ARCH_ARM=y +CONFIG_RT_USING_CPU_FFS=y +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_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 +# CONFIG_RT_USING_LEGACY is not set +CONFIG_RT_USING_MSH=y +CONFIG_RT_USING_FINSH=y +CONFIG_FINSH_USING_MSH=y +CONFIG_FINSH_THREAD_NAME="tshell" +CONFIG_FINSH_THREAD_PRIORITY=20 +CONFIG_FINSH_THREAD_STACK_SIZE=4096 +CONFIG_FINSH_USING_HISTORY=y +CONFIG_FINSH_HISTORY_LINES=5 +CONFIG_FINSH_USING_SYMTAB=y +CONFIG_FINSH_CMD_SIZE=80 +CONFIG_MSH_USING_BUILT_IN_COMMANDS=y +CONFIG_FINSH_USING_DESCRIPTION=y +# CONFIG_FINSH_ECHO_DISABLE_DEFAULT is not set +# CONFIG_FINSH_USING_AUTH is not set +CONFIG_FINSH_ARG_MAX=10 +CONFIG_RT_USING_DFS=y +CONFIG_DFS_USING_POSIX=y +CONFIG_DFS_USING_WORKDIR=y +CONFIG_DFS_FILESYSTEMS_MAX=4 +CONFIG_DFS_FILESYSTEM_TYPES_MAX=4 +CONFIG_DFS_FD_MAX=16 +# CONFIG_RT_USING_DFS_MNTTABLE is not set +# CONFIG_RT_USING_DFS_ELMFAT is not set +# CONFIG_RT_USING_DFS_DEVFS is not set +# CONFIG_RT_USING_DFS_ROMFS is not set +# CONFIG_RT_USING_DFS_RAMFS is not set +# CONFIG_RT_USING_FAL is not set +# CONFIG_RT_USING_LWP is not set + +# +# Device Drivers +# +CONFIG_RT_USING_DEVICE_IPC=y +# CONFIG_RT_USING_SYSTEM_WORKQUEUE is not set +CONFIG_RT_USING_SERIAL=y +CONFIG_RT_USING_SERIAL_V1=y +# CONFIG_RT_USING_SERIAL_V2 is not set +CONFIG_RT_SERIAL_USING_DMA=y +CONFIG_RT_SERIAL_RB_BUFSZ=64 +# CONFIG_RT_USING_CAN 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=y +# 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 is not set +# CONFIG_RT_USING_SPI 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 is not set +# CONFIG_RT_USING_USB_HOST is not set +# CONFIG_RT_USING_USB_DEVICE is not set + +# +# C/C++ and POSIX layer +# +CONFIG_RT_LIBC_DEFAULT_TIMEZONE=8 + +# +# POSIX (Portable Operating System Interface) layer +# +# CONFIG_RT_USING_POSIX_FS is not set +# CONFIG_RT_USING_POSIX_DELAY is not set +# CONFIG_RT_USING_POSIX_CLOCK is not set +# CONFIG_RT_USING_POSIX_TIMER is not set +# CONFIG_RT_USING_PTHREADS is not set +# CONFIG_RT_USING_MODULE is not set + +# +# Interprocess Communication (IPC) +# +# CONFIG_RT_USING_POSIX_PIPE is not set +# CONFIG_RT_USING_POSIX_MESSAGE_QUEUE is not set +# CONFIG_RT_USING_POSIX_MESSAGE_SEMAPHORE is not set + +# +# Socket is in the 'Network' category +# +# CONFIG_RT_USING_CPLUSPLUS is not set + +# +# Network +# +# CONFIG_RT_USING_SAL is not set +# CONFIG_RT_USING_NETDEV is not set +# CONFIG_RT_USING_LWIP is not set +# CONFIG_RT_USING_AT is not set + +# +# Utilities +# +# CONFIG_RT_USING_RYM is not set +# CONFIG_RT_USING_ULOG is not set +# CONFIG_RT_USING_UTEST is not set +# CONFIG_RT_USING_VAR_EXPORT is not set +# CONFIG_RT_USING_RT_LINK is not set +# CONFIG_RT_USING_VBUS is not set + +# +# RT-Thread Utestcases +# +# CONFIG_RT_USING_UTESTCASES is not set + +# +# RT-Thread online packages +# + +# +# IoT - internet of things +# +# CONFIG_PKG_USING_LWIP is not set +# 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_LIBMODBUS is not set +# CONFIG_PKG_USING_FREEMODBUS 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 +# CONFIG_PKG_USING_ZB_COORDINATOR 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_EZ_IOT_OS is not set +# CONFIG_PKG_USING_IOTSHARP_SDK is not set +# CONFIG_PKG_USING_NIMBLE is not set +# CONFIG_PKG_USING_LLSYNC_SDK_ADAPTER 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_AGILE_TELNET is not set +# CONFIG_PKG_USING_NMEALIB 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_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 +# CONFIG_PKG_USING_RT_LINK_HW is not set +# CONFIG_PKG_USING_LORA_PKT_FWD is not set +# CONFIG_PKG_USING_LORA_GW_DRIVER_LIB is not set +# CONFIG_PKG_USING_LORA_PKT_SNIFFER is not set +# CONFIG_PKG_USING_HM is not set +# CONFIG_PKG_USING_SMALL_MODBUS is not set +# CONFIG_PKG_USING_NET_SERVER is not set +# CONFIG_PKG_USING_ZFTP is not set + +# +# security packages +# +# CONFIG_PKG_USING_MBEDTLS is not set +# CONFIG_PKG_USING_LIBSODIUM is not set +# CONFIG_PKG_USING_LIBHYDROGEN 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 +# + +# +# JSON: JavaScript Object Notation, a lightweight data-interchange format +# +# CONFIG_PKG_USING_CJSON is not set +# CONFIG_PKG_USING_LJSON is not set +# CONFIG_PKG_USING_RT_CJSON_TOOLS is not set +# CONFIG_PKG_USING_RAPIDJSON is not set +# CONFIG_PKG_USING_JSMN is not set +# CONFIG_PKG_USING_AGILE_JSMN is not set +# CONFIG_PKG_USING_PARSON is not set + +# +# XML: Extensible Markup Language +# +# CONFIG_PKG_USING_SIMPLE_XML is not set +# CONFIG_PKG_USING_EZXML is not set +# CONFIG_PKG_USING_LUATOS_SOC is not set +# CONFIG_PKG_USING_LUA is not set +# CONFIG_PKG_USING_JERRYSCRIPT is not set +# CONFIG_PKG_USING_MICROPYTHON is not set +# CONFIG_PKG_USING_PIKASCRIPT is not set +# CONFIG_PKG_USING_RTT_RUST is not set + +# +# multimedia packages +# + +# +# LVGL: powerful and easy-to-use embedded GUI library +# +# CONFIG_PKG_USING_LVGL is not set +# CONFIG_PKG_USING_LITTLEVGL2RTT is not set +# CONFIG_PKG_USING_LV_MUSIC_DEMO is not set +# CONFIG_PKG_USING_GUI_GUIDER_DEMO is not set + +# +# u8g2: a monochrome graphic library +# +# CONFIG_PKG_USING_U8G2_OFFICIAL is not set +# CONFIG_PKG_USING_U8G2 is not set +# 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 +# CONFIG_PKG_USING_NUEMWIN is not set +# CONFIG_PKG_USING_MP3PLAYER is not set +# CONFIG_PKG_USING_TINYJPEG is not set +# CONFIG_PKG_USING_UGUI is not set + +# +# PainterEngine: A cross-platform graphics application framework written in C language +# +# CONFIG_PKG_USING_PAINTERENGINE is not set +# CONFIG_PKG_USING_PAINTERENGINE_AUX is not set +# CONFIG_PKG_USING_MCURSES is not set +# CONFIG_PKG_USING_TERMBOX is not set +# CONFIG_PKG_USING_VT100 is not set +# CONFIG_PKG_USING_QRCODE is not set +# CONFIG_PKG_USING_GUIENGINE is not set +# CONFIG_PKG_USING_PERSIMMON 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_SEGGER_RTT is not set +# CONFIG_PKG_USING_RDB 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_REGEX is not set +# CONFIG_PKG_USING_MEM_SANDBOX is not set +# CONFIG_PKG_USING_SOLAR_TERMS is not set +# CONFIG_PKG_USING_GAN_ZHI is not set +# CONFIG_PKG_USING_FDT is not set +# CONFIG_PKG_USING_CBOX is not set +# CONFIG_PKG_USING_SNOWFLAKE is not set +# CONFIG_PKG_USING_HASH_MATCH is not set +# CONFIG_PKG_USING_FIRE_PID_CURVE is not set +# CONFIG_PKG_USING_ARMV7M_DWT_TOOL is not set +# CONFIG_PKG_USING_VOFA_PLUS is not set + +# +# system packages +# + +# +# enhanced kernel services +# +# CONFIG_PKG_USING_RT_MEMCPY_CM is not set +# CONFIG_PKG_USING_RT_KPRINTF_THREADSAFE is not set +# CONFIG_PKG_USING_RT_VSNPRINTF_FULL is not set + +# +# acceleration: Assembly language or algorithmic acceleration packages +# +# 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 + +# +# CMSIS: ARM Cortex-M Microcontroller Software Interface Standard +# +# CONFIG_PKG_USING_CMSIS_5 is not set +# CONFIG_PKG_USING_CMSIS_RTOS1 is not set +# CONFIG_PKG_USING_CMSIS_RTOS2 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_FREERTOS_WRAPPER 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_PERF_COUNTER 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_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 +# CONFIG_PKG_USING_PPOOL is not set +# CONFIG_PKG_USING_OPENAMP 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 +# CONFIG_PKG_USING_ARM_2D is not set +# CONFIG_PKG_USING_MCUBOOT is not set +# CONFIG_PKG_USING_TINYUSB is not set +# CONFIG_PKG_USING_CHERRYUSB is not set +# CONFIG_PKG_USING_KMULTI_RTIMER is not set +# CONFIG_PKG_USING_TFDB is not set +# CONFIG_PKG_USING_QPC is not set +# CONFIG_PKG_USING_AGILE_UPGRADE 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_ADT74XX is not set +# CONFIG_PKG_USING_AS7341 is not set +# CONFIG_PKG_USING_STM32_SDIO is not set +# CONFIG_PKG_USING_ESP_IDF is not set +# CONFIG_PKG_USING_ICM20608 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 + +# +# Kendryte SDK +# +# CONFIG_PKG_USING_K210_SDK is not set +# CONFIG_PKG_USING_KENDRYTE_SDK is not set +# CONFIG_PKG_USING_INFRARED is not set +# CONFIG_PKG_USING_MULTI_INFRARED 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_RS232 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 +# CONFIG_PKG_USING_TCA9534 is not set +# CONFIG_PKG_USING_KOBUKI is not set +# CONFIG_PKG_USING_ROSSERIAL is not set +# CONFIG_PKG_USING_MICRO_ROS is not set +# CONFIG_PKG_USING_MCP23008 is not set +# CONFIG_PKG_USING_BLUETRUM_SDK is not set +# CONFIG_PKG_USING_MISAKA_AT24CXX is not set +# CONFIG_PKG_USING_MISAKA_RGB_BLING is not set +# CONFIG_PKG_USING_LORA_MODEM_DRIVER is not set +# CONFIG_PKG_USING_BL_MCU_SDK is not set +# CONFIG_PKG_USING_SOFT_SERIAL is not set +# CONFIG_PKG_USING_MB85RS16 is not set +# CONFIG_PKG_USING_CW2015 is not set +# CONFIG_PKG_USING_RFM300 is not set +# CONFIG_PKG_USING_IO_INPUT_FILTER is not set +# CONFIG_PKG_USING_RASPBERRYPI_PICO_SDK is not set +# CONFIG_PKG_USING_LRF_NV7LIDAR 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 +# + +# +# project laboratory +# + +# +# 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 + +# +# entertainment: terminal games and other interesting software packages +# +# CONFIG_PKG_USING_CMATRIX is not set +# CONFIG_PKG_USING_SL is not set +# CONFIG_PKG_USING_CAL is not set +# CONFIG_PKG_USING_ACLOCK is not set +# 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_COWSAY is not set +# 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_MINIZIP is not set +# CONFIG_PKG_USING_HEATSHRINK 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 +# 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_UKAL is not set +# CONFIG_PKG_USING_CRCLIB is not set +# CONFIG_PKG_USING_LWGPS is not set +# CONFIG_PKG_USING_STATE_MACHINE is not set +# CONFIG_PKG_USING_DESIGN_PATTERN is not set +# CONFIG_PKG_USING_CONTROLLER is not set +# CONFIG_PKG_USING_PHASE_LOCKED_LOOP is not set +# CONFIG_PKG_USING_MFBD is not set +# CONFIG_PKG_USING_SLCAN2RTT is not set +# CONFIG_PKG_USING_SOEM is not set +# CONFIG_PKG_USING_QPARAM is not set + +# +# Arduino libraries +# +# CONFIG_PKG_USING_RTDUINO is not set + +# +# Projects +# +# CONFIG_PKG_USING_ARDUINO_ULTRASOUND_RADAR is not set +# CONFIG_PKG_USING_ARDUINO_SENSOR_KIT is not set +# CONFIG_PKG_USING_ARDUINO_MATLAB_SUPPORT is not set + +# +# Sensors +# +# CONFIG_PKG_USING_ARDUINO_SEEED_BMP280 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADXL375 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VL53L0X is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_LIS3DHTR is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_DHT is not set +# CONFIG_PKG_USING_ARDUINO_CAPACITIVESENSOR is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SENSOR is not set +# CONFIG_PKG_USING_ADAFRUIT_MAX31855 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX31865 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX31856 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90614 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM9DS1 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AHTX0 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM9DS0 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP280 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADT7410 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP085 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BME680 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP9808 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP4728 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_INA219 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LTR390 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADXL345 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DHT is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP9600 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM6DS is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BNO055 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX1704X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MMC56X3 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90393 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90395 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ICM20X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DPS310 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HTS221 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SHT4X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SHT31 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADXL343 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BME280 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AS726X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AMG88XX is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AM2320 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AM2315 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LTR329_LTR303 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP085_UNIFIED is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP183 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP183_UNIFIED is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP3XX is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MS8607 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS3MDL is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90640 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MMA8451 is not set +# CONFIG_PKG_USING_ADAFRUIT_MSA301 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPL115A2 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BNO08X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BNO08X_RVC is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS2MDL is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM303DLH_MAG is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LC709203F is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_CAP1188 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_CCS811 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_NAU7802 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS331 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LPS2X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LPS35HW is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM303_ACCEL is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS3DH is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCF8591 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPL3115A2 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPR121 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPRLS is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPU6050 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCT2075 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PM25AQI is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_EMC2101 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_FXAS21002C is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SCD30 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_FXOS8700 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HMC5883_UNIFIED is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SGP30 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TMP006 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TLA202X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TCS34725 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI7021 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI1145 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SGP40 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SHTC3 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HDC1000 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HTU21DF is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AS7341 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HTU31D is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SENSORLAB is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_INA260 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TMP007_LIBRARY is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_L3GD20 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TMP117 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TSC2007 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TSL2561 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TSL2591_LIBRARY is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VCNL4040 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VEML6070 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VEML6075 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VEML7700 is not set + +# +# Display +# +# CONFIG_PKG_USING_ARDUINO_U8G2 is not set + +# +# Timing +# +# CONFIG_PKG_USING_ARDUINO_MSTIMER2 is not set + +# +# Data Processing +# +# CONFIG_PKG_USING_ARDUINO_KALMANFILTER is not set +# CONFIG_PKG_USING_ARDUINO_ARDUINOJSON is not set + +# +# Data Storage +# + +# +# Communication +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PN532 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI4713 is not set + +# +# Device Control +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCF8574 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCA9685 is not set + +# +# Other +# + +# +# Signal IO +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BUSIO is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TCA8418 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP23017 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADS1X15 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AW9523 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP3008 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP4725 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BD3491FS is not set + +# +# Uncategorized +# + +# +# Hardware Drivers +# + +# +# On-chip Peripheral Drivers +# +CONFIG_BSP_USING_UART=y +CONFIG_RT_USING_UART1=y +# CONFIG_RT_USING_UART0 is not set + +# +# Board extended module Drivers +# +CONFIG_PHYTIUM_ARCH_AARCH32=y + +# +# Standalone Setting +# +CONFIG_TARGET_ARMV8_AARCH32=y +CONFIG_USE_AARCH64_L1_TO_AARCH32=y + +# +# Board Configuration +# +# CONFIG_TARGET_F2000_4 is not set +# CONFIG_TARGET_D2000 is not set +CONFIG_TARGET_E2000Q=y +# CONFIG_TARGET_E2000D is not set +# CONFIG_TARGET_E2000S is not set +CONFIG_TARGET_E2000=y +CONFIG_DEFAULT_DEBUG_PRINT_UART1=y +# CONFIG_DEFAULT_DEBUG_PRINT_UART0 is not set +# CONFIG_DEFAULT_DEBUG_PRINT_UART2 is not set + +# +# Components Configuration +# +# CONFIG_USE_SPI is not set +# CONFIG_USE_QSPI is not set +CONFIG_USE_GIC=y +CONFIG_ENABLE_GICV3=y +CONFIG_USE_SERIAL=y + +# +# Usart Configuration +# +CONFIG_ENABLE_Pl011_UART=y +# CONFIG_USE_GPIO is not set +# CONFIG_USE_ETH is not set +# CONFIG_USE_CAN is not set +# CONFIG_USE_I2C is not set +# CONFIG_USE_TIMER is not set +# CONFIG_USE_MIO is not set +# CONFIG_USE_SDMMC is not set +# CONFIG_USE_PCIE is not set +# CONFIG_USE_WDT is not set +# CONFIG_USE_DMA is not set +# CONFIG_USE_NAND is not set +# CONFIG_USE_RTC is not set +# CONFIG_USE_SATA is not set +# CONFIG_USE_USB is not set +# CONFIG_USE_ADC is not set +# CONFIG_USE_PWM is not set +# CONFIG_USE_IPC is not set +# CONFIG_LOG_VERBOS is not set +# CONFIG_LOG_DEBUG is not set +# CONFIG_LOG_INFO is not set +# CONFIG_LOG_WARN is not set +CONFIG_LOG_ERROR=y +# CONFIG_LOG_NONE is not set +CONFIG_USE_DEFAULT_INTERRUPT_CONFIG=y +CONFIG_INTERRUPT_ROLE_MASTER=y +# CONFIG_INTERRUPT_ROLE_SLAVE is not set +# CONFIG_LOG_EXTRA_INFO is not set +# CONFIG_BOOTUP_DEBUG_PRINTS is not set diff --git a/bsp/phytium/aarch32/Kconfig b/bsp/phytium/aarch32/Kconfig new file mode 100644 index 0000000000..cc9a3fe042 --- /dev/null +++ b/bsp/phytium/aarch32/Kconfig @@ -0,0 +1,54 @@ +mainmenu "RT-Thread Project Configuration" + +config RTT_DIR + string + option env="RTT_ROOT" + default "../../.." + +config BSP_DIR + string + option env="BSP_ROOT" + default "../." + +config STANDALONE_DIR + string + option env="STANDALONE_DIR" + default ".././libraries/standalone" + +config PKGS_DIR + string + option env="PKGS_ROOT" + default "packages" + +source "$RTT_DIR/Kconfig" +source "$PKGS_DIR/Kconfig" +source "$BSP_DIR/libraries/drivers/Kconfig" + +config PHYTIUM_ARCH_AARCH32 + bool + select ARCH_ARM_CORTEX_A + select RT_USING_COMPONENTS_INIT + select RT_USING_USER_MAIN + select RT_USING_GIC_V3 + select TARGET_ARMV8_AARCH32 + select USE_AARCH64_L1_TO_AARCH32 + default y + +menu "Standalone Setting" + config TARGET_ARMV8_AARCH32 + bool "Armv8 Aarch32" + default y + + config USE_AARCH64_L1_TO_AARCH32 + bool + prompt "Use Aarch64 L1 to Aarch32 code" + default y + help + Use the Aarch64 to Aarch32 mode function + + source "$STANDALONE_DIR/board/Kconfig" + source "$STANDALONE_DIR/drivers/Kconfig" + source "$STANDALONE_DIR/common/Kconfig" + +endmenu + diff --git a/bsp/phytium/aarch32/README.md b/bsp/phytium/aarch32/README.md new file mode 100644 index 0000000000..7dcd689949 --- /dev/null +++ b/bsp/phytium/aarch32/README.md @@ -0,0 +1,108 @@ + +# AARCH32 工作模式使用 + +- 当开发者需要基于 Phytium 系列芯片进行开发时,可以从以下几个步骤出发配置芯片 + + +## 1. 如何选择芯片 + +- Windows Env 环境下 + +```shell + menuconfig +``` + +- Linux 环境下 + +```shell + scons --menuconfig +``` + +开发者通过以下选择进行配置 + +``` +Standalone Setting > Board Configuration > Chip +``` + +![](./figures/chip_select.png) +![](./figures/phytium_cpu_select.png) + +## 2. 如何选择驱动 + + +```shell + scons --menuconfig +``` + +开发者通过以下选项进行驱动的使能 + +``` +Hardware Drivers > On-chip Peripheral Drivers +``` + +![](./figures/select_driver.png) + + +## 3. 开启SDK中内部调试信息 + + +```shell + scons --menuconfig +``` + +开发者通过以下选项进行调试信息等级的设置 + +![](./figures/select_debug_info.png) + + + +## 4. 编译程序 + +```shell + scons -c + scons +``` + +- 完成编译之后目录下将会生成以下几个文件 + +``` +rtthread_a32.bin +rtthread_a32.elf +rtthread_a32.map +``` + +## 5. 打包导出工程源代码 + + +- 指定工程名和路径,打包RT-Thread内核和Phytium BSP代码,可以导出一个工程工程 +``` +python ./export_project.py -n=phytium-a32 -o=D:/proj/rt-thread-e2000/phytium-a32 +``` + +![](./figures/export_project.png) + + +- 进入打包工程的目录,修改工程根目录 Kconfig 中的路径 BSP_DIR 和 STANDALONE_DIR +> env 环境中的 menuconfig 不会调用 SConstruct 修改路径环境变量,因此需要手动修改路径 + +``` +config BSP_DIR + string + option env="BSP_ROOT" + default "." + +config STANDALONE_DIR + string + option env="STANDALONE_DIR" + default "libraries/standalone" +``` + +- 输入 menuconfig 和 scons 完成编译 + + +## 6. 将工程导入 RT-Studio + +- 在 RT-Studio 使用功能`RT-Thread Bsp 到工作空间`,导入 5. 中导出的 BSP 工程 +- 设置 BSP 工程的交叉编译链后进行后续开发 + +![](./figures/import_project.png) \ No newline at end of file diff --git a/bsp/phytium/aarch32/SConscript b/bsp/phytium/aarch32/SConscript new file mode 100644 index 0000000000..4c815c49b8 --- /dev/null +++ b/bsp/phytium/aarch32/SConscript @@ -0,0 +1,15 @@ +# RT-Thread building script for bridge + +import os +from building import * + +cwd = GetCurrentDir() +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/phytium/aarch32/SConstruct b/bsp/phytium/aarch32/SConstruct new file mode 100644 index 0000000000..2d1eaf98f7 --- /dev/null +++ b/bsp/phytium/aarch32/SConstruct @@ -0,0 +1,58 @@ +import os +import sys +import rtconfig + +IS_EXPORTED = False + +# setup RT-Thread Root Path +if os.getenv('RTT_ROOT'): + RTT_ROOT = os.getenv('RTT_ROOT') +else: + RTT_ROOT = os.getcwd() + '/../../..' + +sys.path = sys.path + [os.path.join(RTT_ROOT, 'tools')] +try: + from building import * +except: + print('Cannot found RT-Thread root directory, please check RTT_ROOT') + print(RTT_ROOT) + exit(-1) + +if RTT_ROOT == 'rt-thread': + IS_EXPORTED = True # if kenrel and bsp has been exported by export_project.py + +# setup Phytium BSP Root Path +if IS_EXPORTED: + BSP_ROOT = '.' +else: + BSP_ROOT = RTT_ROOT + '/bsp/phytium' + +TARGET = 'rtthread_a32.' + rtconfig.TARGET_EXT + +DefaultEnvironment(tools=[]) +env = Environment(tools = ['mingw'], + AS = rtconfig.AS, ASFLAGS = rtconfig.AFLAGS, + CC = rtconfig.CC, CFLAGS = rtconfig.CFLAGS, + CXX = rtconfig.CXX, CXXFLAGS = rtconfig.CXXFLAGS, + 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('BSP_ROOT') +Export('rtconfig') + +# prepare building environment +objs = PrepareBuilding(env, RTT_ROOT, has_libcpu = False) + +if not IS_EXPORTED: # if project is not exported, libraries and board need to manually add + # include libraries + objs.extend(SConscript(os.path.join(BSP_ROOT + '/libraries', 'SConscript'))) + + # include board + objs.extend(SConscript(os.path.join(BSP_ROOT + '/board', 'SConscript'))) + +# make a building +DoBuilding(TARGET, objs) + diff --git a/bsp/phytium/aarch32/applications/SConscript b/bsp/phytium/aarch32/applications/SConscript new file mode 100644 index 0000000000..01eb940dfb --- /dev/null +++ b/bsp/phytium/aarch32/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/phytium/aarch32/applications/main.c b/bsp/phytium/aarch32/applications/main.c new file mode 100644 index 0000000000..e19779e113 --- /dev/null +++ b/bsp/phytium/aarch32/applications/main.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Email: opensource_embedded@phytium.com.cn + * + * Change Logs: + * Date Author Notes + * 2022-10-26 huanghe first commit + * + */ + +#include +#include + +#include + +#ifdef RT_USING_SMP + +struct rt_thread test_core[RT_CPUS_NR]; +static char *core_thread_name[8] = +{ + "core0_test", + "core1_test", + "core2_test", + "core3_test", + "core4_test", + "core5_test", + "core6_test", + "core7_test" +}; +static rt_uint8_t core_stack[RT_CPUS_NR][1024]; + +static void demo_core_thread(void *parameter) +{ + rt_base_t level; + while (1) + { + /* code */ + level = rt_cpus_lock(); + rt_kprintf("Hi, core%d \r\n", rt_hw_cpu_id()); + rt_cpus_unlock(level); + rt_thread_mdelay(2000000); + } +} + +void demo_core(void) +{ + rt_ubase_t i; + rt_ubase_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 + +int main(void) +{ +#ifdef RT_USING_SMP + demo_core(); +#endif + return RT_EOK; +} + diff --git a/bsp/phytium/aarch32/boot/SConscript b/bsp/phytium/aarch32/boot/SConscript new file mode 100644 index 0000000000..c05e6a119f --- /dev/null +++ b/bsp/phytium/aarch32/boot/SConscript @@ -0,0 +1,10 @@ +from building import * + +cwd = GetCurrentDir() +src = Glob('*.S') + Glob('*.c') + +CPPPATH = [cwd] + +group = DefineGroup('AARCH32-BOOT', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/phytium/aarch32/boot/aarch32_boot.S b/bsp/phytium/aarch32/boot/aarch32_boot.S new file mode 100644 index 0000000000..c1e3bdf449 --- /dev/null +++ b/bsp/phytium/aarch32/boot/aarch32_boot.S @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Email: opensource_embedded@phytium.com.cn + * + * Change Logs: + * Date Author Notes + * 2022-10-26 huanghe first commit + * + */ + +.global _boot +.set FPEXC_EN, 0x40000000 /* FPU enable bit, (1 << 30) */ +.org 0 +.text + +.section .boot,"ax" + +/* switch from aarch64-el2 to aarch32-el1 */ +_boot: +Startup_Aarch32: + .long 0xd5384240 /* mrs x0, currentel */ + .long 0xd342fc00 /* lsr x0, x0, #2 */ + .long 0x92400400 /* and x0, x0, #0x3 */ + .long 0xf1000c1f /* cmp x0, #0x3 */ + .long 0x540003a1 /* b.ne 1d0080c4 */ + +el3_mode: + .long 0xd53ecca0 /* mrs x0, s3_6_c12_c12_5 - ICC_SRE_EL3 */ + .long 0xb2400c00 /* orr x0, x0, #0xf */ + .long 0xd51ecca0 /* msr s3_6_c12_c12_5, x0 */ + .long 0xd5033fdf /* isb */ + .long 0xd53cc9a0 /* mrs x0, s3_4_c12_c9_5 - ICC_SRE_EL2 */ + .long 0xb2400c00 /* orr x0, x0, #0xf */ + .long 0xd51cc9a0 /* msr s3_4_c12_c9_5, x0 */ + .long 0xd5033fdf /* isb */ + .long 0xd538cca0 /* mrs x0, s3_0_c12_c12_5 - ICC_SRE_EL1 */ + .long 0xb2400000 /* orr x0, x0, #0x1 */ + .long 0xd518cca0 /* msr s3_0_c12_c12_5, x0 */ + .long 0xd5033fdf /* isb */ + + .long 0xd2803620 /* mov x0, #0x1b1 */ + .long 0xd51e1100 /* msr scr_el3, x0 */ + .long 0xd2867fe0 /* mov x0, #0x33ff */ + .long 0xd51c1140 /* msr cptr_el2, x0 */ + .long 0xd2810000 /* mov x0, #0x800 */ + .long 0xf2a61a00 /* movk x0, #0x30d0, lsl #16 */ + .long 0xd5181000 /* msr sctlr_el1, x0 */ + .long 0x910003e0 /* mov x0, sp */ + .long 0xd51c4100 /* msr sp_el1, x0 */ + .long 0xd53ec000 /* mrs x0, vbar_el3 */ + .long 0xd518c000 /* msr vbar_el1, x0 */ + .long 0xd2803a60 /* mov x0, #0x1d3 */ + .long 0xd51e4000 /* msr spsr_el3, x0 */ + .long 0x10000500 /* adr x0, 1d008158 */ + .long 0xd51e4020 /* msr elr_el3, x0 */ + .long 0xd69f03e0 /* eret */ + +el2_mode: + .long 0xd53cc9a0 /* mrs x0, s3_4_c12_c9_5 - ICC_SRE_EL2 */ + .long 0xb2400c00 /* orr x0, x0, #0xf */ + .long 0xd51cc9a0 /* msr s3_4_c12_c9_5, x0 */ + .long 0xd5033fdf /* isb */ + .long 0xd538cca0 /* mrs x0, s3_0_c12_c12_5 - ICC_SRE_EL1 */ + .long 0xb2400000 /* orr x0, x0, #0x1 */ + .long 0xd518cca0 /* msr s3_0_c12_c12_5, x0 */ + .long 0xd5033fdf /* isb */ + .long 0xd53ce100 /* mrs x0, cnthctl_el2 */ + .long 0xb2400400 /* orr x0, x0, #0x3 */ + .long 0xd51ce100 /* msr cnthctl_el2, x0 */ + .long 0xd51ce07f /* msr cntvoff_el2, xzr */ + .long 0xd5380000 /* mrs x0, midr_el1 */ + .long 0xd53800a1 /* mrs x1, mpidr_el1 */ + .long 0xd51c0000 /* msr vpidr_el2, x0 */ + .long 0xd51c00a1 /* msr vmpidr_el2, x1 */ + .long 0xd2867fe0 /* mov x0, #0x33ff */ + .long 0xd51c1140 /* msr cptr_el2, x0 */ + .long 0xd51c117f /* msr hstr_el2, xzr */ + .long 0xd2a00600 /* mov x0, #0x300000 */ + .long 0xd5181040 /* msr cpacr_el1, x0 */ + .long 0xd2800000 /* mov x0, #0x0 */ + .long 0xb2630000 /* orr x0, x0, #0x20000000 */ + .long 0xd51c1100 /* msr hcr_el2, x0 */ + .long 0xd53c1100 /* mrs x0, hcr_el2 */ + .long 0xd2810000 /* mov x0, #0x800 */ + .long 0xf2a61a00 /* movk x0, #0x30d0, lsl #16 */ + .long 0xd5181000 /* msr sctlr_el1, x0 */ + .long 0x910003e0 /* mov x0, sp */ + .long 0xd51c4100 /* msr sp_el1, x0 */ + .long 0xd53cc000 /* mrs x0, vbar_el2 */ + .long 0xd518c000 /* msr vbar_el1, x0 */ + .long 0xd2803a60 /* mov x0, #0x1d3 */ + .long 0xd51c4000 /* msr spsr_el2, x0 */ + .long 0x10000060 /* adr x0, 1d008158 */ + .long 0xd51c4020 /* msr elr_el2, x0 */ + .long 0xd69f03e0 /* eret */ + +el1_mode: + mov r0, #0 + mov r1, #0 + mov r2, #0 + mov r3, #0 + mov r4, #0 + mov r5, #0 + mov r6, #0 + mov r7, #0 + mov r8, #0 + mov r9, #0 + mov r10, #0 + mov r11, #0 + mov r12, #0 + mcr p15, 0, r0, c1, c0, 0 /* reset control register */ + isb + + /* enable vfp, therefore f_prink workable */ + vmrs r1, FPEXC /* read the exception register */ + orr r1,r1, #FPEXC_EN /* set VFP enable bit, leave the others in orig state */ + vmsr FPEXC, r1 /* write back the exception register */ + + bl system_vectors /* jump to libcpu/arm/cortex-a/vector_gcc.S */ + diff --git a/bsp/phytium/aarch32/export_project.py b/bsp/phytium/aarch32/export_project.py new file mode 100644 index 0000000000..d28e6d7430 --- /dev/null +++ b/bsp/phytium/aarch32/export_project.py @@ -0,0 +1,37 @@ +import os +import shutil +import argparse + +parser = argparse.ArgumentParser() +parser.description='please enter two parameters and ...' +parser.add_argument("-n", "--name", help="project name", type=str, default="phytium-a32") +parser.add_argument("-o", "--output", help="export path", type=str, default="./phytium-a32") +args = parser.parse_args() + +print('=== Exporting Phytium BSP for RT-Studio ====') + +board_src_path = os.path.abspath(r'../board') +librs_src_path = os.path.abspath(r'../libraries') + +board_dst_path = os.path.abspath(r'./board') +librs_dst_path = os.path.abspath(r'./libraries') + +print(' Copying BSP board from {} to {}'.format(board_src_path, board_dst_path)) +print(' Copying BSP libraries from {} to {}'.format(librs_src_path, librs_dst_path)) + +if os.path.exists(board_dst_path): + shutil.rmtree(board_dst_path) + +if os.path.exists(librs_dst_path): + shutil.rmtree(librs_dst_path) + +shutil.copytree(board_src_path, board_dst_path) +shutil.copytree(librs_src_path, librs_dst_path) + +os.system('scons --dist-ide --project-name={} --project-path={}'.format(args.name, args.output)) + +if os.path.exists(board_dst_path): + shutil.rmtree(board_dst_path) + +if os.path.exists(librs_dst_path): + shutil.rmtree(librs_dst_path) \ No newline at end of file diff --git a/bsp/phytium/aarch32/figures/chip_select.png b/bsp/phytium/aarch32/figures/chip_select.png new file mode 100644 index 0000000000000000000000000000000000000000..26878fe2355a9095c34b3a4305a861e157cf49a4 GIT binary patch literal 45417 zcmbTec{r4N{61beaU`5l5#e-NlqC@+dn;KdWZwx{r!aQrNH{7~BHNhCGNCcn8QWNk ztW(B57!rdqMwl6lnfX1cb3UKnAK%~gy?)Pi&E*+pp69*X@7L?T@7Md0blX&4@PPP% zJ$v>D8r-^JzGn}={GL7gvVZ3Vexvg-{v+^XZ;-kEwLNA1Cl-M>zj<9Xxw>ahMXJD# z>we%p|NUFGL3{QbJG1-RYhZqQbO*1nXWS1{hUJ+$Z(0-eWCr= z_v~Y}R3!-0L5%XWG~LZ=in9$KHR z?tbb%He;wGrJv&6#W-hm!*}_3WWg;FgYPy{wgZM=#G8pjlS7V^@0ZH9q0kD07%3gG zB*!63dMSx!-CVLAGE~Qm`t~Q#*q?{WRTut$&+k2LIsEVWXR%nI7e9~JO8bodJ-_1q zTgJcVe|P*Y@6pJ8gGlUj4hJ=^;!ifdfHDI%!}>R*Ve{0f>O?&5h*e;bYezI0Rl(X) z*&3f*!CW1Y!B)s6exh3f>Ih zD%t!Ezpi1N)?(h1hzLi*2s`Nrvg!5CD=v#4ts`O1;j442HJVx0rns$$O+U}3_k2#v zri8%RI{P}p;G4m~-!i@)c}a|)zRQ)f#yDU;jrx@5C*eVj=Muy z-3U@lj9g(|C9mUm&PAz&qeh?5qZ}u1r<1YvRGF&WVJ$W{&(!5Sl#Aj7R?<&nCS>V+JoNG_W|62`n0etg)-Nkr)z-Ou zZIGBn+lKl&zk;cfvQ{GXhbdoQ-!{gT4Ps>8X;#b)-_`!LIQsMS_8Cdz7>tFSkJmvZ zoz30%7I>rPJr@N+r_LXg+qf<_CV`*zh?7(T!Frmqy$7CM6Vc;6$Z*E{ktJr<$MDLY zpz;l?u)sg>4}i3|SFX%(tfISfXno(RdMj68>RM)ZVB;_T_~7!#pACXHg%HObu)@)^ z^!u5YCd<8fGafG)7}r^@2)pqZ9b(Ct)x&x+#%$8)`-PicY?W!E<0Au$x`O6eQ_DIU zYq&E!MrSC{LX7s+$t=@46FcFbefYa7U@5jRT^aPWs`Zt!YaC{GIW?@mY|6?>xF_D| zt+VP=6xu#Zsy{SE+KyZcPR3bpgQvr`i@j3&HRB#XJSSbhv%Y#4;}?L_y68OI%bTGm ze$a(ZX(i5d9cqnR4pby{Av{}hP;(PRXVa|P?Jf1%n|8(J=5E=sh077m_h$>INOG_2 zjbVQrw|P9Ko4$=5fGQ~2x|}D1(*xYMr& z<<)cwWJD4KN6BQ-_-5Ks;oGqfH_is_n5^$T1qlfC2;g|eN%%YWJ~FC&gF3GEhmEZA z&fPaBt0Zm2twS|Z9_^Wm(p=WwXlKZMV9x#0oO(a*L-Fy|1q^YS$lF`if-U#ri@|TaU zO4ZU$xm6i5ODuqUh{) zTt7tA0XQk8cPb~#_Sm+P~64^(be5ar@sNiCML zk#!E`~HzE$Bw0u#2# zdHv}hhIkE3mI<-_2=xt&o3jeg)z5(5Wf}B2kTlmKpJ0(bu+J>{k=j5B=d$>YG9yMq zPU*iL8M`#uVm>5MBkaMm2a?El3GKAh#p-|Fa~xI`TmtH&MXPH)H(kfm9-*nBdHu>e zc5mB#fj|fGhy&%MW=6Q2eenMScRr_KWYg{aV7Kpns5WCEyDL-0NQxgN{6P6c zn^#TPQ-P5s48Gzue@)+q^_R*fQi`R10n`icw?Bh7&nD0?AR8OCIX*2{nR08sk2u_2 zNzBgg#vJ`5SK4ar$<_dpB}cAmEf>>bo;3|FywZyz!nam%M=y}NwE7Be*9VNzqcxW& zLA63ji+mX|l0a&-+8hs!8IMf9V^uiU$pA27EKvh1k!St$ojq{6axOPo(Dr0BME@J( zPJnefF=Y5RsFy>Xq{aV4u7#EXHg-UwkM^ju`Fs9^_tbhasGd*&l1*)L^1RLH@Z?bL zRiZtYCjUXKdM*Yd*1nt5R2>y3!eu5tX&KDMH_#pdkzG>9!N{ig_)QNe{!*696*Jr5 zbYXcDPb(gyaI{a)rQ1cJD+w&=Uf$NVVFeb}IkA(;BBh7Xu7|b}aCqN?^2uW)+o5iS zO5JnHZg6)7n9fn~jcQg&iVgH0*&hulZVLUtjCn^N6Ik>TVek$8?1*2TB=~*@bHB*6X)2~mZxxb-!7>tZtlC4rNviP|Z~DuZF48oRyP zataBJ-gye8NVK-J&dx_^CWd=jR=$)KqjMzTd`$O%?Lm2zLk}h^v^{Oa>3}qjo5;p< z(d2eEzELW#iDeb`nuT57mO+m2o zH`4A!csBS5WMo8Ak*>|swu>Na=byoFJK&(UIAMDGp(?*Sw*H=o#xLW#Ia&D1em9pz zZRV5^_-@{+`FrVqwsugB`1Oqu|LHCT_m_6GzfSd_Z025D8w2ZNP(yziabpOWyFbMO zKTHR@MRBauHqGTHg}0|N6q}9-1r=%fRC4|-^7l$F(>yd6a^k-j9$CbCR9MF6bRuXr zYJrzbhCHqNvNKO~rM>Ab_3_*++KQBFv{>T}TPsNWiagx+__+s^J1y6l7b6Rc?7!<$ zJcko*J|@(e;0ENbM4D8%tT_qYo;;;-cbGb5&qGmLfUka&W3#P8a6!rPSU06Pt2zK{ zo^UxDqO62%@3VT;z5T4PJdQjioygaTvNsCRU5M0Q#)HxrX)$W9us;ZxONJxx%s^l7 z`v2B7_>Rl@2VB%f0>CuCLw3%EX1^E4I_qw%Oq&dYNP>yjl}GrP@ApRkglvo25p=gE zN@w!h(al+NIrB`8iq_@T$B08KjmLDf8hejlDto_z*HJH+E(Uq2mJc$r5D9#Fk#ZAZ zLFJ~6_nC03Ne+-Z;^M&L5!~y#!r-YsQx@g^?8kQ|)>g_7$UnrGwH(&R6vxWr9n5xEb54ASYiMDSswS9LO4S!dxvaj-l@) zZ<`HD<9j#771GSDM-OJvB5CXgwcL(c#-(uBA49+Z3;_dR)ZE!2LpWm9dL=NhPT7dhLVRx%>EN z_Q>_Osp_B0yY2Mzn}6G$Em!f728i#J$#*j$ z$b^t|cb4=!PY0L9Re&I`Kd4`b{Kc?6U$vO?SV_gGMo~Y8wk3lS12USo0xm~=4+xun zBR${GcVn{`+#Uy-lJbK~5_dsIP`tX<6 z=CaskLd=Hd&%tYv&+2L!&_~k|3%5^mJ9bL5a!P3j2kvl*H{j4XniZcF}bxC`da0eKuq;`i`%YB}?>a3C`0!2}@9-E>E6=C;~Z z$-V?L-Xl~3KLh_m6jq0y71&bwl6Yi)#`sFJh)`sYPQ60<_%-F|8=6!ap?@t+Ief>o zc?x%*iCT4WdRFQtZ9W8Kf3K#}ak+JI%5^)v71GIcBG|eR^UR;5--Nw=5=W z3to1X@(cF&D=lNrOhoQ7EU$o)vmir66N9khed^%EQ`08R@U~8`HKc#ebdjIhwE4Hy zEc?2ih?#!Pc(ioGcL&eBj9m?8eCw@^n&0j4$%N#_q$v>R=5#@a*=h>8@RU zJGsyF_NP-!OS(BDbTbAUzQ?B~lXTMb$OFXFMx@Z-)`jO+MI8m>C(RmK@ z`7pth@1ZQSSv>DVSc%z4PL~%u+->_=iB!~?b;nRA0p>U3G_URGTWTAZoG=I79)i+| z*E}QN?h3oBh1(g9=!$ycfGRw&bkWAVl{=e?*tN8P!}AWk#e9Sex$D%EHgTuXjF9$+ zRFndwT{I}NO7%@ z?_d0lpORy{d?A3fBZI?5GOeVQ#T!^R~UOZWe6L-_PRmnj*2~d?Q7O-CuEqNvu5F$_swY9Smoh>4k zc5~Y`D}7tvs#38SnFe8}AJXz400p2au}n@EGJuX*%!A-`%5x2_E2UHVZ&0jIP0$a$ z1WPxB9u;9cI{m41qbZZW=V>_G>Z$j}NkGkk5}eGA4jr>|8((~W@ly^Y5tkS<8ly1N z5>eglR;U{7CvD#6a&BP}*{w}|pQUs6{$$5_`8Z{&Op(IVovE)3ry8l#>mQ^=i-d2+pJ*tw z)P_!vYS~1`E8jO2lo#xk`KA9Bw#f;b2wFRVU*2P~in|U_aHy7Kvk*ASJ#2glq)r=a zCTy>SwhN5;i#^-Q>jo87!6IcGIBp@^DQ2#5h7>iw36uFmWe|zGMO}57vhZJP$`3sL zGCJAJy6;Wq1E^Oe^hsT@)FtppZL~&6_CpU6kYwI6nW1eX3{CJ0@fgA3nHp&z9O(f4 zi;vCNwbOzZifyTTZu{9Ul#H8J`Nu?EfmG@gG%g1kHXSZ&)j9235?h#w`J+yIXz8l+ z!~@r;x?|$j0iAdLb!gK3E3MlEaWSQ3)@?_(K7)8)g>Do@#OLHPrw+WL9ptlJyW50Z z6C7S9O$kL(k#z^V1tmVbCzd||7A;sHtFUDN1sGbTV6(|Aze%@%lFnX&Y5*byUKBPa z-NZ13HEDH)n!ND`=L;Vfy{Enw?q~mxA984)-t*{NkCvHtov&NH`_;;fZvT^kk+L*w zQi_1Za_IjkT^ajem&J7uFJx(|_5qM9yz1*cAZwN#V`w~j$uET4O*G^d7 zR9}G#YPH?%Gc4^n)-y34;;7T`mq4*2)&XWv7593u&EtClr<$18QTT0iQEq{hafE}| zt^<)l{66^09G<>gDrHm8MDn0J3KA);ivBSE$LzIP4L?DKtqbYh}FlN;Zor2qU{_R1lGTbBr zNoUHSaYuBr$FOqTk@Xplmy zH?@eqHLSv8WafX8g+oDOb^?*y&9G9)@e6o^rPjNh@rw-2PP6~Oi!ygv#5qR+4ozLx zcDHV`h!nRYH4vGcj!V)U$bme3_ue_8N3#E=*5IcPo zvBfuB&*)Xvz<6G(#M1t$DD5aU1xjX4?@w`h$K0>2#oTx@>;7c-F!o(|rH8hj;_4q2 zFr93{zmEV^HhFdWI{Q!S_h+}C#a`*TMSIjE95SEeEnr5i_IN8pd1^M#(gT9M-)f=F zZ1gR?#TN4$tRx=+=O~2}^TwfrcjRVyxeNNda!5WrR4F|Eoki*`_$gPflP__%qj5-6Cf2iFOvSGO;)GaV*de* z|8-Eu#B1p({BuwiiM8rGbKmdcIo)XsE5FICyOV|^0BhLo3s%fai8@=NDe~TaY}+b4FLU}zFUMes+#*+~SG#Vvx!MwH1)}QQ z1vGi_2$)nWqC@nF3}3hM5l~1cdK=BXHvH?YaMp+Jc3t7r<^kEn*gu_pv(SxF3AP@Sg!Z02bsaQ8#TQE^|9WwI)V}&EfrDq)|wIFVcvycoCEr8FTUJ zT9(8NCsqb|B(Z$DfN<<5*V>bPvivtMJBGno+9O;eQi|F<1#|13Yyb=ZM0@dTgk6RAsKG+qqPQSaq zY?r{cCkDVHcmBV(HAeie;$J3EyK;(H z{I3G|_ul_=1@QkQM*nZs?%%xZj~%btKf;LLlRDRt>gjpxkj*xvVxsU)0E|UcYkIE9 zW-7u|cdR1EajTKst;GrN%Z5K>s!Z&UHs61r7ryYkb&pX;b3J*coUz#1mXFs#XwnQ+ zP%Q)Q!rJ1)92do%Ql~9yyl~XomsQbJ>3LI90YaN|&t?fV^6X*D0GaopNoqo8V3B%X z8H`DsQEXZr8rH#`2;p9UWovoiCV&zDJMCw&^s-g{KBMT!8Ie>{FPvLYtXUq+UK(kK z2)-OL>tvrs;wRIZo&594Us6PH$`Pk`ZpOE~_*wS*ufg5>GwgI2HY+`ExsNU>O^nc&t-DiYgt+aSqhe~xla@Z-Vg;)8R+s6(@IcR%sEf$+;~9bm@fV zoi^(>zDuoe7!-eMhROMsGqXg8iREG_OYN!gsnKgQ<%2Q)R-KTY6{pQEb%KoFRp-{TNJ$3!jC5HPv}c>c zQRgh{!U-u@HK-4w8!YA&fvI65VhXHdn!h)sJ_2j)#I=Si?@G0?iz9zO9OKKI*<(>bq2T|ph~bK#QXd;?p7+9z~Wk9-YZ4hkm-+C^^Ocgc%=-Q7n^03XyE zdPm9oR>jfMWv#fo`ri#Ja(~e>kU!?X^$iq+?6RnvP`;Wj9JM4jgXJxv`X&N(fQ{?A zvmQNvo(;Pc6KSr>n_)N{XGm9QaB_!Y3w<@_)WWsj$)cTimx*aYYSgz=GbAnxhVX?z zXrG{az&{L}l%Xpw#VvfC*`d}g&PvV<7H~zE0yo#_oG6cdr$uR75w+R2R=Nk*(pQu4 zR6*06F9JRe&EheNFAya@OVYJC#z^cS;~8ycyypyZ%SZ*n^dcx@!iRi=Q^aUGp^#D!zN#_}gya zy082VPI;xIQ(i3^!wQN+zl+!)WZ`LBhMP}}Z_lw{?1dJBbBr%J5r>yzfp=$cgSra$ z;&Wy**ytEldHVeM10}(e>qD!r@H!;U@)y7Ra|9THLmcA16s*!Zp+g^G&&f1B;}&8kB?9L>6Z29DT8f% zjGVCaD6c%Atk5K?6H32lLuP3TGs&#M&xEW4F9m*T4H2QOHt7wdb7ZabZw4OCXrKzy zMN5*!#=bW6Wi^xd7YjP}`|9D=AO(k8bDqZ*_ImO3NUr0cLvar}(uuWrfpnW!v+sTm zaN2L_KNn(_itOjLUaYi47}Qd8&xRiZYUM1L>RCiPerUXwGuh1Vyty&A8aq|`AwiJ$ zLgm$TlDN>vZwq`NUT6JRC35A1!tjGl3GZ+F)g|+{JQXN02E5^c$TJh#ui96bEObmy z)8Q48WB+fRe*u*8Ah++&_{GS*Qh$WZi&Y)_?V7lVL><6jMgo3*4_Ap*kNNB4 z)pVfz6?1=9Zbq>I{HPnaT{WwV^MmT2!%Un#SYo^z?bfWf33HptV4*&VI&CGELJ>fN zj@f7w{V;)ZwSOdwmR@nudt0Uq`khHUYv!LFYdgVSg5bIXY%`Ds;iAFOryQa&T$Q{V zj#tw;Osx}5tCMf=2zJhDMQ>UNV2KF&xkuXS_qf^^*O`dl$qI2<6p*GES8a5mAJrI- zUGR@O6SXuWeFE@*BDubn~e$hsPjcYUklI~jgo5TMW zLM})C0*&e)_l_^_hJ=1C-G=Pw!~MTYN504AmU4@GDRqa__FI-YH|6r@KUFhxDK1=A zE9^-+kL}oJG&cA2c>sR1zDhKPV!@jE;-&ef07J2Er_Gsa`c+2!VNWvWdGq#^lwfKh zLGLB;!4F|Gz5H{fcv{u947AT!q>@f#(lj+wp9=B*T*#hIq4ZGF!Dcwfk?6FEN-f-* z*7Z;}sd@7*EN^dfq?|Z*i)=2Vdc? zZ)aB<&Z+gp7G~({zT$n_u{k&tJEx5ykaR_Zm|#KrpKqHy9JvfoyNu4(YD>&grdIhN z=NC1+&-nevZlC*0cM&f)h}^A2eVt~F)gy0=KkdeZzl%kFME~i*McpAXu;L)|#_N-v zZF~dXI$M4Dh+QszQS~SD%GE$D?aydB5fe#f@gUgCERqmn+lITI8cm;XAp;-WWBlqT zfkXB)rRz^{EYDWox$95nt6TqZnV)NNPp+XZs{z^Oywe3Fd@ewW^v)J+pt;k}S0iC- z$*ZGPcyj-ba23q|?>;4<%8&&Z-#}Vzf1=nl$^p@UEp9)L>2ItJq^>0AWAmoyOe$v~ zH6b$hIBGNXyLG^P)DI6+ z^$(l3RzxFmTU`*jts{L0pCvgGlF{zq>kQAa|NRwX9eHv}&m2Qm3)JQ5&+sK0QQc=R zc6N`wZk`{N^h=zpE0F3>OlIe75w85kmPi{VIov}i_AC6ZV6r};4*2&PnA{p_7QeNc z#$4@#Gu-t9PuboB@>unef7CCOh2t@{MBPgdCdwY1*vC)L7NH)@%Q@cgyL@^jb{PFZ zz&?M3`#wlHB5+xrNudugYJmLQ-n!yxUkno+A-9O`~I}Rl)DIdgxbReHHcLXZ(6)p!b`GWPX=3iQ|}X^%~_Q)~QN86Kh@3 z`zF$pzt55%j3^a_ubd#5%1GMxk24BVYMRTwzYQa}b29^jgu)_JqTi#QPzN3+oLYMUAEgq?oLsgOcQIFv5`3UKi_rbclqQiI}oK7p*v!Z`|^bD{!?lOqk zW`})It?=~tCj5b^9yle(TCM%-);G~AUst9bN2Sv>gK|3^8Wi`B4KA%>ecjd7_2kHS zKy=(OcF4MCMK3;3`?GUUZiuy}=qpk7IX9mT$TsL~8s<$QHEj$$bjT)qzAi|TTJ56? zDkTQJXVZ^B-Wf+WxHWNYda~_QfAn~;YCd?W)uHwoE+qJkgquMxI$<(yCmft_i<{S0 zO>A(jVSkwVaCo9PShRt7FXG zIbf(TA52()KUG!9L=356o9hk_hnSKDcWmX?tD<&>ozr~kNe2Cw##33^^8y`6I55W@ zj{xACH(XGNr;3ElyiaKvP#OAS>Q#)$xf_`R)%-2v@|$d(Ve4YeCEFkk`k&;>(f*2l z@)=RvLNa<&^bbyAi*Bu@QIT(o^3f-C`$kA^CyYpEMxWcG3BKQqGm> zn7J6>qzas*QyU{D!}AFr?<<85;0HdZJr%~Zxs~=F6V;5~UR7VtDjxB=SpKMGz-Ey$ zJ4SfF(Ko}b?9af2wtF64u^BtQsr6jK#vGk`8=Yf)2d&9+)AsW_9sBiTEMGxz8xl3U z6@pym?RUkS;{Z0ZTdr8d2?eMwOEn!43bocuE|?bOU)-yLS{Pwmh#&eW0CzS;AcW;4 z8ftpe#))<%LE})dXkRElKD-;kHcV!@+kQF&_Cr&(|lm(#zAT zp2kh~Vz3&M0s?Zb?&PKCi{o)=!z#Y&NlSz2Azmc$N95pp)}d-`-EC2qNlvEq@8ip> z&F~*b7zS>j|E4uv>G=jTUA~#$UaINpeg+j}pgnpp@>C35tA^_B zjr~G5ZK$oDuW78NWj}b2^n8>~J0PA-;F{9@NN;UP*i@$M zB}{G?RlUe}FEuVH=cvno3TDzp%OjCgc;>GH@OT*`Th?D~fj`5|-tMF^|-vXFO4pK5?H&Xi)JoI?9e zowr>w>8*^sYIrnqnWH}Hw1W9ESDr>ZhHVdIM&TCdDNFP&^awn<;!s(2x5^m9?^CsQ znRoj4nt*WWI<)Rw#H16Jzl`6qE+A{Swo;s5tn7dC%ZIINy%kE1T(iez(Y+BlA~xc0 zm>RW10XLTfS5l$fADr*K^V3+aF8)zg{TWEkn->?FfI9Gk9af9Bc zs3FSv!Q!mXe`DUh?U>R$q!8xCFa`N*gu1G~_YX+uqYp7y;zXFe)Ur!N>g7IzJJ?A- z$v?*V82Qa7#+*#Sr|o!%A$1_8D;iosy6R}Nv|NQUmMpE* z9)y>e zEZx`j3z2r#!PXvUAn(V?1EBWSRM5PbVrN=}H|2ZQrB(=i19E|vOQ}52GP&buYAhXC z&}{l@sA2Q+=BEw9c&b_r*vpBBZTeSqpzjoGDfOK)gO>e{hI(g+OYztB)|@m{Tut^t zK4%k8lf{%aASnC>M5cSi$2=jc;sgI7NgJPqiLG0j+5^Wrob%8B1ayM$UV9v8df{>% zs!iR-)`J0lyTJg&2fsR>A8_|n(Jqar4NgNx8+I{W@=m9qdo&Q(40?x&A5i^T6uflx zfs42A$*rjS&MLNO_wM>7Ev3%7kUC3Tf^-l(>Y4{y=IlU7s8Wy?(WrxgYVJ$jzRG%u zkCPzkCRL7nJn&LIAavY;zWyTf!7U5_GH;J1XfwixTD2m4E`jGnd~D$jo{`X!6+|%U z>9(H^M61LJksUEPCW`t#Z)Dx3r|iFdy@TC%zoJHgN_pfRP9BOsKTKiNe{>LeTpN(} z&MwrAa?dIY^LcO=$qzzNy6o4SZ3Ws1s<7#`T|A$}J%2`BCUoaA@AXw*>+d_G`Cy^K z4;Q>VE%MZD7tD9_w&?jXf;iqhs|=O9s%2}ZX-cL=4PhG(-dcWBWpI-2l^!Ao=dmX7 z`cega-5#ZEDg4`Mn=gvP!uB$>n?1!ANW!w%l)nAsn);euYpLeUK!E8BrMios*)^Z9 zz$xihIb932$k@W};C@ZH$vI)f;Kq_yze!FBG0kl0?=O~hE5<--6da6uQysLe-A;d< zL^XH?&mHuap(1Of({#Rm{rCjO<1aqko44n~dwXO1ckZ8+o(S2q2cja0{fX(+oqB{} zs(|H%41Kz$BOxFNm>*ZoQuy52IAWu-bH`)#3xi)wdtQbQ1_oeEgX`TNH9Frv;WR0z z8M!4>Zn<>TqLYw0_hI8?9GgDvg;O$~4~r0IeJNrr8YrV;WQLl~J%2iD7!_Jr0kf8j zh8(w~K>b+7KsnNo33>hlhSw=YFwH{)NDwpTGxXXA-i(Cwth4hI;pOlQO~?tWdPGm= zM{04Uq!QaSqCF&F>z;1j#`i8S%l_7G*(ygfckagec`El#Z~e!o*TpI5Q)Sj4{qUz* z)sHjPL@!~CEsHh733)ZXA50bpTO>jpwKF)$LuAAl{YLxDb^&y{8ntfI@?m*}src@&gJHJO#y3~Yq zzC(fCfWCHgjU^jWX?~EadKDFiRGgE94N)vC(N%L3;TLa`ZHQa6l?n|7+9C2;!n+Y~ z?cxZNbDn<3EkfOv7Rr_V-xROMyA}?ku4r`h`<{|1?Bka|y2}pkm&3|yK2I5hi&D5{6gS=`UA{?7lbt-TKMXgxqwaF2j8D)XTrY@=AVdZ_oIR?o_2**; zY$oP*=WF+2he<{m{7{E`GUZk07n_XsgLvJx;ojJ~On3J@_LO4DF*}Og z*QC>D?&w??95n6B2>%us(R?BK-YLs%Y*7kQsV8aix?T-})3lJv2X^1h)!N=gUuf0z zrQU_6zYbkufCq<|DM!rE`Qu=tp^$9LLf8}a&VSy(2+iSkvTb0ANE-aPmu zzcF%iq*NAye*{3O#Q#EwACUS;2}tcHBmd+K6!YnKwt>)8hLh2ys8c1y5t0_e&}D=& zRCPYnk_lZiD{%rFJC5B7h`^4IZ-yPW0$I8S6&c}Q0@Zo(+qFt&dpEFMp=N|)kV2oN-eVFcZ{O1FeU@+|p~xBy zw#Eg#3bIR?3|fviBEm;nQBXwp%e=_c@F4R^x&}(dvW)*`vV!}PsnMK9cD}K_SRrOT z{zL_3SU@Y}x(5#e@mfu${LX98f~}a(T@)Pqd8$qyyf5Nt<@8shDMeOEzlGi%ZKAGLVK3|_A{KV`MPCdbWySZdejGTIj0WNB)?K}; z!ot&*g0m@gh;*t7-;w2F%652=RS4JDfIBL9v3{~JaaM8-^|dNTW-1xv+jXJI_P9Fs#5R#64#)wAFjC3c`y z@2IYs7s>u~kyB?iu$?%>j#1?rKTCNavw3up)bXD9iTb5k(jGC|U}IY@?0ILLl&(~Q z5F@_2k*C>N9q($nT{kEn03M_Mvltoy{#~wqL3a>4fEo;NR=wR;rfM?f+kUq) z8OCW{D+ujLnbPb(S{CZU{h8ey@2qe2HYGdt>AH$ZyLXt+Hm`9)vhOg$28D_}w^`L_ z*&v6KDkpHndz!A9_bI<>#C>Jbc*0IZsS~0sV)6pnSRE5*NcHb|^l%`Ec9Z$MOzQ4Y|ia_(R^&VrEEB zGe=S09%GT9l@s>ps%~HtjqOBUCGik}_7uf;J=z}L;cxBtBRqjWl-Wa?9?y;4fx+Je z;!2hn#`WORcjNQj%Pcdn9@?BV;EiAVfvDEk-?|DF=51Y39Z4r;{v_}v~< zkVtfy=v47xn_W0n!ps-tDh{tpd}OW-Qc^6;&;+csM|TC&3R&07b8fBdL<#QuiU z_6T9ioy-Ha&^+_xW@2j}R5);P8$(_UAb#va^>;ZimHfRE&FW7cSwNGn(k2u0ryh(F zIfoNQP|R~5dP}JqJ$MqtHjz zhj=Cb15q~T)K$^8Z6{94$imNwNGd3|V`U$jJ&8mP_`q@v6w=+Vk*;+f=)j z2ZNN%%9Kf6k0qZ$CAqJoVx>#;4|_$P5{shQ3P1)EAskBpKyvm#$jjSaIXM_0bt%Nn z1oljg825w>atsF@(Uei$#R{e8(S4H_=@Flk%0|LRZ0aGs$HVsAoC;Q7DjR`EyeQ^5 zQ^ucOY~SwQGa6KtIonpQOd$fgGEbS3k%RD|m$uD;=#|(pqf5 z>f=H$M?W;*-u$w$XwbzC#h`*Jhxr?V&AD))w>8=#oP92}SN)+9y<5Hf?!%+{&B@9; z_edL-_u5nUNX>Q++ks*=>K%GYfai}U1Sr%wtf|k1xtG_kk|)%5wy_(NMK@^zlIBG` z69$Z#i9pJ4eyZ|L3qo(_FFcA*Pc9EqMK+!t%gX=)4X|a~90)W+5uXPc!I(GWuTOW*sxy!|W+`lZP)ow_?)U;%w_O`XAr1kt4|1u&o zV=+I$a*_0SFj`AN*+*R|`qL){VIX`Xkb8}3yQH5pj;y*@!tTF{?SJ|%v^cW@_BN#{ zG&eIsk`Qy)lIU$U$3YeSfXM|m(a#XCDsfK$`Y%2_v%AUt3cKWIE~d=m7#6u4@?k@B zF%|bX??&{c=sTPvDLJzkIgGT4i7!{iZ3NZ-;1-Gv{YEb-AzoV4LA~|Kc_L|FR5FO2 z@y37s8CqWQ7^#i`5Ghjzl9YH*?_+f})3@CKL>4bM-D4e4fwKMWSY9tEZpCX9D^BBS z&lAc5{sDGBsI$0KXDDY%Jyk)(CR-1e7tCEoq75CL1WKtg1xK0>o8m5nd|iT0sb**` z2A}Fs0{Ak3l*LdZK(+msl{zr-3H_pLFTIh)QIi}!!djcCL= z+`^E*>ear`St)ynqxUCWeR0LqB0GQJ)6Ic!nMGssbUwl&sgkz0&qs1W4`si7Zv2~4 z=B|BTQE5?@FGk#3BwAP+?3oq-Zn%&UCPRA!?C4*F`W8C~i;w4JUe#Z&x4aOS(XXXG z(^a5oyWsTuxh1aMi#uEKR?=ZH4-}S7zkkrsd(pe1B^438Pkr9zu*GmOz?%*Dd~=?_ z*+1NYG=Wrgg7Nj02XBf(G%&Pe2UCrhEc?>D`{;q@WOMTO;FoWA1=%Sg*w^`>^MVeQ zy-*J5F?sK|BwNLC@b$MH90h3ZxzwZovFD?N<{@axq3hh?x3C<8Md3I7>0(~J1DTB# zE#|#-K}G<3wDM#p_I+g-uQwK0!`3qtnBQ@d{+8Q|4%WO*6U%d+%&uOjrYX z+|@@)?w;B zy$*=S@A86_Lh>5yO^#vQ$ua%Djys>a*EhtO4mKxN9E6#Xl0z+;V1lU!w1yqi8{gYy z$NHWmYTafy@W*3um;H5uvV_)e@MjoU*5~czoo=fDazq#u5zRzZ=>fhRqaO!*L) zLE0s1P2AUd?2sePOjN0-B&L$U{}L!QX;-d9<`_-tcVmtWKJSl=v`3)J;%f?W$;vbh z$^2ll!(0n!hEcFM9^$3xnI$p=|J`y}VZNXwiJEjg__W)wLeaqi4fW5~cegQ(;Smu$ zbt%iGhrK#nDPjcNl~*r4rc{(VUu0IA0{QEv@UT!wee*@{p~NAjxXoKio$(su-dU|) zgMoIPANm#5?c5(F=hJnwyu1{-m#qt>RG?bPwtyX_T*3~H*{=m!rF+ z1|}b8VFw?)bpYa|Br)ay@f!LdFA%lTIVfb?FL(8{q2kL__JiK1$z2ix@`ZHF*PFAM zn2Yaww}o>)QnQxA4QOEzO7A^l_#;Mf#TP-kq(vt3{)BuL3;?tvU|ZN~?RruvjZR0a zw9}Reva;B^PZb^|hLc?=7IH@X`$4D3rNOxKd>M9cx8we)EX&tDM_>Yv0|Vf*ds+K0 z_^jf{tpI69eE4nTqq)bh0=#G=+~C{gf?~B-L*d!r&Ouo9rJ`MqJ++NV)eoWo_H;_% zU6|@^bbg90ZgzTFB%J{wAm^%6(CtjdAcbIoCN!0Pr~qMwa|3a}Qot&HE=S|;)JoMh82d|bxUYwmlq79vclwQ+HlUfTjVx{s|yNxlfs zC)xM*g9es7M1nWpQ9j9}f1pp!$9=0w$El4u=`mSpOm2X`l;g|pTm zX-@pAst`n$NHrX$LBuHqMUAE6CvozVBA@I%2Ae zqcsTPOQGgC>)Wrwk|DjL%_$;t=CZ?-QHDhgTM-9QLfQV$ZGYtld_J{=w3 zDq;(Z`r+>5*Lv&w))*VcTaDjb8dyey84OHsXn)uIF}1ez(n(HP!mMYSJGZ~_P-oq7 z0E{KgDRoXqRm-yPBCP&^YWcHqOQV%CUmL#B^fFX)qZeABSmq%>CSa>s|gj*q!c$L%CR7)!>U5w@A^FcANJlmtjTTb7iC#c z1Z=1%MQng{=^d<~AVrYgK|rL0-XXduic*%+OF)HyNJ;1&q<0WP???$DGzkzOB)K!V zT&{hdz3+4HdA@V@ckbQ!gNHZCn`F*0N10>%=5Nq%q>7FS@TPvEEW{NF(tAR~RPGB4 z70vx@sGu6tL%h)hPjVl2WrMTIIKxjT!~&9WqNDO`<_qs}F7(41gY^XUjv=A+m4?2a zxF7k?awDZd`}cfwnbW=wy<&`@?s$Led!AQ{HT?414aQ)>vqFormzXZdB*B z056BP<-==vQyR@NTXh)bgGO+TT8jUQ)14H&vS?K z9f``8_VTk`Of%Vc!7%Ty01=qg_G9Il?@q*RF}T=e8}xwq%?7Gn=yyRS>4&u4~o%Hu_pThXg7#nD%R(iv>H1Ex9kN1Cvy*#JT-z_Xs z?fHNIa+=e4l{KE-|AVS%YsKABP9v{~k9K8V_9d|$*;m)7sT{R8n)NLFS=$%h^lN@& zIavOV^T9R7F0^+2N@)fU%q(MhY`BPO>Y9+hMFI=6Bu{VR0xinl?VQN{s^DP`WJ7C= zv5c%Z;U`Di5sw^|&=^c4*3#M-P9Y|*5aeV!0@CE_Y%gf%G@bS7jtao&NA=Ig* zZrok^=3Vv09v>1dK@&JL0aZwELBkfoVN)R)W3HR^kyDnw~xwQ*Q zh>cy)^q^P+Lwe;827R8%wLmE~u(wcVZNQ(t^}=Rzm3Xeyqa zV>b^~s7AT3Ea_9-Xb+u~I36BGMOBpe0{}E?2A`d*?EZ<=c^;|u_jAlTbw6MDAI~NK zKZSztrp=zNDal25iCZ6(B zB~I+n-GX^XYq-%xMsd80y!>%cXf5uUfH0S(I306~h)0X%&O#^tP({v|>l#l2(Pid1 z&fS3SX1l}q@<;%#BQ#lcVIWqZxTLDfwAraJCEubt(pg4U#bW5eqMM#dA68F{VY58b z-&JAP_SxeZIk&C`-Nn(6Gs4A%Pyi`cGR+JPSVqbWvd(rI9i^qEwa1|i$Tfv9d9{Pb z-Now{dwqvQ7LvGN46iRgsM6D?PaNgH(Vw|+mls=Tg4Ck++ieJk%{?QK9!HOo;H2+m z2GlOJwr1!{)*~}QsTQyEbM0IQ{2q@hrEuTC+{>$FcEXzNn9EN3=-cse%;lXgw))_o zvgQqQv-G)|&D-%3*IHcI8AO|7ucw!VSHST~xxIkfj+$W#?KNRm+{iQd+4!x>Q`nU7 z6fDbCHbnxL^nBv1=g6!?-r{-p7!?hKg-WV%cYgeN&SSiDO5KcIFasKCNs9~-*A!j( zteIUVe5MOWlg6ZKT3jBSc#taT@d0J_rQEKfBOW+x!6xnZa)+ZDh%TCY=y`4LdWY%T z8OMt{0bWP1+{^#>%{r-82A2FeSYEMDxxf&2fSjLsb0<%t*>_*gPXz|wI9hG^N)iXR z>gF}@4+LD=&3qJg$Msk>qxzqi+Qf+^=ha-`=Wz3}9E6_K|C{v4o^W7NjD`ChC|1st> zV9c)6uat#d3WP1cN+x=5T5f-vEviE14bk)SInetz-S@$^=Xm?$S|)wWU>B+!e|S3u zyq6_c!1s+1CsqBjvqeEyq{lBcjy?1L<72B-LR`P#J-q|Us7FP{t2vyT-u&6CbKR-N zI<7dSp{L|sSAJfHGA(b-Uj^o)NTJE%Rdat3q__f^=$dN{2+a%LCG z*PIu_yU3wmuw8m zm(3xC#{|R;6&VVo(yuF^IU|T?z;)V7-o*`MCY?lrGcqA?g?n zf;6+4B<|2C?v1S(R%z#t;4c~(V{KrF*gTIB^To}yLowkWa+AM*(dao}Tp%z|-=xs1 zcA~m^+mwffyJ)1z-8zwa5i1yK77<4E!1B@H0fk4C&&-l;$hw8{EwfQgr2$%~4~taa zP4ODKMwKV$M{w*2Y6BW(X&sjg6~oa<8UtG%GqY-+^?ql6f39p}CU2&@s~>%6?$nqw zmC<8D+P}(R4kKLEee4AajL_)%HihrD@}FaFeOv5O(8W}_Uxh&~^V)B)5um4C+se1U zJ(*~&dvG1wgzug<)=gRE#qKGQ=o=o?N#%{YSW3qW@U0f+oa=BwGH6$f-l$>vK626W z#>gQ*^JfrSzHAnZ4;F#49L3abIa?=`B{`~mGnMA`5cr(4vc-m&%sO(%SwN{z-64#p z=bVBb1fq7UMy3-~tQO-wDzU<5bu|Xkz$sM=?av*J#qK-q*yZVd7&7nOmQ#);UvAKH z)+X_ahxyyha+g$2@>yotMK^X}ou#p-FX$$W49Ua%>x8cRjCHLv*KWDUbs+45tEeHB zgXX`3lY1sb1wA7f!-F?Zv`eOI>vfrui&+P}aqI+Zi&>tG(RRJI&>j8FbRDun=w*oE zO65na2?=4V=>vDmfFRB-EkqfN_|MnXwZV}-Zg*28uGwp%j`6yUbOWRjYr$DosKOS^ zqO0d+n3ULYW-l3g&!qEUUs@QH-q44A0IXO>+m==yT1YRtOH^1Xf`O;F?D0c+eRzag zn6Mmw{m_eU6r3+ml{D60SxUqy$4aHJ zEfw!Zre$}(Y%JQ#)9Tm^;3l}flS<%5oznoo3?~L-+Vc7nIffDZKcc0N#0W*KB z53+oWL4g!+b42$tuqUQV&e7l z-uS%D*2x$b>KzQtO`qfqAHu;!7hF!{UgEe7qDa+BsC|V_BeK?>43WLVDJ{r(nMiA_ zQy(1`)Fs8vDHZ?x&IFG;ckHe9EoW~hd4sNT=kqSkBDPYNM!rtYwjVUl4Mzs)31HLl z-4vt@Z9FZuk#nYscI>Rc*ggC1T2)<4j6J#`M|jc~4+&xnK7p^d!fQQud6Q^HzG0of z()K=(X18JXo4U8z;@DWwitG5YS_fjly&(0OvDr2eZdWlw4vVXvMtx4@>#=zKN{Y#cH`Dwb*qjOSWPyd(CFm(@2N&Sr#M!1X$V5Jwmiscyo1>)Y$U#LUeri8Xh;3jCw59zn8*_Q(38V|aj(ocZrt&Hloks}PX2Nz5e)q*M zZ;6r6q|wE&o$!Gd#iuM<+@YxC6_Y?{nIJ6VgzN5>b8Yx+KV(_xSfDXphkV_$NQ8P|V_1PnNWgS%Ij6N|O~#h|PP`d3H14GeM*{+$!3J zHY84Jq%Z_=IO;<+{ay|_ZJ^|H^E+4RQS3mx`&gM?#u&G;OPr~sRufp3nv`Yf0G6fH z!8+&sgX6@oovld)mD*`Nm_ zCZ1x9W-qvyrH6i+xsAv^0!aegk%s0~pQG+Y+azkk`DUfYPK?@A@jP-seM-%#JbI=Zau*(>dvoOUuLya zu9?%>xREM2CuRd356rh1vXfke7~2s&tV4SN`lSe2SgB}-5@|x($UgWzY_!#*IK01A zclul7K)Kt&g8+s(f_lpky^)N$o0Lc&dZ*3U z<>@oYJF3c*mEjK}olur98OH$;qE9&hzT#~|MLr3M&-(}+FK0}8tvc0CC#4CD5b$?f zLFqZE0gF{dUF)$P}kVD(66iQWJgHkFYRQmlHrxB2{u6C2iL*@jO8U_P}_TDMTe7GlgS&K(le@k&BLq2W1b)S9dIRT7aDj*IWMl{x);M2 z8c6fTx;&+|x~e;@eiBDC=YR*Ynp8T54`^(_Nk7b~J z2P$i?)r4GjR&CECJcd(nDj8cjZg(=qYcde++#2}vVmX{urZ*wke|Xh(s(UGhnO()( zTx|||FyelPL)Q8=yxKD9%dYmITl?dlvf)oo_j}?XQ8xT&k&PqZOyMQu z%^QO9wo?Dd+B;A=6?jF|{cAih1HBcJ8c3CTHN97S0kg3eml^87*0p#3yRmNeos4Wk z1WaXvtu~Q-enyWUo4(aO;k{`hya|~)%k01=I2u{q1mPpRn!kS}$4_U2N6_*{e7>Lg zN@x9L%k&G@y)(DTIxWm*sl=@Goq^(L9x`|HOKy}Hdph3}x83P$Pm!$d_^j1>oBp1= z@q+gNXv>>7QO$Rsppj(zyuzjpcItu~8*cm9&YvCGFtdBlltf?mSA>38= zoY3?iAJ)S!)Hki5H~=ljh5CV~-(1pZG9gBs$m)Kt@qGIyW&NI=X0O{NF~r}f9Node zl^f1fll>KMk)=#xksid0c^6|UI5A!U4fR<%bt_x&S}*O28oKjL$n?*2&n;AMdyhUw z?wJ81r~aG`zYbP3I<{^R4ns~(9j-2SZSz)NzxEp5uWW|DL}rvKFdk4@N}0G9g5!ZF zUyA&s?~wT!KtD1z4INc4+Kzj9*Y~_(YcmzHBs2)2!hGtCE<_fh9`EX|HtZ?vaGGmL z$~}%R<^s94EF%-n?ZZQbi%-pGznAh9hl6ypRus)kIs}x}{_>-A!=`A+E^AgrCs9LO z)TU~sQsJsgYQ~k#@^Z@F5FQ=m(Bk1R)h`Ch@!UVfHx}_zd_#T-)u$b7w$DDU0^}-k zXZhSEfdq$0u;eGL}z_l>A$3wD)N99;NFkGFUd9$#Zk)Y=I>^3so8Cf4494AB4qdjux&gpfI-Pm==vBN$ zYvMU!b}?W|Zpu1DcLuFYb_?VNZ+3PftILPzgHKS8bLgq|6E*Slj(|4JJY3$buuRAg ziv;+5U-Gp!-qmXxTCin_;Z3P>wT|mP{9wUFMh}WCs{Yg>l)5)vB4f*f9J}jO^adb9 zc8f3rbw|x3i^u+;3o}!@`#df1(Ws0{q#@ zW(6RbL#1#^m)mTNN>Fg?dzM#kqFQUvlCmW3Y?69fTT(-s)TsJ$9;WJ+ZSDHYd(pK= zb&3bdYo19ab6#$EEqO~S#8Nhg7`c~_iQk^fsU1YnW$8Plh62dl3=695wdtaB&BZ+ zSU8s&zs5{UX$=Zy0XVHrHLSV8gf5Y=lOldO-54C)DlDl|auft1=6gn8OvGve3!ypc zR)>?%tUs?8$~=8zYjmMJ{`GQ}`#a`!kW}$=5#>!soK6A<$h5`ky82T$WhpA6hR(FR zBmPR*AszAjx*h_Yo_Qlla-EM~>bDY{Dg7Z14!MEu`zCHoi*EnSo}o0J(HRK6>PvLE zdmhSUF{CQny1~YN;2MZH_I1Imw|G1X<;Zu_&$hT)3%KCq#4whZg)A2+B4EjY%Y!5K z@-N`0o9IuD?&k)^8^avg{TjYYV4jN*Ru;1dTJj>CwdXMi1%;eD>$GvSIWNdNjYUXlIJ1-1Mw>S(7gs9Ff7ffIm^%eTED8U9Y zFaIQnHV(%$y`o6c#@;8$Wy4IX$v2auDI^cbGIk*=!I68>?TJbBCvSdXOqtb3-*K+V z`H#Cjubi_|YaH5!1nq47Mw=(*V5upVMx!+Ct*(ddz4Z5G%Uf{w`86v#<_a}tN&bgl zixb^@5r;oP_~-{p+M3@LF7fP(cQ$M%L0>f-)yGCfm5~Un+|?tmE-af27N^VB_1WdQH@m5+U^5%}MI;gMB+o9;_>P)wYpf z)!XI{;g12(!}ds{n|W5nL=fY+H>0G~e7ffW1MSs7L|tPm2s=Wyy00C_*Lf3S<6!n3 zi5%%;AB(M_1%&q8GGwV!VK}>GRY9yKHK=u(L>7-sEXL8gdqv%1hZ7nm4kr&3)4e<` zMEtgQ2oa#%ScoDWhS!>0UWKIBY%@bIn=Rc72?bmI>@LKhlzH;5#)FXJKNcrX>K%=3 zlew2sE0u6vH2qa}svz-}7GUm;`BZ!(?O_!^@1${ACujyR@^Ap_aOq?3dBv*S*|o)2 zB+{A^WIPyuOVfh-#cDa zETubp^nKBIZDh&Nvg8n^y1hDQa$>$>^^&}^nZ%Bz+3J=0DgRGO7or}+)hc`Gi>`Uk zeH946@rW8_1G=|JlYF-fw$i74(JK{YbG0OI4?f~V_$&=$t1B8^02O$eb+(N!3)c{v z+y4!-DDs&vZA*NYajd*gsujGoaRrp!ivDVfaFN zBVBka;RXByQOfjbjlvRYqZG!pkg7dU2VYJio-0V&n#@{G30R)~NBhw`=b zAGs|ZEp2m%yV1rMuDx^C)u)ejZ*2C4V`SvOk)L@v-N`IV#W8@4t{iz@TfnXU3450)#BhS{O-}i)0C^Czj8Zj$t_QFV zZh1)A^Hq?QS)iYjQ=3_sBP^42HTLqVy$zoB+GGx9+N88^JeII(9KeDj9H&Mjv{~66 z_L{K8UbPrXTD$jF)!?EXCyHV=RML%Sm+>F%UzPfwEKp07;73N_xJynJ#>mUmi)%k0H zQ1`LBC!7$sUe=FH#3lPk9FH`XNhTA5S$@Iz<*QTHwr@RYuG5b|~2Hz;c)>T4CD??6Ul!Byc!{3;B$?{b+XqRft zXs#Sb*jZNl2xkkJ*sTiJ9#6klMg1K00F-*)gKQ4S=^W#~cp$98o!t(S8w?Lx(Htr7 z>gN0{JqPKdzE>{MYub$TPI(w9=VcXVdIa^+_}~}JY{Ee>WGfD&D`9KdU6*U;A4nvA zuwd*Lf0<^Y9rynMfBwf-L;sc4<^AvY|Kk@}SshR=flNBQjD82j$^8qG4r7|XUie={ zRsKH%3Hbkv(EiU?g!zucFxmsQ0OnZ~ zIX!GJ9UzHfDVemfw=NHdRIMv*-|rH+yFG)huZ203Rc~fFt_@A1)f5Z=#iyrz=_R{Q ziq}nSlF#{nmnYe7r;u^VMr6^-oh($*CDIN3+ap8f^g8nrX(f2ul4xYNmdaw2Pb z&Xu?_K~M0Rlo#skH(sy`Oj`GvJlOq`h=%r`xKmW=j&|6tt&OOM99J2|Das!=@!n#b zo~wvottMox)pd@V%|tk-Lp+mTBFIA4$xu4z?w1~CcFFj#D$eF5TDjIHA0$sM*z4BxA~I4& z>R3#}+fQMue4aiV7St>W+96nz6vg5;)ICYN{@X)-&{4u31DV*fR#H;q%W*(?)n`eS zoGC9bF1KEonr&x_8RlHixUlxJr0$mZ6mCSBe4Z2`?^901Pwoi_1hYM~>c#uMaU|KqIsJqEnrKiB9dr}E$Em~q&vi){`vMNdRlO88gOi#3eb>ax zQ1mU-F~(l(d!!UuwyhMh7DwND*4h%Pn~jEWM(*vW>l;yeUG3c@iON-F^2y`IZMbMn9a z3rkS|xUTn}&0~}he(b)y)(Kas4=C4M_vIUMT(bl$nYM^AfM-`5uu4Vz z(pux19i&`(x*M1NVdexRkf^hZ=)ua+W1AJz+p#s2HS)Yb#WWMdGE`I|>=0g=uIwW* zq%9rdmtKKf=v9w|tNCWpZ>j4!DI7jNS6O7<2}ADDcW;qcPg^! zUkjKmLrv$I7s&^jDBXEV@%Otm$K*Y~d2e?{_{#5*Y6Yl`>2=P_aUE?vqZ(Q2BW_5v z%1;VdOC-;2O+~^SrU_-n zz5rzHW8uHN2>ROL7=PbC5Z24+Eq*A z)Pddbunud%UA3Ju<}J3C6pk!FL`ArGp3ay!>jRx6`fjLNNNwG?k_i?(Ey|G>(KB^Y6=$AZRoh@B+ zln~)&qKAK3(jFA?ESVGZbOSqzVYidI&$uq=dQT?*;K)-C|Dw@Jag+ zml3-!0I}DiPJK;l{QN7yTOT6XbeR}(F$vd@8`T2DnJ>13#?>U>ZP+ws%a~rkLmpB= zXrJKu?v0Xh&miv@crYbeZ)3E}Ealg#YUoaQnCQePo7$*{eCb_<;<~>0L@#hzShyQK`%8$Tu$w_Q@X$@yk6Q}a_Z1MrAQO!Rf^E8H1Ax-MBWMX*?kQY@>evFVt;)@rR}yG{deY zMq)4UtQ&WPEtA(77<6}GFEYN30b4`#TXD^Np{|`JlOkJsN-j?39y&tb(oq(tUmRnd z;+fv#bvz$&1x)msPQ;!cr{|_3l#LXzN^~|Q@6Em!R(mVI%2%UhT^%J)&j$tM^*REU zzncOh=f7UX#um^2&tn&1Hr;2#Ueh(ed^B<#R-f9oR^VNrM}XHXQjTv7j%`9mvHb@#ML+b&`mv(ZC5^M zuab{QV&)_?q)%R8boHJ~sY@a2L3cUOJH5E!4B~7l;msQSgIBQM4cwC~;Ey_B!2xT) z!4uTbOq#2>e;UrYXn-akP~7B2`bi%*fyy2E{96=X^nGz|4aAe8d#jkj;Y&S&fcm?_ zMO=okAs-mwYQmrsimuBi-M<9x`fEC`3^xzItO2TOr!H(t1_6z(?)u1o$X({t?Fo}`!6CI4O5%>P#Oi5 zc9YHIe9-%JXPCJR<=7XStk%xQ@R<23^EZuw&DMWv|A9d|6wZa*NR}rwuSL3+J=mUq zn}N%)_@b~yqp@#q4#IR|{ALpo!6{>vtp3(dcVFmyWhazO(WpL-{TXV5>cTIp0I=mW zEIOu{mX{u0EqvT{l`OKxU3Lo+$R;yDbA#O z!2cuY52)f``X`W=hp_x{Ep9&qI7~YN~l!yZ}WUeltBRT|-BxQ_}ov7cm-7 z!#iH>S5cMMTH}>62sq~djIJIWP@W`A;JE?gE5~2&273-xil_MTc)$GcaEu1ZKP~Eq z^`ty|Cehvh2P^CTdlmrxWPjiA{QKQ09;?}@pb1$4f2e(cu9SGU+8P8ALSYfZlSCTIiVw;rU%$ji_X^r&)yj(}?Ukaty0 z!KoO54!4oTrsy#nfZ6XT$$%Mbxl-8qO{M>J|w#v zXiWk(DwzI9F(h{f2TL5b2aw@`{xPqt)4I=$BR6;EqdSqFa$ZdB`a?N_3u2jib|}u) zm1mq|`K8fN*gaE>Cm@liNNk_JP2keI0J^w^kVwpaIrQM$F9B@yzEYSTfa1f}MkRlP zn;lS&WIii4G-*taYgJixaWN|kX6`?r+?m+8J0@aza&>+}A1w-{Fo|*kAhjaK+YiYa z^UiNbs04YMf7yt}VBD*nI%an=?@Y1v^-zhdEpeT?k#I*!@E^tgXOQv=-*G2mE)jvO zXy!+^Z7JyL-A79^c6AE`^()6Gei__VYdCLf<12+Nu%MX2%Se4mh7?vgJ@xcdUFZLCG)}M5t1svZsr)w z_xr%4kUmdFwl)Bjtxd0#m%V`I{60k7s+t`Rh9#^^d_1@5_z=Z z$PSJ@cjcPBwOrCFA5W$Wt(i?~^_Hd^`5`!dnn$etq2j^E(P|x53L%;nT)sYw-WnH{~Oxn%rM_-{3P}& zURI|_8zlo|ZP>Rm+IBO07h$_xL~tVx#{ANyJr(keqm4S|V+;k9PH8SX$EZJF0mY7Y z)D$xhl&7#ueMp{d@&FiR+a2+`cFOckE(H4rJ2>d@1*jf1m9<;VcCDuY2RHKRtJHgZ zhlbeb`0gD(A~fgg?#pe6k^2pNS3b8d9X1$3z=G;D6p|!z0e<50Bw6+YK zHi*QHWG>GYCc9!)3`&;r=VTsSxVw}xw9GZm_T=G6jaFXCz|KT8EcI4nHn6U6yo~~b zrQ^t``(xltJc@)TKl!N)PWulTF7>UJHFhEeW-0sopr1<4PzqeJu>dk^gtmqVT3ML* zT&EYtFQY#en zJe|;V6%t>tXnW6aU?I+gHEC4|PY6z#Kh3~0q=(_1X~B8|a#?2r2e}bhma^lM_DQ{d zAFzvlaO1)G4}@!3jXAZr;le%i{5xk>?@o0{;<-BQu1Mc>J&*M$y@{K$x@>gXc5Y~s z^V|XTu;{HxivwKIZI{f+4R&gv^{XOTyHTOS7#!bnQhW6pz^0Z^>1|VmCCjc8psSUw zbUSRP_vBfN8^+N+-n4cGb^HBTSx*PE+Mnv!JoU0$;;gj0|?bB*x?}bdZ z8Dkw6`qDXjsaU0BvuVO8a!;82dwK3-6>)Uo@UvNUIIssZ5-$S~Wa2 z8lTbAaOlXuC@wpAJjqQtg-NrS;r7Sd^jBHZ=42krRjtd5GscvJQ(x(|*PoCqS3!ZJ zm~Fg}$FikmB?BZoiIZ>T=CI~>&%<+tu8+tIj%t?4f69dNmpGj9ND zq8R0s1$Oi!0C^p`!|yN;Rm~+%pa#$B6Kppa+JTIxcmX-jzMJfu=@IZ{-y2w+KN9PY zr6uY#vI1nP!;82CLQj zSYO={-dqo_XFLJF6B!C*wFm*_ps6}{-{cVJUFSFQ{*BX=^EY}57rPUVBj}dux4ch{p3gDp3UQj!i`kX)Z0_2fVR(kj&D;nOf!8n{qlvbSNFgi(2EXa znlpm6FE*kbmFp4Hi&f?}c`@qE*FFUQgdaK^EX%oNT<=pCHT){nSst0uv2NbJyf~mF zF_~O4vroNS$gtI1m>DPZDIW^xc!bqN*o(QbNWEH`_$9AB zlS#dWsYEsMI)m8#6s4%@a+{wc@gDYi{rkb38k48p9zZ`0>9IeG^30)ewC$qfyr9oh zqE1O&+fG;c*@U<1cS42Q6R**|8ycOU%X-6Du7-Zl5frppHD<#AoP`xdrL2=dVJ1^joX^b5|_L(g|{b*cr%}@&s?=r84!OK}D;}hCM%1k`R~=kEj3fI^R6h!< z6%E=i^L7Wh{E7TBeoFKZ>KqF~BJ)qV=MH!jl&+T-wK-QAoQM&ai!zex4h>{7kJ2-K zcs{KqI;UtwQ~7oM4S4${XUZV@f5;+Wy*7#%rpjz^-v6VXLbSJT;opS<6*hc0Ur`!Q zmQ-BI>Go*z_7`31`(Ca9#3J#Rnf&f9zj=RqsH=Z%$RmRKWHoM0r(R>8Mw!y$#7$0m z=osxXb0+h2TW77;=kq=c63&ts&DZk-uTu_bKCvznULBd}9P8#3ra*qk-BP`r(s?(bz5s;6O^d!ws>fiQOQQIx5bCxW7wGBbLWbwGt`!IJme69 zeK#np@ZqZ~98s&_EZwZ`MKHT^tqMHz{?2;0m4F407V@At%Gi|CXXaUxh z!00WHoZ#{Lq;8kl6%=(l#Vi69gqSF@G4?LJfr7+yskEWjQndvZ3rcK3nL;n@@8~kl>wJg;hZAUFx)91rh4* zGWLJ^6pAzV@j}mw*F`5lyw6d`9LLvhjoe0`RdH!obQo)ns{LfztW6*QW{R#U?=*si zoR39Skpm`ds*W*OA5_+*yg#@StXKzZ=^>+bI* zJvA8h#c?b-A^^;Cj5i5YwZ(wd4D8^3Oj6RUYRB8yF1(kg6 zebc#|RX!&2v?O11GICKB9som1O>$-J^#6Vdz&fbDUi}#r>Tn=8n4xRYYjZV(m$hCe z05%KHVP;OLZR_6ez&o7prXLrXG*ZsMlz#O)+e4wm5TNg1C`f9k1;;U)Hp|K!KM;sG zv^125j(X$3ZvnWXoaOm$O${d9MaUiHU8bVhRd9Tk@T`kL<_d6~OHU=`+5SdV5wl~c z+fwG`?*hqTICQ@nQ5Cv^@8%2K%<1g1%?hrmk=M7PvVVc)3mmbj$WQEtCAunb^H z2vpFDM`1D4CJ*QOiRlhKa55p_P(Q;O<}mL&9l~a{IA4YKj==Gvp6p|mCjn*30cE~u zLO_c@r?y7TXuCeI=QH)D+LdSG&-;5v72}Boz8lgXw`}f!$YR7Dk*%nIY;;g)Qa&4> zmK}Zd1Fz7#K`!?Qn>UG@MT!)?0U`W;J%x&)l;rBU@d5F2a@13Tm*@ccKY-oN!IZsc z+jTn7IC+!7=;bivyRsMKG%a?)%RGt)Z_PQ(&c8hMnKuJHVmzd8&>HyrJLXQnT5kA? z!|Pj*f_rst(xtm+ZA`g%%5F_ggje;TwL$=7#vYncupF;`TZ-7#|Bch#aCDT82TVMw z+vw;UKzV2EY(X4xcW|5n#=G<@ab^1C-R_-er10Xr?*pwtzC6H}Y;zsXI-omzRGN^p z4A7;*eSgGhj6Y~7Oo@Bm4NQx+EcT_>$h&-leL&72irZ4%jST2%N3QG!eDde)op0=t z%8B({9_kDjceHWiDv0QMxidf-=Jov61L$W%gd|k#D?sL;cEH@H{7oL#J9r6_8(0K8 zO#}rns;@uOrO)RnzPau}tL;07i^RS8qVWj^eSYuV!=Zf;wZyU%7X?EKu2C~!Ub{Xw zgNIVJ19h4oe*Pv79Cb8H13(Z?F{3JxNw1HuID@|Eg5W0u-(f3YJ>Xt(yd!ANUX7lz zD_sP9pZgiw=kq@>AJeb*F&}MJ^T)a>DF>cPI*(>2$pSX3-@42C$H(@M;&c2o_0Q+W zEAvmCd#A(5EGi7boPJ&~o1oAzCQ0tXwlT9oam<*^exA{NPtmMcgfk!Qmb}x8Yg3c&3K`c#8)P z?3(n3TZy5XKdA#K9XZq^g&B0q{Rr+p7LfpAfOzvOX)tShrRHyK&61&sGx$dEU>M@mHp( zW$^CbvqDd$H36w&AsZL_ui|%OI&AVsccFcmdjGUbKO_u#)%`G2|G5M+@Nb)qu7{?W zt>w|>FcnfKp_2bc3nGe0{y>E6h5u6MukIitoM|Jkp#<`Ye?DHTNdH~QH;(?O<^Fv2 z&vMUyE-5qc`rkDhus_i$%!zx6crOY>^3TU5lYdX@cmM1EC)39NxfkT)e?}ZX<|mrUv`pisMRo-c`B{+1Svd_7Jci%n~<>(bLo~4$9L5< zGFjaHDVlKf{m;FbGu-{?Zvl5;nxCV(BCfvPU6=VGZ>pQ+xim4_+S;!n#cPlyPwhhh zgYn~WGFGwX>R&iLs@LV*y4<##?=*h@tCDnoaM54!S1j=OXp9iYUpG8={T&`k^UD&! zOt~!nb}OfDTih=%(@XQ8TlZ)E(tq7F4T{$GwzVmJ?>t8eW9@$ZYqGzsFL?YC^UX&( zhm|dJj5U;gNfM(!zIya8(LFy_tI_o920)$_C0I+@Q|&*}7H`#mf106XN2o zD=6ET-@EYh=_U5OTn+&v#=jQ#anyyOu?|Cj@FkB=9=-)K!$=?)oR^gY%{oDRexA+|%9ewua^uG@{^D&KqQ?*aR zaq9WmU)E#kY+ag&_FsO1N=WO~yG7Z}K26xKrAq}voaFxHES?+wcUY2t{|$vI$in>m z)79JG-h2aE2gYn;o+AB}EzfqpZRt%s)5VL&<#$@u>M*DbaaNyeJiKQ=!iWCJ8&6Y~ zC#%3#S-{q*3akCea}}VN)34wE@e}_SzA<&YBb#*6fBUlervG+E%Z|^3rG%b|d4Urz zNvVo;Z?7uWe@ztsi2mXbdg9DHkGZLgx7^JVr3nGW^cghx#jaQW`n^ow3&b<(`=x7W zPmp%Cx4$>jvO|K7Od1|z>md4UH_A7s48Br58WV0}L?f&`8C+p&Yi4!DUsc$AFcfAW zL-sA*-JUThcPGs(xFiZQbe*$+=fAzdDvNmbz3It$d2tqM!ppjC-#wO*NoZv$QO0k3 zuSrsP@LRKo67p{E-VS5E>#A@FVT5#Jc$7IE>c#IZCo(sKo7Q)#HV$QjXi@?WQ^a30 zX^}ztzSX$q$FA|3;L1-LQ*gbFEL7hidtcXmb~V}*(`sG!7y{|r4B-ucE9mGZ9KRYp z_2ee9ulA7g>~amdD+t3WZnd~Lqjv5pJdB_(Y3wcMyX~}B_Vos3LT(!DFKCKvuVTK! zeWnZgk4)Fux;2xQm)9B7#)H-LtA?d#Wf7OaZ_F2d z$wmtt%LjCbX3tF6Y2KVfNrvMq24vXQ673N+mBwz2hF({qX=Xzuyv=!nw7&9&8DBiX zKe^Qjp4eT@G_`?w0W`+QD~csv@XYIGJzP^>b*?(|u7rFbqY-hewYSGMa4}fA9bbnx~w%I(5t zg^1lotI36qbeH)_mQO75Lat@&o_7pf*$+nAhXrDG(c%^kyfOy6M7i%vx_e0uy}x^n z+9O4LoJ8p8H-b{(w$MVzCAu(Ux`H=}-8p%Yrd&>4Q`@(cd0S&hRl#j8R?!Pq5jL)GRL_A^SMd`3(l-&27HBg zKf%OALR!-&8&l>G=#|y~u_i<1i1M3~x@ea>X75*@S|%4K2Ee+WR<-6$-xxB1m0VS1 zAXo86@=bRzQ&Qd(hkvvAjo*r=1;*@3`VRkCS=E{2{V>gfy55KxGcPCZq&<@&^xZ0L zHf$eHwtC^c!a|g3X6xXE(j9x1PerZkjLhhjE4tj?m~OL0<11$x~|cO%J4w6d;uFVXD%`ka-?d6-@}8p&*towpqQgobz=#&_GO0XI%@c7V_)T8v?0~LY@@`k~@cK*z z466`d@==>4YY&2@gp2gV9<0d#n?id{X zX328DJpbZ?NS+;y6S{$jrI~z9IuXPpx@gR7y$$D}?M};Sjs|GE9{{+CguRN;`!nU4zdW^K+w;3)Sofz3h!pfRqm`e1tXvrD~7PuDZ+jZ}txikVbOF9sC(98z(@qBH0DllzQ`+XZ~=4M0y!sc{C@^#bu5$!ofOE z@Q_Y%b!o1U7{<6!nGwiSg=U#b(6QwT`WgoLCL?dUq+ zx|s5+e~y(X#+~tq+PVypxu#N5@(4&z>Ou8G3^Sp#>bOj2+&o*$sP0fc5M=%+`IF6K z4-YT|oi3ceA#Of>H4fJa^BtTPXu{CTs~-Zz-RY7s$%`!kt}oBlk`Zp_&LqSmKbYRR zc8aUl>lA5zXMYiJJRH#OkT4V&O|#SvHW>;O8&@&*wh1?R=O_FHX8NhEjQ$ML)fy7& z)Kev!^;QF@+QhpZ&5Zt{nXwF+I8D~UkQ7=w1vz4iz2$Kpf?OVMWD7g+aB~3`ad6Hw z*6C+@gj+>zco#S!-+$eXDGwUBRNmb^Z8~TXW147eTqlYVlXR>F%qd3WdI*ivgjtre z=P1;uM9Mk}tI_Sl__12o@$gU9ImekOKZ@jc7_o7|JQ44hSeW?fjDMQ5r%0<#h-+!R zw)&DRFzh7OJsOjDr%zOQiC0`%&ii5oM>Yq=V&$fkuTr z_cWMBpL95zTc8yrnkckv0;T3VII!&haiRtw%H^c)>eQ6I6kt;R`MYG+P6YWr)vzg}MX zd{EL)(exd?f0Rn(%9sfx@GSc>;4^1j_PB7b=Dic!?&ZZ;8pPXYNXk#c)XayX1k2(O zwy5qUH6jUp3D3Ysq%<`A*WdhLCt||x3yYz3iqGSFeXRN-92zbhO){%=mrG;qGcoBp zg`inF9m_0Z6)3-c$s7By&qT*VGOqb%J%5g=jnm`yPC2V0N~&|)nZX=gU1Fk+|5`oA zf}S=P!S^0ZX#=T{7&om#K=yeD8riOCKaRUM_J39jLZ~y`wEOCuhwPW~6Hf1ssC8Qdinb1pjaHTXSP#pvBP+N({@(>{1UAZAB{av z0)Q`g7|R~O1aBDh*_1#?$K>^-y@kq0q1IqRStlA;Z`lj5(NJMKSBm{nRE_y6Co?{e zC@1WS-1wgF%_U<}*;{jRqFpA*+|&U>Xsa*(#o6xGFmGjOmW+lIpJ#LJe9EPAT`N0P z57t7OS#wAuxoS>OjlBu`jN&bfB{rw1D=kYqwNu)P@ZS-|HD$ z>^U$nMxpnrZiukIL>;8A`9k60DyADF;k>x53l3o$9t0{Nk`izRl@E3CTpQ=Vb%oTg z`Hpgo4v2#x2frb($_!UITOO+9pZL=Y{Zr8aFPA>vS`Y3@ zE7xs9FO@;5TS^ZgFOjW55wb&T+A?tw0HeCK$s+EZJ|BWzmj2=J1P9!VxP^T7Kz<|62L*b2xq{+1N~TviO@-f(Yi5b+J?S0`V^$!=Q2TafLlyy4Lz zqxgJcUzOKTmM8{8IAH6+{=G#joPDiht<8kguw!!q&YG$y5I)>1whThe zbWN=@{&-5gujd>rL+zbXeW@V^w)P5LS$mY(iJ#P3xsPT;b@Rx2HlkpDv!CMEMI%~v zq}OK3fvkPp-qA4c)6jKon1UMPw_0j?BhP1fNE@PWFY&OC0;`?Z)hW~?=`KY}JC<$%Y$#RyNOJmpo0-lq2s1Z9HnQnRrq`nFUrAIISf0e;kja7SUQR+EiWWVASAIr_edCROy%y$H;a#l~ zp?xUn!fjqkm5O07&vYY4S@$r|?3uM~w!Xg2W&uU^jJ#;{7`KO`==$8Zi3-H8{fbCU z_MyJdmlJ1TBI_*-9pp-bu+r@gIyB3UYI}#9?Uy=iOdLwundTmq^FA3G^>n`slD$R- z3BuT{pBUR8B29>e2v~wdL81t}ma3wbp|Ea49Hvj00 zmrVRt=zj|l%-2QNum0T8J~7IE`TJsxX_OLI7-}m;_d;GPJk-BRRvcgU2n;V#VBRiY z>Kl%o-defT!9azN!fS}Wrj>2ZxXeD)H|067<&S-$Zf(+EN*8Vm3&bv3gvd6%Ik+M8=;8vS!Rpxd=TQlj7o!v*ya!!pavcS z*)(At5Kavr!OQM^BDa?{90F;bwQFr7Y~Qh@<55^aa)8^*Ac1gNtnnG7l4lSZ<-@DQ zmVC@*Y?Kq(o=^T{lb*d4o2t@QaorGVpJ!T@Q@NPa8c_}}3}gYh7`5s8;vQ#Q@VHkG zeK4`2X}CLsIx@gFPt`GiCrwVt%O5B_)r02*%L6lo_UJ|8HXwMA1mF)Q2TgQR67gM9 z6_&JN!MJ@5IaC$396)yGlTM&f>Zq-SJ2hcQgN-X;ZrJc*hy*}dA*Rl|>ZkYM5^on5 z7bWs%LeDHSO9CSsnHvKri#%#nyIOwsqCN8195CW2Tt8m{Kna+IL!q0RQkuS!g~H~2 zVs4OcK!&d(@qjvaB&(@nNNZumatT_fM(dPTdw8P*fQ@hoau4fbWXqV93f_aIiqd;e z47-(Em!CtPd3s|v(F?Jz9cNj0*I_9@5$IUuOVslmRIOJTKel`53e~7Tl9cj znLJVz6{=wr*Cw7iGP6!8()JMMn?HUJ&45ECxnpcRXiV5(#oMydQm3i`Px}{PLKkb2 z&K~aaDz8lIC;@2<)k@JNBoqb0GnhiwMH+t(MYNHAarX%NJb3GzU1Z{Dpys3gx0fHo zSe=MITBpIzf)(FeusdAl{qwu!x>^d5?ZYn~Bz?aPpbX$Mfg-b~f;+yFJ-GvA2cGMS54zA#ec4ldt`sFlw?>V0!>C$b7@^77A zUpnMf_a`7mndU8yy(}&+=J{{_38X()-M;@X*K@hQz~}5FQH*)M(e~d)cCMi3mwyft z@>y9C$>S4GopB40ynX(#;QhO!uPlb{>%LLoFSc8_XHoIptJJH*M|KPE^X?qFr4mn7-7UPh@b=^j zbEycc-NHrWlOHbTDy|pp79Ok|NqAszW65mqduLUp|At7J$ll!+^wogXU*$$_S@5js8#A}ScJtfC*Q}CqUZ83}EWCBa{WSA$SJ#!(v-ICan#SQo3-{d@UMf)i zR%Q%uKJ;YULd(siZ>p=5IO31$<99{E-GL{VisLAF$_tQqrGx5TP*-b*_RGv@&Zd8^ zoVG0T|K}>^oO$g(S3f@Oi~r|?U!UF8(f|2kt!wouy>X;QF_jL&vM6s3P^BKCq{qS2 z*yIx9U;%g;sBUp=K`ZyFmVb>0pdtK5SOavOWld1wnd>kSwSgep;^MYU461j1$t{sBIM&UO@`S8ZplUee9qS}%pT+Yk%hJ7q*3yT5#$3*;1 zze?2A;htN3t=DQ3m#wL-TLlZ#uPU@=sw(zWMl=Vq3i{$&P&l-C`t;+QvR$CVvqi_| zyj%~?Gup(t=;z-8tuf$;m9+BH2k;U)J@?W{r7h=$0T z)1#aN@dY6QPU8JJMew^74w}f=EW~O8LM4m3$!SXw9<TkM)Pbo37#|DQV=7wZ{?2VfeM=m=ZO70I&ZF>mwE49N6V5wQeLFCV`2`i z349ZHbPe}DXUU7!AKU@?0lhZ2DrpQa^E|kJ$@_m5Mix?*!nuL9_?IU!_l<9R&9Fd8^;3igNQgHKJHeiJs{8|XF zBoD}&?O%lWnKkWUC43kKjse@~p9-rmbW|yM>M>?~DDJv|BiMy)1`=?)C;+WyzABcb zG28`3&SOO8Ol3g7D{anR=|k4bm)Av3kvn(AenD#^#BF3vZD3uULsEi%^}|iS!KU{O z74gV{)QM}Z;N6I27Orf87-#j(pN*`s8GU|r>et;nCg(ztK^tZ)8j zOn=-Scpv24H&k-z>%EC<9MRVY+(d)jVf*GvBv1D$`nP3IoPJr%%o4W$lTWshLKc!vl zeag{JyRVHAM*hbaxte}DdpZBkN3+-%EEhFjaV5k0}JBv0^!v{D49p4`nY|e-; z8n*W4jg;Xn&PAyDHLe%a=(|?9sh-h3Ma#KU4s5@Hxwq1`Z9GDK>phsyT+3XfoGn<% z=~xc>V#5b2sc$v}6T=MCr)qBpq}sa(Xc$$P=B%&6nPyf}NH6!r_B<#~oT{;K24A+# zS6s5ov|g6stTX0%*h&7;^>aIZUv0sh`OYrhgwb=uO>$xHHq%MlPkWGBRb4$6PUTpPnmIO7HXO<#(I zdU_nSj&Y=qIoO`)W@bZqv7FJ%c;LDyb$GI|!q@2DMT>6B=I24y+I8LLqeuMpz=q3f z)90*>eOhsECruw#lDAFg&D!g4k&>@z75pkRcqYXB_3VL zAsb;M*!ufm1{GmDn{y+o8NMNP%@pC^@$hW-CBu@J{TI7{5&Sv?&O%kR$e*T2m$gc=*2;#LFpf5Qh4J#P2?{i4XfLQi z?}LqTA?d)J40)r=SUvu@@`H_z%?KfQtBm2}THXLFu2ZUt>?eqa)r z+6#;jDm@B!zT4WcK53O2zjm(327GDEWl7$Yr%7qEPSynFB74^3E+nI;j|`OFLzT3S zispP<-XC)L>F4KT%kUjsd(JJ* z)sk0n3Cb~2CAuxmirp79Ty3@H#u13~R&ydF8o zpv2-PmzG(Ns$MQD6z;A>Rji>mxf3n&oamekyP5sa*2uqq)}H;pz1AnXldY+4fBebD z8g1Wm^jQZjR?oWPm67waP@9jR4(|Bng2r}hGtN0BR8^_zq1)0!;cq<_blk3&NftHW z^wL2e_8YZT%4z31aCevar6jU`*7z;Ky5U6_!DV9vp8SPpb(s{MmAL5s)F}yfQx8If zq@^_}o1yiM@}aIjjd-}RnfPjcAK6%GW=%ZC~>Itw+w;AV-=A%J}DqzyV!% zEV4)7heRBG1K+l#kJeAT_G7-LN5?Dz9{ zK8Es-t#Kq)n%7A*?r3Gu`7eJ}!;iXR|+b zrTMSBO7^BbtXK7%mvcRf20agdE_Hjm7zd6^mCR2RdRrYlbHtbUzH zH^1p!J|ac-%nP#)v4QXNg|5= zk`z1fyglJK@P_nz&KqAQ>oK@en6Q@R)eiXt_ILvU-V~ z=*}Lfxy)*M`A6jmZgBI9OB)0>V?tlRdEf~5L}f~r7uK;IUQ7S8>(xMw8?uBxiA_&` z?nwkHrPyXodf3E)_zn4reCS5p1u{m_qPZ6`Ybwr$?%*=j<3Fs>PSYQMQ~|S)vSzn7 zUCx^Mk>AQ(R`wqiUh+A@t2e1vQo@Ji6J(dy8mfe7Y>_qjn`)_cH;KKYL%2Rzk@>wM zMRQ+dxaMnAww>-UDN(m$p5nOWKs+=7tJ9m& zn)DxBSTdCgPf4aIFiZN_XAD_|@zxuM(wkZ6?2X9$5=W`Sy{HPe_`7e%E88GGQ${vg zae5rMQGE8G^`J9)EpRXj4R#^%7l0YdsEylnPu#<3d&^1(?F%bkXmm#+EouiykY>Hn zI*smhF-@Y7IeY!ZdwBC>k}#bmY$mL78I-(w)l50A5R|}*M$Ky{xn|~B7(+W0Q2bwFt<^PFxLtMD$(qu1C|IG|1AdrApKfg8#AF z-EK}1Am-9B8X2snGVGlvbax~5#;zuktlNTneZShKGZ8Lvi z`vSkdBV?N>l21X9OVGzLg`)XSw*pWCbSMdXJM7IFej_TGmD#(2k1mB%aVorOt^x38 z<0~|o3RNmv8@wtqw`q5bP|}mcFvpZFHx^Z|KXIUx!@4X);7P&8s;4p+6_5NgnsqWC z>dXby14;=Kc7~#B9SqE4&wZJbI=sNnL8ldYiS5v-`x*Vz@49`;%9OK?D+(Ut#H-#I zryF6THQ@=(8MuoYC89Tb8Mv6^fe|uL7PH@oj6=7>7hIlQ6b|5q*9L?~0R$f99B_tg zCn{r3Oxs9V?1TfC=JA3b+Ej_$c*Vnum@j&>ZI@FXVM)*x@}7M1A5vzb5HU08sz^|G z4SB-WykS@)_4>ShKjW~F8HeF4WoahZ0OAdwHY~IG<^r(Tgmje$U3BO*+2IMc%@Bdm%Z zpVQWhh(ZzZfS(QdKPgFb)yO+w!agikceBJ^b3Nus6}ubGgmEihZDmN zrn|(yHMp_XY&RWCj+!4NiuX^8HL&i-R$;(N4&@mA7ysn2N7wd42-SUqo~|^fwXq_} zFvz>q?VV|hCt_fY{6*9gGBF_Q^u3E`H2o48AGuuxZ|jE#powIl1ge+G7-rF9fa#$c zuZB1#*|R!$(F%dO$swBesu0@{#X^;6+X3KlFl zspn2s)w({r4fcVwok>$y4yM?w=$J-6+$t#CBc`~; zOL#&NlP^P?fr|tB^RJ+9M9iK@uNDgn;TGkr0S$V+1R%}~a0uK7ZBS90ms&{QFM<&z z@RvFBX{hXtlsrWl?|$()Y3OMu%alwIT{VAZy_11jkfx)vMG4G;lcYLIy2gc zZ!bzY#Whv2Uhy(96*5mDDZvO00NVsMg?A-G+n6e=WlU`*piLhbH9h$F#_zjauAy$- zxEwPuf7Ev8%lTh@REoH;J1LyR`beis(f1Y)mA!E8(y}0NP8Vvne?w7pM2ogz;3&QQ zEZ(@fdd{6~>|@#NRY{IXxs-_9QIVi3PV;yvSiFAw>VmLOqBh>bumj?Ofl?`!avL8F zkA$^M4W>`a5|(p!35{snxR7KiXDxlkpm-F+BmTv#9pUj<#Mj_v5>9W&Cj)te{2+Jn zo8}(Y;>RUe`jvZRL{|x z=IlX*uP5!j>1CH-(uU1vvG+duLZ8*err&V|OOV8g#grcaEjt(mo>YB(qCCLYn%`K;@J`)k>-3W*J0 ztNV6#NOM;#)DfY#PpnGkTq`~q(tlBNrl8!V?n%m;+9fBB?!F1-LAch~r8tUY09^zt zj}ZyH4;)iru&1`WtoPn9&b;=CJSCPz^-2H9h!##p!iGh}r^yCE87n>;j7NO!>-j?M z-CgUhSLZN|8`gz(==oE&fMh-9C^*|zBZ8(%?U>lEI(^_9=MpU+G1YZQbnc&iNZwK;z0C-K7DD&jZs5dbo0DN8N~@_bXU$nOc_=-4?cKzO(1qPIC>Y=b$L z3^UW44Z7ini~RfPZgnrbCK>pnDBUHmZBIa~NiaK#^yOrPx;?U56~^*tHzm;hBp=Sr(7isxm+;)xYfes~ouK{AC>9=d|e`kkAuBofZs&@@iy zFpF1<{B5#pVsdAlob*ykKhuJ$PX~{sIyLF9Z2Xozyzscnq1(AahTVv!kg%uPG3O`m zS|tg`T|q2tNUXgsPlP(#?5RGLFJoPvSZim{d4@w0*DeX}Q2GTEHA?OevEl^}{BoBt zG!At(@_QeYii8{>3EIAR&_?Rz$f*e(TJFNd9J6Dmlj+%~KtU1<3;(o_OmrP*6T(0H z3Y?LcICvsL>V0MDNrCJvujx6bWF6QqFeHRHgympgnY;yDyEqldDQCo#&P~Mo zSIq`_;}(tje=YMOpA=?tp@Jh5YI&Q>v`T^u1fs7iZUqPT;U(~u1z~turaBi>the3S#Evtr|#~T8viJspUig2QU<%m-E7tV0R?PnAkeqz(_+gJ%gDpJT zgZSo14x+B^Jo+dvar$7Fb`h`AeQbik2{OYc*CXOh#ki8u++<8_Mru+xZQzaOj7;Bu zQ?M2qsdrU#>EA^g1V7e6@n{I{a32TFQ=fGM-me3sqe7Oa7{z9+*c(mcY4Qk%#u|5u z1S%ClrrAPS_;<}MSCe{@s#kzLAogYAozR0$&C6J6OY`9qBWgSW5cn+U<>DxvXsQR$A~n^@ga@oRKuf-As1EkxA3cdy|~nn z*jMnRk^nWAG7-QWQugg0>ols@?Ia{0-P8#nU%)@4mu;I4S)fbAlu1@eg+~8MM487Q@Px*n!(6c zDrLn4HeH~|gStAn1Gs>rrTh%#{RuD55HyAcS*?HxfsRQMTcDANWK%%xYD*{5Ma160 z${9Mk>}{%?XoYE3B0-QB(epKR z*OlLTO#H1oQ&IbZI+IC{hpOP|I6Ca1K0xf3S46Df8E!l=AWljb<=K zDUc=4E4!IwpB{m(hoWB)Uik29of=Pq?Ph7sTPqjG zNn~GTrbX@ajjf?YlfSY#2_X?at6^#bjY_p~ zkOZ;AV-8;Qnxs0DVr0Srx}CAU;PF`O;na#z{1z}nQ@YK@H1plkU8em(WKy~BSPe_b z;Y~<`8LU{B;AgGv(f#Ex@nowM+o|nynaNmF$;EfSdVdN1K8;j1#`!rR+A*2SXpbFg?wN)?Q;v1g-Z`-E_ z{U5Yn%oi8b*s!;r8|K&`SqCZjvRm>K?UEBfakf2pCJK_ojRoHg<+~G;Y+J{F?|bGt zr>G{j3UyPmDctPv#LND|IhlDJ&NG8gf4p6Xg6XX|rz9}+3pX|zHKzFDoRX7GI?vz7_i%xo4)DE??A z8xCsVtgGN{+L$Vg9JVeCuxD6uv@O!nZ)6^fU_Nx!F={;Z>#JX=sP2+=9N?FB^4C#L z@2{b#z{Mzu>>Jtu$Bk(-93#iaw8vCPCF-}w=*gZOt{dx+OxTE(AbG2p{Qggo(DX_Z zKkjMg*CiR6YOxWbz98!`6X@C#=PN2J!<=6+q48{pX(z-%(?pLp>@9)ayM!K5EMys4 zoiekiM#38zJU59-aBq;qg(DRjXhMSR8J}SK0?El3Zt2i(0w5IXSM}ZkJPQQ^m0veR z{5%vw%;^e+#nC}fYm|iOir%a_)Fe(#?5&`y&|p*cNY-6TUiDF)_j76V6Xp#8 zb+Er1QKgO{jw#P?!J(<@Egq6UX+2YZcQEd@TU*@l&Dxa4!IPv|?}JoYgDw!lfX*qqRQyDm#yx!mI+AJQHSD zFU*(8UIS%O*F~$aO+9p0n%n3pHawyfh*DJLO@gjiNm&4tI+*#KG}cO(2rl4CvFSEV zh^+S|Li7hsG#Zq(p_if2H0awT_=T1FbSc+gqu_oOC-Y4xcq8%gaAK)|%5v(p;3Vj8aDK1^npmM){c_eTLYk_TJ$TL?OJ@)GUI_zACUBirUCyrG61r zHt`)XeHpX5!X^v@O`I2d+(cMDjJYN3e`>RG0Wwakf*r;XuTm}$W5TtyiNfWeJ}V*9 z2^%`CAj}=-rbpg~Pzfy5yPxl&UAR#Mfht|KwBizLPSKs~kwDS*Kl!xk4qfv=^ zoarY9*QViXKVT0nCEU%gkIc3~snm^Ed1AvI2nt?jvetP`?cmjQ#IheZjJaj0zkjrG zN;g3+_|Bo~yL`Xm_5CbjHcO;P+{@sb#GAQrb^5NP@Bf$bM2B_lKUj?aLaah{)J@a; z^0Y$Eoc9GhH3}{4!Vp8%doR4|f3Iu=7H(mR%GCEi7}X(_HK~BH)r38bA+vL7ru88+ zRVf^DKJ4Ov;mP~SYCc<7IY_8xEyZhyF6eSqEcpekjxX3QGo?Z&VNyg5LsSjPSq0qi zM%j=H7Rzp?qS092kGZqD5-ky!_3@S1-0o`=_PsYUbJ3Oqv-@-mUTCl`^AAK~EvnJ; z>c9t*dx*5-J-F88*we&dw7E(m9MW|8LI!#go7}SSR>6tF^VznR&EBxzN&WWMnOs}iJ{ zR01XUq3F7ClS9A8oEEK`j!^hNq&=7GSIwY-N!97^101U3QCA)eL zrNQEV5J0Bk*SE-A(VuZf(zjx&*^<^9oF3)y6U*sP9kk0cm`rRU20BXOUTtN&99zQ4 zLz`ir_6S=}Z$K}oKGFhdbHvqrWDg{@NX2&>P}&h)Uos|dL6q;dk3>G#9D*nsDC~l! zwP$21-;o{$cMjZ*3p=bzO(&^1tR!?WbmBh=7*T!zF79JqNHHnkTgvwMQ5`gxIzZ7>4b^8NT z#E^hx%DmoXB4v8JR<_{943Ns?mfJMDcFPqDOv&_Zytxx&ng)WM0PFYV7R{F$ zL#o_39jO!_Z{+x3k53Azeh-k7b04eaB>ySg}=W$5V#q>fZqDyvi=Y{BLV4nU8RRNTV5gfrCt zFJ>6iEEM%QdQsuWab}~4ff~w;%!Jselr;&Iv|_XWDa*I)r4}};IsvjwhPO=-#}ISm zufjrvjZqC6LMw&!=SV#U+mB7*Tm-^M3ZjG!BH``YvV;)$7ha*TIPntULym+H;7kUX zqT3vMkbLc?RUJl1PbVudsJPB@;93<%F%@rNyoKgjB%3gPsUEC^`cw4u_`(UzF6Ep(3^wh63E)(lZ<+z42UaKDe=Qc3zK;#o6h*0 zfwML&C6#JEewI}5_-Jmx|0cWNkn&I^Kn(yJ`dJrA1foWhBKeh<@!1did*X)-pz#gY z8_Oz`7>SG-0AD@0EYPQ_r3Ct@$iL#$SPtD*e+LvYg6&Ub$s|j!Gj>{x`Ej9QL&C>ns&U- zPb3N%W)uKm#$6S;S9|NZ+q^D*zh~}YKV*|bqEmIPBr-NUjZ;jO4h!baHShJBIA2Wi z&K^t8%sF_cwUjS>Xd?15i6Cscg?ql)a{n}s&1w;OhZtNq)R<|VZhD*Rk3=wq6Zj+a zvHAV4P64hX-^8Dk?ou~M0F%g;ZBb;0p4D_x^`{KYiqBt_yJ zY`UI}Nznq3hc`G4QH?#qv3d}eNwPjJJIQ(Qp9-570YP0@1?}R_nd>l_FF^&9fJ*v-?oabM@4AHc3d;BV){y%_;IH$Lw8pu*H zUxT>9`?5nb$i(6Q5}EAf6{%!RkUW(?IZkbKAJ+>fPHNaaqLz^q+lE~-JdG3JYr?O@ zd1OXCvE<;j6Qj#f7UVCnFV)y_lBNYJ8C)a`CeydBkpVrPM0)wz{{uuc+x9U;jAU~* z{QrPUG9&IeHk^C`PUeMgrJHC?feOla89%Yh95(WU+hHwsIf_{Oi!V5^cv#ZlPXP^R86~VN{Le2p=wW|8J=yz?}ncmnt6@j>TznD zOP5UNFeD__qNd*s;&9;euy9H;5{`KLcEF-g^wlhsiK+nlqD&#BgUf`AcKhiR zQr&K2VNGsx0QUlkQKNCoq(P4a)Z)(qLb>m0h*ba*07ntNDUw72t*R+DRW-@@#k~`N zhLsO?7iQx}_A;<7!9M&=32jh&%g^{1$X|;#GE}TyY`Dy8({k=POqteXr2r>q&$hSSZNb7rMSR3#3R`Ygacd;b>?M1ELYex z$xqyf=b1D{VR?K3UE(~YNSE%m*}KV@`*1nyk1~+wwCz+*D9Dp^GjiBC+Z2{Uz9dJK zJbWC-L{FU4vz~oFNsD+AGyZy;=5_z0(c&TLS5K1=?-{_eZz#v`RH}0Z;^|Mk6Dr44 zhPjU0pRQF9hNJqJ-9v3+0kdH?n^?kK^PLY2RD0BH*76ea^4cWd6 zKO|~LICUpv3uf3P>OAy5i9AS&=x~Y>Uco?7PHb_26Fn@6rh3(*->mu;hc;`UK|X($ z?TLiSg{4DBtI{OsDotDsk6tk45QQO4oP=}aYtB>R#2A&gpaS9yB~H2e6pgF2CDwix z9?(R>s^-XfH4uM-8V22x8#2#zP{-PiP8rMMpI9z2IOIym_s+Idx@~=Kui3dT@msyv{s+7m^N~xc<^lN0l zX`6X%iks1E)b+Cwo=+CwJs!$$&cLIuW;YR83YAccj}rbXd<1CmQN_d>6)>Mdy;Q8> z``VL<^hqgOW+M}ZXsa>SfeR7A(mi!SN41&u=W)`|!WeUwLO)IkIPQV*dhvwIQpKe# zesik}d?j;G{JRz)QEUC)F||5B+j>q<=p&J@>f~K@olh7K`Dutl(RT@)0Z70OAcX~d zjgk|8`Bcxhy0jFt=3fP09VerISAqXK#tj zqPO=Uu}ZJ~;m(>58-CVb_%*GwPAa{&ls7a?+@B{)*J&orZ3WkFb|0wul|P81cAMqG z+EVvms?D0Zf0bu0wfT6&hupgL;O4gU@nwz__sW-B;LkDbs5^n}xLonXFW`QOo(I)i z&2OU2sEqdi>wv+#zsP#-fLqhH4_@2_?H~0dhB;$C8=p}ItlXbr3jPPP(ne zwx7OvU!QrLQ>F9oR8N6pA2wEVW+H&Vpe|}_tni~wWtRnIcLlpUtYZ*q+oqcYBD0%6 zD`z_RkDGMa2mg9&FnRmoj$E>-btj%)yn6mPPs3V2=guz6Q@hR_l$Z{0QB^!n!jc3Q zp5wc(FQq(?_cmooM)uiy*uEZ%n^TlYrJ^a1bnXdGH#j{?rk2t78u?qi zDVjNRqOH;yLxx3<0*e-zAJp4pdv6ZG$b2UVmj=RnN-R<$1%ys>-(#`H;N7IkP(AC1 z97qCg7+N2`ZX)(prnZ4na7pRM+-~`)@a&14=0skd8O1Ynf>yil>%$UH@z22dT9&C= zEnl~)*!1t@S$_qZs`F&Si_A^>VedD9GiLP_?Xu>X%zX{?&pMga_ioQ$tIM&A)^2}4 zby*!;HEEEIlynBf-x;@TCb9n7n-vIU`*Tc!ny zS^Il|drD)A1sAIT0uhSx1aZ(*@)L=&%BitwIe;JVAVyj4aGD@`{&i4V>|3g_x>JGW zE-uxqpl0fP5$`|a2OX?%4t=Q8%tLnOm5H*BN+WH44Q~F~V$8-UJ!@o9{kO7B^Ly9r zFZT097KLAa^Lk7$>zd05O9D!5vu3aD(Rh~Z(kwsicw_5M&nB&QB=YY+%F~7p=BrdwJc>)BOpO==R;Q$GIk;Esz5{FeGZmoT5s3)8^_8ky*Bk zXQ6r*Vfol))#GoP9!F{XzZ4WlT1fiyqAk`eId;ZEtL_oT)7GuqdCX$RPg84of9Oho zGv-(yjpWsO}kg+-ctC1~VFa%~FsX0pP? z*)|PJQ-x#~VA4nwn;8UX1j1AipQ(`Q>ZD-wtev#_{2eFEx<)VkfTk3**W%IKIQ%iN z(6i|{!Bz3z#(V#rncAsqh9>^C^5GQtd|7iR_~slf+KM5MLYCL{VdYmv@>O9j?-$Dk zugvPIl>1uh4m0^=a_x3{T15m+-p5xnZUoS;s$h0@Kx-8%9kqZf-XjZ+yHQ99bEJGb zJu$@RO$LW4x}W-RjVV6pZDQi`X{r+AM?w#<_$=F4bw=eLi?oE{k;dQ-z)y>Uw%d|x z;=3vEuc`pKBJ8@hD*-vPx<)Q71a$n6HKRMNkzE*Q>#cI9X|UDhLnNR`_*3F~VT8x$ zq`Gj^pnXL4!1gn9ap47c8WPk$JYD z8Uk>v;vk6M%wx(PsEwuD8(7<(UN7q%eC)zPP|vD~ePTd+Z^U61vn5QPSU?cV#*+_` zoSRtgJe3`{o|{%y6}9Me7oZwH$Hre!#~5>Bq^Ujc_Na8?pXJQilyR+uEMLyy%*m%E zDILz6yX-#tN6T`zs+a3PhO6NXnA^1A)+oOy%j<1Wt!^BCKGxu1l&eCB2uG2|ka zW4GwUaO9U=`NuCt@5hz30W$e95?+(-3JD*<4N8Wlj`02&KhN5#eHlNSLb8+8+hcri z?g_HukQGQO97Q`r_Z4kn-B{H-;ArePYVyT^Z%ABCTz+IF8Xj=)4&tv!Dx9FEqR=E#s0|iv zM|T#7D0qnJ$8UednaTD6=mvR&P#8vJ3k<~QrIhDr08iA&6Ghv!cvvD}$u26=uP~#69W> z#r2c9xFPj5SCMT~q@x5!m5_N#G124wpH37ClHtR|8pU;u zL>-uQOVp@|F!`c}e(%7YF=-4whI-pi18H*W8U-csPI!Ia&Q$k$;X~TvljAF}p4d%%VRt8dC9=Di zF4cGx3(=HaK35^6lEpFW!D66A74OO1u_39n@JJ9N_-}aav_pTuGPrKL<<3mUWk=O}?c<1a0KQY~&{v}}G{g{;|ev?Z7=3&VH$TnorH&BAm zf0NY>eWtA`_o9CLVK6V;EDI5pH&Lt9&#k8!!Ph&cO(n6g$D!5m4(UshkCfwM|6r@%PyV{pY%03aRB^ihvN z0S|2&oPs#f)fh3^c4_#ND1GQ6j-#rg|E4?A zUlLa_VtZBGZe)K<`qn2(cAsAN@Ht|FmbH+V@$#5pu@Lz_Kf0V`N!E!sno}fymgTa5 zmrxnE4I$to(#Lc`H~A*BjtZ9=J?L2`dhO#dMrAI-L_jOt;sV0+bEtCNt^GiO-&~&l z3C-cnwm>vst>iqHe<)Gv_ZUN5n^O-9>o0#$EKol218iId)EyW+Hxr3%pOh76K=u{D zU(%{_Qba49*~>&+s5&FZOH_nGe7K_?0YG=Q8p-~vQC;Daz;dxSWf6`T~xZw zAoQwd5vfo(v8mMKRDM9~`OCz9K5(Td#`nu947IswSJjR`%@jJM`&%NryO{9p3OKg3( zK3%FDc41?;6jB-K4Acb7DVmP&H+wx{z<5VqPesp4-->{(?%bA~J`H`k%m1A81&XKJ z_InXST2YPfA%EQ9GxSP{a+DMDq()*mm{2Hs)}N2{HQA1Ernfn4*n`$_2fhkZQa1{d zR+p=2s;eZlOXg=aTl5_&##j!Q7I;3YA4e2Plnu7a#Iiw?u@}JgNFm8ciGcq)rF2~J zEK6<^X$&O~e!(K~WN92{D4gdOK}toDMcJjm-$6k7b{XfaR*Nk9+?Lj>6jQvxBMQcC zf-5t6Fn+j^YlU1 zQA;VOdsjQMQPR)j@RsaJ@yCAn8P!?qTPkwcKF-fJJGFFNz766>ri&7X&djT3a1YDs zOS#(h;sdW&$x-#@QJVO?XR#8RT+7vq@Nue~_HnRuLZH0Q(}}MLe4vw`Uq1>VJX2kW_LLU`|cvNwT)Tc<6 z!dFBSx8FC&8zafc5W>!jo=HdAxFBdb82*mP)<~l#825ZDT@vi`H*foG*j=&D z&kK#sk~l-^kp^m?4a0u;WRqRto273j1T^QDZ2BWJiWd;1ExoAqEL|!m>9N6+$|wdn zNjc^c=aMV~Lcx~mSaMv@Sl;#sVgL1;3El%g1_OL^ROuA-&M$C^$Yk=H204*BY_|Cf zs*qdwUagtKkT6AJWoUEG1@*mbVYf7ra6+vGX#$JdAO3HUQ$2_eDB&7Wx7c;{CD&?1 zY}cO*l-Y+)*n1I|=N*rVhY;iSd^;T{n??Tkg>#CE1r3%O@T(dOt?gYj-)_C`9r}?U z?%b8XViu{8Iy!x&D!2GZGVSTgXP164uVu6~2162r2I=hoiW#G+0QDH(ka&ZipWHzI z-w4V711?atAffapNN}c38}KAZBB)hZP70B_?}RZEOr=nKPViHy9?kTKA9{X6?M3_! z(tn3DaL3JtfKjGZ^mfRHvjrX;c&eV3duNpccAG$r1sdH8-suydMLGEhNGYH;x1miiu6-fT1Yb!2EBR z*k@G28e82p_d>RN7voo)AD2bkq-G(JbMEBSsb{3`EGmzz&AOg}6XbJ3Ev2Qc2M6<|ul_NCAGuNBq;|Ca=BB=zAX86X(shu5S%X_ZBDB)$FQU z%}E_AZ@!}H=Ljo(EoTMx|JixycJC?ngW!HqvMOD8{Ac~}ERc08_4ZD?t;YwnCB$!Z z(ul<7MQkB2RkkZ@L890?z{v+wr14R7a7qf^v;OwZaZMxwiE-#=Yry@O-chRlD` zyNYYY9dLA@-_H+BzkBSGk$DiHnBXQh3#6E;ucD^OQ|aCFrjeZ zsMd_KQ4U9RUUmgVKS^ig6u^^$`;VeukruLO;Swb^mqs=63`5^g4FA8CGXB5a0U2&m zEx?(MOCQRTR27KCT#s$v2}4R6#B08_?x5+{>N9ozF5a6KA%+K6W-SXpzCSTWC!Lo( zXWQMcpLq9&Za=Jf-Sk+r9ZJMWsjV-uU~;M%KZba^;Fxv9(S5l+|D9f~>Yo_uYK4F8 zTDyPC`pk7gV;LU#rb7Rn7o8RzLA1PHU*fWZ=^B=~+75){8G3qt^n11FEGK82v>jRPNeSg!DA;O}SIf~Y4N@MJ3R|9Rq*vM9~Kuc@# zNo>$e=uN&~MKVWRD1x1}N%GAk>&yoU57Y{4Ba2_}HQE*FAc5fel~WkGWq! zu-22W-2V8ZHYABfv@w|xlw<7-G^P8mkfE(EDx;M-Wd~HQY-$fWQzuMys6Bi0y*I`5 zIP7F<>CkQaiVVf%?OV^V@I&g)BO(5vmtyaaO#R`3slgGsNBhU;@>3T1e?K{AWDxFo zpZH0HLwx7y%Py+##k@nB*XDo!&~kmf8 zOJ&J*{~_}klFS?u)EVEJuH(~Sp6qhi(EwXf6Z{g6xM|I$Ur_yMx|+jtT-SEi#0P!k zE`QU$2y+L_XxX?c>+_)<9is628|rueS)FY+=T4AW=!&;9oUv-r%JO~-ll zng6S_FOP?EfBPTH_c&TOhgM=LTcs>XvQDKOT` zii1$Lv1Kq#Vr+vkW7g+0Gn~`+_dCDm`981T{N=^C@6Y{NuIqYV*ZX?kjr_xHFCkiT zdg&?f?w)cD^`Pw)d}jL=m9RRPOv?bg{QG;-O4wHXA;)&!pRn#&EiCUR$Ulph{M=nR z?Mt7Zv1b!ME{TA4H!^X-vhpa#?%sXYd zS63SlRRirX9_(Dqa@q`5<0494$?`s>SF?SW+j;in^*Bc(iGb(zB+PXgUt45@#n)kC$Em5I_GfuGJ zKkG6U3338H7Abfx2Y$tO7%@)Sy;S*CoX*yP1ID&S8VPjHpk;^j2if{O9yYI8wAEJ7 zSmu=D`QOSr@6C^ErtUozvBDx#7Z$!B*uhE+H3o41KV3Y1de{xQ-N0 zI_*1jO}}xhBIMyKgOb*ohxu8XD$5J^cGeK2sjB#brSWGpN*h~861w@Wi%0${jrubQ zeu0|f&1niuQqMe~YQ-sH&eJ2*r(BC*?21rp+194c2op5NJk&L$V-exPDsa~+O(tzd ztK$A>Xb5c~TYo3-ot_|Kbb~j86~W%66=3MfIXh)+D}?SD2q!is8f@EK!v-xFxATOI z^}G=1g2qobNZr6_o?}g))3XiVyk0KveA95p$s`;1&&6g(KDYUeyMOU}G!O*o@ot&% zZ8FCt27z`#zG-V4F5oQ%585dgXWy#wdBd9J)JArEX$(XClh#~I8Bw_p zbJ-p@7pu9fTzqbcHncn)^amVsr0;JkvFpbZQqg7=aJyux`ySAv89Ib7;O;Bg4|@cX z#LA7RUM@)_A=9848JELEh-Ht6B*WpYOi-N-a6? zQKe>SNw3}SvYVCK5|7;P7@qk@J8KbAp3myO^%aNGNcQpE(R^XckOX7SqxwfW#Vn=> zL-{mS2c>WIY+^5MyUbq94z`XTQrL!TO zC9ghG--voO@@dR3z+~W7fj)>6zXZF$@u`o(eNC?89~gB57Q6<_)!Sa@u(#?xof=RV z_NeX&%%Y&1-IrazMm+IG`48?7#d$J1BPjK&b!GDXnm7BUi;wlal?J{h&@=QgPr68U z#8%Mbi~a@$a#(4SG{xUsBxTAg5l$-oA=LXiB#}-Qvo1P>nevj}Fn;mNqV)%wCLz6B zc9X7%g?a-R@V2ZK`%CChKurDarOPFzHlU6|mpx|{_Rfe~V{TyL3sW)+Ku&r&!TWVA zrT72hr#i8yk5HR9etNekvDCTd%b>M0Z?N^g`{VMNUrafUg*MP#r+8(Nu!xEYX$My0 znSFS%-8F1`IwS`;+uMu8lXEQT^&)D4kQj7PKyKh$5xrdPx+%bB4v1(Qpec~O2J}Nz z)5tSNhGPbOmq;YqTaihAsqzJ_Xr}qzX%73rTD@{vY^jV15%Lumb@ zxdudmTv;dIfb@OhJ;x@i50ObSbqhPvgfd?e61iFcJ6OW^28Hgb(?ojx^j#%(w(AWa zajU1#1S}mJgyN4hmDZO}PcB$Pdx)}DBjPv)R|%k?TsdDepHvx|bs)K`ypY2LRLh>G zP#dDJjXjIB#HUBj2ItVNNz#i2ty>i)x9w-J?@q%$Wb#tz^UM9BYIQo}=UruvBCdy6 zb>x%B+OM+wt9%bO1sepg;G~t~U7}ngq5+cW5B{yKzXP=OGx%-&5Nx>~N^rk|aT*-H z_sDN{@1i9g7H|)(G)5Bx2ZXJRL;jff1^+9ziD~{R?U%nhFMkS{xn~?GTK${Amj286 zh?jE&xQj7L&4%-i#MquiY~nn?3as)D>jHX-oiKJNX~mY%#u;tZV3x9yV=xm9b6Vh9 zqp9H8{&qzo>{Pm(2BmwK(}7AcZ6aLRqt)W;(F)FFo{EhgX>3f26;X}!h^8ur%;q-CT5bMQkta1XN5I*{zmU#;oJtOPM z$>fm;;cei)P4yqRQF(y*Em1X!&~4*Cf4qq6=|jZn7@Bgj50Io!W^lHSz@ELpSu%nO zYnxDAw>f)FzkYbW`PH6JT7~dpXs>5-Ktz8cf^0#urmN&5FX-Xkywn5f)nQBsy6fTH zahxiHU>stxA+0oTK~c>JSpN0dGTsD z31tCF2#ZXh|236n3KXE$Bm;axk^)24F#=Z*w*XBD5V(`9JzpITBGyvzj`&3`b_qDT zh=;_(z9mD80UZ0I7*isqX$Lxw8qcC*j2~!HE`*73oBpauj zIMp4mops6OZn{k&(O2^^GOvAtGrW6zb4O@(>`M2J9aD6QZ^%*zi~MmxQ`VCOD0Uy} zbZ6E&Xuj&wk&(b#Ro;Tl9DtXy>lbQ}sKQdXx7~9<8Fa-dGwB~o*}m18n88>R){MTg zVwYP8H`!bVH{>RpZGzF6shq>hA@y#qH&DiNm})C-F-jbR9jL;J( z2$YK8sKKE(rtU@>)FlX=%@#XMh8#j!A<7A{Jpv7++nn?-0w%H$->%)V&%Oot9ygCQ zOH!tL^(AN$Zqc{mKF#G_!XTxg`WR;P;^2{(-2D{_%lHAG zFIuFm9X2R}!EF71vjN)B%o>W23OssUd?!WVXwoZAaT8Ae+1rSc^ePul)73wtVrOp!IIP1>HI@vR zN#F{!^<04@kx0duW~p7Z2zBX8Q?pK32Q@=ef zdINHKzjSy?1cozJo)*asi00>AIKNnNB3SaWJNtZc*ea%+5rOwQdcy89zDGr8T7>Q* z>mMv$oTGp)8;1YWSxlcSQitd3!drO0ZZ0~h7xyWAW;(kVj)y%HXB-xLR{j>|%*tn# ziC9%~_TWJi{Q&a~6Mi+S+79I`S*KHFk2DmPHg z^#d4`;~G_HpHX=$1=s(BqHj}N!e-#`XM8-CXc;suVogH>erqiq2hfrcWZ|KCg2d*>;8BCFmKbUKO8X%%ScD5A(2hXxCK8{6=QB` z)eiXyZV*4cW2#W-$AM_^ht`?~b=rj-G2esV>2(u#ah&o@)ym8XbE=LCSSN^~Ja**y zgmG9dLT7$PXtewY(7)k2%C;7s{tX0MIAI1V_J1yuWY&pfx_S|WQ<#+9eVYQV*&i0z zA_*U>ey1hKwHUUN_`hT&e@13&x;HGTS02#jlWHXF>r~8>YoFpSa6XVr{cI8t?lpIg1f(`l!g2&vK5 z#oebTALe?@7Xus~VgLTEX!n)dDee1Vl-DtSj zGuQDP$Ip*U61BYDoT?C1wcRgBGQ1}v6T#hH=P-K+Kr4sU+-RcVCgxFQu>!LCJ{IgVs3 zpVXsv^pY?ymumsb0=Z$aJ$S)mC5lJlW;jK&qDT_@>Q7D)_${VVtxgzk{9v%}j^-R9lG$BVL3Px1DWAB6MogGP<`n;rW7zz zb;*(>&Hsa|%{{sHxj?TV7y6@CB%Ib*37RXfEx1vS;q*$bU$^xf(rre0PL)W0+*TfO zG&!&&RoB7S>E=g2FHl9u9=URRs(r`J2wZ}G`C#{HX($PzjrZ@{al|t4C&>5xnC9#s z=TBFr!a>llGgro@1hlgY8EOY-=S%s5?a)EbD@wa*ab3+qPvFa7Yl!(v1 zu)P{k%yJ8i&JN)+iXnWeWIG{8{8{{9qv=H0z9ucbo!bc-__nEXPwUY39iRElf#N?J z8={>J5AaYRp34O0^(daYpxFoAvE2E$XdaS zC&UBRvA^X6nL+2kxCyY?p#k{A)KZb}v|%p$mOttGcnFCDdD>bDee25-x-Zi3T&uXA zfO(_l7l?fTVBrhz7%?kz$@QVw8{&HyVpas&2g4q>1CPqn3~X!MhjD9YHI$Rd~{P#W0)}1E3ms!yhTmP&+ zkk*d^&~dIEmYhB_p?=TLUr8(3Z;|3I-L5I5{f@rES{}*vGoy?JZwB-H`6pXH+JIW3!X;0Uhhed5!5Qa}4cz^4`KTX}wEU`{K;oS;vnTe%9~oh8u@uacQ*811I#T{sIAw>~`Sv+yLE}meNS1Hg zT52DRu!6pDhI$>`J^>fkLvXdU{(_5J+$x7N}`2#%#YE(6RN(~Lr!x%mt{G|~8W_B)^z~6QOCu>AY zU>5`x<%2FP$jKWXzaVe7I7{lL;}x!!DLnf|N;vVPC2wa%t)k2AP`Dk2Fa)t63VtSmpb+E94&v*r+eGD07a@S3TKY&S8U4ecqg7mAg#Bh|V2J z&Uum!S2Eo8ipHzDEGU=fHkixP3(=Z&;`lQKBx>9-KfyhixsZbE2@X-B%G&l$#_#qB zShChR8|x&2Af>lsOxUXT@}+20F&8ZYym)!cOK`uU@%zeq_cV{iN$i;nmAmfJyD?K; zV9N*jOS41yx@Xne<+JWg3ipQ#_k7w`BdR9jzB<>V<}&BP$}xRbN@p$zDkL;hBk+fH zM$d|tnCNDWS_9$e3sc)A9CX;d!K-9-tdMp*#70tHeHZ1N{Fw@2s{on7*BI}F)>tKr zDYlbO(;0`NYd2w(fQMF>Q5W~s-V`*ZqV1c%SxBS5(AI%Iq4_d~zof&pufa}LzS{hW zd6a_@+%CC$m-g@ab_SP4C3LedaCDpl5oLC;Z23R#^uad>x z*6fyj{M~{z+$G?`n4iIceB|Nw@;QtxGNzTkzrCPhN>~GZ0Kk^NsMH4QVg$Ct+u>ES z@X6HUp=6y8&-ZbW56G{4Jp$t1gcI~dAUvg8HXKh%Mwn~PJZfvydW%G|W)^@<5hYDA zd$Z(48|g8+#U)_hRWWFyS+e+>n+mpCXT5U$Xj}NXRjiB-L;3OK<$s-SksAUr{FJoi z!DGZ^H0F5<=8r9sR}UH;W?s5vRGhd_!8+^ap7LW2VR}_g13>DM@YzT(7jZR%u^HRn zG?06 z@D}`}V%DO{rmY?jJKe>NB?JupRYSIA!%VJ(0}FifQryM*55ptEDN>zxzVXz}AXsJZ zBLZ94YRpmTiW*WqQ);de3$CC=MdCqvdx9sWiE?CQj}nuWo*-{OhCqeS>XdX#NT zK&ny1Sd$iGa==G~Tt!B$?k3gA$sqY@2{$ zR8VZ1PoQg(*{?=#VM&MI=xkpiFVyZSphvb;s^)RHVuUEV&x*C3WK%^Q$G(CLAa#Yz zhQX}-tO>VN^*-|ubr$Szz&j8L@un_O3=7!oAt~uqTBGC7?G^x_F(wBd>)reAH`Pgqo_wX-M zXZ}y7L;pV(ZF?hH0(u8HG~X4z5nxvN!!dC722T-gd%-k$Y>4MvQ?K!FSLGC&22pn0 z{b+UDVuyT>2rN%d?u*Q|S=){CO)`KSf=KR!#h`-SnaWYwGhDUdMXe9dU$sR{rJX$U zzKG~|rxl1KmU7tFZ`)*B)s_ds;FK z;-%<-t>U`NGg3095Tfvf6&LH$iU}obAN2%F@t8vEZfiJOiV&-sEM613qH3%%7tv6$ ze77RDW(WUX{$pUxSF>iAZ4rJJto}Q3w+cv*D~qx4mde=gny8gZVA3NN zxT4khR8;M%s`UzwSOI}J1b~*Ak0-XRy_aUVbxgSb)vIIUH%5*K^ck*ou*3tCvR@&4 z5aBx0eJ(5-b@GiocH!^k?kXZ;Q~)zgaf*v2@CA6q{uqTR{xWZh6&G z=j4fRL(pB3i6ajQk8xmnFWE2tUJ@^h*ptXdr+5S3H98KB*ZFV+{f;!2Ec@Xit z|KJYZR;I4yWz&zXP5J-aG_*RupMQ!4__hr)`;m+YWBIKx2_Sgj%axlIb4~;EzOxk; z!th-I@Ue}j<%;CUlT?pO+$_o}Mz01JVPzmnlCabX)Ra@K_yxc7+>FYCCOIh8<6#S$ zd(25e{2L$rXc`t;$5d&sE}sGiGxVH0{NTRcNu>^e$CjyFnc1LF`tmkBO_2BfFp?BX z7{${3n1Pb?U?t&>E0wjPkG4_|?>&ZNo|Wfc^H~P#m;NvC@WNcS^LzKFkaKv*+J1-N zzPbsV?3PWo3?2Rv~22TH&vdvHO*;T^poIn|%VZ(c&tdXq}n1sd)!Q20%A z=ig#}DucE8&-nwzIW0MCEdcm$D7UrbYd#?+B7#KK8n*MT-ux+$Y^TiRJ1!k4;7JW5 z+D-w?F;7a=$y)|X;u_egycJ}>??!S3@xiBVH}4FkF1b^+^6!hPWq5RpmY7$o(O&|g z%`1*5$&LK)p+|tb{QB+k<uW3$@PYY64{F#Y&tof5QbFb!`A4C_DrH@agaI7hOU# z9(SP@+m!Ku(@CfB{n~u140!1Y!ghw4*n9~zA`qorEQ65q~Z9 zXz3G3Z8p~knSGef{Tj+d{{~-0KXn4l5)K$U1J19Qn@O0fl9MM+RJ}}4@I81k@gZW; z0+Xp|8M)4qb4Eh0_Af+(&8N+JNeeCtbNRU)+tOmp)-}W7t znM6oOH-uYMB5=^C#epVx_Y83*pMCpyzAY^N=)NG)lp9ELy6M62MEatH!<$Ykdxh$b zkC>8X^iUi53c3xgOJ1~9)jD~@}_$<=BZJbC4SF3qQW~z=8d#u<5G!rES<4* z!zNljVte!v(XH<6-lu~r>7u_E$b2+?@d9yb@*eK6J%*EleM0s^+Ihj5THT8V60puO zS`d)VjG{bk;B6UfM%4|$!h}wF?6~ImkZzM!K#l0Of}3g?kZw`MlM1@e z8O@;Qnj46fz27Aq8o%|X($;?sgv%i_8_B+{O4Q2_%=Rvh^VM$cAE!bJ;x=0+_WY2? zo~;J)m+h}) zeDBap`#8hS=5&98G#s?pc4fycx zpa!Vi2U^E!U&je(AKTfQ-iR`4{Z_N<%T9D;WJEc;o4B~=_g#aNNe9x@k4&lmpzn<= zOOg(SnuV1+;X;3aZrEPhH#(E%v+|5iZK(HEVY6vy-b{Bq!qmGwd}WT8(p`0wvxZs% zhkF#&yiTf$sdfDyW8Ooru}U z4-51*=?e78J|>+o)86?V5pV)vVRx2K`|`%9X9mW3KIDz}r*P|Y_kDRWHD`LpvG}dCRRe_Cx1hygo;RNltDodt{j5^~85;ua?!)uy3_@dkiIg2rkEFrjuL9 z5#FFlvn|=rfCx^cV~KlcJvp}m#p&4_>wR(Y_qp~Dop{dOi8hN6y}LB6F$A!3q;Y)yBe#Gk~5GZ;^`f*S{bEJGrB~oukjMzsb zndAW<7}CDhbpjOwW7DqeNF#&*y1Fa^6zlLcH+`Zh>pgcoOwB|MalB9y9}KH77oHyX zd13m#YEin2P&P!0#hB1~D3#PY_2^|3b>-mi(9Tbb*EZ8{Njg&kn(JuEfu3{vef;V6 z|6wdxsPkK^p))gaH$z&SM2Ye}KZ_F;>8c6k{H7d8i57B-(7f5A;y+&bfC9*mU{pyk zHkZEsBzU)NK{H}Hb$8SAv?1TVIhp__gHs&3v#BO}*QY#rdC(ZR@bu`D9wviCh{Yfe zG*_fXX31=Qg#ywW<$#U5r?kK^_fYTv@gv@CF?*JW5UUI?n}j7A;*16N+n324l>>v3 z#TM4Qo8Hx1lqInt$I$yKbiUq42?8S}Ylca^~l4Wtz(Rv!o3B zcFd#SA{*6bpQ6e-`nH{`PT?H><;ke`#00s|)sMMCKacAtG}5k>F)%e956!Avsthiz zyiYNVYjSE=h$$#jWz^ANSpF2D+uK5f_RC(ZvOygq<~|%}H~Rsd zJ?`trezijI7pDa=`+`$Lu`mp>S)As{py=o_Y(CtOzI$fAYVq6*RdUA&J%E5li#ok^ z)Rx2yurSI7)uvgJsJoW3c-^HU4OO=3bYbwjAk~M2O`f39nRg=m-4Y-+;xO`j`Hm8t z&~t;MlR~aRcCFpV-oP`(Jl=$wo5@aJ%Hw;Ne?2r}?jX7!?~3Ejg)u=$E!#KMZ{zw~G{Bu$Sq3J#MI#I9FA<}0>a_FBQ1funkow`U}?Yj-`MV$>VB-gEOFI~|5FDP0P zI8yoeb4>^R9%AnkEA2b{TEncEXi4%>L60-+pdFYXUu-ocG7|k8brQZ}7`^(9j{n$j zdICNNTWxufbIxfp)@V_6RoZx?84u_ID|QJiUc%gx${;QV z2ex+;8?V?G|4zY#Zn{R%#Gtz7RndXv`4;?IQIGOa9H`p5?>7Zj> zt|YJpy%GN#u*&?Kq!?4p#~tqc7c~4J-v9sr literal 0 HcmV?d00001 diff --git a/bsp/phytium/aarch32/figures/import_project.png b/bsp/phytium/aarch32/figures/import_project.png new file mode 100644 index 0000000000000000000000000000000000000000..5aa91d058ecfdc97ef53b1b303a0282ec3b00e20 GIT binary patch literal 22542 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 z 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 G{eJ*YW_wNm literal 0 HcmV?d00001 diff --git a/bsp/phytium/aarch32/figures/select_debug_info.png b/bsp/phytium/aarch32/figures/select_debug_info.png new file mode 100644 index 0000000000000000000000000000000000000000..8d224369405afb948e93467bd23f15fc923ac824 GIT binary patch literal 32841 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 Msf)kjtlaPXFZ`)%<^TWy literal 0 HcmV?d00001 diff --git a/bsp/phytium/aarch32/figures/select_driver.png b/bsp/phytium/aarch32/figures/select_driver.png new file mode 100644 index 0000000000000000000000000000000000000000..3fa21b7e8dfaadc79b39db73ad2ddb7223a67568 GIT binary patch literal 35393 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 literal 0 HcmV?d00001 diff --git a/bsp/phytium/aarch32/link.lds b/bsp/phytium/aarch32/link.lds new file mode 100644 index 0000000000..d1acac738f --- /dev/null +++ b/bsp/phytium/aarch32/link.lds @@ -0,0 +1,116 @@ +OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") +OUTPUT_ARCH(arm) + +ENTRY(_boot) + +SECTIONS +{ + . = 0x80100000; + + .text : + { + + *(.boot) + . = ALIGN(64); + + *(.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__ = .); + /* new GCC version uses .init_array */ + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + 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/phytium/aarch32/rtconfig.h b/bsp/phytium/aarch32/rtconfig.h new file mode 100644 index 0000000000..cdb1dfd16b --- /dev/null +++ b/bsp/phytium/aarch32/rtconfig.h @@ -0,0 +1,261 @@ +#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 8 +#define RT_USING_SMP +#define RT_CPUS_NR 4 +#define RT_ALIGN_SIZE 4 +#define RT_THREAD_PRIORITY_32 +#define RT_THREAD_PRIORITY_MAX 32 +#define RT_TICK_PER_SECOND 1000 +#define RT_USING_OVERFLOW_CHECK +#define RT_USING_HOOK +#define RT_HOOK_USING_FUNC_PTR +#define RT_USING_IDLE_HOOK +#define RT_IDLE_HOOK_LIST_SIZE 4 +#define IDLE_THREAD_STACK_SIZE 256 +#define SYSTEM_THREAD_STACK_SIZE 256 +#define RT_USING_TIMER_SOFT +#define RT_TIMER_THREAD_PRIO 4 +#define RT_TIMER_THREAD_STACK_SIZE 512 + +/* kservice optimization */ + +#define RT_KSERVICE_USING_STDLIB +#define RT_DEBUG + +/* 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_MEMPOOL +#define RT_USING_SMALL_MEM +#define RT_USING_SMALL_MEM_AS_HEAP +#define RT_USING_HEAP + +/* Kernel Device Object */ + +#define RT_USING_DEVICE +#define RT_USING_CONSOLE +#define RT_CONSOLEBUF_SIZE 128 +#define RT_CONSOLE_DEVICE_NAME "uart1" +#define RT_VER_NUM 0x50000 +#define ARCH_ARM +#define RT_USING_CPU_FFS +#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 +#define RT_USING_MSH +#define RT_USING_FINSH +#define FINSH_USING_MSH +#define FINSH_THREAD_NAME "tshell" +#define FINSH_THREAD_PRIORITY 20 +#define FINSH_THREAD_STACK_SIZE 4096 +#define FINSH_USING_HISTORY +#define FINSH_HISTORY_LINES 5 +#define FINSH_USING_SYMTAB +#define FINSH_CMD_SIZE 80 +#define MSH_USING_BUILT_IN_COMMANDS +#define FINSH_USING_DESCRIPTION +#define FINSH_ARG_MAX 10 +#define RT_USING_DFS +#define DFS_USING_POSIX +#define DFS_USING_WORKDIR +#define DFS_FILESYSTEMS_MAX 4 +#define DFS_FILESYSTEM_TYPES_MAX 4 +#define DFS_FD_MAX 16 + +/* Device Drivers */ + +#define RT_USING_DEVICE_IPC +#define RT_USING_SERIAL +#define RT_USING_SERIAL_V1 +#define RT_SERIAL_USING_DMA +#define RT_SERIAL_RB_BUFSZ 64 +#define RT_USING_PIN + +/* Using USB */ + + +/* C/C++ and POSIX layer */ + +#define RT_LIBC_DEFAULT_TIMEZONE 8 + +/* POSIX (Portable Operating System Interface) layer */ + + +/* Interprocess Communication (IPC) */ + + +/* Socket is in the 'Network' category */ + + +/* Network */ + + +/* Utilities */ + + +/* RT-Thread Utestcases */ + + +/* RT-Thread online packages */ + +/* IoT - internet of things */ + + +/* Wi-Fi */ + +/* Marvell WiFi */ + + +/* Wiced WiFi */ + + +/* IoT Cloud */ + + +/* security packages */ + + +/* language packages */ + +/* JSON: JavaScript Object Notation, a lightweight data-interchange format */ + + +/* XML: Extensible Markup Language */ + + +/* multimedia packages */ + +/* LVGL: powerful and easy-to-use embedded GUI library */ + + +/* u8g2: a monochrome graphic library */ + + +/* PainterEngine: A cross-platform graphics application framework written in C language */ + + +/* tools packages */ + + +/* system packages */ + +/* enhanced kernel services */ + + +/* acceleration: Assembly language or algorithmic acceleration packages */ + + +/* CMSIS: ARM Cortex-M Microcontroller Software Interface Standard */ + + +/* Micrium: Micrium software products porting for RT-Thread */ + + +/* peripheral libraries and drivers */ + + +/* Kendryte SDK */ + + +/* AI packages */ + + +/* miscellaneous packages */ + +/* project laboratory */ + +/* samples: kernel and components samples */ + + +/* entertainment: terminal games and other interesting software packages */ + + +/* Arduino libraries */ + + +/* Projects */ + + +/* Sensors */ + + +/* Display */ + + +/* Timing */ + + +/* Data Processing */ + + +/* Data Storage */ + +/* Communication */ + + +/* Device Control */ + + +/* Other */ + +/* Signal IO */ + + +/* Uncategorized */ + +/* Hardware Drivers */ + +/* On-chip Peripheral Drivers */ + +#define BSP_USING_UART +#define RT_USING_UART1 + +/* Board extended module Drivers */ + +#define PHYTIUM_ARCH_AARCH32 + +/* Standalone Setting */ + +#define TARGET_ARMV8_AARCH32 +#define USE_AARCH64_L1_TO_AARCH32 + +/* Board Configuration */ + +#define TARGET_E2000Q +#define TARGET_E2000 +#define DEFAULT_DEBUG_PRINT_UART1 + +/* Components Configuration */ + +#define USE_GIC +#define ENABLE_GICV3 +#define USE_SERIAL + +/* Usart Configuration */ + +#define ENABLE_Pl011_UART +#define LOG_ERROR +#define USE_DEFAULT_INTERRUPT_CONFIG +#define INTERRUPT_ROLE_MASTER + +#endif diff --git a/bsp/phytium/aarch32/rtconfig.py b/bsp/phytium/aarch32/rtconfig.py new file mode 100644 index 0000000000..a5202911db --- /dev/null +++ b/bsp/phytium/aarch32/rtconfig.py @@ -0,0 +1,68 @@ +import os +import rtconfig + +# toolchains options +ARCH='arm' +CPU='cortex-a' +CROSS_TOOL='gcc' + +if os.getenv('RTT_ROOT'): + RTT_ROOT = os.getenv('RTT_ROOT') +else: + RTT_ROOT = r'../../..' + +if os.getenv('RTT_CC'): + CROSS_TOOL = os.getenv('RTT_CC') + +# only support GNU GCC compiler. +PLATFORM = 'gcc' +EXEC_PATH = r'/usr/lib/arm-none-eabi/bin' +if os.getenv('AARCH32_CROSS_PATH'): + EXEC_PATH = os.getenv('AARCH32_CROSS_PATH') + print('EXEC_PATH = {}'.format(EXEC_PATH)) +else: + print('AARCH32_CROSS_PATH not found') + +BUILD = 'debug' + +LIBPATH = EXEC_PATH + r'/../lib' + +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 = ' -g -DGUEST -ffreestanding -Wextra -g -mfpu=crypto-neon-fp-armv8 -mfloat-abi=softfp -march=armv8-a -fdiagnostics-color=always' + + # CFLAGS = DEVICE + ' -Wall' + CFLAGS = DEVICE + AFLAGS = ' -c'+ DEVICE + ' -fsingle-precision-constant -fno-builtin -x assembler-with-cpp -D__ASSEMBLY__' + LINK_SCRIPT = 'link.lds' + LFLAGS = DEVICE + ' -Wl,--gc-sections,-Map=rtthread_a32.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' + + CXXFLAGS = CFLAGS + + POST_ACTION = OBJCPY + ' -O binary $TARGET rtthread_a32.bin\n' +\ + SIZE + ' $TARGET \n' diff --git a/bsp/phytium/aarch32/sdkconfig.h b/bsp/phytium/aarch32/sdkconfig.h new file mode 100644 index 0000000000..8913e55fd9 --- /dev/null +++ b/bsp/phytium/aarch32/sdkconfig.h @@ -0,0 +1,75 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: sdkconfig.h + * Date: 2022-10-13 15:53:46 + * LastEditTime: 2022-10-13 15:53:46 + * Description: This file is for + * + * Modify History: + * Ver Who Date Changes + * ----- ------ -------- -------------------------------------- + */ + +#ifndef SDK_CONFIG_H__ +#define SDK_CONFIG_H__ + +#include "rtconfig.h" + + +/* arch */ + +#if defined(TARGET_ARMV8_AARCH32) + #define CONFIG_TARGET_ARMV8_AARCH32 +#endif + +#if defined(USE_AARCH64_L1_TO_AARCH32) + #define CONFIG_USE_AARCH64_L1_TO_AARCH32 +#endif + +/* board */ + +/* E2000 */ + +#if defined(TARGET_E2000) + #define CONFIG_TARGET_E2000 +#endif + + +/* debug */ + +#ifdef LOG_VERBOS + #define CONFIG_LOG_VERBOS +#endif + +#ifdef LOG_ERROR + #define CONFIG_LOG_ERROR +#endif + +#ifdef LOG_WARN + #define CONFIG_LOG_WARN +#endif + +#ifdef LOG_INFO + #define CONFIG_LOG_INFO +#endif + +#ifdef LOG_DEBUG + #define CONFIG_LOG_DEBUG +#endif + +#ifdef BOOTUP_DEBUG_PRINTS + #define CONFIG_BOOTUP_DEBUG_PRINTS +#endif + +#endif diff --git a/bsp/phytium/aarch64/.config b/bsp/phytium/aarch64/.config new file mode 100644 index 0000000000..a98a9cbc22 --- /dev/null +++ b/bsp/phytium/aarch64/.config @@ -0,0 +1,922 @@ +# +# Automatically generated file; DO NOT EDIT. +# RT-Thread Project Configuration +# + +# +# RT-Thread Kernel +# +CONFIG_RT_NAME_MAX=16 +# CONFIG_RT_USING_ARCH_DATA_TYPE is not set +CONFIG_RT_USING_SMP=y +CONFIG_RT_CPUS_NR=4 +CONFIG_RT_ALIGN_SIZE=4 +# 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_HOOK_USING_FUNC_PTR=y +CONFIG_RT_USING_IDLE_HOOK=y +CONFIG_RT_IDLE_HOOK_LIST_SIZE=4 +CONFIG_IDLE_THREAD_STACK_SIZE=4096 +CONFIG_SYSTEM_THREAD_STACK_SIZE=4096 +CONFIG_RT_USING_TIMER_SOFT=y +CONFIG_RT_TIMER_THREAD_PRIO=4 +CONFIG_RT_TIMER_THREAD_STACK_SIZE=4096 + +# +# kservice optimization +# +CONFIG_RT_KSERVICE_USING_STDLIB=y +# CONFIG_RT_KSERVICE_USING_STDLIB_MEMORY is not set +# CONFIG_RT_KSERVICE_USING_TINY_SIZE is not set +# CONFIG_RT_USING_TINY_FFS is not set +CONFIG_RT_KPRINTF_USING_LONGLONG=y +CONFIG_RT_DEBUG=y +# CONFIG_RT_DEBUG_COLOR is not set +# 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=y +CONFIG_RT_USING_SMALL_MEM=y +# CONFIG_RT_USING_SLAB is not set +# CONFIG_RT_USING_MEMHEAP is not set +CONFIG_RT_USING_SMALL_MEM_AS_HEAP=y +# CONFIG_RT_USING_MEMHEAP_AS_HEAP is not set +# CONFIG_RT_USING_SLAB_AS_HEAP is not set +# CONFIG_RT_USING_USERHEAP is not set +# CONFIG_RT_USING_NOHEAP is not set +# CONFIG_RT_USING_MEMTRACE is not set +# CONFIG_RT_USING_HEAP_ISR 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 is not set +CONFIG_RT_USING_CONSOLE=y +CONFIG_RT_CONSOLEBUF_SIZE=128 +CONFIG_RT_CONSOLE_DEVICE_NAME="uart1" +CONFIG_RT_VER_NUM=0x50000 +CONFIG_ARCH_CPU_64BIT=y +# CONFIG_RT_USING_CPU_FFS is not set +CONFIG_ARCH_ARMV8=y +# 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=4096 +CONFIG_RT_MAIN_THREAD_PRIORITY=10 +# CONFIG_RT_USING_LEGACY is not set +CONFIG_RT_USING_MSH=y +CONFIG_RT_USING_FINSH=y +CONFIG_FINSH_USING_MSH=y +CONFIG_FINSH_THREAD_NAME="tshell" +CONFIG_FINSH_THREAD_PRIORITY=20 +CONFIG_FINSH_THREAD_STACK_SIZE=4096 +CONFIG_FINSH_USING_HISTORY=y +CONFIG_FINSH_HISTORY_LINES=5 +CONFIG_FINSH_USING_SYMTAB=y +CONFIG_FINSH_CMD_SIZE=80 +CONFIG_MSH_USING_BUILT_IN_COMMANDS=y +CONFIG_FINSH_USING_DESCRIPTION=y +# CONFIG_FINSH_ECHO_DISABLE_DEFAULT is not set +# CONFIG_FINSH_USING_AUTH is not set +CONFIG_FINSH_ARG_MAX=10 +CONFIG_RT_USING_DFS=y +CONFIG_DFS_USING_POSIX=y +CONFIG_DFS_USING_WORKDIR=y +CONFIG_DFS_FILESYSTEMS_MAX=4 +CONFIG_DFS_FILESYSTEM_TYPES_MAX=4 +CONFIG_DFS_FD_MAX=16 +# CONFIG_RT_USING_DFS_MNTTABLE is not set +# CONFIG_RT_USING_DFS_ELMFAT is not set +# CONFIG_RT_USING_DFS_DEVFS is not set +# CONFIG_RT_USING_DFS_ROMFS is not set +# CONFIG_RT_USING_DFS_RAMFS is not set +# CONFIG_RT_USING_FAL is not set + +# +# Device Drivers +# +CONFIG_RT_USING_DEVICE_IPC=y +CONFIG_RT_USING_SYSTEM_WORKQUEUE=y +CONFIG_RT_SYSTEM_WORKQUEUE_STACKSIZE=8192 +CONFIG_RT_SYSTEM_WORKQUEUE_PRIORITY=23 +CONFIG_RT_USING_SERIAL=y +CONFIG_RT_USING_SERIAL_V1=y +# CONFIG_RT_USING_SERIAL_V2 is not set +CONFIG_RT_SERIAL_USING_DMA=y +CONFIG_RT_SERIAL_RB_BUFSZ=64 +# CONFIG_RT_USING_CAN 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=y +# 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 is not set +# CONFIG_RT_USING_SPI 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 is not set +# CONFIG_RT_USING_USB_HOST is not set +# CONFIG_RT_USING_USB_DEVICE is not set + +# +# C/C++ and POSIX layer +# +CONFIG_RT_LIBC_DEFAULT_TIMEZONE=8 + +# +# POSIX (Portable Operating System Interface) layer +# +# CONFIG_RT_USING_POSIX_FS is not set +# CONFIG_RT_USING_POSIX_DELAY is not set +# CONFIG_RT_USING_POSIX_CLOCK is not set +# CONFIG_RT_USING_POSIX_TIMER is not set +# CONFIG_RT_USING_PTHREADS is not set +# CONFIG_RT_USING_MODULE is not set + +# +# Interprocess Communication (IPC) +# +# CONFIG_RT_USING_POSIX_PIPE is not set +# CONFIG_RT_USING_POSIX_MESSAGE_QUEUE is not set +# CONFIG_RT_USING_POSIX_MESSAGE_SEMAPHORE is not set + +# +# Socket is in the 'Network' category +# +# CONFIG_RT_USING_CPLUSPLUS is not set + +# +# Network +# +# CONFIG_RT_USING_SAL is not set +# CONFIG_RT_USING_NETDEV is not set +# CONFIG_RT_USING_LWIP is not set +# CONFIG_RT_USING_AT is not set + +# +# Utilities +# +# CONFIG_RT_USING_RYM is not set +# CONFIG_RT_USING_ULOG is not set +# CONFIG_RT_USING_UTEST is not set +# CONFIG_RT_USING_VAR_EXPORT is not set +# CONFIG_RT_USING_RT_LINK is not set +# CONFIG_RT_USING_VBUS is not set + +# +# RT-Thread Utestcases +# +# CONFIG_RT_USING_UTESTCASES is not set + +# +# RT-Thread online packages +# + +# +# IoT - internet of things +# +# CONFIG_PKG_USING_LWIP is not set +# 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_LIBMODBUS is not set +# CONFIG_PKG_USING_FREEMODBUS 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 +# CONFIG_PKG_USING_ZB_COORDINATOR 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_EZ_IOT_OS is not set +# CONFIG_PKG_USING_IOTSHARP_SDK is not set +# CONFIG_PKG_USING_NIMBLE is not set +# CONFIG_PKG_USING_LLSYNC_SDK_ADAPTER 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_AGILE_TELNET is not set +# CONFIG_PKG_USING_NMEALIB 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_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 +# CONFIG_PKG_USING_RT_LINK_HW is not set +# CONFIG_PKG_USING_LORA_PKT_FWD is not set +# CONFIG_PKG_USING_LORA_GW_DRIVER_LIB is not set +# CONFIG_PKG_USING_LORA_PKT_SNIFFER is not set +# CONFIG_PKG_USING_HM is not set +# CONFIG_PKG_USING_SMALL_MODBUS is not set +# CONFIG_PKG_USING_NET_SERVER is not set +# CONFIG_PKG_USING_ZFTP is not set + +# +# security packages +# +# CONFIG_PKG_USING_MBEDTLS is not set +# CONFIG_PKG_USING_LIBSODIUM is not set +# CONFIG_PKG_USING_LIBHYDROGEN 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 +# + +# +# JSON: JavaScript Object Notation, a lightweight data-interchange format +# +# CONFIG_PKG_USING_CJSON is not set +# CONFIG_PKG_USING_LJSON is not set +# CONFIG_PKG_USING_RT_CJSON_TOOLS is not set +# CONFIG_PKG_USING_RAPIDJSON is not set +# CONFIG_PKG_USING_JSMN is not set +# CONFIG_PKG_USING_AGILE_JSMN is not set +# CONFIG_PKG_USING_PARSON is not set + +# +# XML: Extensible Markup Language +# +# CONFIG_PKG_USING_SIMPLE_XML is not set +# CONFIG_PKG_USING_EZXML is not set +# CONFIG_PKG_USING_LUATOS_SOC is not set +# CONFIG_PKG_USING_LUA is not set +# CONFIG_PKG_USING_JERRYSCRIPT is not set +# CONFIG_PKG_USING_MICROPYTHON is not set +# CONFIG_PKG_USING_PIKASCRIPT is not set +# CONFIG_PKG_USING_RTT_RUST is not set + +# +# multimedia packages +# + +# +# LVGL: powerful and easy-to-use embedded GUI library +# +# CONFIG_PKG_USING_LVGL is not set +# CONFIG_PKG_USING_LITTLEVGL2RTT is not set +# CONFIG_PKG_USING_LV_MUSIC_DEMO is not set +# CONFIG_PKG_USING_GUI_GUIDER_DEMO is not set + +# +# u8g2: a monochrome graphic library +# +# CONFIG_PKG_USING_U8G2_OFFICIAL is not set +# CONFIG_PKG_USING_U8G2 is not set +# 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 +# CONFIG_PKG_USING_NUEMWIN is not set +# CONFIG_PKG_USING_MP3PLAYER is not set +# CONFIG_PKG_USING_TINYJPEG is not set +# CONFIG_PKG_USING_UGUI is not set + +# +# PainterEngine: A cross-platform graphics application framework written in C language +# +# CONFIG_PKG_USING_PAINTERENGINE is not set +# CONFIG_PKG_USING_PAINTERENGINE_AUX is not set +# CONFIG_PKG_USING_MCURSES is not set +# CONFIG_PKG_USING_TERMBOX is not set +# CONFIG_PKG_USING_VT100 is not set +# CONFIG_PKG_USING_QRCODE is not set +# CONFIG_PKG_USING_GUIENGINE 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_SEGGER_RTT is not set +# CONFIG_PKG_USING_RDB 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_REGEX is not set +# CONFIG_PKG_USING_MEM_SANDBOX is not set +# CONFIG_PKG_USING_SOLAR_TERMS is not set +# CONFIG_PKG_USING_GAN_ZHI is not set +# CONFIG_PKG_USING_FDT is not set +# CONFIG_PKG_USING_CBOX is not set +# CONFIG_PKG_USING_SNOWFLAKE is not set +# CONFIG_PKG_USING_HASH_MATCH is not set +# CONFIG_PKG_USING_FIRE_PID_CURVE is not set +# CONFIG_PKG_USING_ARMV7M_DWT_TOOL is not set +# CONFIG_PKG_USING_VOFA_PLUS is not set + +# +# system packages +# + +# +# enhanced kernel services +# +# CONFIG_PKG_USING_RT_MEMCPY_CM is not set +# CONFIG_PKG_USING_RT_KPRINTF_THREADSAFE is not set +# CONFIG_PKG_USING_RT_VSNPRINTF_FULL is not set + +# +# acceleration: Assembly language or algorithmic acceleration packages +# +# 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 + +# +# CMSIS: ARM Cortex-M Microcontroller Software Interface Standard +# +# CONFIG_PKG_USING_CMSIS_5 is not set +# CONFIG_PKG_USING_CMSIS_RTOS1 is not set +# CONFIG_PKG_USING_CMSIS_RTOS2 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_FREERTOS_WRAPPER 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_PERF_COUNTER 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_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 +# CONFIG_PKG_USING_PPOOL is not set +# CONFIG_PKG_USING_OPENAMP 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 +# CONFIG_PKG_USING_ARM_2D is not set +# CONFIG_PKG_USING_MCUBOOT is not set +# CONFIG_PKG_USING_TINYUSB is not set +# CONFIG_PKG_USING_CHERRYUSB is not set +# CONFIG_PKG_USING_KMULTI_RTIMER is not set +# CONFIG_PKG_USING_TFDB is not set +# CONFIG_PKG_USING_QPC is not set +# CONFIG_PKG_USING_AGILE_UPGRADE 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_ADT74XX is not set +# CONFIG_PKG_USING_AS7341 is not set +# CONFIG_PKG_USING_STM32_SDIO is not set +# CONFIG_PKG_USING_ESP_IDF is not set +# CONFIG_PKG_USING_ICM20608 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 + +# +# Kendryte SDK +# +# CONFIG_PKG_USING_K210_SDK is not set +# CONFIG_PKG_USING_KENDRYTE_SDK is not set +# CONFIG_PKG_USING_INFRARED is not set +# CONFIG_PKG_USING_MULTI_INFRARED 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_RS232 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 +# CONFIG_PKG_USING_TCA9534 is not set +# CONFIG_PKG_USING_KOBUKI is not set +# CONFIG_PKG_USING_ROSSERIAL is not set +# CONFIG_PKG_USING_MICRO_ROS is not set +# CONFIG_PKG_USING_MCP23008 is not set +# CONFIG_PKG_USING_BLUETRUM_SDK is not set +# CONFIG_PKG_USING_MISAKA_AT24CXX is not set +# CONFIG_PKG_USING_MISAKA_RGB_BLING is not set +# CONFIG_PKG_USING_LORA_MODEM_DRIVER is not set +# CONFIG_PKG_USING_BL_MCU_SDK is not set +# CONFIG_PKG_USING_SOFT_SERIAL is not set +# CONFIG_PKG_USING_MB85RS16 is not set +# CONFIG_PKG_USING_CW2015 is not set +# CONFIG_PKG_USING_RFM300 is not set +# CONFIG_PKG_USING_IO_INPUT_FILTER is not set +# CONFIG_PKG_USING_RASPBERRYPI_PICO_SDK is not set +# CONFIG_PKG_USING_LRF_NV7LIDAR 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 +# + +# +# project laboratory +# + +# +# 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 + +# +# entertainment: terminal games and other interesting software packages +# +# CONFIG_PKG_USING_CMATRIX is not set +# CONFIG_PKG_USING_SL is not set +# CONFIG_PKG_USING_CAL is not set +# CONFIG_PKG_USING_ACLOCK is not set +# 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_COWSAY is not set +# 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_MINIZIP is not set +# CONFIG_PKG_USING_HEATSHRINK 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 +# 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_UKAL is not set +# CONFIG_PKG_USING_CRCLIB is not set +# CONFIG_PKG_USING_LWGPS is not set +# CONFIG_PKG_USING_STATE_MACHINE is not set +# CONFIG_PKG_USING_DESIGN_PATTERN is not set +# CONFIG_PKG_USING_CONTROLLER is not set +# CONFIG_PKG_USING_PHASE_LOCKED_LOOP is not set +# CONFIG_PKG_USING_MFBD is not set +# CONFIG_PKG_USING_SLCAN2RTT is not set +# CONFIG_PKG_USING_SOEM is not set +# CONFIG_PKG_USING_QPARAM is not set + +# +# Arduino libraries +# +# CONFIG_PKG_USING_RTDUINO is not set + +# +# Projects +# +# CONFIG_PKG_USING_ARDUINO_ULTRASOUND_RADAR is not set +# CONFIG_PKG_USING_ARDUINO_SENSOR_KIT is not set +# CONFIG_PKG_USING_ARDUINO_MATLAB_SUPPORT is not set + +# +# Sensors +# +# CONFIG_PKG_USING_ARDUINO_SEEED_BMP280 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADXL375 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VL53L0X is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_LIS3DHTR is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_DHT is not set +# CONFIG_PKG_USING_ARDUINO_CAPACITIVESENSOR is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SENSOR is not set +# CONFIG_PKG_USING_ADAFRUIT_MAX31855 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX31865 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX31856 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90614 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM9DS1 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AHTX0 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM9DS0 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP280 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADT7410 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP085 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BME680 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP9808 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP4728 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_INA219 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LTR390 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADXL345 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DHT is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP9600 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM6DS is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BNO055 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX1704X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MMC56X3 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90393 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90395 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ICM20X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DPS310 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HTS221 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SHT4X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SHT31 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADXL343 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BME280 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AS726X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AMG88XX is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AM2320 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AM2315 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LTR329_LTR303 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP085_UNIFIED is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP183 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP183_UNIFIED is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP3XX is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MS8607 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS3MDL is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90640 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MMA8451 is not set +# CONFIG_PKG_USING_ADAFRUIT_MSA301 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPL115A2 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BNO08X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BNO08X_RVC is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS2MDL is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM303DLH_MAG is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LC709203F is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_CAP1188 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_CCS811 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_NAU7802 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS331 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LPS2X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LPS35HW is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM303_ACCEL is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS3DH is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCF8591 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPL3115A2 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPR121 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPRLS is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPU6050 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCT2075 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PM25AQI is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_EMC2101 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_FXAS21002C is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SCD30 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_FXOS8700 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HMC5883_UNIFIED is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SGP30 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TMP006 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TLA202X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TCS34725 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI7021 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI1145 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SGP40 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SHTC3 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HDC1000 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HTU21DF is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AS7341 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HTU31D is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SENSORLAB is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_INA260 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TMP007_LIBRARY is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_L3GD20 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TMP117 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TSC2007 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TSL2561 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TSL2591_LIBRARY is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VCNL4040 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VEML6070 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VEML6075 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VEML7700 is not set + +# +# Display +# +# CONFIG_PKG_USING_ARDUINO_U8G2 is not set + +# +# Timing +# +# CONFIG_PKG_USING_ARDUINO_MSTIMER2 is not set + +# +# Data Processing +# +# CONFIG_PKG_USING_ARDUINO_KALMANFILTER is not set +# CONFIG_PKG_USING_ARDUINO_ARDUINOJSON is not set + +# +# Data Storage +# + +# +# Communication +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PN532 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI4713 is not set + +# +# Device Control +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCF8574 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCA9685 is not set + +# +# Other +# + +# +# Signal IO +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BUSIO is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TCA8418 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP23017 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADS1X15 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AW9523 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP3008 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP4725 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BD3491FS is not set + +# +# Uncategorized +# + +# +# Hardware Drivers +# + +# +# On-chip Peripheral Drivers +# +CONFIG_BSP_USING_UART=y +CONFIG_RT_USING_UART1=y +# CONFIG_RT_USING_UART0 is not set + +# +# Board extended module Drivers +# +CONFIG_BSP_USING_GIC=y +CONFIG_BSP_USING_GICV3=y +CONFIG_PHYTIUM_ARCH_AARCH64=y + +# +# Standalone Setting +# +CONFIG_TARGET_ARMV8_AARCH64=y + +# +# Board Configuration +# +# CONFIG_TARGET_F2000_4 is not set +# CONFIG_TARGET_D2000 is not set +CONFIG_TARGET_E2000Q=y +# CONFIG_TARGET_E2000D is not set +# CONFIG_TARGET_E2000S is not set +CONFIG_TARGET_E2000=y +CONFIG_DEFAULT_DEBUG_PRINT_UART1=y +# CONFIG_DEFAULT_DEBUG_PRINT_UART0 is not set +# CONFIG_DEFAULT_DEBUG_PRINT_UART2 is not set + +# +# Components Configuration +# +# CONFIG_USE_SPI is not set +# CONFIG_USE_QSPI is not set +# CONFIG_USE_GIC is not set +CONFIG_USE_SERIAL=y + +# +# Usart Configuration +# +CONFIG_ENABLE_Pl011_UART=y +# CONFIG_USE_GPIO is not set +# CONFIG_USE_ETH is not set +# CONFIG_USE_CAN is not set +# CONFIG_USE_I2C is not set +# CONFIG_USE_TIMER is not set +# CONFIG_USE_MIO is not set +# CONFIG_USE_SDMMC is not set +# CONFIG_USE_PCIE is not set +# CONFIG_USE_WDT is not set +# CONFIG_USE_DMA is not set +# CONFIG_USE_NAND is not set +# CONFIG_USE_RTC is not set +# CONFIG_USE_SATA is not set +# CONFIG_USE_USB is not set +# CONFIG_USE_ADC is not set +# CONFIG_USE_PWM is not set +# CONFIG_USE_IPC is not set +# CONFIG_LOG_VERBOS is not set +# CONFIG_LOG_DEBUG is not set +# CONFIG_LOG_INFO is not set +# CONFIG_LOG_WARN is not set +CONFIG_LOG_ERROR=y +# CONFIG_LOG_NONE is not set +# CONFIG_USE_DEFAULT_INTERRUPT_CONFIG is not set +# CONFIG_LOG_EXTRA_INFO is not set +# CONFIG_BOOTUP_DEBUG_PRINTS is not set diff --git a/bsp/phytium/aarch64/Kconfig b/bsp/phytium/aarch64/Kconfig new file mode 100644 index 0000000000..973bc46fe1 --- /dev/null +++ b/bsp/phytium/aarch64/Kconfig @@ -0,0 +1,55 @@ +mainmenu "RT-Thread Project Configuration" + +config RTT_DIR + string + option env="RTT_ROOT" + default "../../.." + +config BSP_DIR + string + option env="BSP_ROOT" + default "../." + +config STANDALONE_DIR + string + option env="STANDALONE_DIR" + default ".././libraries/standalone" + +config PKGS_DIR + string + option env="PKGS_ROOT" + default "packages" + +source "$RTT_DIR/Kconfig" +source "$PKGS_DIR/Kconfig" +source "$BSP_DIR/libraries/drivers/Kconfig" + +config BSP_USING_GIC + bool + default y + config BSP_USING_GICV3 + bool + default y + +config PHYTIUM_ARCH_AARCH64 + bool + select ARCH_ARMV8 + select RT_USING_COMPONENTS_INIT + select RT_USING_USER_MAIN + select ARCH_CPU_64BIT + select TARGET_ARMV8_AARCH64 + default y + +menu "Standalone Setting" + config TARGET_ARMV8_AARCH64 + bool "Armv8 Aarch64" + default y + + source "$STANDALONE_DIR/board/Kconfig" + source "$STANDALONE_DIR/drivers/Kconfig" + source "$STANDALONE_DIR/common/Kconfig" + +endmenu + + + diff --git a/bsp/phytium/aarch64/README.md b/bsp/phytium/aarch64/README.md new file mode 100644 index 0000000000..8ca947b5d8 --- /dev/null +++ b/bsp/phytium/aarch64/README.md @@ -0,0 +1,121 @@ + + +# AARCH64 工作模式使用 + +- 当开发者需要基于 Phytium 系列芯片进行开发时,可以从以下几个步骤出发配置芯片 + +## 1. 如何选择芯片 + +```shell + scons --menuconfig +``` + +开发者通过以下选择进行配置 + +``` +Standalone Setting > Board Configuration > Chip +``` + +![](./figures/chip_select.png) +![](./figures/phytium_cpu_select.png) + +## 2. 如何选择驱动 + + +```shell + scons --menuconfig +``` + +开发者通过以下选项进行驱动的使能 + +``` +Hardware Drivers Config > On-chip Peripheral Drivers +``` + +![](./figures/select_driver.png) + + +## 3. 开启SDK中内部调试信息 + + +```shell + scons --menuconfig +``` + +开发者通过以下选项进行调试信息等级的设置 + +![](./figures/select_debug_info.png) + + + +## 4. 编译程序 + +```shell + scons -c + scons +``` + +- 完成编译之后目录下将会生成以下几个文件 + +``` +rtthread_a64.bin +rtthread_a64.elf +rtthread_a64.map +``` + +## 5. 打包导出工程源代码 + + +- 指定工程名和路径,打包RT-Thread内核和Phytium BSP代码,可以导出一个工程工程 +``` +python ./export_project.py -n=phytium-a64 -o=D:/proj/rt-thread-e2000/phytium-a64 +``` + +![](./figures/export_project.png) + + +- 进入打包工程的目录,修改工程根目录 Kconfig 中的路径 BSP_DIR 和 STANDALONE_DIR +> env 环境中的 menuconfig 不会调用 SConstruct 修改路径环境变量,因此需要手动修改路径 + +``` +config BSP_DIR + string + option env="BSP_ROOT" + default "." + +config STANDALONE_DIR + string + option env="STANDALONE_DIR" + default "libraries/standalone" +``` + +- 输入 menuconfig 和 scons 完成编译 + + +## 6. 将工程导入 RT-Studio + +- 在 RT-Studio 使用功能`RT-Thread Bsp 到工作空间`,导入 5. 中导出的 BSP 工程 +- 设置 BSP 工程的交叉编译链后进行后续开发 + +![](./figures/import_project.png) \ No newline at end of file diff --git a/bsp/phytium/aarch64/SConscript b/bsp/phytium/aarch64/SConscript new file mode 100644 index 0000000000..4c815c49b8 --- /dev/null +++ b/bsp/phytium/aarch64/SConscript @@ -0,0 +1,15 @@ +# RT-Thread building script for bridge + +import os +from building import * + +cwd = GetCurrentDir() +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/phytium/aarch64/SConstruct b/bsp/phytium/aarch64/SConstruct new file mode 100644 index 0000000000..ae659dfb21 --- /dev/null +++ b/bsp/phytium/aarch64/SConstruct @@ -0,0 +1,58 @@ +import os +import sys +import rtconfig + +IS_EXPORTED = False + +# setup RT-Thread Root Path +if os.getenv('RTT_ROOT'): + RTT_ROOT = os.getenv('RTT_ROOT') +else: + RTT_ROOT = os.getcwd() + '/../../..' + +sys.path = sys.path + [os.path.join(RTT_ROOT, 'tools')] +try: + from building import * +except: + print('Cannot found RT-Thread root directory, please check RTT_ROOT') + print(RTT_ROOT) + exit(-1) + +if RTT_ROOT == 'rt-thread': + IS_EXPORTED = True # if kenrel and bsp has been exported by export_project.py + +# setup Phytium BSP Root Path +if IS_EXPORTED: + BSP_ROOT = '.' +else: + BSP_ROOT = RTT_ROOT + '/bsp/phytium' + +TARGET = 'rtthread_a64.' + rtconfig.TARGET_EXT + +DefaultEnvironment(tools=[]) +env = Environment(tools = ['mingw'], + AS = rtconfig.AS, ASFLAGS = rtconfig.AFLAGS, + CC = rtconfig.CC, CFLAGS = rtconfig.CFLAGS, + CXX = rtconfig.CXX, CXXFLAGS = rtconfig.CXXFLAGS, + 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('BSP_ROOT') +Export('rtconfig') + +# prepare building environment +objs = PrepareBuilding(env, RTT_ROOT, has_libcpu = False) + +if not IS_EXPORTED: # if project is not exported, libraries and board need to manually add + # include libraries + objs.extend(SConscript(os.path.join(BSP_ROOT + '/libraries', 'SConscript'))) + + # include board + objs.extend(SConscript(os.path.join(BSP_ROOT + '/board', 'SConscript'))) + +# make a building +DoBuilding(TARGET, objs) + diff --git a/bsp/phytium/aarch64/applications/SConscript b/bsp/phytium/aarch64/applications/SConscript new file mode 100644 index 0000000000..01eb940dfb --- /dev/null +++ b/bsp/phytium/aarch64/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/phytium/aarch64/applications/main.c b/bsp/phytium/aarch64/applications/main.c new file mode 100644 index 0000000000..96c5ad2dc0 --- /dev/null +++ b/bsp/phytium/aarch64/applications/main.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Email: opensource_embedded@phytium.com.cn + * + * Change Logs: + * Date Author Notes + * 2022-10-26 huanghe first commit + * + */ + +#include +#include + +#include + +#ifdef RT_USING_SMP + +struct rt_thread test_core[RT_CPUS_NR]; +static char *core_thread_name[8] = +{ + "core0_test", + "core1_test", + "core2_test", + "core3_test", + "core4_test", + "core5_test", + "core6_test", + "core7_test" +}; +static rt_uint8_t core_stack[RT_CPUS_NR][1024]; + +static void demo_core_thread(void *parameter) +{ + rt_base_t level; + while (1) + { + /* code */ + level = rt_cpus_lock(); + rt_kprintf("Hi, core%d \r\n", rt_hw_cpu_id()); + rt_cpus_unlock(level); + rt_thread_mdelay(2000000); + } +} + +void demo_core(void) +{ + rt_ubase_t i; + rt_ubase_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 + +int main(void) +{ +#ifdef RT_USING_SMP + demo_core(); +#endif + return RT_EOK; +} + diff --git a/bsp/phytium/aarch64/export_project.py b/bsp/phytium/aarch64/export_project.py new file mode 100644 index 0000000000..d4c690c7cb --- /dev/null +++ b/bsp/phytium/aarch64/export_project.py @@ -0,0 +1,37 @@ +import os +import shutil +import argparse + +parser = argparse.ArgumentParser() +parser.description='please enter two parameters and ...' +parser.add_argument("-n", "--name", help="project name", type=str, default="phytium-a64") +parser.add_argument("-o", "--output", help="export path", type=str, default="./phytium-a64") +args = parser.parse_args() + +print('=== Exporting Phytium BSP for RT-Studio ====') + +board_src_path = os.path.abspath(r'../board') +librs_src_path = os.path.abspath(r'../libraries') + +board_dst_path = os.path.abspath(r'./board') +librs_dst_path = os.path.abspath(r'./libraries') + +print(' Copying BSP board from {} to {}'.format(board_src_path, board_dst_path)) +print(' Copying BSP libraries from {} to {}'.format(librs_src_path, librs_dst_path)) + +if os.path.exists(board_dst_path): + shutil.rmtree(board_dst_path) + +if os.path.exists(librs_dst_path): + shutil.rmtree(librs_dst_path) + +shutil.copytree(board_src_path, board_dst_path) +shutil.copytree(librs_src_path, librs_dst_path) + +os.system('scons --dist-ide --project-name={} --project-path={}'.format(args.name, args.output)) + +if os.path.exists(board_dst_path): + shutil.rmtree(board_dst_path) + +if os.path.exists(librs_dst_path): + shutil.rmtree(librs_dst_path) \ No newline at end of file diff --git a/bsp/phytium/aarch64/figures/chip_select.png b/bsp/phytium/aarch64/figures/chip_select.png new file mode 100644 index 0000000000000000000000000000000000000000..26878fe2355a9095c34b3a4305a861e157cf49a4 GIT binary patch literal 45417 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 literal 0 HcmV?d00001 diff --git a/bsp/phytium/aarch64/figures/import_project.png b/bsp/phytium/aarch64/figures/import_project.png new file mode 100644 index 0000000000000000000000000000000000000000..5aa91d058ecfdc97ef53b1b303a0282ec3b00e20 GIT binary patch literal 22542 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 z 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 G{eJ*YW_wNm literal 0 HcmV?d00001 diff --git a/bsp/phytium/aarch64/figures/select_debug_info.png b/bsp/phytium/aarch64/figures/select_debug_info.png new file mode 100644 index 0000000000000000000000000000000000000000..8d224369405afb948e93467bd23f15fc923ac824 GIT binary patch literal 32841 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 Msf)kjtlaPXFZ`)%<^TWy literal 0 HcmV?d00001 diff --git a/bsp/phytium/aarch64/figures/select_driver.png b/bsp/phytium/aarch64/figures/select_driver.png new file mode 100644 index 0000000000000000000000000000000000000000..3fa21b7e8dfaadc79b39db73ad2ddb7223a67568 GIT binary patch literal 35393 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 literal 0 HcmV?d00001 diff --git a/bsp/phytium/aarch64/link.lds b/bsp/phytium/aarch64/link.lds new file mode 100644 index 0000000000..86bb78e88e --- /dev/null +++ b/bsp/phytium/aarch64/link.lds @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * 2017-5-30 bernard first version + */ + +/* _EL1_STACK_SIZE = DEFINED(_EL1_STACK_SIZE) ? _EL1_STACK_SIZE : 0x20000; */ + +SECTIONS +{ + . = 0x80100000; + . = ALIGN(4096); + .text : + { + KEEP(*(.text.entrypoint)) /* The entry point */ + *(.vectors) + *(.text) /* remaining code */ + *(.text.*) /* remaining code */ + + *(.rodata) /* read-only data (constants) */ + *(.rodata*) + *(.glue_7) + *(.glue_7t) + *(.gnu.linkonce.t*) + + *(COMMON) + + /* section information for finsh shell */ + . = ALIGN(16); + __fsymtab_start = .; + KEEP(*(FSymTab)) + __fsymtab_end = .; + . = ALIGN(16); + __vsymtab_start = .; + KEEP(*(VSymTab)) + __vsymtab_end = .; + . = ALIGN(16); + + /* section information for initial. */ + . = ALIGN(16); + __rt_init_start = .; + KEEP(*(SORT(.rti_fn*))) + __rt_init_end = .; + . = ALIGN(16); + + . = ALIGN(16); + _etext = .; + } + . = ALIGN(4); + .eh_frame_hdr : + { + *(.eh_frame_hdr) + *(.eh_frame_entry) + } + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) } + + . = ALIGN(16); + .data : + { + *(.data) + *(.data.*) + + *(.data1) + *(.data1.*) + + . = ALIGN(16); + _gp = ABSOLUTE(.); /* Base of small data */ + + *(.sdata) + *(.sdata.*) + } + + . = ALIGN(16); + .ctors : + { + PROVIDE(__ctors_start__ = .); + /* new GCC version uses .init_array */ + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE(__ctors_end__ = .); + } + . = ALIGN(4); + .dtors : + { + PROVIDE(__dtors_start__ = .); + KEEP(*(SORT(.dtors.*))) + KEEP(*(.dtors)) + PROVIDE(__dtors_end__ = .); + } + + . = ALIGN(16); + .bss : + { + PROVIDE(__bss_start = .); + *(.bss) + *(.bss.*) + *(.dynbss) + . = ALIGN(32); + PROVIDE(__bss_end = .); + } + + . = ALIGN(4); + .heap : + { + PROVIDE(__heap_start = .); + . = ALIGN(8); + PROVIDE(end = .); + } + + _end = .; + + /* 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) } + /* DWARF debug sections. + * Symbols in the DWARF debugging sections are relative to the beginning + * of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } +} + +__bss_size = SIZEOF(.bss); \ No newline at end of file diff --git a/bsp/phytium/aarch64/rtconfig.h b/bsp/phytium/aarch64/rtconfig.h new file mode 100644 index 0000000000..c72b07bd19 --- /dev/null +++ b/bsp/phytium/aarch64/rtconfig.h @@ -0,0 +1,260 @@ +#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 16 +#define RT_USING_SMP +#define RT_CPUS_NR 4 +#define RT_ALIGN_SIZE 4 +#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_HOOK_USING_FUNC_PTR +#define RT_USING_IDLE_HOOK +#define RT_IDLE_HOOK_LIST_SIZE 4 +#define IDLE_THREAD_STACK_SIZE 4096 +#define SYSTEM_THREAD_STACK_SIZE 4096 +#define RT_USING_TIMER_SOFT +#define RT_TIMER_THREAD_PRIO 4 +#define RT_TIMER_THREAD_STACK_SIZE 4096 + +/* kservice optimization */ + +#define RT_KSERVICE_USING_STDLIB +#define RT_KPRINTF_USING_LONGLONG +#define RT_DEBUG + +/* 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_MEMPOOL +#define RT_USING_SMALL_MEM +#define RT_USING_SMALL_MEM_AS_HEAP +#define RT_USING_HEAP + +/* Kernel Device Object */ + +#define RT_USING_DEVICE +#define RT_USING_CONSOLE +#define RT_CONSOLEBUF_SIZE 128 +#define RT_CONSOLE_DEVICE_NAME "uart1" +#define RT_VER_NUM 0x50000 +#define ARCH_CPU_64BIT +#define ARCH_ARMV8 + +/* RT-Thread Components */ + +#define RT_USING_COMPONENTS_INIT +#define RT_USING_USER_MAIN +#define RT_MAIN_THREAD_STACK_SIZE 4096 +#define RT_MAIN_THREAD_PRIORITY 10 +#define RT_USING_MSH +#define RT_USING_FINSH +#define FINSH_USING_MSH +#define FINSH_THREAD_NAME "tshell" +#define FINSH_THREAD_PRIORITY 20 +#define FINSH_THREAD_STACK_SIZE 4096 +#define FINSH_USING_HISTORY +#define FINSH_HISTORY_LINES 5 +#define FINSH_USING_SYMTAB +#define FINSH_CMD_SIZE 80 +#define MSH_USING_BUILT_IN_COMMANDS +#define FINSH_USING_DESCRIPTION +#define FINSH_ARG_MAX 10 +#define RT_USING_DFS +#define DFS_USING_POSIX +#define DFS_USING_WORKDIR +#define DFS_FILESYSTEMS_MAX 4 +#define DFS_FILESYSTEM_TYPES_MAX 4 +#define DFS_FD_MAX 16 + +/* Device Drivers */ + +#define RT_USING_DEVICE_IPC +#define RT_USING_SYSTEM_WORKQUEUE +#define RT_SYSTEM_WORKQUEUE_STACKSIZE 8192 +#define RT_SYSTEM_WORKQUEUE_PRIORITY 23 +#define RT_USING_SERIAL +#define RT_USING_SERIAL_V1 +#define RT_SERIAL_USING_DMA +#define RT_SERIAL_RB_BUFSZ 64 +#define RT_USING_PIN + +/* Using USB */ + + +/* C/C++ and POSIX layer */ + +#define RT_LIBC_DEFAULT_TIMEZONE 8 + +/* POSIX (Portable Operating System Interface) layer */ + + +/* Interprocess Communication (IPC) */ + + +/* Socket is in the 'Network' category */ + + +/* Network */ + + +/* Utilities */ + + +/* RT-Thread Utestcases */ + + +/* RT-Thread online packages */ + +/* IoT - internet of things */ + + +/* Wi-Fi */ + +/* Marvell WiFi */ + + +/* Wiced WiFi */ + + +/* IoT Cloud */ + + +/* security packages */ + + +/* language packages */ + +/* JSON: JavaScript Object Notation, a lightweight data-interchange format */ + + +/* XML: Extensible Markup Language */ + + +/* multimedia packages */ + +/* LVGL: powerful and easy-to-use embedded GUI library */ + + +/* u8g2: a monochrome graphic library */ + + +/* PainterEngine: A cross-platform graphics application framework written in C language */ + + +/* tools packages */ + + +/* system packages */ + +/* enhanced kernel services */ + + +/* acceleration: Assembly language or algorithmic acceleration packages */ + + +/* CMSIS: ARM Cortex-M Microcontroller Software Interface Standard */ + + +/* Micrium: Micrium software products porting for RT-Thread */ + + +/* peripheral libraries and drivers */ + + +/* Kendryte SDK */ + + +/* AI packages */ + + +/* miscellaneous packages */ + +/* project laboratory */ + +/* samples: kernel and components samples */ + + +/* entertainment: terminal games and other interesting software packages */ + + +/* Arduino libraries */ + + +/* Projects */ + + +/* Sensors */ + + +/* Display */ + + +/* Timing */ + + +/* Data Processing */ + + +/* Data Storage */ + +/* Communication */ + + +/* Device Control */ + + +/* Other */ + +/* Signal IO */ + + +/* Uncategorized */ + +/* Hardware Drivers */ + +/* On-chip Peripheral Drivers */ + +#define BSP_USING_UART +#define RT_USING_UART1 + +/* Board extended module Drivers */ + +#define BSP_USING_GIC +#define BSP_USING_GICV3 +#define PHYTIUM_ARCH_AARCH64 + +/* Standalone Setting */ + +#define TARGET_ARMV8_AARCH64 + +/* Board Configuration */ + +#define TARGET_E2000Q +#define TARGET_E2000 +#define DEFAULT_DEBUG_PRINT_UART1 + +/* Components Configuration */ + +#define USE_SERIAL + +/* Usart Configuration */ + +#define ENABLE_Pl011_UART +#define LOG_ERROR + +#endif diff --git a/bsp/phytium/aarch64/rtconfig.py b/bsp/phytium/aarch64/rtconfig.py new file mode 100644 index 0000000000..9bb9671efb --- /dev/null +++ b/bsp/phytium/aarch64/rtconfig.py @@ -0,0 +1,52 @@ +import os + +# toolchains options +ARCH ='aarch64' +CPU ='cortex-a' +CROSS_TOOL ='gcc' + +if os.getenv('RTT_ROOT'): + RTT_ROOT = os.getenv('RTT_ROOT') +else: + RTT_ROOT = r'../../..' + +PLATFORM = 'gcc' +EXEC_PATH = r'/opt/gcc-arm-8.3-2019.03-x86_64-aarch64-elf/bin/' +if os.getenv('AARCH64_CROSS_PATH'): + EXEC_PATH = os.getenv('AARCH64_CROSS_PATH') + print('EXEC_PATH = {}'.format(EXEC_PATH)) +else: + print('AARCH64_CROSS_PATH not found') + +BUILD = 'debug' + +if PLATFORM == 'gcc': + # toolchains + PREFIX = 'aarch64-none-elf-' + 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' + + DEVICE = ' -march=armv8-a -mtune=cortex-a72' + CFLAGS = DEVICE + ' -Wall' + AFLAGS = ' -c' + ' -x assembler-with-cpp -D__ASSEMBLY__' + LFLAGS = DEVICE + ' -nostartfiles -Wl,--gc-sections,-Map=rtthread_a64.map,-cref,-u,system_vectors -T link.lds -fdiagnostics-color=always' + CPATH = '' + LPATH = '' + + if BUILD == 'debug': + CFLAGS += ' -O0 -gdwarf-2' + AFLAGS += ' -gdwarf-2' + else: + CFLAGS += ' -O2' + + CXXFLAGS = CFLAGS + +DUMP_ACTION = OBJDUMP + ' -D -S $TARGET > rtthread_a64.dis\n' +POST_ACTION = OBJCPY + ' -O binary $TARGET rtthread_a64.bin\n' + SIZE + ' $TARGET \n' diff --git a/bsp/phytium/aarch64/sdkconfig.h b/bsp/phytium/aarch64/sdkconfig.h new file mode 100644 index 0000000000..d799f85053 --- /dev/null +++ b/bsp/phytium/aarch64/sdkconfig.h @@ -0,0 +1,71 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: sdkconfig.h + * Date: 2022-10-09 15:04:36 + * LastEditTime: 2022-10-09 15:04:37 + * Description: This file is for + * + * Modify History: + * Ver Who Date Changes + * ----- ------ -------- -------------------------------------- + */ + +#ifndef SDK_CONFIG_H__ +#define SDK_CONFIG_H__ + +#include "rtconfig.h" + +/* board */ + +/* E2000 */ + +#if defined(TARGET_E2000) + #define CONFIG_TARGET_E2000 +#endif + +#if defined(TARGET_E2000Q) + #define CONFIG_TARGET_E2000Q +#endif + +#if defined(TARGET_ARMV8_AARCH64) + #define CONFIG_TARGET_ARMV8_AARCH64 +#endif + +/* debug */ + +#ifdef LOG_VERBOS + #define CONFIG_LOG_VERBOS +#endif + +#ifdef LOG_ERROR + #define CONFIG_LOG_ERROR +#endif + +#ifdef LOG_WARN + #define CONFIG_LOG_WARN +#endif + +#ifdef LOG_INFO + #define CONFIG_LOG_INFO +#endif + +#ifdef LOG_DEBUG + #define CONFIG_LOG_DEBUG +#endif + +#ifdef BOOTUP_DEBUG_PRINTS + #define CONFIG_BOOTUP_DEBUG_PRINTS +#endif + +#endif diff --git a/bsp/phytium/board/SConscript b/bsp/phytium/board/SConscript new file mode 100644 index 0000000000..c86f3413fb --- /dev/null +++ b/bsp/phytium/board/SConscript @@ -0,0 +1,25 @@ +from building import * + +cwd = GetCurrentDir() +src = Glob('*.S') +src += Glob('*.c') + +if GetDepend(['TARGET_E2000']): + if GetDepend(['TARGET_E2000Q']): + src += Glob(cwd + '/e2000/q/parameters.c') + elif GetDepend(['TARGET_E2000D']): + src += Glob(cwd + '/e2000/d/parameters.c') + elif GetDepend(['TARGET_E2000S']): + src += Glob(cwd + '/e2000/s/parameters.c') + +if GetDepend(['TARGET_F2000_4']): + src += Glob(cwd + '/d2000/parameters.c') + +if GetDepend(['TARGET_D2000']): + src += Glob(cwd + '/ft2004/parameters.c') + +CPPPATH = [cwd] + +group = DefineGroup('Board', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/phytium/board/board.c b/bsp/phytium/board/board.c new file mode 100644 index 0000000000..b02fd10b13 --- /dev/null +++ b/bsp/phytium/board/board.c @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Email: opensource_embedded@phytium.com.cn + * + * Change Logs: + * Date Author Notes + * 2022-10-26 huanghe first commit + * 2022-10-26 zhugengyu support aarch64 + * + */ + +#include "rtconfig.h" +#include +#include + +#include + +#include +#if defined(TARGET_ARMV8_AARCH64) + #include + #include + #include +#else + #include "fgeneric_timer.h" /* for aarch32 */ +#endif +#include +#include + +#include "fdebug.h" +#include "fprintk.h" +#include "fearly_uart.h" +#include "fcpu_info.h" +#include "fpsci.h" + +#define LOG_DEBUG_TAG "BOARD" +#define BSP_LOG_ERROR(format, ...) FT_DEBUG_PRINT_E(LOG_DEBUG_TAG, format, ##__VA_ARGS__) +#define BSP_LOG_WARN(format, ...) FT_DEBUG_PRINT_W(LOG_DEBUG_TAG, format, ##__VA_ARGS__) +#define BSP_LOG_INFO(format, ...) FT_DEBUG_PRINT_I(LOG_DEBUG_TAG, format, ##__VA_ARGS__) +#define BSP_LOG_DEBUG(format, ...) FT_DEBUG_PRINT_D(LOG_DEBUG_TAG, format, ##__VA_ARGS__) + +/* mmu config */ +struct mem_desc platform_mem_desc[] = +#if defined(TARGET_E2000) +{ + { + 0x00U, + 0x00U + 0x40000000U, + 0x00U, + DEVICE_MEM + }, + { + 0x40000000U, + 0x40000000U + 0x10000000U, + 0x40000000U, + DEVICE_MEM + }, + { + 0x50000000U, + 0x50000000U + 0x30000000U, + 0x50000000U, + DEVICE_MEM + }, + { + 0x80000000U, + 0xffffffffU, + 0x80000000U, + NORMAL_MEM + }, +#if defined(TARGET_ARMV8_AARCH64) + { + 0x1000000000, + 0x1000000000 + 0x1000000000, + 0x1000000000, + DEVICE_MEM + }, + { + 0x2000000000, + 0x2000000000 + 0x2000000000, + 0x2000000000, + NORMAL_MEM + }, +#endif +}; +#elif defined(TARGET_F2000_4) || defined(TARGET_D2000) +{ + { + 0x80000000, + 0xFFFFFFFF, + 0x80000000, + DDR_MEM + }, + { + 0, //< QSPI + 0x1FFFFFFF, + 0, + DEVICE_MEM + }, + { + 0x20000000, // + +#include "fcpu_info.h" +#include "fparameters.h" + + +/** + * @name: GetCpuMaskToAffval + * @msg: Convert information in cpu_mask to cluster_ID and target_list + * @param {u32} *cpu_mask is each bit of cpu_mask represents a selected CPU, for example, 0x3 represents core0 and CORE1 . + * @param {u32} *cluster_id is information about the cluster in which core resides ,format is + * |--------[bit31-24]-------[bit23-16]-------------[bit15-8]-----------[bit7-0] + * |--------Affinity level3-----Affinity level2-----Affinity level1-----Affinity level0 + * @param {u32} *target_list is core mask in cluster + * @return {u32} 0 indicates that the conversion was not successful , 1 indicates that the conversion was successful + */ +u32 GetCpuMaskToAffval(u32 *cpu_mask, u32 *cluster_id, u32 *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 if (*cpu_mask & 0x30) + { + *cluster_id = 0x200; + if ((*cpu_mask & 0x30) == 0x30) + { + *target_list = 3; + } + else if ((*cpu_mask & 0x10)) + { + *target_list = 1; + } + else + { + *target_list = 2; + } + *cpu_mask &= ~0x30; + } + else if (*cpu_mask & 0xc0) + { + *cluster_id = 0x300; + if ((*cpu_mask & 0xc0) == 0xc0) + { + *target_list = 3; + } + else if ((*cpu_mask & 0x40)) + { + *target_list = 1; + } + else + { + *target_list = 2; + } + *cpu_mask &= ~0xc0; + } + else + { + *cpu_mask = 0; + return 0; + } + + return 1; +} \ No newline at end of file diff --git a/bsp/phytium/board/e2000/d/parameters.c b/bsp/phytium/board/e2000/d/parameters.c new file mode 100644 index 0000000000..5a5c55a845 --- /dev/null +++ b/bsp/phytium/board/e2000/d/parameters.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Email: opensource_embedded@phytium.com.cn + * + * Change Logs: + * Date Author Notes + * 2022-10-26 huanghe first commit + * + */ + +#include "rtconfig.h" +#include + +#include "fcpu_info.h" +#include "fparameters.h" + +u32 GetCpuMaskToAffval(u32 *cpu_mask, u32 *cluster_id, u32 *target_list) +{ + if (*cpu_mask == 0) + { + return 0; + } + + *target_list = 0; + *cluster_id = 0; + + if (*cpu_mask & 0x3) + { + *cluster_id = 0x200; + if ((*cpu_mask & 0x3) == 0x3) + { + *target_list = 3; + } + else if ((*cpu_mask & 0x1)) + { + *target_list = 1; + } + else + { + *target_list = 2; + } + *cpu_mask &= ~0x3; /* clear all mask */ + } + else + { + *cpu_mask = 0; + return 0; + } + + return 1; +} diff --git a/bsp/phytium/board/e2000/q/parameters.c b/bsp/phytium/board/e2000/q/parameters.c new file mode 100644 index 0000000000..d3d3432c15 --- /dev/null +++ b/bsp/phytium/board/e2000/q/parameters.c @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Email: opensource_embedded@phytium.com.cn + * + * Change Logs: + * Date Author Notes + * 2022-10-26 huanghe first commit + * + */ + +#include "rtconfig.h" +#include + +#include "fcpu_info.h" +#include "fparameters.h" + + +/** + * @name: GetCpuMaskToAffval + * @msg: 参考 GetCpuMaskToAffval 进行参数的重新定义 ,两个小核心定义的id 为0,1 ,两个大核的id 为 2,3 + * @return {*} + * @note: + * @param {u32} *cpu_mask + * @param {u32} *cluster_id + * @param {u32} *target_list + */ +u32 GetCpuMaskToAffval(u32 *cpu_mask, u32 *cluster_id, u32 *target_list) +{ + if (*cpu_mask == 0) + { + return 0; + } + + *target_list = 0; + *cluster_id = 0; + + if (*cpu_mask & 0x4) + { + *target_list = 1; + *cpu_mask &= ~0x4; + } + else if (*cpu_mask & 0x8) + { + *cluster_id = 0x100; + *target_list = 1; + *cpu_mask &= ~0x8; + } + else if (*cpu_mask & 0x3) + { + *cluster_id = 0x200; + if ((*cpu_mask & 0x3) == 0x3) + { + *target_list = 3; + } + else if ((*cpu_mask & 0x4)) + { + *target_list = 1; + } + else + { + *target_list = 2; + } + *cpu_mask &= ~0x3; + } + else + { + *cpu_mask = 0; + return 0; + } + + return 1; +} \ No newline at end of file diff --git a/bsp/phytium/board/e2000/s/parameters.c b/bsp/phytium/board/e2000/s/parameters.c new file mode 100644 index 0000000000..b711d6704b --- /dev/null +++ b/bsp/phytium/board/e2000/s/parameters.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Email: opensource_embedded@phytium.com.cn + * + * Change Logs: + * Date Author Notes + * 2022-10-26 huanghe first commit + * + */ + +#include "rtconfig.h" +#include + +#include "fcpu_info.h" +#include "fparameters.h" + +u32 GetCpuMaskToAffval(u32 *cpu_mask, u32 *cluster_id, u32 *target_list) +{ + if (*cpu_mask == 0) + { + return 0; + } + + *target_list = 0; + *cluster_id = 0; + + if (*cpu_mask & 0x1) + { + *target_list = 1; + *cluster_id = 0x200; + *cpu_mask &= ~0x1; + } + else + { + *cpu_mask = 0; + return 0; + } + + return 1; +} diff --git a/bsp/phytium/board/ft2004/parameters.c b/bsp/phytium/board/ft2004/parameters.c new file mode 100644 index 0000000000..8bf9da3f51 --- /dev/null +++ b/bsp/phytium/board/ft2004/parameters.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Email: opensource_embedded@phytium.com.cn + * + * Change Logs: + * Date Author Notes + * 2022-10-26 huanghe first commit + * + */ + +#include "rtconfig.h" +#include + +#include "fcpu_info.h" +#include "fparameters.h" + + +u32 GetCpuMaskToAffval(u32 *cpu_mask, u32 *cluster_id, u32 *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; +} \ No newline at end of file diff --git a/bsp/phytium/board/phytium_cpu.c b/bsp/phytium/board/phytium_cpu.c new file mode 100644 index 0000000000..d4dac0a31e --- /dev/null +++ b/bsp/phytium/board/phytium_cpu.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Email: opensource_embedded@phytium.com.cn + * + * Change Logs: + * Date Author Notes + * 2022-10-26 huanghe first commit + * + */ + +#include "rtconfig.h" +#include +#include "gicv3.h" + +#include "fparameters.h" +#include "fcpu_info.h" + +#include "phytium_cpu.h" + +int phytium_cpu_id_mapping(int cpu_id) +{ +#if defined(TARGET_E2000Q) + switch (cpu_id) + { + case 0: + return 2; + case 1: + return 3; + case 2: + return 0; + case 3: + return 1; + default: + RT_ASSERT(0); + return 0; + break; + } +#else + return (int)cpu_id; +#endif +} + +#if defined(TARGET_ARMV8_AARCH64) + +int phytium_cpu_id(void) +{ + FError ret; + u32 cpu_id; + ret = GetCpuId(&cpu_id); + + if (ret != ERR_SUCCESS) + { + RT_ASSERT(0); + } + return phytium_cpu_id_mapping(cpu_id); +}; + +#else + +int rt_hw_cpu_id(void) +{ + FError ret; + u32 cpu_id; + ret = GetCpuId(&cpu_id); + + if (ret != ERR_SUCCESS) + { + RT_ASSERT(0); + } + + return phytium_cpu_id_mapping(cpu_id); +}; + + + +rt_uint64_t get_main_cpu_affval(void) +{ +#if defined(TARGET_E2000Q) + return CORE2_AFF; +#else + return CORE0_AFF; +#endif +} + + +extern u32 GetCpuMaskToAffval(u32 *cpu_mask, u32 *cluster_id, u32 *target_list); +rt_uint32_t arm_gic_cpumask_to_affval(rt_uint32_t *cpu_mask, rt_uint32_t *cluster_id, rt_uint32_t *target_list) +{ + return GetCpuMaskToAffval(cpu_mask, cluster_id, target_list); +} + + +#ifdef RT_USING_SMP + +void send_core_isg(void) +{ + for (rt_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 + + +#endif \ No newline at end of file diff --git a/bsp/phytium/board/phytium_cpu.h b/bsp/phytium/board/phytium_cpu.h new file mode 100644 index 0000000000..d0d16dff0e --- /dev/null +++ b/bsp/phytium/board/phytium_cpu.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Email: opensource_embedded@phytium.com.cn + * + * Change Logs: + * Date Author Notes + * 2022-10-26 huanghe first commit + * + */ + +#ifndef __PHYTIUM_CPU_H__ +#define __PHYTIUM_CPU_H__ + +#include +#include +#include "fparameters.h" + +#define ARM_GIC_MAX_NR 1 +#define MAX_HANDLERS 160 +#define GIC_IRQ_START 0 +#define GIC_ACK_INTID_MASK 0x000003ff + + +rt_uint64_t get_main_cpu_affval(void); + +rt_inline rt_uint32_t platform_get_gic_dist_base(void) +{ + return GICV3_DISTRIBUTOR_BASEADDRESS; +} + +#if defined(TARGET_ARMV8_AARCH64) + +/* the basic constants and interfaces needed by gic */ +rt_inline rt_uint32_t platform_get_gic_redist_base(void) +{ + extern int phytium_cpu_id(void); + + s32 cpu_offset = 0; +#if defined(FT_GIC_REDISTRUBUTIOR_OFFSET) + cpu_offset = FT_GIC_REDISTRUBUTIOR_OFFSET ; +#endif + +#if defined(TARGET_E2000Q) + u32 cpu_id = 0; + cpu_id = phytium_cpu_id(); + + switch (cpu_id) + { + case 0: + case 1: + cpu_offset = 2; + break; + case 2: + case 3: + cpu_offset = -2; + default: + break; + } +#endif + + rt_kprintf("offset is %x\n", cpu_offset); + return (GICV3_RD_BASEADDRESS + (cpu_offset) * GICV3_RD_OFFSET); +} + +rt_inline rt_uint32_t platform_get_gic_cpu_base(void) +{ + return 0U; /* unused in gicv3 */ +} + +#endif + + +int phytium_cpu_id_mapping(int cpu_id); + + + +#endif // ! \ No newline at end of file diff --git a/bsp/phytium/board/secondary_cpu.c b/bsp/phytium/board/secondary_cpu.c new file mode 100644 index 0000000000..1e46537e59 --- /dev/null +++ b/bsp/phytium/board/secondary_cpu.c @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Email: opensource_embedded@phytium.com.cn + * + * Change Logs: + * Date Author Notes + * 2022-10-26 huanghe first commit + * 2022-10-26 zhugengyu support aarch64 + * + */ + +#include +#include "board.h" +#include +#include "rtconfig.h" +#include "phytium_cpu.h" + +#if defined(TARGET_ARMV8_AARCH64) + #include "cpuport.h" + #include "gtimer.h" + #include "mmu.h" +#endif + +#ifdef RT_USING_SMP +#include + +#if defined(TARGET_ARMV8_AARCH64) + #include "psci.h" +#endif + +#include "fpsci.h" + +rt_uint64_t rt_cpu_mpidr_early[] = +{ +#if defined(TARGET_E2000D) + [0] = 0x80000200, + [1] = 0x80000201, +#elif defined(TARGET_E2000Q) + [0] = 0x80000200, + [1] = 0x80000201, + [2] = 0x80000000, + [3] = 0x80000100, +#elif defined(TARGET_F2000_4) || defined(TARGET_D2000) + [0] = 0x80000000, + [1] = 0x80000001, + [2] = 0x80000100, + [3] = 0x80000101, +#if defined(TARGET_D2000) + [4] = 0x80000200, + [5] = 0x80000201, + [6] = 0x80000300, + [7] = 0x80000301, +#endif +#endif + +}; + +extern int rt_hw_timer_init(void); +extern void secondary_cpu_start(void); + +void rt_hw_secondary_cpu_up(void) +{ + rt_uint32_t i; + rt_uint32_t cpu_mask = 0; + + rt_kprintf("rt_hw_secondary_cpu_up is processing \r\n"); + for (i = 1; i < RT_CPUS_NR; i++) + { + + cpu_mask = 1 << phytium_cpu_id_mapping(i); + + /* code */ + PsciCpuOn(cpu_mask, (uintptr)secondary_cpu_start); + + +#if defined(TARGET_ARMV8_AARCH64) + __DSB(); +#else + __asm__ volatile("dsb" ::: "memory"); +#endif + } +} + +void secondary_cpu_c_start(void) +{ + /* mmu init */ +#if defined(TARGET_ARMV8_AARCH64) + rt_hw_mmu_init(); +#endif + /* spin lock init */ + rt_hw_spin_lock(&_cpus_lock); + /* interrupt init */ +#if defined(TARGET_ARMV8_AARCH64) + arm_gic_cpu_init(0, platform_get_gic_cpu_base()); + arm_gic_redist_init(0, platform_get_gic_redist_base()); +#else + arm_gic_cpu_init(0); + arm_gic_redist_init(0); +#endif + + /* vector init */ + rt_hw_vector_init(); + /* gtimer init */ +#if defined(TARGET_ARMV8_AARCH64) + rt_hw_gtimer_local_enable(); +#else + rt_hw_timer_init(); +#endif + rt_hw_interrupt_umask(RT_SCHEDULE_IPI); + + /* start scheduler */ + + rt_kprintf("\rcall cpu %d on success\n", rt_hw_cpu_id()); + rt_hw_secondary_cpu_idle_exec(); + rt_system_scheduler_start(); +} + +void rt_hw_secondary_cpu_idle_exec(void) +{ +#if defined(TARGET_ARMV8_AARCH64) + __WFE(); +#else + asm volatile("wfe" :: + : "memory", "cc"); +#endif +} + +#endif diff --git a/bsp/phytium/figures/config_tftp32.png b/bsp/phytium/figures/config_tftp32.png new file mode 100644 index 0000000000000000000000000000000000000000..3e52a8d10be34a901d80b016c064c1b178dbd565 GIT binary patch literal 11098 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 F{{mb1vPb{` literal 0 HcmV?d00001 diff --git a/bsp/phytium/figures/result.png b/bsp/phytium/figures/result.png new file mode 100644 index 0000000000000000000000000000000000000000..d9910bc1c721228516b50e927ea72de2dd7cd605 GIT binary patch literal 12890 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 literal 0 HcmV?d00001 diff --git a/bsp/phytium/figures/tftp32_srv.png b/bsp/phytium/figures/tftp32_srv.png new file mode 100644 index 0000000000000000000000000000000000000000..6fedb850f4c2e4d7c520e52f4659e022234abd16 GIT binary patch literal 39206 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∨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 + config = *(const FPl011Config *)FPl011LookupConfig(uart->config.uart_instance); + + RT_ASSERT(FPl011CfgInitialize(uart_hw, &config) == FT_SUCCESS); + FPl011SetHandler(uart_hw, Ft_Os_Uart_Callback, serial); + + FPl011SetRxFifoThreadhold(uart_hw, FPL011IFLS_RXIFLSEL_1_4); + FPl011SetTxFifoThreadHold(uart_hw, FPL011IFLS_TXIFLSEL_1_2); + + //config.isr_event_mask; + FPl011SetInterruptMask(uart_hw, intr_mask); + FPl011SetOptions(uart_hw, FPL011_OPTION_UARTEN | FPL011_OPTION_RXEN | FPL011_OPTION_TXEN | FPL011_OPTION_FIFOEN); + + rt_hw_interrupt_set_priority(uart_hw->config.irq_num, uart->config.isr_priority); + rt_hw_interrupt_install(uart_hw->config.irq_num, rt_hw_uart_isr, uart_hw, "uart"); + rt_hw_interrupt_umask(uart_hw->config.irq_num); + + return RT_EOK; +} + +static rt_err_t uart_control(struct rt_serial_device *serial, int cmd, void *arg) +{ + struct drv_usart *uart = RT_NULL; + FPl011 *uart_ptr = RT_NULL; + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct drv_usart, serial); + uart_ptr = uart->handle; + + switch (cmd) + { + case RT_DEVICE_CTRL_CLR_INT: + /* disable rx irq */ + rt_hw_interrupt_mask(uart_ptr->config.irq_num); + break; + + case RT_DEVICE_CTRL_SET_INT: + /* enable rx irq */ + rt_hw_interrupt_umask(uart_ptr->config.irq_num); + 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 (FPL011_EVENT_RECV_DATA == Event || FPL011_EVENT_RECV_TOUT == Event) + { + if (serial->serial_rx) + rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND); + } + else if (FPL011_EVENT_RECV_ERROR == Event) + { + } + else if (FPL011_EVENT_SENT_DATA == Event) + { + } + else if (FPL011_EVENT_PARE_FRAME_BRKE == Event) + { + } + else if (FPL011_EVENT_RECV_ORERR == Event) + { + } + + if (FPL011_EVENT_SENT_DATA == Event) + { + } + else + { + } +} + +static int uart_putc(struct rt_serial_device *serial, char c) +{ + struct drv_usart *uart = RT_NULL; + FPl011 *uart_ptr = RT_NULL; + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct drv_usart, serial); + uart_ptr = uart->handle; + + FPl011SendByte(uart_ptr->config.base_address, c); + + return 1; +} + +u8 FPl011RecvByteNoBlocking(u32 addr) +{ + u32 recieved_byte; + + while (FUART_ISRECEIVEDATA(addr)) + { + return 0xff; + } + recieved_byte = FUART_READREG32(addr, FPL011DR_OFFSET); + return recieved_byte; +} + +static int uart_getc(struct rt_serial_device *serial) +{ + int ch; + struct drv_usart *uart = RT_NULL; + FPl011 *uart_ptr = RT_NULL; + RT_ASSERT(serial != RT_NULL); + + uart = rt_container_of(serial, struct drv_usart, serial); + uart_ptr = uart->handle; + + ch = FPl011RecvByteNoBlocking(uart_ptr->config.base_address); + if (ch == 0xff) + { + ch = -1; + rt_kprintf("") ; + } + else + { + // + } + + return ch; +} + +static const struct rt_uart_ops _uart_ops = +{ + uart_configure, + uart_control, + uart_putc, + uart_getc, + NULL +}; + +#define RT_USING_UART0 +#define RT_USING_UART1 + + +#ifdef RT_USING_UART0 + static FPl011 Ft_Uart0; + static struct drv_usart _RtUart0; +#endif + +#ifdef RT_USING_UART1 + static FPl011 Ft_Uart1; + static struct drv_usart _RtUart1; +#endif + +int rt_hw_uart_init(void) +{ + struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; + +#ifdef RT_USING_UART0 + config.bufsz = RT_SERIAL_RB_BUFSZ; + _RtUart0.serial.ops = &_uart_ops; + _RtUart0.serial.config = config; + // Ft_Uart0.config.instance_id = FUART0_ID; + + _RtUart0.handle = &Ft_Uart0; + _RtUart0.config.uart_instance = FUART0_ID; + _RtUart0.config.isr_priority = 0xd0; + _RtUart0.config.isr_event_mask = (RTOS_UART_ISR_OEIM_MASK | RTOS_UART_ISR_BEIM_MASK | RTOS_UART_ISR_PEIM_MASK | RTOS_UART_ISR_FEIM_MASK | RTOS_UART_ISR_RTIM_MASK | RTOS_UART_ISR_RXIM_MASK); + _RtUart0.config.uart_baudrate = 115200; + + rt_hw_serial_register(&_RtUart0.serial, "uart0", + RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, + &_RtUart0); +#endif + +#ifdef RT_USING_UART1 + config.bufsz = RT_SERIAL_RB_BUFSZ; + _RtUart1.serial.ops = &_uart_ops; + _RtUart1.serial.config = config; + // Ft_Uart1.config.instance_id = FUART1_ID; + _RtUart1.handle = &Ft_Uart1; + + _RtUart1.config.uart_instance = FUART1_ID; + _RtUart1.config.isr_priority = 0xd0; + _RtUart1.config.isr_event_mask = (RTOS_UART_ISR_OEIM_MASK | RTOS_UART_ISR_BEIM_MASK | RTOS_UART_ISR_PEIM_MASK | RTOS_UART_ISR_FEIM_MASK | RTOS_UART_ISR_RTIM_MASK | RTOS_UART_ISR_RXIM_MASK); + _RtUart1.config.uart_baudrate = 115200; + + 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/phytium/libraries/drivers/drv_usart.h b/bsp/phytium/libraries/drivers/drv_usart.h new file mode 100644 index 0000000000..f82b3dc4d3 --- /dev/null +++ b/bsp/phytium/libraries/drivers/drv_usart.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Email: opensource_embedded@phytium.com.cn + * + * Change Logs: + * Date Author Notes + * 2022-10-26 huanghe first commit + * + */ + +#ifndef __DRV_USART_H__ +#define __DRV_USART_H__ + +#include +#include "rtdevice.h" +#include "fpl011.h" +#include "fpl011_hw.h" + +#define RTOS_UART_ISR_OEIM_MASK FPL011IMSC_OEIM /* Overrun error interrupt mask. */ +#define RTOS_UART_ISR_BEIM_MASK FPL011IMSC_BEIM /* Break error interrupt mask */ +#define RTOS_UART_ISR_PEIM_MASK FPL011IMSC_PEIM /* Parity error interrupt mask. */ +#define RTOS_UART_ISR_FEIM_MASK FPL011IMSC_FEIM /* Framing error interrupt mask. */ +#define RTOS_UART_ISR_RTIM_MASK FPL011IMSC_RTIM /* Receive timeout interrupt mask. */ +#define RTOS_UART_ISR_TXIM_MASK FPL011IMSC_TXIM /* Transmit interrupt mask. */ +#define RTOS_UART_ISR_RXIM_MASK FPL011IMSC_RXIM /* Receive interrupt mask. */ + + +typedef struct +{ + u32 uart_instance; /* select uart global object */ + u32 isr_priority; /* irq Priority */ + u32 isr_event_mask; /* followed by RTOS_UART_ISR_XX */ + u32 uart_baudrate; +} FtFreertosUartConfig; + + +struct drv_usart +{ + FPl011 *handle; + FtFreertosUartConfig config; + struct rt_serial_device serial; +}; + +#endif // ! diff --git a/bsp/phytium/libraries/standalone/LICENSE b/bsp/phytium/libraries/standalone/LICENSE new file mode 100644 index 0000000000..30bd97be52 --- /dev/null +++ b/bsp/phytium/libraries/standalone/LICENSE @@ -0,0 +1,28 @@ + + Phytium Public License 1.0 (PPL-1.0) + +UNLESS IT HAS ITS OWN COPYRIGHT/LICENSE EMBEDDED IN ITS BODY, EACH FILE IS SUBJECT TO THE FOLLOWING LICENSE TERMS + +Copyright (C) 2022, Phytium Technology Co., Ltd. +All Rights Reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that +the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the +following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and +the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. If the name of phytium or the names of its contributors are needed to endorse or promote products +derived from this software ,Prior written permission should be required. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/README.md b/bsp/phytium/libraries/standalone/README.md new file mode 100644 index 0000000000..a9dbcff01a --- /dev/null +++ b/bsp/phytium/libraries/standalone/README.md @@ -0,0 +1,291 @@ +# Phytium-Standalone-SDK + +**v0.3.1** [ReleaseNote](./doc/ChangeLog.md) + +## 1. 项目概要 + +### 1.1 基本介绍 + +本项目发布了 Phytium 系列 CPU 的 嵌入式软件开发工具包,包括板级支持包、第三方开源中间件、交叉编译构建工具、及其 Baremetal 参考例程,在支持多平台裸机应用开发的基础上,能够为多种RTOS提供外设驱动和配置构建工具。 + +![LetterShell](./doc/fig/letter_shell.png) + + +### 1.2 系统架构 + + +本项目的整体设计如下所示,自下而上可以分为平台层、组件层、框架层和应用层。 + +![Framework](./doc/design/system_2.png) + +- 平台层(Platform)在整个软件框架中位于最底层,提供了基本数据结构类型定义、驱动参数标定、硬件平台耦合的寄存器自检、板级启动、CPU 内存虚拟等功能 + +- 组件层(Component)在整个软件框架中位于中间位置,向下依赖于平台层提供的参数配置与内存方案,向上提供应用开发与模块测试的支持 + +- 框架层(Framework)为开发主机提供了开发环境,支持SDK安装,应用工程配置和二进制文件构建及烧录等工具。 + +- 应用层(Application)提供了应用开发模板和例程,帮助开发者迅速熟悉SDK的使用,进行不同类型的应用程序开发 + +### 1.3. 源代码结构 + +``` +. +├── Kconfig --> 配置定义 +├── LICENSE --> 版权声明 +├── README.md --> 使用说明 +├── arch +│   └── armv8 --> 架构相关 +├── baremetal +│   └── example --> 裸机例程 +├── board +│   ├── d2000 +│   ├── e2000 +│   └── ft2004 --> 平台相关 +├── common +│   ├── fprintf.c +│   ├── fprintf.h +│   ├── fsleep.c +│   └── fsleep.h --> 通用方法 +├── configs +│   ├── ft2004_aarch32_defconfig +│   └── ft2004_aarch64_defconfig --> 各平台默认配置 +├── doc +│   ├── ChangeLog.md --> 修改记录 +│   └── reference --> 接口说明文档 +├── drivers +│   ├── can +│   ├── dma +│   └── watchdog --> 外设驱动 +├── install.py --> 安装脚本 +├── lib +│   ├── Kconfiglib +│   ├── lib.mk +│   ├── libc +│   └── nostdlib --> 依赖库 +├── make +│   ├── build_baremetal.mk +│   ├── buildinfo.mk +│   ├── complier.mk +│   └── preconfig.mk --> 编译脚本和链接脚本 +├── requirements.txt --> python环境依赖组件 +├── scripts +├── standalone.mk +├── third-party +│   └── letter-shell-3.1 --> 第三方库 +├── tools +``` + +--- + +## 2. 快速入门 + +- 目前支持在Windows和Linux上使用SDK,支持在x86_64和ARM AARCH64设备上完成交叉编译 + +![windows](./doc/fig/windows.png)![linux](./doc/fig/linux.png)![输入图片说明](./doc/fig/kylin.png) + + +- 参考[Windows10 快速入门](./doc/reference/usr/install_windows.md), [Linux x86_64 快速入门](./doc/reference/usr/install_linux_x86_64.md) + +- 参考[使用说明](./doc/reference/usr/usage.md), 新建Phytium Standalone SDK的应用工程,与开发板建立连接 + +- 参考[例程](./baremetal/example),开始使用SDK + +--- + +## 3. 硬件参考 + +### 3.1 FT2000-4 + +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) +- 集成温度传感器 + +### 3.2 D2000 + +D2000 是一款面向桌面应用的高性能通用 8 核处理器。每 2 个核构成 1 个处理器核簇(Cluster),并共享 L2 Cache。存储系统包含 Cache 子系统和 DDR,I/O 系统包含 PCIe、高速 IO 子系统、千兆位以太网 GMAC 和低速 IO 子系统,主要技术特征如下, + +- 兼容 ARM v8 64 位指令系统,兼容 32 位指令 +- 支持单精度、双精度浮点运算指令 +- 支持 ASIMD 处理指令 +- 集成 2 个 DDR 通道,支持 DDR4 和 LPDDR4,可对 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、SM9 模块 +- 集成 4 个 UART,1 个 LPC,32 个 GPIO,4 个 I2C,1 个 QSPI,2 个通用 SPI,2 个 WDT,16 个外部中断(和 GPIO 共用 IO) +- 集成 2 个温度传感器 + + +### 3.3 E2000D + +- E2000D 1个cluster有2个cpu,共两核。主要技术特征如下: + +- 兼容ARM v8 64 位指令系统,兼容32 位指令 +- 支持单精度、双精度浮点运算指令 +- L1有32KB,L2有256KB +- 集成1个DDR4 通道,可对DDR 存储数据进行实时加密 +- 集成4 Lane PCIE3.0 接口(4X1) +- 集成网络接口4x1000M SGMII,1路支持RGMII/RMII,支持1路TSN +- 集成2个USB2.0(OTG)接口 +- 集成1个HDAudio,支持音频输出;2路DP显示接口 +- 集成2路SATA3.0模块 +- 集成常用低速接口:WDT,DMAC,QSPI,PWM,Nand,SD/SDIO/eMMC ,SPI_M,UART,I2C,MIO,CAN, LPC_M_S,GPIO,LBC,Timer + +### 3.4 E2000S + +- E2000S 1个cluster有1个cpu,单核结构。主要技术特征如下: + +- 兼容ARM v8 64 位指令系统,兼容32 位指令 +- 支持单精度、双精度浮点运算指令 +- L1有32KB,L2有256KB +- 集成1个DDR4 通道,可对DDR 存储数据进行实时加密 +- 集成2 Lane PCIE3.0 接口(2X1) +- 集成网络接口2x1000M SGMII/RGMII/RMII,支持2路NCSI +- 集成2个USB2.0(OTG)接口 +- 集成1个HDAudio,支持音频输出;2路DP显示接口 +- 集成JPEG Encoder模块 +- 集成常用低速接口:WDT,DMAC,PWM,QSPI,SD/SDIO/eMMC,SPI_M,UART,I2C,MIO,I3C,PMBUS, LPC_M_S,GPIO,oneWire,Timer + + +## 4 外设驱动支持情况 + +| Hardware Interface | Platform Supported | Platform Developing | Component | +| ------------------------------ | -------------------------- | --------------------------- | ------------------------- | +| Generic Intrrupt Controller v3 | FT2000/4
E2000
D2000 | | gic/fgic | +| Generic Timer | FT2000/4
E2000
D2000 | | generic_timer | +| UART (PrimeCell PL011) | FT2000/4
E2000
D2000 | | usart/pl011_uart | +| 10/100/1000MB-ETHERNET | FT2000/4
E2000
D2000 | | eth/fgmac
eth/fxmac | +| ADC | E2000 | | adc/fadc | +| CAN | FT2000/4
E2000
D2000 | | can/fcan | +| DDMA | | E2000 | dma/fddma | +| GDMA | E2000 | | dma/gdma | +| GPIO | FT2000/4
E2000
D2000 | | gpio/fgpio | +| I2C | FT2000/4
E2000
D2000 | | i2c/fi2c | +| QSPI (Nor Flash) | FT2000/4
E2000
D2000 | | qspi/fqspi | +| SPI | FT2000/4
E2000
D2000 | | spi/fspim | +| TIMER & TACHO | E2000 | | timer/ftimer_tacho | +| MIO | E2000 | | mio/fmio | +| SDMMC | | FT2000/4
D2000 | mmc/fsdmmc | +| SDIO | E2000 | | mmc/fsdio | +| PCIE | FT2000/4
D2000
E2000 | | pcie/fpcie | +| NAND | E2000 | | nand/fnand | +| RTC | FT2000/4
D2000 | | rtc/frtc | +| SATA | FT2000/4
D2000
E2000 | | sata/fsata | +| USB-PCI | | FT2000/4
E2000
D2000 | usb/fxhci | +| PWM | E2000 | | pwm/fpwm | +| WDT | FT2000/4
D2000
E2000 | | watchdog/fwdt | + + +| Third-Party | Platform Supported | Platform Developing | Component | +| ------------------------------ | -------------------------- | --------------------------- | ------------------------- | +| LWIP 2.1.2 | FT2000/4
D2000
E2000 | | lwip-2.1.2 | +| Letter shell 3.1 | FT2000/4
D2000
E2000 | | letter-shell-3.1 | +| Sdmmc | FT2000/4
D2000 | | sdmmc | +| Sfud 1.1.0 | FT2000/4
D2000
E2000 | | sfud-1.1.0 | +| Backtrace | FT2000/4
D2000
E2000 | | backtrace | +| Tlsf | FT2000/4
D2000
E2000 | | tlsf-3.1.0 | +| Fatfs (RAM/Sd/SATA) | FT2000/4
D2000
E2000 | | fatfs-0.1.3 | +| Ymodem | FT2000/4
D2000
E2000 | | | +| OpenAMP | FT2000/4
D2000
E2000 | | openamp | +| LittleFS-2.4.2 | | FT2000/4
E2000
D2000 | littlefs-2.4.2 | +| SPIFFS-0.3.7 | FT2000/4
D2000
E2000 | | spiffs-0.3.7 | + +--- + +## 5. API指南 + +### 5.1 DRIVERS + +#### 5.1.1 [FI2C](./doc/reference/driver/fi2c.md) + +#### 5.1.2 [FPL011](./doc/reference/driver/fpl011.md) + +#### 5.1.3 [FRTC](./doc/reference/driver/frtc.md) + +#### 5.1.4 [FWDT](./doc/reference/driver/fwdt.md) + +#### 5.1.5 [FSPIM](./doc/reference/driver/fspim.md) + +#### 5.1.6 [FQSPI](./doc/reference/driver/fqspi.md) + +#### 5.1.7 [FSDMMC](./doc/reference/driver/fsdmmc.md) + +#### 5.1.8 [FSATA](./doc/reference/driver/fsata.md) + +#### 5.1.9 [FPCIE](./doc/reference/driver/fpcie.md) + +#### 5.1.10 [FUSB](./doc/reference/driver/fusb.md) + +#### 5.1.11 [FGPIO](./doc/reference/driver/fgpio.md) + +#### 5.1.12 [FGIC](./doc/reference/driver/fgic.md) + +#### 5.1.13 [FDDMA](./doc/reference/driver/fddma.md) + +#### 5.1.14 [FCAN](./doc/reference/driver/fcan.md) + +#### 5.1.15 [FADC](./doc/reference/driver/fadc.md) + +#### 5.1.16 [FPWM](./doc/reference/driver/fpwm.md) + +#### 5.1.17 [FSDIO](./doc/reference/driver/fsdio.md) + +### 5.2 MEMORY +#### 5.2.1 [FMEMORY_POOL](./doc/reference/sdk/fmemory_pool.md) + +### 5.3 CPU + +#### 5.3.1 [MMU](./doc/reference/processor/mmu.md) + +#### 5.3.2 [FPINCTRL](./doc/reference/sdk/fpinctrl.md) + +#### 5.3.2 [INTERRUPT](./doc/reference/processor/interrupt.md) + +--- + +## 6. 贡献方法 + +请联系飞腾嵌入式软件部 + +huanghe@phytium.com.cn + +zhugengyu@phytium.com.cn + +wangxiaodong1030@phytium.com.cn + +liushengming1118@phytium.com.cn + + +--- + +## 6. 相关资源 + + +- ARM Architecture Reference Manual +- ARM Cortex-A Series Programmer’s Guide +- Programmer Guide for ARMv8-A +- ARM System Developers Guide Designing and Optimizing System Software +- FT-2000/4 软件编程手册-V1.4 +- D2000 软件编程手册-V1.0 +- Bare-metal programming for ARM —— A hands-on guide +- Using the GNU Compiler Collection +- Using ld, The GNU Linker +- Using as, The GNU Assembler +- Armv8-A memory model guide + +--- + +## 7. 许可协议 + +Phytium Public License 1.0 (PPL-1.0) diff --git a/bsp/phytium/libraries/standalone/arch/armv8/aarch32/faarch32.h b/bsp/phytium/libraries/standalone/arch/armv8/aarch32/faarch32.h new file mode 100644 index 0000000000..0a7778fad2 --- /dev/null +++ b/bsp/phytium/libraries/standalone/arch/armv8/aarch32/faarch32.h @@ -0,0 +1,416 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: faarch32.h + * Date: 2022-02-10 14:53:41 + * LastEditTime: 2022-02-17 17:28:37 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Huanghe 2021/7/3 init + * 1.1 Wangxiaodong 2021/9/24 modify sys_icc_bpr_set and sys_icc_bpr_get + */ + +#ifndef BSP_AARCH32_ASM_H +#define BSP_AARCH32_ASM_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ftypes.h" + +#define __ASM __asm +#define __STATIC_INLINE static inline +#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; \ + }) + +/* C语言实现MRRC指令 */ +#define __MRRC(coproc, opcode_1, dst_1, dst_2, CRm) ( \ + { \ + __asm__ __volatile__( \ + "MRRC " __STRINGIFY(p##coproc) ", " __STRINGIFY(opcode_1) ", " \ + "%0,%1," __STRINGIFY(c##CRm) \ + : "=r"(dst_1), "=r"(dst_2)); \ + }) + +/** + * @name: aarch32_cntp_ctl_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 aarch32_cntp_ctl_get(void) +{ + /* MRC p15(coproc) 0(opcode1) CR14(n) CR2(m) 1(opcode2) */ + return __MRC(15, 0, 14, 2, 1); +} + +/** + * @name: aarch32_cntp_tlb_get + * @msg: + * @return {*} + * @param {__STATIC_INLINE u32} aarch32_cntp_ctl_get + */ +__attribute__((always_inline)) __STATIC_INLINE u32 aarch32_cntp_tlb_get(void) +{ + return __MRC(15, 0, 0, 2, 0); +} + +/** + * @name: aarch32_cntp_ctl_set + * @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 void aarch32_cntp_ctl_set(u32 regVal) +{ + /* MRC p15(coproc) regVal 0(opcode1) CR14(n) CR2(m) 1(opcode2) */ + __MCR(15, 0, regVal, 14, 2, 1); +} + +/** + * @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 aarch32_cntfrq_get(void) +{ + return __MRC(15, 0, 14, 0, 0); +} + +/** + * @name: aarch32_cntpct_get + * @msg: get the 64-bit physical count value + * @return {*} + * @param {__STATIC_INLINE u64} aarch32_cntpct_get + */ +__attribute__((always_inline)) __STATIC_INLINE u64 aarch32_cntpct_get() +{ + u64 cnt = 0; + u32 cnt_low = 0, cnt_high = 0; + __MRRC(15, 0, cnt_low, cnt_high, 14); + cnt = (u64)cnt_high << 32 | cnt_low; + return cnt; +} + +/** + * @name: 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 aarch32_cntp_tval_set(u32 RegValue) +{ + __MCR(15, 0, RegValue, 14, 2, 0); +} + +/** + * @name: aarch32_sctrl_get + * @msg: read the register that control system + */ +__attribute__((always_inline)) __STATIC_INLINE u32 aarch32_sctrl_get() +{ + return __MRC(15, 0, 1, 0, 0); +} + +/** + * @name: aarch32_sctrl_set + * @msg: read the register that control system + */ +#define AARCH32_SCTRL_CACHE_BIT (1 << 2) /* 1: enable, 0: disable */ +__attribute__((always_inline)) __STATIC_INLINE void aarch32_sctrl_set(u32 RegVal) +{ + __MCR(15, 0, RegVal, 1, 0, 0); +} + +/**********************************************/ + +__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); +} + +/* arm_aarch32_cnttimer_set */ +__attribute__((always_inline)) __STATIC_INLINE void arm_aarch32_cnttimer_set(u32 RegValue) +{ + __MCR(15, 0, RegValue, 14, 2, 2); +} + +/** + * @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 INTERRUPT_DISABLE() \ + __asm volatile("CPSID i" :: \ + : "memory"); \ + __asm volatile("DSB"); \ + __asm volatile("ISB"); + +#define INTERRUPT_ENABLE() \ + __asm volatile("CPSIE i" :: \ + : "memory"); \ + __asm volatile("DSB"); \ + __asm volatile("ISB"); + +#ifdef __cplusplus +} +#endif + +#endif // ! \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/arch/armv8/aarch32/fgeneric_timer.c b/bsp/phytium/libraries/standalone/arch/armv8/aarch32/fgeneric_timer.c new file mode 100644 index 0000000000..082a74eb4e --- /dev/null +++ b/bsp/phytium/libraries/standalone/arch/armv8/aarch32/fgeneric_timer.c @@ -0,0 +1,154 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: generic_timer.c + * Date: 2022-02-10 14:53:41 + * LastEditTime: 2022-02-17 17:30:07 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#include "fparameters.h" +#include "fgeneric_timer.h" +#include "faarch32.h" +#include "sdkconfig.h" + +#ifndef SDK_CONFIG_H__ + #warning "Please include sdkconfig.h" +#endif + +#ifdef CONFIG_USE_SYS_TICK + #include "fassert.h" + #include "finterrupt.h" + + static volatile u32 genericTick; + static GenericTimerTickHandler usr_tick_handler = NULL; +#endif + +#define AARCH32_CNTP_CTL_ENABLE_MASK (1ul << 0) +#define AARCH32_CNTP_CTL_INTERRUPT_MASK (1ul << 1) + +void GenericTimerStart(void) +{ + u32 ctrl = aarch32_cntp_ctl_get(); + + if (!(ctrl & AARCH32_CNTP_CTL_ENABLE_MASK)) + { + ctrl |= AARCH32_CNTP_CTL_ENABLE_MASK; + aarch32_cntp_ctl_set(ctrl); + } +} + +void GenericTimerStop(void) +{ + u32 ctrl = aarch32_cntp_ctl_get(); + if ((ctrl & AARCH32_CNTP_CTL_ENABLE_MASK)) + { + ctrl &= ~AARCH32_CNTP_CTL_ENABLE_MASK; + aarch32_cntp_ctl_set(ctrl); + } +} + +void GenericTimerInterruptEnable(void) +{ + u32 ctrl = aarch32_cntp_ctl_get(); + if (ctrl & AARCH32_CNTP_CTL_INTERRUPT_MASK) + { + ctrl &= ~AARCH32_CNTP_CTL_INTERRUPT_MASK; + aarch32_cntp_ctl_set(ctrl); + } +} + +void GenericTimerInterruptDisable(void) +{ + u64 ctrl = aarch32_cntp_ctl_get(); + if (!(ctrl & AARCH32_CNTP_CTL_INTERRUPT_MASK)) + { + ctrl |= AARCH32_CNTP_CTL_INTERRUPT_MASK; + aarch32_cntp_ctl_set(ctrl); + } +} + +u32 GenericTimerFrequecy(void) +{ + u32 rate = aarch32_cntfrq_get(); + return (rate != 0) ? rate : 1000000; +} + +u64 GenericTimerRead(void) +{ + return aarch32_cntpct_get(); +} + +void GenericTimerCompare(u32 interval) +{ + aarch32_cntp_tval_set(interval); +} + +#ifdef CONFIG_USE_SYS_TICK +static void GenericTimerClearTickIntr(u32 tickRateHz) +{ + GenericTimerCompare(GenericTimerFrequecy() / tickRateHz); +} + +static void GenericTimerTickIntrHandler(s32 vector, void *param) +{ + u32 tickRateHz = (u32)param; + (void)vector; + genericTick++; /* tick */ + GenericTimerClearTickIntr(tickRateHz); /* clear tick intrrupt */ + + if (usr_tick_handler) /* execute user handler */ + usr_tick_handler(); +} +#endif + +void GenericTimerSetupSystick(u32 tickRateHz, GenericTimerTickHandler tickHandler, u32 intrPrority) +{ +#ifdef CONFIG_USE_SYS_TICK + u32 cntFrq; + + /* disable timer and get system frequency */ + GenericTimerStop(); + cntFrq = GenericTimerFrequecy(); + + /* set tick rate */ + GenericTimerCompare(cntFrq / tickRateHz); + GenericTimerInterruptEnable(); + + /* set generic timer intrrupt */ + InterruptSetPriority(GENERIC_TIMER_NS_IRQ_NUM, intrPrority); + + /* install tick handler */ + usr_tick_handler = tickHandler; + InterruptInstall(GENERIC_TIMER_NS_IRQ_NUM, GenericTimerTickIntrHandler, + (void *)tickRateHz, "GenericTimerTick"); + + /* enable intrrupt */ + InterruptUmask(GENERIC_TIMER_NS_IRQ_NUM); + GenericTimerStart(); +#endif +} + +u32 GenericGetTick(void) +{ +#ifdef CONFIG_USE_SYS_TICK + return genericTick; +#else + return 0xffU; +#endif +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/arch/armv8/aarch32/fgeneric_timer.h b/bsp/phytium/libraries/standalone/arch/armv8/aarch32/fgeneric_timer.h new file mode 100644 index 0000000000..5a247c3fbd --- /dev/null +++ b/bsp/phytium/libraries/standalone/arch/armv8/aarch32/fgeneric_timer.h @@ -0,0 +1,51 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgeneric_timer.h + * Date: 2022-02-10 14:53:41 + * LastEditTime: 2022-02-17 17:30:13 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#ifndef BSP_ARCH_ARMV8_AARCH32_GENERIC_TIMER_H +#define BSP_ARCH_ARMV8_AARCH32_GENERIC_TIMER_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ftypes.h" + +typedef void (* GenericTimerTickHandler)(); + +void GenericTimerStart(void); +void GenericTimerStop(void); +void GenericTimerInterruptEnable(void); +void GenericTimerInterruptDisable(void); +u32 GenericTimerFrequecy(void); +u64 GenericTimerRead(void); +void GenericTimerCompare(u32 interval); +void GenericTimerSetupSystick(u32 tickRateHz, GenericTimerTickHandler tickHandler, u32 intrPrority); +u32 GenericGetTick(void); + +#ifdef __cplusplus +} +#endif + +#endif // ! \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/arch/armv8/aarch32/fpsci.c b/bsp/phytium/libraries/standalone/arch/armv8/aarch32/fpsci.c new file mode 100644 index 0000000000..2c2e298b23 --- /dev/null +++ b/bsp/phytium/libraries/standalone/arch/armv8/aarch32/fpsci.c @@ -0,0 +1,75 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: psci.c + * Date: 2022-02-10 14:53:41 + * LastEditTime: 2022-02-17 17:30:35 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#include "fpsci.h" +#include "fsmc.h" +#include "fcpu_info.h" +#include "ferror_code.h" +#include "fparameters.h" +#include "ftypes.h" + +#define PSCI_CPUON_NUM 0x84000003 +#define PSCI_RESET_NUM 0x84000009 + +/** + * @name: FPsci_CpuOn + * @msg: Power up a core + * @in param cpu_id_mask: cpu id mask + * @in param bootaddr: a 32-bit entry point physical address (or IPA). + * @return FError + */ +FError PsciCpuOn(s32 cpu_id_mask, uintptr bootaddr) +{ + FError ret ; + u64 cluster = 0; + FSmc_Data_t input = {0}; + FSmc_Data_t output = {0}; + input.function_identifier = PSCI_CPUON_NUM; + ret = GetCpuAffinityByMask(cpu_id_mask, &cluster); + if (ret != ERR_SUCCESS) + { + return ret; + } + + input.a1 = cluster; + + input.a2 = (u32)(bootaddr & 0xFFFFFFFF); + FSmcCall(&input, &output); + __asm__ volatile("NOP"); + return ERR_SUCCESS; +} + +void PsciCpuReset(void) +{ + + FSmc_Data_t input = {0}; + FSmc_Data_t output = {0}; + + input.function_identifier = PSCI_RESET_NUM; + FSmcCall(&input, &output); + + __asm__ volatile("NOP"); + while (1) + ; +} diff --git a/bsp/phytium/libraries/standalone/arch/armv8/aarch32/fpsci.h b/bsp/phytium/libraries/standalone/arch/armv8/aarch32/fpsci.h new file mode 100644 index 0000000000..d79f0ba548 --- /dev/null +++ b/bsp/phytium/libraries/standalone/arch/armv8/aarch32/fpsci.h @@ -0,0 +1,43 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fpsci.h + * Date: 2022-02-10 14:53:41 + * LastEditTime: 2022-02-17 17:30:40 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#ifndef BSP_ARCH_ARMV8_AARCH32_PSCI_H +#define BSP_ARCH_ARMV8_AARCH32_PSCI_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ftypes.h" +#include "ferror_code.h" + +FError PsciCpuOn(s32 cpu_id_mask, uintptr bootaddr); +void PsciCpuReset(void); + +#ifdef __cplusplus +} +#endif + +#endif // ! diff --git a/bsp/phytium/libraries/standalone/arch/armv8/aarch32/fsmc.h b/bsp/phytium/libraries/standalone/arch/armv8/aarch32/fsmc.h new file mode 100644 index 0000000000..8fc2005940 --- /dev/null +++ b/bsp/phytium/libraries/standalone/arch/armv8/aarch32/fsmc.h @@ -0,0 +1,53 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsmc.h + * Date: 2022-02-10 14:53:41 + * LastEditTime: 2022-02-17 17:30:49 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#ifndef BSP_ARCH_ARMV8_AARCH32_SMC_H +#define BSP_ARCH_ARMV8_AARCH32_SMC_H + +#ifdef __cplusplus +extern "C" +{ +#endif +#include "ftypes.h" + +typedef struct +{ + /* data */ + u32 function_identifier; + u32 a1; + u32 a2; + u32 a3; + u32 a4; + u32 a5; + u32 a6; + +} FSmc_Data_t; + +void FSmcCall(FSmc_Data_t *Input, FSmc_Data_t *Output); + +#ifdef __cplusplus +} +#endif + +#endif // !FT_SMC_H diff --git a/bsp/phytium/libraries/standalone/arch/armv8/aarch32/gcc/fsmccc_call.S b/bsp/phytium/libraries/standalone/arch/armv8/aarch32/gcc/fsmccc_call.S new file mode 100644 index 0000000000..16b14b209e --- /dev/null +++ b/bsp/phytium/libraries/standalone/arch/armv8/aarch32/gcc/fsmccc_call.S @@ -0,0 +1,58 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: smccc-call.S + * Date: 2022-02-10 14:53:41 + * LastEditTime: 2022-02-17 17:28:10 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +/******************************************************************************* +* +* FSmcCall - 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 FSmcCall +* ( +* FSmc_Data_t * input, /@ r0 - input register values @/ +* FSmc_Data_t * output /@ r1 - output register values @/ +* ) +*/ + +.arm +.align 4 +.globl FSmcCall +FSmcCall: + 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 FSmcCall, .- FSmcCall diff --git a/bsp/phytium/libraries/standalone/arch/armv8/aarch64/farm_smccc.h b/bsp/phytium/libraries/standalone/arch/armv8/aarch64/farm_smccc.h new file mode 100644 index 0000000000..c682d38533 --- /dev/null +++ b/bsp/phytium/libraries/standalone/arch/armv8/aarch64/farm_smccc.h @@ -0,0 +1,125 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: farm_smccc.h + * Date: 2022-02-10 14:53:41 + * LastEditTime: 2022-02-17 17:32:15 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef __LINUX_ARM_SMCCC_H +#define __LINUX_ARM_SMCCC_H + +/* + * This file provides common defines for ARM SMC Calling Convention as + * specified in + * http://infocenter.arm.com/help/topic/com.arm.doc.den0028a/index.html + */ + +#define ARM_SMCCC_STD_CALL 0 +#define ARM_SMCCC_FAST_CALL 1 +#define ARM_SMCCC_TYPE_SHIFT 31 + +#define ARM_SMCCC_SMC_32 0 +#define ARM_SMCCC_SMC_64 1 +#define ARM_SMCCC_CALL_CONV_SHIFT 30 + +#define ARM_SMCCC_OWNER_MASK 0x3F +#define ARM_SMCCC_OWNER_SHIFT 24 + +#define ARM_SMCCC_FUNC_MASK 0xFFFF + +#define ARM_SMCCC_IS_FAST_CALL(smc_val) \ + ((smc_val) & (ARM_SMCCC_FAST_CALL << ARM_SMCCC_TYPE_SHIFT)) +#define ARM_SMCCC_IS_64(smc_val) \ + ((smc_val) & (ARM_SMCCC_SMC_64 << ARM_SMCCC_CALL_CONV_SHIFT)) +#define ARM_SMCCC_FUNC_NUM(smc_val) ((smc_val)&ARM_SMCCC_FUNC_MASK) +#define ARM_SMCCC_OWNER_NUM(smc_val) \ + (((smc_val) >> ARM_SMCCC_OWNER_SHIFT) & ARM_SMCCC_OWNER_MASK) + +#define ARM_SMCCC_CALL_VAL(type, calling_convention, owner, func_num) \ + (((type) << ARM_SMCCC_TYPE_SHIFT) | \ + ((calling_convention) << ARM_SMCCC_CALL_CONV_SHIFT) | \ + (((owner)&ARM_SMCCC_OWNER_MASK) << ARM_SMCCC_OWNER_SHIFT) | \ + ((func_num)&ARM_SMCCC_FUNC_MASK)) + +#define ARM_SMCCC_OWNER_ARCH 0 +#define ARM_SMCCC_OWNER_CPU 1 +#define ARM_SMCCC_OWNER_SIP 2 +#define ARM_SMCCC_OWNER_OEM 3 +#define ARM_SMCCC_OWNER_STANDARD 4 +#define ARM_SMCCC_OWNER_TRUSTED_APP 48 +#define ARM_SMCCC_OWNER_TRUSTED_APP_END 49 +#define ARM_SMCCC_OWNER_TRUSTED_OS 50 +#define ARM_SMCCC_OWNER_TRUSTED_OS_END 63 + +#define ARM_SMCCC_QUIRK_NONE 0 +#define ARM_SMCCC_QUIRK_QCOM_A6 1 /* Save/restore register a6 */ + +#ifndef __ASSEMBLY__ + +#include +/** + * struct arm_smccc_res - Result from SMC/HVC call + * @a0-a3 result values from registers 0 to 3 + */ +struct arm_smccc_res +{ + unsigned long a0; + unsigned long a1; + unsigned long a2; + unsigned long a3; +}; + +/** + * struct arm_smccc_quirk - Contains quirk information + * @id: quirk identification + * @state: quirk specific information + * @a6: Qualcomm quirk entry for returning post-smc call contents of a6 + */ +struct arm_smccc_quirk +{ + int id; + union + { + unsigned long a6; + } state; +}; + +/** + * __arm_smccc_smc() - make SMC calls + * @a0-a7: arguments passed in registers 0 to 7 + * @res: result values from registers 0 to 3 + * @quirk: points to an arm_smccc_quirk, or NULL when no quirks are required. + * + * This function is used to make SMC calls following SMC Calling Convention. + * The content of the supplied param are copied to registers 0 to 7 prior + * to the SMC instruction. The return values are updated with the content + * from register 0 to 3 on return from the SMC instruction. An optional + * quirk structure provides vendor specific behavior. + */ +void __arm_smccc_smc(unsigned long a0, unsigned long a1, + unsigned long a2, unsigned long a3, unsigned long a4, + unsigned long a5, unsigned long a6, unsigned long a7, + struct arm_smccc_res *res, struct arm_smccc_quirk *quirk); + +#define arm_smccc_smc(...) __arm_smccc_smc(__VA_ARGS__, NULL) + +#define arm_smccc_smc_quirk(...) __arm_smccc_smc(__VA_ARGS__) + +#endif /*__ASSEMBLY__*/ +#endif /*__LINUX_ARM_SMCCC_H*/ \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/arch/armv8/aarch64/fpsci.c b/bsp/phytium/libraries/standalone/arch/armv8/aarch64/fpsci.c new file mode 100644 index 0000000000..fb5f8ec686 --- /dev/null +++ b/bsp/phytium/libraries/standalone/arch/armv8/aarch64/fpsci.c @@ -0,0 +1,47 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: psci.c + * Date: 2022-02-10 14:53:41 + * LastEditTime: 2022-02-17 17:33:51 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "fpsci.h" +#include "farm_smccc.h" +#include "ftypes.h" +#include "fcpu_info.h" +#include "ferror_code.h" + +FError PsciCpuOn(s32 cpu_id_mask, uintptr bootaddr) +{ + FError ret ; + u64 cluster = 0; + ret = GetCpuAffinityByMask(cpu_id_mask, &cluster); + if (ret != ERR_SUCCESS) + { + printf("GetCpuAffinity is failed \r\n") ; + return ret ; + } + arm_smccc_smc(0xc4000003, cluster, bootaddr, 0, 0, 0, 0, 0, 0); + return ERR_SUCCESS ; +} +void PsciCpuReset(void) +{ + struct arm_smccc_res res; + arm_smccc_smc(0x84000009, 0, 0, 0, 0, 0, 0, 0, &res); +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/arch/armv8/aarch64/fpsci.h b/bsp/phytium/libraries/standalone/arch/armv8/aarch64/fpsci.h new file mode 100644 index 0000000000..513332661b --- /dev/null +++ b/bsp/phytium/libraries/standalone/arch/armv8/aarch64/fpsci.h @@ -0,0 +1,41 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fpsci.h + * Date: 2022-02-10 14:53:41 + * LastEditTime: 2022-02-17 17:34:06 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#ifndef BSP_ARCH_AARMV8_AARCH64_PSCI_H +#define BSP_ARCH_AARMV8_AARCH64_PSCI_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ftypes.h" +#include "ferror_code.h" +void PsciCpuReset(void); +FError PsciCpuOn(s32 cpu_id_mask, uintptr bootaddr); + +#ifdef __cplusplus +} +#endif +#endif // !BSP_ARCH_AARMV8_AARCH64_PSCI_H \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/arch/armv8/aarch64/gcc/fsmccc_call.S b/bsp/phytium/libraries/standalone/arch/armv8/aarch64/gcc/fsmccc_call.S new file mode 100644 index 0000000000..78464c59bf --- /dev/null +++ b/bsp/phytium/libraries/standalone/arch/armv8/aarch64/gcc/fsmccc_call.S @@ -0,0 +1,49 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: smccc-call.S + * Date: 2022-02-10 14:53:41 + * LastEditTime: 2022-02-17 17:31:23 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +.macro SMCCC instr + \instr #0 + ldr x4, [sp] + stp x0, x1, [x4, #0] + stp x2, x3, [x4, #16] + ldr x4, [sp, #8] + cbz x4, 1f /* no quirk structure */ + ldr x9, [x4, #0] + cmp x9, #1 + b.ne 1f + str x6, [x4, 4] +1: ret +.endm SMCCC instr + +/* + * void arm_smccc_smc(unsigned long a0, unsigned long a1, unsigned long a2, + * unsigned long a3, unsigned long a4, unsigned long a5, + * unsigned long a6, unsigned long a7, struct arm_smccc_res *res, + * struct arm_smccc_quirk *quirk) + */ + .globl __arm_smccc_smc +.type __arm_smccc_smc, "function" + .cfi_startproc +__arm_smccc_smc: + SMCCC smc + .cfi_endproc diff --git a/bsp/phytium/libraries/standalone/arch/common/fkernel.h b/bsp/phytium/libraries/standalone/arch/common/fkernel.h new file mode 100644 index 0000000000..60efa30bb4 --- /dev/null +++ b/bsp/phytium/libraries/standalone/arch/common/fkernel.h @@ -0,0 +1,237 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: kernel.h + * Date: 2022-02-10 14:53:41 + * LastEditTime: 2022-02-17 17:35:07 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#ifndef KERNEL_H +#define KERNEL_H + +#ifdef __ASSEMBLY__ + #define _AC(X, Y) X + #define _AT(T, X) X +#else + #define __AC(X, Y) (X##Y) + #define _AC(X, Y) __AC(X, Y) + #define _AT(T, X) ((T)(X)) +#endif + +#define _UL(x) (_AC(x, UL)) +#define _ULL(x) (_AC(x, ULL)) + +#define _BITUL(x) (_UL(1) << (x)) +#define _BITULL(x) (_ULL(1) << (x)) + +#define UL(x) (_UL(x)) +#define ULL(x) (_ULL(x)) + +#define min(x, y) ( \ + { \ + typeof(x) _min1 = (x); \ + typeof(y) _min2 = (y); \ + (void)(&_min1 == &_min2); \ + _min1 < _min2 ? _min1 : _min2; \ + }) + +#define max(x, y) ( \ + { \ + typeof(x) _max1 = (x); \ + typeof(y) _max2 = (y); \ + (void)(&_max1 == &_max2); \ + _max1 > _max2 ? _max1 : _max2; \ + }) + +#define min3(x, y, z) min((typeof(x))min(x, y), z) +#define max3(x, y, z) max((typeof(x))max(x, y), z) + +/** + * clamp - return a value clamped to a given range with strict typechecking + * @val: current value + * @lo: lowest allowable value + * @hi: highest allowable value + * + * This macro does strict typechecking of @lo/@hi to make sure they are of the + * same type as @val. See the unnecessary pointer comparisons. + */ +#define clamp(val, lo, hi) min((typeof(val))max(val, lo), hi) + +/** + * do_div - returns 2 values: calculate remainder and update new dividend + * @n: uint64_t dividend (will be updated) + * @base: uint32_t divisor + * + * Summary: + * ``uint32_t remainder = n % base;`` + * ``n = n / base;`` + * + * Return: (uint32_t)remainder + * + * NOTE: macro parameter @n is evaluated multiple times, + * beware of side effects! + */ +#define do_div(n, base) ( \ + { \ + uint32_t __base = (base); \ + uint32_t __rem; \ + __rem = ((uint64_t)(n)) % __base; \ + (n) = ((uint64_t)(n)) / __base; \ + __rem; \ + }) + +/* The `const' in roundup() prevents gcc-3.3 from calling __divdi3 */ +#define roundup(x, y) ( \ + { \ + const typeof(y) __y = y; \ + ((x + (__y - 1)) / __y) * __y; \ + }) +#define rounddown(x, y) ( \ + { \ + typeof(x) __x = (x); \ + __x - (__x % (y)); \ + }) + +#define DIV_ROUND_UP(n, d) (((n) + (d)-1) / (d)) + +#if defined(__aarch64__) + #define BITS_PER_LONG 64 +#else + #define BITS_PER_LONG 32 +#endif + +#ifndef BITS_PER_LONG_LONG + #define BITS_PER_LONG_LONG 64 +#endif + +#define BIT(nr) (1ULL << (nr)) +#define BIT_ULL(nr) (1ULL << (nr)) +#define BIT_MASK(nr) (BIT(nr) - 1UL) +#define BIT_WORD(nr) ((nr) / BITS_PER_LONG) +#define BIT_ULL_MASK(nr) (1ULL << ((nr) % BITS_PER_LONG_LONG)) +#define BIT_ULL_WORD(nr) ((nr) / BITS_PER_LONG_LONG) +#define BITS_PER_BYTE 8 + +#define DIV_ROUND_DOWN_ULL(ll, d) \ + ({ unsigned long long _tmp = (ll); do_div(_tmp, d); _tmp; }) +#define DIV_ROUND_UP_ULL(ll, d) DIV_ROUND_DOWN_ULL((ll) + (d) - 1, (d)) + +#if BITS_PER_LONG == 32 + #define DIV_ROUND_UP_SECTOR_T(ll,d) DIV_ROUND_UP_ULL(ll, d) +#else + #define DIV_ROUND_UP_SECTOR_T(ll,d) DIV_ROUND_UP(ll,d) +#endif + +/* + * Create a contiguous bitmask starting at bit position @l and ending at + * position @h. For example + * GENMASK_ULL(39, 21) gives us the 64bit vector 0x000000ffffe00000. + */ +#define GENMASK(h, l) \ + (((~0UL) - (1UL << (l)) + 1) & (~0UL >> (BITS_PER_LONG - 1 - (h)))) + +#define GENMASK_ULL(h, l) \ + (((~0ULL) - (1ULL << (l)) + 1) & \ + (~0ULL >> (BITS_PER_LONG_LONG - 1 - (h)))) + +#define SZ_1 0x00000001 +#define SZ_2 0x00000002 +#define SZ_4 0x00000004 +#define SZ_8 0x00000008 +#define SZ_16 0x00000010 +#define SZ_32 0x00000020 +#define SZ_64 0x00000040 +#define SZ_128 0x00000080 +#define SZ_256 0x00000100 +#define SZ_512 0x00000200 + +#define SZ_1K 0x00000400 +#define SZ_2K 0x00000800 +#define SZ_4K 0x00001000 +#define SZ_8K 0x00002000 +#define SZ_16K 0x00004000 +#define SZ_32K 0x00008000 +#define SZ_64K 0x00010000 +#define SZ_128K 0x00020000 +#define SZ_256K 0x00040000 +#define SZ_512K 0x00080000 + +#define SZ_1M 0x00100000 +#define SZ_2M 0x00200000 +#define SZ_4M 0x00400000 +#define SZ_8M 0x00800000 +#define SZ_16M 0x01000000 +#define SZ_32M 0x02000000 +#define SZ_64M 0x04000000 +#define SZ_128M 0x08000000 +#define SZ_256M 0x10000000 +#define SZ_512M 0x20000000 + +#define SZ_1G 0x40000000 +#define SZ_2G 0x80000000 +#define SZ_3G 0xC0000000 +#define SZ_4G 0x100000000ULL +#define SZ_8G 0x200000000ULL + +#define NANO_TO_MICRO 1000 +#define NANO_TO_KILO 1000000 + +/** + * UPPER_32_BITS - return bits 32-63 of a number + * @n: the number we're accessing + * + * A basic shift-right of a 64- or 32-bit quantity. Use this to suppress + * the "right shift count >= width of type" warning when that quantity is + * 32-bits. + * Note that do not input signed int 'n' + */ +#define UPPER_32_BITS(n) ((uint32_t)(((n) >> 16) >> 16)) + +/** + * LOWER_32_BITS - return bits 0-31 of a number + * @n: the number we're accessing + * Note that do not input signed int 'n' + */ +#define LOWER_32_BITS(n) ((uint32_t)((n)&0xffffffff)) +#define IS_ALIGNED(x, a) (((x) & ((typeof(x))(a)-1)) == 0) + +#ifndef __aligned + #define __aligned(x) __attribute__((__aligned__(x))) +#endif + +/** + * CONTAINER_OF - return the member address of ptr, if the type of ptr is the + * struct type. + */ +#define CONTAINER_OF(ptr, type, member) \ + ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member))) + +#ifndef ARRAY_SIZE + #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#endif + +/* set 32-bit register [a:b] as x, where a is high bit, b is low bit, x is setting/getting value */ +#define GET_REG32_BITS(x, a, b) (u32)((((u32)(x)) & GENMASK(a, b)) >> b) +#define SET_REG32_BITS(x, a, b) (u32)((((u32)(x)) << b) & GENMASK(a, b)) + +/* Integer alignment down */ +#define PALIGN_DOWN(x,align) (x & ~(align-1)) +/* Integer alignment up */ +#define PALIGN_UP(x,align) ((x + (align-1)) & ~(align-1)) +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/board/Kconfig b/bsp/phytium/libraries/standalone/board/Kconfig new file mode 100644 index 0000000000..c8fee782a4 --- /dev/null +++ b/bsp/phytium/libraries/standalone/board/Kconfig @@ -0,0 +1,50 @@ +menu "Board Configuration" + + choice BUILD_TARGET_CHIP_TYPE + prompt "Chip" + default TARGET_E2000Q + help + Select chip type for build + + config TARGET_F2000_4 + bool "FT2000-4" + + config TARGET_D2000 + bool "D2000" + + config TARGET_E2000Q + bool "E2000Q" + select TARGET_E2000 + + config TARGET_E2000D + bool "E2000D" + select TARGET_E2000 + + config TARGET_E2000S + bool "E2000S" + select TARGET_E2000 + + endchoice # BUILD_TARGET_CHIP_TYPE + + # an invisible config to define common code of E2000 Q/D/S + config TARGET_E2000 + bool + default y if TARGET_E2000Q || TARGET_E2000D || TARGET_E2000S + + choice DEBUG_PRINT_UART + prompt "Select Debug uart instance" + default DEFAULT_DEBUG_PRINT_UART1 + help + Select arch for build + config DEFAULT_DEBUG_PRINT_UART1 + bool "Use uart1" + config DEFAULT_DEBUG_PRINT_UART0 + bool "Use uart0" + config DEFAULT_DEBUG_PRINT_UART2 + bool "Use uart2" + endchoice # DEBUG_PRINT_UART + + +endmenu + + diff --git a/bsp/phytium/libraries/standalone/board/common/fcpu_asm.S b/bsp/phytium/libraries/standalone/board/common/fcpu_asm.S new file mode 100644 index 0000000000..530186fecf --- /dev/null +++ b/bsp/phytium/libraries/standalone/board/common/fcpu_asm.S @@ -0,0 +1,63 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: _cpu_asm.S + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-17 17:57:55 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "sdkconfig.h" + +#ifdef CONFIG_TARGET_ARMV8_AARCH64 +// ------------------------------------------------------------ + .global GetAffinity + //uint32_t GetAffinity(void); + .type GetAffinity, @function +GetAffinity: + MRS x0, MPIDR_EL1 + RET + + +.global ArchSpinLock; +.text; +ArchSpinLock: + mov w2, #1 + sevl +1: + wfe + ldaxr w1, [x0] + cbnz w1, 1b + stxr w1, w2, [x0] + cbnz w1, 1b + ret + + +.global ArchSpinUnlock; +.text; +ArchSpinUnlock: + stlr xzr, [x0] + ret + +#else + +.globl GetAffinity +GetAffinity: + mrc p15, #0, r0, c0, c0, #5 @ read multiprocessor affinity register + bx lr + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/board/common/fcpu_info.c b/bsp/phytium/libraries/standalone/board/common/fcpu_info.c new file mode 100644 index 0000000000..7e711e2018 --- /dev/null +++ b/bsp/phytium/libraries/standalone/board/common/fcpu_info.c @@ -0,0 +1,264 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: cpu_info.c + * Date: 2022-03-08 19:37:19 + * LastEditTime: 2022-03-15 11:18:14 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "fcpu_info.h" +#include "ferror_code.h" +#include "fparameters.h" +#include "fprintk.h" + +FError GetCpuId(u32 *cpu_id_p) +{ + u32 affinity = GetAffinity(); + FError ret = ERR_SUCCESS ; + + switch (affinity & 0xfff) + { +#ifdef CORE0_AFF + case CORE0_AFF: + *cpu_id_p = 0 ; + break; +#endif +#ifdef CORE1_AFF + case CORE1_AFF: + *cpu_id_p = 1 ; + break; +#endif +#ifdef CORE2_AFF + case CORE2_AFF: + *cpu_id_p = 2; + break; +#endif +#ifdef CORE3_AFF + case CORE3_AFF: + *cpu_id_p = 3 ; + break; +#endif +#ifdef CORE4_AFF + case CORE4_AFF: + *cpu_id_p = 4 ; + break; +#endif +#ifdef CORE5_AFF + case CORE5_AFF: + *cpu_id_p = 5 ; + break; +#endif +#ifdef CORE6_AFF + case CORE6_AFF: + *cpu_id_p = 6 ; + break; +#endif +#ifdef CORE7_AFF + case CORE7_AFF: + *cpu_id_p = 7 ; + break; +#endif + default: + ret = ERR_GENERAL ; + break; + } + return ret; +} + +/** + * @name: GetCpuAffinityByMask + * @msg: Determine the cluster information using the CPU ID + * @param {u32} cpu_id cpu id mask .for example : 1 is core0 ,2 is core1 ..... + * @param {u64} *affinity_level_p cluster information , format is: + * |--------[bit31-24]-------[bit23-16]-------------[bit15-8]--------[bit7-0] + * |--------Affinity level3-----Affinity level2-----Affinity level1---Affinity level0 + * @return {*} ERR_SUCCESS is ok + */ +FError GetCpuAffinityByMask(u32 cpu_id_mask, u64 *affinity_level_p) +{ + FError ret = ERR_SUCCESS ; + switch (cpu_id_mask) + { +#ifdef CORE0_AFF + case (1<<0): + *affinity_level_p = CORE0_AFF; + break ; +#endif +#ifdef CORE1_AFF + case (1<<1): + *affinity_level_p = CORE1_AFF; + break ; +#endif +#ifdef CORE2_AFF + case (1<<2): + *affinity_level_p = CORE2_AFF; + break ; +#endif +#ifdef CORE3_AFF + case (1<<3): + *affinity_level_p = CORE3_AFF; + break ; +#endif +#ifdef CORE4_AFF + case (1<<4): + *affinity_level_p = CORE4_AFF; + break ; +#endif +#ifdef CORE5_AFF + case (1<<5): + *affinity_level_p = CORE5_AFF; + break ; +#endif +#ifdef CORE6_AFF + case (1<<6): + *affinity_level_p = CORE6_AFF; + break ; +#endif +#ifdef CORE7_AFF + case (1<<7): + *affinity_level_p = CORE7_AFF; + break ; +#endif + default: + ret = ERR_GENERAL; + break; + } + return ret; +} + + +/** + * @name: GetCpuAffinity + * @msg: Determine the cluster information using the CPU ID + * @param {u32} cpu_id cpu id .for example : 0 is core0 ,1 is core1 ..... + * @param {u64} *affinity_level_p cluster information , format is: + * |--------[bit31-24]-------[bit23-16]-------------[bit15-8]--------[bit7-0] + * |--------Affinity level3-----Affinity level2-----Affinity level1---Affinity level0 + * @return {*} ERR_SUCCESS is ok + */ +FError GetCpuAffinity(u32 cpu_id, u64 *affinity_level_p) +{ + FError ret = ERR_SUCCESS ; + switch (cpu_id) + { +#ifdef CORE0_AFF + case (0): + *affinity_level_p = CORE0_AFF; + break ; +#endif +#ifdef CORE1_AFF + case (1): + *affinity_level_p = CORE1_AFF; + break ; +#endif +#ifdef CORE2_AFF + case (2): + *affinity_level_p = CORE2_AFF; + break ; +#endif +#ifdef CORE3_AFF + case (3): + *affinity_level_p = CORE3_AFF; + break ; +#endif +#ifdef CORE4_AFF + case (4): + *affinity_level_p = CORE4_AFF; + break ; +#endif +#ifdef CORE5_AFF + case (5): + *affinity_level_p = CORE5_AFF; + break ; +#endif +#ifdef CORE6_AFF + case (6): + *affinity_level_p = CORE6_AFF; + break ; +#endif +#ifdef CORE7_AFF + case (7): + *affinity_level_p = CORE7_AFF; + break ; +#endif + default: + ret = ERR_GENERAL; + break; + } + return ret; +} + + +/** + * @name: UseAffinityGetCpuId + * @msg: Get the core value from affinity level + * @param {u64} affinity_level is cpu affinity level value + * @param {u32*} cpu_id_p is pointer to get cpu id value + * @return {*} ERR_SUCCESS is ok , ERR_GENERAL is fail + */ +FError UseAffinityGetCpuId(u64 affinity_level, u32 *cpu_id_p) +{ + FError ret = ERR_SUCCESS ; + switch (affinity_level) + { +#ifdef CORE0_AFF + case CORE0_AFF: + *cpu_id_p = 0; + break ; +#endif +#ifdef CORE1_AFF + case CORE1_AFF: + *cpu_id_p = 1; + break ; +#endif +#ifdef CORE2_AFF + case CORE2_AFF: + *cpu_id_p = 2; + break ; +#endif +#ifdef CORE3_AFF + case CORE3_AFF: + *cpu_id_p = 3; + break ; +#endif +#ifdef CORE4_AFF + case CORE4_AFF: + *cpu_id_p = 4; + break ; +#endif +#ifdef CORE5_AFF + case CORE5_AFF: + *cpu_id_p = 5; + break ; +#endif +#ifdef CORE6_AFF + case CORE6_AFF: + *cpu_id_p = 6; + break ; +#endif +#ifdef CORE7_AFF + case CORE7_AFF: + *cpu_id_p = 7; + break ; +#endif + default: + ret = ERR_GENERAL; + break; + } + return ret; +} diff --git a/bsp/phytium/libraries/standalone/board/common/fcpu_info.h b/bsp/phytium/libraries/standalone/board/common/fcpu_info.h new file mode 100644 index 0000000000..b5434f2d19 --- /dev/null +++ b/bsp/phytium/libraries/standalone/board/common/fcpu_info.h @@ -0,0 +1,44 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fcpu_info.h + * Date: 2022-03-08 19:37:19 + * LastEditTime: 2022-03-15 11:18:07 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef BOARD_COMMON_CPU_INFO_H +#define BOARD_COMMON_CPU_INFO_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ftypes.h" +#include "ferror_code.h" +u32 GetAffinity(void); +FError GetCpuId(u32 *cpu_id_p); +FError GetCpuAffinity(u32 cpu_id, u64 *cluster_value_p); +FError GetCpuAffinityByMask(u32 cpu_id, u64 *affinity_level_p); +FError UseAffinityGetCpuId(u64 affinity_level, u32 *cpu_id_p); +u32 GetCpuMaskToAffval(u32 *cpu_mask, u32 *cluster_id, u32 *target_list); +#ifdef __cplusplus +} +#endif + +#endif // ! \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/board/common/fsmp.h b/bsp/phytium/libraries/standalone/board/common/fsmp.h new file mode 100644 index 0000000000..b0ee12f339 --- /dev/null +++ b/bsp/phytium/libraries/standalone/board/common/fsmp.h @@ -0,0 +1,34 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsmp.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-17 17:58:18 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#ifndef BSP_BOARD_COMMON_SMP_H +#define BSP_BOARD_COMMON_SMP_H + +#include "ftypes.h" + +void SpinLockInit(unsigned long global_addr); +void SpinLock(void); +void SpinUnlock(void); + +#endif // DEBUG \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/board/e2000/d/fiopad.h b/bsp/phytium/libraries/standalone/board/e2000/d/fiopad.h new file mode 100644 index 0000000000..41af7df2ed --- /dev/null +++ b/bsp/phytium/libraries/standalone/board/e2000/d/fiopad.h @@ -0,0 +1,269 @@ + +#ifndef BOARD_E2000D_FIOPAD_H +#define BOARD_E2000D_FIOPAD_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ +#include "fiopad_comm.h" + +/************************** Constant Definitions *****************************/ +/* register offset of iopad function / pull / driver strength */ +#define FIOPAD_AN55 (FPinIndex)FIOPAD_INDEX(FIOPAD_0_FUNC_OFFSET) +#define FIOPAD_AW43 (FPinIndex)FIOPAD_INDEX(FIOPAD_2_FUNC_OFFSET) +#define FIOPAD_AR51 (FPinIndex)FIOPAD_INDEX(FIOPAD_9_FUNC_OFFSET) +#define FIOPAD_AJ51 (FPinIndex)FIOPAD_INDEX(FIOPAD_10_FUNC_OFFSET) +#define FIOPAD_AL51 (FPinIndex)FIOPAD_INDEX(FIOPAD_11_FUNC_OFFSET) +#define FIOPAD_AL49 (FPinIndex)FIOPAD_INDEX(FIOPAD_12_FUNC_OFFSET) +#define FIOPAD_AN47 (FPinIndex)FIOPAD_INDEX(FIOPAD_13_FUNC_OFFSET) +#define FIOPAD_AR47 (FPinIndex)FIOPAD_INDEX(FIOPAD_14_FUNC_OFFSET) +#define FIOPAD_BA53 (FPinIndex)FIOPAD_INDEX(FIOPAD_15_FUNC_OFFSET) +#define FIOPAD_BA55 (FPinIndex)FIOPAD_INDEX(FIOPAD_16_FUNC_OFFSET) +#define FIOPAD_AW53 (FPinIndex)FIOPAD_INDEX(FIOPAD_17_FUNC_OFFSET) +#define FIOPAD_AW55 (FPinIndex)FIOPAD_INDEX(FIOPAD_18_FUNC_OFFSET) +#define FIOPAD_AU51 (FPinIndex)FIOPAD_INDEX(FIOPAD_19_FUNC_OFFSET) +#define FIOPAD_AN53 (FPinIndex)FIOPAD_INDEX(FIOPAD_20_FUNC_OFFSET) +#define FIOPAD_AL55 (FPinIndex)FIOPAD_INDEX(FIOPAD_21_FUNC_OFFSET) +#define FIOPAD_AJ55 (FPinIndex)FIOPAD_INDEX(FIOPAD_22_FUNC_OFFSET) +#define FIOPAD_AJ53 (FPinIndex)FIOPAD_INDEX(FIOPAD_23_FUNC_OFFSET) +#define FIOPAD_AG55 (FPinIndex)FIOPAD_INDEX(FIOPAD_24_FUNC_OFFSET) +#define FIOPAD_AG53 (FPinIndex)FIOPAD_INDEX(FIOPAD_25_FUNC_OFFSET) +#define FIOPAD_AE55 (FPinIndex)FIOPAD_INDEX(FIOPAD_26_FUNC_OFFSET) +#define FIOPAD_AC55 (FPinIndex)FIOPAD_INDEX(FIOPAD_27_FUNC_OFFSET) +#define FIOPAD_AC53 (FPinIndex)FIOPAD_INDEX(FIOPAD_28_FUNC_OFFSET) +#define FIOPAD_AR45 (FPinIndex)FIOPAD_INDEX(FIOPAD_31_FUNC_OFFSET) +#define FIOPAD_BA51 (FPinIndex)FIOPAD_INDEX(FIOPAD_32_FUNC_OFFSET) +#define FIOPAD_BA49 (FPinIndex)FIOPAD_INDEX(FIOPAD_33_FUNC_OFFSET) +#define FIOPAD_AR55 (FPinIndex)FIOPAD_INDEX(FIOPAD_34_FUNC_OFFSET) +#define FIOPAD_AU55 (FPinIndex)FIOPAD_INDEX(FIOPAD_35_FUNC_OFFSET) +#define FIOPAD_AR53 (FPinIndex)FIOPAD_INDEX(FIOPAD_36_FUNC_OFFSET) +#define FIOPAD_BA45 (FPinIndex)FIOPAD_INDEX(FIOPAD_37_FUNC_OFFSET) +#define FIOPAD_AW51 (FPinIndex)FIOPAD_INDEX(FIOPAD_38_FUNC_OFFSET) +#define FIOPAD_A31 (FPinIndex)FIOPAD_INDEX(FIOPAD_39_FUNC_OFFSET) +#define FIOPAD_R53 (FPinIndex)FIOPAD_INDEX(FIOPAD_40_FUNC_OFFSET) +#define FIOPAD_R55 (FPinIndex)FIOPAD_INDEX(FIOPAD_41_FUNC_OFFSET) +#define FIOPAD_U55 (FPinIndex)FIOPAD_INDEX(FIOPAD_42_FUNC_OFFSET) +#define FIOPAD_W55 (FPinIndex)FIOPAD_INDEX(FIOPAD_43_FUNC_OFFSET) +#define FIOPAD_U53 (FPinIndex)FIOPAD_INDEX(FIOPAD_44_FUNC_OFFSET) +#define FIOPAD_AA53 (FPinIndex)FIOPAD_INDEX(FIOPAD_45_FUNC_OFFSET) +#define FIOPAD_AA55 (FPinIndex)FIOPAD_INDEX(FIOPAD_46_FUNC_OFFSET) +#define FIOPAD_AW47 (FPinIndex)FIOPAD_INDEX(FIOPAD_47_FUNC_OFFSET) +#define FIOPAD_AU47 (FPinIndex)FIOPAD_INDEX(FIOPAD_48_FUNC_OFFSET) +#define FIOPAD_A35 (FPinIndex)FIOPAD_INDEX(FIOPAD_49_FUNC_OFFSET) +#define FIOPAD_C35 (FPinIndex)FIOPAD_INDEX(FIOPAD_50_FUNC_OFFSET) +#define FIOPAD_C33 (FPinIndex)FIOPAD_INDEX(FIOPAD_51_FUNC_OFFSET) +#define FIOPAD_A33 (FPinIndex)FIOPAD_INDEX(FIOPAD_52_FUNC_OFFSET) +#define FIOPAD_A37 (FPinIndex)FIOPAD_INDEX(FIOPAD_53_FUNC_OFFSET) +#define FIOPAD_A39 (FPinIndex)FIOPAD_INDEX(FIOPAD_54_FUNC_OFFSET) +#define FIOPAD_A41 (FPinIndex)FIOPAD_INDEX(FIOPAD_55_FUNC_OFFSET) +#define FIOPAD_C41 (FPinIndex)FIOPAD_INDEX(FIOPAD_56_FUNC_OFFSET) +#define FIOPAD_A43 (FPinIndex)FIOPAD_INDEX(FIOPAD_57_FUNC_OFFSET) +#define FIOPAD_A45 (FPinIndex)FIOPAD_INDEX(FIOPAD_58_FUNC_OFFSET) +#define FIOPAD_C45 (FPinIndex)FIOPAD_INDEX(FIOPAD_59_FUNC_OFFSET) +#define FIOPAD_A47 (FPinIndex)FIOPAD_INDEX(FIOPAD_60_FUNC_OFFSET) +#define FIOPAD_A29 (FPinIndex)FIOPAD_INDEX(FIOPAD_61_FUNC_OFFSET) +#define FIOPAD_C29 (FPinIndex)FIOPAD_INDEX(FIOPAD_62_FUNC_OFFSET) +#define FIOPAD_C27 (FPinIndex)FIOPAD_INDEX(FIOPAD_63_FUNC_OFFSET) +#define FIOPAD_A27 (FPinIndex)FIOPAD_INDEX(FIOPAD_64_FUNC_OFFSET) +#define FIOPAD_AJ49 (FPinIndex)FIOPAD_INDEX(FIOPAD_65_FUNC_OFFSET) +#define FIOPAD_AL45 (FPinIndex)FIOPAD_INDEX(FIOPAD_66_FUNC_OFFSET) +#define FIOPAD_AL43 (FPinIndex)FIOPAD_INDEX(FIOPAD_67_FUNC_OFFSET) +#define FIOPAD_AN45 (FPinIndex)FIOPAD_INDEX(FIOPAD_68_FUNC_OFFSET) +#define FIOPAD_AG47 (FPinIndex)FIOPAD_INDEX(FIOPAD_148_FUNC_OFFSET) +#define FIOPAD_AJ47 (FPinIndex)FIOPAD_INDEX(FIOPAD_69_FUNC_OFFSET) +#define FIOPAD_AG45 (FPinIndex)FIOPAD_INDEX(FIOPAD_70_FUNC_OFFSET) +#define FIOPAD_AE51 (FPinIndex)FIOPAD_INDEX(FIOPAD_71_FUNC_OFFSET) +#define FIOPAD_AE49 (FPinIndex)FIOPAD_INDEX(FIOPAD_72_FUNC_OFFSET) +#define FIOPAD_AG51 (FPinIndex)FIOPAD_INDEX(FIOPAD_73_FUNC_OFFSET) +#define FIOPAD_AJ45 (FPinIndex)FIOPAD_INDEX(FIOPAD_74_FUNC_OFFSET) +#define FIOPAD_AC51 (FPinIndex)FIOPAD_INDEX(FIOPAD_75_FUNC_OFFSET) +#define FIOPAD_AC49 (FPinIndex)FIOPAD_INDEX(FIOPAD_76_FUNC_OFFSET) +#define FIOPAD_AE47 (FPinIndex)FIOPAD_INDEX(FIOPAD_77_FUNC_OFFSET) +#define FIOPAD_W47 (FPinIndex)FIOPAD_INDEX(FIOPAD_78_FUNC_OFFSET) +#define FIOPAD_W51 (FPinIndex)FIOPAD_INDEX(FIOPAD_79_FUNC_OFFSET) +#define FIOPAD_W49 (FPinIndex)FIOPAD_INDEX(FIOPAD_80_FUNC_OFFSET) +#define FIOPAD_U51 (FPinIndex)FIOPAD_INDEX(FIOPAD_81_FUNC_OFFSET) +#define FIOPAD_U49 (FPinIndex)FIOPAD_INDEX(FIOPAD_82_FUNC_OFFSET) +#define FIOPAD_AE45 (FPinIndex)FIOPAD_INDEX(FIOPAD_83_FUNC_OFFSET) +#define FIOPAD_AC45 (FPinIndex)FIOPAD_INDEX(FIOPAD_84_FUNC_OFFSET) +#define FIOPAD_AE43 (FPinIndex)FIOPAD_INDEX(FIOPAD_85_FUNC_OFFSET) +#define FIOPAD_AA43 (FPinIndex)FIOPAD_INDEX(FIOPAD_86_FUNC_OFFSET) +#define FIOPAD_AA45 (FPinIndex)FIOPAD_INDEX(FIOPAD_87_FUNC_OFFSET) +#define FIOPAD_W45 (FPinIndex)FIOPAD_INDEX(FIOPAD_88_FUNC_OFFSET) +#define FIOPAD_AA47 (FPinIndex)FIOPAD_INDEX(FIOPAD_89_FUNC_OFFSET) +#define FIOPAD_U45 (FPinIndex)FIOPAD_INDEX(FIOPAD_90_FUNC_OFFSET) +#define FIOPAD_G55 (FPinIndex)FIOPAD_INDEX(FIOPAD_91_FUNC_OFFSET) +#define FIOPAD_J55 (FPinIndex)FIOPAD_INDEX(FIOPAD_92_FUNC_OFFSET) +#define FIOPAD_L53 (FPinIndex)FIOPAD_INDEX(FIOPAD_93_FUNC_OFFSET) +#define FIOPAD_C55 (FPinIndex)FIOPAD_INDEX(FIOPAD_94_FUNC_OFFSET) +#define FIOPAD_E55 (FPinIndex)FIOPAD_INDEX(FIOPAD_95_FUNC_OFFSET) +#define FIOPAD_J53 (FPinIndex)FIOPAD_INDEX(FIOPAD_96_FUNC_OFFSET) +#define FIOPAD_L55 (FPinIndex)FIOPAD_INDEX(FIOPAD_97_FUNC_OFFSET) +#define FIOPAD_N55 (FPinIndex)FIOPAD_INDEX(FIOPAD_98_FUNC_OFFSET) +#define FIOPAD_C53 (FPinIndex)FIOPAD_INDEX(FIOPAD_29_FUNC_OFFSET) +#define FIOPAD_E53 (FPinIndex)FIOPAD_INDEX(FIOPAD_30_FUNC_OFFSET) +#define FIOPAD_E27 (FPinIndex)FIOPAD_INDEX(FIOPAD_99_FUNC_OFFSET) +#define FIOPAD_G27 (FPinIndex)FIOPAD_INDEX(FIOPAD_100_FUNC_OFFSET) +#define FIOPAD_N37 (FPinIndex)FIOPAD_INDEX(FIOPAD_101_FUNC_OFFSET) +#define FIOPAD_N35 (FPinIndex)FIOPAD_INDEX(FIOPAD_102_FUNC_OFFSET) +#define FIOPAD_J29 (FPinIndex)FIOPAD_INDEX(FIOPAD_103_FUNC_OFFSET) +#define FIOPAD_N29 (FPinIndex)FIOPAD_INDEX(FIOPAD_104_FUNC_OFFSET) +#define FIOPAD_L29 (FPinIndex)FIOPAD_INDEX(FIOPAD_105_FUNC_OFFSET) +#define FIOPAD_N41 (FPinIndex)FIOPAD_INDEX(FIOPAD_106_FUNC_OFFSET) +#define FIOPAD_N39 (FPinIndex)FIOPAD_INDEX(FIOPAD_107_FUNC_OFFSET) +#define FIOPAD_L27 (FPinIndex)FIOPAD_INDEX(FIOPAD_108_FUNC_OFFSET) +#define FIOPAD_J27 (FPinIndex)FIOPAD_INDEX(FIOPAD_109_FUNC_OFFSET) +#define FIOPAD_J25 (FPinIndex)FIOPAD_INDEX(FIOPAD_110_FUNC_OFFSET) +#define FIOPAD_E25 (FPinIndex)FIOPAD_INDEX(FIOPAD_111_FUNC_OFFSET) +#define FIOPAD_G25 (FPinIndex)FIOPAD_INDEX(FIOPAD_112_FUNC_OFFSET) +#define FIOPAD_N23 (FPinIndex)FIOPAD_INDEX(FIOPAD_113_FUNC_OFFSET) +#define FIOPAD_L25 (FPinIndex)FIOPAD_INDEX(FIOPAD_114_FUNC_OFFSET) +#define FIOPAD_J33 (FPinIndex)FIOPAD_INDEX(FIOPAD_115_FUNC_OFFSET) +#define FIOPAD_J35 (FPinIndex)FIOPAD_INDEX(FIOPAD_116_FUNC_OFFSET) +#define FIOPAD_G37 (FPinIndex)FIOPAD_INDEX(FIOPAD_117_FUNC_OFFSET) +#define FIOPAD_E39 (FPinIndex)FIOPAD_INDEX(FIOPAD_118_FUNC_OFFSET) +#define FIOPAD_L39 (FPinIndex)FIOPAD_INDEX(FIOPAD_119_FUNC_OFFSET) +#define FIOPAD_C39 (FPinIndex)FIOPAD_INDEX(FIOPAD_120_FUNC_OFFSET) +#define FIOPAD_E37 (FPinIndex)FIOPAD_INDEX(FIOPAD_121_FUNC_OFFSET) +#define FIOPAD_L41 (FPinIndex)FIOPAD_INDEX(FIOPAD_122_FUNC_OFFSET) +#define FIOPAD_J39 (FPinIndex)FIOPAD_INDEX(FIOPAD_123_FUNC_OFFSET) +#define FIOPAD_J37 (FPinIndex)FIOPAD_INDEX(FIOPAD_124_FUNC_OFFSET) +#define FIOPAD_L35 (FPinIndex)FIOPAD_INDEX(FIOPAD_125_FUNC_OFFSET) +#define FIOPAD_E33 (FPinIndex)FIOPAD_INDEX(FIOPAD_126_FUNC_OFFSET) +#define FIOPAD_E31 (FPinIndex)FIOPAD_INDEX(FIOPAD_127_FUNC_OFFSET) +#define FIOPAD_G31 (FPinIndex)FIOPAD_INDEX(FIOPAD_128_FUNC_OFFSET) +#define FIOPAD_J31 (FPinIndex)FIOPAD_INDEX(FIOPAD_129_FUNC_OFFSET) +#define FIOPAD_L33 (FPinIndex)FIOPAD_INDEX(FIOPAD_130_FUNC_OFFSET) +#define FIOPAD_N31 (FPinIndex)FIOPAD_INDEX(FIOPAD_131_FUNC_OFFSET) +#define FIOPAD_R47 (FPinIndex)FIOPAD_INDEX(FIOPAD_132_FUNC_OFFSET) +#define FIOPAD_R45 (FPinIndex)FIOPAD_INDEX(FIOPAD_133_FUNC_OFFSET) +#define FIOPAD_N47 (FPinIndex)FIOPAD_INDEX(FIOPAD_134_FUNC_OFFSET) +#define FIOPAD_N51 (FPinIndex)FIOPAD_INDEX(FIOPAD_135_FUNC_OFFSET) +#define FIOPAD_L51 (FPinIndex)FIOPAD_INDEX(FIOPAD_136_FUNC_OFFSET) +#define FIOPAD_J51 (FPinIndex)FIOPAD_INDEX(FIOPAD_137_FUNC_OFFSET) +#define FIOPAD_J41 (FPinIndex)FIOPAD_INDEX(FIOPAD_138_FUNC_OFFSET) +#define FIOPAD_E43 (FPinIndex)FIOPAD_INDEX(FIOPAD_139_FUNC_OFFSET) +#define FIOPAD_G43 (FPinIndex)FIOPAD_INDEX(FIOPAD_140_FUNC_OFFSET) +#define FIOPAD_J43 (FPinIndex)FIOPAD_INDEX(FIOPAD_141_FUNC_OFFSET) +#define FIOPAD_J45 (FPinIndex)FIOPAD_INDEX(FIOPAD_142_FUNC_OFFSET) +#define FIOPAD_N45 (FPinIndex)FIOPAD_INDEX(FIOPAD_143_FUNC_OFFSET) +#define FIOPAD_L47 (FPinIndex)FIOPAD_INDEX(FIOPAD_144_FUNC_OFFSET) +#define FIOPAD_L45 (FPinIndex)FIOPAD_INDEX(FIOPAD_145_FUNC_OFFSET) +#define FIOPAD_N49 (FPinIndex)FIOPAD_INDEX(FIOPAD_146_FUNC_OFFSET) +#define FIOPAD_J49 (FPinIndex)FIOPAD_INDEX(FIOPAD_147_FUNC_OFFSET) + +/* register offset of iopad delay */ +#define FIOPAD_AJ51_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_10_DELAY_OFFSET) +#define FIOPAD_AL51_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_11_DELAY_OFFSET) +#define FIOPAD_AL49_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_12_DELAY_OFFSET) +#define FIOPAD_AN47_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_13_DELAY_OFFSET) +#define FIOPAD_AR47_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_14_DELAY_OFFSET) +#define FIOPAD_AJ53_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_23_DELAY_OFFSET) +#define FIOPAD_AG55_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_24_DELAY_OFFSET) +#define FIOPAD_AG53_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_25_DELAY_OFFSET) +#define FIOPAD_AE55_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_26_DELAY_OFFSET) +#define FIOPAD_BA51_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_32_DELAY_OFFSET) +#define FIOPAD_BA49_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_33_DELAY_OFFSET) +#define FIOPAD_AR55_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_34_DELAY_OFFSET) +#define FIOPAD_AU55_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_35_DELAY_OFFSET) +#define FIOPAD_A41_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_55_DELAY_OFFSET) +#define FIOPAD_C41_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_56_DELAY_OFFSET) +#define FIOPAD_A43_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_57_DELAY_OFFSET) +#define FIOPAD_A45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_58_DELAY_OFFSET) +#define FIOPAD_C45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_59_DELAY_OFFSET) +#define FIOPAD_A47_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_60_DELAY_OFFSET) +#define FIOPAD_A29_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_61_DELAY_OFFSET) +#define FIOPAD_C29_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_62_DELAY_OFFSET) +#define FIOPAD_C27_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_63_DELAY_OFFSET) +#define FIOPAD_A27_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_64_DELAY_OFFSET) +#define FIOPAD_AJ49_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_65_DELAY_OFFSET) +#define FIOPAD_AL45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_66_DELAY_OFFSET) +#define FIOPAD_AL43_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_67_DELAY_OFFSET) +#define FIOPAD_AN45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_68_DELAY_OFFSET) +#define FIOPAD_AG47_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_148_DELAY_OFFSET) +#define FIOPAD_AJ47_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_69_DELAY_OFFSET) +#define FIOPAD_AG45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_70_DELAY_OFFSET) +#define FIOPAD_AE51_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_71_DELAY_OFFSET) +#define FIOPAD_AE49_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_72_DELAY_OFFSET) +#define FIOPAD_AG51_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_73_DELAY_OFFSET) +#define FIOPAD_AJ45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_74_DELAY_OFFSET) +#define FIOPAD_AC51_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_75_DELAY_OFFSET) +#define FIOPAD_AC49_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_76_DELAY_OFFSET) +#define FIOPAD_AE47_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_77_DELAY_OFFSET) +#define FIOPAD_W47_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_78_DELAY_OFFSET) +#define FIOPAD_W49_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_80_DELAY_OFFSET) +#define FIOPAD_U51_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_81_DELAY_OFFSET) +#define FIOPAD_U49_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_82_DELAY_OFFSET) +#define FIOPAD_AE45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_83_DELAY_OFFSET) +#define FIOPAD_AC45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_84_DELAY_OFFSET) +#define FIOPAD_AE43_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_85_DELAY_OFFSET) +#define FIOPAD_AA43_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_86_DELAY_OFFSET) +#define FIOPAD_AA45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_87_DELAY_OFFSET) +#define FIOPAD_W45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_88_DELAY_OFFSET) +#define FIOPAD_AA47_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_89_DELAY_OFFSET) +#define FIOPAD_U45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_90_DELAY_OFFSET) +#define FIOPAD_J55_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_92_DELAY_OFFSET) +#define FIOPAD_L53_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_93_DELAY_OFFSET) +#define FIOPAD_C55_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_94_DELAY_OFFSET) +#define FIOPAD_E55_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_95_DELAY_OFFSET) +#define FIOPAD_J53_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_96_DELAY_OFFSET) +#define FIOPAD_L55_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_97_DELAY_OFFSET) +#define FIOPAD_N55_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_98_DELAY_OFFSET) +#define FIOPAD_E27_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_99_DELAY_OFFSET) +#define FIOPAD_G27_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_100_DELAY_OFFSET) +#define FIOPAD_N37_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_101_DELAY_OFFSET) +#define FIOPAD_N35_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_102_DELAY_OFFSET) +#define FIOPAD_J29_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_103_DELAY_OFFSET) +#define FIOPAD_N29_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_104_DELAY_OFFSET) +#define FIOPAD_L29_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_105_DELAY_OFFSET) +#define FIOPAD_N41_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_106_DELAY_OFFSET) +#define FIOPAD_N39_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_107_DELAY_OFFSET) +#define FIOPAD_L27_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_108_DELAY_OFFSET) +#define FIOPAD_J27_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_109_DELAY_OFFSET) +#define FIOPAD_J25_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_110_DELAY_OFFSET) +#define FIOPAD_E25_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_111_DELAY_OFFSET) +#define FIOPAD_G25_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_112_DELAY_OFFSET) +#define FIOPAD_J33_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_115_DELAY_OFFSET) +#define FIOPAD_J35_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_116_DELAY_OFFSET) +#define FIOPAD_G37_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_117_DELAY_OFFSET) +#define FIOPAD_E39_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_118_DELAY_OFFSET) +#define FIOPAD_L39_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_119_DELAY_OFFSET) +#define FIOPAD_C39_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_120_DELAY_OFFSET) +#define FIOPAD_E37_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_121_DELAY_OFFSET) +#define FIOPAD_L41_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_122_DELAY_OFFSET) +#define FIOPAD_J39_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_123_DELAY_OFFSET) +#define FIOPAD_J37_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_124_DELAY_OFFSET) +#define FIOPAD_L35_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_125_DELAY_OFFSET) +#define FIOPAD_E33_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_126_DELAY_OFFSET) +#define FIOPAD_E31_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_127_DELAY_OFFSET) +#define FIOPAD_G31_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_128_DELAY_OFFSET) +#define FIOPAD_L51_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_136_DELAY_OFFSET) +#define FIOPAD_J51_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_137_DELAY_OFFSET) +#define FIOPAD_J41_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_138_DELAY_OFFSET) +#define FIOPAD_E43_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_139_DELAY_OFFSET) +#define FIOPAD_G43_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_140_DELAY_OFFSET) +#define FIOPAD_J43_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_141_DELAY_OFFSET) +#define FIOPAD_J45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_142_DELAY_OFFSET) +#define FIOPAD_N45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_143_DELAY_OFFSET) +#define FIOPAD_L47_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_144_DELAY_OFFSET) +#define FIOPAD_L45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_145_DELAY_OFFSET) +#define FIOPAD_N49_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_146_DELAY_OFFSET) +#define FIOPAD_J49_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_147_DELAY_OFFSET) + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/*****************************************************************************/ + + + +#ifdef __cplusplus +} + +#endif + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/board/e2000/d/fiopad_config.c b/bsp/phytium/libraries/standalone/board/e2000/d/fiopad_config.c new file mode 100644 index 0000000000..b1faaa7dad --- /dev/null +++ b/bsp/phytium/libraries/standalone/board/e2000/d/fiopad_config.c @@ -0,0 +1,562 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fiopad_config.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:25:29 + * Description:  This files is for io-pad function definition + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 huanghe 2021/11/5 init commit + * 1.1 zhugengyu 2022/3/21 adopt to lastest tech spec. + */ + +/***************************** Include Files *********************************/ +#include "fiopad.h" +#include "fparameters.h" +#include "fdebug.h" +#include "fpinctrl.h" +#include "fassert.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FIOPAD_DEBUG_TAG "FIOPAD-CFG" +#define FIOPAD_ERROR(format, ...) FT_DEBUG_PRINT_E(FIOPAD_DEBUG_TAG, format, ##__VA_ARGS__) +#define FIOPAD_WARN(format, ...) FT_DEBUG_PRINT_W(FIOPAD_DEBUG_TAG, format, ##__VA_ARGS__) +#define FIOPAD_INFO(format, ...) FT_DEBUG_PRINT_I(FIOPAD_DEBUG_TAG, format, ##__VA_ARGS__) +#define FIOPAD_DEBUG(format, ...) FT_DEBUG_PRINT_D(FIOPAD_DEBUG_TAG, format, ##__VA_ARGS__) + +/************************** Function Prototypes ******************************/ + +/*****************************************************************************/ +/** + * @name: FIOPadSetSpimMux + * @msg: set iopad mux for spim + * @return {*} + * @param {u32} spim_id, instance id of spi + */ +void FIOPadSetSpimMux(u32 spim_id) +{ + if (FSPI2_ID == spim_id) + { + FPinSetFunc(FIOPAD_A29, FPIN_FUNC0); /* sclk */ + FPinSetFunc(FIOPAD_C29, FPIN_FUNC0); /* txd */ + FPinSetFunc(FIOPAD_C27, FPIN_FUNC0); /* rxd */ + FPinSetFunc(FIOPAD_A27, FPIN_FUNC0); /* csn0 */ + } +} + +/** + * @name: FIOPadSetGpioMux + * @msg: set iopad mux for gpio + * @return {*} + * @param {u32} gpio_id, instance id of gpio + * @param {u32} pin_id, index of pin + */ +void FIOPadSetGpioMux(u32 gpio_id, u32 pin_id) +{ + if (FGPIO_ID_3 == gpio_id) + { + switch (pin_id) + { + case 3: /* gpio 3-a-3 */ + FPinSetFunc(FIOPAD_A29, FPIN_FUNC6); + break; + case 4: /* gpio 3-a-4 */ + FPinSetFunc(FIOPAD_C29, FPIN_FUNC6); + break; + case 5: /* gpio 3-a-5 */ + FPinSetFunc(FIOPAD_C27, FPIN_FUNC6); + break; + case 6: /* gpio 3-a-6 */ + FPinSetFunc(FIOPAD_A27, FPIN_FUNC6); + break; + case 7: /* gpio 3-a-7 */ /*cannot use this pin*/ + FPinSetFunc(FIOPAD_AJ49, FPIN_FUNC6); + break; + case 8: /* gpio 3-a-8 */ + FPinSetFunc(FIOPAD_AL45, FPIN_FUNC6); + break; + case 9: /* gpio 3-a-9 */ + FPinSetFunc(FIOPAD_AL43, FPIN_FUNC6); + break; + default: + break; + } + } +} + +/** + * @name: FIOPadSetCanMux + * @msg: set iopad mux for can + * @return {*} + * @param {u32} can_id, instance id of can + */ +void FIOPadSetCanMux(u32 can_id) +{ + if (can_id == FCAN_INSTANCE_0) + { + /* mio0 */ + FPinSetFunc(FIOPAD_A37, FPIN_FUNC0); /* can0-tx: func 0 */ + FPinSetFunc(FIOPAD_A39, FPIN_FUNC0); /* can0-rx: func 0 */ + } + else if (can_id == FCAN_INSTANCE_1) + { + /* mio1 */ + FPinSetFunc(FIOPAD_A41, FPIN_FUNC0); /* can1-tx: func 0 */ + FPinSetFunc(FIOPAD_C41, FPIN_FUNC0); /* can1-rx: func 0 */ + } + else + { + FIOPAD_ERROR("can id is error.\r\n"); + } +} + +/** + * @name: FIOPadSetQspiMux + * @msg: set iopad mux for qspi + * @return {*} + * @param {u32} qspi_id, id of qspi instance + * @param {u32} cs_id, id of qspi cs + */ +void FIOPadSetQspiMux(u32 qspi_id, u32 cs_id) +{ + + if (qspi_id == FQSPI_INSTANCE_0) + { + /* add sck, io0-io3 iopad multiplex */ + } + + if (cs_id == FQSPI_CS_0) + { + FPinSetFunc(FIOPAD_AR51, FPIN_FUNC0); + } + else if (cs_id == FQSPI_CS_1) + { + FPinSetFunc(FIOPAD_AR45, FPIN_FUNC0); + } + else if (cs_id == FQSPI_CS_2) + { + FPinSetFunc(FIOPAD_C33, FPIN_FUNC5); + } + else if (cs_id == FQSPI_CS_3) + { + FPinSetFunc(FIOPAD_A33, FPIN_FUNC5); + } + else + { + FIOPAD_ERROR("can id is error.\r\n"); + } +} + + +/** + * @name: FIOPadSetPwmMux + * @msg: set iopad mux for pwm + * @return {*} + * @param {u32} pwm_id, id of pwm instance + * @param {u32} pwm_channel, channel of pwm instance + */ +void FIOPadSetPwmMux(u32 pwm_id, u32 pwm_channel) +{ + FASSERT(pwm_id < FPWM_INSTANCE_NUM); + FASSERT(pwm_channel < FPWM_CHANNEL_NUM); + + switch (pwm_id) + { + case FPWM_INSTANCE_0: + if (pwm_channel == 0) + { + FPinSetFunc(FIOPAD_AL55, FPIN_FUNC1); /* PWM0_OUT: func 1 */ + } + if (pwm_channel == 1) + { + FPinSetFunc(FIOPAD_AJ53, FPIN_FUNC1); /* PWM1_OUT: func 1 */ + } + break; + + case FPWM_INSTANCE_1: + if (pwm_channel == 0) + { + FPinSetFunc(FIOPAD_AG53, FPIN_FUNC1); /* PWM2_OUT: func 1 */ + } + if (pwm_channel == 1) + { + FPinSetFunc(FIOPAD_AC55, FPIN_FUNC1); /* PWM3_OUT: func 1 */ + } + break; + + case FPWM_INSTANCE_2: + if (pwm_channel == 0) + { + FPinSetFunc(FIOPAD_BA51, FPIN_FUNC1); /* PWM4_OUT: func 1 */ + } + if (pwm_channel == 1) + { + FPinSetFunc(FIOPAD_C35, FPIN_FUNC2); /* PWM5_OUT: func 2 */ + } + break; + + case FPWM_INSTANCE_3: + if (pwm_channel == 0) + { + FPinSetFunc(FIOPAD_A33, FPIN_FUNC2); /* PWM6_OUT: func 2 */ + } + if (pwm_channel == 1) + { + FPinSetFunc(FIOPAD_A39, FPIN_FUNC2); /* PWM7_OUT: func 2 */ + } + break; + + case FPWM_INSTANCE_4: + if (pwm_channel == 0) + { + FPinSetFunc(FIOPAD_C41, FPIN_FUNC2); /* PWM8_OUT: func 2 */ + } + if (pwm_channel == 1) + { + FPinSetFunc(FIOPAD_A45, FPIN_FUNC2); /* PWM9_OUT: func 2 */ + } + break; + + case FPWM_INSTANCE_5: + if (pwm_channel == 0) + { + FPinSetFunc(FIOPAD_A47, FPIN_FUNC2); /* PWM10_OUT: func 2 */ + } + if (pwm_channel == 1) + { + FPinSetFunc(FIOPAD_C29, FPIN_FUNC2); /* PWM11_OUT: func 2 */ + } + break; + + case FPWM_INSTANCE_6: + if (pwm_channel == 0) + { + FPinSetFunc(FIOPAD_A27, FPIN_FUNC2); /* PWM12_OUT: func 2 */ + } + if (pwm_channel == 1) + { + FPinSetFunc(FIOPAD_J35, FPIN_FUNC3); /* PWM13_OUT: func 3 */ + } + break; + + case FPWM_INSTANCE_7: + if (pwm_channel == 0) + { + FPinSetFunc(FIOPAD_E39, FPIN_FUNC3); /* PWM14_OUT: func 3 */ + } + if (pwm_channel == 1) + { + FPinSetFunc(FIOPAD_C39, FPIN_FUNC3); /* PWM15_OUT: func 3 */ + } + break; + + default: + FIOPAD_ERROR("pwm id is error.\r\n"); + break; + } +} + + +/** + * @name: FIOPadSetAdcMux + * @msg: set iopad mux for adc + * @return {*} + * @param {u32} adc_id, id of adc instance + * @param {u32} adc_channel, id of adc channel + */ +void FIOPadSetAdcMux(u32 adc_id, u32 adc_channel) +{ + + if (adc_id == FADC_INSTANCE_0) + { + switch (adc_channel) + { + case FADC_CHANNEL_0: + FPinSetFunc(FIOPAD_R47, FPIN_FUNC7); /* adc0-0: func 7 */ + break; + case FADC_CHANNEL_1: + FPinSetFunc(FIOPAD_R45, FPIN_FUNC7); /* adc0-1: func 7 */ + break; + case FADC_CHANNEL_2: + FPinSetFunc(FIOPAD_N47, FPIN_FUNC7); /* adc0-2: func 7 */ + break; + case FADC_CHANNEL_3: + FPinSetFunc(FIOPAD_N51, FPIN_FUNC7); /* adc0-3: func 7 */ + break; + case FADC_CHANNEL_4: + FPinSetFunc(FIOPAD_L51, FPIN_FUNC7); /* adc0-4: func 7 */ + break; + case FADC_CHANNEL_5: + FPinSetFunc(FIOPAD_J51, FPIN_FUNC7); /* adc0-5: func 7 */ + break; + case FADC_CHANNEL_6: + FPinSetFunc(FIOPAD_J41, FPIN_FUNC7); /* adc0-6: func 7 */ + break; + case FADC_CHANNEL_7: + FPinSetFunc(FIOPAD_E43, FPIN_FUNC7); /* adc0-7: func 7 */ + break; + default: + FIOPAD_ERROR("adc %d channel %d is error.\r\n", adc_id, adc_channel); + break; + } + } + else if (adc_id == FADC_INSTANCE_1) + { + switch (adc_channel) + { + case FADC_CHANNEL_0: + FPinSetFunc(FIOPAD_G43, FPIN_FUNC7); /* adc1-0: func 7 */ + break; + case FADC_CHANNEL_1: + FPinSetFunc(FIOPAD_J43, FPIN_FUNC7); /* adc1-1: func 7 */ + break; + case FADC_CHANNEL_2: + FPinSetFunc(FIOPAD_J45, FPIN_FUNC7); /* adc1-2: func 7 */ + break; + case FADC_CHANNEL_3: + FPinSetFunc(FIOPAD_N45, FPIN_FUNC7); /* adc1-3: func 7 */ + break; + case FADC_CHANNEL_4: + FPinSetFunc(FIOPAD_L47, FPIN_FUNC7); /* adc1-4: func 7 */ + break; + case FADC_CHANNEL_5: + FPinSetFunc(FIOPAD_L45, FPIN_FUNC7); /* adc1-5: func 7 */ + break; + case FADC_CHANNEL_6: + FPinSetFunc(FIOPAD_N49, FPIN_FUNC7); /* adc1-6: func 7 */ + break; + case FADC_CHANNEL_7: + FPinSetFunc(FIOPAD_J49, FPIN_FUNC7); /* adc1-7: func 7 */ + break; + default: + FIOPAD_ERROR("adc %d channel %d is error.\r\n", adc_id, adc_channel); + break; + } + } + else + { + FIOPAD_ERROR("adc %d channel %d is error.\r\n", adc_id, adc_channel); + } +} + +/** + * @name: FIOPadSetMioMux + * @msg: set iopad mux for mio + * @return {*} + * @param {u32} mio_id, instance id of i2c + */ +void FIOPadSetMioMux(u32 mio_id) +{ + switch (mio_id) + { + case MIO_INSTANCE_0: + { + FPinSetFunc(FIOPAD_A37, FPIN_FUNC5); /* scl */ + FPinSetFunc(FIOPAD_A39, FPIN_FUNC5); /* sda */ + } + break; + case MIO_INSTANCE_1: + { + FPinSetFunc(FIOPAD_A41, FPIN_FUNC5); /* scl */ + FPinSetFunc(FIOPAD_C41, FPIN_FUNC5); /* sda */ + } + break; + case MIO_INSTANCE_2: + { + FPinSetFunc(FIOPAD_A43, FPIN_FUNC5); /* scl */ + FPinSetFunc(FIOPAD_A45, FPIN_FUNC5); /* sda */ + } + break; + case MIO_INSTANCE_3: + { + FPinSetFunc(FIOPAD_BA51, FPIN_FUNC4); /* scl */ + FPinSetFunc(FIOPAD_BA49, FPIN_FUNC4); /* sda */ + } + break; + case MIO_INSTANCE_4: + { + FPinSetFunc(FIOPAD_R55, FPIN_FUNC4); /* scl */ + FPinSetFunc(FIOPAD_U55, FPIN_FUNC4); /* sda */ + } + break; + case MIO_INSTANCE_5: + { + FPinSetFunc(FIOPAD_W45, FPIN_FUNC4); /* scl */ + FPinSetFunc(FIOPAD_U53, FPIN_FUNC4); /* sda */ + } + break; + case MIO_INSTANCE_6: + { + FPinSetFunc(FIOPAD_AA53, FPIN_FUNC4); /* scl */ + FPinSetFunc(FIOPAD_AA55, FPIN_FUNC4); /* sda */ + } + break; + case MIO_INSTANCE_7: + { + FPinSetFunc(FIOPAD_A35, FPIN_FUNC4); /* scl */ + FPinSetFunc(FIOPAD_C35, FPIN_FUNC4); /* sda */ + } + break; + case MIO_INSTANCE_8: + { + FPinSetFunc(FIOPAD_AA45, FPIN_FUNC4); /* scl */ + FPinSetFunc(FIOPAD_W45, FPIN_FUNC4); /* sda */ + } + break; + case MIO_INSTANCE_9: + { + FPinSetFunc(FIOPAD_AA47, FPIN_FUNC4); /* scl */ + FPinSetFunc(FIOPAD_U45, FPIN_FUNC4); /* sda */ + } + break; + case MIO_INSTANCE_10: + { + FPinSetFunc(FIOPAD_C45, FPIN_FUNC5); /* scl */ + FPinSetFunc(FIOPAD_A47, FPIN_FUNC5); /* sda */ + } + break; + case MIO_INSTANCE_11: + { + FPinSetFunc(FIOPAD_N23, FPIN_FUNC3); /* scl */ + FPinSetFunc(FIOPAD_L25, FPIN_FUNC3); /* sda */ + } + break; + case MIO_INSTANCE_12: + { + FPinSetFunc(FIOPAD_E37, FPIN_FUNC3); /* scl */ + FPinSetFunc(FIOPAD_L41, FPIN_FUNC3); /* sda */ + } + break; + case MIO_INSTANCE_13: + { + FPinSetFunc(FIOPAD_J45, FPIN_FUNC6); /* scl */ + FPinSetFunc(FIOPAD_N45, FPIN_FUNC6); /* sda */ + } + break; + case MIO_INSTANCE_14: + { + FPinSetFunc(FIOPAD_L47, FPIN_FUNC6); /* scl */ + FPinSetFunc(FIOPAD_L45, FPIN_FUNC6); /* sda */ + } + break; + case MIO_INSTANCE_15: + { + FPinSetFunc(FIOPAD_N49, FPIN_FUNC6); /* scl */ + FPinSetFunc(FIOPAD_J49, FPIN_FUNC6); /* sda */ + } + break; + default: + break; + } +} + +/** + * @name: FIOPadSetTachoMux + * @msg: set iopad mux for pwm_in + * @return {*} + * @param {u32} pwm_in_id, instance id of tacho + */ +void FIOPadSetTachoMux(u32 pwm_in_id) +{ + switch (pwm_in_id) + { + case TACHO_INSTANCE_0: + FPinSetFunc(FIOPAD_AN53, FPIN_FUNC1); + break; + case TACHO_INSTANCE_1: + FPinSetFunc(FIOPAD_AJ55, FPIN_FUNC1); + break; + case TACHO_INSTANCE_2: + FPinSetFunc(FIOPAD_AG55, FPIN_FUNC1); + break; + case TACHO_INSTANCE_3: + FPinSetFunc(FIOPAD_AE55, FPIN_FUNC1); + break; + case TACHO_INSTANCE_4: + FPinSetFunc(FIOPAD_AC53, FPIN_FUNC1); + break; + case TACHO_INSTANCE_5: + FPinSetFunc(FIOPAD_BA49, FPIN_FUNC1); + break; + case TACHO_INSTANCE_6: + FPinSetFunc(FIOPAD_C33, FPIN_FUNC2); + break; + case TACHO_INSTANCE_7: + FPinSetFunc(FIOPAD_A37, FPIN_FUNC2); + break; + case TACHO_INSTANCE_8: + FPinSetFunc(FIOPAD_A41, FPIN_FUNC2); + break; + case TACHO_INSTANCE_9: + FPinSetFunc(FIOPAD_A43, FPIN_FUNC2); + break; + case TACHO_INSTANCE_10: + FPinSetFunc(FIOPAD_C45, FPIN_FUNC2); + break; + case TACHO_INSTANCE_11: + FPinSetFunc(FIOPAD_A29, FPIN_FUNC2); + break; + case TACHO_INSTANCE_12: + FPinSetFunc(FIOPAD_C27, FPIN_FUNC2); + break; + case TACHO_INSTANCE_13: + FPinSetFunc(FIOPAD_AA45, FPIN_FUNC2); + break; + case TACHO_INSTANCE_14: + FPinSetFunc(FIOPAD_AA47, FPIN_FUNC2); + break; + case TACHO_INSTANCE_15: + FPinSetFunc(FIOPAD_G55, FPIN_FUNC2); + break; + default: + break; + } +} + +/** + * @name: FIOPadSetUartMux + * @msg: set iopad mux for uart + * @return {*} + * @param {u32} uart_id, instance id of uart + */ +void FIOPadSetUartMux(u32 uart_id) +{ + switch (uart_id) + { + case FUART0_ID: + FPinSetFunc(FIOPAD_J33, FPIN_FUNC4); + FPinSetFunc(FIOPAD_J35, FPIN_FUNC4); + break; + case FUART1_ID: + FPinSetFunc(FIOPAD_AW47, FPIN_FUNC0); + FPinSetFunc(FIOPAD_AU47, FPIN_FUNC0); + break; + case FUART2_ID: + FPinSetFunc(FIOPAD_A43, FPIN_FUNC0); + FPinSetFunc(FIOPAD_A45, FPIN_FUNC0); + break; + case FUART3_ID: + FPinSetFunc(FIOPAD_L33, FPIN_FUNC2); + FPinSetFunc(FIOPAD_N31, FPIN_FUNC2); + break; + default: + break; + } +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/board/e2000/d/fparameters.h b/bsp/phytium/libraries/standalone/board/e2000/d/fparameters.h new file mode 100644 index 0000000000..3703d3e78b --- /dev/null +++ b/bsp/phytium/libraries/standalone/board/e2000/d/fparameters.h @@ -0,0 +1,55 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fparameters.h + * Date: 2022-02-11 13:33:28 + * LastEditTime: 2022-02-17 18:00:50 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef BOARD_E2000D_PARAMTERERS_H +#define BOARD_E2000D_PARAMTERERS_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ +#include "fparameters_comm.h" + +/************************** Constant Definitions *****************************/ +#define CORE0_AFF 0x200U +#define CORE1_AFF 0x201U + +#define FT_CPUS_NR 2U + +/* GIC offset */ + +#define FT_GIC_REDISTRUBUTIOR_OFFSET 2 + +/*****************************************************************************/ + + + + +#ifdef __cplusplus +} + +#endif + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/board/e2000/fearly_uart.c b/bsp/phytium/libraries/standalone/board/e2000/fearly_uart.c new file mode 100644 index 0000000000..a330aa37b3 --- /dev/null +++ b/bsp/phytium/libraries/standalone/board/e2000/fearly_uart.c @@ -0,0 +1,59 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: early_uart.c + * Date: 2022-02-11 13:33:28 + * LastEditTime: 2022-02-17 17:59:26 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +/***************************** Include Files *********************************/ +#include "fkernel.h" +#include "fio.h" +#include "fparameters.h" +#include "fearly_uart.h" + +/**************************** Type Definitions *******************************/ + +/************************** Constant Definitions *****************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/*****************************************************************************/ +void OutByte(s8 byte) +{ + /* wait until tx fifo is not full */ + while ((FtIn32(EARLY_UART_UARTFR) & EARLY_UART_TXFF) == EARLY_UART_TXFF) + { + + } + + FtOut32(EARLY_UART_UARTDR, (((u32)byte) & EARLY_UART_DATA_MASK)); +} + +char GetByte(void) +{ + /* wait until rx fifo is not empty */ + while ((FtIn32(EARLY_UART_UARTFR) & EARLY_UART_RXFE) == EARLY_UART_RXFE) + { + + } + + return (char)(EARLY_UART_DATA_MASK & FtIn32(EARLY_UART_UARTDR)); +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/board/e2000/fearly_uart.h b/bsp/phytium/libraries/standalone/board/e2000/fearly_uart.h new file mode 100644 index 0000000000..cbb47a898f --- /dev/null +++ b/bsp/phytium/libraries/standalone/board/e2000/fearly_uart.h @@ -0,0 +1,83 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fearly_uart.h + * Date: 2022-02-11 13:33:28 + * LastEditTime: 2022-02-17 18:00:16 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 rtos 2022/6/25 init commit + */ +#ifndef BOARD_E2000_EARLY_UART_H +#define BOARD_E2000_EARLY_UART_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ +#include "ftypes.h" +#include "fio.h" +#include "fparameters.h" +#include "sdkconfig.h" + +/**************************** Type Definitions *******************************/ + +/************************** Constant Definitions *****************************/ +#if defined(CONFIG_DEFAULT_DEBUG_PRINT_UART2) +#define EARLY_UART_BASE FUART2_BASE_ADDR +#define EARLY_UART_IRQ_NUM FUART2_IRQ_NUM +#elif defined(CONFIG_DEFAULT_DEBUG_PRINT_UART0) +#define EARLY_UART_BASE FUART0_BASE_ADDR +#define EARLY_UART_IRQ_NUM FUART0_IRQ_NUM +#else +#define EARLY_UART_BASE FUART1_BASE_ADDR +#define EARLY_UART_IRQ_NUM FUART1_IRQ_NUM +#endif + +#define EARLY_UART_UARTDR (EARLY_UART_BASE + 0x0) /* UART 数据寄存器地址 */ +#define EARLY_UART_UARTFR (EARLY_UART_BASE + 0x18) /* UART 状态寄存器地址 */ +#define EARLY_UART_UARTCR (EARLY_UART_BASE + 0x30) +#define EARLY_UART_UARTCR_UARTEN BIT(0) +#define EARLY_UART_UARTCR_TXE BIT(8) +#define EARLY_UART_UARTCR_RXE BIT(9) +#define EARLY_UART_UARTCR_INIT (EARLY_UART_UARTCR_UARTEN | EARLY_UART_UARTCR_TXE | \ + EARLY_UART_UARTCR_RXE) +#define EARLY_UART_UARTIMSC (EARLY_UART_BASE + 0x38) +#define EARLY_UART_UARTIMSC_RXIM BIT(4) +#define EARLY_UART_UARTIMSC_RTIM BIT(6) +#define EARLY_UART_UARTMIS (EARLY_UART_BASE + 0x40) +#define EARLY_UART_UARTICR (EARLY_UART_BASE + 0x44) +#define EARLY_UART_TXFF BIT(5) /* 发送 FIFO 已满标志位 */ +#define EARLY_UART_RXFE BIT(4) /* 接收 FIFO 为空标志位 */ +#define EARLY_UART_DATA_MASK GENMASK(7, 0) +#define EARLY_UART_RXI_MASK BIT(4) + +#define STDOUT_BASEADDRESS +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/*****************************************************************************/ +void OutByte(s8 byte); +char GetByte(void); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/board/e2000/fiopad_comm.c b/bsp/phytium/libraries/standalone/board/e2000/fiopad_comm.c new file mode 100644 index 0000000000..e26cafcc8d --- /dev/null +++ b/bsp/phytium/libraries/standalone/board/e2000/fiopad_comm.c @@ -0,0 +1,572 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fiopad_comm.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:25:29 + * Description:  This files is for io-pad function definition + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 huanghe 2021/11/5 init commit + * 1.1 zhugengyu 2022/3/21 adopt to lastest tech spec. + */ + + +/***************************** Include Files *********************************/ +#include "fparameters.h" +#include "fio.h" +#include "fkernel.h" +#include "fassert.h" +#include "fdebug.h" +#include "stdio.h" +#include "fpinctrl.h" + +/************************** Constant Definitions *****************************/ +/** @name IO PAD Control Register + */ +#define FIOPAD_X_REG0_BEG_OFFSET 0x0 /* 上下拉/驱动能力/复用功能配置 */ +#define FIOPAD_X_REG0_END_OFFSET 0x24c + +#define FIOPAD_X_REG1_BEG_OFFSET 0x1024 /* 输入/输出延时配置 */ +#define FIOPAD_X_REG1_END_OFFSET 0x124c + +/** @name X_reg0 Register + */ +#define FIOPAD_X_REG0_PULL_MASK GENMASK(9, 8) /* 上下拉配置 */ +#define FIOPAD_X_REG0_PULL_GET(x) GET_REG32_BITS((x), 9, 8) +#define FIOPAD_X_REG0_PULL_SET(x) SET_REG32_BITS((x), 9, 8) + +#define FIOPAD_X_REG0_DRIVE_MASK GENMASK(7, 4) /* 驱动能力配置 */ +#define FIOPAD_X_REG0_DRIVE_GET(x) GET_REG32_BITS((x), 7, 4) +#define FIOPAD_X_REG0_DRIVE_SET(x) SET_REG32_BITS((x), 7, 4) + +#define FIOPAD_X_REG0_FUNC_MASK GENMASK(2, 0) /* 引脚复用配置 */ +#define FIOPAD_X_REG0_FUNC_GET(x) GET_REG32_BITS((x), 2, 0) +#define FIOPAD_X_REG0_FUNC_SET(x) SET_REG32_BITS((x), 2, 0) + +/** @name X_reg1 Register + */ +#define FIOPAD_X_REG1_OUT_DELAY_EN BIT(8) +#define FIOPAD_X_REG1_OUT_DELAY_DELICATE_MASK GENMASK(11, 9) +#define FIOPAD_X_REG1_OUT_DELAY_DELICATE_GET(x) GET_REG32_BITS((x), 11, 9) /* 延时精调 */ +#define FIOPAD_X_REG1_OUT_DELAY_DELICATE_SET(x) SET_REG32_BITS((x), 11, 9) +#define FIOPAD_X_REG1_OUT_DELAY_ROUGH_MASK GENMASK(14, 12) +#define FIOPAD_X_REG1_OUT_DELAY_ROUGH_GET(x) GET_REG32_BITS((x), 14, 12) /* 延时粗调 */ +#define FIOPAD_X_REG1_OUT_DELAY_ROUGH_SET(x) SET_REG32_BITS((x), 14, 12) + +#define FIOPAD_X_REG1_IN_DELAY_EN BIT(0) +#define FIOPAD_X_REG1_IN_DELAY_DELICATE_MASK GENMASK(3, 1) +#define FIOPAD_X_REG1_IN_DELAY_DELICATE_GET(x) GET_REG32_BITS((x), 3, 1) /* 延时精调 */ +#define FIOPAD_X_REG1_IN_DELAY_DELICATE_SET(x) SET_REG32_BITS((x), 3, 1) +#define FIOPAD_X_REG1_IN_DELAY_ROUGH_MASK GENMASK(6, 4) +#define FIOPAD_X_REG1_IN_DELAY_ROUGH_GET(x) GET_REG32_BITS((x), 6, 4) /* 延时粗调 */ +#define FIOPAD_X_REG1_IN_DELAY_ROUGH_SET(x) SET_REG32_BITS((x), 6, 4) + +#define FIOPAD_DELAY_MAX 15 + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +static inline u32 FIOPadRead(FPinIndex pin) +{ + return FtIn32(FIOPAD_BASE_ADDR + pin.reg_off); +} + +static inline void FIOPadWrite(FPinIndex pin, u32 reg_val) +{ + FtOut32(FIOPAD_BASE_ADDR + pin.reg_off, reg_val); + return; +} + +#define FIOPAD_ASSERT_REG0_OFF(pin) FASSERT_MSG((FIOPAD_X_REG0_END_OFFSET >= pin.reg_off), "invalid reg0 offset @0x%x\r\n", (pin.reg_off)) +#define FIOPAD_ASSERT_FUNC(func) FASSERT_MSG((func < FPIN_NUM_OF_FUNC), "invalid func as %d\r\n", (func)) +#define FIOPAD_ASSERT_PULL(pull) FASSERT_MSG((pull < FPIN_NUM_OF_PULL), "invalid pull as %d\r\n", (pull)) +#define FIOPAD_ASSERT_DRIVE(drive) FASSERT_MSG((drive < FPIN_NUM_OF_DRIVE), "invalid pull as %d\r\n", (drive)) + +#define FIOPAD_ASSERT_REG1_OFF(pin) FASSERT_MSG(((FIOPAD_X_REG1_BEG_OFFSET <= pin.reg_off) && (FIOPAD_X_REG1_END_OFFSET >= pin.reg_off)), "invalid reg1 offset @0x%x\r\n", (pin.reg_off)) +#define FIOPAD_ASSERT_DELAY(delay) FASSERT_MSG((delay < FPIN_NUM_OF_DELAY), "invalid delay as %d\r\n", (delay)) + +#define FIOPAD_DEBUG_TAG "FIOPAD" +#define FIOPAD_ERROR(format, ...) FT_DEBUG_PRINT_E(FIOPAD_DEBUG_TAG, format, ##__VA_ARGS__) +#define FIOPAD_WARN(format, ...) FT_DEBUG_PRINT_W(FIOPAD_DEBUG_TAG, format, ##__VA_ARGS__) +#define FIOPAD_INFO(format, ...) FT_DEBUG_PRINT_I(FIOPAD_DEBUG_TAG, format, ##__VA_ARGS__) +#define FIOPAD_DEBUG(format, ...) FT_DEBUG_PRINT_D(FIOPAD_DEBUG_TAG, format, ##__VA_ARGS__) + +/************************** Function Prototypes ******************************/ +/** + * @name: FPinGetFunc + * @msg: 获取IO引脚当前的复用功能 + * @return {FPinFunc} 当前的复用功能 + * @param {FPinIndex} pin IO引脚索引 + * @note 参考编程手册,使用 FIOPAD_INDEX 宏定义index的值 + */ +FPinFunc FPinGetFunc(const FPinIndex pin) +{ + FIOPAD_ASSERT_REG0_OFF(pin); + u32 func = FIOPAD_X_REG0_FUNC_GET(FIOPadRead(pin)); + FIOPAD_ASSERT_FUNC(func); + return (FPinFunc)func; +} + +/** + * @name: FPinSetFunc + * @msg: 设置IO引脚复用功能 + * @return {*} + * @param {FPinIndex} pin IO引脚索引 + * @param {FPinFunc} func IO复用功能 + * @note 参考编程手册,使用 FIOPAD_INDEX 宏定义index的值 + */ +void FPinSetFunc(const FPinIndex pin, FPinFunc func) +{ + FIOPAD_ASSERT_REG0_OFF(pin); + FIOPAD_ASSERT_FUNC(func); + u32 reg_val = FIOPadRead(pin); + u32 test_val = 0; + + reg_val &= ~FIOPAD_X_REG0_FUNC_MASK; + reg_val |= FIOPAD_X_REG0_FUNC_SET(func); + + FIOPadWrite(pin, reg_val); + + test_val = FIOPadRead(pin); + + if (reg_val != test_val) + { + FIOPAD_ERROR("ERROR: FIOPad write is failed ,pin is %x\n, 0x%x != 0x%x", + pin.reg_off, reg_val, test_val); + } + + return; +} + +/** + * @name: FPinGetDrive + * @msg: 获取IO引脚的驱动能力 + * @return {FPinDrive} 引脚的当前的驱动能力 + * @param {FPinIndex} pin IO引脚索引 + */ +FPinDrive FPinGetDrive(const FPinIndex pin) +{ + FIOPAD_ASSERT_REG0_OFF(pin); + u32 drive = FIOPAD_X_REG0_DRIVE_GET(FIOPadRead(pin)); + FIOPAD_ASSERT_DRIVE(drive); + return (FPinDrive)drive; +} + +/** + * @name: FPinSetDrive + * @msg: 设置IO引脚的驱动能力 + * @return {*} + * @param {FPinIndex} pin, IO引脚索引 + * @param {FPinDrive} drive, 引脚驱动能力设置 + */ +void FPinSetDrive(const FPinIndex pin, FPinDrive drive) +{ + FIOPAD_ASSERT_REG0_OFF(pin); + FIOPAD_ASSERT_DRIVE(drive); + u32 reg_val = FIOPadRead(pin); + + reg_val &= ~FIOPAD_X_REG0_DRIVE_MASK; + reg_val |= FIOPAD_X_REG0_DRIVE_SET(drive); + + FIOPadWrite(pin, reg_val); + return; +} + +void FPinGetConfig(const FPinIndex pin, FPinFunc *func, FPinPull *pull, FPinDrive *drive) +{ + FIOPAD_ASSERT_REG0_OFF(pin); + u32 reg_val = FIOPadRead(pin); + + if (func) + { + *func = FIOPAD_X_REG0_FUNC_GET(reg_val); + } + + if (pull) + { + *pull = FIOPAD_X_REG0_PULL_GET(reg_val); + } + + if (drive) + { + *pull = FIOPAD_X_REG0_DRIVE_GET(reg_val); + } + + return; +} + +void FPinSetConfig(const FPinIndex pin, FPinFunc func, FPinPull pull, FPinDrive drive) +{ + FIOPAD_ASSERT_REG0_OFF(pin); + u32 reg_val = FIOPadRead(pin); + + reg_val &= ~FIOPAD_X_REG0_FUNC_MASK; + reg_val |= FIOPAD_X_REG0_FUNC_SET(func); + + reg_val &= ~FIOPAD_X_REG0_PULL_MASK; + reg_val |= FIOPAD_X_REG0_PULL_SET(pull); + + reg_val &= ~FIOPAD_X_REG0_DRIVE_MASK; + reg_val |= FIOPAD_X_REG0_DRIVE_SET(drive); + + FIOPadWrite(pin, reg_val); + return; +} + +/** + * @name: FPinGetPull + * @msg: 获取IO引脚当前的上下拉设置 + * @return {*} + * @param {FPinIndex} pin IO引脚索引 + * @note 参考编程手册,使用 FIOPAD_INDEX 宏定义index的值 + */ +FPinPull FPinGetPull(const FPinIndex pin) +{ + FIOPAD_ASSERT_REG0_OFF(pin); + u32 pull = FIOPAD_X_REG0_PULL_GET(FIOPadRead(pin)); + FIOPAD_ASSERT_PULL(pull); + return (FPinPull)pull; +} + +/** + * @name: FPinSetPull + * @msg: 设置IO引脚当前的上下拉 + * @return {*} + * @param {FPinIndex} pin IO引脚索引 + * @param {FPinPull} pull 上下拉设置 + */ +void FPinSetPull(const FPinIndex pin, FPinPull pull) +{ + FIOPAD_ASSERT_REG0_OFF(pin); + FIOPAD_ASSERT_PULL(pull); + + u32 reg_val = FIOPadRead(pin); + + reg_val &= ~FIOPAD_X_REG0_PULL_MASK; + reg_val |= FIOPAD_X_REG0_PULL_SET(pull); + + FIOPadWrite(pin, reg_val); + return; +} + +/** + * @name: FPinGetDelay + * @msg: 获取IO引脚当前的延时设置 + * @return {FPinDelay} 当前的延时设置 + * @param {FPinIndex} pin IO引脚延时设置索引 + * @param {FPinDelayDir} dir 输入/输出延时 + * @param {FPinDelayType} type 精调/粗调延时 + */ +FPinDelay FPinGetDelay(const FPinIndex pin, FPinDelayDir dir, FPinDelayType type) +{ + FIOPAD_ASSERT_REG1_OFF(pin); + const u32 reg_val = FIOPadRead(pin); + u8 delay = 0; + + if (FPIN_OUTPUT_DELAY == dir) + { + if (FPIN_DELAY_FINE_TUNING == type) + { + delay = FIOPAD_X_REG1_OUT_DELAY_DELICATE_GET(reg_val); + } + else if (FPIN_DELAY_COARSE_TUNING == type) + { + delay = FIOPAD_X_REG1_OUT_DELAY_ROUGH_GET(reg_val); + } + else + { + FASSERT(0); + } + } + else if (FPIN_INPUT_DELAY == dir) + { + if (FPIN_DELAY_FINE_TUNING == type) + { + delay = FIOPAD_X_REG1_IN_DELAY_DELICATE_GET(reg_val); + } + else if (FPIN_DELAY_COARSE_TUNING == type) + { + delay = FIOPAD_X_REG1_IN_DELAY_ROUGH_GET(reg_val); + } + else + { + FASSERT(0); + } + } + else + { + FASSERT(0); + } + + FIOPAD_ASSERT_DELAY(delay); + return (FPinDelay)delay; +} + +/** + * @name: FPinGetDelayEn + * @msg: 获取IO引脚当前的延时使能标志位 + * @return {*} + * @param {FPinIndex} pin IO引脚延时设置索引 + * @param {FPinDelayDir} dir 输入/输出延时 + */ +boolean FPinGetDelayEn(const FPinIndex pin, FPinDelayDir dir) +{ + FIOPAD_ASSERT_REG1_OFF(pin); + const u32 reg_val = FIOPadRead(pin); + boolean enabled = FALSE; + + if (FPIN_OUTPUT_DELAY == dir) + { + if (FIOPAD_X_REG1_OUT_DELAY_EN & reg_val) + enabled = TRUE; + else + enabled = FALSE; + } + else if (FPIN_INPUT_DELAY == dir) + { + if (FIOPAD_X_REG1_IN_DELAY_EN & reg_val) + enabled = TRUE; + else + enabled = FALSE; + } + else + { + FASSERT(0); + } + + return enabled; +} + +/** + * @name: FPinSetDelay + * @msg: 设置IO引脚延时 + * @return {*} + * @param {FPinIndex} pin IO引脚延时设置索引 + * @param {FPinDelayDir} dir 输入/输出延时 + * @param {FPinDelayType} type 精调/粗调延时 + * @param {FPinDelay} delay 延时设置 + */ +void FPinSetDelay(const FPinIndex pin, FPinDelayDir dir, FPinDelayType type, FPinDelay delay) +{ + FIOPAD_ASSERT_REG1_OFF(pin); + FIOPAD_ASSERT_DELAY(delay); + u32 reg_val = FIOPadRead(pin); + + if (FPIN_OUTPUT_DELAY == dir) + { + if (FPIN_DELAY_FINE_TUNING == type) + { + reg_val &= ~FIOPAD_X_REG1_OUT_DELAY_DELICATE_MASK; + reg_val |= FIOPAD_X_REG1_OUT_DELAY_DELICATE_SET(delay); + } + else if (FPIN_DELAY_COARSE_TUNING == type) + { + reg_val &= ~FIOPAD_X_REG1_OUT_DELAY_ROUGH_MASK; + reg_val |= FIOPAD_X_REG1_OUT_DELAY_ROUGH_SET(delay); + } + else + { + FASSERT(0); + } + } + else if (FPIN_INPUT_DELAY == dir) + { + if (FPIN_DELAY_FINE_TUNING == type) + { + reg_val &= ~FIOPAD_X_REG1_IN_DELAY_DELICATE_MASK; + reg_val |= FIOPAD_X_REG1_IN_DELAY_DELICATE_SET(delay); + } + else if (FPIN_DELAY_COARSE_TUNING == type) + { + reg_val &= ~FIOPAD_X_REG1_IN_DELAY_ROUGH_MASK; + reg_val |= FIOPAD_X_REG1_IN_DELAY_ROUGH_SET(delay); + } + else + { + FASSERT(0); + } + } + else + { + FASSERT(0); + } + + FIOPadWrite(pin, reg_val); + return; +} + +/** + * @name: FPinSetDelayEn + * @msg: 使能/去使能IO引脚延时 + * @return {*} + * @param {FPinIndex} pin IO引脚延时设置索引 + * @param {FPinDelayDir} dir 输入/输出延时 + * @param {boolean} enable TRUE: 使能, FALSE: 去使能 + */ +void FPinSetDelayEn(const FPinIndex pin, FPinDelayDir dir, boolean enable) +{ + FIOPAD_ASSERT_REG1_OFF(pin); + u32 reg_val = FIOPadRead(pin); + + if (FPIN_OUTPUT_DELAY == dir) + { + if (enable) + reg_val |= FIOPAD_X_REG1_OUT_DELAY_EN; + else + reg_val &= ~FIOPAD_X_REG1_OUT_DELAY_EN; + } + else if (FPIN_INPUT_DELAY == dir) + { + if (enable) + reg_val |= FIOPAD_X_REG1_IN_DELAY_EN; + else + reg_val &= ~FIOPAD_X_REG1_IN_DELAY_EN; + } + else + { + FASSERT(0); + } + + FIOPadWrite(pin, reg_val); + return; +} + + +/** + * @name: FPinSetDelayConfig + * @msg: Update and enable common IO pin delay config + * @return {NONE} + * @param {FPinIndex} pin, IO pin index + * @param {FPinDelayIOType} in_out_type, Select the input and output types , + * @param {FPinDelay} roungh_delay, delay rough setting + * @param {FPinDelay} delicate_delay, delay delicate setting + * @param {boolean} enable, enable delay + */ +void FPinSetDelayConfig(const FPinIndex pin, FPinDelayIOType in_out_type, FPinDelay roungh_delay, FPinDelay delicate_delay, boolean enable) +{ + FIOPAD_ASSERT_REG1_OFF(pin); + u32 reg_val = FIOPadRead(pin); + + if (in_out_type == FPIN_DELAY_IN_TYPE) + { + reg_val = FIOPadRead(pin); + + /* update delicate input delay */ + reg_val &= ~FIOPAD_X_REG1_IN_DELAY_DELICATE_MASK; + reg_val |= FIOPAD_X_REG1_IN_DELAY_DELICATE_SET(delicate_delay); + + /* update rough input delay */ + reg_val &= ~FIOPAD_X_REG1_IN_DELAY_ROUGH_MASK; + reg_val |= FIOPAD_X_REG1_IN_DELAY_ROUGH_SET(roungh_delay); + + /* enable input delay */ + if (enable) + { + reg_val |= FIOPAD_X_REG1_IN_DELAY_EN; + } + else + { + reg_val &= ~FIOPAD_X_REG1_IN_DELAY_EN; + } + } + else + { + /* update delicate output delay */ + reg_val &= ~FIOPAD_X_REG1_OUT_DELAY_DELICATE_MASK; + reg_val |= FIOPAD_X_REG1_OUT_DELAY_DELICATE_SET(delicate_delay); + + /* update rough output delay */ + reg_val &= ~FIOPAD_X_REG1_OUT_DELAY_ROUGH_MASK; + reg_val |= FIOPAD_X_REG1_OUT_DELAY_ROUGH_SET(roungh_delay); + + /* enable output delay */ + if (enable) + { + reg_val |= FIOPAD_X_REG1_OUT_DELAY_EN; + } + else + { + reg_val &= ~FIOPAD_X_REG1_OUT_DELAY_EN; + } + } + + FIOPadWrite(pin, reg_val); + return; +} + +/** + * @name: FPinGetDelayConfig + * @msg: Get current common IO pin delay config + * @return {NONE} + * @param {FPinIndex} pin, IO pin index + * @param {FPinDelay} *in_roungh_delay, input delay rough setting (输入粗调) + * @param {FPinDelay} *in_delicate_delay, input delay delicate setting (输入精调) + * @param {FPinDelay} *out_roungh_delay, output delay rough setting (输出粗调) + * @param {FPinDelay} *out_delicate_delay, output delay delicate setting (输出精调) + */ +void FPinGetDelayConfig(const FPinIndex pin, FPinDelay *in_roungh_delay, FPinDelay *in_delicate_delay, + FPinDelay *out_roungh_delay, FPinDelay *out_delicate_delay) +{ + FIOPAD_ASSERT_REG1_OFF(pin); + u32 reg_val = FIOPadRead(pin); + + if (out_delicate_delay) + { + *out_delicate_delay = FIOPAD_X_REG1_OUT_DELAY_DELICATE_GET(reg_val); + } + + if (out_roungh_delay) + { + *out_roungh_delay = FIOPAD_X_REG1_OUT_DELAY_ROUGH_GET(reg_val); + } + + if (in_delicate_delay) + { + *in_delicate_delay = FIOPAD_X_REG1_IN_DELAY_DELICATE_GET(reg_val); + } + + if (in_roungh_delay) + { + *in_roungh_delay = FIOPAD_X_REG1_IN_DELAY_ROUGH_GET(reg_val); + } + + return; +} + +/** + * @name: FIOPadDumpPadFunc + * @msg: print information of all iopad + * @return {*} + */ +void FIOPadDumpPadFunc(void) +{ + uintptr beg_off = FIOPAD_0_FUNC_OFFSET; + uintptr end_off = FIOPAD_147_FUNC_OFFSET; + uintptr off; + FPinIndex pin; + const char *pull_state[FPIN_NUM_OF_PULL] = {"none", "down", "up"}; + + FIOPAD_DEBUG("Pad Func Info..."); + for (off = beg_off; off <= end_off; off += 4U) + { + pin.reg_off = off; + FIOPAD_DEBUG(" [0x%x] func: %d, ds: %d, pull: %s ", + pin.reg_off, + FPinGetFunc(pin), + FPinGetDrive(pin), + pull_state[FPinGetPull(pin)]); + } +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/board/e2000/fiopad_comm.h b/bsp/phytium/libraries/standalone/board/e2000/fiopad_comm.h new file mode 100644 index 0000000000..311fb6f076 --- /dev/null +++ b/bsp/phytium/libraries/standalone/board/e2000/fiopad_comm.h @@ -0,0 +1,310 @@ +#ifndef BOARD_E2000_FIOPAD_COMMON_H +#define BOARD_E2000_FIOPAD_COMMON_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ +#include "ftypes.h" + +/**************************** Type Definitions *******************************/ + +/************************** Constant Definitions *****************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FIOPAD_INDEX(offset) \ + { \ + /* reg_off */ (offset), \ + /* reg_bit */ (0) \ + } + +/*****************************************************************************/ +/* register offset of iopad function / pull / driver strength */ +#define FIOPAD_0_FUNC_OFFSET 0x0000U +#define FIOPAD_2_FUNC_OFFSET 0x0004U +#define FIOPAD_3_FUNC_OFFSET 0x0008U +#define FIOPAD_4_FUNC_OFFSET 0x000CU +#define FIOPAD_5_FUNC_OFFSET 0x0010U +#define FIOPAD_6_FUNC_OFFSET 0x0014U +#define FIOPAD_7_FUNC_OFFSET 0x0018U +#define FIOPAD_8_FUNC_OFFSET 0x001CU +#define FIOPAD_9_FUNC_OFFSET 0x0020U +#define FIOPAD_10_FUNC_OFFSET 0x0024U +#define FIOPAD_11_FUNC_OFFSET 0x0028U +#define FIOPAD_12_FUNC_OFFSET 0x002CU +#define FIOPAD_13_FUNC_OFFSET 0x0030U +#define FIOPAD_14_FUNC_OFFSET 0x0034U +#define FIOPAD_15_FUNC_OFFSET 0x0038U +#define FIOPAD_16_FUNC_OFFSET 0x003CU +#define FIOPAD_17_FUNC_OFFSET 0x0040U +#define FIOPAD_18_FUNC_OFFSET 0x0044U +#define FIOPAD_19_FUNC_OFFSET 0x0048U +#define FIOPAD_20_FUNC_OFFSET 0x004CU +#define FIOPAD_21_FUNC_OFFSET 0x0050U +#define FIOPAD_22_FUNC_OFFSET 0x0054U +#define FIOPAD_23_FUNC_OFFSET 0x0058U +#define FIOPAD_24_FUNC_OFFSET 0x005CU +#define FIOPAD_25_FUNC_OFFSET 0x0060U +#define FIOPAD_26_FUNC_OFFSET 0x0064U +#define FIOPAD_27_FUNC_OFFSET 0x0068U +#define FIOPAD_28_FUNC_OFFSET 0x006CU +#define FIOPAD_31_FUNC_OFFSET 0x0070U +#define FIOPAD_32_FUNC_OFFSET 0x0074U +#define FIOPAD_33_FUNC_OFFSET 0x0078U +#define FIOPAD_34_FUNC_OFFSET 0x007CU +#define FIOPAD_35_FUNC_OFFSET 0x0080U +#define FIOPAD_36_FUNC_OFFSET 0x0084U +#define FIOPAD_37_FUNC_OFFSET 0x0088U +#define FIOPAD_38_FUNC_OFFSET 0x008CU +#define FIOPAD_39_FUNC_OFFSET 0x0090U +#define FIOPAD_40_FUNC_OFFSET 0x0094U +#define FIOPAD_41_FUNC_OFFSET 0x0098U +#define FIOPAD_42_FUNC_OFFSET 0x009CU +#define FIOPAD_43_FUNC_OFFSET 0x00A0U +#define FIOPAD_44_FUNC_OFFSET 0x00A4U +#define FIOPAD_45_FUNC_OFFSET 0x00A8U +#define FIOPAD_46_FUNC_OFFSET 0x00ACU +#define FIOPAD_47_FUNC_OFFSET 0x00B0U +#define FIOPAD_48_FUNC_OFFSET 0x00B4U +#define FIOPAD_49_FUNC_OFFSET 0x00B8U +#define FIOPAD_50_FUNC_OFFSET 0x00BCU +#define FIOPAD_51_FUNC_OFFSET 0x00C0U +#define FIOPAD_52_FUNC_OFFSET 0x00C4U +#define FIOPAD_53_FUNC_OFFSET 0x00C8U +#define FIOPAD_54_FUNC_OFFSET 0x00CCU +#define FIOPAD_55_FUNC_OFFSET 0x00D0U +#define FIOPAD_56_FUNC_OFFSET 0x00D4U +#define FIOPAD_57_FUNC_OFFSET 0x00D8U +#define FIOPAD_58_FUNC_OFFSET 0x00DCU +#define FIOPAD_59_FUNC_OFFSET 0x00E0U +#define FIOPAD_60_FUNC_OFFSET 0x00E4U +#define FIOPAD_61_FUNC_OFFSET 0x00E8U +#define FIOPAD_62_FUNC_OFFSET 0x00ECU +#define FIOPAD_63_FUNC_OFFSET 0x00F0U +#define FIOPAD_64_FUNC_OFFSET 0x00F4U +#define FIOPAD_65_FUNC_OFFSET 0x00F8U +#define FIOPAD_66_FUNC_OFFSET 0x00FCU +#define FIOPAD_67_FUNC_OFFSET 0x0100U +#define FIOPAD_68_FUNC_OFFSET 0x0104U +#define FIOPAD_148_FUNC_OFFSET 0x0108U +#define FIOPAD_69_FUNC_OFFSET 0x010CU +#define FIOPAD_70_FUNC_OFFSET 0x0110U +#define FIOPAD_71_FUNC_OFFSET 0x0114U +#define FIOPAD_72_FUNC_OFFSET 0x0118U +#define FIOPAD_73_FUNC_OFFSET 0x011CU +#define FIOPAD_74_FUNC_OFFSET 0x0120U +#define FIOPAD_75_FUNC_OFFSET 0x0124U +#define FIOPAD_76_FUNC_OFFSET 0x0128U +#define FIOPAD_77_FUNC_OFFSET 0x012CU +#define FIOPAD_78_FUNC_OFFSET 0x0130U +#define FIOPAD_79_FUNC_OFFSET 0x0134U +#define FIOPAD_80_FUNC_OFFSET 0x0138U +#define FIOPAD_81_FUNC_OFFSET 0x013CU +#define FIOPAD_82_FUNC_OFFSET 0x0140U +#define FIOPAD_83_FUNC_OFFSET 0x0144U +#define FIOPAD_84_FUNC_OFFSET 0x0148U +#define FIOPAD_85_FUNC_OFFSET 0x014CU +#define FIOPAD_86_FUNC_OFFSET 0x0150U +#define FIOPAD_87_FUNC_OFFSET 0x0154U +#define FIOPAD_88_FUNC_OFFSET 0x0158U +#define FIOPAD_89_FUNC_OFFSET 0x015CU +#define FIOPAD_90_FUNC_OFFSET 0x0160U +#define FIOPAD_91_FUNC_OFFSET 0x0164U +#define FIOPAD_92_FUNC_OFFSET 0x0168U +#define FIOPAD_93_FUNC_OFFSET 0x016CU +#define FIOPAD_94_FUNC_OFFSET 0x0170U +#define FIOPAD_95_FUNC_OFFSET 0x0174U +#define FIOPAD_96_FUNC_OFFSET 0x0178U +#define FIOPAD_97_FUNC_OFFSET 0x017CU +#define FIOPAD_98_FUNC_OFFSET 0x0180U +#define FIOPAD_29_FUNC_OFFSET 0x0184U +#define FIOPAD_30_FUNC_OFFSET 0x0188U +#define FIOPAD_99_FUNC_OFFSET 0x018CU +#define FIOPAD_100_FUNC_OFFSET 0x0190U +#define FIOPAD_101_FUNC_OFFSET 0x0194U +#define FIOPAD_102_FUNC_OFFSET 0x0198U +#define FIOPAD_103_FUNC_OFFSET 0x019CU +#define FIOPAD_104_FUNC_OFFSET 0x01A0U +#define FIOPAD_105_FUNC_OFFSET 0x01A4U +#define FIOPAD_106_FUNC_OFFSET 0x01A8U +#define FIOPAD_107_FUNC_OFFSET 0x01ACU +#define FIOPAD_108_FUNC_OFFSET 0x01B0U +#define FIOPAD_109_FUNC_OFFSET 0x01B4U +#define FIOPAD_110_FUNC_OFFSET 0x01B8U +#define FIOPAD_111_FUNC_OFFSET 0x01BCU +#define FIOPAD_112_FUNC_OFFSET 0x01C0U +#define FIOPAD_113_FUNC_OFFSET 0x01C4U +#define FIOPAD_114_FUNC_OFFSET 0x01C8U +#define FIOPAD_115_FUNC_OFFSET 0x01CCU +#define FIOPAD_116_FUNC_OFFSET 0x01D0U +#define FIOPAD_117_FUNC_OFFSET 0x01D4U +#define FIOPAD_118_FUNC_OFFSET 0x01D8U +#define FIOPAD_119_FUNC_OFFSET 0x01DCU +#define FIOPAD_120_FUNC_OFFSET 0x01E0U +#define FIOPAD_121_FUNC_OFFSET 0x01E4U +#define FIOPAD_122_FUNC_OFFSET 0x01E8U +#define FIOPAD_123_FUNC_OFFSET 0x01ECU +#define FIOPAD_124_FUNC_OFFSET 0x01F0U +#define FIOPAD_125_FUNC_OFFSET 0x01F4U +#define FIOPAD_126_FUNC_OFFSET 0x01F8U +#define FIOPAD_127_FUNC_OFFSET 0x01FCU +#define FIOPAD_128_FUNC_OFFSET 0x0200U +#define FIOPAD_129_FUNC_OFFSET 0x0204U +#define FIOPAD_130_FUNC_OFFSET 0x0208U +#define FIOPAD_131_FUNC_OFFSET 0x020CU +#define FIOPAD_132_FUNC_OFFSET 0x0210U +#define FIOPAD_133_FUNC_OFFSET 0x0214U +#define FIOPAD_134_FUNC_OFFSET 0x0218U +#define FIOPAD_135_FUNC_OFFSET 0x021CU +#define FIOPAD_136_FUNC_OFFSET 0x0220U +#define FIOPAD_137_FUNC_OFFSET 0x0224U +#define FIOPAD_138_FUNC_OFFSET 0x0228U +#define FIOPAD_139_FUNC_OFFSET 0x022CU +#define FIOPAD_140_FUNC_OFFSET 0x0230U +#define FIOPAD_141_FUNC_OFFSET 0x0234U +#define FIOPAD_142_FUNC_OFFSET 0x0238U +#define FIOPAD_143_FUNC_OFFSET 0x023CU +#define FIOPAD_144_FUNC_OFFSET 0x0240U +#define FIOPAD_145_FUNC_OFFSET 0x0244U +#define FIOPAD_146_FUNC_OFFSET 0x0248U +#define FIOPAD_147_FUNC_OFFSET 0x024CU + +/* register offset of iopad delay */ +#define FIOPAD_10_DELAY_OFFSET 0x1024U +#define FIOPAD_11_DELAY_OFFSET 0x1028U +#define FIOPAD_12_DELAY_OFFSET 0x102CU +#define FIOPAD_13_DELAY_OFFSET 0x1030U +#define FIOPAD_14_DELAY_OFFSET 0x1034U +#define FIOPAD_23_DELAY_OFFSET 0x1058U +#define FIOPAD_24_DELAY_OFFSET 0x105CU +#define FIOPAD_25_DELAY_OFFSET 0x1060U +#define FIOPAD_26_DELAY_OFFSET 0x1064U +#define FIOPAD_32_DELAY_OFFSET 0x1074U +#define FIOPAD_33_DELAY_OFFSET 0x1078U +#define FIOPAD_34_DELAY_OFFSET 0x107CU +#define FIOPAD_35_DELAY_OFFSET 0x1080U +#define FIOPAD_55_DELAY_OFFSET 0x10D0U +#define FIOPAD_56_DELAY_OFFSET 0x10D4U +#define FIOPAD_57_DELAY_OFFSET 0x10D8U +#define FIOPAD_58_DELAY_OFFSET 0x10DCU +#define FIOPAD_59_DELAY_OFFSET 0x10E0U +#define FIOPAD_60_DELAY_OFFSET 0x10E4U +#define FIOPAD_61_DELAY_OFFSET 0x10E8U +#define FIOPAD_62_DELAY_OFFSET 0x10ECU +#define FIOPAD_63_DELAY_OFFSET 0x10F0U +#define FIOPAD_64_DELAY_OFFSET 0x10F4U +#define FIOPAD_65_DELAY_OFFSET 0x10F8U +#define FIOPAD_66_DELAY_OFFSET 0x10FCU +#define FIOPAD_67_DELAY_OFFSET 0x1100U +#define FIOPAD_68_DELAY_OFFSET 0x1104U +#define FIOPAD_148_DELAY_OFFSET 0x1108U +#define FIOPAD_69_DELAY_OFFSET 0x110CU +#define FIOPAD_70_DELAY_OFFSET 0x1110U +#define FIOPAD_71_DELAY_OFFSET 0x1114U +#define FIOPAD_72_DELAY_OFFSET 0x1118U +#define FIOPAD_73_DELAY_OFFSET 0x111CU +#define FIOPAD_74_DELAY_OFFSET 0x1120U +#define FIOPAD_75_DELAY_OFFSET 0x1124U +#define FIOPAD_76_DELAY_OFFSET 0x1128U +#define FIOPAD_77_DELAY_OFFSET 0x112CU +#define FIOPAD_78_DELAY_OFFSET 0x1130U +#define FIOPAD_80_DELAY_OFFSET 0x1138U +#define FIOPAD_81_DELAY_OFFSET 0x113CU +#define FIOPAD_82_DELAY_OFFSET 0x1140U +#define FIOPAD_83_DELAY_OFFSET 0x1144U +#define FIOPAD_84_DELAY_OFFSET 0x1148U +#define FIOPAD_85_DELAY_OFFSET 0x114CU +#define FIOPAD_86_DELAY_OFFSET 0x1150U +#define FIOPAD_87_DELAY_OFFSET 0x1154U +#define FIOPAD_88_DELAY_OFFSET 0x1158U +#define FIOPAD_89_DELAY_OFFSET 0x115CU +#define FIOPAD_90_DELAY_OFFSET 0x1160U +#define FIOPAD_92_DELAY_OFFSET 0x1168U +#define FIOPAD_93_DELAY_OFFSET 0x116CU +#define FIOPAD_94_DELAY_OFFSET 0x1170U +#define FIOPAD_95_DELAY_OFFSET 0x1174U +#define FIOPAD_96_DELAY_OFFSET 0x1178U +#define FIOPAD_97_DELAY_OFFSET 0x117CU +#define FIOPAD_98_DELAY_OFFSET 0x1180U +#define FIOPAD_99_DELAY_OFFSET 0x118CU +#define FIOPAD_100_DELAY_OFFSET 0x1190U +#define FIOPAD_101_DELAY_OFFSET 0x1194U +#define FIOPAD_102_DELAY_OFFSET 0x1198U +#define FIOPAD_103_DELAY_OFFSET 0x119CU +#define FIOPAD_104_DELAY_OFFSET 0x11A0U +#define FIOPAD_105_DELAY_OFFSET 0x11A4U +#define FIOPAD_106_DELAY_OFFSET 0x11A8U +#define FIOPAD_107_DELAY_OFFSET 0x11ACU +#define FIOPAD_108_DELAY_OFFSET 0x11B0U +#define FIOPAD_109_DELAY_OFFSET 0x11B4U +#define FIOPAD_110_DELAY_OFFSET 0x11B8U +#define FIOPAD_111_DELAY_OFFSET 0x11BCU +#define FIOPAD_112_DELAY_OFFSET 0x11C0U +#define FIOPAD_115_DELAY_OFFSET 0x11CCU +#define FIOPAD_116_DELAY_OFFSET 0x11D0U +#define FIOPAD_117_DELAY_OFFSET 0x11D4U +#define FIOPAD_118_DELAY_OFFSET 0x11D8U +#define FIOPAD_119_DELAY_OFFSET 0x11DCU +#define FIOPAD_120_DELAY_OFFSET 0x11E0U +#define FIOPAD_121_DELAY_OFFSET 0x11E4U +#define FIOPAD_122_DELAY_OFFSET 0x11E8U +#define FIOPAD_123_DELAY_OFFSET 0x11ECU +#define FIOPAD_124_DELAY_OFFSET 0x11F0U +#define FIOPAD_125_DELAY_OFFSET 0x11F4U +#define FIOPAD_126_DELAY_OFFSET 0x11F8U +#define FIOPAD_127_DELAY_OFFSET 0x11FCU +#define FIOPAD_128_DELAY_OFFSET 0x1200U +#define FIOPAD_136_DELAY_OFFSET 0x1220U +#define FIOPAD_137_DELAY_OFFSET 0x1224U +#define FIOPAD_138_DELAY_OFFSET 0x1228U +#define FIOPAD_139_DELAY_OFFSET 0x122CU +#define FIOPAD_140_DELAY_OFFSET 0x1230U +#define FIOPAD_141_DELAY_OFFSET 0x1234U +#define FIOPAD_142_DELAY_OFFSET 0x1238U +#define FIOPAD_143_DELAY_OFFSET 0x123CU +#define FIOPAD_144_DELAY_OFFSET 0x1240U +#define FIOPAD_145_DELAY_OFFSET 0x1244U +#define FIOPAD_146_DELAY_OFFSET 0x1248U +#define FIOPAD_147_DELAY_OFFSET 0x124CU + +/************************** Function Prototypes ******************************/ +/* set iopad mux for spim */ +void FIOPadSetSpimMux(u32 spim_id); + +/* set iopad mux for gpio */ +void FIOPadSetGpioMux(u32 gpio_id, u32 pin_id); + +/* set iopad mux for mio */ +void FIOPadSetMioMux(u32 mio_id); + +/* print information of all iopad */ +void FIOPadDumpPadFunc(void); + +/* set iopad mux for can */ +void FIOPadSetCanMux(u32 can_id); + +/* set iopad mux for qspi */ +void FIOPadSetQspiMux(u32 qspi_id, u32 cs_id); + +/* set iopad mux for pwm */ +void FIOPadSetPwmMux(u32 pwm_id, u32 pwm_channel); + +/* set iopad mux for adc */ +void FIOPadSetAdcMux(u32 adc_id, u32 adc_channel); + +/* set iopad mux for tacho*/ +void FIOPadSetTachoMux(u32 pwm_in_id); + +/* set iopad mux for uart*/ +void FIOPadSetUartMux(u32 uart_id); + +#ifdef __cplusplus +} + +#endif + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/board/e2000/fparameters_comm.h b/bsp/phytium/libraries/standalone/board/e2000/fparameters_comm.h new file mode 100644 index 0000000000..5f807e7843 --- /dev/null +++ b/bsp/phytium/libraries/standalone/board/e2000/fparameters_comm.h @@ -0,0 +1,624 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fparameters_comm.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-17 18:01:11 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef BOARD_E2000_PARAMTERERS_COMMON_H +#define BOARD_E2000_PARAMTERERS_COMMON_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ +#if !defined(__ASSEMBLER__) +#include "ftypes.h" +#endif + +/************************** Constant Definitions *****************************/ +/* CACHE */ +#define CACHE_LINE_ADDR_MASK 0x3FU +#define CACHE_LINE 64U + +/* DEVICE Register Address */ +#define FT_DEV_BASE_ADDR 0x28000000U +#define FT_DEV_END_ADDR 0x2FFFFFFFU + +/* PCI */ +#define FT_PCIE_NUM 1 +#define FT_PCIE0_ID 0 +#define FT_PCIE0_MISC_IRQ_NUM 40 + +#define FT_PCIE_CFG_MAX_NUM_OF_BUS 256 +#define FT_PCIE_CFG_MAX_NUM_OF_DEV 32 +#define FT_PCIE_CFG_MAX_NUM_OF_FUN 8 + +#define FT_PCI_CONFIG_BASEADDR 0x40000000U +#define FT_PCI_CONFIG_REG_LENGTH 0x10000000U + +#define FT_PCI_IO_CONFIG_BASEADDR 0x50000000U +#define FT_PCI_IO_CONFIG_REG_LENGTH 0x08000000U + +#define FT_PCI_MEM32_BASEADDR 0x58000000U +#define FT_PCI_MEM32_REG_LENGTH 0x27FFFFFFU + +#define FT_PCI_MEM64_BASEADDR 0x1000000000U +#define FT_PCI_MEM64_REG_LENGTH 0x1000000000U + +#define FT_PCI_EU0_C0_CONTROL_BASEADDR 0x29000000U +#define FT_PCI_EU0_C1_CONTROL_BASEADDR 0x29010000U +#define FT_PCI_EU0_C2_CONTROL_BASEADDR 0x29020000U +#define FT_PCI_EU1_C0_CONTROL_BASEADDR 0x29030000U +#define FT_PCI_EU1_C1_CONTROL_BASEADDR 0x29040000U +#define FT_PCI_EU1_C2_CONTROL_BASEADDR 0x29050000U + +#define FT_PCI_EU0_CONFIG_BASEADDR 0x29100000U +#define FT_PCI_EU1_CONFIG_BASEADDR 0x29101000U + +#define FT_PCI_INTA_IRQ_NUM 36 +#define FT_PCI_INTB_IRQ_NUM 37 +#define FT_PCI_INTC_IRQ_NUM 38 +#define FT_PCI_INTD_IRQ_NUM 39 + +#define FT_PCI_NEED_SKIP 0 + +#define FT_PCI_INTX_PEU0_STAT 0x29100000U +#define FT_PCI_INTX_PEU1_STAT 0x29101000U + +#define FT_PCI_INTX_EU0_C0_CONTROL 0x29000184U +#define FT_PCI_INTX_EU0_C1_CONTROL 0x29010184U +#define FT_PCI_INTX_EU0_C2_CONTROL 0x29020184U +#define FT_PCI_INTX_EU1_C0_CONTROL 0x29030184U +#define FT_PCI_INTX_EU1_C1_CONTROL 0x29040184U +#define FT_PCI_INTX_EU1_C2_CONTROL 0x29050184U + +#define FT_PCI_INTX_CONTROL_NUM 6 /* Total number of controllers */ +#define FT_PCI_INTX_SATA_NUM 2 /* Total number of controllers */ + + +/* platform ahci host */ +#define PLAT_AHCI_HOST_MAX_COUNT 5 +#define AHCI_BASE_0 0 +#define AHCI_BASE_1 0 +#define AHCI_BASE_2 0 +#define AHCI_BASE_3 0 +#define AHCI_BASE_4 0 + +#define AHCI_IRQ_0 0 +#define AHCI_IRQ_1 0 +#define AHCI_IRQ_2 0 +#define AHCI_IRQ_3 0 +#define AHCI_IRQ_4 0 + +/* sata controller */ +#define FSATA0_BASEADDR 0x31A40000U +#define FSATA1_BASEADDR 0x32014000U + +#define FSATA0_IRQNUM 74 +#define FSATA1_IRQNUM 75 + +#if !defined(__ASSEMBLER__) +typedef enum +{ + FSATA_INSTANCE_0 = 0, + FSATA_INSTANCE_1 = 1, + + FSATA_INSTANCE_NUM +} FSataInstance; +#endif + +/* Generic Timer */ +#define GENERIC_TIMER_CLK_FREQ_MHZ 48U +#define GENERIC_TIMER_NS_IRQ_NUM 30U +#define GENERIC_TIMER_NS_CLK_FREQ 2000000U +#define COUNTS_PER_SECOND GENERIC_TIMER_NS_CLK_FREQ + +/* UART */ +#define FUART_NUM 4U +#define FUART_REG_LENGTH 0x18000U + +#define FUART0_ID 0U +#define FUART0_IRQ_NUM (85 + 30) +#define FUART0_BASE_ADDR 0x2800c000U +#define FUART0_CLK_FREQ_HZ 100000000U + +#define FUART1_ID 1U +#define FUART1_IRQ_NUM (86 + 30) +#define FUART1_BASE_ADDR 0x2800d000U +#define FUART1_CLK_FREQ_HZ 100000000U + +#define FUART2_ID 2U +#define FUART2_IRQ_NUM (87 + 30) +#define FUART2_BASE_ADDR 0x2800e000U +#define FUART2_CLK_FREQ_HZ 100000000U + +#define FUART3_BASE_ADDR 0x2800f000U +#define FUART3_ID 3U +#define FUART3_IRQ_NUM (88 + 30) +#define FUART3_CLK_FREQ_HZ 100000000U + +#define FT_STDOUT_BASE_ADDR FUART1_BASE_ADDR +#define FT_STDIN_BASE_ADDR FUART1_BASE_ADDR + +/****** GIC v3 *****/ +#define FT_GICV3_INSTANCES_NUM 1U +#define GICV3_REG_LENGTH 0x00009000U + +/* + * 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_NR_IRQS 160U +#define ARM_GIC_IRQ_START 0U +#define FGIC_NUM 1U + +#define ARM_GIC_IPI_COUNT 16U /* MPCore IPI count */ +#define SGI_INT_MAX 16U +#define SPI_START_INT_NUM 32U /* SPI start at ID32 */ +#define PPI_START_INT_NUM 16U /* PPI start at ID16 */ +#define GIC_INT_MAX_NUM 1020U /* GIC max interrupts count */ + +#define GICV3_BASEADDRESS 0x30800000U +#define GICV3_DISTRIBUTOR_BASEADDRESS (GICV3_BASEADDRESS + 0) +#define GICV3_RD_BASEADDRESS (GICV3_BASEADDRESS + 0x80000U) +#define GICV3_RD_OFFSET (2U << 16) +#define FT_GICV3_VECTORTABLE_NUM GIC_INT_MAX_NUM + +/* GPIO */ +#define FGPIO_ID_0 0U +#define FGPIO_ID_1 1U +#define FGPIO_ID_2 2U +#define FGPIO_WITH_PIN_IRQ 2U /* max id of gpio assign irq for each pin */ +#define FGPIO_ID_3 3U +#define FGPIO_ID_4 4U +#define FGPIO_ID_5 5U +#define FGPIO_NUM 6U + +#define FGPIO_0_BASE_ADDR 0x28034000U +#define FGPIO_1_BASE_ADDR 0x28035000U +#define FGPIO_2_BASE_ADDR 0x28036000U +#define FGPIO_3_BASE_ADDR 0x28037000U +#define FGPIO_4_BASE_ADDR 0x28038000U +#define FGPIO_5_BASE_ADDR 0x28039000U + +#define FGPIO_CTRL_PIN_NUM 16U + +#define FGPIO_PIN_IRQ_BASE 140U +#define FGPIO_PIN_IRQ_NUM_GET(id, pin) (FGPIO_PIN_IRQ_BASE + FGPIO_CTRL_PIN_NUM * (id) + (pin)) + +#define FGPIO_3_IRQ_NUM 188U +#define FGPIO_4_IRQ_NUM 189U +#define FGPIO_5_IRQ_NUM 190U + +#define FGPIO_PIN_IRQ_TOTAL 51U + +/* SPI */ +#define FSPI0_BASE 0x2803A000U +#define FSPI1_BASE 0x2803B000U +#define FSPI2_BASE 0x2803C000U +#define FSPI3_BASE 0x2803D000U +#define FSPI0_ID 0U +#define FSPI1_ID 1U +#define FSPI2_ID 2U +#define FSPI3_ID 3U + +#define FSPI0_IRQ_NUM 191U +#define FSPI1_IRQ_NUM 192U +#define FSPI2_IRQ_NUM 193U +#define FSPI3_IRQ_NUM 194U + +#define FSPI_FREQ 50000000U +#define FSPI_DEVICE_NUM 4U + +/* XMAC */ +#define FT_XMAC_NUM 4U + +#define FT_XMAC0_ID 0U +#define FT_XMAC1_ID 1U +#define FT_XMAC2_ID 2U +#define FT_XMAC3_ID 3U + +#define FT_XMAC0_BASEADDRESS 0x3200C000U +#define FT_XMAC1_BASEADDRESS 0x3200E000U +#define FT_XMAC2_BASEADDRESS 0x32010000U +#define FT_XMAC3_BASEADDRESS 0x32012000U + +#define FT_XMAC0_MODE_SEL_BASEADDRESS 0x3200DC00U +#define FT_XMAC0_LOOPBACK_SEL_BASEADDRESS 0x3200DC04U +#define FT_XMAC1_MODE_SEL_BASEADDRESS 0x3200FC00U +#define FT_XMAC1_LOOPBACK_SEL_BASEADDRESS 0x3200FC04U +#define FT_XMAC2_MODE_SEL_BASEADDRESS 0x32011C00U +#define FT_XMAC2_LOOPBACK_SEL_BASEADDRESS 0x32011C04U +#define FT_XMAC3_MODE_SEL_BASEADDRESS 0x32013C00U +#define FT_XMAC3_LOOPBACK_SEL_BASEADDRESS 0x32013C04U + +#define FT_XMAC0_PCLK 50000000U +#define FT_XMAC1_PCLK 50000000U +#define FT_XMAC2_PCLK 50000000U +#define FT_XMAC3_PCLK 50000000U +#define FT_XMAC0_HOTPLUG_IRQ_NUM (53U + 30U) +#define FT_XMAC1_HOTPLUG_IRQ_NUM (54U + 30U) +#define FT_XMAC2_HOTPLUG_IRQ_NUM (55U + 30U) +#define FT_XMAC3_HOTPLUG_IRQ_NUM (56U + 30U) + +#define FT_XMAC_QUEUE_MAX_NUM 16U + +#define FT_XMAC0_QUEUE0_IRQ_NUM (57U + 30U) +#define FT_XMAC0_QUEUE1_IRQ_NUM (58U + 30U) +#define FT_XMAC0_QUEUE2_IRQ_NUM (59U + 30U) +#define FT_XMAC0_QUEUE3_IRQ_NUM (60U + 30U) +#define FT_XMAC0_QUEUE4_IRQ_NUM (30U + 30U) +#define FT_XMAC0_QUEUE5_IRQ_NUM (31U + 30U) +#define FT_XMAC0_QUEUE6_IRQ_NUM (32U + 30U) +#define FT_XMAC0_QUEUE7_IRQ_NUM (33U + 30U) + +#define FT_XMAC1_QUEUE0_IRQ_NUM (61U + 30U) +#define FT_XMAC1_QUEUE1_IRQ_NUM (62U + 30U) +#define FT_XMAC1_QUEUE2_IRQ_NUM (63U + 30U) +#define FT_XMAC1_QUEUE3_IRQ_NUM (64U + 30U) + +#define FT_XMAC2_QUEUE0_IRQ_NUM (66U + 30U) +#define FT_XMAC2_QUEUE1_IRQ_NUM (67U + 30U) +#define FT_XMAC2_QUEUE2_IRQ_NUM (68U + 30U) +#define FT_XMAC2_QUEUE3_IRQ_NUM (69U + 30U) + +#define FT_XMAC3_QUEUE0_IRQ_NUM (70U + 30U) +#define FT_XMAC3_QUEUE1_IRQ_NUM (71U + 30U) +#define FT_XMAC3_QUEUE2_IRQ_NUM (72U + 30U) +#define FT_XMAC3_QUEUE3_IRQ_NUM (73U + 30U) + +#define FT_XMAC_PHY_MAX_NUM 32U + +/* QSPI */ + +#define FQSPI_BASEADDR 0x028008000U + +#if !defined(__ASSEMBLER__) + +typedef enum +{ + FQSPI_INSTANCE_0 = 0, + + FQSPI_INSTANCE_NUM +} FQspiInstance; + +/* FQSPI cs 0_3, chip number */ +typedef enum +{ + FQSPI_CS_0 = 0, + FQSPI_CS_1 = 1, + FQSPI_CS_2 = 2, + FQSPI_CS_3 = 3, + FQSPI_CS_NUM +} FQspiChipCS; + +#endif + +#define FQSPI_MEM_START_ADDR 0x0U +#define FQSPI_MEM_END_ADDR 0x0FFFFFFFU /* 256MB */ +#define FQSPI_MEM_START_ADDR_64 0x100000000U +#define FQSPI_MEM_END_ADDR_64 0x17FFFFFFFU /* 2GB */ + +/* TIMER and TACHO */ +#define TIMER_NUM 38U +#define TACHO_NUM 16U +#define TIMER_CLK_FREQ_HZ 50000000U /* 50MHz */ +#define TIMER_TICK_PERIOD_NS 20U /* 20ns */ +#define TIMER_TACHO_IRQ_ID(n) (226U + (n)) +#define TIMER_TACHO_BASE_ADDR(n) (0x28054000U + 0x1000U * (n)) + +#if !defined(__ASSEMBLER__) +typedef enum +{ + TACHO_INSTANCE_0 = 0, + TACHO_INSTANCE_1 = 1, + TACHO_INSTANCE_2 = 2, + TACHO_INSTANCE_3 = 3, + TACHO_INSTANCE_4 = 4, + TACHO_INSTANCE_5 = 5, + TACHO_INSTANCE_6 = 6, + TACHO_INSTANCE_7 = 7, + TACHO_INSTANCE_8 = 8, + TACHO_INSTANCE_9 = 9, + TACHO_INSTANCE_10 = 10, + TACHO_INSTANCE_11 = 11, + TACHO_INSTANCE_12 = 12, + TACHO_INSTANCE_13 = 13, + TACHO_INSTANCE_14 = 14, + TACHO_INSTANCE_15 = 15, + + TACHO_INSTANCE_NUM +} TachoInstance; +#endif + +/* GDMA */ +#define FGDMA0_ID 0U +#define FGDMA0_BASE_ADDR 0x32B34000U +#define FGDMA0_IRQ_NUM 266U + +#define FGDMA_INSTANCE_NUM 1U + +/* CANFD */ +#define FCAN_REF_CLOCK 200000000U + +#define FCAN0_BASEADDR 0x2800A000U +#define FCAN1_BASEADDR 0x2800B000U + +#define FCAN0_IRQNUM 113U +#define FCAN1_IRQNUM 114U + +#if !defined(__ASSEMBLER__) +typedef enum +{ + FCAN_INSTANCE_0 = 0, + FCAN_INSTANCE_1 = 1, + + FCAN_INSTANCE_NUM +} FCanInstance; +#endif + +/* WDT */ +#if !defined(__ASSEMBLER__) +typedef enum +{ + FWDT_INSTANCE_0 = 0, + FWDT_INSTANCE_1, + + FWDT_INSTANCE_NUM +} FWdtInstance; +#endif + +#define FWDT0_REFRESH_BASE 0x28040000U +#define FWDT0_CONTROL_BASE 0x28041000U +#define FWDT1_REFRESH_BASE 0x28042000U +#define FWDT1_CONTROL_BASE 0x28043000U + +#define FWDT0_INTR_IRQ 196U +#define FWDT1_INTR_IRQ 197U + +#define FWDT_CLK 48000000U /* 48MHz */ + +/*MIO*/ +#define FMIO_NUM 16 +#define FMIO_BASE_ADDR(n) (0x28014000 + 0x2000 * (n)) +#define FMIO_CONF_ADDR(n) FMIO_BASE_ADDR(n)+0x1000 +#define FMIO_IRQ_NUM(n) (124+n) +#define MIO_REF_CLK_HZ 50000000 /* 50MHz */ + +#if !defined(__ASSEMBLER__) +typedef enum +{ + MIO_INSTANCE_0 = 0, + MIO_INSTANCE_1, + MIO_INSTANCE_2, + MIO_INSTANCE_3, + MIO_INSTANCE_4, + MIO_INSTANCE_5, + MIO_INSTANCE_6, + MIO_INSTANCE_7, + MIO_INSTANCE_8, + MIO_INSTANCE_9, + MIO_INSTANCE_10, + MIO_INSTANCE_11, + MIO_INSTANCE_12, + MIO_INSTANCE_13, + MIO_INSTANCE_14, + MIO_INSTANCE_15, + + MIO_INSTANCE_NUM +} MioInstance; +#endif + +#if !defined(__ASSEMBLER__) +/*I2C0 -> PMBUS0 +* I2C1 -> PMBUS1 +* I2C2 -> SMBUS0 +*/ +typedef enum +{ + I2C_INSTANCE_0 = 0, + I2C_INSTANCE_1, + I2C_INSTANCE_2, + + I2C_INSTANCE_NUM +} I2cInstance; +#endif + +#define I2C_0_BASEADDR 0x28011000 +#define I2C_1_BASEADDR 0x28012000 +#define I2C_2_BASEADDR 0x28013000 + +#define I2C_0_INTR_IRQ 121 +#define I2C_1_INTR_IRQ 122 +#define I2C_2_INTR_IRQ 123 + +#define I2C_REF_CLK_HZ 50000000 /* 50MHz */ + +/* SDIO */ +#if !defined(__ASSEMBLER__) +enum +{ + FSDIO_HOST_INSTANCE_0 = 0, + FSDIO_HOST_INSTANCE_1, + + FSDIO_HOST_INSTANCE_NUM +}; +#endif + +#define FSDIO_HOST_0_BASE_ADDR 0x28000000U +#define FSDIO_HOST_1_BASE_ADDR 0x28001000U + +#define FSDIO_HOST_0_IRQ_NUM 104U +#define FSDIO_HOST_1_IRQ_NUM 105U + +#define FSDIO_CLK_RATE_HZ (1200000000UL) /* 1.2GHz */ + +/* NAND */ +#define FNAND_NUM 1U +#define FNAND_INSTANCE0 0U +#define FNAND_BASEADDRESS 0x28002000U +#define FNAND_IRQ_NUM (106U) +#define FNAND_CONNECT_MAX_NUM 1U + +#define FIOPAD_BASE_ADDR 0x32B30000U + +/* DDMA */ +#define FDDMA0_ID 0U +#define FDDMA0_BASE_ADDR 0x28003000U +#define FDDMA0_IRQ_NUM 107U + +#define FDDMA1_ID 1U +#define FDDMA1_BASE_ADDR 0x28004000U +#define FDDMA1_IRQ_NUM 108U + +#define FDDMA_INSTANCE_NUM 2U + +#define FDDMA0_UART0_TX_SLAVE_ID 2U /* uart0 tx slave-id */ +#define FDDMA0_UART1_TX_SLAVE_ID 3U /* uart1 tx slave-id */ +#define FDDMA0_UART2_TX_SLAVE_ID 4U /* uart2 tx slave-id */ +#define FDDMA0_UART3_TX_SLAVE_ID 5U /* uart3 tx slave-id */ + +#define FDDMA0_SPIM0_TX_SLAVE_ID 6U /* spi0 tx slave-id */ +#define FDDMA0_SPIM1_TX_SLAVE_ID 7U /* spi1 tx slave-id */ +#define FDDMA0_SPIM2_TX_SLAVE_ID 8U /* spi2 tx slave-id */ +#define FDDMA0_SPIM3_TX_SLAVE_ID 9U /* spi3 tx slave-id */ + +#define FDDMA0_UART0_RX_SLAVE_ID 15U /* uart0 rx slave-id */ +#define FDDMA0_UART1_RX_SLAVE_ID 16U /* uart1 rx slave-id */ +#define FDDMA0_UART2_RX_SLAVE_ID 17U /* uart2 rx slave-id */ +#define FDDMA0_UART3_RX_SLAVE_ID 18U /* uart3 rx slave-id */ + +#define FDDMA0_SPIM0_RX_SLAVE_ID 19U /* spi0 rx slave-id */ +#define FDDMA0_SPIM1_RX_SLAVE_ID 20U /* spi1 rx slave-id */ +#define FDDMA0_SPIM2_RX_SLAVE_ID 21U /* spi2 rx slave-id */ +#define FDDMA0_SPIM3_RX_SLAVE_ID 22U /* spi3 rx slave-id */ + +#define FDDMA_MIN_SLAVE_ID 0U +#define FDDMA_MAX_SLAVE_ID 31U + +/* ADC */ +#if !defined(__ASSEMBLER__) +typedef enum +{ + FADC_INSTANCE_0 = 0, + FADC_INSTANCE_1, + + FADC_INSTANCE_NUM +} FAdcInstance; + +typedef enum +{ + FADC_CHANNEL_0 = 0, + FADC_CHANNEL_1 = 1, + FADC_CHANNEL_2, + FADC_CHANNEL_3, + FADC_CHANNEL_4, + FADC_CHANNEL_5, + FADC_CHANNEL_6, + FADC_CHANNEL_7, + + FADC_CHANNEL_NUM +} FAdcChannel; + +#endif + +#define FADC0_CONTROL_BASE 0x2807B000U +#define FADC1_CONTROL_BASE 0x2807C000U + +#define FADC0_INTR_IRQ 264U +#define FADC1_INTR_IRQ 265U + +/* PWM */ +#if !defined(__ASSEMBLER__) +typedef enum +{ + FPWM_INSTANCE_0 = 0, + FPWM_INSTANCE_1, + FPWM_INSTANCE_2, + FPWM_INSTANCE_3, + FPWM_INSTANCE_4, + FPWM_INSTANCE_5, + FPWM_INSTANCE_6, + FPWM_INSTANCE_7, + + FPWM_INSTANCE_NUM +} FPwmInstance; + +typedef enum +{ + FPWM_CHANNEL_0 = 0, + FPWM_CHANNEL_1, + + FPWM_CHANNEL_NUM +} FPwmChannel; +#endif + +#define FPWM_CONTROL_BASE 0x2804A000U + +#define FPWM_CLK 50000000U /* 50MHz */ + +#define FPWM0_INTR_IRQ 205U +#define FPWM1_INTR_IRQ 206U +#define FPWM2_INTR_IRQ 207U +#define FPWM3_INTR_IRQ 208U +#define FPWM4_INTR_IRQ 209U +#define FPWM5_INTR_IRQ 210U +#define FPWM6_INTR_IRQ 211U +#define FPWM7_INTR_IRQ 212U +#define FPWM8_INTR_IRQ 213U +#define FPWM9_INTR_IRQ 214U +#define FPWM10_INTR_IRQ 215U +#define FPWM11_INTR_IRQ 216U +#define FPWM12_INTR_IRQ 217U +#define FPWM13_INTR_IRQ 218U +#define FPWM14_INTR_IRQ 219U +#define FPWM15_INTR_IRQ 220U + +/* Semaphore */ +#define FSEMA0_ID 0U +#define FSEMA0_BASE_ADDR 0x32B36000U +#define FSEMA_INSTANCE_NUM 1U + +/* LSD Config */ +#define FLSD_CONFIG_BASE 0x2807E000U +#define FLSD_NAND_MMCSD_HADDR 0xC0U +#define FLSD_CK_STOP_CONFIG0_HADDR 0x10U + +/* USB3 */ +#define FUSB3_ID_0 0U +#define FUSB3_ID_1 1U +#define FUSB3_NUM 2U +#define FUSB3_XHCI_OFFSET 0x8000U +#define FUSB3_0_BASE_ADDR 0x31A00000U +#define FUSB3_1_BASE_ADDR 0x31A20000U +#define FUSB3_0_IRQ_NUM 48U +#define FUSB3_1_IRQ_NUM 49U +/*****************************************************************************/ + +#ifdef __cplusplus +} + +#endif + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/board/e2000/q/fiopad.h b/bsp/phytium/libraries/standalone/board/e2000/q/fiopad.h new file mode 100644 index 0000000000..74eb5aa77e --- /dev/null +++ b/bsp/phytium/libraries/standalone/board/e2000/q/fiopad.h @@ -0,0 +1,266 @@ + +#ifndef BOARD_E2000Q_FIOPAD_H +#define BOARD_E2000Q_FIOPAD_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ +#include "fiopad_comm.h" + +/************************** Constant Definitions *****************************/ +/* register offset of iopad function / pull / driver strength */ +#define FIOPAD_AN59 (FPinIndex)FIOPAD_INDEX(FIOPAD_0_FUNC_OFFSET) +#define FIOPAD_AW47 (FPinIndex)FIOPAD_INDEX(FIOPAD_2_FUNC_OFFSET) +#define FIOPAD_AR55 (FPinIndex)FIOPAD_INDEX(FIOPAD_9_FUNC_OFFSET) +#define FIOPAD_AJ55 (FPinIndex)FIOPAD_INDEX(FIOPAD_10_FUNC_OFFSET) +#define FIOPAD_AL55 (FPinIndex)FIOPAD_INDEX(FIOPAD_11_FUNC_OFFSET) +#define FIOPAD_AL53 (FPinIndex)FIOPAD_INDEX(FIOPAD_12_FUNC_OFFSET) +#define FIOPAD_AN51 (FPinIndex)FIOPAD_INDEX(FIOPAD_13_FUNC_OFFSET) +#define FIOPAD_AR51 (FPinIndex)FIOPAD_INDEX(FIOPAD_14_FUNC_OFFSET) +#define FIOPAD_BA57 (FPinIndex)FIOPAD_INDEX(FIOPAD_15_FUNC_OFFSET) +#define FIOPAD_BA59 (FPinIndex)FIOPAD_INDEX(FIOPAD_16_FUNC_OFFSET) +#define FIOPAD_AW57 (FPinIndex)FIOPAD_INDEX(FIOPAD_17_FUNC_OFFSET) +#define FIOPAD_AW59 (FPinIndex)FIOPAD_INDEX(FIOPAD_18_FUNC_OFFSET) +#define FIOPAD_AU55 (FPinIndex)FIOPAD_INDEX(FIOPAD_19_FUNC_OFFSET) +#define FIOPAD_AN57 (FPinIndex)FIOPAD_INDEX(FIOPAD_20_FUNC_OFFSET) +#define FIOPAD_AL59 (FPinIndex)FIOPAD_INDEX(FIOPAD_21_FUNC_OFFSET) +#define FIOPAD_AJ59 (FPinIndex)FIOPAD_INDEX(FIOPAD_22_FUNC_OFFSET) +#define FIOPAD_AJ57 (FPinIndex)FIOPAD_INDEX(FIOPAD_23_FUNC_OFFSET) +#define FIOPAD_AG59 (FPinIndex)FIOPAD_INDEX(FIOPAD_24_FUNC_OFFSET) +#define FIOPAD_AG57 (FPinIndex)FIOPAD_INDEX(FIOPAD_25_FUNC_OFFSET) +#define FIOPAD_AE59 (FPinIndex)FIOPAD_INDEX(FIOPAD_26_FUNC_OFFSET) +#define FIOPAD_AC59 (FPinIndex)FIOPAD_INDEX(FIOPAD_27_FUNC_OFFSET) +#define FIOPAD_AC57 (FPinIndex)FIOPAD_INDEX(FIOPAD_28_FUNC_OFFSET) +#define FIOPAD_AR49 (FPinIndex)FIOPAD_INDEX(FIOPAD_31_FUNC_OFFSET) +#define FIOPAD_BA55 (FPinIndex)FIOPAD_INDEX(FIOPAD_32_FUNC_OFFSET) +#define FIOPAD_BA53 (FPinIndex)FIOPAD_INDEX(FIOPAD_33_FUNC_OFFSET) +#define FIOPAD_AR59 (FPinIndex)FIOPAD_INDEX(FIOPAD_34_FUNC_OFFSET) +#define FIOPAD_AU59 (FPinIndex)FIOPAD_INDEX(FIOPAD_35_FUNC_OFFSET) +#define FIOPAD_AR57 (FPinIndex)FIOPAD_INDEX(FIOPAD_36_FUNC_OFFSET) +#define FIOPAD_BA49 (FPinIndex)FIOPAD_INDEX(FIOPAD_37_FUNC_OFFSET) +#define FIOPAD_AW55 (FPinIndex)FIOPAD_INDEX(FIOPAD_38_FUNC_OFFSET) +#define FIOPAD_A35 (FPinIndex)FIOPAD_INDEX(FIOPAD_39_FUNC_OFFSET) +#define FIOPAD_R57 (FPinIndex)FIOPAD_INDEX(FIOPAD_40_FUNC_OFFSET) +#define FIOPAD_R59 (FPinIndex)FIOPAD_INDEX(FIOPAD_41_FUNC_OFFSET) +#define FIOPAD_U59 (FPinIndex)FIOPAD_INDEX(FIOPAD_42_FUNC_OFFSET) +#define FIOPAD_W59 (FPinIndex)FIOPAD_INDEX(FIOPAD_43_FUNC_OFFSET) +#define FIOPAD_U57 (FPinIndex)FIOPAD_INDEX(FIOPAD_44_FUNC_OFFSET) +#define FIOPAD_AA57 (FPinIndex)FIOPAD_INDEX(FIOPAD_45_FUNC_OFFSET) +#define FIOPAD_AA59 (FPinIndex)FIOPAD_INDEX(FIOPAD_46_FUNC_OFFSET) +#define FIOPAD_AW51 (FPinIndex)FIOPAD_INDEX(FIOPAD_47_FUNC_OFFSET) +#define FIOPAD_AU51 (FPinIndex)FIOPAD_INDEX(FIOPAD_48_FUNC_OFFSET) +#define FIOPAD_A39 (FPinIndex)FIOPAD_INDEX(FIOPAD_49_FUNC_OFFSET) +#define FIOPAD_C39 (FPinIndex)FIOPAD_INDEX(FIOPAD_50_FUNC_OFFSET) +#define FIOPAD_C37 (FPinIndex)FIOPAD_INDEX(FIOPAD_51_FUNC_OFFSET) +#define FIOPAD_A37 (FPinIndex)FIOPAD_INDEX(FIOPAD_52_FUNC_OFFSET) +#define FIOPAD_A41 (FPinIndex)FIOPAD_INDEX(FIOPAD_53_FUNC_OFFSET) +#define FIOPAD_A43 (FPinIndex)FIOPAD_INDEX(FIOPAD_54_FUNC_OFFSET) +#define FIOPAD_A45 (FPinIndex)FIOPAD_INDEX(FIOPAD_55_FUNC_OFFSET) +#define FIOPAD_C45 (FPinIndex)FIOPAD_INDEX(FIOPAD_56_FUNC_OFFSET) +#define FIOPAD_A47 (FPinIndex)FIOPAD_INDEX(FIOPAD_57_FUNC_OFFSET) +#define FIOPAD_A49 (FPinIndex)FIOPAD_INDEX(FIOPAD_58_FUNC_OFFSET) +#define FIOPAD_C49 (FPinIndex)FIOPAD_INDEX(FIOPAD_59_FUNC_OFFSET) +#define FIOPAD_A51 (FPinIndex)FIOPAD_INDEX(FIOPAD_60_FUNC_OFFSET) +#define FIOPAD_A33 (FPinIndex)FIOPAD_INDEX(FIOPAD_61_FUNC_OFFSET) +#define FIOPAD_C33 (FPinIndex)FIOPAD_INDEX(FIOPAD_62_FUNC_OFFSET) +#define FIOPAD_C31 (FPinIndex)FIOPAD_INDEX(FIOPAD_63_FUNC_OFFSET) +#define FIOPAD_A31 (FPinIndex)FIOPAD_INDEX(FIOPAD_64_FUNC_OFFSET) +#define FIOPAD_AJ53 (FPinIndex)FIOPAD_INDEX(FIOPAD_65_FUNC_OFFSET) +#define FIOPAD_AL49 (FPinIndex)FIOPAD_INDEX(FIOPAD_66_FUNC_OFFSET) +#define FIOPAD_AL47 (FPinIndex)FIOPAD_INDEX(FIOPAD_67_FUNC_OFFSET) +#define FIOPAD_AN49 (FPinIndex)FIOPAD_INDEX(FIOPAD_68_FUNC_OFFSET) +#define FIOPAD_AG51 (FPinIndex)FIOPAD_INDEX(FIOPAD_148_FUNC_OFFSET) +#define FIOPAD_AJ51 (FPinIndex)FIOPAD_INDEX(FIOPAD_69_FUNC_OFFSET) +#define FIOPAD_AG49 (FPinIndex)FIOPAD_INDEX(FIOPAD_70_FUNC_OFFSET) +#define FIOPAD_AE55 (FPinIndex)FIOPAD_INDEX(FIOPAD_71_FUNC_OFFSET) +#define FIOPAD_AE53 (FPinIndex)FIOPAD_INDEX(FIOPAD_72_FUNC_OFFSET) +#define FIOPAD_AG55 (FPinIndex)FIOPAD_INDEX(FIOPAD_73_FUNC_OFFSET) +#define FIOPAD_AJ49 (FPinIndex)FIOPAD_INDEX(FIOPAD_74_FUNC_OFFSET) +#define FIOPAD_AC55 (FPinIndex)FIOPAD_INDEX(FIOPAD_75_FUNC_OFFSET) +#define FIOPAD_AC53 (FPinIndex)FIOPAD_INDEX(FIOPAD_76_FUNC_OFFSET) +#define FIOPAD_AE51 (FPinIndex)FIOPAD_INDEX(FIOPAD_77_FUNC_OFFSET) +#define FIOPAD_W51 (FPinIndex)FIOPAD_INDEX(FIOPAD_78_FUNC_OFFSET) +#define FIOPAD_W55 (FPinIndex)FIOPAD_INDEX(FIOPAD_79_FUNC_OFFSET) +#define FIOPAD_W53 (FPinIndex)FIOPAD_INDEX(FIOPAD_80_FUNC_OFFSET) +#define FIOPAD_U55 (FPinIndex)FIOPAD_INDEX(FIOPAD_81_FUNC_OFFSET) +#define FIOPAD_U53 (FPinIndex)FIOPAD_INDEX(FIOPAD_82_FUNC_OFFSET) +#define FIOPAD_AE49 (FPinIndex)FIOPAD_INDEX(FIOPAD_83_FUNC_OFFSET) +#define FIOPAD_AC49 (FPinIndex)FIOPAD_INDEX(FIOPAD_84_FUNC_OFFSET) +#define FIOPAD_AE47 (FPinIndex)FIOPAD_INDEX(FIOPAD_85_FUNC_OFFSET) +#define FIOPAD_AA47 (FPinIndex)FIOPAD_INDEX(FIOPAD_86_FUNC_OFFSET) +#define FIOPAD_AA49 (FPinIndex)FIOPAD_INDEX(FIOPAD_87_FUNC_OFFSET) +#define FIOPAD_W49 (FPinIndex)FIOPAD_INDEX(FIOPAD_88_FUNC_OFFSET) +#define FIOPAD_AA51 (FPinIndex)FIOPAD_INDEX(FIOPAD_89_FUNC_OFFSET) +#define FIOPAD_U49 (FPinIndex)FIOPAD_INDEX(FIOPAD_90_FUNC_OFFSET) +#define FIOPAD_G59 (FPinIndex)FIOPAD_INDEX(FIOPAD_91_FUNC_OFFSET) +#define FIOPAD_J59 (FPinIndex)FIOPAD_INDEX(FIOPAD_92_FUNC_OFFSET) +#define FIOPAD_L57 (FPinIndex)FIOPAD_INDEX(FIOPAD_93_FUNC_OFFSET) +#define FIOPAD_C59 (FPinIndex)FIOPAD_INDEX(FIOPAD_94_FUNC_OFFSET) +#define FIOPAD_E59 (FPinIndex)FIOPAD_INDEX(FIOPAD_95_FUNC_OFFSET) +#define FIOPAD_J57 (FPinIndex)FIOPAD_INDEX(FIOPAD_96_FUNC_OFFSET) +#define FIOPAD_L59 (FPinIndex)FIOPAD_INDEX(FIOPAD_97_FUNC_OFFSET) +#define FIOPAD_N59 (FPinIndex)FIOPAD_INDEX(FIOPAD_98_FUNC_OFFSET) +#define FIOPAD_C57 (FPinIndex)FIOPAD_INDEX(FIOPAD_29_FUNC_OFFSET) +#define FIOPAD_E57 (FPinIndex)FIOPAD_INDEX(FIOPAD_30_FUNC_OFFSET) +#define FIOPAD_E31 (FPinIndex)FIOPAD_INDEX(FIOPAD_99_FUNC_OFFSET) +#define FIOPAD_G31 (FPinIndex)FIOPAD_INDEX(FIOPAD_100_FUNC_OFFSET) +#define FIOPAD_N41 (FPinIndex)FIOPAD_INDEX(FIOPAD_101_FUNC_OFFSET) +#define FIOPAD_N39 (FPinIndex)FIOPAD_INDEX(FIOPAD_102_FUNC_OFFSET) +#define FIOPAD_J33 (FPinIndex)FIOPAD_INDEX(FIOPAD_103_FUNC_OFFSET) +#define FIOPAD_N33 (FPinIndex)FIOPAD_INDEX(FIOPAD_104_FUNC_OFFSET) +#define FIOPAD_L33 (FPinIndex)FIOPAD_INDEX(FIOPAD_105_FUNC_OFFSET) +#define FIOPAD_N45 (FPinIndex)FIOPAD_INDEX(FIOPAD_106_FUNC_OFFSET) +#define FIOPAD_N43 (FPinIndex)FIOPAD_INDEX(FIOPAD_107_FUNC_OFFSET) +#define FIOPAD_L31 (FPinIndex)FIOPAD_INDEX(FIOPAD_108_FUNC_OFFSET) +#define FIOPAD_J31 (FPinIndex)FIOPAD_INDEX(FIOPAD_109_FUNC_OFFSET) +#define FIOPAD_J29 (FPinIndex)FIOPAD_INDEX(FIOPAD_110_FUNC_OFFSET) +#define FIOPAD_E29 (FPinIndex)FIOPAD_INDEX(FIOPAD_111_FUNC_OFFSET) +#define FIOPAD_G29 (FPinIndex)FIOPAD_INDEX(FIOPAD_112_FUNC_OFFSET) +#define FIOPAD_N27 (FPinIndex)FIOPAD_INDEX(FIOPAD_113_FUNC_OFFSET) +#define FIOPAD_L29 (FPinIndex)FIOPAD_INDEX(FIOPAD_114_FUNC_OFFSET) +#define FIOPAD_J37 (FPinIndex)FIOPAD_INDEX(FIOPAD_115_FUNC_OFFSET) +#define FIOPAD_J39 (FPinIndex)FIOPAD_INDEX(FIOPAD_116_FUNC_OFFSET) +#define FIOPAD_G41 (FPinIndex)FIOPAD_INDEX(FIOPAD_117_FUNC_OFFSET) +#define FIOPAD_E43 (FPinIndex)FIOPAD_INDEX(FIOPAD_118_FUNC_OFFSET) +#define FIOPAD_L43 (FPinIndex)FIOPAD_INDEX(FIOPAD_119_FUNC_OFFSET) +#define FIOPAD_C43 (FPinIndex)FIOPAD_INDEX(FIOPAD_120_FUNC_OFFSET) +#define FIOPAD_E41 (FPinIndex)FIOPAD_INDEX(FIOPAD_121_FUNC_OFFSET) +#define FIOPAD_L45 (FPinIndex)FIOPAD_INDEX(FIOPAD_122_FUNC_OFFSET) +#define FIOPAD_J43 (FPinIndex)FIOPAD_INDEX(FIOPAD_123_FUNC_OFFSET) +#define FIOPAD_J41 (FPinIndex)FIOPAD_INDEX(FIOPAD_124_FUNC_OFFSET) +#define FIOPAD_L39 (FPinIndex)FIOPAD_INDEX(FIOPAD_125_FUNC_OFFSET) +#define FIOPAD_E37 (FPinIndex)FIOPAD_INDEX(FIOPAD_126_FUNC_OFFSET) +#define FIOPAD_E35 (FPinIndex)FIOPAD_INDEX(FIOPAD_127_FUNC_OFFSET) +#define FIOPAD_G35 (FPinIndex)FIOPAD_INDEX(FIOPAD_128_FUNC_OFFSET) +#define FIOPAD_J35 (FPinIndex)FIOPAD_INDEX(FIOPAD_129_FUNC_OFFSET) +#define FIOPAD_L37 (FPinIndex)FIOPAD_INDEX(FIOPAD_130_FUNC_OFFSET) +#define FIOPAD_N35 (FPinIndex)FIOPAD_INDEX(FIOPAD_131_FUNC_OFFSET) +#define FIOPAD_R51 (FPinIndex)FIOPAD_INDEX(FIOPAD_132_FUNC_OFFSET) +#define FIOPAD_R49 (FPinIndex)FIOPAD_INDEX(FIOPAD_133_FUNC_OFFSET) +#define FIOPAD_N51 (FPinIndex)FIOPAD_INDEX(FIOPAD_134_FUNC_OFFSET) +#define FIOPAD_N55 (FPinIndex)FIOPAD_INDEX(FIOPAD_135_FUNC_OFFSET) +#define FIOPAD_L55 (FPinIndex)FIOPAD_INDEX(FIOPAD_136_FUNC_OFFSET) +#define FIOPAD_J55 (FPinIndex)FIOPAD_INDEX(FIOPAD_137_FUNC_OFFSET) +#define FIOPAD_J45 (FPinIndex)FIOPAD_INDEX(FIOPAD_138_FUNC_OFFSET) +#define FIOPAD_E47 (FPinIndex)FIOPAD_INDEX(FIOPAD_139_FUNC_OFFSET) +#define FIOPAD_G47 (FPinIndex)FIOPAD_INDEX(FIOPAD_140_FUNC_OFFSET) +#define FIOPAD_J47 (FPinIndex)FIOPAD_INDEX(FIOPAD_141_FUNC_OFFSET) +#define FIOPAD_J49 (FPinIndex)FIOPAD_INDEX(FIOPAD_142_FUNC_OFFSET) +#define FIOPAD_N49 (FPinIndex)FIOPAD_INDEX(FIOPAD_143_FUNC_OFFSET) +#define FIOPAD_L51 (FPinIndex)FIOPAD_INDEX(FIOPAD_144_FUNC_OFFSET) +#define FIOPAD_L49 (FPinIndex)FIOPAD_INDEX(FIOPAD_145_FUNC_OFFSET) +#define FIOPAD_N53 (FPinIndex)FIOPAD_INDEX(FIOPAD_146_FUNC_OFFSET) +#define FIOPAD_J53 (FPinIndex)FIOPAD_INDEX(FIOPAD_147_FUNC_OFFSET) + +/* register offset of iopad delay */ +#define FIOPAD_AJ55_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_10_DELAY_OFFSET) +#define FIOPAD_AL55_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_11_DELAY_OFFSET) +#define FIOPAD_AL53_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_12_DELAY_OFFSET) +#define FIOPAD_AN51_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_13_DELAY_OFFSET) +#define FIOPAD_AR51_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_14_DELAY_OFFSET) +#define FIOPAD_AJ57_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_23_DELAY_OFFSET) +#define FIOPAD_AG59_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_24_DELAY_OFFSET) +#define FIOPAD_AG57_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_25_DELAY_OFFSET) +#define FIOPAD_AE59_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_26_DELAY_OFFSET) +#define FIOPAD_BA55_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_32_DELAY_OFFSET) +#define FIOPAD_BA53_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_33_DELAY_OFFSET) +#define FIOPAD_AR59_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_34_DELAY_OFFSET) +#define FIOPAD_AU59_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_35_DELAY_OFFSET) +#define FIOPAD_A45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_55_DELAY_OFFSET) +#define FIOPAD_C45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_56_DELAY_OFFSET) +#define FIOPAD_A47_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_57_DELAY_OFFSET) +#define FIOPAD_A49_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_58_DELAY_OFFSET) +#define FIOPAD_C49_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_59_DELAY_OFFSET) +#define FIOPAD_A51_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_60_DELAY_OFFSET) +#define FIOPAD_A33_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_61_DELAY_OFFSET) +#define FIOPAD_C33_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_62_DELAY_OFFSET) +#define FIOPAD_C31_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_63_DELAY_OFFSET) +#define FIOPAD_A31_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_64_DELAY_OFFSET) +#define FIOPAD_AJ53_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_65_DELAY_OFFSET) +#define FIOPAD_AL49_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_66_DELAY_OFFSET) +#define FIOPAD_AL47_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_67_DELAY_OFFSET) +#define FIOPAD_AN49_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_68_DELAY_OFFSET) +#define FIOPAD_AG51_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_148_DELAY_OFFSET) +#define FIOPAD_AJ51_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_69_DELAY_OFFSET) +#define FIOPAD_AG49_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_70_DELAY_OFFSET) +#define FIOPAD_AE55_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_71_DELAY_OFFSET) +#define FIOPAD_AE53_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_72_DELAY_OFFSET) +#define FIOPAD_AG55_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_73_DELAY_OFFSET) +#define FIOPAD_AJ49_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_74_DELAY_OFFSET) +#define FIOPAD_AC55_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_75_DELAY_OFFSET) +#define FIOPAD_AC53_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_76_DELAY_OFFSET) +#define FIOPAD_AE51_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_77_DELAY_OFFSET) +#define FIOPAD_W51_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_78_DELAY_OFFSET) +#define FIOPAD_W53_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_80_DELAY_OFFSET) +#define FIOPAD_U55_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_81_DELAY_OFFSET) +#define FIOPAD_U53_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_82_DELAY_OFFSET) +#define FIOPAD_AE49_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_83_DELAY_OFFSET) +#define FIOPAD_AC49_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_84_DELAY_OFFSET) +#define FIOPAD_AE47_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_85_DELAY_OFFSET) +#define FIOPAD_AA47_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_86_DELAY_OFFSET) +#define FIOPAD_AA49_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_87_DELAY_OFFSET) +#define FIOPAD_W49_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_88_DELAY_OFFSET) +#define FIOPAD_AA51_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_89_DELAY_OFFSET) +#define FIOPAD_U49_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_90_DELAY_OFFSET) +#define FIOPAD_J59_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_92_DELAY_OFFSET) +#define FIOPAD_L57_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_93_DELAY_OFFSET) +#define FIOPAD_C59_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_94_DELAY_OFFSET) +#define FIOPAD_E59_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_95_DELAY_OFFSET) +#define FIOPAD_J57_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_96_DELAY_OFFSET) +#define FIOPAD_L59_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_97_DELAY_OFFSET) +#define FIOPAD_N59_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_98_DELAY_OFFSET) +#define FIOPAD_E31_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_99_DELAY_OFFSET) +#define FIOPAD_G31_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_100_DELAY_OFFSET) +#define FIOPAD_N41_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_101_DELAY_OFFSET) +#define FIOPAD_N39_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_102_DELAY_OFFSET) +#define FIOPAD_J33_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_103_DELAY_OFFSET) +#define FIOPAD_N33_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_104_DELAY_OFFSET) +#define FIOPAD_L33_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_105_DELAY_OFFSET) +#define FIOPAD_N45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_106_DELAY_OFFSET) +#define FIOPAD_N43_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_107_DELAY_OFFSET) +#define FIOPAD_L31_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_108_DELAY_OFFSET) +#define FIOPAD_J31_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_109_DELAY_OFFSET) +#define FIOPAD_J29_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_110_DELAY_OFFSET) +#define FIOPAD_E29_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_111_DELAY_OFFSET) +#define FIOPAD_G29_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_112_DELAY_OFFSET) +#define FIOPAD_J37_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_115_DELAY_OFFSET) +#define FIOPAD_J39_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_116_DELAY_OFFSET) +#define FIOPAD_G41_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_117_DELAY_OFFSET) +#define FIOPAD_E43_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_118_DELAY_OFFSET) +#define FIOPAD_L43_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_119_DELAY_OFFSET) +#define FIOPAD_C43_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_120_DELAY_OFFSET) +#define FIOPAD_E41_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_121_DELAY_OFFSET) +#define FIOPAD_L45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_122_DELAY_OFFSET) +#define FIOPAD_J43_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_123_DELAY_OFFSET) +#define FIOPAD_J41_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_124_DELAY_OFFSET) +#define FIOPAD_L39_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_125_DELAY_OFFSET) +#define FIOPAD_E37_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_126_DELAY_OFFSET) +#define FIOPAD_E35_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_127_DELAY_OFFSET) +#define FIOPAD_G35_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_128_DELAY_OFFSET) +#define FIOPAD_L55_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_136_DELAY_OFFSET) +#define FIOPAD_J55_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_137_DELAY_OFFSET) +#define FIOPAD_J45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_138_DELAY_OFFSET) +#define FIOPAD_E47_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_139_DELAY_OFFSET) +#define FIOPAD_G47_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_140_DELAY_OFFSET) +#define FIOPAD_J47_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_141_DELAY_OFFSET) +#define FIOPAD_J49_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_142_DELAY_OFFSET) +#define FIOPAD_N49_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_143_DELAY_OFFSET) +#define FIOPAD_L51_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_144_DELAY_OFFSET) +#define FIOPAD_L49_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_145_DELAY_OFFSET) +#define FIOPAD_N53_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_146_DELAY_OFFSET) +#define FIOPAD_J53_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_147_DELAY_OFFSET) + +/***************** Macros (Inline Functions) Definitions *********************/ + +/*****************************************************************************/ + + +#ifdef __cplusplus +} + +#endif + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/board/e2000/q/fiopad_config.c b/bsp/phytium/libraries/standalone/board/e2000/q/fiopad_config.c new file mode 100644 index 0000000000..b29a9e0a2b --- /dev/null +++ b/bsp/phytium/libraries/standalone/board/e2000/q/fiopad_config.c @@ -0,0 +1,619 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fiopad_config.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:25:29 + * Description:  This files is for io-pad function definition + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 huanghe 2021/11/5 init commit + * 1.1 zhugengyu 2022/3/21 adopt to lastest tech spec. + */ + +/***************************** Include Files *********************************/ +#include "fiopad.h" +#include "fparameters.h" +#include "fdebug.h" +#include "fpinctrl.h" +#include "fassert.h" +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FIOPAD_DEBUG_TAG "FIOPAD-CFG" +#define FIOPAD_ERROR(format, ...) FT_DEBUG_PRINT_E(FIOPAD_DEBUG_TAG, format, ##__VA_ARGS__) +#define FIOPAD_WARN(format, ...) FT_DEBUG_PRINT_W(FIOPAD_DEBUG_TAG, format, ##__VA_ARGS__) +#define FIOPAD_INFO(format, ...) FT_DEBUG_PRINT_I(FIOPAD_DEBUG_TAG, format, ##__VA_ARGS__) +#define FIOPAD_DEBUG(format, ...) FT_DEBUG_PRINT_D(FIOPAD_DEBUG_TAG, format, ##__VA_ARGS__) + +/************************** Function Prototypes ******************************/ + +/*****************************************************************************/ +/** + * @name: FIOPadSetSpimMux + * @msg: set iopad mux for spim + * @return {*} + * @param {u32} spim_id, instance id of spi + */ +void FIOPadSetSpimMux(u32 spim_id) +{ + if (FSPI0_ID == spim_id) + { + FIOPAD_INFO("%d-%d-%d-%d", FPinGetFunc(FIOPAD_W55), + FPinGetFunc(FIOPAD_W53), FPinGetFunc(FIOPAD_U55), + FPinGetFunc(FIOPAD_U53)); + FPinSetFunc(FIOPAD_W55, FPIN_FUNC2); /* sclk */ + FPinSetFunc(FIOPAD_W53, FPIN_FUNC2); /* txd */ + FPinSetFunc(FIOPAD_U55, FPIN_FUNC2); /* rxd */ + FPinSetFunc(FIOPAD_U53, FPIN_FUNC2); /* csn0 */ + FIOPAD_INFO("%d-%d-%d-%d", FPinGetFunc(FIOPAD_W55), + FPinGetFunc(FIOPAD_W53), FPinGetFunc(FIOPAD_U55), + FPinGetFunc(FIOPAD_U53)); + } + else if (FSPI1_ID == spim_id) + { + FIOPAD_INFO("%d-%d-%d-%d", FPinGetFunc(FIOPAD_N43), + FPinGetFunc(FIOPAD_L31), FPinGetFunc(FIOPAD_J31), + FPinGetFunc(FIOPAD_J29)); + FPinSetFunc(FIOPAD_N43, FPIN_FUNC4); /* sclk */ + FPinSetFunc(FIOPAD_L31, FPIN_FUNC4); /* txd */ + FPinSetFunc(FIOPAD_J31, FPIN_FUNC4); /* rxd */ + FPinSetFunc(FIOPAD_J29, FPIN_FUNC4); /* csn0 */ + FIOPAD_INFO("%d-%d-%d-%d", FPinGetFunc(FIOPAD_N43), + FPinGetFunc(FIOPAD_L31), FPinGetFunc(FIOPAD_J31), + FPinGetFunc(FIOPAD_J29)); + } + else if (FSPI2_ID == spim_id) + { + FPinSetFunc(FIOPAD_A33, FPIN_FUNC0); /* sclk */ + FPinSetFunc(FIOPAD_C33, FPIN_FUNC0); /* txd */ + FPinSetFunc(FIOPAD_C31, FPIN_FUNC0); /* rxd */ + FPinSetFunc(FIOPAD_A31, FPIN_FUNC0); /* csn0 */ + } + else if (FSPI3_ID == spim_id) + { + FPinSetFunc(FIOPAD_AC55, FPIN_FUNC2); /* sclk */ + FPinSetFunc(FIOPAD_AC53, FPIN_FUNC2); /* txd */ + FPinSetFunc(FIOPAD_AE51, FPIN_FUNC2); /* rxd */ + FPinSetFunc(FIOPAD_W51, FPIN_FUNC2); /* csn0 */ + } +} + +/** + * @name: FIOPadSetGpioMux + * @msg: set iopad mux for gpio + * @return {*} + * @param {u32} gpio_id, instance id of gpio + * @param {u32} pin_id, index of pin + */ +void FIOPadSetGpioMux(u32 gpio_id, u32 pin_id) +{ + if (FGPIO_ID_2 == gpio_id) + { + switch (pin_id) + { + case 11: /* gpio 2-a-11 */ + FPinSetFunc(FIOPAD_N49, FPIN_FUNC0); + break; + case 12: /* gpio 2-a-12 */ + FPinSetFunc(FIOPAD_L51, FPIN_FUNC0); + break; + case 13: /* gpio 2-a-13 */ + FPinSetFunc(FIOPAD_L49, FPIN_FUNC0); + break; + case 14: /* gpio 2-a-14 */ + FPinSetFunc(FIOPAD_N53, FPIN_FUNC0); + break; + case 15: /* gpio 2-a-15 */ + FPinSetFunc(FIOPAD_J53, FPIN_FUNC0); + break; + } + } + else if (FGPIO_ID_3 == gpio_id) + { + switch (pin_id) + { + case 3: /* gpio 3-a-3 */ + FPinSetFunc(FIOPAD_A33, FPIN_FUNC6); + break; + case 4: /* gpio 3-a-4 */ + FPinSetFunc(FIOPAD_C33, FPIN_FUNC6); + break; + case 5: /* gpio 3-a-5 */ + FPinSetFunc(FIOPAD_C31, FPIN_FUNC6); + break; + case 6: /* gpio 3-a-6 */ + FPinSetFunc(FIOPAD_A31, FPIN_FUNC6); + break; + default: + break; + } + } + else if (FGPIO_ID_4 == gpio_id) + { + switch (pin_id) + { + case 5: /* gpio 4-a-5 */ + FPinSetFunc(FIOPAD_W51, FPIN_FUNC6); + break; + case 9: /* gpio 4-a-9 */ + FPinSetFunc(FIOPAD_U53, FPIN_FUNC6); + break; + default: + break; + } + } +} + + +/** + * @name: FIOPadSetCanMux + * @msg: set iopad mux for can + * @return {*} + * @param {u32} can_id, instance id of can + */ +void FIOPadSetCanMux(u32 can_id) +{ + if (can_id == FCAN_INSTANCE_0) + { + /* mio0 */ + FPinSetFunc(FIOPAD_A41, FPIN_FUNC0); /* can0-tx: func 0 */ + FPinSetFunc(FIOPAD_A43, FPIN_FUNC0); /* can0-rx: func 0 */ + } + else if (can_id == FCAN_INSTANCE_1) + { + /* mio1 */ + FPinSetFunc(FIOPAD_A45, FPIN_FUNC0); /* can1-tx: func 0 */ + FPinSetFunc(FIOPAD_C45, FPIN_FUNC0); /* can1-rx: func 0 */ + } + else + { + FIOPAD_ERROR("can id is error.\r\n"); + } +} + +/** + * @name: FIOPadSetQspiMux + * @msg: set iopad mux for qspi + * @return {*} + * @param {u32} qspi_id, id of qspi instance + * @param {u32} cs_id, id of qspi cs + */ +void FIOPadSetQspiMux(u32 qspi_id, u32 cs_id) +{ + + if (qspi_id == FQSPI_INSTANCE_0) + { + /* add sck, io0-io3 iopad multiplex */ + } + + if (cs_id == FQSPI_CS_0) + { + FPinSetFunc(FIOPAD_AR55, FPIN_FUNC0); + } + else if (cs_id == FQSPI_CS_1) + { + FPinSetFunc(FIOPAD_AR49, FPIN_FUNC0); + } + else if (cs_id == FQSPI_CS_2) + { + FPinSetFunc(FIOPAD_C37, FPIN_FUNC5); + } + else if (cs_id == FQSPI_CS_3) + { + FPinSetFunc(FIOPAD_A37, FPIN_FUNC5); + } + else + { + FIOPAD_ERROR("can id is error.\r\n"); + } +} + +/** + * @name: FIOPadSetPwmMux + * @msg: set iopad mux for pwm + * @return {*} + * @param {u32} pwm_id, id of pwm instance + * @param {u32} pwm_channel, channel of pwm instance + */ +void FIOPadSetPwmMux(u32 pwm_id, u32 pwm_channel) +{ + FASSERT(pwm_id < FPWM_INSTANCE_NUM); + FASSERT(pwm_channel < FPWM_CHANNEL_NUM); + + switch (pwm_id) + { + case FPWM_INSTANCE_0: + if (pwm_channel == 0) + { + FPinSetFunc(FIOPAD_AL59, FPIN_FUNC1); /* PWM0_OUT: func 1 */ + } + if (pwm_channel == 1) + { + FPinSetFunc(FIOPAD_AJ57, FPIN_FUNC1); /* PWM1_OUT: func 1 */ + } + break; + + case FPWM_INSTANCE_1: + if (pwm_channel == 0) + { + FPinSetFunc(FIOPAD_AG57, FPIN_FUNC1); /* PWM2_OUT: func 1 */ + } + if (pwm_channel == 1) + { + FPinSetFunc(FIOPAD_AC59, FPIN_FUNC1); /* PWM3_OUT: func 1 */ + } + break; + + case FPWM_INSTANCE_2: + if (pwm_channel == 0) + { + FPinSetFunc(FIOPAD_BA55, FPIN_FUNC1); /* PWM4_OUT: func 1 */ + } + if (pwm_channel == 1) + { + FPinSetFunc(FIOPAD_C39, FPIN_FUNC2); /* PWM5_OUT: func 2 */ + } + break; + + case FPWM_INSTANCE_3: + if (pwm_channel == 0) + { + FPinSetFunc(FIOPAD_A37, FPIN_FUNC2); /* PWM6_OUT: func 2 */ + } + if (pwm_channel == 1) + { + FPinSetFunc(FIOPAD_A43, FPIN_FUNC2); /* PWM7_OUT: func 2 */ + } + break; + + case FPWM_INSTANCE_4: + if (pwm_channel == 0) + { + FPinSetFunc(FIOPAD_C45, FPIN_FUNC2); /* PWM8_OUT: func 2 */ + } + if (pwm_channel == 1) + { + FPinSetFunc(FIOPAD_A49, FPIN_FUNC2); /* PWM9_OUT: func 2 */ + } + break; + + case FPWM_INSTANCE_5: + if (pwm_channel == 0) + { + FPinSetFunc(FIOPAD_A51, FPIN_FUNC2); /* PWM10_OUT: func 2 */ + } + if (pwm_channel == 1) + { + FPinSetFunc(FIOPAD_C33, FPIN_FUNC2); /* PWM11_OUT: func 2 */ + } + break; + + case FPWM_INSTANCE_6: + if (pwm_channel == 0) + { + FPinSetFunc(FIOPAD_A31, FPIN_FUNC2); /* PWM12_OUT: func 2 */ + } + if (pwm_channel == 1) + { + FPinSetFunc(FIOPAD_J39, FPIN_FUNC3); /* PWM13_OUT: func 3 */ + } + break; + + case FPWM_INSTANCE_7: + if (pwm_channel == 0) + { + FPinSetFunc(FIOPAD_E43, FPIN_FUNC3); /* PWM14_OUT: func 3 */ + } + if (pwm_channel == 1) + { + FPinSetFunc(FIOPAD_C43, FPIN_FUNC3); /* PWM15_OUT: func 3 */ + } + break; + + default: + FIOPAD_ERROR("pwm id is error.\r\n"); + break; + } +} + + +/** + * @name: FIOPadSetAdcMux + * @msg: set iopad mux for adc + * @return {*} + * @param {u32} adc_id, id of adc instance + * @param {u32} adc_channel, id of adc channel + */ +void FIOPadSetAdcMux(u32 adc_id, u32 adc_channel) +{ + if (adc_id == FADC_INSTANCE_0) + { + switch (adc_channel) + { + case FADC_CHANNEL_0: + FPinSetFunc(FIOPAD_R51, FPIN_FUNC7); /* adc0-0: func 7 */ + break; + case FADC_CHANNEL_1: + FPinSetFunc(FIOPAD_R49, FPIN_FUNC7); /* adc0-1: func 7 */ + break; + case FADC_CHANNEL_2: + FPinSetFunc(FIOPAD_N51, FPIN_FUNC7); /* adc0-2: func 7 */ + break; + case FADC_CHANNEL_3: + FPinSetFunc(FIOPAD_N55, FPIN_FUNC7); /* adc0-3: func 7 */ + break; + case FADC_CHANNEL_4: + FPinSetFunc(FIOPAD_L55, FPIN_FUNC7); /* adc0-4: func 7 */ + break; + case FADC_CHANNEL_5: + FPinSetFunc(FIOPAD_J55, FPIN_FUNC7); /* adc0-5: func 7 */ + break; + case FADC_CHANNEL_6: + FPinSetFunc(FIOPAD_J45, FPIN_FUNC7); /* adc0-6: func 7 */ + break; + case FADC_CHANNEL_7: + FPinSetFunc(FIOPAD_E47, FPIN_FUNC7); /* adc0-7: func 7 */ + break; + default: + FIOPAD_ERROR("adc %d channel %d is error.\r\n", adc_id, adc_channel); + break; + } + } + else if (adc_id == FADC_INSTANCE_1) + { + switch (adc_channel) + { + case FADC_CHANNEL_0: + FPinSetFunc(FIOPAD_G47, FPIN_FUNC7); /* adc1-0: func 7 */ + break; + case FADC_CHANNEL_1: + FPinSetFunc(FIOPAD_J47, FPIN_FUNC7); /* adc1-1: func 7 */ + break; + case FADC_CHANNEL_2: + FPinSetFunc(FIOPAD_J49, FPIN_FUNC7); /* adc1-2: func 7 */ + break; + case FADC_CHANNEL_3: + FPinSetFunc(FIOPAD_N49, FPIN_FUNC7); /* adc1-3: func 7 */ + break; + case FADC_CHANNEL_4: + FPinSetFunc(FIOPAD_L51, FPIN_FUNC7); /* adc1-4: func 7 */ + break; + case FADC_CHANNEL_5: + FPinSetFunc(FIOPAD_L49, FPIN_FUNC7); /* adc1-5: func 7 */ + break; + case FADC_CHANNEL_6: + FPinSetFunc(FIOPAD_N53, FPIN_FUNC7); /* adc1-6: func 7 */ + break; + case FADC_CHANNEL_7: + FPinSetFunc(FIOPAD_J53, FPIN_FUNC7); /* adc1-7: func 7 */ + break; + default: + FIOPAD_ERROR("adc %d channel %d is error.\r\n", adc_id, adc_channel); + break; + } + } + else + { + FIOPAD_ERROR("adc %d channel %d is error.\r\n", adc_id, adc_channel); + } +} + +/** + * @name: FIOPadSetMioMux + * @msg: set iopad mux for mio + * @return {*} + * @param {u32} mio_id, instance id of i2c + */ +void FIOPadSetMioMux(u32 mio_id) +{ + switch (mio_id) + { + case MIO_INSTANCE_0: + { + FPinSetFunc(FIOPAD_A41, FPIN_FUNC5); /* scl */ + FPinSetFunc(FIOPAD_A43, FPIN_FUNC5); /* sda */ + } + break; + case MIO_INSTANCE_1: + { + FPinSetFunc(FIOPAD_A45, FPIN_FUNC5); /* scl */ + FPinSetFunc(FIOPAD_C45, FPIN_FUNC5); /* sda */ + } + break; + case MIO_INSTANCE_2: + { + FPinSetFunc(FIOPAD_A47, FPIN_FUNC5); /* scl */ + FPinSetFunc(FIOPAD_A49, FPIN_FUNC5); /* sda */ + } + break; + case MIO_INSTANCE_3: + { + FPinSetFunc(FIOPAD_BA55, FPIN_FUNC4); /* scl */ + FPinSetFunc(FIOPAD_BA53, FPIN_FUNC4); /* sda */ + } + break; + case MIO_INSTANCE_4: + { + FPinSetFunc(FIOPAD_R59, FPIN_FUNC4); /* scl */ + FPinSetFunc(FIOPAD_U59, FPIN_FUNC4); /* sda */ + } + break; + case MIO_INSTANCE_5: + { + FPinSetFunc(FIOPAD_W49, FPIN_FUNC4); /* scl */ + FPinSetFunc(FIOPAD_U57, FPIN_FUNC4); /* sda */ + } + break; + case MIO_INSTANCE_6: + { + FPinSetFunc(FIOPAD_AA57, FPIN_FUNC4); /* scl */ + FPinSetFunc(FIOPAD_AA59, FPIN_FUNC4); /* sda */ + } + break; + case MIO_INSTANCE_7: + { + FPinSetFunc(FIOPAD_A39, FPIN_FUNC4); /* scl */ + FPinSetFunc(FIOPAD_C39, FPIN_FUNC4); /* sda */ + } + break; + case MIO_INSTANCE_8: + { + FPinSetFunc(FIOPAD_AA49, FPIN_FUNC4); /* scl */ + FPinSetFunc(FIOPAD_W49, FPIN_FUNC4); /* sda */ + } + break; + case MIO_INSTANCE_9: + { + FPinSetFunc(FIOPAD_AA51, FPIN_FUNC4); /* scl */ + FPinSetFunc(FIOPAD_U49, FPIN_FUNC4); /* sda */ + } + break; + case MIO_INSTANCE_10: + { + FPinSetFunc(FIOPAD_C49, FPIN_FUNC5); /* scl */ + FPinSetFunc(FIOPAD_A51, FPIN_FUNC5); /* sda */ + } + break; + case MIO_INSTANCE_11: + { + FPinSetFunc(FIOPAD_N27, FPIN_FUNC3); /* scl */ + FPinSetFunc(FIOPAD_L29, FPIN_FUNC3); /* sda */ + } + break; + case MIO_INSTANCE_12: + { + FPinSetFunc(FIOPAD_E41, FPIN_FUNC3); /* scl */ + FPinSetFunc(FIOPAD_L45, FPIN_FUNC3); /* sda */ + } + break; + case MIO_INSTANCE_13: + { + FPinSetFunc(FIOPAD_J49, FPIN_FUNC6); /* scl */ + FPinSetFunc(FIOPAD_N49, FPIN_FUNC6); /* sda */ + } + break; + case MIO_INSTANCE_14: + { + FPinSetFunc(FIOPAD_L51, FPIN_FUNC6); /* scl */ + FPinSetFunc(FIOPAD_L49, FPIN_FUNC6); /* sda */ + } + break; + case MIO_INSTANCE_15: + { + FPinSetFunc(FIOPAD_N53, FPIN_FUNC6); /* scl */ + FPinSetFunc(FIOPAD_J53, FPIN_FUNC6); /* sda */ + } + break; + default: + break; + } +} + +/** + * @name: FIOPadSetTachoMux + * @msg: set iopad mux for pwm_in + * @return {*} + * @param {u32} pwm_in_id, instance id of tacho + */ +void FIOPadSetTachoMux(u32 pwm_in_id) +{ + switch (pwm_in_id) + { + case TACHO_INSTANCE_0: + FPinSetFunc(FIOPAD_AN57, FPIN_FUNC1); + break; + case TACHO_INSTANCE_1: + FPinSetFunc(FIOPAD_AJ59, FPIN_FUNC1); + break; + case TACHO_INSTANCE_2: + FPinSetFunc(FIOPAD_AG59, FPIN_FUNC1); + break; + case TACHO_INSTANCE_3: + FPinSetFunc(FIOPAD_AE59, FPIN_FUNC1); + break; + case TACHO_INSTANCE_4: + FPinSetFunc(FIOPAD_AC57, FPIN_FUNC1); + break; + case TACHO_INSTANCE_5: + FPinSetFunc(FIOPAD_BA53, FPIN_FUNC1); + break; + case TACHO_INSTANCE_6: + FPinSetFunc(FIOPAD_C37, FPIN_FUNC2); + break; + case TACHO_INSTANCE_7: + FPinSetFunc(FIOPAD_A41, FPIN_FUNC2); + break; + case TACHO_INSTANCE_8: + FPinSetFunc(FIOPAD_A45, FPIN_FUNC2); + break; + case TACHO_INSTANCE_9: + FPinSetFunc(FIOPAD_A47, FPIN_FUNC2); + break; + case TACHO_INSTANCE_10: + FPinSetFunc(FIOPAD_C49, FPIN_FUNC2); + break; + case TACHO_INSTANCE_11: + FPinSetFunc(FIOPAD_A33, FPIN_FUNC2); + break; + case TACHO_INSTANCE_12: + FPinSetFunc(FIOPAD_C31, FPIN_FUNC2); + break; + case TACHO_INSTANCE_13: + FPinSetFunc(FIOPAD_AA49, FPIN_FUNC2); + break; + case TACHO_INSTANCE_14: + FPinSetFunc(FIOPAD_AA51, FPIN_FUNC2); + break; + case TACHO_INSTANCE_15: + FPinSetFunc(FIOPAD_G59, FPIN_FUNC2); + break; + default: + break; + } +} + +/** + * @name: FIOPadSetUartMux + * @msg: set iopad mux for uart + * @return {*} + * @param {u32} uart_id, instance id of uart + */ +void FIOPadSetUartMux(u32 uart_id) +{ + switch (uart_id) + { + case FUART0_ID: + FPinSetFunc(FIOPAD_J37, FPIN_FUNC4); + FPinSetFunc(FIOPAD_J39, FPIN_FUNC4); + break; + case FUART1_ID: + FPinSetFunc(FIOPAD_AW51, FPIN_FUNC0); + FPinSetFunc(FIOPAD_AU51, FPIN_FUNC0); + break; + case FUART2_ID: + FPinSetFunc(FIOPAD_A47, FPIN_FUNC0); + FPinSetFunc(FIOPAD_A49, FPIN_FUNC0); + break; + case FUART3_ID: + FPinSetFunc(FIOPAD_L37, FPIN_FUNC2); + FPinSetFunc(FIOPAD_N35, FPIN_FUNC2); + break; + default: + break; + } +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/board/e2000/q/fparameters.h b/bsp/phytium/libraries/standalone/board/e2000/q/fparameters.h new file mode 100644 index 0000000000..9d7a5691e8 --- /dev/null +++ b/bsp/phytium/libraries/standalone/board/e2000/q/fparameters.h @@ -0,0 +1,50 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fparameters.h + * Date: 2022-02-11 13:33:28 + * LastEditTime: 2022-02-17 18:00:50 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef BOARD_E2000Q_PARAMTERERS_H +#define BOARD_E2000Q_PARAMTERERS_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ +#include "fparameters_comm.h" + +/************************** Constant Definitions *****************************/ +#define CORE0_AFF 0x000U +#define CORE1_AFF 0x100U +#define CORE2_AFF 0x200U +#define CORE3_AFF 0x201U + +#define FT_CPUS_NR 4U +/*****************************************************************************/ + + +#ifdef __cplusplus +} + +#endif + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/board/e2000/s/fiopad.h b/bsp/phytium/libraries/standalone/board/e2000/s/fiopad.h new file mode 100644 index 0000000000..d6588ad99e --- /dev/null +++ b/bsp/phytium/libraries/standalone/board/e2000/s/fiopad.h @@ -0,0 +1,270 @@ + +#ifndef BOARD_E2000Q_FIOPAD_H +#define BOARD_E2000Q_FIOPAD_H + +#ifdef __cplusplus +extern "C" +{ +#endif + + +/***************************** Include Files *********************************/ +#include "fiopad_comm.h" + +/************************** Constant Definitions *****************************/ +/* register offset of iopad function / pull / driver strength */ +#define FIOPAD_AN55 (FPinIndex)FIOPAD_INDEX(FIOPAD_0_FUNC_OFFSET) +#define FIOPAD_AW43 (FPinIndex)FIOPAD_INDEX(FIOPAD_2_FUNC_OFFSET) +#define FIOPAD_AR51 (FPinIndex)FIOPAD_INDEX(FIOPAD_9_FUNC_OFFSET) +#define FIOPAD_AJ51 (FPinIndex)FIOPAD_INDEX(FIOPAD_10_FUNC_OFFSET) +#define FIOPAD_AL51 (FPinIndex)FIOPAD_INDEX(FIOPAD_11_FUNC_OFFSET) +#define FIOPAD_AL49 (FPinIndex)FIOPAD_INDEX(FIOPAD_12_FUNC_OFFSET) +#define FIOPAD_AN47 (FPinIndex)FIOPAD_INDEX(FIOPAD_13_FUNC_OFFSET) +#define FIOPAD_AR47 (FPinIndex)FIOPAD_INDEX(FIOPAD_14_FUNC_OFFSET) +#define FIOPAD_BA53 (FPinIndex)FIOPAD_INDEX(FIOPAD_15_FUNC_OFFSET) +#define FIOPAD_BA55 (FPinIndex)FIOPAD_INDEX(FIOPAD_16_FUNC_OFFSET) +#define FIOPAD_AW53 (FPinIndex)FIOPAD_INDEX(FIOPAD_17_FUNC_OFFSET) +#define FIOPAD_AW55 (FPinIndex)FIOPAD_INDEX(FIOPAD_18_FUNC_OFFSET) +#define FIOPAD_AU51 (FPinIndex)FIOPAD_INDEX(FIOPAD_19_FUNC_OFFSET) +#define FIOPAD_AN53 (FPinIndex)FIOPAD_INDEX(FIOPAD_20_FUNC_OFFSET) +#define FIOPAD_AL55 (FPinIndex)FIOPAD_INDEX(FIOPAD_21_FUNC_OFFSET) +#define FIOPAD_AJ55 (FPinIndex)FIOPAD_INDEX(FIOPAD_22_FUNC_OFFSET) +#define FIOPAD_AJ53 (FPinIndex)FIOPAD_INDEX(FIOPAD_23_FUNC_OFFSET) +#define FIOPAD_AG55 (FPinIndex)FIOPAD_INDEX(FIOPAD_24_FUNC_OFFSET) +#define FIOPAD_AG53 (FPinIndex)FIOPAD_INDEX(FIOPAD_25_FUNC_OFFSET) +#define FIOPAD_AE55 (FPinIndex)FIOPAD_INDEX(FIOPAD_26_FUNC_OFFSET) +#define FIOPAD_AC55 (FPinIndex)FIOPAD_INDEX(FIOPAD_27_FUNC_OFFSET) +#define FIOPAD_AC53 (FPinIndex)FIOPAD_INDEX(FIOPAD_28_FUNC_OFFSET) +#define FIOPAD_AR45 (FPinIndex)FIOPAD_INDEX(FIOPAD_31_FUNC_OFFSET) +#define FIOPAD_BA51 (FPinIndex)FIOPAD_INDEX(FIOPAD_32_FUNC_OFFSET) +#define FIOPAD_BA49 (FPinIndex)FIOPAD_INDEX(FIOPAD_33_FUNC_OFFSET) +#define FIOPAD_AR55 (FPinIndex)FIOPAD_INDEX(FIOPAD_34_FUNC_OFFSET) +#define FIOPAD_AU55 (FPinIndex)FIOPAD_INDEX(FIOPAD_35_FUNC_OFFSET) +#define FIOPAD_AR53 (FPinIndex)FIOPAD_INDEX(FIOPAD_36_FUNC_OFFSET) +#define FIOPAD_BA45 (FPinIndex)FIOPAD_INDEX(FIOPAD_37_FUNC_OFFSET) +#define FIOPAD_AW51 (FPinIndex)FIOPAD_INDEX(FIOPAD_38_FUNC_OFFSET) +#define FIOPAD_A31 (FPinIndex)FIOPAD_INDEX(FIOPAD_39_FUNC_OFFSET) +#define FIOPAD_R53 (FPinIndex)FIOPAD_INDEX(FIOPAD_40_FUNC_OFFSET) +#define FIOPAD_R55 (FPinIndex)FIOPAD_INDEX(FIOPAD_41_FUNC_OFFSET) +#define FIOPAD_U55 (FPinIndex)FIOPAD_INDEX(FIOPAD_42_FUNC_OFFSET) +#define FIOPAD_W55 (FPinIndex)FIOPAD_INDEX(FIOPAD_43_FUNC_OFFSET) +#define FIOPAD_U53 (FPinIndex)FIOPAD_INDEX(FIOPAD_44_FUNC_OFFSET) +#define FIOPAD_AA53 (FPinIndex)FIOPAD_INDEX(FIOPAD_45_FUNC_OFFSET) +#define FIOPAD_AA55 (FPinIndex)FIOPAD_INDEX(FIOPAD_46_FUNC_OFFSET) +#define FIOPAD_AW47 (FPinIndex)FIOPAD_INDEX(FIOPAD_47_FUNC_OFFSET) +#define FIOPAD_AU47 (FPinIndex)FIOPAD_INDEX(FIOPAD_48_FUNC_OFFSET) +#define FIOPAD_A35 (FPinIndex)FIOPAD_INDEX(FIOPAD_49_FUNC_OFFSET) +#define FIOPAD_C35 (FPinIndex)FIOPAD_INDEX(FIOPAD_50_FUNC_OFFSET) +#define FIOPAD_C33 (FPinIndex)FIOPAD_INDEX(FIOPAD_51_FUNC_OFFSET) +#define FIOPAD_A33 (FPinIndex)FIOPAD_INDEX(FIOPAD_52_FUNC_OFFSET) +#define FIOPAD_A37 (FPinIndex)FIOPAD_INDEX(FIOPAD_53_FUNC_OFFSET) +#define FIOPAD_A39 (FPinIndex)FIOPAD_INDEX(FIOPAD_54_FUNC_OFFSET) +#define FIOPAD_A41 (FPinIndex)FIOPAD_INDEX(FIOPAD_55_FUNC_OFFSET) +#define FIOPAD_C41 (FPinIndex)FIOPAD_INDEX(FIOPAD_56_FUNC_OFFSET) +#define FIOPAD_A43 (FPinIndex)FIOPAD_INDEX(FIOPAD_57_FUNC_OFFSET) +#define FIOPAD_A45 (FPinIndex)FIOPAD_INDEX(FIOPAD_58_FUNC_OFFSET) +#define FIOPAD_C45 (FPinIndex)FIOPAD_INDEX(FIOPAD_59_FUNC_OFFSET) +#define FIOPAD_A47 (FPinIndex)FIOPAD_INDEX(FIOPAD_60_FUNC_OFFSET) +#define FIOPAD_A29 (FPinIndex)FIOPAD_INDEX(FIOPAD_61_FUNC_OFFSET) +#define FIOPAD_C29 (FPinIndex)FIOPAD_INDEX(FIOPAD_62_FUNC_OFFSET) +#define FIOPAD_C27 (FPinIndex)FIOPAD_INDEX(FIOPAD_63_FUNC_OFFSET) +#define FIOPAD_A27 (FPinIndex)FIOPAD_INDEX(FIOPAD_64_FUNC_OFFSET) +#define FIOPAD_AJ49 (FPinIndex)FIOPAD_INDEX(FIOPAD_65_FUNC_OFFSET) +#define FIOPAD_AL45 (FPinIndex)FIOPAD_INDEX(FIOPAD_66_FUNC_OFFSET) +#define FIOPAD_AL43 (FPinIndex)FIOPAD_INDEX(FIOPAD_67_FUNC_OFFSET) +#define FIOPAD_AN45 (FPinIndex)FIOPAD_INDEX(FIOPAD_68_FUNC_OFFSET) +#define FIOPAD_AG47 (FPinIndex)FIOPAD_INDEX(FIOPAD_148_FUNC_OFFSET) +#define FIOPAD_AJ47 (FPinIndex)FIOPAD_INDEX(FIOPAD_69_FUNC_OFFSET) +#define FIOPAD_AG45 (FPinIndex)FIOPAD_INDEX(FIOPAD_70_FUNC_OFFSET) +#define FIOPAD_AE51 (FPinIndex)FIOPAD_INDEX(FIOPAD_71_FUNC_OFFSET) +#define FIOPAD_AE49 (FPinIndex)FIOPAD_INDEX(FIOPAD_72_FUNC_OFFSET) +#define FIOPAD_AG51 (FPinIndex)FIOPAD_INDEX(FIOPAD_73_FUNC_OFFSET) +#define FIOPAD_AJ45 (FPinIndex)FIOPAD_INDEX(FIOPAD_74_FUNC_OFFSET) +#define FIOPAD_AC51 (FPinIndex)FIOPAD_INDEX(FIOPAD_75_FUNC_OFFSET) +#define FIOPAD_AC49 (FPinIndex)FIOPAD_INDEX(FIOPAD_76_FUNC_OFFSET) +#define FIOPAD_AE47 (FPinIndex)FIOPAD_INDEX(FIOPAD_77_FUNC_OFFSET) +#define FIOPAD_W47 (FPinIndex)FIOPAD_INDEX(FIOPAD_78_FUNC_OFFSET) +#define FIOPAD_W51 (FPinIndex)FIOPAD_INDEX(FIOPAD_79_FUNC_OFFSET) +#define FIOPAD_W49 (FPinIndex)FIOPAD_INDEX(FIOPAD_80_FUNC_OFFSET) +#define FIOPAD_U51 (FPinIndex)FIOPAD_INDEX(FIOPAD_81_FUNC_OFFSET) +#define FIOPAD_U49 (FPinIndex)FIOPAD_INDEX(FIOPAD_82_FUNC_OFFSET) +#define FIOPAD_AE45 (FPinIndex)FIOPAD_INDEX(FIOPAD_83_FUNC_OFFSET) +#define FIOPAD_AC45 (FPinIndex)FIOPAD_INDEX(FIOPAD_84_FUNC_OFFSET) +#define FIOPAD_AE43 (FPinIndex)FIOPAD_INDEX(FIOPAD_85_FUNC_OFFSET) +#define FIOPAD_AA43 (FPinIndex)FIOPAD_INDEX(FIOPAD_86_FUNC_OFFSET) +#define FIOPAD_AA45 (FPinIndex)FIOPAD_INDEX(FIOPAD_87_FUNC_OFFSET) +#define FIOPAD_W45 (FPinIndex)FIOPAD_INDEX(FIOPAD_88_FUNC_OFFSET) +#define FIOPAD_AA47 (FPinIndex)FIOPAD_INDEX(FIOPAD_89_FUNC_OFFSET) +#define FIOPAD_U45 (FPinIndex)FIOPAD_INDEX(FIOPAD_90_FUNC_OFFSET) +#define FIOPAD_G55 (FPinIndex)FIOPAD_INDEX(FIOPAD_91_FUNC_OFFSET) +#define FIOPAD_J55 (FPinIndex)FIOPAD_INDEX(FIOPAD_92_FUNC_OFFSET) +#define FIOPAD_L53 (FPinIndex)FIOPAD_INDEX(FIOPAD_93_FUNC_OFFSET) +#define FIOPAD_C55 (FPinIndex)FIOPAD_INDEX(FIOPAD_94_FUNC_OFFSET) +#define FIOPAD_E55 (FPinIndex)FIOPAD_INDEX(FIOPAD_95_FUNC_OFFSET) +#define FIOPAD_J53 (FPinIndex)FIOPAD_INDEX(FIOPAD_96_FUNC_OFFSET) +#define FIOPAD_L55 (FPinIndex)FIOPAD_INDEX(FIOPAD_97_FUNC_OFFSET) +#define FIOPAD_N55 (FPinIndex)FIOPAD_INDEX(FIOPAD_98_FUNC_OFFSET) +#define FIOPAD_C53 (FPinIndex)FIOPAD_INDEX(FIOPAD_29_FUNC_OFFSET) +#define FIOPAD_E53 (FPinIndex)FIOPAD_INDEX(FIOPAD_30_FUNC_OFFSET) +#define FIOPAD_E27 (FPinIndex)FIOPAD_INDEX(FIOPAD_99_FUNC_OFFSET) +#define FIOPAD_G27 (FPinIndex)FIOPAD_INDEX(FIOPAD_100_FUNC_OFFSET) +#define FIOPAD_N37 (FPinIndex)FIOPAD_INDEX(FIOPAD_101_FUNC_OFFSET) +#define FIOPAD_N35 (FPinIndex)FIOPAD_INDEX(FIOPAD_102_FUNC_OFFSET) +#define FIOPAD_J29 (FPinIndex)FIOPAD_INDEX(FIOPAD_103_FUNC_OFFSET) +#define FIOPAD_N29 (FPinIndex)FIOPAD_INDEX(FIOPAD_104_FUNC_OFFSET) +#define FIOPAD_L29 (FPinIndex)FIOPAD_INDEX(FIOPAD_105_FUNC_OFFSET) +#define FIOPAD_N41 (FPinIndex)FIOPAD_INDEX(FIOPAD_106_FUNC_OFFSET) +#define FIOPAD_N39 (FPinIndex)FIOPAD_INDEX(FIOPAD_107_FUNC_OFFSET) +#define FIOPAD_L27 (FPinIndex)FIOPAD_INDEX(FIOPAD_108_FUNC_OFFSET) +#define FIOPAD_J27 (FPinIndex)FIOPAD_INDEX(FIOPAD_109_FUNC_OFFSET) +#define FIOPAD_J25 (FPinIndex)FIOPAD_INDEX(FIOPAD_110_FUNC_OFFSET) +#define FIOPAD_E25 (FPinIndex)FIOPAD_INDEX(FIOPAD_111_FUNC_OFFSET) +#define FIOPAD_G25 (FPinIndex)FIOPAD_INDEX(FIOPAD_112_FUNC_OFFSET) +#define FIOPAD_N23 (FPinIndex)FIOPAD_INDEX(FIOPAD_113_FUNC_OFFSET) +#define FIOPAD_L25 (FPinIndex)FIOPAD_INDEX(FIOPAD_114_FUNC_OFFSET) +#define FIOPAD_J33 (FPinIndex)FIOPAD_INDEX(FIOPAD_115_FUNC_OFFSET) +#define FIOPAD_J35 (FPinIndex)FIOPAD_INDEX(FIOPAD_116_FUNC_OFFSET) +#define FIOPAD_G37 (FPinIndex)FIOPAD_INDEX(FIOPAD_117_FUNC_OFFSET) +#define FIOPAD_E39 (FPinIndex)FIOPAD_INDEX(FIOPAD_118_FUNC_OFFSET) +#define FIOPAD_L39 (FPinIndex)FIOPAD_INDEX(FIOPAD_119_FUNC_OFFSET) +#define FIOPAD_C39 (FPinIndex)FIOPAD_INDEX(FIOPAD_120_FUNC_OFFSET) +#define FIOPAD_E37 (FPinIndex)FIOPAD_INDEX(FIOPAD_121_FUNC_OFFSET) +#define FIOPAD_L41 (FPinIndex)FIOPAD_INDEX(FIOPAD_122_FUNC_OFFSET) +#define FIOPAD_J39 (FPinIndex)FIOPAD_INDEX(FIOPAD_123_FUNC_OFFSET) +#define FIOPAD_J37 (FPinIndex)FIOPAD_INDEX(FIOPAD_124_FUNC_OFFSET) +#define FIOPAD_L35 (FPinIndex)FIOPAD_INDEX(FIOPAD_125_FUNC_OFFSET) +#define FIOPAD_E33 (FPinIndex)FIOPAD_INDEX(FIOPAD_126_FUNC_OFFSET) +#define FIOPAD_E31 (FPinIndex)FIOPAD_INDEX(FIOPAD_127_FUNC_OFFSET) +#define FIOPAD_G31 (FPinIndex)FIOPAD_INDEX(FIOPAD_128_FUNC_OFFSET) +#define FIOPAD_J31 (FPinIndex)FIOPAD_INDEX(FIOPAD_129_FUNC_OFFSET) +#define FIOPAD_L33 (FPinIndex)FIOPAD_INDEX(FIOPAD_130_FUNC_OFFSET) +#define FIOPAD_N31 (FPinIndex)FIOPAD_INDEX(FIOPAD_131_FUNC_OFFSET) +#define FIOPAD_R47 (FPinIndex)FIOPAD_INDEX(FIOPAD_132_FUNC_OFFSET) +#define FIOPAD_R45 (FPinIndex)FIOPAD_INDEX(FIOPAD_133_FUNC_OFFSET) +#define FIOPAD_N47 (FPinIndex)FIOPAD_INDEX(FIOPAD_134_FUNC_OFFSET) +#define FIOPAD_N51 (FPinIndex)FIOPAD_INDEX(FIOPAD_135_FUNC_OFFSET) +#define FIOPAD_L51 (FPinIndex)FIOPAD_INDEX(FIOPAD_136_FUNC_OFFSET) +#define FIOPAD_J51 (FPinIndex)FIOPAD_INDEX(FIOPAD_137_FUNC_OFFSET) +#define FIOPAD_J41 (FPinIndex)FIOPAD_INDEX(FIOPAD_138_FUNC_OFFSET) +#define FIOPAD_E43 (FPinIndex)FIOPAD_INDEX(FIOPAD_139_FUNC_OFFSET) +#define FIOPAD_G43 (FPinIndex)FIOPAD_INDEX(FIOPAD_140_FUNC_OFFSET) +#define FIOPAD_J43 (FPinIndex)FIOPAD_INDEX(FIOPAD_141_FUNC_OFFSET) +#define FIOPAD_J45 (FPinIndex)FIOPAD_INDEX(FIOPAD_142_FUNC_OFFSET) +#define FIOPAD_N45 (FPinIndex)FIOPAD_INDEX(FIOPAD_143_FUNC_OFFSET) +#define FIOPAD_L47 (FPinIndex)FIOPAD_INDEX(FIOPAD_144_FUNC_OFFSET) +#define FIOPAD_L45 (FPinIndex)FIOPAD_INDEX(FIOPAD_145_FUNC_OFFSET) +#define FIOPAD_N49 (FPinIndex)FIOPAD_INDEX(FIOPAD_146_FUNC_OFFSET) +#define FIOPAD_J49 (FPinIndex)FIOPAD_INDEX(FIOPAD_147_FUNC_OFFSET) + +/* register offset of iopad delay */ +#define FIOPAD_AJ51_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_10_DELAY_OFFSET) +#define FIOPAD_AL51_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_11_DELAY_OFFSET) +#define FIOPAD_AL49_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_12_DELAY_OFFSET) +#define FIOPAD_AN47_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_13_DELAY_OFFSET) +#define FIOPAD_AR47_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_14_DELAY_OFFSET) +#define FIOPAD_AJ53_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_23_DELAY_OFFSET) +#define FIOPAD_AG55_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_24_DELAY_OFFSET) +#define FIOPAD_AG53_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_25_DELAY_OFFSET) +#define FIOPAD_AE55_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_26_DELAY_OFFSET) +#define FIOPAD_BA51_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_32_DELAY_OFFSET) +#define FIOPAD_BA49_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_33_DELAY_OFFSET) +#define FIOPAD_AR55_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_34_DELAY_OFFSET) +#define FIOPAD_AU55_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_35_DELAY_OFFSET) +#define FIOPAD_A41_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_55_DELAY_OFFSET) +#define FIOPAD_C41_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_56_DELAY_OFFSET) +#define FIOPAD_A43_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_57_DELAY_OFFSET) +#define FIOPAD_A45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_58_DELAY_OFFSET) +#define FIOPAD_C45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_59_DELAY_OFFSET) +#define FIOPAD_A47_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_60_DELAY_OFFSET) +#define FIOPAD_A29_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_61_DELAY_OFFSET) +#define FIOPAD_C29_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_62_DELAY_OFFSET) +#define FIOPAD_C27_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_63_DELAY_OFFSET) +#define FIOPAD_A27_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_64_DELAY_OFFSET) +#define FIOPAD_AJ49_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_65_DELAY_OFFSET) +#define FIOPAD_AL45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_66_DELAY_OFFSET) +#define FIOPAD_AL43_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_67_DELAY_OFFSET) +#define FIOPAD_AN45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_68_DELAY_OFFSET) +#define FIOPAD_AG47_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_148_DELAY_OFFSET) +#define FIOPAD_AJ47_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_69_DELAY_OFFSET) +#define FIOPAD_AG45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_70_DELAY_OFFSET) +#define FIOPAD_AE51_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_71_DELAY_OFFSET) +#define FIOPAD_AE49_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_72_DELAY_OFFSET) +#define FIOPAD_AG51_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_73_DELAY_OFFSET) +#define FIOPAD_AJ45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_74_DELAY_OFFSET) +#define FIOPAD_AC51_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_75_DELAY_OFFSET) +#define FIOPAD_AC49_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_76_DELAY_OFFSET) +#define FIOPAD_AE47_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_77_DELAY_OFFSET) +#define FIOPAD_W47_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_78_DELAY_OFFSET) +#define FIOPAD_W49_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_80_DELAY_OFFSET) +#define FIOPAD_U51_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_81_DELAY_OFFSET) +#define FIOPAD_U49_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_82_DELAY_OFFSET) +#define FIOPAD_AE45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_83_DELAY_OFFSET) +#define FIOPAD_AC45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_84_DELAY_OFFSET) +#define FIOPAD_AE43_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_85_DELAY_OFFSET) +#define FIOPAD_AA43_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_86_DELAY_OFFSET) +#define FIOPAD_AA45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_87_DELAY_OFFSET) +#define FIOPAD_W45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_88_DELAY_OFFSET) +#define FIOPAD_AA47_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_89_DELAY_OFFSET) +#define FIOPAD_U45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_90_DELAY_OFFSET) +#define FIOPAD_J55_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_92_DELAY_OFFSET) +#define FIOPAD_L53_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_93_DELAY_OFFSET) +#define FIOPAD_C55_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_94_DELAY_OFFSET) +#define FIOPAD_E55_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_95_DELAY_OFFSET) +#define FIOPAD_J53_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_96_DELAY_OFFSET) +#define FIOPAD_L55_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_97_DELAY_OFFSET) +#define FIOPAD_N55_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_98_DELAY_OFFSET) +#define FIOPAD_E27_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_99_DELAY_OFFSET) +#define FIOPAD_G27_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_100_DELAY_OFFSET) +#define FIOPAD_N37_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_101_DELAY_OFFSET) +#define FIOPAD_N35_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_102_DELAY_OFFSET) +#define FIOPAD_J29_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_103_DELAY_OFFSET) +#define FIOPAD_N29_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_104_DELAY_OFFSET) +#define FIOPAD_L29_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_105_DELAY_OFFSET) +#define FIOPAD_N41_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_106_DELAY_OFFSET) +#define FIOPAD_N39_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_107_DELAY_OFFSET) +#define FIOPAD_L27_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_108_DELAY_OFFSET) +#define FIOPAD_J27_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_109_DELAY_OFFSET) +#define FIOPAD_J25_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_110_DELAY_OFFSET) +#define FIOPAD_E25_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_111_DELAY_OFFSET) +#define FIOPAD_G25_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_112_DELAY_OFFSET) +#define FIOPAD_J33_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_115_DELAY_OFFSET) +#define FIOPAD_J35_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_116_DELAY_OFFSET) +#define FIOPAD_G37_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_117_DELAY_OFFSET) +#define FIOPAD_E39_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_118_DELAY_OFFSET) +#define FIOPAD_L39_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_119_DELAY_OFFSET) +#define FIOPAD_C39_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_120_DELAY_OFFSET) +#define FIOPAD_E37_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_121_DELAY_OFFSET) +#define FIOPAD_L41_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_122_DELAY_OFFSET) +#define FIOPAD_J39_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_123_DELAY_OFFSET) +#define FIOPAD_J37_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_124_DELAY_OFFSET) +#define FIOPAD_L35_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_125_DELAY_OFFSET) +#define FIOPAD_E33_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_126_DELAY_OFFSET) +#define FIOPAD_E31_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_127_DELAY_OFFSET) +#define FIOPAD_G31_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_128_DELAY_OFFSET) +#define FIOPAD_L51_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_136_DELAY_OFFSET) +#define FIOPAD_J51_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_137_DELAY_OFFSET) +#define FIOPAD_J41_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_138_DELAY_OFFSET) +#define FIOPAD_E43_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_139_DELAY_OFFSET) +#define FIOPAD_G43_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_140_DELAY_OFFSET) +#define FIOPAD_J43_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_141_DELAY_OFFSET) +#define FIOPAD_J45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_142_DELAY_OFFSET) +#define FIOPAD_N45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_143_DELAY_OFFSET) +#define FIOPAD_L47_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_144_DELAY_OFFSET) +#define FIOPAD_L45_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_145_DELAY_OFFSET) +#define FIOPAD_N49_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_146_DELAY_OFFSET) +#define FIOPAD_J49_DELAY (FPinIndex)FIOPAD_INDEX(FIOPAD_147_DELAY_OFFSET) + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/*****************************************************************************/ + + + +#ifdef __cplusplus +} + +#endif + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/board/e2000/s/fiopad_config.c b/bsp/phytium/libraries/standalone/board/e2000/s/fiopad_config.c new file mode 100644 index 0000000000..8ab06f4837 --- /dev/null +++ b/bsp/phytium/libraries/standalone/board/e2000/s/fiopad_config.c @@ -0,0 +1,291 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fiopad_config.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:25:29 + * Description:  This files is for io-pad function definition + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 huanghe 2021/11/5 init commit + * 1.1 zhugengyu 2022/3/21 adopt to lastest tech spec. + */ + +/***************************** Include Files *********************************/ +#include "fiopad.h" +#include "fparameters.h" +#include "fpinctrl.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/*****************************************************************************/ +/** + * @name: FIOPadSetSpimMux + * @msg: set iopad mux for spim + * @return {*} + * @param {u32} spim_id, instance id of spi + */ +void FIOPadSetSpimMux(u32 spim_id) +{ + if (FSPI2_ID == spim_id) + { + FPinSetFunc(FIOPAD_A29, FPIN_FUNC0); /* sclk */ + FPinSetFunc(FIOPAD_C29, FPIN_FUNC0); /* txd */ + FPinSetFunc(FIOPAD_C27, FPIN_FUNC0); /* rxd */ + FPinSetFunc(FIOPAD_A27, FPIN_FUNC0); /* csn0 */ + } +} + +/** + * @name: FIOPadSetGpioMux + * @msg: set iopad mux for gpio + * @return {*} + * @param {u32} gpio_id, instance id of gpio + * @param {u32} pin_id, index of pin + */ +void FIOPadSetGpioMux(u32 gpio_id, u32 pin_id) +{ + if (FGPIO_ID_3 == gpio_id) + { + switch (pin_id) + { + case 3: /* gpio 3-a-3 */ + FPinSetFunc(FIOPAD_A29, FPIN_FUNC6); + break; + case 4: /* gpio 3-a-4 */ + FPinSetFunc(FIOPAD_C29, FPIN_FUNC6); + break; + case 5: /* gpio 3-a-5 */ + FPinSetFunc(FIOPAD_C27, FPIN_FUNC6); + break; + case 6: /* gpio 3-a-6 */ + FPinSetFunc(FIOPAD_A27, FPIN_FUNC6); + break; + default: + break; + } + } +} + +/** + * @name: FIOPadSetMioMux + * @msg: set iopad mux for mio + * @return {*} + * @param {u32} mio_id, instance id of i2c + */ +void FIOPadSetMioMux(u32 mio_id) +{ + switch (mio_id) + { + case MIO_INSTANCE_0: + { + FPinSetFunc(FIOPAD_A37, FPIN_FUNC5); /* scl */ + FPinSetFunc(FIOPAD_A39, FPIN_FUNC5); /* sda */ + } + break; + case MIO_INSTANCE_1: + { + FPinSetFunc(FIOPAD_A41, FPIN_FUNC5); /* scl */ + FPinSetFunc(FIOPAD_C41, FPIN_FUNC5); /* sda */ + } + break; + case MIO_INSTANCE_2: + { + FPinSetFunc(FIOPAD_A43, FPIN_FUNC5); /* scl */ + FPinSetFunc(FIOPAD_A45, FPIN_FUNC5); /* sda */ + } + break; + case MIO_INSTANCE_3: + { + FPinSetFunc(FIOPAD_BA51, FPIN_FUNC4); /* scl */ + FPinSetFunc(FIOPAD_BA49, FPIN_FUNC4); /* sda */ + } + break; + case MIO_INSTANCE_4: + { + FPinSetFunc(FIOPAD_R55, FPIN_FUNC4); /* scl */ + FPinSetFunc(FIOPAD_U55, FPIN_FUNC4); /* sda */ + } + break; + case MIO_INSTANCE_5: + { + FPinSetFunc(FIOPAD_W45, FPIN_FUNC4); /* scl */ + FPinSetFunc(FIOPAD_U53, FPIN_FUNC4); /* sda */ + } + break; + case MIO_INSTANCE_6: + { + FPinSetFunc(FIOPAD_AA53, FPIN_FUNC4); /* scl */ + FPinSetFunc(FIOPAD_AA55, FPIN_FUNC4); /* sda */ + } + break; + case MIO_INSTANCE_7: + { + FPinSetFunc(FIOPAD_A35, FPIN_FUNC4); /* scl */ + FPinSetFunc(FIOPAD_C35, FPIN_FUNC4); /* sda */ + } + break; + case MIO_INSTANCE_8: + { + FPinSetFunc(FIOPAD_AA45, FPIN_FUNC4); /* scl */ + FPinSetFunc(FIOPAD_W45, FPIN_FUNC4); /* sda */ + } + break; + case MIO_INSTANCE_9: + { + FPinSetFunc(FIOPAD_AA47, FPIN_FUNC4); /* scl */ + FPinSetFunc(FIOPAD_U45, FPIN_FUNC4); /* sda */ + } + break; + case MIO_INSTANCE_10: + { + FPinSetFunc(FIOPAD_C45, FPIN_FUNC5); /* scl */ + FPinSetFunc(FIOPAD_A47, FPIN_FUNC5); /* sda */ + } + break; + case MIO_INSTANCE_11: + { + FPinSetFunc(FIOPAD_N23, FPIN_FUNC3); /* scl */ + FPinSetFunc(FIOPAD_L25, FPIN_FUNC3); /* sda */ + } + break; + case MIO_INSTANCE_12: + { + FPinSetFunc(FIOPAD_E37, FPIN_FUNC3); /* scl */ + FPinSetFunc(FIOPAD_L41, FPIN_FUNC3); /* sda */ + } + break; + case MIO_INSTANCE_13: + { + FPinSetFunc(FIOPAD_J45, FPIN_FUNC6); /* scl */ + FPinSetFunc(FIOPAD_N45, FPIN_FUNC6); /* sda */ + } + break; + case MIO_INSTANCE_14: + { + FPinSetFunc(FIOPAD_L47, FPIN_FUNC6); /* scl */ + FPinSetFunc(FIOPAD_L45, FPIN_FUNC6); /* sda */ + } + break; + case MIO_INSTANCE_15: + { + FPinSetFunc(FIOPAD_N49, FPIN_FUNC6); /* scl */ + FPinSetFunc(FIOPAD_J49, FPIN_FUNC6); /* sda */ + } + break; + default: + break; + } +} + +/** + * @name: FIOPadSetTachoMux + * @msg: set iopad mux for pwm_in + * @return {*} + * @param {u32} pwm_in_id, instance id of tacho + */ +void FIOPadSetTachoMux(u32 pwm_in_id) +{ + switch (pwm_in_id) + { + case TACHO_INSTANCE_0: + FPinSetFunc(FIOPAD_AN53, FPIN_FUNC1); + break; + case TACHO_INSTANCE_1: + FPinSetFunc(FIOPAD_AJ55, FPIN_FUNC1); + break; + case TACHO_INSTANCE_2: + FPinSetFunc(FIOPAD_AG55, FPIN_FUNC1); + break; + case TACHO_INSTANCE_3: + FPinSetFunc(FIOPAD_AE55, FPIN_FUNC1); + break; + case TACHO_INSTANCE_4: + FPinSetFunc(FIOPAD_AC53, FPIN_FUNC1); + break; + case TACHO_INSTANCE_5: + FPinSetFunc(FIOPAD_BA49, FPIN_FUNC1); + break; + case TACHO_INSTANCE_6: + FPinSetFunc(FIOPAD_C33, FPIN_FUNC2); + break; + case TACHO_INSTANCE_7: + FPinSetFunc(FIOPAD_A37, FPIN_FUNC2); + break; + case TACHO_INSTANCE_8: + FPinSetFunc(FIOPAD_A41, FPIN_FUNC2); + break; + case TACHO_INSTANCE_9: + FPinSetFunc(FIOPAD_A43, FPIN_FUNC2); + break; + case TACHO_INSTANCE_10: + FPinSetFunc(FIOPAD_C45, FPIN_FUNC2); + break; + case TACHO_INSTANCE_11: + FPinSetFunc(FIOPAD_A29, FPIN_FUNC2); + break; + case TACHO_INSTANCE_12: + FPinSetFunc(FIOPAD_C27, FPIN_FUNC2); + break; + case TACHO_INSTANCE_13: + FPinSetFunc(FIOPAD_AA45, FPIN_FUNC2); + break; + case TACHO_INSTANCE_14: + FPinSetFunc(FIOPAD_AA47, FPIN_FUNC2); + break; + case TACHO_INSTANCE_15: + FPinSetFunc(FIOPAD_G55, FPIN_FUNC2); + break; + default: + break; + } +} + +/** + * @name: FIOPadSetUartMux + * @msg: set iopad mux for uart + * @return {*} + * @param {u32} uart_id, instance id of uart + */ +void FIOPadSetUartMux(u32 uart_id) +{ + switch (uart_id) + { + case FUART0_ID: + FPinSetFunc(FIOPAD_J33, FPIN_FUNC4); + FPinSetFunc(FIOPAD_J35, FPIN_FUNC4); + break; + case FUART1_ID: + FPinSetFunc(FIOPAD_AW47, FPIN_FUNC0); + FPinSetFunc(FIOPAD_AU47, FPIN_FUNC0); + break; + case FUART2_ID: + FPinSetFunc(FIOPAD_A43, FPIN_FUNC0); + FPinSetFunc(FIOPAD_A45, FPIN_FUNC0); + break; + case FUART3_ID: + FPinSetFunc(FIOPAD_L33, FPIN_FUNC2); + FPinSetFunc(FIOPAD_N31, FPIN_FUNC2); + break; + default: + break; + } +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/board/e2000/s/fparameters.h b/bsp/phytium/libraries/standalone/board/e2000/s/fparameters.h new file mode 100644 index 0000000000..24b67ac122 --- /dev/null +++ b/bsp/phytium/libraries/standalone/board/e2000/s/fparameters.h @@ -0,0 +1,53 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fparameters.h + * Date: 2022-02-11 13:33:28 + * LastEditTime: 2022-02-17 18:00:50 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef BOARD_E2000S_PARAMTERERS_H +#define BOARD_E2000S_PARAMTERERS_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ +#include "fparameters_comm.h" + +/************************** Constant Definitions *****************************/ +#define CORE0_AFF 0x200U + +#define FT_CPUS_NR 1U + + +/* GIC offset */ + +#define FT_GIC_REDISTRUBUTIOR_OFFSET 2 + +/*****************************************************************************/ + + +#ifdef __cplusplus +} + +#endif + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/common/Kconfig b/bsp/phytium/libraries/standalone/common/Kconfig new file mode 100644 index 0000000000..3ffaf15c65 --- /dev/null +++ b/bsp/phytium/libraries/standalone/common/Kconfig @@ -0,0 +1,64 @@ + + + +choice DEBUG_LOG_LEVEL + prompt "Debug Log Level" + default LOG_ERROR + help + VERBOS: Print bigger chunks of debugging information + DEBUG: Print extra information for debugging + INFO: Print necessary information messages + WARN: Print error conditions from which recovery measures have been taken + ERROR: Print critical errors, software module can not recover on its own + + config LOG_VERBOS + bool "VERBOS" + config LOG_DEBUG + bool "DEBUG" + config LOG_INFO + bool "INFO" + config LOG_WARN + bool "WARN" + config LOG_ERROR + bool "ERROR" + config LOG_NONE + bool "NONE" + +endchoice # DEBUG_LOG_LEVEL + + +config USE_DEFAULT_INTERRUPT_CONFIG + bool + prompt "Use default interrupt configuration" + default y + help + "If this option is not selected, core0 is used as the main core by default and all interrupt driver modules are initialized. Non-0 core initializes only the necessary interrupt driver modules. If this option is selected, the developer needs to initiate each module independently " + if USE_DEFAULT_INTERRUPT_CONFIG + choice INTERRUPT_ROLE_SELECT + prompt "Interrupt role select" + default INTERRUPT_ROLE_MASTER + help + "Select Interrupt role" + + config INTERRUPT_ROLE_MASTER + bool "use master role" + + config INTERRUPT_ROLE_SLAVE + bool "use slave role" + + endchoice # INTERRUPT_ROLE_SELECT + endif + +config LOG_EXTRA_INFO + bool "Debug Log with Extra Info" + default n + help + Print debug information with source file name and source code line num. + +config BOOTUP_DEBUG_PRINTS + bool + prompt "Bootup debug" + default n + help + Enable Bootup debug printing + diff --git a/bsp/phytium/libraries/standalone/common/fassert.c b/bsp/phytium/libraries/standalone/common/fassert.c new file mode 100644 index 0000000000..ec1ade974b --- /dev/null +++ b/bsp/phytium/libraries/standalone/common/fassert.c @@ -0,0 +1,118 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: ft_assert.c + * Date: 2021-04-07 09:53:07 + * LastEditTime: 2022-02-17 18:04:28 + * Description:  This files is for assertion implmentation + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 huanghe 2021.4 init commit + * 1.1 zhugengyu 2022.3 re-define assert macro + */ + +/***************************** Include Files *********************************/ +#include "ftypes.h" +#include "fassert.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ +typedef struct +{ + u32 status; /* 当前断言状态 */ + FAssertCB cb; /* 断言回调函数 */ +} FAssertInfo; /* 断言实例类型 */ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ +static void FAssertCallback(const char *file, s32 line, int ret); + +/************************** Variable Definitions *****************************/ +static FAssertInfo assert_info = +{ + .status = FASSERT_NONE, + .cb = FAssertCallback +}; /* 断言实例 */ + +/*****************************************************************************/ +/** + * @name: FAssertSetStatus + * @msg: 设置断言状态 + * @return {*} + * @param {FAssertStatus} status, 断言状态 + */ +void FAssertSetStatus(FAssertStatus status) +{ + assert_info.status = status; +} + +/** + * @name: FAssertGetStatus + * @msg: 获取当前断言状态 + * @return {FAssertStatus} 当前断言状态 + */ +FAssertStatus FAssertGetStatus(void) +{ + return assert_info.status; +} + +/** + * @name: FAssertCallback + * @msg: 默认的断言回调函数 + * @return {*} + * @param {char} *file, 断言发生的源文件 + * @param {s32} line, 断言发生的源文件行号 + * @param {int} ret, 保留给Non-block断言使用 + */ +static void FAssertCallback(const char *file, s32 line, int ret) +{ + f_printk("Assert Error at %s : %ld \r\n", file, line); +} + +/** + * @name: FAssertSetCB + * @msg: 设置断言回调函数 + * @return {*} + * @param {FAssertCB} cb, 断言回调函数 + */ +void FAssertSetCB(FAssertCB cb) +{ + if (NULL != cb) + assert_info.cb = cb; +} + +/** + * @name: FAssert + * @msg: 断言实现 + * @return {*} + * @param {char} *file, 断言发生的源文件 + * @param {s32} line, 断言发生的源文件行号 + * @param {int} code, 断言发生的退出码,保留给Non-block断言使用 + */ +void FAssert(const char *file, s32 line, int code) +{ + if (NULL != assert_info.cb) + { + /* 如果要实现Non-block断言,需要在回调中返回 */ + assert_info.cb(file, line, code); + } + + while (TRUE) + { + ; + } +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/common/fassert.h b/bsp/phytium/libraries/standalone/common/fassert.h new file mode 100644 index 0000000000..9bb44362ff --- /dev/null +++ b/bsp/phytium/libraries/standalone/common/fassert.h @@ -0,0 +1,102 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fassert.h + * Date: 2021-04-07 09:53:07 + * LastEditTime: 2022-02-17 18:04:35 + * Description:  This files is for assertion defintion + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 huanghe 2021.4 init commit + * 1.1 zhugengyu 2022.3 re-define assert macro + */ + +#ifndef FT_ASSERT_H +#define FT_ASSERT_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ +#include "fprintk.h" +#include "ferror_code.h" +#include "ftypes.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ +typedef enum +{ + FASSERT_NONE = 0, + FASSERT_OCCURRED +} FAssertStatus; /* 断言状态 */ + +/* 断言处理回调函数 */ +typedef void (*FAssertCB)(const char *file, s32 line, int ret); + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FASSERT_MSG(expression, fmt, ...) \ + { \ + if (expression) \ + { \ + FAssertSetStatus(FASSERT_NONE); \ + } \ + else \ + { \ + FAssertSetStatus(FASSERT_OCCURRED); \ + f_printk(fmt, ##__VA_ARGS__); \ + FAssert(__FILE__, __LINE__, 0xff); \ + } \ + } + +#define FASSERT(expression)\ + { \ + if (expression) \ + { \ + FAssertSetStatus(FASSERT_NONE); \ + } \ + else \ + { \ + FAssertSetStatus(FASSERT_OCCURRED); \ + FAssert(__FILE__, __LINE__, 0xff); \ + } \ + } + +/* 检查静态断言状态 */ +#define FASSERT_STATIC(expression) \ + extern int assert_static[(expression) ? 1 : -1] + +/************************** Function Prototypes ******************************/ +/* 设置断言状态 */ +void FAssertSetStatus(FAssertStatus status); + +/* 获取当前断言状态 */ +FAssertStatus FAssertGetStatus(void); + +/* 设置断言回调函数 */ +void FAssertSetCB(FAssertCB cb); + +/* 断言实现 */ +void FAssert(const char *file, s32 line, int code); + +#ifdef __cplusplus +} +#endif + +#endif // ! diff --git a/bsp/phytium/libraries/standalone/common/fdebug.c b/bsp/phytium/libraries/standalone/common/fdebug.c new file mode 100644 index 0000000000..2a6d906e59 --- /dev/null +++ b/bsp/phytium/libraries/standalone/common/fdebug.c @@ -0,0 +1,106 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: ft_debug.c + * Date: 2021-04-25 16:44:23 + * LastEditTime: 2022-02-17 18:04:50 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "fdebug.h" +#include "fprintf.h" +#include "stdio.h" + +#define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ') +void FtDumpHexByte(const u8 *ptr, u32 buflen) +{ + u8 *buf = (u8 *)ptr; + fsize_t i, j; + + for (i = 0; i < buflen; i += 16) + { + printf("%p: ", ptr + i); + + for (j = 0; j < 16; j++) + if (i + j < buflen) + printf("%02X ", buf[i + j]); + else + printf(" "); + printf(" "); + + for (j = 0; j < 16; j++) + if (i + j < buflen) + printf("%c", (char)(__is_print(buf[i + j]) ? buf[i + j] : '.')); + printf("\r\n"); + } +} + +void FtDumpHexByteDebug(const u8 *ptr, u32 buflen) +{ + u8 *buf = (u8 *)ptr; + fsize_t i, j; + + for (i = 0; i < buflen; i += 16) + { + f_printf("%x: ", ptr + i); + + for (j = 0; j < 16; j++) + if (i + j < buflen) + f_printf("%x ", buf[i + j]); + else + f_printf(" "); + f_printf(" "); + + for (j = 0; j < 16; j++) + if (i + j < buflen) + f_printf("%c", (char)(__is_print(buf[i + j]) ? buf[i + j] : '.')); + f_printf("\r\n"); + } +} + + +void FtDumpHexWord(const u32 *ptr, u32 buflen) +{ + u32 *buf = (u32 *)ptr; + u8 *char_data = (u8 *)ptr; + fsize_t i, j; + buflen = buflen / 4; + for (i = 0; i < buflen; i += 4) + { + printf("%p: ", ptr + i); + + for (j = 0; j < 4; j++) + { + if (i + j < buflen) + { + printf("%lx ", buf[i + j]); + } + else + { + printf(" "); + } + } + + printf(" "); + + for (j = 0; j < 16; j++) + if (i + j < buflen) + printf("%c", (char)(__is_print(char_data[i + j]) ? char_data[i + j] : '.')); + + printf("\r\n"); + } +} diff --git a/bsp/phytium/libraries/standalone/common/fdebug.h b/bsp/phytium/libraries/standalone/common/fdebug.h new file mode 100644 index 0000000000..84f283b75e --- /dev/null +++ b/bsp/phytium/libraries/standalone/common/fdebug.h @@ -0,0 +1,127 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fdebug.h + * Date: 2021-04-07 09:53:07 + * LastEditTime: 2022-02-17 18:04:58 + * Description:  This files is for debug functions + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef BSP_COMMON_FT_DEBUG_H +#define BSP_COMMON_FT_DEBUG_H +#include +#include "sdkconfig.h" +#include "ftypes.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 LOG_COLOR(LOG_COLOR_CYAN) +#define LOG_COLOR_V LOG_COLOR(LOG_COLOR_PURPLE) + +/* select debug log level */ +#ifdef CONFIG_LOG_VERBOS + #define LOG_LOCAL_LEVEL FT_LOG_VERBOSE +#endif + +#ifdef CONFIG_LOG_ERROR + #define LOG_LOCAL_LEVEL FT_LOG_ERROR +#endif + +#ifdef CONFIG_LOG_WARN + #define LOG_LOCAL_LEVEL FT_LOG_WARN +#endif + +#ifdef CONFIG_LOG_INFO + #define LOG_LOCAL_LEVEL FT_LOG_INFO +#endif + +#ifdef CONFIG_LOG_DEBUG + #define LOG_LOCAL_LEVEL FT_LOG_DEBUG +#endif + +#define LOG_FORMAT(letter, format) LOG_COLOR_##letter " %s: " format LOG_RESET_COLOR "\r\n" + +#define PORT_KPRINTF printf + +#ifndef CONFIG_LOG_EXTRA_INFO +#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) +#else +#include +#define __FILENAME__ (strrchr(__FILE__, '/') ? (strrchr(__FILE__, '/') + 1):__FILE__) +/* print debug information with source file name and source code line num. */ +#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" @%s:%d"), tag, ##__VA_ARGS__, __FILENAME__, __LINE__); \ + } while (0) +#endif + +#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__) + +/* do not compile log if define CONFIG_LOG_NONE */ +#ifndef CONFIG_LOG_NONE + #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__) +#else + #define FT_DEBUG_PRINT_I(TAG, format, ...) + #define FT_DEBUG_PRINT_E(TAG, format, ...) + #define FT_DEBUG_PRINT_D(TAG, format, ...) + #define FT_DEBUG_PRINT_W(TAG, format, ...) + #define FT_DEBUG_PRINT_V(TAG, format, ...) +#endif + +#define FT_RAW_PRINTF(format, ...) PORT_KPRINTF(format, ##__VA_ARGS__) + +void FtDumpHexWord(const u32 *ptr, u32 buflen); +void FtDumpHexByte(const u8 *ptr, u32 buflen); +#endif // ! diff --git a/bsp/phytium/libraries/standalone/common/ferror_code.h b/bsp/phytium/libraries/standalone/common/ferror_code.h new file mode 100644 index 0000000000..f00bcba001 --- /dev/null +++ b/bsp/phytium/libraries/standalone/common/ferror_code.h @@ -0,0 +1,106 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: ferror_code.h + * Date: 2021-04-07 09:53:30 + * LastEditTime: 2022-02-17 18:05:27 + * 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 "ftypes.h" + +typedef u32 FError; + +#define FT_SUCCESS 0 + +/* 系统错误码模块定义 */ +typedef enum +{ + ErrorModGeneral = 0, + ErrModBsp, + ErrModAssert, + ErrModPort, + StatusModBsp, + ErrModMaxMask = 255, + +} FtErrorCodeModuleMask; + +/* COMMON组件的错误码子模块定义 */ +typedef enum +{ + ErrCommGeneral = 0, + ErrCommMemp, + ErrInterrupt, +} FtErrCodeCommMask; + +/* BSP模块的错误子模块定义 */ +typedef enum +{ + ErrBspGeneral = 0, + ErrBspClk, + ErrBspRtc, + ErrBspTimer, + ErrBspUart, + ErrBspGpio, + ErrBspSpi, + ErrBspEth, + ErrBspCan, + ErrPcie, + ErrBspQSpi, + ErrBspMio, + ErrBspI2c, + ErrBspMmc, + ErrBspWdt, + ErrGic, + ErrGdma, + ErrNand, + ErrIoMux, + ErrBspSata, + ErrUsb, + ErrEthPhy, + ErrDdma, + ErrBspAdc, + ErrBspPwm, + ErrSema, + + ErrBspModMaxMask = 255 +} FtErrCodeBspMask; + +#define FT_ERRCODE_SYS_MODULE_OFFSET (u32)24 +#define FT_ERRCODE_SUB_MODULE_OFFSET (u32)16 + +#define FT_ERRCODE_SYS_MODULE_MASK ((u32)0xff << FT_ERRCODE_SYS_MODULE_OFFSET) /* bit 24 .. 31 */ +#define FT_ERRCODE_SUB_MODULE_MASK ((u32)0xff << FT_ERRCODE_SUB_MODULE_OFFSET) /* bit 16 .. 23 */ +#define FT_ERRCODE_TAIL_VALUE_MASK ((u32)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((u32)sys_mode, FT_ERRCODE_SYS_MODULE_OFFSET, FT_ERRCODE_SYS_MODULE_MASK)) | \ + (FT_ERRCODE_OFFSET((u32)sub_mode, FT_ERRCODE_SUB_MODULE_OFFSET, FT_ERRCODE_SUB_MODULE_MASK)) | \ + ((u32)tail & FT_ERRCODE_TAIL_VALUE_MASK)) +#define FT_CODE_ERR FT_MAKE_ERRCODE + +#define ERR_SUCCESS FT_MAKE_ERRCODE(ErrorModGeneral, ErrBspGeneral, 0) /* 成功 */ +#define ERR_GENERAL FT_MAKE_ERRCODE(ErrorModGeneral, ErrBspGeneral, 1) /* 一般错误 */ + +#endif diff --git a/bsp/phytium/libraries/standalone/common/fio.h b/bsp/phytium/libraries/standalone/common/fio.h new file mode 100644 index 0000000000..1f1d8331cf --- /dev/null +++ b/bsp/phytium/libraries/standalone/common/fio.h @@ -0,0 +1,113 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fio.h + * Date: 2021-04-07 09:53:07 + * LastEditTime: 2022-02-18 08:24:01 + * Description:  This files is for general reigster io functions + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef FT_IO_H +#define FT_IO_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ftypes.h" + +static _INLINE u8 FtIn8(uintptr addr) +{ + return *(volatile u8 *)addr; +} + +static _INLINE u16 FtIn16(uintptr addr) +{ + return *(volatile u16 *)addr; +} + +static _INLINE u32 FtIn32(uintptr addr) +{ + return *(volatile u32 *)addr; +} + +static _INLINE u64 FtIn64(uintptr addr) +{ + return *(volatile u64 *)addr; +} + +static _INLINE void FtOut8(uintptr addr, u8 value) +{ + volatile u8 *local_addr = (volatile u8 *)addr; + *local_addr = value; +} + +static _INLINE void FtOut16(uintptr addr, u16 value) +{ + volatile u16 *local_addr = (volatile u16 *)addr; + *local_addr = value; +} + +static _INLINE void FtOut32(uintptr addr, u32 value) +{ + volatile u32 *local_addr = (volatile u32 *)addr; + *local_addr = value; +} + +static _INLINE void FtOut64(uintptr addr, u64 value) +{ + volatile u64 *local_addr = (volatile u64 *)addr; + *local_addr = value; +} + +static _INLINE void FtSetBit32(uintptr addr, u32 value) +{ + volatile u32 last_value; + last_value = FtIn32(addr); + last_value |= value; + FtOut32(addr, last_value); +} + +static _INLINE void FtClearBit32(uintptr addr, u32 value) +{ + volatile u32 last_value; + last_value = FtIn32(addr); + last_value &= ~value; + FtOut32(addr, last_value); +} + +static _INLINE void FtToggleBit32(uintptr addr, u32 toggle_pos) +{ + volatile u32 value; + value = FtIn32(addr); + value ^= (1 << toggle_pos); + FtOut32(addr, value); +} + +static _INLINE u16 FtEndianSwap16(u16 data) +{ + return (u16)(((data & 0xFF00U) >> 8U) | ((data & 0x00FFU) << 8U)); +} +#define FT_WRITE32(_reg, _val) (*(volatile uint32_t *)&_reg = _val) +#define FT_READ32(_reg) (*(volatile uint32_t *)&_reg) + +#ifdef __cplusplus +} +#endif + +#endif // ! diff --git a/bsp/phytium/libraries/standalone/common/fpinctrl.h b/bsp/phytium/libraries/standalone/common/fpinctrl.h new file mode 100644 index 0000000000..891e8f74de --- /dev/null +++ b/bsp/phytium/libraries/standalone/common/fpinctrl.h @@ -0,0 +1,211 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fpinctrl.h + * Date: 2022-03-28 14:16:09 + * LastEditTime: 2022-03-28 14:16:10 + * Description:  This files is for IO pin ctrl API definition + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2022/3/28 init commit + */ +#ifndef COMMON_FPINCTRL_H +#define COMMON_FPINCTRL_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ +#include "ftypes.h" +#include "sdkconfig.h" + +#if defined(CONFIG_TARGET_F2000_4) || defined(CONFIG_TARGET_D2000) +#ifndef FPIN_IO_CTRL +#define FPIN_IO_CTRL +#endif +#endif + +#if defined(CONFIG_TARGET_E2000) +#ifndef FPIN_IO_PAD +#define FPIN_IO_PAD +#endif +#endif + +#if defined(FPIN_IO_CTRL) +#include "fioctrl.h" +#endif + +#if defined(FPIN_IO_PAD) +#include "fiopad.h" +#endif + +/**************************** Type Definitions *******************************/ + +typedef enum +{ + FPIN_FUNC0 = 0b000, + FPIN_FUNC1, + FPIN_FUNC2, + FPIN_FUNC3 = 0b011, +#if defined(FPIN_IO_PAD) /* E2000 support more pin func */ + FPIN_FUNC4, + FPIN_FUNC5, + FPIN_FUNC6, + FPIN_FUNC7 = 0b111, +#endif + FPIN_NUM_OF_FUNC +} FPinFunc; /* 引脚复用功能配置, func0为默认功能 */ + +#if defined(FPIN_IO_PAD) /* Only support driver strength config in E2000 */ +typedef enum +{ + FPIN_DRV0 = 0b0000, + FPIN_DRV1, + FPIN_DRV2, + FPIN_DRV3, + FPIN_DRV4, + FPIN_DRV5, + FPIN_DRV6, + FPIN_DRV7, + FPIN_DRV8, + FPIN_DRV9, + FPIN_DRV10, + FPIN_DRV11, + FPIN_DRV12, + FPIN_DRV13, + FPIN_DRV14, + FPIN_DRV15 = 0b1111, + + FPIN_NUM_OF_DRIVE +} FPinDrive; /* 引脚驱动能力配置 */ +#endif + +typedef enum +{ + FPIN_PULL_NONE = 0b00, + FPIN_PULL_DOWN = 0b01, + FPIN_PULL_UP = 0b10, + + FPIN_NUM_OF_PULL +} FPinPull; /* 引脚上下拉配置 */ + +typedef enum +{ + FPIN_OUTPUT_DELAY = 0, /* 延时设置方向为输出 */ + FPIN_INPUT_DELAY, /* 延时设置方向为输入 */ + + FPIN_NUM_OF_DELAY_DIR +} FPinDelayDir; /* 引脚延时配置方向 */ + +typedef enum +{ + FPIN_DELAY_COARSE_TUNING = 0, /* 延迟粗调档位 */ + FPIN_DELAY_FINE_TUNING, /* 延迟精调档位 */ + + FPIN_NUM_OF_DELAY_TYPE +} FPinDelayType; /* 引脚延时配置类型 */ + +typedef enum +{ + FPIN_DELAY_NONE = 0, + FPIN_DELAY_1, + FPIN_DELAY_2, + FPIN_DELAY_3, + FPIN_DELAY_4, + FPIN_DELAY_5, + FPIN_DELAY_6, + FPIN_DELAY_7, + + FPIN_NUM_OF_DELAY +} FPinDelay; + +typedef enum +{ + FPIN_DELAY_IN_TYPE = 0, /* input delay */ + FPIN_DELAY_OUT_TYPE = 1, /* output delay */ +} FPinDelayIOType; + +typedef struct +{ + u32 reg_off; /* 引脚配置寄存器偏移量 */ + u32 reg_bit; /* 引脚配置起始位 */ +} FPinIndex; /* 引脚索引 */ +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FPIN_NULL {0xffffffff, 0} + +/************************** Function Prototypes ******************************/ +/* 获取IO引脚当前的复用功能 */ +FPinFunc FPinGetFunc(const FPinIndex pin); + +/* 设置IO引脚复用功能 */ +void FPinSetFunc(const FPinIndex pin, FPinFunc func); + +/* 获取IO引脚当前的上下拉设置 */ +FPinPull FPinGetPull(const FPinIndex pin); + +/* 设置IO引脚的上下拉 */ +void FPinSetPull(const FPinIndex pin, FPinPull pull); + +#if defined(FPIN_IO_PAD) +/* 获取IO引脚的驱动能力 */ +FPinDrive FPinGetDrive(const FPinIndex pin); + +/* 设置IO引脚的驱动能力 */ +void FPinSetDrive(const FPinIndex pin, FPinDrive drive); + +/* 获取IO引脚的复用、上下拉和驱动能力设置 */ +void FPinGetConfig(const FPinIndex pin, FPinFunc *func, FPinPull *pull, FPinDrive *drive); + +/* 设置IO引脚的复用、上下拉和驱动能力 */ +void FPinSetConfig(const FPinIndex pin, FPinFunc func, FPinPull pull, FPinDrive drive); + +#else + +/* 获取IO引脚的复用、上下拉和驱动能力设置 */ +void FPinGetConfig(const FPinIndex pin, FPinFunc *func, FPinPull *pull); + +/* 设置IO引脚的复用、上下拉和驱动能力 */ +void FPinSetConfig(const FPinIndex pin, FPinFunc func, FPinPull pull); + +#endif + +/* 获取IO引脚当前的单项延时设置 */ +FPinDelay FPinGetDelay(const FPinIndex pin, FPinDelayDir dir, FPinDelayType type); + +/* 检查IO引脚延时是否使能 */ +boolean FPinGetDelayEn(const FPinIndex pin, FPinDelayDir dir); + +/* 设置IO引脚单项延时 */ +void FPinSetDelay(const FPinIndex pin, FPinDelayDir dir, FPinDelayType type, FPinDelay delay); + +/* 使能或去使能IO引脚延时 */ +void FPinSetDelayEn(const FPinIndex pin, FPinDelayDir dir, boolean enable); + +/* Update and enable common IO pin delay config */ +void FPinSetDelayConfig(const FPinIndex pin, FPinDelayIOType in_out_type, FPinDelay roungh_delay, FPinDelay delicate_delay, boolean enable); + +/* Get current common IO pin delay config */ +void FPinGetDelayConfig(const FPinIndex pin, FPinDelay *in_roungh_delay, FPinDelay *in_delicate_delay, + FPinDelay *out_roungh_delay, FPinDelay *out_delicate_delay); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/common/fprintf.c b/bsp/phytium/libraries/standalone/common/fprintf.c new file mode 100644 index 0000000000..a37c4d3e2a --- /dev/null +++ b/bsp/phytium/libraries/standalone/common/fprintf.c @@ -0,0 +1,265 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: f_printf.c + * Date: 2021-08-23 16:24:02 + * LastEditTime: 2022-02-17 18:01:19 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#include +#include "fearly_uart.h" + +#define putchar(c) OutByte(c) +#define PAD_RIGHT 1 +#define PAD_ZERO 2 + +static void printchar(char **str, int c) +{ + if (str) + { + **str = c; + ++(*str); + } + else + (void)putchar((const char)c); +} + +static int prints(char **out, const char *string, int width, int pad) +{ + register int pc = 0, padchar = ' '; + + if (width > 0) + { + register int len = 0; + register const char *ptr; + for (ptr = string; *ptr; ++ptr) + ++len; + if (len >= width) + width = 0; + else + width -= len; + if (pad & PAD_ZERO) + padchar = '0'; + } + if (!(pad & PAD_RIGHT)) + { + for (; width > 0; --width) + { + printchar(out, padchar); + ++pc; + } + } + for (; *string; ++string) + { + printchar(out, *string); + ++pc; + } + for (; width > 0; --width) + { + printchar(out, padchar); + ++pc; + } + + return pc; +} + +/* the following should be enough for 32 bit int */ +#define PRINT_BUF_LEN 12 + +static int printi(char **out, int i, int b, int sg, int width, int pad, int letbase) +{ + char print_buf[PRINT_BUF_LEN]; + register char *s; + register int t, neg = 0, pc = 0; + register unsigned int u = i; + + if (i == 0) + { + print_buf[0] = '0'; + print_buf[1] = '\0'; + return prints(out, print_buf, width, pad); + } + + if (sg && b == 10 && i < 0) + { + neg = 1; + u = -i; + } + + s = print_buf + PRINT_BUF_LEN - 1; + *s = '\0'; + + while (u) + { + t = u % b; + if (t >= 10) + t += letbase - '0' - 10; + *--s = t + '0'; + u /= b; + } + + if (neg) + { + if (width && (pad & PAD_ZERO)) + { + printchar(out, '-'); + ++pc; + --width; + } + else + { + *--s = '-'; + } + } + + return pc + prints(out, s, width, pad); +} + +static int print(char **out, const char *format, va_list args) +{ + register int width, pad; + register int pc = 0; + char scr[2]; + + for (; *format != 0; ++format) + { + if (*format == '%') + { + ++format; + width = pad = 0; + if (*format == '\0') + break; + if (*format == '%') + goto out; + if (*format == '-') + { + ++format; + pad = PAD_RIGHT; + } + while (*format == '0') + { + ++format; + pad |= PAD_ZERO; + } + for (; *format >= '0' && *format <= '9'; ++format) + { + width *= 10; + width += *format - '0'; + } + if (*format == 's') + { + //register char *s = (char *)va_arg( args, int ); + register char *s = (char *)va_arg(args, char *); + pc += prints(out, s ? s : "(null)", width, pad); + continue; + } + if (*format == 'd') + { + pc += printi(out, va_arg(args, int), 10, 1, width, pad, 'a'); + continue; + } + if (*format == 'x') + { + pc += printi(out, va_arg(args, int), 16, 0, width, pad, 'a'); + continue; + } + if (*format == 'X') + { + pc += printi(out, va_arg(args, int), 16, 0, width, pad, 'A'); + continue; + } + if (*format == 'u') + { + pc += printi(out, va_arg(args, int), 10, 0, width, pad, 'a'); + continue; + } + if (*format == 'c') + { + /* char are converted to int then pushed on the stack */ + scr[0] = (char)va_arg(args, int); + scr[1] = '\0'; + pc += prints(out, scr, width, pad); + continue; + } + } + else + { +out: + printchar(out, *format); + ++pc; + } + } + if (out) + **out = '\0'; + va_end(args); + return pc; +} + +int f_printf(const char *format, ...) +{ + va_list args; + + va_start(args, format); + return print(0, format, args); +} + +#ifdef TEST_PRINTF +int test_printf(void) +{ + char *ptr = "Hello world!"; + char *np = 0; + int i = 5; + unsigned int bs = sizeof(int) * 8; + int mi; + char buf[80]; + + mi = (1 << (bs - 1)) + 1; + f_printf("%s\n", ptr); + f_printf("printf test\n"); + f_printf("%s is null pointer\n", np); + f_printf("%d = 5\n", i); + f_printf("%d = - max int\n", mi); + f_printf("char %c = 'a'\n", 'a'); + f_printf("hex %x = ff\n", 0xff); + f_printf("hex %02x = 00\n", 0); + f_printf("signed %d = unsigned %u = hex %x\n", -3, -3, -3); + f_printf("%d %s(s)%", 0, "message"); + f_printf("\n"); + f_printf("%d %s(s) with %%\n", 0, "message"); + sprintf(buf, "justif: \"%-10s\"\n", "left"); + f_printf("%s", buf); + sprintf(buf, "justif: \"%10s\"\n", "right"); + f_printf("%s", buf); + sprintf(buf, " 3: %04d zero padded\n", 3); + f_printf("%s", buf); + sprintf(buf, " 3: %-4d left justif.\n", 3); + f_printf("%s", buf); + sprintf(buf, " 3: %4d right justif.\n", 3); + f_printf("%s", buf); + sprintf(buf, "-3: %04d zero padded\n", -3); + f_printf("%s", buf); + sprintf(buf, "-3: %-4d left justif.\n", -3); + f_printf("%s", buf); + sprintf(buf, "-3: %4d right justif.\n", -3); + f_printf("%s", buf); + + return 0; +} + +#endif diff --git a/bsp/phytium/libraries/standalone/common/fprintf.h b/bsp/phytium/libraries/standalone/common/fprintf.h new file mode 100644 index 0000000000..4877234b82 --- /dev/null +++ b/bsp/phytium/libraries/standalone/common/fprintf.h @@ -0,0 +1,38 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fprintf.h + * Date: 2021-08-23 16:24:02 + * LastEditTime: 2022-02-17 18:01:24 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef COMMON_F_PRINTF_H +#define COMMON_F_PRINTF_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +int f_printf(const char *format, ...) ; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsp/phytium/libraries/standalone/common/fprintk.c b/bsp/phytium/libraries/standalone/common/fprintk.c new file mode 100644 index 0000000000..d07f6b994a --- /dev/null +++ b/bsp/phytium/libraries/standalone/common/fprintk.c @@ -0,0 +1,418 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: f_printk.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-17 18:01:29 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#include +#include +#include +#include "fkernel.h" +#include "ftypes.h" +#include "fearly_uart.h" + +#ifndef __fallthrough + #if __GNUC__ >= 7 + #define __fallthrough __attribute__((fallthrough)) + #else + #define __fallthrough + #endif /* __GNUC__ >= 7 */ +#endif + +typedef int (*cbprintf_cb)(int c, void *ctx); +#define CONFIG_CBPRINTF_FULL_INTEGRAL + +#ifdef CONFIG_CBPRINTF_FULL_INTEGRAL + typedef intmax_t int_value_type; + typedef uintmax_t uint_value_type; + #define DIGITS_BUFLEN 21 +#else + typedef s32 int_value_type; + typedef u32 uint_value_type; + #define DIGITS_BUFLEN 10 +#endif + +#define ALPHA(fmt) (((fmt)&0x60) - '0' - 10 + 1) + +struct str_context +{ + char *str; + int max; + int count; +}; + +static int char_out(int c, void *ctx_p) +{ + struct str_context *ctx = ctx_p; + + ctx->count++; + OutByte((s8)c); + return 1; +} + +/* Convert value to string, storing characters downwards */ +static inline int convert_value(uint_value_type num, unsigned int base, + unsigned int alpha, char *buftop) +{ + int i = 0; + + do + { + unsigned int c = num % base; + if (c >= 10) + { + c += alpha; + } + buftop[--i] = c + '0'; + num /= base; + } + while (num); + + return -i; +} + +#define OUTC(_c) \ + do \ + { \ + out((int)(_c), ctx); \ + count++; \ + } while (0) + +#define PAD_ZERO BIT(0) +#define PAD_TAIL BIT(1) + +/** + * @brief Printk internals + * + * See printk() for description. + * @param fmt Format string + * @param ap Variable parameters + * + * @return printed byte count if CONFIG_CBPRINTF_LIBC_SUBSTS is set + */ +int cbvprintf(cbprintf_cb out, void *ctx, const char *fmt, va_list ap) +{ + size_t count = 0; + char buf[DIGITS_BUFLEN]; + char *prefix, *data; + int min_width, precision, data_len; + char padding_mode, length_mod, special; + + /* we pre-increment in the loop afterwards */ + fmt--; + +start: + while (*++fmt != '%') + { + if (*fmt == '\0') + { + return count; + } + OUTC(*fmt); + } + + min_width = -1; + precision = -1; + prefix = ""; + padding_mode = 0; + length_mod = 0; + special = 0; + + for (fmt++;; fmt++) + { + switch (*fmt) + { + case 0: + return count; + + case '%': + OUTC('%'); + goto start; + + case '-': + padding_mode = PAD_TAIL; + continue; + + case '.': + precision = 0; + padding_mode &= (char)~PAD_ZERO; + continue; + + case '0': + if (min_width < 0 && precision < 0 && !padding_mode) + { + padding_mode = PAD_ZERO; + continue; + } + __fallthrough; + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (precision >= 0) + { + precision = 10 * precision + *fmt - '0'; + } + else + { + if (min_width < 0) + { + min_width = 0; + } + min_width = 10 * min_width + *fmt - '0'; + } + continue; + + case '*': + if (precision >= 0) + { + precision = va_arg(ap, int); + } + else + { + min_width = va_arg(ap, int); + if (min_width < 0) + { + min_width = -min_width; + padding_mode = PAD_TAIL; + } + } + continue; + + case '+': + case ' ': + case '#': + special = *fmt; + continue; + + case 'h': + case 'l': + case 'z': + if (*fmt == 'h' && length_mod == 'h') + { + length_mod = 'H'; + } + else if (*fmt == 'l' && length_mod == 'l') + { + length_mod = 'L'; + } + else if (length_mod == '\0') + { + length_mod = *fmt; + } + else + { + OUTC('%'); + OUTC(*fmt); + goto start; + } + continue; + + case 'd': + case 'i': + case 'u': + { + uint_value_type d; + + if (length_mod == 'z') + { + d = va_arg(ap, ssize_t); + } + else if (length_mod == 'l') + { + d = va_arg(ap, long); + } + else if (length_mod == 'L') + { + long long lld = va_arg(ap, long long); + + if (sizeof(int_value_type) < 8U && + lld != (int_value_type)lld) + { + data = "ERR"; + data_len = 3; + precision = 0; + break; + } + d = (uint_value_type)lld; + } + else if (*fmt == 'u') + { + d = va_arg(ap, unsigned int); + } + else + { + d = va_arg(ap, int); + } + + if (*fmt != 'u' && (int_value_type)d < 0) + { + d = -d; + prefix = "-"; + min_width--; + } + else if (special == ' ') + { + prefix = " "; + min_width--; + } + else if (special == '+') + { + prefix = "+"; + min_width--; + } + else + { + ; + } + data_len = convert_value(d, 10, 0, buf + sizeof(buf)); + data = buf + sizeof(buf) - data_len; + break; + } + + case 'p': + case 'x': + case 'X': + { + uint_value_type x; + + if (*fmt == 'p') + { + x = (uintptr_t)va_arg(ap, void *); + if (x == (uint_value_type)0) + { + data = "(nil)"; + data_len = 5; + precision = 0; + break; + } + special = '#'; + } + else if (length_mod == 'l') + { + x = va_arg(ap, unsigned long); + } + else if (length_mod == 'L') + { + x = va_arg(ap, unsigned long long); + } + else + { + x = va_arg(ap, unsigned int); + } + if (special == '#') + { + prefix = (*fmt & 0x20) ? "0x" : "0x"; + min_width -= 2; + } + data_len = convert_value(x, 16, ALPHA(*fmt), + buf + sizeof(buf)); + data = buf + sizeof(buf) - data_len; + break; + } + + case 's': + { + data = va_arg(ap, char *); + data_len = strlen(data); + if (precision >= 0 && data_len > precision) + { + data_len = precision; + } + precision = 0; + break; + } + + case 'c': + { + int c = va_arg(ap, int); + + buf[0] = c; + data = buf; + data_len = 1; + precision = 0; + break; + } + + default: + OUTC('%'); + OUTC(*fmt); + goto start; + } + + if (precision < 0 && (padding_mode & PAD_ZERO)) + { + precision = min_width; + } + min_width -= data_len; + precision -= data_len; + if (precision > 0) + { + min_width -= precision; + } + + if (!(padding_mode & PAD_TAIL)) + { + while (--min_width >= 0) + { + OUTC(' '); + } + } + while (*prefix) + { + OUTC(*prefix++); + } + while (--precision >= 0) + { + OUTC('0'); + } + while (--data_len >= 0) + { + OUTC(*data++); + } + while (--min_width >= 0) + { + OUTC(' '); + } + + goto start; + } +} + +static int f_vprintf(const char *restrict format, va_list vargs) +{ + struct str_context ctx = {0}; + cbvprintf(char_out, &ctx, format, vargs); +} + +void f_printk(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + f_vprintf(fmt, ap); + va_end(ap); +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/common/fprintk.h b/bsp/phytium/libraries/standalone/common/fprintk.h new file mode 100644 index 0000000000..907b841ca7 --- /dev/null +++ b/bsp/phytium/libraries/standalone/common/fprintk.h @@ -0,0 +1,38 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fprintk.h + * Date: 2021-08-23 16:24:02 + * LastEditTime: 2022-02-17 18:01:35 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef COMMON_F_PRINTK_H +#define COMMON_F_PRINTK_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +int f_printk(const char *format, ...) ; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsp/phytium/libraries/standalone/common/fsleep.h b/bsp/phytium/libraries/standalone/common/fsleep.h new file mode 100644 index 0000000000..f9d29c201e --- /dev/null +++ b/bsp/phytium/libraries/standalone/common/fsleep.h @@ -0,0 +1,34 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsleep.h + * Date: 2021-05-28 08:48:40 + * LastEditTime: 2022-02-17 18:02:51 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#ifndef _BSP_ARCH_ARMV8_FSLEEP_H +#define _BSP_ARCH_ARMV8_FSLEEP_H + +#include "ftypes.h" + +u32 fsleep_seconds(u32 seconds); /* 按秒延迟 */ +u32 fsleep_millisec(u32 mseconds); /* 按毫秒延迟 */ +u32 fsleep_microsec(u32 useconds); /* 按微秒延迟 */ + +#endif // ! \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/common/ftypes.h b/bsp/phytium/libraries/standalone/common/ftypes.h new file mode 100644 index 0000000000..aa097b3ade --- /dev/null +++ b/bsp/phytium/libraries/standalone/common/ftypes.h @@ -0,0 +1,95 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: ftypes.h + * Date: 2021-05-27 13:30:03 + * LastEditTime: 2022-02-18 08:24:15 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#ifndef _BSP_COMMON_FT_TYPE_H +#define _BSP_COMMON_FT_TYPE_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include + +#define FT_COMPONENT_IS_READY 0x11111111U +#define FT_COMPONENT_IS_STARTED 0x22222222U + +typedef uint8_t u8; /* unsigned 8-bit */ +typedef char s8; /* signed 8-bit */ +typedef uint16_t u16; /* unsigned 16-bit */ +typedef short s16; /* signed 16-bit */ +typedef uint32_t u32; /* unsigned 32-bit */ +typedef int32_t s32; /* signed 32-bit */ +typedef uint64_t u64; /* unsigned 64-bit */ +typedef int64_t s64; /* unsigned */ +typedef float f32; /* 32-bit floating point */ +typedef double f64; /* 64-bit double precision FP */ +typedef unsigned long boolean; /* boolean */ +typedef uint64_t _time_t; +typedef size_t fsize_t; + +typedef intptr_t intptr; /* intptr_t是为了跨平台,其长度总是所在平台的位数,所以用来存放地址。 */ +typedef uintptr_t uintptr; +typedef ptrdiff_t ptrdiff; + +#ifdef __aarch64__ +typedef u64 tick_t; +#else +typedef u32 tick_t; +#endif + +/** @}*/ +#if !defined(LONG) || !defined(ULONG) +typedef long LONG; +typedef unsigned long ULONG; +#endif + +#define ULONG64_HI_MASK 0xFFFFFFFF00000000U +#define ULONG64_LO_MASK ~ULONG64_HI_MASK + +#ifndef TRUE +#define TRUE 1U +#endif + +#ifndef FALSE +#define FALSE 0U +#endif + +#ifndef NULL +#define NULL 0U +#endif + +#define _INLINE inline +#define _WEAK __attribute__((weak)) + +typedef void (*FIrqHandler)(void *InstancePtr); + +typedef void (*FExceptionHandler)(void *InstancePtr); + +#ifdef __cplusplus +} +#endif + +#endif // ! diff --git a/bsp/phytium/libraries/standalone/doc/ChangeLog.md b/bsp/phytium/libraries/standalone/doc/ChangeLog.md new file mode 100644 index 0000000000..376e459c68 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/ChangeLog.md @@ -0,0 +1,1431 @@ +# Phytium Standalone SDK 2022-10-26 ChangeLog + +Change Log sinc 2022-10-21 + +## aarch + +- rename system file with f prefix + +## board + +- rename system file with f prefix + +## common + +- rename system file with f prefix + +## board + +- adopt to new system header file + +## tools + +- add script to export rt-trhead BSP from SDK +- adjust Kconfig blank line to support scons + +# Phytium Standalone SDK 2022-10-21 ChangeLog + +Change Log sinc 2022-10-15 + +## drivers + +- Optimize sata +- Optimize can + +## third-party + +- Adapt fatfs to e2000 demo board for sata + +## Phytium Standalone SDK 2022-10-10 ChangeLog + +Change Log since 2022-9-26 + +## drivers + +- support fxhci in E2000 +- remove support of fxhci with PCIe in FT2000/4 + +## example + +- add hid support for fxhci_host, demo keyboard input + +## third-party + +- remove usb disk port in fatfs + +## Phytium Standalone SDK 2022-9-26 ChangeLog + +Change Log since 2022-9-23 + +## drivers + +- Move some function from fpl011.c to fpl011_options.c +- Add RTS CTS DDMA option define + +## example + +- add flow control and ddma example + +# Phytium Standalone SDK 2022-9-23 ChangeLog + +Change Log since 2022-9-15 + +## drivers + +- Optimize can drivers interface adapter freertos + +## example + +- Modify can test example, add auto loopback test + +# Phytium Standalone SDK 2022-9-7 ChangeLog + +Change Log since 2022-08-30 + +## drivers + +- Add qspi boya flash quad read function +- Optimize qspi drivers interface adapter freertos +- Optimize sata drivers interface adapter freertos + +## example + +- Modify qspi, sfud, spiffs test example, add auto test +- Modify sata test example, add auto test + +Change Log since 2022-08-29 + +## drivers + +- repair timer_tacho error + +## example + +- Modify i2c test example, add auto test +- Modify timer_tacho test example, add auto test +- remove E2000 i2c_master_slave example +- move E2000 RTC example to i2c + +# Phytium Standalone SDK 2022-8-29 ChangeLog + +Change Log since 2022-08-24 + +## drivers + +- Add adc drivers interface adapter freertos + +## example + +- Modify adc test example, add auto test + + +# Phytium Standalone SDK 2022-8-18 ChangeLog + +Change Log since 2022-08-16 + +## drivers + +- Add pwm drivers interface adapter freertos + +## example + +- Modify pwm test example, add auto test + +# Phytium Standalone SDK 2022-8-11 ChangeLog + +Change Log since 2022-8-16 + +## common + +- fix generic timer tick bug +- fix early trace issue: extra operation for early trace call + +## drivers + +- delete spi poll-by-byte API and related code +- makeup FGpioGetPinIrqSourceType API +- fix uart compile issue + +## third-party + +- sdmmc: merge fsdmmc_irq and fsdmmc_poll +- sdmmc: fix csd issue, which is reversed in FT20004/D2000 +- sdmmc: remove cmd-23 for FT20004/D2000, since they do not support SD-3.0 +- fastfs-sd: compatible with sdmmc modifications + +## example + +- modify fspim_loopback, support FT20004/D2000/E2000 +- modify fgpio_test, simplify code implementation +- add fddma_spi, test ok in E2000 + +# Phytium Standalone SDK 2022-8-16 ChangeLog + +Change Log since 2022-08-11 + +## board/e2000/q + +- add E2000Q mio pin function,board support + +## example + +- Modify i2c fi2c_master_slave example to support e2000q,add e2000q default configs +- Modify serial example to support e2000q,add e2000q default configs +- Modify rtc rtc_ds1339 example to support e2000q,add e2000q default configs +- Modify timer timer_tacho example to support e2000q,add e2000q default configs + + +# Phytium Standalone SDK 2022-8-11 v0.3.1 ChangeLog + +Change Log since v0.3.0 + +## README +- add E2000D/S description + +# Phytium Standalone SDK 2022-8-5 v0.3.0 ChangeLog + +Change Log since 2022-08-04 + +## drivers + +- Add and restruct some drivers adapter e2000 interface + +## example + +- Add and restruct some test examples adapter e2000 interface +- Modify the example documentations and default configs + +## third-party + +- Restruct adapter e2000 + +# Phytium Standalone SDK 2022-08-04 ChangeLog + +Change Log since 2022-08-03 + +## example + +- Modify the delay interface function in the XMAC example + +# Phytium Standalone SDK 2022-08-04 ChangeLog + +Change Log since 2022-08-01 + +## common + +- add e2000d sata controller +- add can2.0 and canfd test choose config +- delete ddma and littlefs examples + +# Phytium Standalone SDK 2022-08-03 ChangeLog + +Change Log since 2022-08-02 + +## common + +- add e2000d some configuration for interrupt + +# Phytium Standalone SDK 2022-08-03 ChangeLog + +Change Log since 2022-08-02 + +## drivers + +- fix fgpio FGpioGetPinIrqSourceType bug + +# Phytium Standalone SDK 2022-08-02 ChangeLog + +Change Log since 2022-07-31 + +## example + +- fix tftp bug + +# Phytium Standalone SDK 2022-07-31 ChangeLog + +Change Log since 2022-07-30 + +## board + +- fix ROARSE and FRAC delay mis-typing, replace with ROUGH and DELICATE, for E2000 and D2000/FT2004 +- add shortcut API to support set delay and get delay + +## driver + +- remove is_busy flag from fspim +- fix cpol and cpha mistype in fspim +- fix register value overlapping in fspim +- simplify fgpio API FGpioGetPinIrqSourceType + +## third-party + +- support BY25Q32BS and BY25Q64BS in sfud + +# Phytium Standalone SDK 2022-07-30 ChangeLog + +Change Log since 2022-7-29 + +## driver + +- fix fnand bug +- fix fxmac bug + +## third-party + +- add lwip_port some user setting parameters + +## example + +- Modify the lwip_echo example +# Phytium Standalone SDK 2022-07-29 ChangeLog + +Change Log since 2022-07-18 + +# board + +- fix parameters pcie mem32 space, support for pcie-sata aarch32 read/write + +# driver + +- modify qspi, support spiffs read/write +- modify wdt, support get timeout remaining time +- fix pwm config, support pwm 0~15 channel configuration + +# third-party + +- fix sfud, spiffs, fatfs modules +# Phytium Standalone SDK 2022-07-27 ChangeLog + +Change Log since 2022-07-14 + +## board + +- fix parameters sdio clk hz + +## driver + +- modify fsdio, support DMA and PIO read/write + +## third-party + +- modify sdmmc, support eMMc + +# Phytium Standalone SDK 2022-07-14 ChangeLog + +Change Log since 2022-7-05 + +## driver + +- add fnand controler +- add fxmac controler + +## example + +- add fnand_example + +## third-party + +- Port the fnand controller to lwip + +# Phytium Standalone SDK 2022-07-13 ChangeLog + +Change Log since 2022-6-20 + +# board + +- modify e2000 iomux set function +- modify some parameters + +# driver + +- modify qspi read and write driver for E2000, add register port read and write data +- modify sata controller and pcie-sata read and write driver for E2000 +- modify can driver for E2000, support for can and canfd +- modify pwm driver for E2000 +- modify adc driver for E2000, support for adc0-0 +- modify wdt driver for E2000 + +# example + +- modify qspi norflash example +- modify sata controller read and write to support E2000 +- modify can send and receive example +- modify adc example to collect voltage + +# Phytium Standalone SDK 2022-07-12 ChangeLog + +Change Log since 2022-07-05 + +# driver + +- move spi dma function to fspim_dma +- modify gdma api + +# board + +- remove parameters of gdma1 + +# example + +- modify fgdma and fddma example + +# Phytium Standalone SDK 2022-07-05 ChangeLog + +Change Log since 2022-6-30 + +# board + +- modify e2000 fparameters_comm.h and add set mio function + +# driver + +- add fi2c configs and init things +- create Mio driver for E2000 +- modify uart configs to support E2000 + +# example + +- add RTC1339 example +- modify i2c/fi2c_master_slave to support E2000 +- modify serial/fpl011_test to support E2000 +- modify timer_tacho adapt to new iopad modifications + +# Phytium Standalone SDK 2022-06-30 ChangeLog + +Change Log since 2022-6-28 + +# board + +- modify GPIO parameters in FT2000/4 and D2000 +- add iopad configs for spi 0~3 + +# driver + +- fix bug that spi busy status mis-set in interrupt mode +- add cs-set function for E2000 + +# third-party + +- modify sfud fspim port to support cs-set + +# example + +- modify fspim_loopback and tested in E2000 +- modify spi_sfud and tested in E2000 +- modify littlefs_test and tested in E2000 +- modify spiffs_test and tested in E2000 + +# Phytium Standalone SDK 2022-06-28 ChangeLog + +Change Log since 2022-6-20 + +# board + +- merge common parameters / early uart implmenetation of E2000 D/Q/S +- implment all io pad definition +- add iopad function to set func, pull, drive strength at one call + +# driver + +- update fgpio for E2000 + + +# Phytium Standalone SDK 2022-06-20 ChangeLog + +Change Log since 2022-6-16 +## arch + +- fix aarch32 Bss clear bug + +# Phytium Standalone SDK 2022-6-16 v0.2.0 ChangeLog + +Change Log since 2022-5-30 + +## drivers + +- Restruct gmac driver +- adapt to freertos lwip function + +## example + +- Restruct lwip_echo example + +## third-party + +- modify lwip config + +# Phytium Standalone SDK 2022-06-15 ChangeLog + +Change Log since 2022-6-14 + +## drivers + +- add timer_tacho driver +- modified fi2c_g.c to support e2000 + +## example + +- add timer example +- add tacho example + +## board + +- Modify fparameters.h to support timer_tacho and i2c + +# Phytium Standalone SDK 2022-06-14 ChangeLog + +Change Log since 2022-6-10 + +## arch + +- Add aarch32/aarch64 trace uart in assembly +- Add trace stub function in bootup process + +## example + +- Add example to demo exception trap + +## board + +- Modify fparameters.h to support assembly + +# Phytium Standalone SDK 2022-6-09 ChangeLog + +Change Log since 2022-6-10 + +## drivers + +- Add nand driver + +## example + +- Add nand flash example + +## arch + +- clear HCR_EL2.TGE +- AARCH64 enable irq exception + +## board + +- FPinSetPull mistype + +## README + +- remove Linux arm aarch64 development environment + +# Phytium Standalone SDK 2022-6-10 ChangeLog + +Change Log since 2022-5-24 + +## drivers + +- Restruct SDIO driver + +## example + +- Delete fsdio_probe example +- Add fsdio_cmd example + +## third-party + +- port fsdio to sdmmc freamwork +- add shell title for E2000 D/Q/S +- add prompt info for building E2000 D/Q/S images + +# Phytium Standalone SDK 2022-5-24 ChangeLog + +Change Log since 2022-5-18 + +## drivers + +- Add Semaphore driver +- Restruct GDMA driver + +## example + +- Restruct GDMA async memcpy example +- Add Semaphore lock/unlock example + +## common + +- Change interrupt source trace to DEBUG level +- Fix memory-pool bug: not set is_ready flag when deinit memory-pool +- Add FASSERT_STATIC to check structure size + +# Phytium Standalone SDK 2022-5-18 ChangeLog + +Change Log since 2022-5-7 + +## drivers + +- Restruct can driver +- Add pwm driver to support E2000 +- Add adc driver to support E2000 + +## example + +- Add can send and recv test +- Add pwm test +- Add adc test + +## doc + +- Add fcan.md + +# Phytium Standalone SDK 2022-5-13 ChangeLog + + +Change Log since 2022-5-5 + +## drivers + +- Add DDMA driver +- Modify SPIM driver to support E2000 +- Modify GPIO driver to support E2000 + +## example + +- Add SPI + DDMA loopback test +- Modify SPI loopback test to support E2000 + +## common + +- Add interrupt source trace +- Skip l3 cache operations when it disabled + +## doc + +- Update code_convention.md +- Update PR check list + +# Phytium Standalone SDK 2022-5-5 ChangeLog + +Change Log since 2022-4-15 + +## drivers + +- Slave interrupt handle modified + +## example + +- Change the command interface +- Add virtual eeprom +- Simulate master-slave communication at D2000 + +# Phytium Standalone SDK 2022-4-22 ChangeLog + +Change Log since 2022-4-15 +## drivers + +- Restruct I2C driver +- modified master poll write read +- add master intr poll write read + +## example + +- Restruct fi2c_eeprom example +- Solve the problem of reading across pages +- complete eeprom page alignment + +# Phytium Standalone SDK 2022-4-20 ChangeLog + +Change Log since 2022-4-11 + +## drivers + +- Restruct gmac driver +- Restruct xmac driver + +## example + +- Restruct ipv4 test +- Add ipv4 dhcp test +- Add ipv6 test + +## third-party + +- Add mac lwip port layer to support gmac and xmac +- Restruct gmac and xmac lwip interface +# Phytium Standalone SDK 2022-4-15 ChangeLog + +Change Log since 2022-4-8 + +## drivers + +- Restruct GIC driver + +## common + +- Restruct Interrupt code + +## arch + +- aarch32/64 support for interrupt preemption + +# Phytium Standalone SDK 2022-4-14 ChangeLog + +Change Log since 2022-4-8 + +## drivers + +- support test mode in fspim +- support tx and rx run at the same transfer api call + +## example + +- add fspim loopback test +- add fspim sfud test +- add spiffs filesystem test +- add littlefs filesystem test + +## third-party + +- add spiffs +- add littlefs, support littlefs dry-run + +## common + +- modify the way debug trace to have src file + src line tag + +# Phytium Standalone SDK 2022-4-8 v0.1.17 ChangeLog + +Change Log since 2022-2-18 + +- update openamp function +- update assert method +- re-construct fgpio, support gpio interrupt +- re-construct qspi norflash and watchdog driver + +# Phytium Standalone SDK 2022-3-31 ChangeLog + +## drivers + +- Restruct watch dog driver, add some additional functions + +## example + +- Restruct example of wdt test +- Improve manual documentation + +# Phytium Standalone SDK 2022-3-28 ChangeLog + +## drivers + +- Restruct qspi norflash driver, add some additional functions +- Adapt to different norflash manufacturers + +## example + +- re-organize example of qspi test, broken down into peripheral and storage + + +# Phytium Standalone SDK 2022-3-25 ChangeLog + +Chang Log since 2022-3-18 + +## driver + +- re-construct fgpio, support gpio interrupt +- re-construct fioctrl and fiopad + +## example + +- add fgpio-irq to demo usage of gpio interrupt +- add fgpio-softpwm to demo generate pwm with gpio +- add fioctrl-test to demo usage of ioctrl +- add fiopad-test to demo usage of iopad + +## common + +- add e2000 s/d/q default configs +- support print source file and source code line in FT_DEBUG +- convert config item DON_T_BINARY_OUTPUT to OUTPUT_BINARY + +# Phytium Standalone SDK 2022-3-18 ChangeLog + +Chang Log since 2022-2-18 + +## script + +- Support SDK version +- Move uninstall.py to unsetup.py + +## common + +- Unify assert api with FASSERT and FASSERT_MSG + +## third-party + +- Letter-shell: add SHELL_EXPORT_EXIT_MSG and SHELL_EXPORT_EXIT_MSG to support exit msg print when return from cmd rountine + +## tools + +- Remove build_all_app and intergrate_test_app + +# Phytium Standalone SDK 2022-3-09 ChangeLog + +Change Log since 2022-2-18 + +## arch + +- Modified some parameters in the MMU and added FSetTlbAttributes interfaces + +## third-party + +- Add OpenAMP library + +## example + +- Add OpenAMP example + +# Phytium Standalone SDK 2022-2-18 v0.1.16 ChangeLog + +Change Log since 2022-2-15 + +- replace LICENSE with Phytium Public License 1.0 (PPL-1.0) +- update file COPYRIGHT declaration with PPL-1.0 + +# Phytium Standalone SDK 2022-2-15 ChangeLog + +Change Log since 2022-2-7 + +## drivers + +- add fusb driver +- add fxhci driver + +## example + +- add fxhci-pcie-usb example to support usb device discovery +- add fusbdisk example to port fatfs for usb mass storage device + +# Phytium Standalone SDK 2022-2-10 ChangeLog + +Change Log since 2021-02-7 + +## arch + +- Modifying Some variable definitions in cache + +## common + +- Modify the function interface in the _cpu.c document to change the core content not to respond when the work core does not support it + +- Fixing interrupt.c initialization problems + +## gicv3 + +- Modifying cpu interface processing of multi-core interfaces in gicv3 + +## example + +- Modifying the handling of multi-core function interfaces in Libmetal + +## board + +- Add a new cpu directory + +# Phytium Standalone SDK 2022-2-07 ChangeLog + +Change Log since 2021-12-10 + +## drivers + +- add sata driver +- add fpcie driver + +## example + +- add sata test example +- add sata fatfs test example +- add pcie probe test example + +# Phytium Standalone SDK 2021-12-10 v0.1.15 ChangeLog + +Change Log since 2021-12-07 + +## third-party + +- fix get ocr timeout in ft2004 +- rename assert and delay macro +- rename ymodem + +## example + +- unify example makefile setting + +# Phytium Standalone SDK 2021-12-07 ChangeLog + +Change Log since 2021-12-6 + +## third-party + +- add ymodem transfer + +## example + +- add rtc module test function + +# Phytium Standalone SDK 2021-12-6 ChangeLog + +Change Log since 2021-11-29 + +## drivers + +1. re-construct fsdio and fsdmmc + +## example + +1. add memory test example +2. add fsdio probe example +3. add fsdmmc probe example +4. add fsdmmc cmd example +5. add fsdmmc fatfs example + +## common + +1. add slink fslink_list.c +2. add memory pool fmemory_pool.c + +## configs + +1. update default configs for all platform + +## third-party + +1. re-construct sdmmc port in poll and irq +2. add tlfs to support fmemory_pool.c + +## script + +1. modify serial_trans.py to improve cmd-trans in D2000 + +# Phytium Standalone SDK 2021-11-29 ChangeLog + +Change Log since 2021-11-25 + +## arch + +1. Add stack initialization +2. Locate the final mode in SVC mode +3. Initialize the BSS and SBSS segments +4. Copy data to the RAM +5. Enable the FPU function +6. Fpu is pushed when irq is abnormal +7. Other exceptions are treated as error exceptions + +## ld + +1. Add stack parameter Settings for different exceptions +2. Rename variables in different sections + +## example + +1. Letter_shell test modifies the makefile + + +# Phytium Standalone SDK 2021-11-25 ChangeLog + +Change Log since v0.1.14 + +## third-party + +- add sfud qspi test +- restruct sfud_port.c, add spi and qspi probe + +## drivers + +- restruct qspi norflash driver + +## example + +- re-organize example of qspi test +- make spi and qspi compatible in sfud + +# Phytium Standalone SDK 2021-11-23 ChangeLog + +Change Log since v0.1.13 + +## third-party + +- add coremark 1.01 for core performace test +- add llcbench for cache performance test +- add memperf for memory performace test +- add unity-2.5.2 to support unit test + +## drivers + +- rename fxmac according to name convention +- update user interface of frtc + +## example + +- re-organize example with category, e.g. benchmark, eth +- add catche_bench、cormark_bench and memperf_bench + +## tools + +- add intergrate_test_app and unit_test_app to demo usage of two + +# Phytium Standalone SDK 2021-11-17 ChangeLog + +Change Log since v0.1.12 + +## aarch64/gcc + +- remodify boot.S +- remodify crt0.S +- remodify vector.S + +## aarch64 + +- remodify exception.c +- remodify mmu.c +- add l3cache.c + +## board + +- remodify parameters.c + +# Phytium Standalone SDK v0.1.12 ChangeLog + +Change Log since v0.1.11, 2021.11.15 + +## example + +- add fgmac link example +- add fgmac lwip echo-ping example +- add fgmac lwip tftp example + +## driver + +- re-construct fgmac driver + +## third-party + +- modify port of fgmac lwip +- add fatfs, port for ramdisk + +# Phytium Standalone SDK v0.1.11 ChangeLog + +Change Log since v0.1.10, 2021.11.9 + +## example + +- add fspi nor flash example + +## driver + +- re-construct fspim driver + +## script + +- add flash_boot.mk, support make flash monitor + +## doc + +- add fspim driver api reference +- add sfud reference + +# Phytium Standalone SDK v0.1.10 ChangeLog + +Change Log since v0.1.9, 2021.11.5 + +## example + +- add rtc driver and test example + +## driver + +- re-construct wdt drivers +- add rtc driver + +## doc + +- add wdt driver api reference +- add rtc driver api reference + +# Phytium Standalone SDK v0.1.9 ChangeLog + +Change Log since v0.1.8, 2021.11.1 + +## example + +- add uart test example + +## driver + +- re-construct uart fpl011 drivers + +## doc + +- add fpl011 driver api reference +- add uart test readme + +## arch + +- add L3 cache disable +- modify the savefloatRegister location +- add + +# Phytium Standalone SDK v0.1.8 ChangeLog + +Change Log since v0.1.7, 2021.11.1 + +## example + +- add i2c eeprom example +- add i2c slave example + +## driver + +- re-construct i2c drivers +- support i2c slave + +## tools + +- add test utility to build example images for all supported platform + +## script + +- add build_all.mk to support test utility +- support make ldconfig and make setconfig + +## doc + +- add i2c driver api refernce +- add i2c slave & i2c eeprom readme +- add driver template +- add more design figure *.dio + +## bug-fix + +- fix CONFIG_USE_LIBC bug, which is converted to CONFIG_USE_G_LIBC in all example + +# Phytium Standalone SDK v0.1.7 ChangeLog + +Change Log since v0.1.6, 2021.10.20 + +- re-organize readme and docs + +# Phytium Standalone SDK v0.1.6 ChangeLog + +Change Log since v0.1.5, 2021.10.19 + +## example + +- add project to demo usage of newlib + +## script + +- add `PHYTIUM_DEV_PATH` for all platforms +- install cross tool to `PHYTIUM_DEV_PATH` +- update GNU CC version to 10.3.1-2021.07 +- modify CC libc.a for printf issue +- merge newlib to CC tool + +# Phytium Standalone SDK v0.1.5 ChangeLog + +Change Log since v0.1.4, 2021.10.14 + +## example + +- add project template to support Windows10 + mingw64 developing + +## script + +- modify install.py to support Windows10 + mingw64 +- add Windows10 cmd script to access mingw64 shell and Windows tftpd tool +- modify compiler.mk to support Windows10 + mingw64 +- add uninstall.py to support uninstall sdk + +# Phytium Standalone SDK v0.1.4 ChangeLog + +Change Log since v0.1.3, 2021.10.13 + +## driver + +- add iomux for E2000 +- add nandflash driver for E2000 + +## third-party + +- add yaffs2 for ramsim + +## lib + +- Modify the standard system call implementation + +## common + +- add printf for trap functions + +# Phytium Standalone SDK v0.1.3 ChangeLog + +Change Log since v0.1.2, 2021.10.08 + +## drivers + +- modify sdmmc drivers for FT2000/4 and D2000 to adopt sdmmc cmd component +- add sdio driver for E2000 + +## example + +- add mmc cmds for overall_test example + +## third-party + +- modify letter shell to get reture result +- add sdmmc cmd component + +## script + +- add install.py as alternative install script + +# Phytium Standalone SDK v0.1.2 ChangeLog + +Change Log since v0.1.1, 2021.9.24 + +## drivers + +- modify gmac_dma driver adapting to freertos lwip + +# Phytium Standalone SDK v0.1.1 ChangeLog + +Change Log since v0.1.0 + +## drivers + +- support watchdog timer + +## baremetal/example + +- add wdt_test example + +# Phytium Standalone SDK v0.1.0 ChangeLog + +Change Log since v0.0.11 + +## drivers + +- support gicv3 init with multiple cores +- support watchdog timer + +## baremetal/example + +- add letter shell test to demo application of shell +- add libmetal test to demo core0 - core1 commuication with libmetal support + +## third-party + +- add letter shell 3.1 +- add libmetal 1.0.0 + +## tools + +- include elfio tools + +## script + +- support linkscript config with sdkconfig.h + +# Phytium Standalone SDK v0.0.11 ChangeLog +Change Log since v0.0.10 + +## drivers + +- add f_gmac for FT2000-4 and D2000 +- support generic timer tick +- unify api interface for cache operation + +## baremetal/example + +- add gmac_test example to support gmac 'recv intrrupt' +- add lwip_test example to support 'host ping' + +## third-party + +- port lwip 2.1.2 for FT2000-4 and D2000 with f_gmac + +# Phytium Standalone SDK v0.0.10 ChangeLog + +Change Log since v0.0.9 + +## drivers + +- add gdma for E2000 + +## baremetal/example + +- add gdma example for aarch32/aarch64 +- gdma example surpport direct and bdl mode + +# Phytium Standalone SDK v0.0.9 ChangeLog + +Change Log since v0.0.8 + +## drivers + +- add pcie for FT200-4 + +## board + +- merge D2000 board + +## baremetal/example + +- add pcie example for aarch32/aarch64 +- pcie example surpport dma and mmio + +# Phytium Standalone SDK v0.0.8 ChangeLog + +Change Log since v0.0.7 + +## board + +- support D2000 +- add D2000 AARCH32/AARCH64 deconfigs + +## drivers + +- add sdci for D2000/FT2000-4 +- add mci for E2000 (to do) +- fix timer & tacho review issues + +## make + +- support switch platform + +Change Log sinc v0.0.6 + +## board + +- add ft2004 io mux parameters + +## common + +- support delay sleep by ms and us + +## configs + +- add default configs for supported platform + +## drivers + +- add i2c drivers +- add qspi drivers +- add timer & tacho drivers + +## example + +- add i2c eeprom master example +- add qspi nor flash example +- add timer example for e2000 + +# Phytium Standalone SDK v0.0.6 ChangeLog + +Change Log sinc v0.0.5 + +## driver + +- add canfd +- xmac +- spi + +## baremetal/example + +- add can_test +- add spi_test + +# Phytium Standalone SDK v0.0.5 ChangeLog + +Change Log sinc v0.0.4 + +## BSP + +- add board to support platforms +- support iomux, gpio and eth drivers + +## Scripts + +- update install.sh + +## Others + +- add git attr to fix cr/lr issue + +# Phytium Standalone SDK v0.0.4 ChangeLog + +Change Log sinc v0.0.2 + +## BSP + +- support Rt-Thread 32 bit single and smp Mode +- Support Rt-Thread 64 bit single Mode +- 32bit , 64 bit baremetal support libc +- 32bit , 64 bit support fpu + +## baremetal/example + +- aarch32_math_test + +## tools + +- add sdkconfig.h header + +# Phytium Standalone SDK v0.0.2 ChangeLog + +Change Log sinc v0.0.1 + +## BSP + +- support SYS Mode for Freertos +- support OS defined Irq/Swi handler for FreeRTOS + +## Doc + +- add checklist for pre-release check +- update Readme + +Change Log since init + +## Baremetal + +### add aarch32 & aarch64 example + +- aarch32_hello_world: hello world run in ft2000-4/e2000 +- aarch32_qemu_debug: hello world and step debug in qemu +- aarch32_timer_irq: run with generic timer tick irq +- aarch32_cache_mmu_wr: run with cache (L1/L2/L3) and mmu enabled +- aarch32_uart_irq: run with uart tx and rx irq +- aarch64_uart_irq_send: run with uart tx and rx irq in aarch64 + +## BSP + +- support armv8 aarch32/aarch64 +- support platform FT2000-4/E2000/Qemu-AARCH32 +- support cache and mmu +- support irq, system trap +- support generic timer +- support assert and debug trace +- support early uart print during system init +- support system error coding + +## Lib + +- support c standard lib +- support c no standard lib +- add kconfiglib to support menuconfig + +## Make + +- add basic compile scripts, 'complier.mk' 'ld.mk' +- add compile info print script, 'buildinfo.mk' +- add menuconfig setting script, 'preconfig.mk' +- add source code export script, 'packsource.mk' + +## Scripts + +- add sdk install and register script, 'export.sh' +- add utility script diff --git a/bsp/phytium/libraries/standalone/doc/design/baremetal.dio b/bsp/phytium/libraries/standalone/doc/design/baremetal.dio new file mode 100644 index 0000000000..34341657ca --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/design/baremetal.dio @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/doc/design/build.dio b/bsp/phytium/libraries/standalone/doc/design/build.dio new file mode 100644 index 0000000000..080a9d64b6 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/design/build.dio @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/doc/design/driver.dio b/bsp/phytium/libraries/standalone/doc/design/driver.dio new file mode 100644 index 0000000000..0c0205c4c5 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/design/driver.dio @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/doc/design/framework.dio b/bsp/phytium/libraries/standalone/doc/design/framework.dio new file mode 100644 index 0000000000..0e48de454e --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/design/framework.dio @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/doc/design/freertos_system.dio b/bsp/phytium/libraries/standalone/doc/design/freertos_system.dio new file mode 100644 index 0000000000..ee395bec82 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/design/freertos_system.dio @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/doc/design/freertos_system.png b/bsp/phytium/libraries/standalone/doc/design/freertos_system.png new file mode 100644 index 0000000000000000000000000000000000000000..ed6d47af0dadd63ef425f25bde3e4fd158c70e64 GIT binary patch literal 185751 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/doc/design/freertos_system_deps.png b/bsp/phytium/libraries/standalone/doc/design/freertos_system_deps.png new file mode 100644 index 0000000000000000000000000000000000000000..5e85f2ac14ad3ae5f3a679eecc5c4f0cdf6d4a83 GIT binary patch literal 177601 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�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 kJD4i6Oo*OLWUJ?44a3@xcT=C0xk9>XTCjj3 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

tX^AYe;G6Cfvk@bn;Mhb>$ucH-fn*8P6m{H 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 Iikbia0kDA>vH$=8 literal 0 HcmV?d00001 diff --git a/bsp/phytium/libraries/standalone/doc/design/middleware.dio b/bsp/phytium/libraries/standalone/doc/design/middleware.dio new file mode 100644 index 0000000000..ecfe1e64b4 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/design/middleware.dio @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/doc/design/platform_test.dio b/bsp/phytium/libraries/standalone/doc/design/platform_test.dio new file mode 100644 index 0000000000..a557546310 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/design/platform_test.dio @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/doc/design/system.dio b/bsp/phytium/libraries/standalone/doc/design/system.dio new file mode 100644 index 0000000000..1dc4317445 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/design/system.dio @@ -0,0 +1,253 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/doc/design/system.png b/bsp/phytium/libraries/standalone/doc/design/system.png new file mode 100644 index 0000000000000000000000000000000000000000..a6919e5ff1dbedb2596a1b1a0f6b2c7ad9882f9e GIT binary patch literal 314383 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

-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�Gy8RgdN2~HU2dFlrN){b^R6z$c7a9;T{A{*% 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/doc/design/system_2_deps.png b/bsp/phytium/libraries/standalone/doc/design/system_2_deps.png new file mode 100644 index 0000000000000000000000000000000000000000..dbc5e89b2ff73cdeb92ec856e295e8148ac677b3 GIT binary patch literal 207455 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

`$RyMa01yzE#9Vhii1K~4?xWJVx}rP~ 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%0A*J>&JKNL_%RAN0SHd9r3f)D-{W~w4aqjd-+ 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 literal 0 HcmV?d00001 diff --git a/bsp/phytium/libraries/standalone/doc/driver_template/foox_hw.c b/bsp/phytium/libraries/standalone/doc/driver_template/foox_hw.c new file mode 100644 index 0000000000..75fb743759 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/driver_template/foox_hw.c @@ -0,0 +1,54 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: foox_hw.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:24:47 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#include "foox_hw.h" +#include "fooxx.h" + + +/***************************** Include Files *********************************/ + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +void FooxHwWrite(uintptr base, char byte) +{ + +} + +char FooxHwRead(uintptr base) +{ + +} + +void FooxxReset(Fooxx *instance_p) +{ + +} diff --git a/bsp/phytium/libraries/standalone/doc/driver_template/foox_hw.h b/bsp/phytium/libraries/standalone/doc/driver_template/foox_hw.h new file mode 100644 index 0000000000..47bbfb4a1f --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/driver_template/foox_hw.h @@ -0,0 +1,84 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: foox_hw.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:24:52 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#ifndef DRIVERS_XXXX_FOOXX_HW_H +#define DRIVERS_XXXX_FOOXX_HW_H + + +/* - 传入模块基地址,不能复杂结构体 +- hardware interface of device || low-level driver function prototypes + +- 包括驱动寄存器参数和low-level操作定义 +1. 定义寄存器偏移 +2. 对上提供该模块寄存器操作的接口 +3. 一些简单外设提供直接操作接口 +4. 可以定义一些状态的接口,用于响应驱动状态的变化 + +note: 本文件不能引用fooxx.h +*/ + +#ifdef __cplusplus +extern "C" +{ +#endif +/***************************** Include Files *********************************/ + +#include "fparameters.h" +#include "fio.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/** @name Register Map + * + * Register offsets from the base address of an SD device. + * @{ + */ + +#define FOOXX_XX_OFFSET + +/** @name FOOXX_XX_OFFSET Register + */ + +#define FOOXX_XX_STATE + + +#define FOOXX_READ_REG32(addr, reg_offset) FtIn32((addr) + (u32)(reg_offset)) +#define FOOXX_WRITE_REG32(addr, reg_offset, reg_value) FtOut32((addr) + (u32)(reg_offset), (u32)(reg_value)) + + +/************************** Function Prototypes ******************************/ +void FooxHwWrite(uintptr base, char byte); + +char FooxHwRead(uintptr base); + +#ifdef __cplusplus +} +#endif + diff --git a/bsp/phytium/libraries/standalone/doc/driver_template/foox_options.c b/bsp/phytium/libraries/standalone/doc/driver_template/foox_options.c new file mode 100644 index 0000000000..cd0ae66d34 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/driver_template/foox_options.c @@ -0,0 +1,73 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: foox_options.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:24:58 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +/* + - The implementation of the options functions for the driver. + - 用户选项实现 + 1. 速度参数配置 + 2. 工作模式参数 + 3. fifo功能 + 1. 深度 + 2. 使能 + - 用户常用的功能寄存器读写动作,建议通过option提供快捷方式 + +*/ + +/***************************** Include Files *********************************/ + +#include "fooxx.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Variable Definitions *****************************/ + +/************************** Function Prototypes ******************************/ + + + +u32 FXxxGetOptions(FXxx *instance_p) +{ + +} + +u32 FXxxSetOptions(FXxx *instance_p, u32 options) +{ + +} + +u32 FXxxSetData(FXxx *instance_p, void *data_struct) +{ + +} + +u32 FXxxGetOptions(FXxx *instance_p, void *data_struct) +{ + +} + + diff --git a/bsp/phytium/libraries/standalone/doc/driver_template/foox_role.c b/bsp/phytium/libraries/standalone/doc/driver_template/foox_role.c new file mode 100644 index 0000000000..90960fc4f1 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/driver_template/foox_role.c @@ -0,0 +1,50 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: foox_role.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:25:03 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +/* + - 一些驱动模块,直接操作硬件的I/O接口,无法实现有意义的操作,此时需要针对中间件或者用户使用习惯设计此模块 (i2c,nand,eth) + - 部分场景适用, 分角色的 I/O 操作 + - 此模块的函数原型,在fooxx.h 中声明一次,方便用户或者中间件层调用 + +*/ + + +/***************************** Include Files *********************************/ + +#include "fio.h" +#include "foox_hw.h" +#include "fooxx.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + +/*****************************************************************************/ + diff --git a/bsp/phytium/libraries/standalone/doc/driver_template/fooxx.c b/bsp/phytium/libraries/standalone/doc/driver_template/fooxx.c new file mode 100644 index 0000000000..539688f246 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/driver_template/fooxx.c @@ -0,0 +1,67 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fooxx.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:25:29 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +/***************************** Include Files *********************************/ + +#include "fio.h" +#include "ferror_code.h" +#include "ftypes.h" +#include "foox_hw.h" +#include "fooxx.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + +/*****************************************************************************/ + +/* 此文件主要为了完成用户对外接口,用户可以使用这些接口直接开始工作 */ + +/* - 包括用户API的定义和实现 + - 同时包含必要的OPTION方法,方便用户进行配置 + - 如果驱动可以直接进行I/O操作,在此源文件下可以将API 进行实现 */ + +/** + * @name: FooxxCfgInitialize + * @msg: Initializes a specific instance such that it is ready to be used. + * @param {Fooxx} *instance_p is a pointer to the driver instance. + * @param {FooxxConfig} *config_p is a reference to a structure containing information + * about a specific driver. + * @return SUCCESS if initialization was successful + * ERROR + */ +FError FooxxCfgInitialize(Fooxx *instance_p, FooxxConfig *config_p) +{ + +} + +/* - 包括用户API的定义和实现 + - 同时包含必要的OPTION方法,方便用户进行配置 + - 如果驱动可以直接进行I/O操作,在此源文件下可以将API 进行实现 */ \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/doc/driver_template/fooxx.h b/bsp/phytium/libraries/standalone/doc/driver_template/fooxx.h new file mode 100644 index 0000000000..ad27bbaf05 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/driver_template/fooxx.h @@ -0,0 +1,106 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fooxx.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:25:35 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#ifndef DRIVERS_模块名_FOOXX_H +#define DRIVERS_模块名_FOOXX_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ + +#include "ftypes.h" +#include "fassert.h" + +/************************** Constant Definitions *****************************/ + +/* Configuration options */ +#define FOOXX_OPTION_XX + +/* Operational Mode */ + +#define FOOXX_OPER_MODE_XX + +/**************************** Type Definitions *******************************/ + +/** + * This typedef contains configuration information for the device. + */ +typedef struct +{ + u32 instance_id; /**< Device instance id */ + uintptr base_addr; /**< Device base address */ +} FooxxConfig; + +/** + * This typedef contains driver instance data. The user is required to allocate a + * variable of this type for every device in the system. A pointer + * to a variable of this type is then passed to the driver API functions. + */ +typedef struct +{ + FooxxConfig config; /**< Current active configs */ + u32 is_ready; /**< Device is initialized and ready */ +} Fooxx; /**< Device instance */ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ +/** + * @name: FXxxLookupConfig + * @msg: Get the device instance default configure + * @return {*} + * @param {u32} instance_id + */ +const FooxxConfig *FXxxLookupConfig(u32 instance_id); + +/* + * @name: FooxxCfgInitialize + * @msg: Initializes a specific instance such that it is ready to be used. + * @param {Fooxx} *instance_p is a pointer to the driver instance. + * @param {FooxxConfig} *config_p is a reference to a structure containing information + * about a specific driver. + * @return SUCCESS if initialization was successful + * ERROR + */ +FError FooxxCfgInitialize(Fooxx *instance_p, const FooxxConfig *cofig_p); + +/** + * @name: FooxxDeInitialize + * @msg: DeInitialization function for the device instance + * @return {*} + * @param {Fooxx} *instance_p + */ +void FooxxDeInitialize(Fooxx *instance_p); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/bsp/phytium/libraries/standalone/doc/driver_template/fooxx_g.c b/bsp/phytium/libraries/standalone/doc/driver_template/fooxx_g.c new file mode 100644 index 0000000000..f3a10eec57 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/driver_template/fooxx_g.c @@ -0,0 +1,53 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fooxx_g.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:25:09 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +/* - This file contains a configuration table that specifies the configuration +- 驱动全局变量定义,包括静态配置参数 */ + +/***************************** Include Files *********************************/ + +#include "ftypes.h" +#include "fparameters.h" +#include "fooxx.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + +const FooxxConfig FOOXX_CONFIG_TBL[NUM] = +{ + { + .instance_id = 0, /* Id of device*/ + .base_address = FOOXX_BASE, + }, +}; + + +/*****************************************************************************/ diff --git a/bsp/phytium/libraries/standalone/doc/driver_template/fooxx_sinit.c b/bsp/phytium/libraries/standalone/doc/driver_template/fooxx_sinit.c new file mode 100644 index 0000000000..fa31af0ae9 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/driver_template/fooxx_sinit.c @@ -0,0 +1,61 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fooxx_sinit.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:25:15 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + + +/* - This file contains the implementation of driver's static initialization functionality. +- 驱动静态初始化 */ + +/***************************** Include Files *********************************/ + +#include "ftypes.h" +#include "fparameters.h" +#include "fooxx.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Variable Definitions *****************************/ + +extern const FooxxConfig FOOXX_CONFIG_TBL[NUM]; + +/************************** Function Prototypes ******************************/ +const FooxxConfig *FooxxLookupConfig(u32 instance_id) +{ + const FooxxConfig *ptr = NULL; + u32 index; + + for (index = 0; index < (u32)NUM; index++) + { + if (FOOXX_CONFIG_TBL[index].instance_id == instance_id) + { + ptr = &FOOXX_CONFIG_TBL[index]; + break; + } + } + + return (const FooxxConfig *)ptr; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/doc/fig/Serial_inform.png b/bsp/phytium/libraries/standalone/doc/fig/Serial_inform.png new file mode 100644 index 0000000000000000000000000000000000000000..ea19d25db37f0dcabf26ddbb1f805a473c80378b GIT binary patch literal 115462 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 LTlMVxAOHXWGUNUl literal 0 HcmV?d00001 diff --git a/bsp/phytium/libraries/standalone/doc/fig/admin_rights.png b/bsp/phytium/libraries/standalone/doc/fig/admin_rights.png new file mode 100644 index 0000000000000000000000000000000000000000..66a6320f6bc6014a9571d354178af9a9b54bfba4 GIT binary patch literal 3648 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

Ojb7W$p1;d_#31H`8J=(Ws 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 VCJX)}^q=F&A1JCTVD6f}{4bM>LLUGC literal 0 HcmV?d00001 diff --git a/bsp/phytium/libraries/standalone/doc/fig/check_env_for_aarch64.png b/bsp/phytium/libraries/standalone/doc/fig/check_env_for_aarch64.png new file mode 100644 index 0000000000000000000000000000000000000000..6b02afe82d49b944b1723b5215256b0e05e453a1 GIT binary patch literal 49014 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 F004GAi@5** literal 0 HcmV?d00001 diff --git a/bsp/phytium/libraries/standalone/doc/fig/check_env_for_win.png b/bsp/phytium/libraries/standalone/doc/fig/check_env_for_win.png new file mode 100644 index 0000000000000000000000000000000000000000..fa1e3f6f5eb57096133ea59cc1f68b38de673158 GIT binary patch literal 9594 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 YDvRybAe9;Uyn@tfRcwVG7#P_91Mxz_5C8xG literal 0 HcmV?d00001 diff --git a/bsp/phytium/libraries/standalone/doc/fig/check_env_for_x86.png b/bsp/phytium/libraries/standalone/doc/fig/check_env_for_x86.png new file mode 100644 index 0000000000000000000000000000000000000000..26a7f2d2c8b4bb161d23cb3a8456d9f831a8e553 GIT binary patch literal 62404 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 CXv&oU literal 0 HcmV?d00001 diff --git a/bsp/phytium/libraries/standalone/doc/fig/config_tftp32.png b/bsp/phytium/libraries/standalone/doc/fig/config_tftp32.png new file mode 100644 index 0000000000000000000000000000000000000000..3e52a8d10be34a901d80b016c064c1b178dbd565 GIT binary patch literal 11098 zcmZX4Q*b2=&}?kmPEL{&ZnCj$+cr10ZEtLCtc`8k$;P&wbHD%LR$aVJO;7jC%T#qu zjf%8{gbEN0OjBG$NkfT?Oya*7Z5%8YoIV6X0bC$SAX~PysF;s8D0R|0|dbPiwzG2^b8zdh;A_J)3=U)vkNS8nt z0lsg`?}|^oSD=o7RX_EYm`~6X$cTANFb3rNJ^aP_J^Jcc^snWG=ah6mpxR&E7u<*S zj`<;fQt%G60;0QhxeHhZm47dMgFv8n!h6S~fLp=R?;}630H$y1Z^Ad`Z`2RNtpE_{ z{(bRd_D!%36r4~F>UswICj2z~WPT&P0^J6*{)f-LX1t@mGhYdI2CV$21$u*ht9w>_ zGu-tD7fb{Zg9N`0KI*ok&q!ZlAjP+9RE_~-fIYFHSAE80@N|il zcJOcwDB==4?>se}gQ{`eDPVzesMe?>?;6FDqDG5CW)4LO1U6UJj6Q+k2k$$=UDAJv z)fJt-yIuMgp@(LJSxp96)N^!~z5EU7X8HV-cA^6^OI%=rsOS?WX{(y(-q{dTJ_hjd zKh7OC$Y;#CSF;vBFoCSCtVqDVjYyrvocd{GS1)=Iun0tU8sFTOo4=LdE{N(z%sWIX zq$nML+Fy_PO^@1bGkax-f);(Rq>#J5s6m&)^J)7d%a3Cm`3W~1A3T==XqH4nG8x^$ zfz>cB8>868eKcndR1^$biKm8iR3^r_L%IE23#`oT@0yw4&la#mCX%V=t08%}DXaQMlah&4yqUpY$G_tk=caKNEiZrb zQV!C(dMR#7jTB*VUG|zlT5?ReTbRKldMnAn2eD9z;cDJ{w}#Wq zpeIH(;8+9CTO_st{H`mS4t3<9yQq z0yGC;;&6jPbS`n=sXj;_i>o7UpF_#sN^mVEl)jaeX0OBPE0u0pR36%jPT&8=b_ul? zmuF-)`fP8liVhXj9}f;?j@Z`LR4;5Eb&BI}_MItGCHMPm5#E}Z3wFr&{gak10ZZQTy|)%?H1OPx!o-G2ztnBk{&)q!6)99E z#w5_AeO6H$ywPxSoTVC^(y#1eADov|&XzlnISyD2`OmbFc+R!0$AwWu=q+_wpQ&?0$wud!bZ0-C{wV82aMU3qw^9^J(nc{v*^Y4HM7xyu57giYzjrzpG* zn&g)4Ey z@p#>X;xrXjcPZf(%ZL+nKcml+)c@%N-(I2|ve=G3&m-s#$y--PPfD)+L=JBC<|}R> z;<$b-m%Fq5VI{mNg0@ajqAlKI%yBt08%SxV>(m@Kr3^fVLyL#X419dt6HVBRnkr7% zJYA40%d~oobN|}j;HAC@GHFt?x68`(gW5|7?L?tSd`#Nd(YI?sJ=GwUzm)S$9sEV< zo8Grkb|8$%5^nLUmYiXW%#?*aZ9)&|SQi(LCxr9(9u=(cAzXQ_W!a(1Ze>GnLuv$n+ zoxeq_6vw9&Nv_b><5wJ(jED8~QLI;G^bolh0!4ltiJL&wghqf+R#KUIqNwMlr2QV$ zxm_Omnbc*Q+$(WO^}U zdnmlOI2avMTiGTBS3w~#ws*;3`}H}62`*?hv0)hPWEE8-8>+0lhmrKkx5MgLDBSNV zx&cn}Y;CDDv`q8C?NK6_NWRH1mi8cA6ChK{fO8-8TJ&V`-S!oR!t~KBg4uVbA+x`k zcTt6;gU#BGK6(U62O=&R(yRn7Yib*gU7vsdwHRCgBB*=Qfkjb=nWQ@=C!IM!qri)p zhI3co=81ePBWT*BmnEHX5!WrV z|A}_U6u3m+|M=u;O_pc?-cy=arx9W{Yl1}y-b`LVplfBZujriIfKR>U6rg@I%v-eH z#65uQ1!pDClBJA@yH43387dWevG8fIf3sJ;qEzMGdJ7}#3jO$ zRQc$b`crj5gfIs^8*$df(T8!qKXgK<@gO^0R=M4x#}Mc z-wPFR)sBpAdtCz$y$~_9i!7=`ESfTetomegNp4$$QTyZ6YGyBQ0($$VU%s}CEz1+^ z=#V}t*^+xO!D_$0k7G&KWt4ys0V-6hN9=?|Q$U`h$ZzGswA3DU2mZzp?fF#S^^p50 zlh!XagAEGcnQ~X*RLb&$o+;d|0f%sUIaA`0KxT+l^S2-SSy(yPbpo;%DiC=z3sz~h zT0OI1euh>Gs}pG2hj(&tz>$(GYVnJJ4qP~I7SAi!X%E>D(-6udQy{aFCNSTB^MFd? zRw&cq4tN$M9hJka0K^**vmTPoI`B8Ofh3(3kr;jay+_4$V~3OqO~(;kjyDW=aNJ^={T2ob zff%fbEoLQP**4$pAxyInKUnilBm+wdITXo5#svm-kRa_;oE3oqQsU&3zf99iI%C|J zYh9t)D8L5puB2Kjbu{{$m9qre@xP`!U&|bRR}NPoF=dDbaMX?_K-rbqHz|Oh1F@_v z^#B;jC{}kLKouqKR{UNq%dj<4i!J4bIQ0D*F*ikAaOzFSj0CkY!bu%xEigBJ=&;zO zZOQ66;9f;hYqSeS>_YH2noEWmA2K-SSm ze|D=S=O)3(6gV~lCvmDH!5asLp0whRaoTx77Sjs@Azja#e|-~f@3hsV$CYi0(EVSz zmE~>!iN+#YY}5SZD=<@k59A{E&W^qz1gzgnb=@W)rRRtRd(P^r9KUK_Y#Rruzz6RC z(uxP_8Pd1x@JjW`s7w{BNd^MOpEyD*=zo5m;Lu%A;*&6ow{e&^@O#7C2JT)lp%Ev0 z%dR4|rPfHE$p?;6dP-77-0;*%Q(Epxg#Iy(k+Md2CpO~w()-El&Bk0xd( z!?hOpJZTU;^<%pr>gSlyrM8C;mq!&}iOCm%7FWiC3mZ4uo$uJ@Y$y<&-dba-;#wx9 z!spoo9!o7lr)2Iulp(;$0GpY>S^$l9_E0VT;tnZXwP@Cy{WtY!ptfH~@FdR|_$SaO zP2~eTX6h;Hm};Wm?{T{LX?)i(Wpnqjw-N)x(X{V<6MoKUM)+hRb<&ZyuCa-v-Us8p z|2>7nAzCYy9mI~XU(`vG9u!C%F5*PQ(8exXsYyLw*m})+?A^ij0D;9_6>)alr~NFT z?JDDfTySRw>9yRYb}mia5m3RP`<>dyh^>q5?(g;1v2X}6^^a6VX*~7msnbrpCH#&( zrN~HGdx^uQzRqXEB%~V2Dxj*~Alj}76Ar_JYJv=R9XoD12XFa@3eF(iPf>Y39-R`N zslf|^p^~G1K~7Kp*RFa9TD@bE*Tf_rJJj$UgkeD#JnC_l8k;yLK{ni)RikPda+^_R z$IkWe{tCJHH12f7s+ebh58a>m7-qM% zn0q5C*p~lTPb5aj3iMI!!}~M%`p7S{tPd+vnrXssi@Rqy_^4LWrl6HC$_Cy7RB!e+ zR)cAj+W~X`?zmS@ZlG&tQ@OS9DU~;r;n^cX6pTN~Mg6v^Klz@jV!#rbt+}~~FQ(kl z-ZfES+CzbUD?m#ddd%~#x%5>b9Ov`eGqABRSntwTBvF70c=i{(pHOFQ2>LeR|G>?M ziO9>O(+l37LAh9aCeote@4Oa`9-s^R-j%H09kO z6^%P=CupxOet>+HW{jxJ>R$$cD}B?o=N&lEYh0d{204A5zwvMp(MZAaz0mlsfP^h? zh*Fqmbv<9Wslrx2u(??$sJ+y?WN}A>QF!afha+`fsLUF!W5&6CorKe90m(5}zw+D+ zlgDCEXX7@=dWaqzi$5+p++q@?Ez#Qk*=eaQ`>bvOij8S zjy$oy{0LxED$^8`7#9wU<#Cc#_5U&xd&xFb=NXhc#l~S=$~g}6{1K(SwMV_~9rRW@_%avavtF{Pi& zoj=PQAkGCLFnytELk|7}0)D@tQ-MgRRV&t#Da;s%gc^k{RY-~AHk;^+x51P=Vcx?n zm9b~B2gSne24ngBHQ6xSWn5E_`8&O|Bn~fQWZ2|L279f?hJ*E7Tk>HX=eNiwJ50wW zx@dLqsbXURB&9xgcu#@T$_XyZ;d)m#h?Zq^X_>;?r+x)o*5*BE{^m$=-^E+*fY~l^ ziphpr=0jD-j#Fusb3W!W4c#O|k_%rW`3+U-=^C0mP)U!%+c@&0!Og3h_B1s}(}e=f z;0KL3Ref)71~PZpODGE?MLGnFC0an=<1wyj>ij0NLk|GV)Jq;TvIQ zsYwQO^Co-1m}Gm}EOlcOcfLS~l`P{6U%Lhyn{lUGcXmC5&mG_l`Ko?XGt<`**w2m_ zANBA7AF?*K?9hC#adz;x5>{0!JD{+N&Zsc!!n{hcGL>GkYL?iItxG5rQq>}$&F26@ zls3|2yn4ga;WhhkdhMGn?&BKkSB%oZsYn}#>0M|lF_c1RO9vW9jQ-5_T48;5snYNG zVA+r3C2?o23JtXg=h&{+JQolzNUPby2y_R9Q_x%v{T^*?(R@Fv_Qj&U%h=|oE)=w@ zO2i;g@Xd}G8;Q~tG7z2&F3Z-i@GWIVW8G$KhUIusq z_lADK@Y#$m*5y`!+BYgp0hHs;MW>9-{0TyfWOD#r!eKX)f3+wb90a^zxN@eIj$;a;atZQ^t#w+tuuI{kd~w0Qw+ znEopGLjg9nN6P8O?n_nTnsS5c%kGvmif5|cH0s5^`Fc9=V@0UN+Zc(yS;;0WV~17w z10judc<-;R-$fPWlkZ{cKB5sHD0{-`N8;4?q7D7r=}8?@Z2{`!6LBEz<_Cr#RpK@D z!O`zz1iyb7n~&DFN2~I`12q~8RdV-VFB*_#4k<&g6^tQ+C1-COjNQ$)3&aCkv@Py# zXR{~@gH=xK*We24qhQm|Mve#zxu<*YGV(+sJ{5Lx(6mza8SL|BoAj?-7{tAUPd7w{uIsPm2m4jW!;P8U$Y z-`J&Vg?BeqqC#lRTm70@pKqvlI1-U}I>h&yZcKP()g&L1kC++3e||8dOOaRo+pory zoI7;Cc**ySWKDJ*)yIpSyKn(2PsNI;Z>tfS<0YRzbX{yv5YP7LM+>$AnLS?*uacxv zOZKpq3<|=~F^V)Sx=Mr5o~2aFWY0I8xh48{TCB+Km`AkQpmX6qyE0ualpvP*v>@_k zfQC@#en`;rs8+|{e^tP6Ke`r==ch!aR57PL8?Lfk{9^u^%|QbmzuOY>XN#&aN(w6~ z%1mymJE17hBU^cu$#{BEA*tVtp;b|$aZ;XIxd$T<%Gv{*D=f@GByuP7TVq{&pb+|6 z^&(^fl$B}4A55#8zBZAZ=;1p@QNj!VonZ##h7KB-e!LG(Jgp@Oc`C;@J_XZQzJWyw zN}Ex&rdy@)sk?w(FJ5UJZ=4_=1eSmsGOf8P?!cb65WYh;AL;6Zc$1OIOM@Ggon*=G zpVRQQS%V`U#!QKG)e62=CAUQg^jZFmL0T#mNYfmgk@e)`CM?NQgne7cj zv6gjxFRtDgfKDn)OHR(WcobZbx0Cs(f7mystzKs%bGd1e%~jT;CHO7$Kb zWRjQrbe?AVwBQPUVW!zX_Vgx0a+mwJ(1G1m!IS6`j`l{LF^ckGx{S- z*Rc9xS0}bNkV^{_VPz>Db0Y(@A*}^LMR|`wNt-ao_`BFJqpLDjWZ#4#A21thQNFIW zyWgvyXb+`2>>L~aTs-9vEH|U3YOQ9*cOoTp-!-sNj;Wh?T`AWjDS(ifj{5-WjNY4D zqkRTg{&A|da(y#AwOzRn%%bNhk-8}2)!^_{)kC){B@g{az*G(h=dVTjtD+(H_@AZ9 zES>O9?SD4Jf%=4&U~u-A{Nq|H)Vz^vz$m!N(A`1rn;O-7mm$m6ukeF$oz2J5cq(6nYa0LoGNd_-shV;)o4YBj=Q8KbckidPI(e&#x%_)4N=pO1q53Z?4Tw4n zpVSWz`07Y3Z!jzuIbKZT%M}LLdsg zqkvoNB#(1SVX5;^;MzVD;~He5I_lJJjfv9UZ}_&#Sdj?) zm0X4(g6(nUkC>o2Q_IR_%4x9}e`^Fmw4*2ug|KTInm_sFI%SBanJ_<88vpqaJAWf= z6la2|KiU*0TFMod-{bDG?pdHMc`4eR4SQD zmHMs54qJ-Q@MVo#CU`-(d#(pTF`rAKDRRpl;~*A={&juNp}K8!@J~zxZeUp+_ac-Q zj}g>@LM!bPgy7B>&Asb5@Jo<0QS8vQZk;1V^G8@BU(`X*>lOAG3~XREaWgkh;seL^ z&p-x`fcy>*xDl1cq>rYsMgddQS1SGULG>F<5(w$ksI6>eWgdP+McCqQl&987n15xo zQ_kqr5FZFSJ4sFRT;BZ5Kh3JVS&ku4k~Xb^B(gK zFlUM0VqIDf?$Zysk}1pc>yvm)EJ%uR^nw1G|KS6dqO=KDibL=}OM2}{Bnt;wxZ1hU z*YKxzdy8~!_u_$6B3|>i`r2uW++J~)-if{Ws`VWS2Z;Gylrlh-csjc+TtX#x<8MbV zjsEK~Z;EHxIp<+iE47+vw({OQRREQhA}kt5H3$%-a_?y$AS@`t?w3%)_25K@YG)qimQ=F6@fOWljn3t< zeaV5$fx2GOt|>KhLko7qPCpw)CSXGozX@E$gc*-=DsHh$0TK|aU>~F;bd$i0%Sg)3 zG%Yvi6tqQzvZO$)Vw{ZO%N}ItHpPMkUXh>}^;_wqxY!OngGdyO#{1)xPy{*ps-b%X zr1HWST!w3C8&*o^ELaEnEqBrjHsa|enStsD}l>U~$fKpLJu_BkK&TKcaj6$o^wh1y$($Z~naT z<=}8LRVdOSPixR4gjA5n+=SiC+RG<{;^inj^-i(3p*U?Rq0EP5jU_Xqa6;)g1>xYs zrtZARV`KAndK$KkhaLl2Jc6=44k6IKVi>2UASahl1$0T&vy%~vIb(^tZe@PBLg`FrmBO~b!tQ7nI0A!llP@DHzq zWjlliInu*1i50-0KsMeU>iOYkhMIYx; z1IQ!FW>{czXAtfjpBe;b0CW!|V&HR|^g;>b)p3!aL{|UmEoE{?g_hLILlg&n3o|8D z@Ai-6CEpxB@N6X3)&VJ1(S6`>&;)0zDC_%-_UE;<=u9eeQwwB`CX0$1%2{5{yA@BcYeGJQ#ayO)Q|NOSLN-`w>c@h z^x-4T=8mK*Jb-nY$iqtQpE=WVSeGI0a&IvE6o>{vX&O9#PW4Lvw{oB*Mw3;WYC45f zkC~fp!j&AcV6D19xA7nL&iKRkcRV6AtvbT8UqsMG+14obtLyp?`Ce*Zxb7wPclMdSOs@u8NgFB%+^_jxhp6QkE0n1vC*nx-^} z`en4{*~(3Qb@$&pPG_IVEB?U<-qrXCs&{*B%$UtB{KOw^4MjiEzmSryT@QWt65mGn z&@G}&`1nbdkr?2nh!GebZgE(_qt{}_-A63?pGN~S_&Y!|8Ya?-=)nc+5x)z zoh;I|ikp@8Ow!5r85+{RYK$a~x|m=JWxhX~8A~@=6340KUgA~pqEON^tVZ&?1|#0J zTE(4j4+V*rsE%yfZW3BhUQn;f;DUn_d5~xM`os0H{Zv_l8=&746~+-~nAoLRJKpgJ z_jtqn?RO@Jzea6mF1@9qii2rcm6YgbzSo%CR$}Os-9Po6)c@)tWRb;9G2!{!eO zlnw_EEc%?FFdZNx6ldeS` zIYn|gCuB>Y68Ied{-9Q_hn6E1O~A$bo<{D zpXd3xhB9OE{W#=mh{}@4k@-K(3HU9WC$y%a!a z>1puNSf5CLhWGtyPa|VEcV}C*O5`d%NPSuiLeWY~@9>4omg*vVmU*;os^FZ;S-@j# z?=ETEen@DxLIX*X?Qg730Rvq`ay@x>#d)ppA1GIhdZ%6(4vitSZ!|hBm49 zfSGH3u*Fm=`_@z$u>is)!+yF!FKWF{=uKW7%)olw)#n+)Qrv7QsPfSnTk7lZ`TdM) zp%y;_NpHi@+iD?oMHh`9A^#Azj^uV%Rxva^c9Z^dq84@~V@ry@pzk|>6`g5KQssZ* zs9_&OaioE^=R;LZXkPdjv~gH0tVY;tx?a>04~dafGfY8M_og)GavpWmg%F4~^!o=k zvdRi?m|r_S4A)SJTh)kXoESxIv4(177K5e^+Gp2>|FEe6t$&-pV;;{=l@3@NbG`W= zXXfR%th1w;5bL=@aP%WcsUV_KZp+l&o^5OVo9QW1)WPQSxkL0W^u{7mJXVQ`f|6E- zWqGqzBx#^G3^T(t!u+#o!jh8V$PxN7*uO55160xi56sv&h9XTzm)f~*%zr`T_tefJ z!>z4yC;qhKeT4}J(xaO*izEpIC1P131ymv5qFj6I zh1N2*ro{7f`gp52%ZEPjQ=!q*dXb<9LXPsvj0 z9}2_zGps4cw8YPyPTYWXU$%{}z4b0xbzKc$V6akq(}l=-jP{IFgmUGb4gC=0f@UDt F{{mb1vPb{` literal 0 HcmV?d00001 diff --git a/bsp/phytium/libraries/standalone/doc/fig/git_url.png b/bsp/phytium/libraries/standalone/doc/fig/git_url.png new file mode 100644 index 0000000000000000000000000000000000000000..93cc44497e5e89e8449d51ec62ae3b7803557ba9 GIT binary patch literal 24688 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 literal 0 HcmV?d00001 diff --git a/bsp/phytium/libraries/standalone/doc/fig/install_for_aarch64.png b/bsp/phytium/libraries/standalone/doc/fig/install_for_aarch64.png new file mode 100644 index 0000000000000000000000000000000000000000..d6d81a2cf78c0e697523af499193cc08a44e1c54 GIT binary patch literal 36086 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 z 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 O00000000000000-D=vQk literal 0 HcmV?d00001 diff --git a/bsp/phytium/libraries/standalone/doc/fig/install_for_mingw.png b/bsp/phytium/libraries/standalone/doc/fig/install_for_mingw.png new file mode 100644 index 0000000000000000000000000000000000000000..1b7ff94d9bda474b9add41f4632893b72fcc05ff GIT binary patch literal 37836 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 z7L*nv_Lvx_+l%@36vzPMfID-1KmY&$0000000000000000000000000000000EjfL Ak^lez literal 0 HcmV?d00001 diff --git a/bsp/phytium/libraries/standalone/doc/fig/install_for_x86.png b/bsp/phytium/libraries/standalone/doc/fig/install_for_x86.png new file mode 100644 index 0000000000000000000000000000000000000000..be52daa09bcb3d6fb3d4941d383c3f787b5a137c GIT binary patch literal 114606 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 literal 0 HcmV?d00001 diff --git a/bsp/phytium/libraries/standalone/doc/fig/install_msys2.png b/bsp/phytium/libraries/standalone/doc/fig/install_msys2.png new file mode 100644 index 0000000000000000000000000000000000000000..9aa9105490ff3f32174642f7977b128945420e7a GIT binary patch literal 7564 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 literal 0 HcmV?d00001 diff --git a/bsp/phytium/libraries/standalone/doc/fig/installing_msys2.png b/bsp/phytium/libraries/standalone/doc/fig/installing_msys2.png new file mode 100644 index 0000000000000000000000000000000000000000..d48d93a074043cb054356b0c5357b4a81acc35d3 GIT binary patch literal 10704 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 literal 0 HcmV?d00001 diff --git a/bsp/phytium/libraries/standalone/doc/fig/ipv4_setting.png b/bsp/phytium/libraries/standalone/doc/fig/ipv4_setting.png new file mode 100644 index 0000000000000000000000000000000000000000..f18182064ef2f5f19d41029c10380b40f70a024f GIT binary patch literal 127004 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 literal 0 HcmV?d00001 diff --git a/bsp/phytium/libraries/standalone/doc/fig/is_aarch64.png b/bsp/phytium/libraries/standalone/doc/fig/is_aarch64.png new file mode 100644 index 0000000000000000000000000000000000000000..7ff6724a89b43f8a328f87032f6410b0d90a56a4 GIT binary patch literal 12778 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 CTaecP literal 0 HcmV?d00001 diff --git a/bsp/phytium/libraries/standalone/doc/fig/linux.png b/bsp/phytium/libraries/standalone/doc/fig/linux.png new file mode 100644 index 0000000000000000000000000000000000000000..a9e7c11a6af15644830007750b1c22e6cd7a57bb GIT binary patch literal 2504 zcmV;(2{-mqNk&G%2><|BMM6+kP&gp82><|)H~^giD#rlF06vjKol7O7qNk+NnYi#0 z31x2cz!O3E07uuSmF77NxYNNL6DGs6tZMqD{MYDB(yz=1?3b)Zp|A5kpdBT?fF4~w z$^YKw7-U((oY`M9=N^|FeSgaJUCvk5|FYi!AIti8{@Q>4_YnPeAstm!RaI40RYEuS zshlbednXg+Y|PBe#Tl5KHnaGAxFRZ%*#nZIN)g!+5fJ3fBz*wTg6EdwJeMe&%}DGU z2(vRYGWEO3o^-$b{@rwuv!Qdl>%7c+7}{@TEgN=IGczg{$16Sl(Lm&$wWE95TVnH3 z-5n8fj){~>5-r0sFJ4f#qBibGa$H#vo1C52w>}DQZBh*!KHE0I0ly*xEs!i%m7P^1arHmR=S^_f0Q(}qk}a{ng|iK?Z8?x|j)jB;bKFYuOr(O% zZM0PBI0RHnu_w8Nl-MI`P@-}Sx*GI*#ufa?GGM#br}y``5o#E$?3_Nt~& zP;@4ZFx_Z4h95n?adti<&jPVBmfqxoSk30Rpqqb{JOlEy!h`XXwEllGkeq7I`a==?GY_UEE(n3i`I*c zr`USlSm;sCN8a^6*v)HU zGWdK^icT=0+yR*kT=z(SC*d+hJ`z6%2z1Faqjn!_w1yPw8DnBz0c$+8KpxQ_gt`i~ia@y>?pSLj zEX&RbEqi+vpUP`NG*suftb?=>K`f2-?1-~uvjFlBZbRx%dg<~InT}GW8fP^Q0w-U` zRYH?&8GJQJ7;6F~wH}_zl^HySv8-Y zP_SgS@qK&%{Tp0>10t(~8DbSwI2Hj~B?*I10WW~zz^ z^s3d8;{yomTr85;&HmTEKdDo=BCI>6b20O}*ge;35>aYr`r8{Lsk+ZPA zlZ+9WMC-$W;|W?{rNVpJ_%^3XK`a#s_<}VI*HT#qwU3svs?lNbgif-TZC$;~ z%{sr?^$*ZFUMw!O0wyIP^P%p@pm11BOYS1w3Fi+0bi=1}CR|O8T+$;iAHL;PvwI^~ z4864vf(#{}U1s#Xk%psI>09cYa+<}q8EieL$Itn{_^Bh~cVHY!k3*9Q`<78>M7^`l`=Fi~rNlcbJkw(`&3AT~7oQps&YC)(UnIf(*M zGR9_NN4lhDLnCcx;7$vN5@#G30POud52{XRpEJ}F9ey;_$>DM$rr%FiC;#(t4JrUg zr}Id{?_ydB`K*GiCL}78Zvt$W{zgG@HZ~p}1cdOEH-1uUT;?emOpb zlR+saed_=LnzrLno91rXfp6wlRTi0P8_ypyma{9A3ILk=J3XJ=3>pXQvu&IH-3u-5 zRYZS~ngZ_h`GEq_1t?z^r-e0uoB}>Ab<)Lzt+s7eX)}e$PI^R9YXHX#GmuLkDChzS z3}*d(+W2?A-X8LJjMZ*V*8l8QR~Qk|{yk%j3{UXW%6&hODWU~Ns2R&ExO?f=m@K~6 z0j83jcp9b%-}Pf^E~h8eu$Qow|C9@Nsvg;C^LXW5b!QNLYL4|$eFPsr(tD=F@-@RdGEqbU3KX}Q^huf=K1`*GXmj<16Cnd zGIGBT%fwuSFG-*nwtw!-*Dgch)?I(e3=XVDvxADi*(K3J6v_F+h`#-BY+jt4fW>u? zq}8{a3xppguh2~tngWXMmb&^;k&kn^N&h0$DmWC-+z@$vcwZxw>=_R4-a@#qn zG1i}oX|b(HstQjBF;f?()teMW{9A2bBlk~d|HiGkd5k%i$Ih3lMRb`rC3ZTg)Fs`4 z63~8Cp5?E`>_nWdpt{3bjI=_v@(7?G-yRjas+hDtCYeEyJ_`uO`y=KAN~_1?_d-@dQ5l2 zpNvW{iJ2o5YznUuwEa)UfkFqz-&Km|f}2T#temx3aTQMI^EoByf*r30JlS)mg=Ryx zk!m%%8d}(*3^%l-!n`WjhHh9YjH=+y(}d&bj4`rMziP-i2*58o|0Bqu30UHK2F?J= S+Z#WjnQ68>c9r6Q0001u=+FiL literal 0 HcmV?d00001 diff --git a/bsp/phytium/libraries/standalone/doc/fig/load_image.png b/bsp/phytium/libraries/standalone/doc/fig/load_image.png new file mode 100644 index 0000000000000000000000000000000000000000..83a6ee547034620f829bb5d9092ea2a706c5ebaf GIT binary patch literal 39950 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 }r$RYu8}n zr_6UoB{|38Te&6>hljK;jfgI^$s4m#)#B;Im_^q8T%q%a^mE&L)+*mFZ8lj_(x98~ z8~{j}o<+QSne`=r5TsZ2QY05Ir=PUMjh%^~Gd8)wom%H`l}Ak_bFeusJGq=%ZgdS)?I@u8}%tgJ~rBvp&o=q)-`ksOs`{nT-7^!&K<-TzbkUlXR|iX?4^P~67?FOSCkz`Jw(YCr2B)tZ|4;wIVNzk= zaWz`m$neSF1YSQ@eS%w~3KhILSUr{hIT<2o=a>r_xU`@{?>SF(f>Fn+ri48f^|eZ> zRXZe8C1bKfC#d|RK(e5g;asTgER+76T>LE?o~3LD@Z&*P-YVOQ_Nh9BuX3ew4zEUu z=xTm0ktbop9$T*h@kpY~p2be0e<~n?U{!HQ4`~eJK-f{&zQlr%HdC=cHD{w85c2Y% z+yd(e7b!cL#GCaJ{MNm?wu4`Crj(lrr!l(BODynl5Tshp`tgNzQIXPQ~I)=NUq!h5qJ7)Hi#K*;?KF8Kj%0HuHUl zyo|~HjrbaVQ}CPo_cEsH6YhPlk&hzJA%zSo(Fe_%DU@<{*`$K<6E8tcWy&DpxC>Zr zj*=iKh(U{hL_y9jWwpXkvI^8#ms?R@@0S2>F>OtW+O zg5i39sN@@eI+qHvfhrSk9>q*cgx z2)4%!S|K&q9?4=54gZo#LQ@Va{dv|9AD$Y=@)#r_S2%aW8z@cSQef!v+e$ z69$gV6Q$~{M#MVGJN>BPzO+;V({;%8Zg>4OWn-`&XAG1qxoS2IpxCEG$hrI#B)j)3 zDz)0DhAa;9*x%dS32f7=4ePD0+Ck{P56UVAX~fnU@`)WyAq3D%xb_%j|0NMe)UD4DBn5-uC%bS9WXA z@tZjJukH7O%<+xzFX{xX2j(qY7N7G8`mERpPIWjVc4PRL-33)WrJksh3xax%Ol+L6 z&F(#c)>M6PfdY-qjXPlF

1Nm=-iAt5p@<2Gr(?x7qe{G*%*IfZ%)s{|A4S zuQ!dF-0^qgD~QkEte8gtp5DDJQtR@jsCYe$C9(yl(P&b0@l2U7JYy|9t?MquC+AG- zwm!36>9n9U1M%zy=62N2>A-k#1wER|CRH8#yB7bTewnL%?A(~V2A5!_7}KlGkx-jK z1)<*E6Y z!-Sv?TUcH9um-*Eu1Qx1O9)pY*Vr99srZ32KUvj>#0wm9nV#9Z6xLO(40vo0ssW|^ z6C}H+D4YeFoYKfLN*pWDqbsx@xBhCEpoP2RO4`Wvmpsba-lzh~5VRpulLi%C7c1j9 zRgB(8<4PDWvGiK9?DZ};Gx}jIkbp4#4){TzMZ=ew%A4i1e?+Lp4ETjct7nn4_xvaR zq&v$0Og3>-GV(N_g;&E*TG_ld&}!HyxJD^d-kV*pPsH?A^|zfCSO#}|ir z?E+}p4=>kzEyhhHF7^VUIS`)KJ3vM8>a_T}$JQXR|KS++;(4(*P@Q>%)V9WXtbDCF zk3YVb*GFHHw?#wEho|A&s1EmtWcryrKIQ*EW6gP>#5ZX>y@e=_OvJu&3pVU{tN4*$ zOP@$G;JTd~7pd=#ZiK;pw>HaAqbnjP&HP?k<7m00T&txh7B*~Zulk)0-4Lh0fkE}i zd*ORr@TN#9bwTiYrn^3IM@nUpj}o0z=3}!W{iXoBHM8C*-v2CVu=%H92X5$~?{G4) zXZ{JvzE?%KnN(VQ`g5RkB4PfGAeDhasnpOi$()c8duKwC8}Q0g z-ySfHIPVd=H7|VPHGPh=EvODxc+yyWQ`Z7Py_QL1ru;K&S8$o}$q-<5pl5v~ld&L` zV2W>ah?#@ct!CfnUTiH+bc=CzIPmo8XP>fn2~+8E)8<4}h;S%O5$n+A>$HlCt)0#nQl4^ppDcbW03scoiD>O8WZ$ap9U6G) zijwjedpzo-R>CRufy3#51ND75J6FYG{5CeyS`ph}vOp05ga*1TT~~CmK@?u2lz}W@ z<(J(Hcuwgjw9L5sTRQ!BW$@oCKGvsERnFS@1GVWfs2vJj0gR5B2%~2DL-9!&x^Iaq z7f&*3B9RbGS#%)8U$(VKef6YgQ=*E|&n9m?aa>X6TEHGx8=!;hb|p13NNi3PHA^X6 zT!J%Iyk?zMeIc!XX5ceN^1>L!xr4;RqFG0hc7F;-dAPPEQ-GPJZOul=LtvCCQDSm> z#3z(LW{9y7BzwFOA2wrh9;2>Jz%Jr9wHsW83D@HN!q3b&GPs^UVPk(wR^ncoX;+H@ zjJyiy>829NXWRR!q;>CDBefUph_4L*i?&lIfJ- znT_Dxtag;n1?C6BE~H)hm=}Pw1{ckgOOeK!;~BwABs=Cc#?5<3y!C<|N@8xFfV~Ia z(3wq#POOvA+K28pZpXuxP)~dl&lqF8o_TF+OYu$l(ur z3m&tMskFK?s1u(j&dvI3wuT}s{0XHx7O4d`*a3auljqmN$(%0y{LVj=AmNKb5cbxI z+8)v{>wfWMg^^x|9#-a@T+Yb`(+$|*xX?hFbakx;UPyJ5i7nL4%6(vk0hd9u1r9B* z&pNePRJ>JaiE+)z25zCpKsA`@x@JljRn~PUFMbfIn{_9~I1-p=X^v%+ZKq-;*H4M= z@3;h1oeL0BT47iG7kA{a2FeTk-}hI2#lhE+=(4deDo}B-PBHmT4NJ7ByTDvckBE%P zwIxKVd_;=UOUpltH7Eu_25^+WNY?c~QwMb~QA1vcj9~~5MX@e10t^ zSC}u-=v$YTB_1!TIQOBh+`yEm-JW5NZykqe`OLP+Ox z9dp{>fm!|_leB&SCeNE49hmS|;(b&asrc*W_0MqQl#8wDU0VQFyfWM``f=pc2Oh@z zFUcr;vrr1uxXabqmahsLdg2m7-;FXNcxa|m-zn>W+(gZ^=&vy-DeI-wY$o;TrzS>h zgdE+f$?#@p!sZ)n$FT!eVP&8Ok2?+WYP8TNH>$|k|- ze`7P0ug%#dI;q*Hq}DXW;=zi8^`J2_p(}p2dqp&GFG!C4>I5d4hXqCn3R;Fof1+q& ziu(Lv)yMjt#Ql4Rod6|G{Yw+!sFF@ghU zt7$%A_N~{2s=k?M`TS$C97q&9P-D`qDpD1-TLseNT^kk~DrB_Lab%V`n@kJNaMsuc zEb|RO$sQ4ppI*+hY9N@7 zdV5FHjPLY9hMuxrj~nM_KJb$1$P|hVEt5>~>4Ag7;41#U7Ho6Fy5@xy?N?azndJvY zbtc8u_8}M9K%}733gUk&&3_!1IvTsX?l-c>Av?w=vo*vfkT**gbBnj*O|2it9#NWF zjSFyS=unnE){~g&gG%P~m36#=UtR1iu6(*&qXR>_nI_BMQBS2i-Fi{XehR$(u20c! zqL07HE$r9RT-8EB%HIwLNwb7`0F~}~Eyp*zIpK~2r2cCfYB;;_X>i1L7JwV%5L#V# zzFv*Bl;0Zf%p5%+Ijj7THcM?}z>K$0ra#%14?H(f$3`W8?sdx@ZZL>V0LgI)sJYkl zT)tY>mh8=_8EXZet$YJgdxUinvNTaHy&l_JA*E)Yys@CZa4R+WI0Ctghtq0$sNB~RDQcLbfEBwGFD(4S&PQ0gJ)!M z&VCDDm8Eh7svLaryEigCw!uaMHnqQ68NoVmE#e$)IooJohKBMtz(;Gi(3hT~ zk*sxh-y@4FxOcKKq#KvKl~BcmEDDfQo2gmTD(#LX_~zN_Eq)8Y7QRclmpq!jB#`=+ zd}T%**gQJ+??3k8=&?d~e((LQ70A648WKAcv@xCpOIqrGNvL}jl_y-*W@2H|l5AJ= zo;&GAh~)2B(q+5jX`BCeQcN?#sui*7YEj$!X=Z_dWE^XWGu)%cnBp|!)rJ%{x!N$}wRjQF@N+zgJt1~HNY`)-%6J1tc@g2C`o7Q^@%6s*x}n2|8$P zo-YQ9_h4z1YhXw+reli$wO%{2+2m4gBqa39~d}Z?&+TA{zJb2+kKTF*b5pS zMZ^nd_pHYbr^Slz%#%0Or^m4A&)%X4Rzkta*MD%i;@{zFQ+S~|Bhw^B?+y&?I?HSn zZMzuQb|-Ksn6|Jx`j`RcNJ2uO@8z}c1XPn@%AaqpH4wYqV~m^NX8bG3)=(&HzsVJI zTYpsYc1W3K8ZG{TZOq-y>-4L5$Y8jC!&Z-A1ZMU(Dd{8;Ib{_xoNXWVDuTT%@pekc8)H zB0k*`b@2isC=^~wtQh#|>W|4Vp9!whlw%GCm5yEkn-1XOC*}($yMsei7so+|V{pmqYm3&?6@e;;)GsMh?h3W8XQ%R|D8h>1HUO8zg zsHu2XL;awrO%n}kYeE{3m4;6&63N@)cl_x#%|Nn@^%Ybw|AZutjW?T4=y)+0daHA; zQkQpci}2u{A;Rxu35r{+`_yH^Mh^qs;Ux`X1F^mKARDns3UQ2U(Qocu2pngSkTLPL^N9%jhPXn61UpYo-=e+ zbo8Bc%)*QfwTVtmRDC{4qIv^AR2an1J`U6Yi(CHB>^2QoEJO#yKzBDH^78%M4y(*V zgGesJQ9PuFg}Fr_2`0DR9jY;mV#iO#_%# z#clmL$52$Mo(&=VD;Bw5;0sm76Spkb%N{$x%%;in9s##-a-v=RGs3DAr)|~#4P*vc z5eTZ#nfBu+!Scj^_PD$p-f|*i<~U*qvx2uw^C)ACUs#O(ni1AvLg7xC)8G z+2C5-o;Om_uDi|*L$mTw5ZK(Y1Vim$>H4JdPbZyut3JHNdj7^@Wn{TK%ibv(SLe{7 zfRjFvc2J4pwP@!KY_6!}MK;~@m z$YM{L=mTp!5nJ$9Ja9K0-w;x=bULQocDa##|A%wpFcr+;-5fZ;IJ5}QlEi@ik1A$( z6X>f5dqY*pqHt>13z~ejX@zo;c9jrV8*KMOafoj@1JSS?>#$-9uqE3~fgxb~Bx$IV zbr`Gv^QJTbw9%0=32I6-v|WmI;Y7kE3&qf<_A+gFU;NQxeDCt5nTi@85n_a^XRvCM zT7^#eH%pSj+ZUA0Dv2uEl#Uu|jPeEnJuh9k3pa8TJEvXB(%r;GZHiZVCEUoj@t3mq ztN>ZAEYzP3q5Vc5I~ro>Ly1W_LFEwsQDzLapo#U+fu&{;Z{ToJ&uT}tx^UsFPH;h>^3}v*7_GBD^Ccx7%`vJ z26W_w)=B(qS^~04bRu80*M@-wGcrX8v~GK2=5w4+vt!8RUsMR|ibDMd9-x;4U#_*s z0ICu@kjw4mVSXbJZ^ufYt;xS`quUP;VM{G8vk zVsO<`Fxm*APsRzIAGO9rx;b}(i7bJy)sKWjOP}xot$Xg~P;#=L4-6!VUB*Eycc*2G zxZtXlHXULHohZ&Hj!A1ty3FIsgZMzXn$~b?r=+1JLiA`HlQX;Ns5W3tVcLnp5;#w%*vjA!a~W zEInjC`^7+(S6{&M6bL)p<;QT65fyF%%$D6qwSy^ET*o7eRyWlV?9vVTK#2GYsre&_;hh(gbv|A`$=NUtJn1K4Dv}xml>$ zSBy0Eof;EJ*YXS)9fvv;f^pkzXf*7_97O=9gzOh4)+pht{pzVq&(bq&@vVwK54=Js zSK(Mo2wd&NA#=@S*l483Z_?!mw873+J7W<6Ti(Qj#WkYT;`NAl8Pf@Q5eAh9We-8> zEEe!s`-F9~snCjT5hdOHRwKj~@NcvJrCu;&)3_lRV4YKxC=Qf>L|7Un_QSmRLg~TG zB$|lLVLd5hbXJ;~Z7@0y*y@eF+QiQmGJKr!fHOthORO(-2y`aqNb$d2$Iit&h}%+H z{4$2*S@oH-y3@UW2R(LJLhk8;f{Q$m_%fG$=PZ05A(n#eW%3N%&xvNR)m`QyZ~ESp z`JD0#(@4Zd=xh&Ts9MpJm2B@pdvyo?;<0QHz~|?h@t&i>j2MPW;d+JZHvoUIIjB8l zUUKN0{a=1Gl)Oul-l$-MkpCn_#YW!uZeagL3e}#~L|-}C#Iygq$4z>+ zlE^hcddS4&-9Q_MB}vVJCUZ`F?u-ur2q2` zXKTBILyn(=(1IbTMSykDN@Qec7P{@yA!Ut-7Y985FwdoM#5V3IoX5?{OUwjblI_3! zYuNTGF-f2r;8tX`%+)d{nl0E8u1_f+OdMEOWZ-ZL3F~fJ<^wYjK{I(O3+)?%#+%q* z#l#Dj=f@+9d2G;cegvqSqlI#WP6^R{j|*9dq?$ZEnRtUlX&}qp&pVL0j3NM~Enopz zY-&v&pgVcNYR@H`bk+T#r6X4jRg|k>|hW`GkJ^1QGM6n2v_Z z!Vjq)&tYI4(;&EnUJ@ir zh}_*o))RfER>MtaaNM!$BaTjLtFI27e}tj4g|b|Xzm^-!P7LaEz?FGUHxGO{?qVcH z-By`}n+8-4GPOskR(K1TQtyjQ$z_}hNr7DZ2wta{SGo4UWkwkSO-NS!?9^#HEi4WQks*GOJ5U>&$|JL0n!efG9LXt!B&I^^jMJq`#hr7S;$u zvxaw0=2W=gwp&w1A&;#DEG6yeu?kRdex(>zBM9u=2Hl5c>Z-vyP}(Zuw7fO>gs4p4yA(YFg7y(C~QG9wnaz@fip4&fg>i?3Gcft>WM-<^k8cFe5WwPO`|Wg&kh^|##A+R@7+_3O3uu>3htl?`X!vngd8 zSc{2p24SnVX{N$RL@cfa8IOGmf_1auj*8gFv1FRY`b9{f59yIRAUN)I>FIZe>JUSy zjSzX?ZgqucW1f@6K6yq$`3JT7@94sJNHC_0lRs749+oJSndEQgZ2N|pgPa@<8!;f8 z+qz?-K{j;{60mU58H*oSoNlE$kzXSR4S|-Ra%4}?hGN2<@dvpjOSoI@Jmu!f5y*0| zWuM-Nu$Y2AXFn?Vv7F|GWCL&)&OI?lQ^c*jhl49G-F89pYrV1;T)Ca+SKOM|i z7m(|B{8Eik<0M6dV56Tw%JdKf{#|k#DG^Z`K^heI2k%~=aG!fw|L!lDJ%NFp2wIQXo|AJ#CRLh+M= zl`7hXvM_p(JU?=XKqbF$N}E?-tna=#`ii>Pp1==K_>U_q$%I8T^G8;D_@`0vug+vAhU5)J1r7tWfOmVAM*#dY8F; zye|cEk$u{-H}L&qLLt&7-x#1qt?{FK+jgh@HxZ5&1U^sWxC4j3;62k4>=Fu$3K6=D zurLI{bS=R|(=8**4|dB{UD-A!|Ejm)9*p-ue(wNcEuFO=5ZNgp`iK=d?mz zl4m8;U}l%)!rb~|)7swe{nvsM5U|r8GWe=SSxRQ^3+sD}kc2=`lFWrU^IT`0ldncZT+uT(hraUF`teBw7jREVuesUptdm zSgQar~l5Wh14RQn{ql=ZQThZtH(aInhPw6x6PSO8}D2C5Ovpi79;hQl;x0nyMu zYmjEgGbHRz%-9_I(IH2U7obb^7sz}tak>2ws0#ypdC>877Co_DEQnk@Z{FPq%r7DQ56yw{uf z%i4Q4X8B0(!j90~NhgtQ+dmXAygjL_x^S_(p!;x>SO8jF4^eL?GsU6bTHX=>3`y2% z0koQ)dz%}xH%jLg?rNJ+y9-*3Q8q`s`7c%0l!Zs;p5BAjOAX%p4=Y!y8j?lg^!=Bw zjoQ{;tkwK}oo9V1v><^N$Ut_Bt$CpEd;qSkJ_@LsGzzyWWr{^wL$m3&OtvuT!AwYd zATP3^uh#=*70vVmAN2&5Vb^r#8#f2S>qY$cCl{M+H^@+43WfXGg%URx7I60X5VN&m zpWazZvd`5@nrkZ?H?Ch*Ci@stXaPqSO#qSq90ht;*{467IWoOxyPc7=lrT3ma^7|& zHisNO>w(=4R&5qh+hfm>;}@}C#a02OS!#s}kajwxbKJTF4JfF0n?9~}le3dcQZ03Y z_^npRms@=Fhj$(GWqU{WD`c&(clTw>E5@=;D^qsbrGn$Wy&f|Kue+GkO8bK9j>p_F z`BAH7=%}GvBlK+aH&whGGcv7y8y2rv$N5+LK7KPVOcDp!o#V$E*bU{nX@HYE!(Ypu zui?d6L8Nt@=c?h%^sTFLFa#R5^h)&rD{`DyP1UBMc|Lzf`(^MgiG0~>tCm|-{_x%TKXkoQo1nqAZke`i+h(P0+qP}9(zb2e zwr$&Xo?2_~e){x9|A7y25qRgABWO}n!#y0EEM*EpJwCF+qyMuaps8T|1NsvLM2wJ z!;_SEXaY6AU%l8I{Y4zwVjCigW5E$t&*nK*2${8OJl|gallj9*dk&a@^PYs?RO|~! z_7^Y=>6hGewfkrSI)w zEXLH+ZcDhO=$xzj9>s8TgC6(LMT zM)BQW*Tm-zJ*FC)Wp9JP!43ilvJkYH;+o0^A{T%n9F3Hz ze#K@V3dI;2JV~)NW_Y3aHiw2dav~2K)C3f`{c$(3_4vRpK|AxB__*aGbh{34dvi%f zwC`X*Pd}Ql5~IlKFmK8l5RE949befYZ0`R{9sz%^ zD4nBZE9S30SjIsm`H1g0MfIukSdC88j$SuNx^KbxI0yip1;T}vUcngTZ@Ux3VZN>9 z2rpyKn%7M-gs)I3hX|E(?pY_sXbNaianHATwM~}r?0)9M53>m}{m$ogn#V~X3xO>; zhXG!gugkiN1k!f6GW)V>_=qwMf2cC~i~$S6pJVxwvb%-eSm_te9`!79Y&*fbl4P`y z6&r(jf6iFlUxfuBK%m@DcZjN**ABX6M#FXG`-I`@^wf675Vn`I%25bJ&u22b>L^YH z6lCfL>MV|z%H|#5;u0uk*aj9?_raSpk9guR(CVkR&>-_frJ}pYCt!50W?++Nxav5a zGPihw_;)5|{Bv6?hhI@YjUpmP<1urlkmT1sp=E#P74#9)035MHr=Km4J~7UR))d4fPg)!~ZO4$irnwj(&OK4wlK1_TOnq!bwOBR?HqWkgi@%M7bkAd|1L zt~PnNRF^yiMWqP5{#{!sHpyGMSkohJGZmjF+Lkk8S2HvNqcH2%VNJ!gOXQR}LL6tp zr&JYU-ELiRY$c4=W-$#D?h6Td1983Vb&nXy!h6WcngMpUwH(k1mvu7dHsKNAUA9{2 zs2VPIx3#dEWiX-mxV=(LukWW#PZnstt_ZGV)JNCn^(%$a1O6Q9GwIx2bU>>?D=Zhc z0&v41cLcP}AeXti_K(g|6fsl)ap;Rz#X>H1|K-{;+#*K*FDQJbNi+pwu7jdj9LbGBY={R;`bRNtUu(1X%0Av2!MPjo%T8%@!#h`0hqP{blIl; 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 H|Lpz?)_7mI literal 0 HcmV?d00001 diff --git a/bsp/phytium/libraries/standalone/doc/fig/setup_aarch64_dev.png b/bsp/phytium/libraries/standalone/doc/fig/setup_aarch64_dev.png new file mode 100644 index 0000000000000000000000000000000000000000..f9775e8520ea3bea6880c90ed6e6ffc33e2016dd GIT binary patch literal 25260 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 b|Dhm|?ELkt@EpeA($g#|B>(&WKd=7>3=RV% literal 0 HcmV?d00001 diff --git a/bsp/phytium/libraries/standalone/doc/fig/setup_win.png b/bsp/phytium/libraries/standalone/doc/fig/setup_win.png new file mode 100644 index 0000000000000000000000000000000000000000..5b287cf7456a05fa1123b5761358b2c0170d2456 GIT binary patch literal 47170 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 am;e9(0000A1dU{pMZaM)>(F&1pa1}K@wAZu literal 0 HcmV?d00001 diff --git a/bsp/phytium/libraries/standalone/doc/fig/setup_x86_dev.png b/bsp/phytium/libraries/standalone/doc/fig/setup_x86_dev.png new file mode 100644 index 0000000000000000000000000000000000000000..f9775e8520ea3bea6880c90ed6e6ffc33e2016dd GIT binary patch literal 25260 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 b|Dhm|?ELkt@EpeA($g#|B>(&WKd=7>3=RV% literal 0 HcmV?d00001 diff --git a/bsp/phytium/libraries/standalone/doc/fig/tftp32_srv.png b/bsp/phytium/libraries/standalone/doc/fig/tftp32_srv.png new file mode 100644 index 0000000000000000000000000000000000000000..6fedb850f4c2e4d7c520e52f4659e022234abd16 GIT binary patch literal 39206 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∨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

)e@9J$;8m3!NHTJXgf=gSViu4v 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 K)6crYfJbozDBS9M5eR+6BX6XP} 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 F008-!3{n69 literal 0 HcmV?d00001 diff --git a/bsp/phytium/libraries/standalone/doc/fig/windows.png b/bsp/phytium/libraries/standalone/doc/fig/windows.png new file mode 100644 index 0000000000000000000000000000000000000000..4389fb08c7e10fe8137c6bebfcf9ff7195912c72 GIT binary patch literal 1310 zcmV+(1>yQqNk&E%1pok7MM6+kP&gn81polhCIFoQD#rlF06vjMok^vnBOxl-D3Guc z2~FP%=^$mXnruyb-{t?c|49FLttWoJC3BzokGQX!f8%{k{%!S^@BsHO{IC7@sBhR0 zTaWf$ra$I-P^Mlw1+2$v=0U~;Olw2H%fS!Eq5_-hamXZ!LFB}aK={78eKQTW zFk&R@wA&5q*{S7@7cfG8>GBk@)PUIE8wE9E`5|l)|3fKe-KfoP>pj3KyxWg)X~rq? z$B4CkU}Y&24USin-0Qe65!9SZ1Bk?u%;~d|FlIZ%>5aU@qRscMXHBVPX6xZSs-rLL zzGyMH6mVx|j6xJKnkVwJDQeaIEm_&pbBl?4a^%FlcmM$Y{xiq{j;H~$J3q+(ZJN|* zs9&AA`PrK*2<{db_!#Kxxo?)jqBY3;tkn)X=lgUT3Vjp7B9P#x66oB@ zAcy5;_9pWeizp{nU}9iwFYNw`vD4d*ax)*|Sv?Wb=J#3TJE}O?FIeAP2+~+Zuz{_a zcf5PPu=XeMjXyK>nt|spox%$GQ4%Cz??f$VcGTVvdZsqZ%q{RE7h~y)_Nu4KVT1Jn z9r97Dy}50C83Rx1@@x(}i=My6{FlW=ne{~F%s6!qTr-w3q%OvW0XmnMn*A8SSxE@G z^DyFv5DosS5eX^m;ya@Nqp$y44LW-_kBp}8{?~;2{R{H|@8dx1Q5Vx9yfxCtPsQvH zN{%Hed0->b|6v`@7`A&>zP0Jke_&Ym>CCNxr4e_l$C@V<79DCAc|oDojpUF`n7yL zGV>f4VNo0O;F#L|rMmDZ>??TdDqPqym017m#<`ab}C3=J$YqJGxZYOwk*b{ZN}3Qhbv$GkW?m2kKUE z{Af1$;yTukvrs;j2cmuy0HtiJL&2!fIR04BPw{{cIxDalg)@yiRaXV~y1>8|LHo

h0{k@p}7&T$1vM9<9?@45aTR2Y-9vAv(#%N4c)W)j~5~WEH z000LxZbilV=VGM)auGe##&R&>1xq#>95_Zxj($X_d2Q?d-&~>_qy05q7f8SH@HA=l UW8Uo!sz?g)bfF&|C;$Ke07gBN82|tP literal 0 HcmV?d00001 diff --git a/bsp/phytium/libraries/standalone/doc/fig/wsl_l_v.png b/bsp/phytium/libraries/standalone/doc/fig/wsl_l_v.png new file mode 100644 index 0000000000000000000000000000000000000000..a6def1b048b766787708eaa7f3609558984e096b GIT binary patch literal 12553 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 M>FVdQ&MBb@00k0Tf&c&j literal 0 HcmV?d00001 diff --git a/bsp/phytium/libraries/standalone/doc/fig/wsl_teiminal.png b/bsp/phytium/libraries/standalone/doc/fig/wsl_teiminal.png new file mode 100644 index 0000000000000000000000000000000000000000..740ced10c9d8d87dceeb49e6bc574b19e427bd97 GIT binary patch literal 208242 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≷5Zi2kkgRW)cZ;U6LVH#L)Svss_j! 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%0Dw>2@`5 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 + +# interrupt + +## 1概述 + +此处提到的中断,主要是面向与Armv8 架构中异步异常IRQ 这个中断这个概念进行对齐,本模块主要为开发者提供软件层面管理中断相关接口,具体实现了以下这些特性: +1.基于中断id 的开关功能 +2.中断生命周期中需要使用到的,模式切换函数。(sdk 自动调用,一般不需要开发者操作) +3.核间中断触发接口 +4.中断优先级相关接口(优先级设置、优先级掩码、优先级抢占分组设置) +5.提供两种角色选择初始化中断 + +## 2驱动功能 + +驱动组成由以下所示 +. +├── finterrupt.c +└── finterrupt.h + +为用户提供两种初始化中断的方式: +1.不使用默认初始化的方式,在参数配置项选择“Not use default interrupt configuration”,参数配置如下: + +![](./figs/NotUseDefaultConfig.png) + +用户在这种模式下手动编写初始化代码,具体可以参考 /baremetal/example/peripheral/gic/fgic_test 下例程 + +2.使用默认初始化的方式,此时默认以CORE0 作为主核,默认会初始化中断驱动中的所有组件,非0核 将只会初始化与多核特性相关的部分组件。具体配置如下: + +![](./figs/UseDefaultConfig.png) + +## 3数据结构 + +``` +typedef void (*IrqHandler)(s32 vector, void *param); /* IRQ 中断回调函数的类型 */ + +``` + +``` +struct IrqDesc +{ + IrqHandler handler; /* IRQ 中断回调函数的指针 */ + void *param; /* IRQ 中断回调函数的变量 */ +}; +``` + + +``` +#define INTERRUPT_CPU_ALL_SELECT 0xffffffffffffffffULL /* 当进行核间,发送给所有核心时需要用到的参数 */ +#define INTERRUPT_CPU_TARGET_ALL_SET 0xffffffffUL /* 设置SPI 中断亲和度时,此值默认SPI 中断发送给所有人 */ + +#define IRQ_MODE_TRIG_LEVEL (0x00) /* Trigger: level triggered interrupt */ +#define IRQ_MODE_TRIG_EDGE (0x01) /* Trigger: edge triggered interrupt */ +``` + +``` +typedef enum +{ + INTERRUPT_ROLE_MASTER = 0 , /* 作为主核模式进行中断初始化,会初始化中断驱动中的所有组件 */ + INTERRUPT_ROLE_SLAVE, /* 作为从核模式进行中断初始化,会初始化与多核特性相关的部分组件 */ +}INTERRUPT_ROLE_SELECT; /* 此枚举应用于手动初始化中断的接口中 */ +``` + +``` +#define IRQ_GROUP_PRIORITY_3 3 /* group priority valid mask is bit[7:3],subpriority valid mask is bit[4:0] */ +#define IRQ_GROUP_PRIORITY_4 4 /* group priority valid mask is bit[7:4],subpriority valid mask is bit[3:0] */ +#define IRQ_GROUP_PRIORITY_5 5 /* group priority valid mask is bit[7:5],subpriority valid mask is bit[4:0] */ +#define IRQ_GROUP_PRIORITY_6 6 /* group priority valid mask is bit[7:6],subpriority valid mask is bit[5:0] */ +#define IRQ_GROUP_PRIORITY_7 7 /* group priority valid mask is bit[7],subpriority valid mask is bit[6:0] */ +``` + +IRQ_GROUP_PRIORITY_* 用于定义组优先级的有效位。 当存在抢占中断时,group priority 为抢占优先级,subpriority 为子优先级,如果某个中断的组优先级的值比当前正在运行中断的组优先级要小,则此中断将会抢占当前运行的优先级 。 + +``` +#define IRQ_PRIORITY_VALUE_0 0 +#define IRQ_PRIORITY_VALUE_1 0x10 +#define IRQ_PRIORITY_VALUE_2 0x20 +#define IRQ_PRIORITY_VALUE_3 0x30 +#define IRQ_PRIORITY_VALUE_4 0x40 +#define IRQ_PRIORITY_VALUE_5 0x50 +#define IRQ_PRIORITY_VALUE_6 0x60 +#define IRQ_PRIORITY_VALUE_7 0x70 +#define IRQ_PRIORITY_VALUE_8 0x80 +#define IRQ_PRIORITY_VALUE_9 0x90 +#define IRQ_PRIORITY_VALUE_10 0xa0 +#define IRQ_PRIORITY_VALUE_11 0xb0 +#define IRQ_PRIORITY_VALUE_12 0xc0 +#define IRQ_PRIORITY_VALUE_13 0xe0 +#define IRQ_PRIORITY_VALUE_14 0xf0 +``` + +IRQ_PRIORITY_VALUE_* 中断优先级一共支持以上这16个挡位,优先级的值越低优先级越高。 + +``` +#define IRQ_PRIORITY_MASK_0 0 +#define IRQ_PRIORITY_MASK_1 0x10 +#define IRQ_PRIORITY_MASK_2 0x20 +#define IRQ_PRIORITY_MASK_3 0x30 +#define IRQ_PRIORITY_MASK_4 0x40 +#define IRQ_PRIORITY_MASK_5 0x50 +#define IRQ_PRIORITY_MASK_6 0x60 +#define IRQ_PRIORITY_MASK_7 0x70 +#define IRQ_PRIORITY_MASK_8 0x80 +#define IRQ_PRIORITY_MASK_9 0x90 +#define IRQ_PRIORITY_MASK_10 0xa0 +#define IRQ_PRIORITY_MASK_11 0xb0 +#define IRQ_PRIORITY_MASK_12 0xc0 +#define IRQ_PRIORITY_MASK_13 0xe0 +#define IRQ_PRIORITY_MASK_14 0xf0 +``` + +IRQ_PRIORITY_MASK_* 中断优先级掩码一共支持以上这16个挡位,当设置掩码之后,中断优先级的值必须比此值小,才能被CPU进行响应 + +## 4错误码定义 + +#define FINT_SET_TARGET_ERR /* 涉及到CPU id 的配置时,CPU 不具有此ID 信息 */ +#define FINT_INT_NUM_NOT_FIT /* 使用中断号不符合当前实际情况 */ + + +## 5应用示例 +/baremetal/example/peripheral/gic/fgic_test gic与interrupt 特性例程 + + +## 6API使用步骤 + +1. 初始化中断模块,根据当前使用此核心角色的定位(主核、从核),进行初始化 + +``` +InterruptInit(&interrupt_instance,INTERRUPT_DRV_INTS_ID,INTERRUPT_ROLE_MASTER); +``` + +2. 全局设置组优先级 ,此接口影响中断嵌套过程中的抢占优先级有效位 + +``` +InterruptSetPriorityGroupBits(IRQ_GROUP_PRIORITY_*); +``` + +3. 全局设置中断优先级掩码,此参数设置之后,CPU响应中断优先级的值必须比此值小 + +``` +InterruptSetPriorityMask(IRQ_PRIORITY_MASK_*); +``` + +4. 设置具体中断的优先级。此优先级由抢占优先级与子优先级组成,具体优先级的划分由 InterruptSetPriorityGroupBits 决定。 + +``` +InterruptSetPriority(INT_NUM,priority); +``` + +5. 设置中断路由至特定CPU + +``` +InterruptSetTargetCpus(INT_NUM,CPU_ID) +``` + +6. 中断回调函数注册,当中断事件出现时,回调此注册函数 + +``` +InterruptInstall(INT_NUM,int_handler,int_args,"int_name") +``` + +7. 使能具体中断号的中断 + +``` +InterruptUmask(INT_NUM) +``` + +8. 如果是使用核间中断,使用以下接口进行触发 + +``` +InterruptCoreInterSend(INT_NUM,CPU_MASK) +``` + +9. 关闭具体中断号的中断 + +``` +InterruptMask(INT_NUM) +``` + +## 7API介绍 + +### 1. InterruptInit + + +``` +void InterruptInit(InterruptDrvType * int_driver_p,u32 instance_id,INTERRUPT_ROLE_SELECT role_select) +``` + +#### 介绍 +初始化中断模块的接口函数 + +#### 参数 +- InterruptDrvType * int_driver_p : 指向中断驱动实例的指针 +- u32 instance_id: 驱动实例的标号 +- INTERRUPT_ROLE_SELECT role_select :初始化中断接口时的角色选择。INTERRUPT_ROLE_MASTER 作为主核角色,INTERRUPT_ROLE_SLAVE 作为从核角色。具体特点参照数据结构中的描述 + +#### 返回 +无 + +### 2. InterruptMask + +``` +void InterruptMask(int int_id) +``` + +#### 介绍 +基于中断ID关闭对应的中断 + +#### 参数 +- int int_id :中断的id 编号 + +#### 返回 +无 + +### 3. InterruptUmask + +``` +void InterruptUmask(int int_id) +``` + +#### 介绍 +基于中断ID开启对应的中断 + +#### 参数 +- int int_id :中断的id 编号 + +#### 返回 +无 + +### 4. InterruptSetTargetCpus + +``` +FError InterruptSetTargetCpus(int int_id,u32 cpu_id) +``` + +#### 介绍 +将中断路由给特定的CPU,或者路由给所有的CPU + +#### 参数 +- int int_id :中断的id 编号 ,中断优先级范围为 32-1019 +- u32 cpu_id : 需要路由给CPU的编号,如果值为INTERRUPT_CPU_TARGET_ALL_SET 则路由给芯片中所有可以接收此中断的CPU + +#### 返回 +FError FINT_SUCCESS:设置成功,FINT_INT_NUM_NOT_FIT:使用中断号不符合当前实际情况 ,FINT_SET_TARGET_ERR: 涉及到CPU id 的配置时,CPU 不具有此ID 信息 + +### 5. InterruptGetTargetCpus + +``` +FError InterruptGetTargetCpus(int int_id,u32 *cpu_p) +``` + +#### 介绍 +基于中断ID 获取中断的路由信息 + +#### 参数 +- int int_id :中断的id 编号 +- u32 *cpu_p : 它的值为:需要路由给CPU的编号,如果值为INTERRUPT_CPU_TARGET_ALL_SET 则路由给芯片中所有可以接收此中断的CPU + +#### 返回 +FError FINT_SUCCESS:设置成功,FINT_INT_NUM_NOT_FIT:使用中断号不符合当前实际情况 ,FINT_SET_TARGET_ERR: 涉及到CPU id 的配置时,CPU 不具有此ID 信息 + +### 6. InterruptSetTrigerMode + +``` +void InterruptSetTrigerMode(int int_id, unsigned int mode) +``` + +#### 介绍 +基于中断ID 设置中断的触发方式 + +#### 参数 +- int int_id :中断的id 编号 +- unsigned int mode : IRQ_MODE_TRIG_LEVEL :(0x00) /* Trigger: level triggered interrupt */ +- IRQ_MODE_TRIG_EDGE :(0x01) /* Trigger: edge triggered interrupt */ + +#### 返回 +无 + +### 7. InterruptGetTrigerMode + +``` +unsigned int InterruptGetTrigerMode(int int_id) +``` + +#### 介绍 +基于中断ID 获取中断的触发方式 + +#### 参数 +- int int_id :中断的id 编号 + +#### 返回 +- unsigned int mode : IRQ_MODE_TRIG_LEVEL :(0x00) /* Trigger: level triggered interrupt */ +- IRQ_MODE_TRIG_EDGE :(0x01) /* Trigger: edge triggered interrupt */ + +### 8. InterruptSetPriority + +``` +void InterruptSetPriority(int int_id, unsigned int priority) +``` + +#### 介绍 +基于中断ID 设置中断的触发方式 + +#### 参数 +- int int_id :中断的id 编号 +- unsigned int priority :中断优先级的值 ,采用IRQ_PRIORITY_VALUE_*的值作为输入 + +#### 返回 +无 + +### 9. InterruptGetPriority + +``` + unsigned int InterruptGetPriority(int int_id) +``` + +#### 介绍 +基于中断ID获取中断的触发方式 + +#### 参数 +- int int_id :中断的id 编号 +- unsigned int priority :中断优先级的值 + +#### 返回 +无 + +### 10. InterruptSetPriorityMask + +``` +void InterruptSetPriorityMask(unsigned int priority) +``` + +#### 介绍 +设置中断优先级掩码 + +#### 参数 +- unsigned int priority :中断掩码值,当设置此掩码之后,各个中断优先级的值必须小于此值,才能被CPU 承认,并且转为激活态 。采用IRQ_PRIORITY_MASK_* 参数作为输入 + +#### 返回 +无 + +### 11. InterruptGetPriorityMask + +``` +void InterruptGetPriorityMask(void) +``` + +#### 介绍 +获取中断优先级掩码 + +#### 参数 + +#### 返回 +- unsigned int priority :中断掩码值,当设置此掩码之后,各个中断优先级的值必须小于此值,才能被CPU 承认,并且转为激活态 + +### 12. InterruptSetPriorityGroupBits + +``` +void InterruptSetPriorityGroupBits(unsigned int bits) +``` + +#### 介绍 +设置中断优先级分组位 + +#### 参数 +- unsigned int bits :该字段的值控制如何将8位中断优先级字段拆分为组优先级字段与子优先级字段,采用IRQ_GROUP_PRIORITY_*参数作为输入。 分组关系如下: + * |bits 取值 ----------------0-------1--------2------3-------4------5-------6-------7 + * |组 优先级有效值取值------[---]----[7:1]---[7:2]--[7:3]---[7:4]--[7:5]--[7:6]---[7] + * |子 优先级有效值取值------[---]-----[0]----[1:0]--[2:0]---[3:0]---[4:0]--[5:0]--[6:0] + +#### 返回 +无 + +### 13. InterruptInstall + +``` +IrqHandler InterruptInstall(int int_id, IrqHandler handler,void *param, const char *name) +``` + +#### 介绍 +本函数将自定义的中断回调函数与回调参数注册至对应中断id数据结构中 + +#### 参数 +- int int_id:中断的id 编号 +- IrqHandler handler:中断回调函数 +- void *param:中断回调参数 +- const char *name:中断函数的命名 + +#### 返回 +无 + +### 14. InterruptCoreInterSend + +``` +void InterruptCoreInterSend(int ipi_vector, u64 cpu_mask) +``` + +#### 介绍 +核心间中断触发函数 + +#### 参数 +- int int_id:中断的id 编号 ,中断范围 0~15 +- u64 cpu_mask:cpu_mask表示每一位代表所选CPU,例如,0x3代表core0和CORE1。 + +#### 返回 +无 + +### 15. InterruptEarlyInit + +``` +void InterruptEarlyInit(void) +``` + +#### 介绍 +中断提前初始化函数,此函数一般在汇编代码时被调用,当用户设置默认初始化模式时,本函数将会使用CORE0为主核心并且初始化中断驱动中所有组件,其他CORE为从属核心将初始化中断驱动中必备的组件。 + +#### 参数 +无 + +#### 返回 +无 \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/doc/reference/cpu/mmu.md b/bsp/phytium/libraries/standalone/doc/reference/cpu/mmu.md new file mode 100644 index 0000000000..d7389a32df --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/cpu/mmu.md @@ -0,0 +1,98 @@ + + +# MMU + +## 1. 概述 + +- 虚拟内存系统(VMSA)提供了一套内存管理单元(MMU), 它控制着地址转换、访问权限和内存属性的确定和检查。 + +- 地址转换过程将CPU使用的虚拟地址(VAs)映射到物理内存系统的物理地址(PAs)上。 + +- 在本SDK中 aarch32 state 采用 section 格式的转换表 +- 在本SDK中 aarch64 state 采用 4KB 粒度 格式的转换表 + +## 2. 功能 + +- MMU功能支持用户根据需要修改MMU表的默认内存属性。 + +## 3. 配置方法 + +## 4. 应用示例 + +"baremetal/example/system/amp/libmetal_test" +"baremetal/example/system/amp/openamp" + +## 5. API参考 + + +### 5.1 用户定义 + + +/* 访问权限 */ +``` +#define MT_P_NA_U_NA /* higher Exception level access=NA, user=NA */ +#define MT_P_RW_U_NA /* higher Exception level access=RW, user=NA */ +#define MT_P_RW_U_RO /* higher Exception level access=RW, user=RO */ +#define MT_P_RW_U_RW /* higher Exception level access=RW, user=RW */ +#define MT_P_RO_U_NA /* higher Exception level access=RO, user=NA */ +#define MT_P_RO_U_RO /* higher Exception level access=RO, user=RO */ +``` + +/* 内存属性参数 */ +``` +#define MT_DEVICE_NGNRNE /* Device-nGnRnE , Outer Shareable */ +#define MT_DEVICE_NGNRE /* Device-nGnRE , Outer Shareable */ + +#define MT_NORMAL_WT /* Outer and Inner Write-Through, Read-Allocate No Write-Allocate , Outer Shareable */ + +#define MT_NORMAL_WB_WCN /* Outer and Inner Write-Back, Read-Allocate No Write-Allocate , Outer Shareable */ + +#define MT_NORMAL_NC /* Outer and Inner Non-cacheable , Outer Shareable , Outer Shareable */ +#define MT_NORMAL /* Outer and Inner Write-Back, Read-Allocate Write-Allocate , Outer Shareable , Outer Shareable */ + +``` + +### 5.2 用户API接口 + +- 此函数用于配置转换表中地址对应条目的内存属性与访问权限 + +```c +void FSetTlbAttributes(uintptr addr, fsize_t size, u32 attrib); +``` + + Note: + aarch32 state 采用 section 页表,由此addr 建议采用1MB 地址对齐,size 建议为MB的整数倍。 + aarch64 state 采用 4KB 粒度 页表,由此addr 建议采用4KB 地址对齐,size 建议为4KB的整数倍。 + + Input: + + - addr , 需要为内存区域设置属性与权限的起始地址 + + - size , 内存区域的范围 + + - attrib,指定内存区域的属性 ,内存属性的取值由 访问权限 与 内存属性参数 组成,具体类型参考 ### 5.1 用户定义 + + Return: + + - void diff --git a/bsp/phytium/libraries/standalone/doc/reference/driver/fadc.md b/bsp/phytium/libraries/standalone/doc/reference/driver/fadc.md new file mode 100644 index 0000000000..eaee72661c --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/driver/fadc.md @@ -0,0 +1,327 @@ +# FADC 驱动程序 + +## 1. 概述 + +ADC,Analog-to-Digital Converter的缩写,指模/数转换器或者模拟/数字转换器。是将连续变量的模拟信号转换为离散的数字信号的器件。真实世界的模拟信号,例如温度、压力、声音或者图像等,需要转换成更容易储存、处理和发射的数字形式。模/数转换器可以实现这个功能,典型的模拟数字转换器将模拟信号转换为表示一定比例电压值的数字信号。 + +## 2. 功能 + +adc控制器驱动提供了adc的控制访问方法, +- 初始化adc控制器,配置相关参数,如转换模式,通道使能等 +- 读adc转换数据 +- 转换完成与结果阈值的中断触发 + +驱动相关的源文件包括 +``` +. +├── fadc_g.c +├── fadc_hw.c +├── fadc_hw.h +├── fadc_intr.c +├── fadc_sinit.c +├── fadc.c +└── fadc.h +``` + +## 3. 配置方法 + +以下部分将指导您完成 fadc 驱动的软件配置: + +- 初始化adc控制器 +- 设置adc的中断处理函数 +- 根据adc转换完成状态读取adc转换结果 + +## 4 应用示例 + +### [adc收发数据](../../../baremetal/example/peripheral/adc) + +## 5. API参考 + +### 5.1. 用户数据结构 + +- fadc控制数据 + +```c +typedef struct +{ + FAdcConfig config;/* adc config */ + FAdcConvertConfig convert_config; /* adc convert config */ + FAdcThresholdConfig threshold_config; /* adc channel threshold config */ + u32 is_ready;/* adc init ready flag */ + u16 value[FADC_CHANNEL_NUM]; /* adc value */ + boolean convert_complete[FADC_CHANNEL_NUM]; /*!< Specifies whether the conversion is complete> */ + FAdcIntrEventHandler event_handler[FADC_INTR_EVENT_NUM]; /* event handler for interrupt */ + void *event_param[FADC_INTR_EVENT_NUM]; /* parameters of event handler */ + +}FAdcCtrl; +``` + +- fadc配置数据,FAdcConfig主要是adc控制器id、基地址和中断号,FAdcConvertConfig主要包括用户可配置的参数,包括转换间隔时间、转换模式、通道模式等,FAdcThresholdConfig主要包含具体通道的结果阈值 + +```c +typedef struct +{ + u32 instance_id;/* adc id */ + uintptr base_addr;/* adc control register base address*/ + u32 irq_num;/* adc interrupt number */ + u32 irq_prority;/* adc interrupt priority*/ + const char *instance_name;/* instance name */ + +} FAdcConfig; + +typedef struct +{ + u32 convert_interval; /* convert interval time */ + u32 clk_div; /* clock divider */ + FAdcConvertMode convert_mode;/*!< convert mode */ + FAdcChannelMode channel_mode;/*!< channel mode */ + +} FAdcConvertConfig; + +typedef struct +{ + u16 high_threshold[FADC_CHANNEL_NUM]; /*!< Configures the ADC analog high threshold value. + This parameter must be a 10-bit value. */ + u16 low_threshold[FADC_CHANNEL_NUM]; /*!< Configures the ADC analog low threshold value. + This parameter must be a 10-bit value. */ +} FAdcThresholdConfig; +``` + +- fadc转换模式,单次转换和连续转换 +```c +typedef enum +{ + FADC_CONTINUOUS_CONVERT = 0,/* continuous conversion*/ + FADC_SINGLE_CONVERT = 1, /* single conversion*/ + + FADC_CONVERT_MODE_NUM + +} FAdcCovertMode; +``` + +- fadc通道模式,多通道顺序转换和固定通道转换 +```c +typedef enum +{ + FADC_MULTI_CHANNEL = 0, /* multi channel conversion*/ + FADC_FIXED_CHANNEL = 1, /* fixed channel conversion*/ + + FADC_CHANNEL_MODE_NUM +} FAdcChannelMode; +``` + +- fadc中断事件类型 +```c +typedef enum +{ + FADC_INTR_EVENT_COVFIN = 0, /**< Handler type for convert finish interrupt */ + FADC_INTR_EVENT_DLIMIT = 1, /**< Handler type for low limit interrupt*/ + FADC_INTR_EVENT_ULIMIT = 2, /**< Handler type for high limit interrupt*/ + FADC_INTR_EVENT_ERROR = 3, /**< Handler type for error interrupt*/ + + FADC_INTR_EVENT_NUM +} FAdcIntrEventType; + +``` + +### 5.2 错误码定义 + +- FADC_SUCCESS 执行成功 +- FADC_ERR_INVAL_PARM 参数无效 +- FADC_ERR_NOT_READY 驱动未初始化 +- FADC_ERR_TIMEOUT 超时 +- FADC_ERR_CMD_FAILED 执行失败 + +### 5.3. 用户API接口 + +#### FAdcLookupConfig + +- 获取fadc控制器默认配置 + +```c +const FAdcConfig *FAdcLookupConfig(FAdcInstance instance_id); +``` + +Note: + +- 获取默认配置参数,包括基地址、中断号等 + +Input: + +- {FAdcInstance} instance_id,adc控制器id号 + +Return: + +- {const FAdcConfig *} adc默认配置,返回NULL如果找不到默认配置 + +#### FAdcCfgInitialize + +- 初始化fadc控制器, 使之可以使用 + +```c +FError FAdcCfgInitialize(FAdcCtrl *pctrl, const FAdcConfig *input_config_p); +``` + +Note: + +- 输入配置通过FAdcLookupConfig获取,用户按照需要修改后传入此函数 + +Input: + +- {FAdcCtrl} *pctrl,adc驱动控制数据 +- {FAdcConfig} *input_config_p,adc用户输入配置 + +Return: + +- {FError} 驱动初始化的错误码信息,FADC_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + +#### FAdcVariableConfig + +- 设置adc可配置参数 + +```c +FError FAdcVariableConfig(FAdcCtrl *pctrl, u8 channel, FAdcConvertConfig *convert_config, + FAdcThresholdConfig *threshold_config); +``` + +Note: + +- 设置指定adc控制器的可配置参数,包括转换模式,通道阈值等 + +Input: + +- {FAdcCtrl} *pctrl,adc驱动控制数据 +- {u8} channel,adc通道号 +- {FAdcConvertConfig} *convert_config,adc转换参数配置 +- {FAdcThresholdConfig} *threshold_config,adc通道转换结果阈值配置 + +Return: + +- {FError} 驱动初始化的错误码信息,FADC_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + +#### FAdcChannelThresholdSet + +- 设置adc通道的转换结果阈值,使能该通道 + +```c +FError FAdcChannelThresholdSet(FAdcCtrl *pctrl, u8 channel, FAdcThresholdConfig *threshold_config); +``` + +Note: + +- 设置指定adc控制器的指定通道的转换结果阈值,使能该通道 + +Input: + +- {FAdcCtrl} *pctrl,adc驱动控制数据 +- {u8} channel,adc通道号 +- {FAdcThresholdConfig} *threshold_config,adc通道转换结果阈值配置 + +Return: + +- {FError} 驱动初始化的错误码信息,FADC_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + +#### FAdcConvertStart + +- 设置adc控制器转换开始 + +```c +FError FAdcConvertStart(FAdcCtrl *pctrl); +``` + +Note: + +- 设置指定adc控制器转换开始 + +Input: + +- {FAdcCtrl} *pctrl,adc驱动控制数据 + +Return: + +- {FError} 驱动初始化的错误码信息,FADC_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + +#### FAdcInterruptEnable + +- 使能adc通道的中断 + +```c +FError FAdcInterruptEnable(FAdcCtrl *pctrl, u8 channel, FAdcIntrEventType event_type); +``` + +Note: + +- 使能指定adc控制器的指定通道的中断 + +Input: + +- {FAdcCtrl} *pctrl,adc驱动控制数据 +- {u8} channel,adc通道号 +- {FAdcIntrEventType} event_type,中断事件类型 + +Return: + +- {FError} 驱动初始化的错误码信息,FADC_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + + +#### FAdcReadConvertResult + +- 读adc通道的转换结果 + +```c +FError FAdcReadConvertResult(FAdcCtrl *pctrl, u8 channel, u16 *val); +``` + +Note: + +- 读指定adc控制器的指定通道的转换结果 + +Input: + +- {FAdcCtrl} *pctrl,adc驱动控制数据 +- {u8} channel,adc通道号 +- {u16} *val,存储转换结果的指针 + +Return: + +- {FError} 驱动初始化的错误码信息,FADC_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + + +#### FAdcRegisterInterruptHandler + +- 注册adc中断事件函数 + +```c +void FAdcRegisterInterruptHandler(FAdcCtrl *instance_p, FAdcIntrEventType event_type, + FAdcIntrEventHandler handler, void *param); +``` + +Note: +- 无 + +Input: +- {FAdcCtrl} *instance_p,fadc驱动控制数据 +- {FAdcIntrEventType} event_type,中断事件类型 +- {FAdcIntrEventHandler} handler,中断事件回调函数 +- {void} *param,回调函数参数 + +Return: +- 无 + +#### FAdcIntrHandler + +- adc中断处理函数入口 + +```c +void FAdcIntrHandler(s32 vector, void *args); +``` + +Note: +- 根据中断类型,设置对应的回调函数和参数传入 + +Input: +- {s32} vector +- {void} *param, 输入参数,指向fadc驱动控制数据 + +Return: +- 无 diff --git a/bsp/phytium/libraries/standalone/doc/reference/driver/fcan.md b/bsp/phytium/libraries/standalone/doc/reference/driver/fcan.md new file mode 100644 index 0000000000..449d9c8993 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/driver/fcan.md @@ -0,0 +1,248 @@ +# FCAN 驱动程序 + +## 1. 概述 + +CAN 是控制器局域网络(Controller Area Network)的缩写,由以研发和生产汽车电子产品著称的德国BOSCH公司开发,并最终成为国际标准(ISO 11898),是国际上应用最广泛的现场总线之一。 + +## 2. 功能 + +CAN控制器驱动提供了CAN的控制访问方法, +- 初始化CAN控制器 +- 以轮询方式发送/接收数据 +- 发送/接收数据的中断触发 + +驱动相关的源文件包括, +``` +. +├── fcan_g.c +├── fcan_hw.c +├── fcan_hw.h +├── fcan_intr.c +├── fcan_sinit.c +├── fcan.c +└── fcan.h +``` + +## 3. 配置方法 + +以下部分将指导您完成 fcan 驱动的软件配置: + +- 初始化CAN控制器 +- 设置CAN的中断处理函数,包括收发中断 + +## 4 应用示例 + +### [can收发数据](../../../baremetal/example/peripheral/can) + +## 5. API参考 + +### 5.1. 用户数据结构 + +- fcan控制数据 + +```c +typedef struct +{ + FCanConfig config; + u32 is_ready; /* Device is initialized and ready */ + boolean use_canfd; /* if use canfd function */ + + FCanIntrEventConfig intr_event[FCAN_INTR_EVENT_NUM];/* event handler and parameters for interrupt */ +} FCanCtrl; +``` + +- fcan配置数据,FCanConfig主要是can控制器id、基地址和中断号,FCanIntrEventConfig主要包括中断处理函数 + +```c +typedef struct +{ + u32 instance_id; /* Id of device */ + uintptr base_address; /* Can base Address */ + u32 irq_num; /* interrupt number */ + u32 irq_prority;/* interrupt priority*/ +}FCanConfig; +``` + +- fcan波特率配置 +```c +typedef struct +{ + FCanSegmentType segment; + boolean auto_calc; /* if auto calculate baudrate parameters */ + u32 baudrate; /* baudrate */ + u32 sample_point; /* sample point */ + 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; /* Baudrate prescaler */ +}FCanBaudrateConfig; +``` + +- fcan报文 +```c +typedef struct +{ + u32 canid; + u8 candlc; + u8 flags; /* additional flags for CAN FD */ + u8 data[FCAN_DATA_LENGTH] __attribute__((aligned(8))); +}FCanFrame; +``` + +- fcan中断事件类型 +```c +typedef enum +{ + FCAN_INTR_EVENT_SEND = 0, /* Handler type for frame sending interrupt */ + FCAN_INTR_EVENT_RECV = 1, /* Handler type for frame reception interrupt */ + FCAN_INTR_EVENT_ERROR, /* Handler type for error interrupt */ + FCAN_INTR_EVENT_NUM +} FCanIntrEventType; +``` + +### 5.2 错误码定义 + +- FCAN_SUCCESS 执行成功 +- FCAN_NOT_READY 驱动未初始化 +- FCAN_FAILURE 执行失败 +- FCAN_INVAL_PARAM 参数无效 + +### 5.3. 用户API接口 + +#### FCanLookupConfig + +- 获取Fata控制器默认配置 + +```c +const FCanConfig *FCanLookupConfig(FCanInstance instance_id); +``` + +Note: + +- 获取默认配置参数,包括基地址、中断号等 + +Input: + +- {FCanInstance} instance_id,控制器id号 + +Return: + +- {const FCanConfig *} fcan默认配置,返回NULL如果找不到默认配置 + +#### FCanCfgInitialize + +- 初始化fcan控制器, 使之可以使用 + +```c +FError FCanCfgInitialize(FCanCtrl *instance_p, const FCanConfig *input_config_p); +``` + +Note: + +- 输入配置通过FCanLookupConfig获取,用户按照需要修改后传入此函数 + +Input: + +- {FCanCtrl} *instance_p fcan驱动控制数据 +- {FCanConfig} *input_config_p fcan用户输入配置 + +Return: + +- {FError} 驱动初始化的错误码信息,FCAN_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + +#### FCanSend + +- 发送can数据 + +```c +FError FCanSend(FCanCtrl *instance_p, FCanFrame *frame_p); +``` + +Note: + +- 指定can控制器发送can数据 + +Input: + +- {FCanCtrl} *instance_p,fcan驱动控制数据 +- {FCanFrame} *frame_p,can数据 + +Return: + +- {FError} 驱动初始化的错误码信息,FCAN_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + +#### FCanRecv + +- 接收can数据 + +```c +FError FCanRecv(FCanCtrl *instance_p, FCanFrame *frame_p); +``` + +Note: + +- 指定can控制器接收can数据 + +Input: + +- {FCanCtrl} *instance_p,fcan驱动控制数据 +- {FCanFrame} *frame_p,can数据 + +Return: + +- {FError} 驱动初始化的错误码信息,FCAN_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + +#### FCanRegisterInterruptHandler + +- 注册can中断事件函数 + +```c +void FCanRegisterInterruptHandler(FCanCtrl *instance_p, FCanIntrEventConfig *intr_event_p); +``` + +Note: +- 无 + +Input: +- {FCanCtrl} *instance_p,fcan驱动控制数据 +- {FCanIntrEventConfig} *intr_event_p,中断事件类型,回调函数,回调函数参数 + +Return: +- 无 + +#### FCanIntrHandler + +- can中断处理函数入口 + +```c +void FCanIntrHandler(s32 vector, void *args); +``` + +Note: +- 根据中断类型,设置对应的回调函数和参数传入 + +Input: +- {s32} vector +- {void} *param, 输入参数,指向fcan驱动控制数据 + +Return: +- 无 + +#### FCanIdMaskFilterSet + +- can id过滤设置 + +```c +FError FCanIdMaskFilterSet(FCanCtrl *instance_p, FCanIdMaskConfig *id_mask_p); +``` + +Note: +- 设置可接收帧id值和掩码 + +Input: +- {FCanCtrl} *instance_p, fcan驱动控制数据 +- {FCanIdMaskConfig} *id_mask_p, 过滤寄存器序号,可接收帧id,可接收帧id掩码 + +Return: +- {FError} 驱动初始化的错误码信息,FCAN_SUCCESS 表示初始化成功,其它返回值表示初始化失败 \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/doc/reference/driver/fddma.md b/bsp/phytium/libraries/standalone/doc/reference/driver/fddma.md new file mode 100644 index 0000000000..0663e4b980 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/driver/fddma.md @@ -0,0 +1,288 @@ +# FDDMA 驱动程序 + +## 1. 概述 + +DDMA(Device Direct Memory Access)是E2000提供的一个通用DMA控制模块,支持典型的DMA操作,提供多个DMA通道,多个通道可以同时工作,独立配置给不同外设使用 + +## 2. 功能 + +FDDMA 驱动程序主要完成 DDMA 模块的初始化,DDMA通道的分配与释放, +相关源文件为: +``` +fddma + . + ├── fddma.c + ├── fddma.h + ├── fddma_g.c + ├── fddma_hw.c + ├── fddma_hw.h + ├── fddma_intr.c + ├── fddma_selftest.c + └── fddma_sinit.c +``` + +## 3. 配置方法 + +以下部分将指导您完成 FDDMA 驱动的软件配置: + +- 初始化 DDMA 控制器 +- 配置 DDMA 通道,与外设完成绑定 +- 启动 DDMA 通道 + +## 4 应用示例 + +### [通过DDMA搬运SPI数据完成回环测试](../../../baremetal/example/peripheral/dma/fddma_spi) + +## 5. API参考 + +### 5.1. 用户数据结构 + +#### FDdmaConfig + +- DDMA 实例配置 + +```c +typedef struct +{ + u32 id; /* DDMA ctrl id */ + uintptr base_addr; /* DDMA ctrl base address */ + u32 irq_num; /* DDMA ctrl interrupt id */ + u32 irq_prority; /* DDMA ctrl interrupt priority */ +} FDdmaConfig; /* DDMA instance configuration */ +``` + +#### FDdmaChanConfig + +- DDMA 通道配置 + +```c +typedef struct +{ + FDdmaChanIndex id; /* DMA channel index */ + u32 slave_id; /* Perpherial slave id for DDMA */ + FDdmaChanRequst req_mode; /* DMA transfer direction */ + uintptr ddr_addr; /* DMA channel DDR address, could be source or destination */ + u32 dev_addr; /* DMA channel Perpherial, could be source or destination */ + u32 trans_len; /* DMA channel transfer length */ +#define FDDMA_MAX_TRANSFER_LEN 64 /* max bytes in transfer */ +#define FDDMA_MIN_TRANSFER_LEN 4 /* min bytes in transfer */ + u32 timeout; /* timeout = 0 means no use DMA timeout */ +} FDdmaChanConfig; /* DDMA channel instance */ +``` + +#### FDdmaChan + +- DDMA 通道实例 + +```c +typedef struct FDdmaChan_ +{ + FDdmaChanConfig config; /* DDMA channel configuration */ + boolean is_used; /* TRUE means channel is in use */ + FDdma *dma; /* DMA instance of this channel */ + FDdmaChanEvtHandler evt_handler[FDDMA_NUM_OF_CHAN_EVT]; /* interrupt evt */ + void *evt_handler_args[FDDMA_NUM_OF_CHAN_EVT]; /* interrupt evt args */ +} FDdmaChan; /* DDMA channel instance */ +``` + +#### FDdma + +- DDMA 控制器实例 + +```c +typedef struct FDdma_ +{ + FDdmaConfig config; /* DDMA instance configuration */ + FDdmaChan *chan[FDDMA_NUM_OF_CHAN]; /* DDMA channel instance, NULL means channel not yet allocate */ + u32 is_ready; /* TRUE means DDMA init ok */ + u32 bind_status; /* channel bind status, BIT(n) = 1 means channel n is allocated */ +} FDdma; /* DDMA instance */ +``` + +### 5.2 错误码定义 + + +- FDDMA_SUCCESS : 成功 +- FDDMA_ERR_NOT_INIT :驱动未初始化 +- FDDMA_ERR_CHAN_BINDED :通道已经绑定无法分配 +- FDDMA_ERR_CHAN_RUNNING : 通道正在工作无法分配 +- FDDMA_ERR_INVALID_TRANS_SIZE : DMA传输字节数不合法 +- FDDMA_ERR_WAIT_TIMEOUT : DMA等待超时 +- FDDMA_ERR_INVALID_DDR_ADDR : DMA传输地址不合法 + +### 5.3. 用户API接口 + +#### FDdmaLookupConfig + + +```c +const FDdmaConfig *FDdmaLookupConfig(u32 instance_id); +``` + +Note: + +- 获取DDMA实例默认配置 + +Input: + +- {u32} instance_id, DDMA实例号 + +Return: + +- {const FDdmaConfig *} DDMA控制器默认配置 + +#### FDdmaCfgInitialization + + +```c +FError FDdmaCfgInitialization(FDdma *const instance, const FDdmaConfig *input_config); +``` + +Note: + +- 初始化DDMA控制器 + +Input: + +- {FDdma} *instance, DDMA控制器实例 +- {FDdmaConfig} *input_config, DDMA控制器配置 + +Return: + +- {FError} FDDMA_SUCCESS表示初始化成功,其它返回值表示初始化失败 + +#### FDdmaDeInitialization + + +```c +void FDdmaDeInitialization(FDdma *const instance); +``` + +Note: + +- 去初始化DDMA控制器 + +Input: + +- {FDdma} *instance, DDMA控制器实例 + +Return: + +- 无 + +#### FDdmaAllocateChan + +```c +FError FDdmaAllocateChan(FDdma *const instance, FDdmaChan *const dma_chan, const FDdmaChanConfig *dma_chan_config); +``` + +Note: + +- 按照配置分配并使能DDMA通道 + +Input: + +- {FDdma} *instance, DDMA控制器实例 +- {FDdmaChan} *dma_chan, DDMA通道实例 +- {FDdmaChanConfig} *dma_chan_config, DDMA通道配置 + +Return: + +- {FError} FDDMA_SUCCESS表示分配成功,其它返回值表示分配失败 + +#### FDdmaDellocateChan + +```c +FError FDdmaDellocateChan(FDdmaChan *const dma_chan); +``` + +Note: + +- 释放之前分配的DDMA通道 + +Input: + +- {FDdmaChan} *dma_chan, DDMA控制器实例 + +Return: + +- {FError} FDDMA_SUCCESS表示释放成功,其它返回值表示释放失败 + +#### FDdmaStart + +```c +FError FDdmaStart(FDdma *const instance); +``` + +Note: + +- 启动DDMA控制器,开始传输 + +Input: + +- {FDdma} *instance, DDMA控制器实例 + +Return: + +- {FError} FDDMA_SUCCESS表示启动成功,其它返回值表示启动失败 + +#### FDdmaStop + +```c +FError FDdmaStop(FDdma *const instance); +``` + +Note: + +- 停止DDMA控制器 + +Input: + +- {FDdma} *instance, DDMA控制器实例 + +Return: + +- {FError} FDDMA_SUCCESS表示停止成功,其它返回值表示停止失败 + +#### FDdmaIrqHandler + +```c +void FDdmaIrqHandler(s32 vector, void *args) +``` + +Note: + +- DDMA中断处理函数 + +Input: + +- {s32} vector +- {void} *param, 输入参数 + +Return: + +- 无 + +#### FDdmaRegisterChanEvtHandler + +```c +void FDdmaRegisterChanEvtHandler(FDdmaChan *const dma_chan, + FDdmaChanEvt evt, + FDdmaChanEvtHandler handler, + void *handler_arg); +``` + +Note: + +- 注册DDMA通道中断响应事件函数 + +Input: + +- {FDdmaChan} *dma_chan, DDMA通道 +- {FDdmaChanEvt} evt, 中断事件 +- {FDdmaChanEvtHandler} handler, 中断响应事件函数 +- {void} *handler_arg, 中断响应事件函数输入参数 + +Return: + +- 无 \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/doc/reference/driver/fgdma.md b/bsp/phytium/libraries/standalone/doc/reference/driver/fgdma.md new file mode 100644 index 0000000000..4305d062be --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/driver/fgdma.md @@ -0,0 +1,372 @@ +# FGDMA 驱动程序 + +## 1. 概述 + +GDMA(Generic Direct Memory Access),提供多个DMA通道,多个通道可以同时工作,独立配置给不同内存数据搬运使用 + + +## 2. 功能 + +FGDMA 驱动程序主要完成 GDMA 模块的初始化,GDMA 通道的分配与释放, +相关源文件为: +``` +fgdma + . + ├── fgdma.c + ├── fgdma.h + ├── fgdma_g.c + ├── fgdma_hw.h + ├── fgdma_intr.c + ├── fgdma_selftest.c + └── fgdma_sinit.c +``` + +## 3. 配置方法 + +以下部分将指导您完成 FGDMA 驱动的软件配置: + +- 初始化 GDMA 控制器 +- 配置 GDMA 通道,使用直接模式或者 BDL 模式进行操作 +- 启动 GDMA 通道 + +## 4. 应用示例 + +### [通过GDMA拷贝内存数据](../../../baremetal/example/peripheral/dma/fgdma_async_memcpy) + +## 5. API参考 + +### 5.1. 用户数据结构 + +#### FGdmaConfig + +- GDMA控制器配置 + +```c +typedef struct +{ + u32 instance_id; /* GDMA控制器ID */ + u32 irq_num; /* GDMA控制器中断号 */ + u32 irq_prority; /* GDMA控制器中断优先级 */ + volatile uintptr_t base_addr; /* GDMA控制器基地址 */ + FGdmaOperPriority rd_qos; /* 读操作优先级 */ + FGdmaOperPriority wr_qos; /* 写操作优先级 */ +} FGdmaConfig; +``` + +#### FGdmaChanConfig + +- DMA通道配置 + +```c +typedef struct +{ + FGdmaChanIndex chan_id; /* DMA通道ID */ + FGdmaOperPriority rd_qos; /* DMA通道读Qos配置 */ + FGdmaOperPriority wr_qos; /* DMA通道写Qos配置 */ + FGdmaOperMode trans_mode; /* DMA通道的操作模式,直接模式或者BDL模式 */ + /* Direct模式有效 */ + FGdmaBurstSize rd_align; /* DMA读请求的Burst对齐方式 */ + FGdmaBurstSize wr_align; /* DMA写请求的Burst对齐方式 */ + /* BDL模式有效 */ + boolean roll_back; /* 循环模式,TRUE: 当前BDL列表完成后,从第一个BDL项从新开始传输 */ + FGdmaBdlDesc *descs; + u32 total_desc_num; + u32 valid_desc_num; +} FGdmaChanConfig; /* DMA通道配置 */ +``` + +#### FGdmaChan + +- GDMA通道实例 + +```c +typedef struct _FGdmaChan +{ + FGdmaChanConfig config; /* DMA通道配置 */ + FGdma *gdma; /* DMA控制器实例 */ + FGdmaChanEvtHandler evt_handlers[FGDMA_CHAN_NUM_OF_EVT]; /* DMA通道事件回调函数 */ + void *evt_handler_args[FGDMA_CHAN_NUM_OF_EVT]; /* DMA通道事件回调函数入参 */ +} FGdmaChan; /* GDMA通道实例 */ +``` + +#### FGdma + +- GDMA控制器实例 + +```c +typedef struct _FGdma +{ + FGdmaConfig config; /* GDMA控制器配置 */ + u32 is_ready; /* GDMA控制器初始化是否完成 */ + FGdmaChan *chans[FGDMA_NUM_OF_CHAN]; /* GDMA通道实例,如果通道没有分配,值为NULL */ +} FGdma; /* GDMA控制器实例 */ +``` + +#### FGdmaBdlDesc + +- BDL描述符 + +```c +typedef struct +{ + u32 src_addr_l; /* 0x0, 数据源地址低32位 */ + u32 src_addr_h; /* 0x4, 数据源地址高32位 */ + u32 dst_addr_l; /* 0x8, 数据目的地址低32位 */ + u32 dst_addr_h; /* 0xc, 数据目的地址高32位 */ +#define FGDMA_SRC_TC_BDL_BURST_SET(x) SET_REG32_BITS((x), 1U, 0U) +#define FGDMA_SRC_TC_BDL_SIZE_SET(x) SET_REG32_BITS((x), 6U, 4U) +#define FGDMA_SRC_TC_BDL_LEN_SET(x) SET_REG32_BITS((x), 15U, 8U) + u32 src_tc; /* 0x10, 源传输控制位 */ +#define FGDMA_DST_TC_BDL_BURST_SET(x) SET_REG32_BITS((x), 1U, 0U) +#define FGDMA_DST_TC_BDL_SIZE_SET(x) SET_REG32_BITS((x), 6U, 4U) +#define FGDMA_DST_TC_BDL_LEN_SET(x) SET_REG32_BITS((x), 15U, 8U) + u32 dst_tc; /* 0x14, 目的传输控制 */ + u32 total_bytes;/* 0x18, 传输数据总量,以Byte为单位 */ + u32 ioc; /* 0x1c, 该条目传输完成中断产生控制位 */ +} __attribute__((__packed__)) FGdmaBdlDesc; /* BDL描述符 */ +``` + +### 5.2 错误码定义 + +#define FGDMA_SUCCESS : 成功 +#define FGDMA_ERR_NOT_INIT : 驱动未初始化 +#define FGDMA_ERR_CHAN_IN_USE : 通道已经绑定无法分配 +#define FGDMA_ERR_CHAN_NOT_INIT : 通道未初始化 +#define FGDMA_ERR_INVALID_ADDR : 传输地址非法 +#define FGDMA_ERR_INVALID_SIZE : 传输字节数非法 +#define FGDMA_ERR_BDL_NOT_ENOUGH : BDL已经使用完 + + +### 5.3. 用户API接口 + +#### FGdmaLookupConfig + +```c +const FGdmaConfig *FGdmaLookupConfig(u32 instance_id) +``` + +Note: + +- 获取GDMA控制器默认配置 + +Input: + +- {u32} instance_id, GDMA控制器ID + +Return: + +- {const FGdmaConfig *} 控制器默认配置 + +#### FGdmaCfgInitialize + +```c +FError FGdmaCfgInitialize(FGdma *const instance_p, const FGdmaConfig *input_config) +``` + +Note: + +- 初始化GDMA控制器实例 + +Input: + +- FGdma *const instance_p, GDMA控制器实例 +- const FGdmaConfig *input_config, GDMA控制器配置 + +Return: + +- {FError} 返回FGDMA_SUCCESS表示初始化成功,返回其它表示失败 + +#### FGdmaDeInitialize + +```c +void FGdmaDeInitialize(FGdma *const instance_p) +``` + +Note: + +- 去初始化GDMA控制器实例 + +Input: + +- FGdma *const instance_p, GDMA控制器实例 + +Return: + +- 无 + +#### FGdmaAllocateChan + +```c +FError FGdmaAllocateChan(FGdma *const instance_p, FGdmaChan *const dma_chan, + const FGdmaChanConfig *dma_chan_config) +``` + +Note: + +- 分配指定GDMA通道 + +Input: + +- FGdma *const instance_p, GDMA控制器实例 +- FGdmaChan *const dma_chan, GDMA通道实例 +- const FGdmaChanConfig *dma_chan_config, GDMA通道配置 + +Return: + +- {FError} FGDMA_SUCCESS表示分配成功,返回其它值表示分配失败 + +#### FGdmaDellocateChan + +```c +FError FGdmaDellocateChan(FGdmaChan *const dma_chan) +``` + +Note: + +- 释放GDMA通道 + +Input: + +- FGdmaChan *const dma_chan, GDMA通道实例 + +Return: + +- {FError} FGDMA_SUCCESS表示处理成功 + +#### FGdmaDirectTransfer + +```c +FError FGdmaDirectTransfer(FGdmaChan *const chan_p, uintptr src_addr, uintptr dst_addr, fsize_t data_len); + +``` + +Note: + +- 直接操作模式下发起DMA传输 + +Input: + +- FGdmaChan *const chan_p, GDMA通道实例 +- uintptr src_addr, 传输源地址 +- uintptr dst_addr, 传输目的地址 + +Return: + +- {FError} FGDMA_SUCCESS表示传输成功 + +#### FGdmaAppendBDLEntry + +```c +FError FGdmaAppendBDLEntry(FGdmaChan *const chan_p, uintptr src_addr, uintptr dst_addr, fsize_t data_len) +``` + +Note: + +- 设置BDL描述符的一个条目 + +Input: + +- FGdmaBdlDesc *desc_entry, 一条BDL描述符 +- uintptr src_addr, 传输源地址 +- uintptr dst_addr, 传输目的地址 +- fsize_t data_len, 传输数据长度 + +Return: + +- {FError} FGDMA_SUCCESS 表示设置成功 + +#### FGdmaBDLTransfer + +```c +FError FGdmaBDLTransfer(FGdmaChan *const chan_p) +``` + +Note: + +- BDL操作模式下发起DMA传输 + +Input: + +- FGdmaChan *const chan_p, DMA通道实例 + +Return: + +- {FError} FGDMA_SUCCESS 表示传输成功 + +#### FGdmaStart + +```c +FError FGdmaStart(FGdma *const instance_p) +``` + +Note: + +- 使能启动GDMA控制器 +- 先调用此函数,后调用FGdmaAllocateChan配置特定通道 + +Input: + +- FGdma *const instance_p, GDMA控制器实例 + +Return: + +- {FError} FGDMA_SUCCESS表示启动成功 + +#### FGdmaStop + +```c +FError FGdmaStop(FGdma *const instance_p) +``` + +Note: + +- 停止GDMA控制器 + +Input: + +- FGdma *const instance_p, GDMA控制器实例 + +Return: + +- {FError} FGDMA_SUCCESS表示处理成功 + +#### FGdmaIrqHandler + +```c +void FGdmaIrqHandler(s32 vector, void *args) +``` + +Note: + +- GDMA中断处理函数 + +Input: + +- {s32} vector, 中断号 +- {void} *args, 中断参数 + +Return: + +- 无 + +#### FGdmaChanRegisterEvtHandler + +```c +void FGdmaChanRegisterEvtHandler(FGdmaChan *const chan_p, FGdmaChanEvtType evt, + FGdmaChanEvtHandler handler, void *handler_arg) +``` + +Note: + +- 注册GDMA通道事件回调函数 + +Input: + +- {FGdmaChan} *chan_p, GDMA通道实例 +- {FGdmaChanEvtType} evt, 通道事件 +- {FGdmaChanEvtHandler} handler, 事件回调函数 +- {void} *handler_arg, 事件回调函数输入参数 + +Return: + +- 无 diff --git a/bsp/phytium/libraries/standalone/doc/reference/driver/fgic.md b/bsp/phytium/libraries/standalone/doc/reference/driver/fgic.md new file mode 100644 index 0000000000..88ca7a1ee8 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/driver/fgic.md @@ -0,0 +1,441 @@ + + + +# GIC 驱动说明 + +## 1.驱动概述 + +GIC 是通用中断控制器,它为ARM CPU提供外设中断与软件中断的控制,GIC主要支持SPI\SGI\PPI等不同类型的中断,本版本支持的驱动版本为GICv3,并且支持了以下特性: +1.SPI支持多核心路由配置、优先级配置、使能关闭功能 +2.SGI提供软件触发接口、优先级配置、支持多核触发、目标核心触发、使能关闭功能 +3.PPI 提供优先级配置、使能关闭功能 +4.支持CPU interface 中断掩码配置 +5.支持组优先级分组配置 +6.当前仅支持双安全态下的Non-secure访问 + +## 2.驱动功能 + +驱动组成由以下所示 +``` +. +├── fgic.c +├── fgic.h +├── fgic_cpu_interface.S +├── fgic_cpu_interface.h +├── fgic_distributor.h +├── fgic_g.c +├── fgic_hw.h +├── fgic_redistributor.h +└── fgic_sinit.c +``` +- 其中fgic.c/h 为开发者提供以下功能: +1. 初始化GIC 中断实例 +2. 提供基于中断号中断开关功能 +3. 提供基于中断号中断优先级设置与获取功能 +4. 提供基于中断号触发方式 +5. 提供SGI中断触发功能 +6. 提供中断承认(Acknowledge)接口 +7. 提供中断优先级掩码配置与获取功能 +8. 提供Distrutior、Redistrubutior、CPU interface 的 初始化功能 + +## 3.数据结构 + +``` +typedef struct +{ +    u32 instance_id; /*Gic 实例编号*/ +    uintptr dis_base; /* Distributor 基地址*/ +    +} FGicConfig; + +``` + +``` +typedef struct +{ +    FGicConfig config; /* 配置数据结构*/ +    u32 is_ready;       /* 驱动实例的状态 */ +    uintptr redis_base; /* 当前实例核心对应的Redistributor 地址  */ +    SECURITY_STATE security ; /* GIC 驱动当前支持的安全态模式 */ +    s32 max_spi_num;    /* SPI 最大中断优先级ID */ +} FGic; +``` + +## 4.错误码定义 + +``` +FGIC_CTLR_ERR_TYPE   /* 错误选择CTLR 寄存器 */ +FGIC_CTLR_ERR_NUM     /* 当前控制器不支持此中断id */ +FGIC_CTLR_ERR_IN_SET   /* 在设置过程中出现的异常 */ +FGIC_CTLR_ERR_IN_GET   /* 在获取过程中出现的异常 */ +``` + +## 5.应用示例 + +common/interrupt.c 中断接口提供底层支持 +/baremetal/example/peripheral/gic/fgic_test gic特性例程 + +## 6.API介绍 + +### 1. FGicLookupConfig + +``` +FGicConfig *FGicLookupConfig(u32 instance_id) +``` + +#### 介绍 +获取当前GIC 驱动默认配置 +#### 参数 +- u32 instance_id :当前GIC 驱动中对应的ID + +#### 返回 +- FGicConfig * :静态默认配置 + +### 2. FGicCfgInitialize + +``` +FError FGicCfgInitialize(FGic *instance_p, const FGicConfig *input_config_p , uintptr redis_base) +``` + +#### 介绍 +根据传入配置,初始化GIC 驱动实例 + +#### 参数 +- FGic *instance_p:指向FGic实例的指针 +- const FGicConfig *input_config_p:需要应用于示例中的配置项 +- redis_base:是当前内核的重分发地址 + +#### 返回 +- FError :FGIC_SUCCESS 为初始成功 + + +### 3. FGicDistrubutiorInit + +``` +void FGicDistrubutiorInit(FGic *instance_p) +``` + +#### 介绍 +初始化Distrubutior 模块 + +#### 参数 +- FGic *instance_p:指向FGic实例的指针 + +#### 返回 +无 + +### 4. FGicRedistrubutiorInit + +``` +FError FGicRedistrubutiorInit(FGic *instance_p) +``` + +#### 介绍 +初始化Redistrubutior 模块 + +#### 参数 +- FGic *instance_p:指向FGic实例的指针 + +#### 返回 +- {FError} FGIC_SUCCESS is success ,FGIC_ERR_IN_TIMEOUT is timeout + +### 5. FGicCpuInterfaceInit + +``` +void FGicCpuInterfaceInit(void) +``` + +#### 介绍 +初始化当前核心下的cpu interface模块 + +#### 返回 +无 + +### 6. FGicIntEnable + +``` +FError FGicIntEnable(FGic *instance_p,s32 int_id) +``` + +#### 介绍 +基于中断号使能中断功能 + +#### 参数 +- FGic *instance_p:指向FGic实例的指针 +- s32 int_id:中断ID + +#### 返回 +- FError :FGIC_SUCCESS 为操作成功 + +### 7. FGicIntDisable + +``` +FError FGicIntDisable(FGic *instance_p,s32 int_id) +``` + +#### 介绍 +基于中断号关闭中断功能 + +#### 参数 +- FGic *instance_p:指向FGic实例的指针 +- s32 int_id:中断ID + +#### 返回 +- FError :FGIC_SUCCESS 为操作成功 + +### 8. FGicSetPriority + +``` +FError FGicSetPriority(FGic *instance_p,s32 int_id,u32 priority) +``` + +#### 介绍 +基于中断号设置当前中断优先级的值 + +#### 参数 +- FGic *instance_p:指向FGic实例的指针 +- s32 int_id:中断ID +- u32 priority: 中断优先级的值,有效值8bit + +#### 返回 +- FError :FGIC_SUCCESS 为操作成功 ,当反馈为FGIC_CTLR_ERR_IN_GET 时,表示中断ID超过范围 + +### 9. FGicGetPriority + +``` +u32 FGicGetPriority(FGic *instance_p,s32 int_id) +``` + +#### 介绍 +基于中断号获取当前中断优先级的值 + +#### 参数 +- FGic *instance_p:指向FGic实例的指针 +- s32 int_id:中断ID + +#### 返回 +- u32 :中断优先级的值,有效值8bit 。当反馈为FGIC_CTLR_ERR_IN_GET 时,表示中断ID超过范围 + +### 10. FGicSetTriggerLevel + +``` +FError FGicSetTriggerLevel(FGic *instance_p,s32 int_id,TRIGGER_LEVEL trigger_way) +``` + +#### 介绍 +基于当前中断号配置中断触发模式 + +#### 参数 +- FGic *instance_p:指向FGic实例的指针 +- s32 int_id:中断ID +- TRIGGER_LEVEL trigger_way:中断触发模式 , 参数为0的时为电平触发,参数为1的时为电平触发 + +#### 返回 +- FError :FGIC_SUCCESS 为操作成功 ,当反馈为FGIC_CTLR_ERR_IN_GET 时,表示中断ID超过范围 + +### 11. FGicGetTriggerLevel + +``` +u32 FGicGetTriggerLevel(FGic *instance_p,s32 int_id) +``` + +#### 介绍 +基于当前中断号获取中断触发模式 + +#### 参数 +- FGic *instance_p:指向FGic实例的指针 +- s32 int_id:中断ID + +#### 返回 +- TRIGGER_LEVEL trigger_way:中断触发模式 , 参数为0的时为电平触发,参数为1的时为电平触发 + +### 12. FGicSetSpiAffinityRouting + +``` +FError FGicSetSpiAffinityRouting(FGic *instance_p,s32 int_id,SPI_ROUTING_MODE route_mode,u64 affinity) +``` + +#### 介绍 +为特定的SPI中断设置中间路由信息 + +#### 参数 +- FGic *instance_p:指向FGic实例的指针 +- s32 int_id:中断ID +- SPI_ROUTING_MODE route_mode:中断路由模式 ,SPI_ROUTING_TO_SPECIFIC spi 中断路由给特定PE ,SPI_ROUTING_TO_ANY spi 中断将路由给任何开启本SPI 的PE处 +- u64 affinity:亲和度参数,它的格式为: + * |--------[bit39-32]-------[bit23-16]-------------[bit15-8]--------[bit7-0] + * |--------Affinity level3-----Affinity level2-----Affinity level1---Affinity level0 + +#### 返回 +- FError :FGIC_SUCCESS 为操作成功,FGIC_CTLR_ERR_IN_SET 为中断ID 不符合SPI的范围 + + +### 13. FGicGetAffinityRouting + +``` +FError FGicGetAffinityRouting(FGic *instance_p,s32 int_id,SPI_ROUTING_MODE *route_mode_p,u64 *affinity_p) +``` + +#### 介绍 +获取特定的SPI中断设置路由信息 + +#### 参数 +- FGic *instance_p:指向FGic实例的指针 +- s32 int_id:中断ID +- SPI_ROUTING_MODE *route_mode_p:中断路由模式实例的指针 ,当参数为SPI_ROUTING_TO_SPECIFIC spi 中断路由给特定PE , SPI_ROUTING_TO_ANY spi 中断将路由给任何开启本SPI 中断的PE处 +- u64 *affinity_p:亲和度参数实例的指针,它的值格式为: + * |--------[bit39-32]-------[bit23-16]-------------[bit15-8]--------[bit7-0] + * |--------Affinity level3-----Affinity level2-----Affinity level1---Affinity level0 + +#### 返回 +- FError :FGIC_SUCCESS 为操作成功,FGIC_CTLR_ERR_IN_GET 为中断ID 不符合SPI的范围 + +### 14. FGicGenerateSgi +``` +FError FGicGenerateSgi(FGic *instance_p,s32 int_id,u32 target_list,SGI_ROUTING_MODE routing_mode,u64 affinity) +``` + +#### 介绍 +基于中断id产生SGI中断的接口 + +#### 参数 +- FGic *instance_p:指向FGic实例的指针 +- s32 int_id:中断ID +- u32 target_list:将为其生成SGI中断的pe集合 。每一位对应一个集群内的一个PE +SGI_ROUTING_MODE routing_mode:SGI 的路由模式,SGI_ROUTING_TO_SPECIFIC 为特定的PE,SGI_ROUTING_TO_ANY 为所有的PE 除开自己这个核心。 +- u64 affinity :亲和度参数,它的格式为: + * |--------[bit55-48]-------[bit39-32]-------------[bit23-16] + * |--------Affinity level3-----Affinity level2-----Affinity level1 + +#### 返回 +- FError :FGIC_SUCCESS 为操作成功,FGIC_CTLR_ERR_IN_SET 为中断ID 不符合SGI的范围 + +### 15. FGicDeactionInterrupt + +``` +void FGicDeactionInterrupt(FGic *instance_p,s32 int_id) +``` + +#### 介绍 +钝化正处于激活态的中断 + +#### 参数 +- FGic *instance_p:指向FGic实例的指针 +- s32 int_id:中断ID + +#### 返回 +无 + +### 16. FGicAcknowledgeInt + +``` +s32 FGicAcknowledgeInt(FGic *instance_p) +``` + +#### 介绍 +承认当前被挂起的中断 + +#### 参数 +- FGic *instance_p:指向FGic实例的指针 + +#### 返回 +- s32 int_id:被承认的中断ID + + +### 17. FGicSetPriorityFilter + +``` +void FGicSetPriorityFilter(FGic *instance_p,u32 priority_mask) +``` + +#### 介绍 +通过设置ICC_PMR参数,确定中断控制器可以响应的中断范围 + +#### 参数 +- FGic *instance_p:指向FGic实例的指针 +- u32 priority_mask:如果一个中断的优先级低于该字段所指定的值,则接口向PE发送中断信号。 priority_mask的参考值如下所示 : + * |priority_mask---------------256-------254--------252------248-------240 + * |Implemented priority bits---[7:0]----[7:1]------[7:2]-----[7:3]-----[7:4] + * |priority the growing steps--any-----even value----4---------8--------16 + +#### 返回 +无 + +### 18. FGicSetPriorityGroup + +``` +void FGicSetPriorityGroup(FGic *instance_p,u32 binary_point) +``` + +#### 介绍 +设置当前组优先级的值 + +#### 参数 +- FGic *instance_p:指向FGic实例的指针 +- u32 binary_point:该字段的值控制如何将8位中断优先级字段拆分为组优先级和子优先级, +- binary_point值与分组的关系如下所示 + * |return value----------------0-------1--------2------3-------4------5------6-------7 + * |Group priority field------[---]----[7:1]---[7:2]--[7:3]---[7:4]---[7:5]---[7:6]---[7] + * |Subpriority field---------[---]-----[0]----[1:0]--[2:0]---[3:0]---[4:0]---[5:0]---[6:0] + +#### 返回 +无 + + +### 19. FGicGetPriorityFilter + +``` +u32 FGicGetPriorityFilter(FGic *instance_p) +``` + +#### 介绍 +获取当前优先级过滤值 + +#### 参数 +- FGic *instance_p:指向FGic实例的指针 + +#### 返回 +- u32 Priority Mask,CPU接口中的优先级掩码 ,如果一个中断的优先级低于该字段所指定的值,则接口向PE发送中断信号。 priority_mask的参考值如下所示 : + * |return value------------------256------254--------252------248-------240 + * |Implemented priority bits---[7:0]----[7:1]------[7:2]-----[7:3]-----[7:4] + * |priority the growing steps---any-----even value----4--------8-------16 + +### 20. FGicGetPriorityGroup + +``` +u32 FGicGetPriorityGroup(FGic *instance_p) +``` + +#### 介绍 +获取当前CPU interface 中的分组值 + +#### 参数 +- FGic *instance_p:指向FGic实例的指针 + +#### 返回 +- u32 Priority group,该字段的值控制如何将8位中断优先级字段拆分为组优先级和子优先级, +binary_point值与分组的关系如下所示 + * |return value----------------0-------1--------2------3-------4------5------6-------7 + * |Group priority field------[---]----[7:1]---[7:2]--[7:3]---[7:4]---[7:5]---[7:6]---[7] + * |Subpriority field---------[---]-----[0]----[1:0]--[2:0]---[3:0]---[4:0]---[5:0]---[6:0] \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/doc/reference/driver/fgmac.md b/bsp/phytium/libraries/standalone/doc/reference/driver/fgmac.md new file mode 100644 index 0000000000..6fa5768cf2 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/driver/fgmac.md @@ -0,0 +1,466 @@ +# FGMAC 驱动程序 + +## 1. 概述 + +以太网控制器(GMAC)的主要功能是在兼容 IEEE802.3-2005 标准的以太网中发送和接收数据,支持 RGMII 的 PHY 接口 + +GMAC 接口特点包括 +- 支持速率 1000Mbps/100Mbps/10Mbps +- 支持 IEEE 802.3-2005 Ethernet MAC,Reduced Gigabit Media Independent Interface (RGMII) + +## 2. 功能 + +FGMAC 驱动程序提供了以太网控制器初始化,发送/接收数据和配置PHY接口等功能 + +FGMAC 驱动程序的源文件包括, + +``` +├── Kconfig +├── fgmac.c +├── fgmac.h +├── fgmac_dma.c +├── fgmac_g.c +├── fgmac_hw.c +├── fgmac_hw.h +├── fgmac_intr.c +├── fgmac_sinit.c +└── phy + ├── ar803x + │   ├── fgmac_ar803x.c + │   └── fgmac_ar803x.h + ├── fgmac_phy.c + └── fgmac_phy.h +``` + +## 3. 配置方法 + +以下部分将指导您完成 FGMAC 驱动的硬件/软件配置: + +- 选择开发板上的特定 GMAC 控制器,连通网线 +- 通过驱动 API,获取指定 GMAC 控制器的默认配置 +- 按需要修改获取的 GMAC 默认配置,通过驱动API,进行 GMAC 控制器的初始化 +- 通过驱动API,获取 PHY 的默认配置 +- 按需要修改获取的 PHY 默认配置,通过驱动API,进行 PHY 的初始化 +- 分配 GMAC 数据传输使用的 DMA 描述符和 DMA 缓存区,通过驱动 API 进行注册 +- 通过驱动 API 发送/接收数据 + +网络通信依赖协议栈,可以参考应用例程,使用LWIP网络协议栈进行通信, + +## 4 应用示例 + +### [fgmac_link](../../../baremetal/example/fgmac_link/README.md) + +启动GMAC,接收网络数据并打印 + +### [fgmac_lwip_echo](../../../baremetal/example/fgmac_lwip_echo/README.md) + +启动LWIP网络协议栈,通过FGMAC驱动,支持开发板和网络主机的ping通 + +### [fgmac_lwip_tftp](../../../baremetal/example/fgmac_lwip_tftp/README.md) + +启动LWIP网络协议栈,通过FGMAC驱动,支持开发板通过tftp服务获取文件 + +## 5. API参考 + +### 5.1. 用户数据结构 + +- FGMAC 驱动配置数据 +```c +typedef struct +{ + u32 instance_id; /* device instance id */ + uintptr base_addr; /* device base address */ + u32 irq_num; /* irq num */ + u32 cheksum_mode; /* hardware or software checksum */ + u32 duplex_mode; /* selects the MAC duplex mode: Half-Duplex or Full-Duplex mode */ + u32 max_packet_size; /* max num of bytes in frame transfer */ + u32 mdc_clk_hz; /* MDC clock access PHY. [1.0MHz ~2.5MHz] */ + boolean en_auto_negtiation; /* auto-negotiation or not */ + u32 speed; /* sets the Ethernet speed: 10/100/1000 Mbps.*/ + +} FGmacConfig; /* FGMAC 驱动配置数据 */ +``` + +- FGMAC 驱动控制数据 +```c + +typedef struct +{ + FGmacConfig config; /* Current active configs */ + u32 is_ready; /* Device is initialized and ready */ + FGmacRingDescData rx_ring; /* RX DMA descriptor data (idx, length) */ + volatile FGmacDmaDesc *rx_desc; /* RX DMA descriptor table in ring */ + FGmacRingDescData tx_ring; /* TX DMA descriptor data (idx, length) */ + volatile FGmacDmaDesc *tx_desc; /* TX DMA descriptor table in ring */ + FGmacEvtHandler evt_handler[FGMAC_INTR_EVT_NUM]; /* User registered interrupt handler */ + u32 phy_valid_mask; + u32 phy_speed; + u32 phy_addr; /* phy ic addr */ +} FGmac; /* FGMAC 驱动控制数据 */ +``` + +- FGMAC DMA描述符 + +```c +typedef struct +{ + volatile u32 status; + u32 ctrl; + u32 buf_addr; + u32 next; +} FGmacDmaDesc; +``` + +- FGMAC DMA描述符表(链式)相关数据 +```c +typedef struct +{ + u32 desc_idx; /* For Current Desc position */ + u32 desc_buf_idx; /* For Current Desc buffer buf position */ + u32 desc_max_num; /* Max Number for Desc and Desc buffer */ + u8 *desc_buf_base; /* Desc buffer Base */ +} FGmacRingDescData; +``` + +- FGMAC 校验方法选择 +```c +enum +{ + FGMAC_CHECKSUM_BY_SOFTWARE = 0, + FGMAC_CHECKSUM_BY_HARDWARE +}; +``` + +- FGMAC 中断事件类型 +```c +enum +{ + FGMAC_TX_COMPLETE_EVT = 0, + FGMAC_RX_COMPLETE_EVT, + FGMAC_LINK_STATUS_EVT, + FGMAC_PHY_STATUS_EVT, + FGMAC_DMA_ERR_EVT, + + FGMAC_INTR_EVT_NUM +}; +``` + +### 5.2 错误码定义 + +- 模块错误码编号:0x1070000 +- [0x0] FGMAC: Success +- [0x1070001] FGMAC: wait timeout +- [0x1070002] FGMAC: DMA address invalid +- [0x1070003] FGMAC: driver not ready +- [0x1070004] FGMAC: data transaction failed +- [0x1070005] FGMAC: PHY type not support +- [0x1070006] FGMAC: PHY is not found + +### 5.3. 用户API接口 + +#### FGmacLookupConfig + +- 获取FGMAC驱动的默认配置参数 + +```c +const FGmacConfig *FGmacLookupConfig(u32 instance_id); +``` + +Note: + +- 返回FGMAC的默认配置,复制后修改配置 +- 需要确认当前平台支持输入的instance_id + +Input: + +- {u32} instance_id, 驱动控制器号 + +Return: + +- {const FGmacConfig *}, 驱动默认配置 +#### FGmacCfgInitialize + +- 完成FGMAC驱动实例的初始化,使之可以使用 + +```c +FError FGmacCfgInitialize(FGmac *instance_p, const FGmacConfig *cofig_p); +``` + +Note: + +- 此函数会重置FGMAC控制器和FGMAC控制数据 + +Input: + +- {FGmac} *instance_p 驱动控制数据 + +- {FGmacConfig} *cofig_p 驱动配置数据 + +Return: + +- {FError} 驱动初始化的错误码信息,FGMAC_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + +#### FGmacDeInitialize + +- 完成FGMAC驱动实例去使能,清零实例数据 + +```c +FError FGmacDeInitialize(FGmac *instance_p); +``` + +Note: + +- 此函数会重置FGMAC控制数据 + +Input: + +- {FGmac} *instance_p 驱动控制数据 + +Return: + +- {FError} 驱动初始化的错误码信息,FGMAC_SUCCESS 表示去初始化成功,其它返回值表示去初始化失败 + +#### FGmacSetupTxDescRing + +- 配置FGMAC的发送DMA描述符和缓冲区 + +```c +FError FGmacSetupTxDescRing(FGmac *instance_p, volatile FGmacDmaDesc *tx_desc_tbl, u8 *tx_buf, const fsize_t tx_pre_buf_len, const fsize_t tx_buf_num); +``` + +Note: + +- 传入的tx_desc_tbl和tx_buf必须为32位空间地址 + +Input: + +- {FGmac *}instance_p 驱动控制数据 + +- {volatile FGmacDmaDesc *} tx_desc_tbl 发送DMA描述符表(数组) + +- {u8} *tx_buf 发送DMA缓冲区(数组,每一个描述符对应一个缓冲区) + +- {const fsize_t} tx_pre_buf_len 单个DMA缓冲区的字节数 + +- {const fsize_t} tx_buf_num DMA描述符或者DMA缓存区的数目 + +Return: + +- {FError} TX DMA初始化的错误码信息,FGMAC_SUCCESS 表示TX DMA初始化成功,其它返回值表示TX DMA初始化失败 + +#### FGmacSetupRxDescRing + +- 配置FGMAC的接收DMA描述符和缓冲区 + +```c +FError FGmacSetupRxDescRing(FGmac *instance_p, volatile FGmacDmaDesc *rx_desc_tbl, u8 *rx_buf, const fsize_t rx_pre_buf_len, const fsize_t rx_buf_num); +``` + +Note: + +- 传入的rx_desc_tbl和rx_buf必须为32位空间地址 + +Input: + +- {FGmac *}instance_p 驱动控制数据 +- {volatile FGmacDmaDesc *} rx_desc_tbl 接收DMA描述符表(数组) +- {u8} *rx_buf 接收DMA缓冲区(数组,每一个描述符对应一个缓冲区) +- {const fsize_t} rx_pre_buf_len 单个DMA缓冲区的字节数 +- {const fsize_t} rx_buf_num DMA描述符或者DMA缓存区的数目 + +Return: + +- {FError} RX DMA初始化的错误码信息,FGMAC_SUCCESS 表示RX DMA初始化成功,其它返回值表示RX DMA初始化失败 + +#### FGmacInterruptHandler + +- FGMAC中断处理函数 + +```c +void FGmacInterruptHandler(s32 vector, void *param); +``` + +Note: + +- 此函数运行在中断上下文 + +Input: + +- {s32} vector, 中断向量号,此处没有用到 +- {void} *param, 中断输入参数,此处传入的是FGMAC的驱动控制数据 + +Return: + +无 + +#### FGmacRegisterEvtHandler + +- 注册FGMAC中断事件响应函数 + +```c +void FGmacRegisterEvtHandler(FGmac *instance_p, u32 evt, FGmacEvtHandler handler); +``` + +Note: + +- 注册的函数handler会在中断上下文执行 + +Input: + +- {FGmac} *instance_p 驱动控制数据 +- {u32} evt 中断事件类型 +- {FGmacEvtHandler} handler 中断事件响应函数 + +Return: + +无 + +#### FGmacStartTrans + +- 使能FGMAC DMA,使之可以接收/发送数据 + +```c +FError FGmacStartTrans(FGmac *instance_p); +``` + +Note: + +- 调用函数前需要确保FGMAC驱动初始化成功 + +Input: + +- {FGmac} *instance_p 驱动控制数据 + +Return: + +- {FError} FGMAC_SUCCESS 表示启动成功,其它返回值表示启动失败 + +#### FGmacStopTrans + +- 去使能FGMAC DMA, 使之不再能接收/发送数据 + +```c +FError FGmacStopTrans(FGmac *instance_p); +``` + +Note: + +- 调用函数前需要确保FGMAC驱动初始化成功 + +Input: + +- {FGmac} *instance_p 驱动控制数据 + +Return: + +- {FError} FGMAC_SUCCESS 表示去启动成功,其它返回值表示去启动失败 + +#### FGmacRecvFrame + +- 通过FGMAC接收数据帧 + +```c +FError FGmacRecvFrame(FGmac *instance_p) +``` + +Note: + +- 调用函数前需要确保FGMAC驱动初始化成功 + +Input: + +- {FGmac} *instance_p 驱动控制数据 + +Return: + +- {FError} FGMAC_SUCCESS 表示接收数据帧成功,其它返回值表示接收数据帧失败 + +#### FGmacSendFrame + +- 通过FGMAC发送数据帧 + +```c +FError FGmacSendFrame(FGmac *instance_p, u32 frame_len); +``` + +Note: + +- 通过FGMAC发送数据帧 + +Input: + +- {FGmac} *instance_p 驱动控制数据 + +- {u32} frame_len 数据帧长度 + +Return: + +- {FError} FGMAC_SUCCESS 表示发送数据帧成功,其它返回值表示发送数据帧失败 + + +#### FGmacPhyLookupConfig + +- 获取FGMAC PHY默认配置参数 + +```c +void FGmacPhyLookupConfig(u32 gmac_instance_id, FGmac *instance_p); +``` + +Note: + +调用此函数前确保 FGMAC 驱动初始化已经成功 + +Input: + +- {u32} gmac_instance_id FGMAC id +- {FGmac} *instance_p FGMAC 控制数据 + +Return: + +无 + +#### FGmacPhyCfgInitialize + +- 查找GMAC连接的phy芯片地址,完成FGMAC PHY驱动实例的初始化,使之可以使用 + +```c +FError FGmacPhyCfgInitialize(FGmac *instance_p); +``` + +Note: + +- 调用此函数前确保 FGMAC 驱动初始化已经成功 + +Input: + +- {FGmac} *instance_p GMAC控制数据 + +Return: + +- {FError} FGMAC_SUCCESS 表示PHY设置成功,其它返回值表示PHY设置失败 + +#### FGmacSetInterruptMask + +- 设置FGMAC中断屏蔽位 + +```c +void FGmacSetInterruptMask(FGmac *instance_p, u32 intr_type, u32 mask, boolean enable); +``` + +Note: + +- 在FGMAC驱动初始化成功后调用此函数 + +Input: + +- {FGmac} *instance_p 驱动控制数据 + +- {u32} intr_type 中断类型 GMAC中断/DMA中断 + +- {u32} mask 中断屏蔽标志位 + +- {boolean} enable TRUE: 使能中断,FALSE: 去使能中断 + +Return: + +无 diff --git a/bsp/phytium/libraries/standalone/doc/reference/driver/fgpio.md b/bsp/phytium/libraries/standalone/doc/reference/driver/fgpio.md new file mode 100644 index 0000000000..26010668e1 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/driver/fgpio.md @@ -0,0 +1,454 @@ +# FGPIO 驱动程序 + +## 1. 概述 + +- GPIO(General-purpose input/output),即通用型输入输出,其引脚可以供使用者通过程序控制其输入、输出,常用于产生时钟,作为片选信号和模拟低速通信协议等场景 + +- FT2000/4和D2000提供两个 GPIO 模块,每个 GPIO 模块有 16 位接口,每8位一组,分别是A组和B组,GPIO可以控制外部IO的输入输出方向,作为输出时,内部寄存器的数据输出到片外,作为输入时,片外的数据被锁存在内部寄存器 + +- FGPIO 驱动支持配置 GPIO 引脚的输入输出方向,输出高低电平,或者获取输入电平,配置引脚的中断触发模式,配置引脚的中断响应回调函数等 + +- FGPIO_VERSION_1 对应 FT2000/4和D2000 的 GPIO,FGPIO_VERSION_2 对应 E2000的 GPIO,具体差异请参考软件编程手册 + +## 2. 功能 + +- FGPIO 驱动程序主要完成GPIO相关的功能配置,包括 + +- 1. GPIO 控制器初始化 +- 2. GPIO 引脚输入输出方向设置 +- 3. GPIO 引脚输出和输入 +- 4. GPIO 引脚中断使能和屏蔽 +- 5. GPIO 引脚中断类型设置 +- 6. GPIO 引脚中断回调函数注册 + +- 驱动相关的源文件如下, +- drivers/pin/fgpio +``` +. +├── Kconfig +├── fgpio.c +├── fgpio.h +├── fgpio_g.c +├── fgpio_hw.h +├── fgpio_selftest.c +├── fgpio_intr.c +└── fgpio_sinit.c +``` + +## 3. 配置方法 + +- FGPIO 驱动支持 FT2000/4, D2000和E2000,在 D2000 和 E2000 上完成测试 + +- 参考以下步骤完成 FGPIO 硬件配置, +- 1. 获取FT2000/4, D2000或E2000的软件编程手册,参考引脚复用表,设置引脚复用为 GPIO +- 2. 初始化 GPIO 控制器实例 +- 3. 设置 GPIO 引脚的输入,输出方向 +- 4. 获取 GPIO 引脚上的输入,或者设置 GPIO 引脚的输出电平 +- 5. 设置 GPIO 引脚的中断屏蔽位和中断触发类型,注册引脚的中断回调函数 + +## 4 应用示例 + +- 设置 GPIO 引脚的输出电平,获取引脚的输入电平,通过外部输入触发 GPIO 中断 + +### [fgpio_irq](../../../baremetal/example/peripheral/pin/fgpio_irq) + +- 通过 GPIO 产生占空比和频率可配的 PWM 波形 + +### [fgpio_soft_pwm](../../../baremetal/example/peripheral/pin/fgpio_soft_pwm) + +## 5. API参考 + +### 5.1. 用户数据结构 + +#### FGpioConfig + +```c +typedef struct +{ + u32 instance_id; /* GPIO实例ID */ + uintptr base_addr; /* GPIO控制器基地址 */ +#if defined(FGPIO_VERSION_1) /* FT2000-4, D2000 */ + u32 irq_num; /* GPIO控制器中断号 */ +#elif defined(FGPIO_VERSION_2) /* E2000 GPIO 0 ~ 5 */ + u32 irq_num[FGPIO_PIN_NUM]; /* GPIO各引脚的中断号 */ +#endif + u32 irq_priority; /* 中断优先级 */ +} FGpioConfig; /* GPIO控制器配置 */ +``` + +#### FGpioPin + +```c +typedef struct _FGpioPin +{ + FGpioPinId index; /* 索引 */ + u32 is_ready; + FGpio *instance; + FGpioInterruptCallback irq_cb; /* 中断回调函数, Port-A有效 */ + void *irq_cb_params; /* 中断回调函数的入参, Port-A有效 */ + boolean irq_one_time; /* Port-A有效, TRUE: 进入中断后关闭该引脚的中断,用于电平敏感中断,防止一直进入中断 */ +} FGpioPin; /* GPIO引脚实例 */ +``` + +#### FGpio + +```c +typedef struct _FGpio +{ + FGpioConfig config; + u32 is_ready; + FGpioPin *pins[FGPIO_PORT_NUM][FGPIO_PIN_NUM]; +} FGpio; /* GPIO控制器实例 */ +``` +### 5.2 错误码定义 + +- [0x0] FGPIO_SUCCESS : success + +- [0x1050000] FGPIO_ERR_INVALID_PARA : invalid input parameters + +- [0x1050001] FGPIO_ERR_INVALID_STATE : invalid state + + +### 5.3 用户API接口 + +#### FGpioLookupConfig + +```c +const FGpioConfig *FGpioLookupConfig(u32 instance_id); +``` + +Note: + +- 获取GPIO控制器的默认配置 + +Input: + +- {u32} instance_id, GPIO控制器实例号 + +Return: + +- {const FGpioConfig *} GPIO控制器的默认配置 + +#### FGpioCfgInitialize + +```c +FError FGpioCfgInitialize(FGpio *const instance, const FGpioConfig *const config); +``` + +Note: + +- 初始化GPIO控制器实例 + +Input: + +- {FGpio} *instance, GPIO控制器实例 +- {FGpioConfig} *config, GPIO控制器配置 + +Return: + +- {FError} FGPIO_SUCCESS 表示初始化成功 + +#### FGpioDeInitialize + +```c +void FGpioDeInitialize(FGpio *const instance); +``` + +Note: + +- 去初始化GPIO控制器实例 + +Input: + +- {FGpio} *instance, GPIO控制器实例 + +Return: + +- 无 + +#### FGpioPinInitialize + +```c +FError FGpioPinInitialize(FGpio *const instance, FGpioPin *const pin, + const FGpioPinId pin_id); +``` + +Note: + +- 初始化GPIO引脚实例 + +Input: + +- {FGpio} *instance, GPIO控制器实例 +- {FGpioPin} *pin_instance, GPIO引脚实例 +- {FGpioPinId} index, GPIO引脚索引 + +Return: + +- {FError} FGPIO_SUCCESS 表示初始化成功 + +#### FGpioPinDeInitialize + +```c +void FGpioPinDeInitialize(FGpioPin *const pin); +``` + +Note: + +- 去初始化GPIO引脚实例 + +Input: + +- {FGpioPin} *pin_instance, GPIO引脚实例 + +Return: + +- {FError} FGPIO_SUCCESS 表示初始化成功 + +#### FGpioGetPinIrqSourceType + +```c +FGpioIrqSourceType FGpioGetPinIrqSourceType(FGpioPin *const pin); +``` + +Note: + +- 获取引脚中断的上报方式 + +Input: + +- {FGpioPin} *pin_instance, GPIO引脚实例 + +Return: + +- {FGpioIrqSourceType} 引脚中断的上报方式 + +#### FGpioSetDirection + +```c +void FGpioSetDirection(FGpioPin *const pin, FGpioDirection dir); +``` + +Note: + +- 设置GPIO引脚的输入输出方向 +- 初始化 GPIO 实例后使用此函数 + +Input: + +- @param {FGpioPin} *instance, GPIO控制器实例 +- @param {FGpioDirection} dir, 待设置的GPIO的方向 + +Return: + +- 无 + +#### FGpioGetDirection + +```c +FGpioDirection FGpioGetDirection(FGpioPin *const pin); +``` + +Note: + +- 获取GPIO引脚的输入输出方向 +- 初始化 GPIO 实例后使用此函数 + +Input: + +- {FGpioPin} *pin, GPIO引脚实例 + +Return: + +- {FGpioDirection} GPIO引脚方向 + +#### FGpioSetOutputValue + +```c +FError FGpioSetOutputValue(FGpioPin *const pin, const FGpioPinVal output); +``` + +Note: + +- 设置GPIO引脚的输出值 +- 初始化 GPIO 实例后使用此函数,先设置 GPIO 引脚为输出后调用此函数 + +Input: + +- {FGpioPin} *pin, GPIO引脚实例 +- {FGpioPinVal} output, GPIO引脚的输出值 + +Return: + +- {FError} FGPIO_SUCCESS 表示设置成功 + +#### FGpioGetInputValue + +- 获取GPIO引脚的输入值 + +```c +FGpioPinVal FGpioGetInputValue(FGpioPin *const pin); +``` + +Note: + +- 初始化 GPIO 实例后使用此函数,先设置 GPIO 引脚为输入后调用此函数 + +Input: + +- {FGpioPin} *instance, GPIO引脚实例 +- {FGpioPinVal} output, GPIO引脚的输出值 + +Return: + +- {FGpioPinVal} 获取的输入值,高电平/低电平 + +#### FGpioGetInterruptMask + +- 获取GPIO A组引脚的中断屏蔽位 + +```c +void FGpioGetInterruptMask(FGpio *const instance, u32 *mask, u32 *enabled) +``` + +Note: + +- 获取的是A组所有Pin的中断屏蔽位和中断使能位 + +Input: + +- {FGpio} *instance, GPIO控制器实例 +- {u32} *mask, 返回的GPIO A组引脚中断屏蔽位 +- {u32} *enabled, 返回的GPIO A组中断使能位 + +Return: + +- 无 +#### FGpioSetInterruptMask + +- 设置GPIO A组引脚的中断屏蔽位 + +```c +void FGpioSetInterruptMask(FGpioPin *const pin, boolean enable); +``` + +Note: + +- index对应的引脚必须为A组引脚,B组引脚不支持中断 + +Input: + +- {FGpioPin} *pin, GPIO引脚实例 +- {boolean} enable, TRUE表示使能GPIO引脚中断,FALSE表示去使能GPIO引脚中断 + +Return: + +- 无 +#### FGpioGetInterruptType + +- 获取GPIO A组引脚的中断类型和中断极性 + +```c +void FGpioGetInterruptType(FGpio *const instance, u32 *levels, u32 *polarity) +``` + +Note: + +- 获取的是A组所有Pin的电平和极性 + +Input: + +- {FGpio} *instance, GPIO控制器实例 +- {u32} *levels, GPIO A组引脚中断电平类型 +- {u32} *polarity, GPIO A组引脚中断极性类型 + +Return: + +- 无 + +#### FGpioSetInterruptType + +- 设置GPIO引脚的中断类型 + +```c +void FGpioSetInterruptType(FGpioPin *const pin, const FGpioIrqType type) +``` + +Note: + +- index对应的引脚必须为A组引脚,B组引脚不支持中断 + +Input: + +- {FGpioPin} *pin, GPIO引脚实例 +- {FGpioIrqType} type, GPIO引脚中断触发类型 + +Return: + +- 无 + +#### FGpioInterruptHandler + +- GPIO中断处理函数 + +```c +void FGpioInterruptHandler(s32 vector, void *param) +``` + +Note: + +- 需要用户将此函数注册到Interrtup上,使能GPIO中断才能生效 + +Input: + +- {s32} vector, 中断输入参数1 +- {void} *param, 中断输入参数2 + +Return: + +- 无 + + +#### FGpioPinInterruptHandler + +- GPIO中断处理函数 + +```c +void FGpioPinInterruptHandler(s32 vector, void *param) +``` + +Note: + +- 需要用户将此函数注册Gic上,才能生效 + +Input: + +- {s32} vector, 中断输入参数1 +- {void} *param, 中断输入参数2 + +Return: + +- 无 + +#### FGpioRegisterInterruptCB + +- 注册GPIO引脚中断回调函数 + +```c +void FGpioRegisterInterruptCB(FGpioPin *const pin, FGpioInterruptCallback cb, void *cb_param, boolean irq_one_time) +``` + +Note: + +- 注册的回调函数在`FGpioInterruptHandler`中被调用 + +Input: + +- {FGpioPin} pin, GPIO引脚实例 +- {FGpioInterruptCallback} cb, GPIO引脚中断回调函数 +- {void} *cb_param, GPIO引脚中断回调函数输入参数 +- {boolean} irq_one_time, TRUE表示引脚中断触发一次后自动关闭中断,用于电平敏感中断 + +Return: + +- 无 diff --git a/bsp/phytium/libraries/standalone/doc/reference/driver/fi2c.md b/bsp/phytium/libraries/standalone/doc/reference/driver/fi2c.md new file mode 100644 index 0000000000..5321e1a7c9 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/driver/fi2c.md @@ -0,0 +1,340 @@ +# I2C 驱动程序 + +## 1. 概述 + + +- I2C 是一种串行同步半双工通信协议,总线上可以同时挂载多个主机和从机。I2C 总线由串行数据线 (SDA) 和串行时钟线 (SCL) 线构成。这些线都需要上拉电阻。 + +- I2C 具有简单且制造成本低廉等优点,主要用于低速外围设备的短距离通信(一英尺以内)。 + +- I2C 驱动支持的平台包括 FT2000/4、D2000。 + + +## 2. 驱动功能 + +I2C 驱动程序管理在 I2C 总线上设备的通信,该驱动程序具备以下功能: + +- 在主机模式下读写字节 +- 支持从机模式 + +## 3. 使用方法 + +以下部分将指导您完成 I2C 驱动的硬件配置: + +- 1. I2C驱动支持 FT2000/4, D2000和E2000,在FT2000/4上完成测试 +- 2. FT2000/4, D2000上,使用I2C驱动需要打开IO复用,I2C0的引脚默认设置为给I2C使用,I2C1/I2C2/I2C3需要先设置IO复用才能使用 +- 3. E2000上,使用MIO的的IIC驱动需要打开MIO配置功能寄存器,设置IIC模式,引脚复用设置为MIO功能,才能使用 +- 4. FT2000/4, D2000上,I2C的参考时钟为48MHz,在E2000上,I2C的参考时钟为50MHz 不支持修改设置 + +以下部分将指导您完成 I2C 驱动的软件配置: + +- 1. 配置驱动程序,新建应用工程,使能I2C驱动模块 +- 2. 设置配置参数,选择为 I2C 主机还是从机,调整默认的 I2C 通信参数(如时序、位序等) +- 3. 配置中断服务 +- 4. 处理错误码 +- 5. 去使能驱动程序 + +## 4. 应用示例 + +### [fi2c_eeprom](../../../baremetal/example/peripheral/i2c/fi2c_eeprom/README.md) + +### [fi2c_slave](../../../baremetal/example/peripheral/i2c/fi2c_master_slave/README.md) + + +## 5. API参考 + + +### 5.1. 用户数据结构 + +- drivers/i2c/fi2c/fi2c.h + +```c + +typedef struct +{ + u32 instance_id; /* Device instance id */ + uintptr base_addr; /* Device base address */ + u32 irq_num; /* Device intrrupt id */ + u32 irq_prority; /* Device intrrupt priority */ + u32 ref_clk_hz; /* Input reference clock frequency in Hz */ + u32 work_mode; /* Device work mode Slave or Master */ + u32 slave_addr; /* Slave Address writing/reading to/from */ + boolean use_7bit_addr; /* Slave in-chip address offset in 7bit or 10bit */ + u32 speed_rate; /* I2C speed rate */ +} FI2cConfig; /* Device configure setting */ +``` + +- I2C驱动实例配置 + +```c + +typedef struct +{ + FI2cConfig config; /* Current active configs */ + u32 is_ready; /* Device is initialized and ready */ + volatile u32 status; + FI2cFrameTX txframe; + FI2cFrameRX rxframe; + /** only apply to master device **/ + /* Master intrrupt handler */ + FI2cEvtHandler master_evt_handlers[FI2C_MASTER_INTR_EVT_NUM]; + + /** only apply to slave device **/ + /* Slave intrrupt handler */ + FI2cEvtHandler slave_evt_handlers[FI2C_SLAVE_INTR_EVT_NUM]; + +} FI2c; /* Device instance */ +``` + +- I2C驱动实例 + +### 5.2 错误码定义 + +- 模块错误码编号 `0x10b0000` + +- [0x0] FI2C_SUCCESS : fi2c success + +- [0x10b0001] FI2C_ERR_INVAL_PARM : fi2c invalid input parameters + +- [0x10b0002] FI2C_ERR_NOT_READY : fi2c driver not ready + +- [0x10b0001] FI2C_ERR_INVAL_PARM : fi2c invalid input parameters + +- [0x10b0003] FI2C_ERR_TIMEOUT : fi2c wait timeout + +- [0x10b0004] FI2C_ERR_NOT_SUPPORT : fi2c non support operation + +- [0x10b0005] FI2C_ERR_INVAL_STATE : fi2c invalid state + +### 5.3 用户API接口 + + +```c +const FI2cConfig *FI2cLookupConfig(u32 instance_id); +``` +- 获取I2C驱动的默认配置参数 + +Note: + + - 用户需要修改配置参数时,可以通过修改返回的FI2cConfig副本,作为FI2cCfgInitialize函数的入参, + +Input: + + - u32 instance_id, 当前控制的I2C控制器实例号 + +Return: + + - const FI2cConfig *, 返回驱动默认参数, NULL表示失败 + + +```c +FError FI2cCfgInitialize(FI2c *instance_p, const FI2cConfig *cofig_p); +``` +- 完成I2C驱动实例的初始化,使之可以使用 + +Note: + + - 此函数会将驱动实例中的所有数据全部重置,同时会进行I2C控制器的重置,请不要在I2C数据传输过程中调用此函数 + +Input: + + - FI2c *instance_p, I2C驱动实例数据 + + - const FI2cConfig *cofig_p, I2C驱动配置数据 + +Return: + + FError,参考6.2章错误码定义 + +```c +void FI2cDeInitialize(FI2c *instance_p); +``` + +- 完成I2C驱动实例去使能,清零实例数据 + +Note: + + - 此函数会将驱动实例中的所有数据全部重置, + +Input: + + - FI2c *instance_p, I2C驱动实例数据 + +Return: + + 无 + +```c +const char *FI2cErrorToMessage(FError error); +``` + +- 获取I2C模块错误码对应的错误信息 + +Note: + + - 请输入属于FI2C模块的错误码,否则返回的错误信息为空 + +Input: + + - {FError} error, I2C输入错误码 + +Return: + + - {const char *}, 错误码信息,NULL表示失败 + +```c +FError FI2cMasterReadPoll(FI2c *instance_p, u32 mem_addr, u8 mem_byte_len, u8 *buf_p, u32 buf_len); +``` +- I2C主机读,阻塞直到完成读操作或失败 + +Note: + - 请先初始化I2C驱动再调用此函数 + - 请在Master工作模式下调用此函数 + +Input: + - @param {FI2c} *instance_p I2C驱动实例数据 + - @param {u32} inchip_addr 从机的内部偏移地址 + - @param {u8} mem_byte_len, Size of internal memory address 1->8bit ~ 4->32bit + - @param {u8} *buf_p 读目的缓冲区 + - @param {int} buf_len 读目的缓冲区长度 + +Return: + - @return {FError *} 返回错误码 + +```c +FError FI2cMasterReadIntr(FI2c *instance_p, u32 mem_addr, u8 mem_byte_len, u8 *buf_p, u32 buf_len); +``` +- I2C主机读,接收中断读操作或者失败 + +Note: + - 请先初始化I2C驱动再调用此函数 + - 请在Master工作模式下调用此函数 + +Input: + - @param {FI2c} *instance_p I2C驱动实例数据 + - @param {u32} inchip_addr 从机的内部偏移地址 + - @param {u8} *buf_p 读目的缓冲区 + - @param {u8} mem_byte_len, Size of internal memory address 1->8bit ~ 4->32bit + - @param {int} buf_len 读目的缓冲区长度 + +Return: + - @return {FError *} 返回错误码 + +```c +FError FI2cMasterWriteIntr(FI2c *instance_p, u32 mem_addr, u8 mem_byte_len, const u8 *buf_p, u32 buf_len); +``` +- I2C主机读,中断发送直到完成写操作或失败 +Note: + - 请先初始化I2C驱动再调用此函数 + - 请在Master工作模式下调用此函数 + +Input: + - @param {FI2c} *instance_p I2C驱动实例数据 + - @param {u32} inchip_addr 从机的内部偏移地址 + - @param {u8} mem_byte_len, Size of internal memory address 1->8bit ~ 4->32bit + - @param {u8} *buf_p 写源缓冲区 + - @param {size_t} buf_len 写源缓冲区长度 + +Return: + - @return {FError *} 返回错误码 + +```c +FError FError FI2cMasterWritePoll(FI2c *instance_p, u32 mem_addr, u8 mem_byte_len, const u8 *buf_p, u32 buf_len); +``` +- I2C主机写,阻塞直到完成写操作或失败 + +Note: + - 请先初始化I2C驱动再调用此函数 + - 请在Master工作模式下调用此函数 + +Input: + - @param {FI2c} *instance_p I2C驱动实例数据 + - @param {u32} inchip_addr 从机的内部偏移地址 + - @param {u8} mem_byte_len, Size of internal memory address 1->8bit ~ 4->32bit + - @param {u8} *buf_p 写源缓冲区 + - @param {size_t} buf_len 写源缓冲区长度 + +Return: + - @return {FError *} 返回错误码 + +```c +u32 FI2cGetIntr(FI2c *instance_p); +``` +- 获取I2C instance_p的中断值 + +Note: + - 请先初始化I2C驱动再调用此函数 + +Input: + - @param {FI2c} *instance_p I2C驱动实例数据 + +Return: + - @return {u32} 中断状态寄存器的值 + +```c +FError FI2cMasterSetupIntr(FI2c *instance_p,u32 mask); +``` +- 设置I2C主机的中断 + +Note: + - 请先初始化I2C驱动再调用此函数 + - 请在Master工作模式下调用此函数 + +Input: + - {FI2c} *instance_p I2C驱动实例数据 + - {u32} mask 需要操作的中断寄存器位 +Return: + - {FError *} 返回错误码 + +```c +void FI2cMasterRegisterIntrHandler(FI2c *instance_p, u32 evt, FI2cEvtHandler handler); +``` +- 注册I2C主机中断事件函数 + +Note: + 无 + +Input: + - @param {FI2c} *instance_p I2C驱动实例数据 + - @param {u32} evt 中断事件,参考 FI2C_MASTER_INTR_EVT_NUM + - @param {FI2cEvtHandler} handler 中断事件回调函数 + +Return: + + 无 + +```c +FError FI2cSlaveSetupIntr(FI2c *instance_p); +``` + +- 设置I2C从机的中断 + +Note: + - 请先初始化I2C驱动再调用此函数 + - 请在Slave工作模式下调用此函数 + +Input: + - @param {FI2c} *instance_p I2C驱动实例数据 + - @param {u32} evt 中断事件,参考 FI2C_MASTER_INTR_EVT_NUM + - @param {FI2cEvtHandler} handler 中断事件回调函数 + +Return: + - {FError *} 返回错误码 + +```c +void FI2cSlaveRegisterIntrHandler(FI2c *instance_p, u32 evt, FI2cEvtHandler handler); +``` + +- 注册I2C从机中断事件函数 + +Note: + 无 + +Input: + - @param {FI2c} *instance_p I2C驱动实例数据 + - @param {u32} evt 中断事件,参考 FI2C_SLAVE_INTR_EVT_NUM + - @param {FI2cEvtHandler} handler 中断事件回调函数 + +Return: + + 无 diff --git a/bsp/phytium/libraries/standalone/doc/reference/driver/figs/pwm_duty.png b/bsp/phytium/libraries/standalone/doc/reference/driver/figs/pwm_duty.png new file mode 100644 index 0000000000000000000000000000000000000000..8de9c1cd8236ea874da38eb6f33f0c3ab7c6ab95 GIT binary patch literal 24636 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/doc/reference/driver/figs/spi_app.png b/bsp/phytium/libraries/standalone/doc/reference/driver/figs/spi_app.png new file mode 100644 index 0000000000000000000000000000000000000000..8dbd4fcdc1438cf1738675e4d9dd94a0baff9b50 GIT binary patch literal 40146 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 Ms+LNjl2!2k0fj1_$^ZZW literal 0 HcmV?d00001 diff --git a/bsp/phytium/libraries/standalone/doc/reference/driver/fmio.md b/bsp/phytium/libraries/standalone/doc/reference/driver/fmio.md new file mode 100644 index 0000000000..02fd557322 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/driver/fmio.md @@ -0,0 +1,180 @@ +# I2C 驱动程序 + +## 1. 概述 + +- E2000 的 MIO 接口可配置为 UART 或 I2C,具体时序特性要求需满足复用功能的时序特性要求。当需要使用对应的功能时,只需要软件进行相应配置. +- MIO 仅仅支持E2000系列芯片 + +## 2. 驱动功能 + +MIO 驱动程序管理MIO的功能复用,该驱动程序具备以下功能: + +- 选择设置I2C模式或者串口模式 +- 获取状态信息和版本信息 + +## 3. 使用方法 + +以下部分将指导您完成 MIO 驱动的硬件配置: + +- 1. MIO驱动支持 E2000 Q D S,在E2000上完成测试 +- 2. 本驱动仅仅作为IIC、串口的功能开启关闭使用,搭配串口驱动,与IIC驱动去使用,无法单独调用去实现具体功能 + +以下部分将指导您完成 MIO 驱动的软件配置: + +- 1. 配置驱动程序,新建应用工程,传入设备ID参数,获取设备参数 +- 2. 得到设备参数,操作设置配置项目 +- 3. 进行I2C或者uart操作配置流程 + +## 5. API参考 + +### 5.1. 用户数据结构 + +- drivers/mio/fmio/fmio.h + +```c + +typedef struct +{ + FMioConfig config; /* mio config */ + u32 is_ready; /* mio initialize the complete flag */ +}FMioCtrl; + +``` + +```c + +typedef struct +{ + u32 instance_id; /*mio id*/ + uintptr func_base_addr; /*I2C or UART function address*/ + u32 irq_num; /* Device intrrupt id */ + uintptr mio_base_addr; /*MIO control address*/ + u32 version; /*mio version*/ + u32 function_type; /*mio function type*/ + u32 mio_status; /*mio function type status*/ +} FMioConfig; /*mio configs*/ + +``` +- MIO驱动实例 + +### 5.3 用户API接口 + +```c +const FMioConfig *FMioLookupConfig(u32 instance_id); +``` +- 获取MIO驱动的默认配置参数 + +Note: + + - 用户需要修改配置参数时,可以通过修改返回的FMioConfig副本,作为FMioSelectFunc函数的入参, + +Input: + + - u32 instance_id, 当前控制的MIO控制器实例号 + +Return: + + - const FMioConfig *, 返回驱动默认参数, NULL表示失败 + + +```c +FError FMioSelectFunc(uintptr addr, u32 mio_type); +``` +- 设置MIO驱动的功能配置 + +Note: + - 设置Mio功能 + +Input: + - uintptr addr, 当前控制器的MIO基地址 + - u32 mio_type, 想要设置的MIO功能 +Return: + - @return {FError *} 返回错误码 + +```c +u32 FMioGetFunc(uintptr addr); +``` +- 获取当前MIO的配置 + +Note: + - 获取Mio功能 + +Input: + - uintptr addr, 当前控制器的MIO基地址 + +Return: + - @return {u32} 返回MIO的状态 + +```c +u32 FMioGetVersion(uintptr addr); +``` +- 获取版本信息 + +Note: + - 获取Mio版本 + +Input: + - uintptr addr, 当前控制器的MIO基地址 + +Return: + - @return {u32} 返回MIO的版本 + +```c +FError FMioFuncInit(FMioCtrl *instance_p, u32 mio_type) +``` +- 初始化MIO + +Note: + - 初始化MIO的功能 + +Input: + - FMioCtrl *instance_p, 当前控制器的结构体 + - u32 mio_type,需要配置的选项,串口还是IIC + +Return: + - @return {u32} 返回初始化的状态 + +```c +FError FMioFuncDeinit(FMioCtrl *instance_p) +``` + +- 去初始化MIO + +Note: + - 去初始化MIO的结构体,和相关寄存器 + +Input: + - FMioCtrl *instance_p, 当前控制器的结构体 + +Return: + - @return {u32} 返回去初始化的状态 + +```c +uintptr FMioFuncGetAddress(FMioCtrl *instance_p,u32 mio_type); +``` +- 获取功能配置的基地址 + +Note: + - 获取功能配置的基地址,如果当前配置和目标配置不一致,则失败 + +Input: + - FMioCtrl *instance_p, 当前控制器的结构体 + - u32 mio_type, 目标配置的类型UART或者I2c + +Return: + - @return {uintptr} 返回基地址的值 + +```c +u32 FMioFuncGetIrqNum(FMioCtrl *instance_p,u32 mio_type); +``` +- 获取功能的中断号 + +Note: + - 获取功能配置的中断号,如果当前配置和目标配置不一致,则失败 + +Input: + - FMioCtrl *instance_p, 当前控制器的结构体 + - u32 mio_type, 目标配置的类型UART或者I2c + +Return: + - @return {u32} 返回中断号 \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/doc/reference/driver/fnand.md b/bsp/phytium/libraries/standalone/doc/reference/driver/fnand.md new file mode 100644 index 0000000000..75e7aab9e4 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/driver/fnand.md @@ -0,0 +1,447 @@ + + +# 驱动概述 +- NAND 作为廉价的存储介质,在大文件的存储场景中占有重要的地位,由此在嵌入式方案中,如何将此驱动做稳定做易用,尤为重要。NAND 控制器驱动具有以下特性: + +1. 支持模式 Toggle 介质模式 +2. 支持模式时序配置 ,同步模式,异步模式 +3. 支持请求之间时间间隔 +4. 采用dma 数据方式进行传输 +5. 支持时序模式下的高频时钟采样相位调节 +6. 支持硬件ECC纠错 +当前NAND 驱动控制器主要为用户提供了以下功能接口: +1. NAND 控制器状态初始化函数 +2. NAND 介质扫描接口 +3. NAND 介质page读/写接口,块擦除接口,spare space 读写接口 +4. 中断相关接口 +5. 坏块管理接口 + +## 驱动功能 + +驱动组成由以下所示 : + +. +├── fnand_bbm.c +├── fnand_bbm.h +├── fnand.c +├── fnand_dma.c +├── fnand_dma.h +├── fnand_ecc.c +├── fnand_ecc.h +├── fnand_g.c +├── fnand.h +├── fnand_hw.c +├── fnand_hw.h +├── fnand_intr.c +├── fnand_option.c +├── fnand_sinit.c +├── fnand_timing.c +├── fnand_timing.h +├── fnand_toggle.c +└── fnand_toggle.h + +其中fnand.h 为用户开发者主要使用接口,提供了以下功能: +1. Nand 控制器初始化接口 +2. 根据Nand 介质id 初始化介质页/块的大小 +3. Nand 控制器相关状态位的回调函数注册 +4. Nand 控制器中断处理函数 +5. Nand 坏块管理函数 + + +## 数据结构 +```c + typedef struct _FNand + { + u32 is_ready; /* Device is ininitialized and ready*/ + FNandConfig config; + u32 work_mode; /* NAND controler work mode */ + + /* nand flash info */ + FNandInterMode inter_mode[FNAND_CONNECT_MAX_NUM]; /* NAND controler timing work mode */ + FNandTimingMode timing_mode[FNAND_CONNECT_MAX_NUM]; + u32 nand_flash_interface[FNAND_CONNECT_MAX_NUM] ; /* Nand Flash Interface , followed by FNAND_ONFI_MODE \ FNAND_TOGGLE_MODE*/ + + struct FNandDmaBuffer dma_data_buffer; /* DMA data buffer */ + struct FNandDmaBuffer descriptor_buffer; /* DMA descriptor */ + struct FNandDmaDescriptor descriptor[2]; /* DMA descriptor */ + struct FNandSdrTimings sdr_timing; /* SDR NAND chip timings */ + + /* bbm */ + FNandBadBlockManager bbt_manager[FNAND_CONNECT_MAX_NUM]; /* bad block manager handler */ + /* nand detect */ + FNandNandGeometry nand_geometry[FNAND_CONNECT_MAX_NUM]; /* nand flash infomation */ + /* dma 页操作 */ + FnandIrqEventHandler irq_event_fun_p; /* Interrupt event response function */ + void *irq_args; + + FNandOperationWaitIrqCallback wait_irq_fun_p; /* The NAND controller operates the wait function */ + void *wait_args; + + /* operations */ + FNandTransferP write_p ; /* Write page function */ + FNandTransferP read_p ; /* Read page function */ + FNandTransferP write_oob_p ; /* Write page spare space function */ + FNandTransferP read_oob_p ; /* Read page spare space function */ + FNandTransferP write_hw_ecc_p ; /* Write page with hardware function */ + FNandTransferP read_hw_ecc_p ; /* Read page with hardware function */ + FNandEraseP erase_p; /* Erase block function */ + } FNand; +``` + +## 错误码定义 +``` +FNAND_ERR_OPERATION /* NAND 控制器操作NAND flash 失败 */ +FNAND_ERR_INVAILD_PARAMETER /* 当NAND 控制器配置信息不存在 */ +FNAND_IS_BUSY /* NAND 控制器操作NAND flash ,控制器正忙 */ +FNAND_OP_TIMEOUT /* NAND 控制器操作超时*/ +FNAND_VALUE_ERROR /* NAND 控制器在进行BBM 搜索过程时,获取坏块管理信息不匹配 */ +FNAND_VALUE_FAILURE /* 获取的数据与预期不相符合 */ +FNAND_NOT_FET_TOGGLE_MODE /* toggle 模式 */ +FNAND_ERR_READ_ECC /* 读取过程中,进行硬件ecc ,错误超过纠错的范围 */ +FNAND_ERR_IRQ_OP_FAILED /* 中断进行读/写/擦操作时,回调函数反馈错误 */ +FNAND_ERR_IRQ_LACK_OF_CALLBACK /* 中断进行读/写/擦操作时,缺少回调函数 */ +FNAND_ERR_IRQ_OP_FAILED /* 等待中断回应失败 */ +FNAND_ERR_NOT_MATCH /* 进行flash id 检测时,检测结果与预期不符合 */ +``` +## 应用例程 +- baremetal/example/peripheral/nand/nand_test +## API 介绍 + +### 1. FNandLookupConfig + +``` +FNandConfig *FNandLookupConfig(u32 instance_id) +``` + +#### 介绍 +- 获取当前FNand驱动默认配置 + +#### 参数 +- u32 instance_id :当前Nand驱动中对应的ID + +#### 返回 +FGicConfig * :静态默认配置 + + +### 2. FNandCfgInitialize + +``` +FError FNandCfgInitialize(FNand *instance_p,FNandConfig *config_p) +``` + +#### 介绍 +- 根据传入配置,初始化NAND驱动实例 + +#### 参数 +- FNand *instance_p FNand 控制器实例的指针 +- FNandConfig * 需要应用于示例中的配置项 + +#### 返回 +- FError :FT_SUCCESS 为初始成功 + +### 3. FNandScan + +``` +FError FNandScan(FNand *instance_p) +``` + +#### 介绍 +- Nand flash 扫描,此接口调用之后会自动扫描Nand flash 介质信息 + +#### 参数 +- FNand *instance_p FNand 控制器实例的指针 + +#### 返回 +- FError :FT_SUCCESS 为初始成功 + +### 4. FNandWritePage + +``` +FError FNandWritePage(FNand *instance_p,u32 page_addr,u8 *buffer,u32 page_copy_offset ,u32 length,u8 *oob_buffer,u32 oob_copy_offset,u32 oob_length,u32 chip_addr) +``` + +#### 介绍 +- 每次写一个页面的操作,包括写页面数据和空闲数据 ,默认会进行硬件ecc 编码写入 + +#### 参数 +- FNand *instance_p FNand 控制器实例的指针 +- u32 page_addr 页操作地址,单位为页 +- u8 *buffer 指向写入内容缓冲区的指针 +- u32 page_copy_offset 写入某一页中的具体位置,当此参数非0 时,写入的地址为 ,在page_addr 对应的页面下,0 + page_copy_offset 开始的地址,未覆盖的地方默认填入0xff +- u32 length 数据写入页面下的长度 +- u8 *oob_buffer 指向写入spare space内容 缓冲区的指针 +- u32 oob_copy_offset 写入某一页中spare space 的具体位置,当此参数非0 时,在page_addr 对应的页面下,写入的地址为 页长度 + page_copy_offset 开始的地址,未覆盖的地方默认填入0xff +- u32 oob_length spare space数据写入页面下的长度 +- u32 chip_addr 芯片地址 + +#### 返回 +- FError :FT_SUCCESS 为写入成功 + +### 5. FNandWritePageRaw + +``` +FError FNandWritePageRaw(FNand *instance_p,u32 page_addr,u8 *buffer,u32 page_copy_offset ,u32 length,u8 *oob_buffer,u32 oob_copy_offset,u32 oob_length,u32 chip_addr) +``` + +#### 介绍 +- 每次写一个页面的操作,包括写页面数据和空闲数据 ,默认不会进行硬件ecc 编码写入 + +#### 参数 +- FNand *instance_p FNand 控制器实例的指针 +- u32 page_addr 页操作地址,单位为页 +- u8 *buffer 指向写入内容缓冲区的指针 +- u32 page_copy_offset 写入某一页中的具体位置,当此参数非0 时,写入的地址为 ,在page_addr 对应的页面下,0 + page_copy_offset 开始的地址,未覆盖的地方默认填入0xff +- u32 length 数据写入页面下的长度 +- u8 *oob_buffer 指向写入spare space内容 缓冲区的指针 +- u32 oob_copy_offset 写入某一页中spare space 的具体位置,当此参数非0 时,在page_addr 对应的页面下,写入的地址为 页长度 + page_copy_offset 开始的地址,未覆盖的地方默认填入0xff +- u32 oob_length spare space数据写入页面下的长度 +- u32 chip_addr 芯片地址 + +#### 返回 +- FError :FT_SUCCESS 为写入成功 + + +### 6. FNandReadPage + +``` +FError FNandReadPage(FNand *instance_p,u32 page_addr,u8 *buffer,u32 page_copy_offset,u32 length,u8 *oob_buffer,u32 oob_copy_offset,u32 oob_length,u32 chip_addr) +``` + +#### 介绍 +- 每次读出一个页面的操作,包括读页面数据和空闲数据 ,默认会进行ecc 纠错 + +#### 参数 +``` +FNand *instance_p FNand 控制器实例的指针 +u32 page_addr 页操作地址,单位为页 +u8 *buffer 指向读出内容缓冲区的指针 +u32 page_copy_offset 读出某一页中的具体位置,当此参数非0 时,读出的地址为 ,在page_addr 对应的页面下 0 + page_copy_offset 开始的地址 +u32 length 数据读出页面下的长度 +u8 *oob_buffer 指向读出spare space内容 缓冲区的指针 +u32 oob_copy_offset 读出某一页中spare space 的具体位置,当此参数非0 时,在page_addr 对应的页面下,读出的地址为 页长度 + page_copy_offset 开始的地址 +u32 oob_length spare space数据读出的长度 +u32 chip_addr 芯片地址 +``` + + +#### 返回 +- FError :FT_SUCCESS 为读出成功 + +### 7. FNandReadPageRaw + +``` +FError FNandReadPageRaw(FNand *instance_p,u32 page_addr,u8 *buffer,u32 page_copy_offset,u32 length,u8 *oob_buffer,u32 oob_copy_offset,u32 oob_length,u32 chip_addr) +``` + +#### 介绍 +- 每次读出一个页面的操作,包括读页面数据和空闲数据 ,不会进行ecc 纠错 + +#### 参数 +``` +FNand *instance_p FNand 控制器实例的指针 +u32 page_addr 页操作地址,单位为页 +u8 *buffer 指向读出内容缓冲区的指针 +u32 page_copy_offset 读出某一页中的具体位置,当此参数非0 时,读出的地址为 ,在page_addr 对应的页面下 0 + page_copy_offset 开始的地址 +u32 length 数据读出页面下的长度 +u8 *oob_buffer 指向读出spare space内容 缓冲区的指针 +u32 oob_copy_offset 读出某一页中spare space 的具体位置,当此参数非0 时,在page_addr 对应的页面下,读出的地址为 页长度 + page_copy_offset 开始的地址 +u32 oob_length spare space数据读出的长度 +u32 chip_addr 芯片地址 +``` + + +#### 返回 +- FError :FT_SUCCESS 为读出成功 + +### 8. FNandEraseBlock + +``` +FError FNandEraseBlock(FNand *instance_p, u32 block, u32 chip_addr) +``` + +#### 介绍 +- 擦除块数据 + +#### 参数 +FNand *instance_p FNand 控制器实例的指针 +u32 block 块的位置号 +u32 chip_addr 芯片地址 + +#### 返回 +- FError :FT_SUCCESS 为写入成功 + + + +### 9. FNandReadPageOOb + +``` +FError FNandReadPageOOb(FNand *instance_p,u32 page_addr,u8 *oob_buffer,u32 oob_copy_offset,u32 oob_length,u32 chip_addr) +``` + +#### 介绍 +- 读取每一页中的 spare space 内容 + +#### 参数 +- FNand *instance_p FNand 控制器实例的指针 +- u32 page_addr 需要读取空闲空间的Row Address +- u8 * oob_buffer 指向读取数据的缓冲区 +- u32 oob_copy_offset 读出某一页中spare space 中位置的偏移,当此参数非0 时,读出的地址为 ,在page_addr 对应的页面下 page length + oob_copy_offset 开始的地址 +- u32 oob_length 需要读取是页面中spare space 中的长度 +- u32 chip_addr 芯片地址 + +#### 返回 +- FError :FT_SUCCESS 为写入成功 + +### 10. FNandWritePageOOb + + +``` +FError FNandWritePageOOb(FNand *instance_p,u32 page_addr,u8 *oob_buffer,u32 page_copy_offset,u32 oob_length,u32 chip_addr) +``` + +#### 介绍 +- 读取每一页中的 spare space 内容 + +#### 参数 +- FNand *instance_p FNand 控制器实例的指针 +- u32 page_addr 需要写入空闲空间的Row Address +- u8 * oob_buffer 指向写入数据的缓冲区 +- u32 oob_copy_offset 写入某一页中spare space 中位置的偏移,当此参数非0时,写入的地址为 ,在page_addr 对应的页面下 page length + oob_copy_offset 开始的地址 +- u32 oob_length 需要写入是页面中spare space 中的长度 +- u32 chip_addr 芯片地址 + +#### 返回 +- FError :FT_SUCCESS 为写入成功 + + +### 11. FNandSetIsrHandler + +``` +void FNandSetIsrHandler(FNand *instance_p, FnandIrqEventHandler event_p, void *irq_args) +``` + +#### 介绍 +- 初始化中断事件回调函数 + +#### 参数 +- FNand *instance_p FNand 控制器实例的指针 +- FnandIrqEventHandler event_p 中断事件回调函数 +- void *irq_args 回调函数传入参数 + +#### 返回 +无 + +### 12. FNandIrqHandler + +``` +void FNandIrqHandler(s32 vector, void *param) +``` + +#### 介绍 +- Nand 控制器中断响应函数 + +#### 参数 +- s32 vector 中断ID +- void * param 中断传入参数 + +#### 返回 +- 无 + +### 13. FNandInitBbtDesc + +``` +void FNandInitBbtDesc(FNand *instance_p) +``` + +#### 介绍 +- 坏块表初始化 + +#### 参数 +- FNand *instance_p FNand 控制器实例的指针 + +#### 返回 +- 无 + + +### 10. FNandScanBbt + +``` +FError FNandScanBbt(FNand *instance_p, u32 target_addr) +``` + +#### 介绍 +- 在Nand flash中扫描具体目标地址的坏块表 + +#### 参数 +- FNand *instance_p FNand 控制器实例的指针 +- u32 chip_addr 芯片地址 + +#### 返回 +- FError :FT_SUCCESS 为扫描成功 + +### 11. FNandIsBlockBad + +``` +FError FNandIsBlockBad(FNand *instance_p, u32 block, u32 target_addr) +``` +#### 介绍 +- 检查当前块是否为坏块 + +#### 参数 +- FNand *instance_p FNand 控制器实例的指针 +- u32 block 需要检查的块ID号 +- u32 chip_addr 芯片地址 + +#### 返回 +- FError :FT_SUCCESS 为当前块为坏块 + +### 12. FNandOperationWaitIrqRegister + +``` +void FNandOperationWaitIrqRegister(FNand *instance_p,FNandOperationWaitIrqCallback wait_irq_fun_p ,void *wait_args) +``` + +#### 参数 +- FNand *instance_p FNand 控制器实例的指针 +- FNandOperationWaitIrqCallback 用户使用读/写/擦接口过程中,等待中断完成的回调函数接口 ,用户的回调函数,驱动以FT_SUCCESS 作为成功判断,否则为失败 +- void *wait_args 用户需要传入至回调函数中的参数 + +### 返回 + +无 + + +### 13. FNandSetOption + +- 依据options 选项参数,配置对应参数 + +```c +FError FNandSetOption(FNand *instance_p,u32 options,u32 value) +``` + +#### 参数 +- FNand *instance_p FNand 控制器实例的指针 +- u32 option 具体配置项 +- u32 value 配置项中对应的参数 + diff --git a/bsp/phytium/libraries/standalone/doc/reference/driver/fpcie.md b/bsp/phytium/libraries/standalone/doc/reference/driver/fpcie.md new file mode 100644 index 0000000000..4a93dd9539 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/driver/fpcie.md @@ -0,0 +1,450 @@ +# FPCIE 驱动程序 + +## 1. 概述 + + +- PCIe总线使用端到端的连接方式,在一条PCIe链路的两端只能各连接一个设备,这两个设备互为是数据发送端和数据接收端。PCIe总线除了总线链路外,还具有多个层次,发送端发送数据时将通过这些层次,而接收端接收数据时也使用这些层次。似 + +- FPCIE 模块内置两个 PCIE 单元(PCI-E Unit,PEU),分别为 PEU0 和 PEU1。每个 PEU 包含 3 个控制器:C0、C1 和 C2。当 PEU 拆分模式为 X16 时,C1 不可见。 + +- 本模块特点如下 + +1. 支持 Root Complex 和 End Point 两种模式; +2. 共 34 lane,两路 X16(可拆分为 2 个 X8)和两路 X1; +3. 内部集成 DMA 引擎,一读一写两个通道。 + +## 2. 功能 + + +- 驱动相关的源文件如下, +- drivers/pcie/fpcie + +``` +. +├── fpcie.c +├── fpcie.h +├── fpcie.md +├── fpcie_common.h +├── fpcie_config.c +├── fpcie_dma.c +├── fpcie_dma.h +├── fpcie_ep.c +├── fpcie_g.c +├── fpcie_hw.c +├── fpcie_hw.h +├── fpcie_misc.c +├── fpcie_sinit.c +├── fpcir_intx.c +└── fspim.md +``` + +## 3. 配置方法 + +以下部分将指导您完成 PCIE 驱动的软件配置: + +- 初始化PCIE控制器 +- 通过API 获取特定设备的 bar 空间地址 +- 使用INTX 中断响应设备中断响应函数 +- 使用DMA模式完成数据通信 + +## 4. 应用示例 + +"/baremetal/example/peripheral/fpcie_probe " + +## 5. API参考 + +### 5.1 用户数据结构 + + + +- 中断注册回调函数 + +```c + typedef struct + { + void (*IntxCallBack)(void *args) ; + void *args ; + s32 bdf ; + } FPcieIntxFun; +``` + +- 初始化配置空间地址 + +```c + struct FPcieRegion { + FPcieAddr bus_start; /* Start on the bus */ + FPciePhysAddr phys_start; /* Start in physical address space */ + FPcieSize size; /* Size */ + unsigned long flags; /* Resource flags */ + FPcieAddr bus_lower; + u32 exist_flg; /* exist flg */ + }; +``` + +- 驱动配置数据 + +```c + typedef struct + { + u32 instance_id; /* Id of device */ + u32 irq_num; /* Irq number */ + uintptr_t ecam; /* The Memory way */ + uintptr_t peu0_config_address; + uintptr_t peu1_config_address; + + uintptr_t control_c0_address; + uintptr_t control_c1_address; + uintptr_t control_c2_address; + uintptr_t control_c3_address; + uintptr_t control_c4_address; + uintptr_t control_c5_address; + + u32 io_base_addr; + u32 io_size ; + u32 npmem_base_addr; + u32 npmem_size; + u64 pmem_base_addr; /* Prefetchable memory */ + u64 pmem_size; + + u8 inta_irq_num ; + u8 intb_irq_num ; + u8 intc_irq_num ; + u8 intd_irq_num ; + u8 need_skip ; + + } FPcieConfig; +``` + +- 驱动控制数据 + +```c + typedef struct + { + u32 is_ready; /* Device is ininitialized and ready*/ + FPcieConfig config; + + struct FPcieRegion mem; + struct FPcieRegion mem_prefetch; + struct FPcieRegion mem_io; + + s32 bus_max; /* 当前最大bus num */ + + FPcieIrqCallBack fpcie_dma_rx_cb; + void *dma_rx_args; + + FPcieIrqCallBack fpcie_dma_tx_cb; + void *dma_tx_args; + + FPcieIrqCallBack fpcie_dma_rx_error_cb; + void *dma_rx_error_args; + + FPcieIrqCallBack fpcie_dma_tx_error_cb; + void *dma_tx_error_args; + + FPcieIntxFun inta_fun; + + FPcieIntxFun intb_fun; + + FPcieIntxFun intc_fun; + + FPcieIntxFun intd_fun; + + } FPcie; +``` + + +- 配置空间标记参数 + +``` +#define FPCIE_REGION_MEM 0x00000000 /* PCI memory space */ +#define FPCIE_REGION_IO 0x00000001 /* PCI IO space */ +#define PCI_REGION_PREFETCH 0x00000008 /* prefetchable PCI memory */ +``` + + +- 配置空间中对应的bar 标号 + +``` +#define FPCIE_BAR_0 0 +#define FPCIE_BAR_1 1 +#define FPCIE_BAR_2 2 +#define FPCIE_BAR_3 3 +#define FPCIE_BAR_4 4 +#define FPCIE_BAR_5 5 +``` + + +### 5.2 错误码定义 + +- 模块错误码编号:0x1090000 +- [0x0] FT_SUCCESS +- [0x1090001] FPCIE_ERR_INVALID_PARAM +- [0x1090002] FPCIE_ERR_OUTOF_BUS +- [0x1090003] FPCIE_ERR_CONFIG_WRITE +- [0x1090004] FPCIE_ERR_TYPE0 +- [0x1090005] FPCIE_ERR_TIMEOUT +- [0x1090006] FPCIE_NEED_SKIP +- [0x1090007] FPCIE_NOT_FOUND + + +### 5.3 用户API接口 + + +#### FPcieLookupConfig + +- 获取FPCIE驱动的默认配置参数 + +```c +const FPcieConfig *FPcieLookupConfig(u32 instance_id) +``` + +Note: + +- 用户可以通过此接口获取驱动默认配置的副本,进行修改后,作为`FPcieCfgInitialize`函数的入参使用 + +Input: + +- u32 instance_id, 选择的FPcie控制器实例号 + +Return: + +- const FPcieConfig *, 返回的默认驱动配置,返回NULL表示失败 + + + +#### FPcieCfgInitialize + +- 初始化配置空间和FPCIE 实例 + +```c +FError FPcieCfgInitialize(FPcie *instance_p, FPcieConfig *config_p) +``` + +Note: + +- 用户可以使用'FPcieLookupConfig'所产生的配置参数进行初始化,也可以自己组织配置参数进行初始化 + +Input: + +- FPcie *instance_p 指向FPcie实例的指针。 + +- FPcieConfig *config_p 指向FPcieConfig的指针。 + +Return : + +- 成功返回 FT_SUCCESS + + +#### FPcieDmaDescSet + +- PCIE DMA描述符分组包 + +```c +FError FPcieDmaDescSet(uintptr axi_addr, + uintptr bar_addr, + u32 length, + struct FPcieDmaDescriptor *desc, + struct FPcieDmaDescriptor *next_desc) +``` + +Note: + +- 用户使用dma 方式进行PCIE 数据传输时,用此函数将源地址与目标地址进行组合打包 + + +Input: + +- uintptr axi_addr 内存地址,可以为接收地址也可为发送地址 + +- uintptr bar_addr 需要通信function中对应的bar寄存器中分配的地址空间,可以为接收地址也可为发送地址 + +- u32 length 需要传输的字节长度 + +- struct FPcieDmaDescriptor *next_desc 是下一个需要发送的描述符 + +Output: + +- struct FPcieDmaDescriptor *desc 需要要配置的描述符 + + + +#### FPcieDmaRead + +Note: + +- 通过dma的方式读取Pcie function + +```c +void FPcieDmaRead(uintptr bar_address, struct FPcieDmaDescriptor *desc) +``` + +Input: + +- uintptr bar_address 基地地址寄存器的值 + +- struct FPcieDmaDescriptor *desc 接收描述符的起始地址 + +#### FPcieDmaWrite + +- 通过dma的方式写入Pcie function + +```c +void FPcieDmaWrite(uintptr bar_address, struct FPcieDmaDescriptor *desc) +``` + +Input: + +- uintptr bar_address 基地地址寄存器的值 + +- struct FPcieDmaDescriptor *desc 发送描述符的起始地址 + + +#### FPcieDmaPollDone + +- 轮询等待DMA完成 + +```c +FError FPcieDmaPollDone(struct FPcieDmaDescriptor *desc, u32 wait_cnt) +``` + +Input: + +- struct FPcieDmaDescriptor *desc Desc是需要等待完成的dma 描述符 + +- u32 wait_cnt 是需要等待结束的计数 + +#### FPcieFetchDeviceInBus + +- 该功能用于扫描整个总线上的树形结构,并且对其中的节点进行初始化与配置空间的设置 + +```c +FError FPcieFetchDeviceInBus(FPcie *instance_p, u32 bus_num) +``` + +Input: + +- FPcie *instance_p 指向FPcie实例的指针。 + +- u32 bus_num 扫描对应总线上已经连接的网桥/端点。 + +Output: + +- FError FT_SUCCESS 为成功 + + +#### FPcieFindDeviceNum + +- 根据输入的Vendor ID 与 Device ID ,获取当前PCIE 总线上一共存在多少此类设备 + +```c +u32 FPcieFindDeviceNum(FPcie *instance_p, u32 bus_num,u32 vendor_id,u32 device_id) +``` + +Input: + +- FPcie *instance_p 指向FPcie实例的指针。 + +- u32 bus_num 需要查找的bus号 + +- u32 vendor_id 目标 Vendor ID + +- u32 device_id 目标 Device ID + +Output: + +- u32 return 所需查找设备的数量 + + + +#### FPcieGetBusDeviceBarInfo + +- 通过Vendor ID和device ID获取对应的function id、device id 和 bar 空间 + +```c +FError FPcieGetBusDeviceBarInfo(FPcie *instance_p,u32 bus,u32 vendor_id,u32 device_id,u32 bar_num ,u32 *device_p,u32 *function_p,uintptr *bar_addr_p) +``` + +Input: +- FPcie *instance_p 指向FPcie实例的指针。 + +- u32 bus 需要查找的bus号 + +- u32 vendor_id 目标 Vendor ID + +- u32 device_id 目标 Device ID + +- u32 bar_num 需要查找对应bar空间的编号 + +Output: + +- u32 * device_p 需要获取对应设备号的指针 + +- u32 * function_p 需要获取对应功能号的指针 + +- uintptr * bar_addr_p 需要获取对应bar地址空间的指针 + +- FError return FT_SUCCESS 为成功 + + +#### FPcieSearchFunByClass + +- 使用 class code 获取设备的信息 + +```c +u32 FPcieSearchFunByClass(FPcie *instance_p,u32 class_code,FPcieSearchFunNode *node_p ,u32 node_num) +``` + +Input: + +- FPcie *instance_p 指向FPcie实例的指针。 + +- u32 class_code 对应的类号 + +- FPcieSearchFunNode * node_p 是一个存放特定函数信息缓冲区的指针 + +- u32 node_num 缓冲器的数量 + +Output: + +- u32 return 输出中的实际节点的个数 + + + +#### FPcieIntxIrqHandler + +- fpcie的Intx中断服务函数 + +```c +void FPcieIntxIrqHandler(s32 vector, void *args) +``` + +Input: + +- s32 vector 中断向量号 + +- void * args 需要传入的参数 + + +#### FPcieIntxRegiterIrqHandler + +- 使用bus id、device id 和function id在INTX上注册中断响应函数 + +```c +FError FPcieIntxRegiterIrqHandler(FPcie *instance_p, + u32 bus, + u32 device, + u32 function, + FPcieIntxFun *intx_fun_p) +``` + +Input: + +- FPcie *instance_p 指向FPcie实例的指针。 + +- u32 bus 需要配置的bus id + +- u32 device 需要配置的device id + +- u32 function 需要配置的function id + +- FPcieIntxFun * intx_fun_p 是用户用来注册回调函数信息的指针 diff --git a/bsp/phytium/libraries/standalone/doc/reference/driver/fpl011.md b/bsp/phytium/libraries/standalone/doc/reference/driver/fpl011.md new file mode 100644 index 0000000000..0cff6c4bba --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/driver/fpl011.md @@ -0,0 +1,368 @@ +# FPL011 驱动程序 + +## 1. 概述 + +- 通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),通常称作UART。它将要传输的资料在串行通信与并行通信之间加以转换。作为把并行输入信号转成串行输出信号的芯片,UART通常被集成于其他通讯接口的连结上。 + +- 本模块目前支持FT2000-4/D2000/E2000 + +- 本模块可使用MIO的uart功能 +## 2. 功能 + +- 1. 支持轮询接收发送数据 + 2. 中断接收发送数据 + 3. 接收发送有独立fifo + 4. 支持回传测试模式 + +相关源文件为: +``` +fpl011 + ├── fpl011.c + ├── fpl011.h + ├── fpl011_g.c + ├── fpl011_hw.c + ├── fpl011_hw.h + ├── fpl011_intr.c + ├── fpl011_options.c + └── fpl011_sinit.c +``` + + +## 3. 配置方法 + +### 参数配置 + +#### 通信参数配置 + +FPl011SetDataFormat()并将一个FPl011Format结构传递给它。FPl011Format结构应该包含所有必需的参数。参见下面的示例。 + +``` +FPl011Format format = +{ + u32 baudrate = 115200; /* In bps, ie 1200 */ + u32 data_bits = FPL011_FORMAT_WORDLENGTH_8BIT ; /* Number of data bits */ + u32 parity = FPL011_FORMAT_NO_PARITY ; /* Parity */ + u8 stopbits = FPL011_FORMAT_1_STOP_BIT ; /* Number of stop bits */ +}; + +FPl011SetDataFormat(&uart,&format); + +``` + +#### Running UART Communication + +- Transmitting ,此接口兼容轮询与中断模式,用户传入特定buffer,函数外通过判断标志位确认发送是否完成。 + +``` +u8 buffer_p[10] ; +u32 len = 0 ; +len = FPl011Send(&uart,buffer_p,sizeof(buffer_p)) ; +``` + +- Receiving ,此接口兼容轮询与中断模式,用户传入特定buffer,函数外通过判断标志位确认发送是否完成。 + +``` +u8 buffer_p[10] ; +u32 len = 0 ; +len = FPl011Receive(&uart,buffer_p,sizeof(buffer_p)) ; +``` + +## 4 应用示例 + +baremetal/example/uart_test + +## 4. API参考 + +## FPl011Config *FPl011LookupConfig(u32 instance_id) + +初始化特定的FPl011实例,以便它可以被使用。 +Initializes a specific FPl011 instance such that it is ready to be used. + +- 参数 +instance_id 是对应驱动的编号 + + +- Parameters + +instance_id contains the ID of the device + +- 返回 + +FPl011Config *指向配置结构的指针,如果指定的设备不在系统中,则为NULL。 + + +- Return + +FPl011Config *A pointer to the configuration structure or NULL if the specified device is not in the system. + + +## u32 FPl011Send(FPl011 *uart_p, u8 *byte_p, u32 length) + +这个函数使用设备发送指定的缓冲区,采用轮询或中断驱动模式。 + +This functions sends the specified buffer using the device in either + polled or interrupt driven mode. + + +- 参数 + +uart_p 是指向FPl011实例的指针。 +byte_p 是指向要发送的数据缓冲区的指针。 +length 表示发送的字节数。任何已经放入传输FIFO的数据将被发送。 + +- Parameters +uart_p is a pointer to the FPl011 instance. +byte_p is pointer to a buffer of data to be sent. +length contains the number of bytes to be sent. Any data that was already put into the transmit FIFO will be sent. + +- 返回 + +实际发送的字节数。 + + +- Return + +The number of bytes actually sent. + +## u32 FPl011Receive(FPl011 *uart_p, u8 *byte_p, u32 length) + +此函数尝试从设备接收指定字节数的数据,并将其存储到指定的缓冲区中。 + +This function attempts to receive a specified number of bytes of data from the device and store it into the specified buffer. + +- 参数 + +uart_p 是指向FPl011实例的指针 +byte_p 是用于接收数据的缓冲区指针 +length 表示接收的字节数。 + +- Parameters + +uart_p is a pointer to the FPl011 instance +byte_p is pointer to buffer for data to be received into +length is the number of bytes to be received. + +- 返回 + +接收的字节数。 + +- Return + +The number of bytes received. + + +## void FPl011ProgramCtlReg(FPl011 *uart_p,u32 ctrl_reg) + + This function reprograms the control register according to the following + sequence mentioned in the TRM + +- 参数 +uart_p 是指向FPl011实例的指针 +ctrl_reg 想要被写入的控制寄存器的参数 + + - Parameters + +uart_p is a pointer to the FPl011 instance +ctrl_reg value to be written + + + +## void FPl011SetOperMode(FPl011 *uart_p,u8 operation_mode) + +该函数设置UART的操作模式。 UART可以操作 两种模式之一:正常模式、本地环回模式。 + +This function sets the operational mode of the UART. The UART can operate + in one of four modes: Normal, Local Loopback. + +- 参数 + +uart_p是指向FPl011实例的指针。 + +operation mode为UART模式。 + +- Parameters + + uart_p is a pointer to the FPl011 instance. + + operation_mode is the mode of the UART. + +## void FPl011SetSpecificOptions(FPl011 *uart_p, u32 options) + +为指定的驱动程序实例设置选项。 + +Sets the options for the specified driver instance. + +- Parameters + +uart_p是指向FPl011实例的指针。 + +uart_p is a pointer to the FPl011 instance. + +options contains the options to be set which are bit masks contained in the file FPl011_uart.h and named FUART_OPTION_*. + + +## void FPl011ClearSpecificOptions(FPl011 *uart_p, u32 options) + + +清除指定驱动程序实例的选项。 + +Clear the options for the specified driver instance. + +- 参数 + +uart_p是指向fpl011实例的指针。 + +options 包含要设置的选项,这些选项是包含在文件FPl011_uart.h和名为FUART_OPTION_*的位掩码。 + +- Parameters + +uart_p is a pointer to the fpl011 instance. + +options contains the options to be set which are bit masks contained in the file FPl011_uart.h and named FUART_OPTION_*. + +## FError FPl011SetBaudRate(FPl011 *uart_p, u32 baudrate) + +设置设备波特率。 + +Sets the baud rate for the device. + +- 参数 + +uart p是指向FPl011实例的指针 + +BaudRate 所要设置波特率 + +- Parameters + +uart_p is a pointer to the FPl011 instance + +BaudRate to be set + +- 返回 + +如果一切都按照预期配置,则返回FT_SUCCESS + +如果请求的速率不可用,则返回FPL011_ERROR_PARAM + +- Return + + FT_SUCCESS if everything configured as expected + + FPL011_ERROR_PARAM if the requested rate is not available + because there was too much error + +## void FPl011GetDataFormat(FPl011 *uart_p,FPl011Format *format_p) + +获取指定UART的数据格式。 + +Gets the data format for the specified UART. + +- 参数 + +uart_p 是指向fpl011实例的指针。 + +format_p 是一个指向格式结构的指针,该结构将在调用完成后包含数据格式。 + +- Parameters + +uart_p is a pointer to the fpl011 instance. + +format_p is a pointer to a format structure that will contain the data format after this call completes. + +## FError FPl011SetDataFormat(FPl011 *uart_p,FPl011Format *format_p) + +将需要的参数格式,设置至特定的串口驱动中 + +Sets the data format for the specified UART. + +- 参数 + +Uart_p 是指向fpl011实例的指针。 + +format_p 是一个指向格式结构的指针,该结构将在调用完成后包含数据格式。 + +- Parameters + +uart_p is a pointer to the fpl011 instance. + +format_p is a pointer to a format structure that will contain the data format after this call completes. + +- 返回 + + 如果一切都按照预期配置,则使用FT_SUCCESS + 如果其中一个参数无效,则返回FPL011_ERROR_PARAM + +- Return + + FT_SUCCESS if everything configured as expected + FPL011_ERROR_PARAM if one of the parameters was not valid. + + +## void FPl011SetTxFifoThreadHold(FPl011 *uart_p, u8 trigger_level) + +这个函数设置Tx FIFO触发器级别为'TriggerLevel'参数。 + +This functions sets the Tx FIFO trigger level to the 'TriggerLevel' argument. + +- 参数 + +uart_p 是指向fpl011实例的指针。 +Trigger_level包含要设置的触发器级别。 这个值从0-32 (FPL011IFLS_TXIFLSEL_1_8 - FPL011IFLS_TXIFLSEL_7_8) + + +- Parameters + +uart_p is a pointer to the fpl011 instance. + +trigger_level contains the trigger level to set. This is a value from 0-32 (FPL011IFLS_TXIFLSEL_1_8 - FPL011IFLS_TXIFLSEL_7_8) + + +## void FPl011SetRxFifoThreadhold(FPl011 *uart_p, u8 trigger_level) + +这个函数将Rx FIFO触发器级别设置为'TriggerLevel'参数。 + +This functions sets the Rx FIFO trigger level to the 'TriggerLevel' argument. + +- 参数 + +uart_p 是指向fpl011实例的指针。 +Trigger_level包含要设置的触发器级别。 这个值从0-32 (FPL011IFLS_RXIFLSEL_1_8 - FPL011IFLS_RXIFLSEL_7_8) + +- Parameters + +uart_p is a pointer to the fpl011 instance. + +trigger_level contains the trigger level to set. This is a value from 0-32 (FPL011IFLS_RXIFLSEL_1_8 - FPL011IFLS_RXIFLSEL_7_8) + +## u32 FPl011GetInterruptMask(FPl011 *uart_p) + +这个函数获取中断掩码。 + +This function gets the interrupt mask. + +- 参数 + +uart_p是指向PFl011实例的指针 + +- Parameters + +uart_p is a pointer to the PFl011 instance + + +## void FPl011SetInterruptMask(FPl011 *uart_p, u32 mask) + +这个函数设置中断掩码。 + +This function sets the interrupt mask. + +- 参数 + +uart_p是指向PFl011实例的指针 + +mask包含要启用或禁用的中断。 '1'启用中断,'0'禁用中断。 + +- Parameters + +uart_p is a pointer to the PFl011 instance + +mask contains the interrupts to be enabled or disabled. A '1' enables an interrupt, and a '0' disables. diff --git a/bsp/phytium/libraries/standalone/doc/reference/driver/fpwm.md b/bsp/phytium/libraries/standalone/doc/reference/driver/fpwm.md new file mode 100644 index 0000000000..c39a9c29a4 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/driver/fpwm.md @@ -0,0 +1,313 @@ +# FPWM 驱动程序 + +## 1. 概述 + +PWM(Pulse Width Modulation)简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在测量、通信、工控等方面。 + +## 2. 功能 + +pwm控制器驱动提供了pwm的控制访问方法, +- 初始化pwm控制器,配置相关参数,如时钟分频,周期,占空比,输出极性等 +- 计数中断与FIFO_EMPTY中断的触发等 +- E2000共有8个PWM模块(控制器),每个模块集成了两个子模块pwm0和pwm1,我们习惯性的称为channel0和channel1,在死区输出模式配置为bypass时,这两个channel可以作为两路独立的pwm输出使用,在非bypass模式下,则输出为死区配置,此时需要选择输入源是channel0还是channel1,然后配置对应项即可 + +驱动相关的源文件包括 +``` +. +├── fpwm_g.c +├── fpwm_hw.c +├── fpwm_hw.h +├── fpwm_intr.c +├── fpwm_sinit.c +├── fpwm.c +└── fpwm.h +``` + +## 3. 配置方法 + +以下部分将指导您完成 fpwm 驱动的软件配置: + +- 初始化pwm控制器 +- 配置pwm的两个通道的输出模式,以及是否死区输出等 +- 注册中断处理函数,使能中断 +- pwm的周期,频率,占空比设置方法如下: +![pwm_duty](./figs/pwm_duty.png) + +e2000的参考时钟是50M,freq为频率,duty为占空比,1/freq就是周期; +perioad对应寄存器FPWM_PERIOD_OFFSET,div对应FPWM_TIM_CTRL_OFFSET寄存器中的DIV,ccr对应寄存器FPWM_CCR_OFFSET; + +## 4 应用示例 + +### [pwm波形输出](../../../baremetal/example/peripheral/pwm) + +## 5. API参考 + +### 5.1. 用户数据结构 + +- fpwm控制数据 + +```c +typedef struct +{ + FPwmConfig config;/* Pwm配置 */ + u32 is_ready;/* Pwm初始化完成标志 */ + + u8 channel_ctrl_enable[2]; /* pwm channel ctrl enable state */ + + FPwmIntrEventHandler event_handler[FPWM_INTR_EVENT_NUM]; /* event handler for interrupt */ + void *event_param[FPWM_INTR_EVENT_NUM]; /* parameters ptr of event handler */ + +} FPwmCtrl; +``` + +- fpwm配置数据,FPwmConfig主要是pwm控制器id、基地址和中断号,FPwmDbVariableConfig主要包括用户可配置的死区参数,包括死区输出模式、输出极性、输入源选择、上下沿延迟等,FPwmVariableConfig主要包含pwm的输出参数,包括分频、周期、占空比、极性等 + +```c +typedef struct +{ + u8 instance_id;/* pwm id */ + uintptr db_base_addr; + uintptr pwm_base_addr; + + u64 base_clk; + u32 irq_num; /* pwm irq num*/ + u32 irq_prority; /* pwm irq priority */ + const char *instance_name;/* instance name */ + +}FPwmConfig;/* Pwm配置 */; + +typedef struct +{ + FPwmDbPolarity db_polarity_sel; + FPwmDbOutMode db_out_mode; + FPwmDbInMode db_in_mode; + u16 db_fall_cycle; + u16 db_rise_cycle; +}FPwmDbVariableConfig; + +typedef struct +{ + u8 tim_ctrl_enable;/* pwm time ctrl enable state */ + FPwmTimCtrlMode tim_ctrl_mode; + u16 tim_ctrl_div; + u16 pwm_period; + FPwmCtrlMode pwm_mode; + FPwmPolarity pwm_polarity; + FPwmDutySourceMode pwm_duty_source_mode; + u16 pwm_pulse; + +}FPwmVariableConfig; +``` + +- 死区输出模式 +```c +typedef enum +{ + FPWM_DB_OUT_MODE_BYPASS = 0b00, + FPWM_DB_OUT_MODE_FORBID_RISE = 0b01, + FPWM_DB_OUT_MODE_FORBID_FALL = 0b10, + FPWM_DB_OUT_MODE_ENABLE_RISE_FALL = 0b11, + FPWM_DB_OUT_MODE_NUM +} FPwmDbOutMode; +``` + +- fpwm duty比较值来源选择 +```c +typedef enum +{ + FPWM_DUTY_CCR = 0, + FPWM_DUTY_FIFO = 1, + + FPWM_DUTY_SEL_MODE_NUM + +} FPwmDutySourceMode;; +``` + +- fpwm中断事件类型 +```c +typedef enum +{ + FPWM_INTR_EVENT_COUNTER = 0, /**< Handler type for counter interrupt */ + FPWM_INTR_EVENT_FIFO_EMPTY = 1, /**< Handler type for fifo empty interrupt*/ + FPWM_INTR_EVENT_NUM +} FPwmIntrEventType; + +``` + +### 5.2 错误码定义 + +- FPWM_SUCCESS 执行成功 +- FPWM_ERR_INVAL_PARM 参数无效 +- FPWM_ERR_NOT_READY 驱动未初始化 +- FPWM_ERR_TIMEOUT 超时 +- FPWM_ERR_NOT_SUPPORT 不支持 +- FPWM_ERR_CMD_FAILED 执行失败 + +### 5.3. 用户API接口 + +#### FPwmLookupConfig + +- 获取Fata控制器默认配置 + +```c +const FPwmConfig *FPwmLookupConfig(FPwmInstance instance_id); +``` + +Note: + +- 获取默认配置参数,包括基地址、中断号等 + +Input: + +- {FPwmInstance} instance_id,pwm控制器id号 + +Return: + +- {const FPwmConfig *} pwm默认配置,返回NULL如果找不到默认配置 + +#### FPwmCfgInitialize + +- 初始化fpwm控制器, 使之可以使用 + +```c +FError FPwmCfgInitialize(FPwmCtrl *pctrl, const FPwmConfig *input_config_p); +``` + +Note: + +- 输入配置通过FPwmLookupConfig获取,用户按照需要修改后传入此函数 + +Input: + +- {FPwmCtrl} *pctrl,pwm驱动控制数据 +- {FPwmConfig} *input_config_p,pwm用户输入配置 + +Return: + +- {FError} 驱动初始化的错误码信息,FPWM_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + +#### FPwmDbVariableSet + +- 设置pwm死区输出的配置参数 + +```c +FError FPwmDbVariableSet(FPwmCtrl *pctrl, FPwmDbVariableConfig *db_cfg); +``` + +Note: + +- 设置指定pwm控制器的死区可配置参数,包括死区输出模式、输出极性、输入源选择、上下沿延迟等 + +Input: + +- {FPwmCtrl} *pctrl,pwm驱动控制数据 +- {FPwmDbVariableConfig} *db_cfg,pwm死区参数配置 + +Return: + +- {FError} 驱动初始化的错误码信息,FPWM_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + +#### FPwmVariableSet + +- 设置pwm通道的参数,使能该通道 + +```c +FError FPwmVariableSet(FPwmCtrl *pctrl, u32 channel, FPwmVariableConfig *pwm_cfg); +``` + +Note: + +- 设置指定pwm控制器的指定通道的分频、周期、占空比、极性,使能该通道 + +Input: + +- {FPwmCtrl} *pctrl,pwm驱动控制数据 +- {u32} channel,pwm通道号 +- {FPwmVariableConfig} *pwm_cfg,pwm通道配置 + +Return: + +- {FError} 驱动初始化的错误码信息,FPWM_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + + +#### FPwmPulseSet + +- 设置pwm通道的占空比比较值 + +```c +FError FPwmPulseSet(FPwmCtrl *pctrl, u32 channel, u16 pwm_ccr); +``` + +Note: + +- 设置指定pwm控制器的指定通道的占空比比较值 + +Input: + +- {FPwmCtrl} *pctrl,pwm驱动控制数据 +- {u32} channel,pwm通道号 +- {u16} pwm_ccr,占空比比较值 + +Return: + +- {FError} 驱动初始化的错误码信息,FPWM_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + +#### FPwmEnable + +- 使能pwm控制器 + +```c +void FPwmEnable(FPwmCtrl *pctrl, u32 channel); +``` + +Note: + +- 使能指定pwm控制器的指定通道 + +Input: + +- {FPwmCtrl} *pctrl,pwm驱动控制数据 +- {u32} channel,pwm通道号 + +Return: + +- {FError} 驱动初始化的错误码信息,FPWM_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + +#### FPwmRegisterInterruptHandler + +- 注册pwm中断事件函数 + +```c +void FPwmRegisterInterruptHandler(FPwmCtrl *instance_p, FPwmIntrEventType event_type, + FPwmIntrEventHandler handler, void *param); +``` + +Note: +- 无 + +Input: +- {FPwmCtrl} *instance_p,fpwm驱动控制数据 +- {FPwmIntrEventType} event_type,中断事件类型 +- {FPwmIntrEventHandler} handler,中断事件回调函数 +- {void} *param,回调函数参数 + +Return: +- 无 + +#### FPwmIntrHandler + +- pwm中断处理函数入口 + +```c +void FPwmIntrHandler(s32 vector, void *args); +``` + +Note: +- 根据中断类型,设置对应的回调函数和参数传入 + +Input: +- {s32} vector +- {void} *param, 输入参数,指向fpwm驱动控制数据 + +Return: +- 无 diff --git a/bsp/phytium/libraries/standalone/doc/reference/driver/fqspi.md b/bsp/phytium/libraries/standalone/doc/reference/driver/fqspi.md new file mode 100644 index 0000000000..1cbb485681 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/driver/fqspi.md @@ -0,0 +1,409 @@ +# FQSPI 驱动程序 + +## 1. 概述 + +- QSPI是Motorola公司推出的SPI接口的扩展,比SPI应用更加广泛。在SPI协议的基础上,Motorola公司对其功能进行了增强,大幅提升了数据交换能力。QSPI 是一种专用的通信接口,连接单、双或四(条数据线) SPI Flash 存储介质。 + +- 本驱动程序提供了FT2000/4、D2000、E2000平台的QSPI功能 + +- FT2000/4、D2000上包含 1 个通用 QSPI 接口控制器,作为QSPI Flash接口使用,片最大支持 2Gb(256MB)的容量,最大支持连接四个相同容量的Flash + + +## 2. 功能 + + +- 驱动相关的源文件如下, +- drivers/qspi/fqspi + +``` +. +├── fqspi_norflash.c +├── fqspi_norflash.h +├── fqspi_g.c +├── fqspi_hw.c +├── fqspi_hw.h +├── fqspi_sinit.c +├── fqspi.h +└── fqspi.c +``` + + +## 3. 配置方法 + + +以下部分将指导您完成 FQSPI 驱动的软件配置: + +### 3.1 使用 SFUD 通用SPI协议框架 + +- 使能 CONFIG_USE_QSPI 和 CONFIG_USE_SFUD 配置 +- 初始化 SFUD 框架 +- 调用 SFUD 提供的 API 读写 QSPI 设备 + +关于 SFUD 框架的使用,可以参考[sfud.md](./sfud.md) + +### 3.2 不使用 SFUD 通用SPI协议框架 + +- 使能 CONFIG_USE_QSPI 配置 +- 初始 QSPI 驱动 +- 调用 QSPI 提供的 API 读写 QSPI 设备,需要按照 QSPI 设备的手册实现相关的命令和协议 + +## 4 应用示例 + +### [fqspi_nor_flash](../../../baremetal/example/peripheral/qspi/qspi_nor_flash/README.md) + +### [qspi_sfud](../../../baremetal/example/storage/qspi_sfud/README.md) + +## 4. API参考 + +### 4.1 用户数据结构 + +- QSPI 驱动配置数据 +```c +typedef struct +{ + u32 instance_id; /* Id of device */ + uintptr base_addr; /* Base address of qspi */ + uintptr mem_start; /* Start address of qspi memory */ + u32 capacity; /* Flash capacity */ + u32 dev_num; /* Qspi device number */ + u32 channel; /* channel number */ +} FQspiConfig; +``` + +- QSPI 驱动控制数据 +```c +typedef struct +{ + FQspiConfig config; + FQspiRdCfgDef rd_cfg; + FQspiWrCfgDef wr_cfg; + FQspiCommandPortDef cmd_def; + FQspiCsTimingCfgDef cs_timing_cfg; + u32 is_ready; /**< Device is initialized and ready */ + u32 flash_size; /* size of QSPI flash */ +} FQspiCtrl; +``` + +- QSPI 传输命令协议,指定传输的指令、地址和修饰符、数据三者的宽度 +```c +typedef enum +{ + FQSPI_TRANSFER_1_1_1 = 0x0, + FQSPI_TRANSFER_1_1_2 = 0x1, + FQSPI_TRANSFER_1_1_4 = 0x2, + FQSPI_TRANSFER_1_2_2 = 0x3, + FQSPI_TRANSFER_1_4_4 = 0x4, + FQSPI_TRANSFER_2_2_2 = 0x5, + FQSPI_TRANSFER_4_4_4 = 0x6 +}FQspiTransferMode; +``` + +- QSPI Flash的容量大小 +```c +typedef enum +{ + FQSPI_FLASH_CAP_4MB = 0b000, + FQSPI_FLASH_CAP_8MB = 0b001, + FQSPI_FLASH_CAP_16MB = 0b010, + FQSPI_FLASH_CAP_32MB = 0b011, + FQSPI_FLASH_CAP_64MB = 0b100, + FQSPI_FLASH_CAP_128MB = 0b101, + FQSPI_FLASH_CAP_256MB = 0b110, +} FQspiFlashCapcityType; +``` + +- QSPI的SCK分频系数 +```c +typedef enum +{ + FQSPI_SCK_DIV_128 = 0x0, + FQSPI_SCK_DIV_2 = 0x1, + FQSPI_SCK_DIV_4 = 0x2, + FQSPI_SCK_DIV_8 = 0x3, + FQSPI_SCK_DIV_16 = 0x4, + FQSPI_SCK_DIV_32 = 0x5, + FQSPI_SCK_DIV_64 = 0x6 +}FQspiSckDivType; +``` + +- QSPI的地址长度格式 +```c +typedef enum +{ + FQSPI_ADDR_SEL_3 = 0x0, + FQSPI_ADDR_SEL_4 = 0x1, +}FQspiAddrType; +``` + +### 4.2 错误码定义 + +- FQSPI_SUCCESS : fqspi success +- FQSPI_INVAL_PARAM : fqspi invalid input parameters +- FQSPI_NOT_READY : fqspi driver not ready +- FQSPI_NOT_ALLIGN : fqspi address not alligned +- FQSPI_NOT_SUPPORT : fqspi not support operation +- FQSPI_TIMEOUT : fqspi wait timeout + +### 4.3 用户API接口 + +#### FQspiLookupConfig + +- 获取FQSPI驱动的默认配置参数 + +```c +const FQspiConfig *FQspiLookupConfig(u32 instance_id) +``` + +Note: + +- 用户可以通过此接口获取驱动默认配置的副本,进行修改后,作为`FQspiCfgInitialize`函数的入参使用 + +Input: + +- u32 instance_id, 选择的FQSPI控制器实例号 + +Return: + +- const FQspiConfig *, 返回的默认驱动配置,返回NULL表示失败 + + +#### FQspiCfgInitialize + +- 完成FQSPI驱动实例的初始化,使之可以使用 + +```c +FError FQspiCfgInitialize(FQspiCtrl *instance_p, const FQspiConfig *input_config_p); +``` + +Note: + +- 此函数会重置FQSPI控制器和FQSPI控制数据 + +Input: + +- FQspiCtrl *instance_p, FQSPI驱动控制数据 + +- const FQspiConfig *input_config_p, FQSPI驱动配置数据 + +Return: + +- FError, 错误码信息,FQSPI_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + + +#### FQspiDeInitialize + +- 完成FQSPI驱动实例去初始化,之后不能使用 + +```c +void FQspiDeInitialize(FQspiCtrl *instance_p) +``` + +Note: + +- 此函数会重置FQSPI控制数据 + +Input: + +- FQspiCtrl *instance_p, FQSPI驱动控制数据 + +Return: + +无 + + +#### FQspiCommandPortConfig +- 配置FQSPI命令端口寄存器的值 + +```c +FError FQspiCommandPortConfig(FQspiCtrl *pctrl) +``` + +Note: + +- 使用此函数前需要确保FQSPI驱动初始化成功 +- 配置FQSPI命令端口寄存器 + +Input: + +- FQspiCtrl *pctrl, FQSPI驱动控制数据 + +Return: + +- FError, 错误码信息,FQSPI_SUCCESS 表示数据读取成功,其它返回值表示读取失败 + + +#### FQspiRdCfgConfig + +- 配置FQSPI地址访问读配置寄存器的值 + +```c +FError FQspiRdCfgConfig(FQspiCtrl *pctrl) +``` + +Note: + +- 使用此函数前需要确保FQSPI驱动初始化成功 +- 配置QSPI地址访问读配置寄存器 + +Input: + +- FQspiCtrl *pctrl, FQSPI驱动控制数据 + +Return: + +- FError, 错误码信息,FQSPI_SUCCESS 表示数据读取成功,其它返回值表示读取失败 + + +#### FQspiSetLdPortData + +- 写FQSPI低位数据端口寄存器的值 + +```c +FError FQspiSetLdPortData(FQspiCtrl *pctrl, const u8 *buf, size_t len) +``` + +Note: + +- 使用此函数前需要确保FQSPI驱动初始化成功 +- 设置寄存器的值,可用于向flash传送数据 + +Input: + +- FQspiCtrl *pctrl, FQSPI驱动控制数据 + +- const u8 *buf, 写缓存,存储要写入的数据 + +- size_t len, 要读取的buf长度 + +Return: + +- FError, 错误码信息,FQSPI_SUCCESS 表示数据写入成功,其它返回值表示写入失败 + +#### FQspiFlashSpecialInstruction +- 读flash某些状态寄存器的值,此函数适配的flash型号为S25FS256S NorFlash芯片 + +```c +FError FQspiFlashSpecialInstruction(FQspiCtrl *pctrl, u8 cmd, u8 *buf, size_t len); +``` + +Note: + +- 使用此函数前需要确保FQSPI驱动初始化成功 +- 读取flash寄存器的值,主要支持RDID, RDSR1, RDSR2, RDCR指令 + +Input: + +- FQspiCtrl *pctrl, FQSPI驱动控制数据 + +- u8 cmd, 读寄存器状态的指令,具体参见flash芯片手册 + +- u8 *buf, 读缓存,存储读到的寄存器值 + +- size_t len, 要读取的buf长度 + +Return: + +- FError, 错误码信息,FQSPI_SUCCESS 表示数据读取成功,其它返回值表示读取失败 + +#### FQspiFlashWriteReg +- 写flash寄存器的值 + +```c +FError FQspiFlashWriteReg(FQspiCtrl *pctrl, u8 command, const u8 *buf, size_t len) +``` + +Note: + +- 使用此函数前需要确保FQSPI驱动初始化成功 + +Input: + +- FQspiCtrl *pctrl, FQSPI驱动控制数据 + +- u8 command, 写寄存器的指令 + +- const u8 *buf, 写缓存,存储写入的寄存器值 + +- size_t len, 要写入的buf长度 + +Return: + +- FError, 错误码信息,FQSPI_SUCCESS 表示数据写入成功,其它返回值表示写入失败 + +#### FQspiFlashReadDataConfig + +- 读flash配置 + +```c +FError FQspiFlashReadDataConfig(FQspiCtrl *pctrl, u8 command) +``` + +Note: + +- 使用此函数前需要确保FQSPI驱动初始化成功 +- 配置采用何种方式读flash中的数据,read、fast read、quad read + +Input: + +- FQspiCtrl *pctrl, FQSPI驱动控制数据 + +- u8 command 读flash数据的指令,具体参见flash芯片手册 + +Return: + +- FError, 错误码信息,FQSPI_SUCCESS 表示读配置成功,其它返回值表示读配置失败 + +#### FQspiFlashReadData + +- 读取norflash的数据 + +```c +size_t FQspiFlashReadData(FQspiCtrl *pctrl, u32 chip_addr, u8 *buf, size_t len) +``` + +Note: + +- 使用此函数前需要确保FQSPI驱动初始化成功 +- 使用此函数前需要使用FQspiFlashReadDataConfig函数配置读方式 + +Input: + +- FQspiCtrl *pctrl, FQSPI驱动控制数据 + +- u32 chip_addr, 读数据的起始地址 + +- u8 *buf 读缓存, 存储读到的数据 + +- size_t len, 要读取的buf长度 + +Return: + +- FError, 错误码信息,FQSPI_SUCCESS 表示数据读取成功,其它返回值表示读取失败 + +#### FQspiNorFlashWrite + +- 写norflash数据函数 + +```c +FError FQspiFlashWriteData(FQspiCtrl *pctrl, u8 command, u32 chip_addr, const u8 *buf, size_t len) +``` + +Note: + +- 使用此函数前需要确保FQSPI驱动初始化成功 + +Input: + +- FQspiCtrl *pctrl, FQSPI驱动控制数据 + +- u8 command 写flash数据的指令,具体参见flash手册 + +- u32 chip_addr, 写数据的起始地址 + +- u8 *buf 写缓存, 存储要写入的数据 + +- size_t len, 要写入的buf长度 + +Return: + +- FError, 错误码信息,FQSPI_SUCCESS 表示数据写入成功,其它返回值表示写入失败 diff --git a/bsp/phytium/libraries/standalone/doc/reference/driver/frtc.md b/bsp/phytium/libraries/standalone/doc/reference/driver/frtc.md new file mode 100644 index 0000000000..3458c320f8 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/driver/frtc.md @@ -0,0 +1,196 @@ +# RTC 驱动程序 + +## 1. 概述 + +- 实时时钟(RTC)提供可靠的系统时间,包括年月日和时分秒。 + +- 通过采用电池供电,在系统处于关机状态时,RTC也能正常工作。 + +- RTC 驱动支持的平台包括 FT2000/4、D2000。 + +## 2. 功能 + +RTC 驱动程序主要完成RTC模块的初始化、时间设置和读取,该驱动程序具备以下功能: +- 初始化RTC模块 +- 设置RTC时间 +- 读取RTC时间 + +相关源文件为: +``` +frtc + ├── frtc.c + ├── frtc.h + ├── frtc_g.c + ├── frtc_hw.c + ├── frtc_hw.h + ├── frtc_intr.c + └── frtc_sinit.c +``` + + +## 3. 配置方法 + +以下部分将指导您完成 RTC 驱动的软件配置: + +- 配置驱动程序,新建应用工程,使能RTC驱动模块 +- 设置配置参数 +- 设置RTC时间 +- 读取RTC时间 + +## 4. 应用示例 + + +### [rtc_test](../../../baremetal/example/rtc_test/README.md) + + +## 5. API参考 + + +### 5.1. 用户数据结构 + +- drivers/rtc/frtc/frtc.h + +```c +typedef struct +{ + uintptr control_base_addr; /* rtc控制寄存器基地址 */ + const char *instance_name; /* instance name */ +} FRtcConfig; /* rtc配置 */ + +typedef struct +{ + FRtcConfig config; /* rtc配置 */ + u32 is_ready; /* rtc初始化完成标志 */ +} FRtcCtrl; +``` + +- RTC时间实例配置 + +```c +typedef struct +{ + u16 year; /*Specifies the RTC Date Year. + This parameter must be a number between Min_Data = 2000 and Max_Data = 2099 */ + u8 month; /*Specifies the RTC Date Month. + This parameter must be a number between Min_Data = 1 and Max_Data = 12 */ + u8 mday; /*Specifies the RTC day of Month. + This parameter must be a number between Min_Data = 1 and Max_Data = 31 */ + u8 hour; /*Specifies the RTC Time Hour. + This parameter must be a number between Min_Data = 0 and Max_Data = 23 */ + u8 minute; /*Specifies the RTC Time Minute. + This parameter must be a number between Min_Data = 0 and Max_Data = 59 */ + u8 second; /*Specifies the RTC Time Second. + This parameter must be a number between Min_Data = 0 and Max_Data = 59 */ +} FRtcDateTime; +``` +### 5.2 错误码定义 + +- 模块错误码编号 `0x1020000` + +- [0x0] FRTC_SUCCESS : success + +- [0x1020001] FRTC_ERR_DATE_INVALID : invalid date parameters + +- [0x1020002] FRTC_ERR_TIME_INVALID : invalid time parameters + + +### 5.3. 用户API接口 + +- 获取RTC驱动的默认配置参数 + +```c +const FRtcConfig *FRtcLookupConfig(void); +``` + + Note: + + - 用户需要修改配置参数时,可以通过修改返回的FRtcConfig副本,作为后续使用函数的入参, + + Input: + + - void, 只有一个RTC模块 + + Return: + + - const FRTCConfig *, 返回驱动默认参数, NULL表示失败 + + +- 初始化RTC驱动 +```c +FError FRtcCfgInitialize(FRtcCtrl *instance_p, const FRtcConfig *config_p); +``` + + Note: + + - 用户需要修改配置参数时,可以通过修改返回的FRtcConfig副本,作为后续使用函数的入参, + + Input: + + - FRtcCtrl *instance_p, RTC驱动控制块 + - const FRtcConfig *config_p, RTC驱动配置数据 + + Return: + + - 返回初始化错误码,FRTC_SUCCESS表示初始化成功 + +- 设置RTC时间 + +```c +FError FRtcSetDateTime(FRtcCtrl *pctrl, const FRtcDateTime *date_time); +``` + + Note: + + - 此函数会根据传入的时间初始化RTC时间寄存器 + + Input: + + - FRtcCtrl *pctrl, RTC驱动实例数据 + + - const FRtcDateTime *date, 设置的RTC时间年月日时分秒 + + Return: + + - u32, 参考5.2章错误码定义 + +- 读取RTC时间 + +```c +FError FRtcGetDateTime(FRtcCtrl *pctrl, FRtcDateTime *date_time); +``` + + Note: + + - 此函数会获取当前的RTC时间 + + Input: + + - FRtcCtrl *pctrl, RTC驱动实例数据 + + - FRtcDateTime *date, 获取的RTC时间年月日时分秒 + + Return: + + - u32, 参考5.2章错误码定义 + +- 读取RTC时间戳 + +```c +time_t FRtcReadTimeStamp(FRtcCtrl *pctrl, time_t *sec, time_t *msec); +``` + + Note: + + - 此函数会读取RTC时间,并返回time_t格式的时间值 + + Input: + + - FRtcCtrl *pctrl, RTC驱动实例数据 + + - time_t *sec, 获取的秒时间戳,传入NULL表示不需要获取 + + - time_t *msec, 获取的毫秒时间戳,传入NULL表示不需要获取 + + Return: + + - time_t, 详见中的定义 diff --git a/bsp/phytium/libraries/standalone/doc/reference/driver/fsata.md b/bsp/phytium/libraries/standalone/doc/reference/driver/fsata.md new file mode 100644 index 0000000000..7ac282a90c --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/driver/fsata.md @@ -0,0 +1,310 @@ +# FSATA 驱动程序 + +## 1. 概述 + +串口硬盘SATA(Serial ATA)与以往的并口硬盘PATA(Parallel ATA)相比,数据传输速度更加快捷,并支持热插拔; +SATA总线使用嵌入式时钟信号,具备了更强的纠错能力,能对传输指令进行检查,如果发现错误会自动矫正,提高了数据传输的可靠性; + +## 2. 功能 + +AHCI控制器驱动提供了SATA的控制访问方法, +- 初始化AHCI控制器 +- 以PIO方式发送/接收数据和命令 +- 以DMA方式发送/接收数据和命令 +- 设置AHCI控制器的中断工作模式和中断响应函数 + +驱动相关的源文件包括, +``` +fsata + + ├── fsata_g.c + ├── fsata_hw.c + ├── fsata_hw.h + ├── fsata_intr.c + ├── fsata_sinit.c + ├── fsata.c + └── fsata.h +``` + +## 3. 配置方法 + +以下部分将指导您完成 fsata 驱动的软件配置: + +- 初始化ahci控制器 +- 通过协议命令读写sata数据 + +## 4 应用示例 + +### [读写sata二进制数据](../../../baremetal/example/peripheral/sata) + +### [通过文件系统使用sata](../../../baremetal/example/storage/sata_fatfs) + +## 5. API参考 + +### 5.1. 用户数据结构 + +- fsata控制数据 + +```c +typedef struct +{ + FSataConfig config; /* sata配置 */ + u32 is_ready; /* sata初始化完成标志 */ + u32 private_data; + FSataAhciIoPorts port[FSATA_AHCI_MAX_PORTS]; + u16 *ataid[FSATA_AHCI_MAX_PORTS]; + u32 n_ports; + u32 cap; /* cache of HOST_CAP register */ + u32 port_map; /* cache of HOST_PORTS_IMPL reg */ + u32 link_port_map; /*linkup port map*/ + + FSataIrqCallBack fsata_dhrs_cb; /* device-to-host register fis interrupt */ + void *dhrs_args; + FSataIrqCallBack fsata_pss_cb; /* pio setup fis interrupt */ + void *pss_args; + FSataIrqCallBack fsata_dss_cb; /* dma setup fis interrupt */ + void *dss_args; + FSataIrqCallBack fsata_sdbs_cb; /* set device bits interrupt */ + void *sdbs_args; + FSataIrqCallBack fsata_pcs_cb; /* port connect change status interrupt */ + void *pcs_args; +} FSataCtrl; +``` + +- fsata配置数据 + +```c +typedef struct +{ + uintptr base_addr; /* sata控制寄存器基地址 */ + const char *instance_name; /* instance name */ + u32 irq_num; /* Irq number */ +} FSataConfig; /* sata配置 */ +``` + +- fsata port memmory +```c +typedef struct +{ + uintptr port_mmio; + FSataAhciCommandList *cmd_list; /* Command List structure, will include cmd_tbl's address */ + uintptr cmd_tbl_base_addr; /* command table addr, also the command table's first part */ + FSataAhciCommandTablePrdt *cmd_tbl_prdt; /* command table's second part , cmd_tbl + cmd_tbl_prdt = command table*/ + FSataAhciRecvFis *rx_fis; /* Received FIS Structure */ + uintptr mem; + FSataInfo dev_info; +} FSataAhciIoPorts; +``` + +- fsata属性 +```c +typedef struct +{ + unsigned char if_type; /* type of the interface */ + unsigned char part_type; /* partition type */ + unsigned char type; /* device type */ + unsigned char removable; /* removable device */ + char vendor[BLK_VEN_SIZE + 1]; /* device vendor string */ + char product[BLK_PRD_SIZE + 1]; /* device product number */ + char revision[BLK_REV_SIZE + 1]; /* firmware revision */ + unsigned long lba; /* number of blocks */ + unsigned long blksz; /* block size */ + +}FSataInfo; +``` + +- fsata中断类型和中断事件 +```c +#define FSATA_PORT_IRQ_COLD_PRES BIT(31) /* cold presence detect */ +#define FSATA_PORT_IRQ_TF_ERR BIT(30) /* task file error */ +#define FSATA_PORT_IRQ_HBUS_ERR BIT(29) /* host bus fatal error */ +#define FSATA_PORT_IRQ_HBUS_DATA_ERR BIT(28) /* host bus data error */ +#define FSATA_PORT_IRQ_IF_ERR BIT(27) /* interface fatal error */ +#define FSATA_PORT_IRQ_IF_NONFATAL BIT(26) /* interface non-fatal error */ +#define FSATA_PORT_IRQ_OVERFLOW BIT(24) /* xfer exhausted available S/G */ +#define FSATA_PORT_IRQ_BAD_PMP BIT(23) /* incorrect port multiplier */ + +#define FSATA_PORT_IRQ_PHYRDY BIT(22) /* PhyRdy changed */ +#define FSATA_PORT_IRQ_DEV_ILCK BIT(7) /* device interlock */ +#define FSATA_PORT_IRQ_CONNECT BIT(6) /* port connect change status */ +#define FSATA_PORT_IRQ_SG_DONE BIT(5) /* descriptor processed */ +#define FSATA_PORT_IRQ_UNK_FIS BIT(4) /* unknown FIS rx'd */ +#define FSATA_PORT_IRQ_SDB_FIS BIT(3) /* Set Device Bits FIS rx'd */ +#define FSATA_PORT_IRQ_DMAS_FIS BIT(2) /* DMA Setup FIS rx'd */ +#define FSATA_PORT_IRQ_PIOS_FIS BIT(1) /* PIO Setup FIS rx'd */ +#define FSATA_PORT_IRQ_D2H_REG_FIS BIT(0) /* D2H Register FIS rx'd */ +``` + +### 5.2 错误码定义 + +- FSATA_SUCCESS : success +- FSATA_ERR_INVAILD_PARAMETER : 参数无效 +- FSATA_ERR_TIMEOUT : 数据或者命令传输等待超时 +- FSATA_ERR_OPERATION : 错误操作 +- FSATA_UNKNOWN_DEVICE : 未知设备 + +### 5.3. 用户API接口 + +#### FSataLookupConfig + +- 获取Fata控制器默认配置 + +```c +const FSataConfig *FSataLookupConfig(void); +``` + +Note: + +- 获取默认配置参数,包括中断号,instance_name等 + +Input: + +- {void} + +Return: + +- {const FSataConfig *} fsata默认配置,返回NULL如果找不到默认配置 + +#### FSataCfgInitialize + +- 初始化fsata控制器, 使之可以使用 + +```c +FError FSataCfgInitialize(FSataCtrl *instance_p, const FSataConfig *input_config_p); +``` + +Note: + +- 输入配置通过FSataLookupConfig获取,用户按照需要修改后传入此函数 + +Input: + +- {FSataCtrl} *instance_p fsata驱动控制数据 +- {FSataConfig} *input_config_p fsata用户输入配置 + +Return: + +- {FError} 驱动初始化的错误码信息,FSATA_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + +#### FSataAhciInit + +- 初始化ahci, 使之可以使用 + +```c +FError FSataAhciInit(FSataCtrl *instance_p); +``` + +Note: + +- 包含ahci初始化和port memory的内存分配输入,用户需保证mem的大小足够 + +Input: + +- {FSataCtrl} *instance_p fsata驱动控制数据 + +Return: + +- {FError} 驱动初始化的错误码信息,FSATA_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + + +#### FSataAhciReadInfo + +- 读取sata信息,包括型号、容量等,通过串口输出 + +```c +FError FSataAhciReadInfo(FSataCtrl *instance_p, u8 port); +``` + +Note: + +- 输入配置通过FSataLookupConfig获取,用户按照需要修改后传入此函数 + +Input: + +- {FSataCtrl} *instance_p fsata驱动控制数据 +- {u8} port fsata的port端口号 + +Return: + +- {FError} 驱动初始化的错误码信息,FSATA_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + +#### FSataReadWrite + +- pio方式读写sata数据 + +```c +FError FSataReadWrite(FSataCtrl *instance_p, u8 port, u32 start, + u16 blk_cnt, u8 *buffer, u8 is_write); +``` + +Note: + +- 以pio方式读写sata数据,需注意传入的buffer是否完整 + +Input: + +- {FSataCtrl} *instance_p fsata驱动控制数据 +- {u8} port fsata的port端口号 +- {u32} start 读写的起始block +- {u16} blk_cnt 读写的block个数 +- {u8} *buffer 读写数据的缓存地址 +- {u8} is_write 读/写的标志位 + +Return: + +- {FError} 驱动初始化的错误码信息,FSATA_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + +#### FSataFPDmaReadWrite + +- dma方式读写sata数据 + +```c +FError FSataFPDmaReadWrite(FSataCtrl *instance_p, u8 port, u32 start, + u16 blk_cnt, u8 *buffer, u8 is_write); +``` + +Note: + +- 以dma方式读写sata数据,需注意传入的buffer是否完整 + +Input: + +- {FSataCtrl} *instance_p fsata驱动控制数据 +- {u8} port fsata的port端口号 +- {u32} start 读写的起始block +- {u16} blk_cnt 读写的block个数 +- {u8} *buffer 读写数据的缓存地址 +- {u8} is_write 读/写的标志位 + +Return: + +- {FError} 驱动初始化的错误码信息,FSATA_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + +/* set specific sata irq function entry */ +FError FSataSetHandler(FSataCtrl *instance_p, u32 handler_type, + void *func_pointer, void *call_back_ref); + + +#### FSataSetHandler + +- 设置各中断的处理函数 + +```c +FError FSataSetHandler(FSataCtrl *instance_p, u32 handler_type, + void *func_pointer, void *call_back_ref); +``` + +Note: + +- 根据中断类型,设置对应的回调函数和参数传入 + +Input: + +- {FSataCtrl} *instance_p fsata驱动控制数据 +- {u32} handler_type 中断类型 +- {void} *func_pointer 回调函数入口 +- {void} *call_back_ref 回调函数参数 + +Return: + +- {FError} 驱动初始化的错误码信息,FSATA_SUCCESS 表示初始化成功,其它返回值表示初始化失败 diff --git a/bsp/phytium/libraries/standalone/doc/reference/driver/fsdio.md b/bsp/phytium/libraries/standalone/doc/reference/driver/fsdio.md new file mode 100644 index 0000000000..ec8029e973 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/driver/fsdio.md @@ -0,0 +1,436 @@ +# FSDIO 驱动程序 + +## 1. 概述 + +SD/SDIO/eMMC控制器,主要支持SD卡,eMMC介质的访问能力,同时支持连接SDIO接口设备,目前 FSDIO 驱动已经支持 SD 卡的访问 + +## 2. 功能 + +FSDIO 驱动提供了SD/MMC卡的控制访问方法, +- 初始化SD/MMC控制器 +- 以轮询方式发送/接收数据和命令 + +访问SD/MMC卡需要兼容一系列协议命令,这一部分驱动不提供,可以通过第三方框架sdmmc使用 + +驱动相关的源文件包括, +``` +fsdio + ├── fsdio.c + ├── fsdio.h + ├── fsdio_cmd.c + ├── fsdio_dma.c + ├── fsdio_g.c + ├── fsdio_hw.h + ├── fsdio_intr.c + ├── fsdio_pio.c + ├── fsdio_selftest. +``` + +## 3. 配置方法 + +以下部分将指导您完成 FSDIO 驱动的软件配置: + +- 初始化 FSDIO 控制器 +- 通过协议命令完成 SD/MMC 卡初始化 +- 通过协议命令读写 SD/MMC 卡数据 + +## 4 应用示例 + + +### [通过协议命令读写SD卡](../../../baremetal/example/storage/sdio_cmds) + +## 5. API参考 + +### 5.1. 用户数据结构 + +#### FSdio + +- SDIO intance + +```c +typedef struct _FSdio +{ + FSdioConfig config; /* Current active configs */ + u32 is_ready; /* Device is initialized and ready */ + FSdioIDmaDescList desc_list; /* DMA descriptor list, valid in DMA trans mode */ + FSdioEvtHandler evt_handlers[FSDIO_NUM_OF_EVT]; /* call-backs for interrupt event */ + void *evt_args[FSDIO_NUM_OF_EVT]; /* arguments for event call-backs */ +} FSdio; /* SDIO intance */ +``` + +#### FSdioConfig + +- SDIO intance configuration + +```c +typedef struct +{ + u32 instance_id; /* Device instance id */ + uintptr base_addr; /* Device base address */ + u32 irq_num; /* Interrupt num */ + FSdioTransMode trans_mode; /* Trans mode, PIO/DMA */ + FSdioSpeedType speed; /* Trans speed type */ + FSdioVoltageType voltage; /* Card voltage type */ +} FSdioConfig; /* SDIO intance configuration */ +``` +#### FSdioCmdData + +- SDIO trans command and data + +```c +typedef struct +{ + u32 cmdidx; /* command index */ + u32 cmdarg; /* command argument */ + u32 response[4]; /* command response buffer */ + u32 flag; /* command flags */ +#define FSDIO_CMD_FLAG_NEED_INIT BIT(1) /* need initialization */ +#define FSDIO_CMD_FLAG_EXP_RESP BIT(2) /* need reply */ +#define FSDIO_CMD_FLAG_EXP_LONG_RESP BIT(3) /* need 136 bits long reply */ +#define FSDIO_CMD_FLAG_NEED_RESP_CRC BIT(4) /* need CRC */ +#define FSDIO_CMD_FLAG_EXP_DATA BIT(5) /* need trans data */ +#define FSDIO_CMD_FLAG_WRITE_DATA BIT(6) /* need trans data to write card */ +#define FSDIO_CMD_FLAG_READ_DATA BIT(7) /* need trans data to read card */ +#define FSDIO_CMD_FLAG_NEED_AUTO_STOP BIT(8) /* need auto stop after command */ +#define FSDIO_CMD_FLAG_ADTC BIT(9) /* need ADTC */ +#define FSDIO_CMD_FLAG_SWITCH_VOLTAGE BIT(10) /* need switch voltage */ + FSdioData *data_p; /* SDIO trans data */ +} FSdioCmdData; /* SDIO trans command and data */ +``` +#### FSdioIDmaDesc + +- SDIO DMA descriptor + +```c +typedef struct +{ + u32 attribute; /* ds0 */ +#define FSDIO_IDMAC_DES0_DIC BIT(1)/* 内部描述表不触发TI/RI中断 */ +#define FSDIO_IDMAC_DES0_LD BIT(2)/* 数据的最后一个描述符 */ +#define FSDIO_IDMAC_DES0_FD BIT(3)/* 数据的第一个描述符 */ +#define FSDIO_IDMAC_DES0_CH BIT(4)/* 链接下一个描述符地址 */ +#define FSDIO_IDMAC_DES0_ER BIT(5)/* 链表已经到达最后一个链表 */ +#define FSDIO_IDMAC_DES0_CES BIT(30)/* RINTSTS寄存器错误汇总 */ +#define FSDIO_IDMAC_DES0_OWN BIT(31)/* 描述符关联DMA,完成传输后该位置置0 */ + u32 non1; /* ds1 --> unused */ + u32 len; /* ds2 bit[25:13] buffer2 size,bit[12:0] buffer1 size*/ +#define FSDIO_IDMAC_DES2_BUF1_MASK GENMASK(12, 0) +#define FSDIO_IDMAC_DES2_BUF1_SIZE(x) (FSDIO_IDMAC_DES2_BUF1_MASK & (x)) +#define FSDIO_IDMAC_DES2_BUF2_MASK GENMASK(25, 13) +#define FSDIO_IDMAC_DES2_BUF2_SIZE(x) (FSDIO_IDMAC_DES2_BUF2_MASK & (x << 13)) + u32 non2; /* ds3 --> unused */ + u32 addr_lo; /* ds4 Lower 32-bits of Buffer Address Pointer 1 --> buffer 1 */ + u32 addr_hi; /* ds5 Upper 32-bits of Buffer Address Pointer 1 */ + u32 desc_lo; /* ds6 Lower 32-bits of Next Descriptor Address --> buffer 2 */ + u32 desc_hi; /* ds7 Upper 32-bits of Next Descriptor Address */ +} __attribute__ ((packed)) __attribute((aligned(4))) FSdioIDmaDesc; /* SDIO DMA descriptor */ + +``` + +### 5.2 错误码定义 + +- FSDIO_SUCCESS : 操作成功 +- FSDIO_ERR_TIMEOUT :操作超时失败 +- FSDIO_ERR_NOT_INIT :控制器未初始化 +- FSDIO_ERR_SHORT_BUF :缓冲区大小不足 +- FSDIO_ERR_NOT_SUPPORT :操作不支持 +- FSDIO_ERR_INVALID_STATE :控制器的状态不合法 +- FSDIO_ERR_TRANS_TIMEOUT :传输数据超时失败 +- FSDIO_ERR_CMD_TIMEOUT :传输命令超时失败 +- FSDIO_ERR_NO_CARD :卡不在位 +- FSDIO_ERR_BUSY : 卡处于繁忙状态 + +### 5.3. 用户API接口 + +#### FSdioLookupConfig + +```c +const FSdioConfig *FSdioLookupConfig(u32 instance_id) +``` + +Note: + +- Get the device instance default configure + +Input: + +- {u32} instance_id + +Return: + +- {const FSdioConfig *} default configure + +#### FSdioCfgInitialize + +```c +FError FSdioCfgInitialize(FSdio *const instance_p, const FSdioConfig *input_config_p) +``` + +Note: + +- initialization SDIO controller instance +- get into card-detect mode after initialization, bus width = 1, card freq = 400kHz + +Input: + +- {FSdio} *instance_p, SDIO controller instance +- {FSdioConfig} *input_config_p, SDIO controller configure + +Return: + +- {FError} FSDIO_SUCCESS if initialization success, otherwise failed + +#### FSdioDeInitialize + +```c +void FSdioDeInitialize(FSdio *const instance_p) +``` + +Note: + +- deinitialization SDIO controller instance + +Input: + +- {FSdio} *instance_p, SDIO controller instance + +Return: + +- {NONE} + +#### FSdioSetIDMAList + +```c +FError FSdioSetIDMAList(FSdio *const instance_p, volatile FSdioIDmaDesc *desc, u32 desc_num) +``` + +Note: + +- Setup DMA descriptor for SDIO controller instance + +Input: + +- {FSdio} *instance_p, SDIO controller instance +- {volatile FSdioIDmaDesc} *desc, first item in DMA descriptor lists +- {u32} desc_num, number of items in DMA descriptor lists + +Return: + +- {FError} FSDIO_SUCCESS if setup done, otherwise failed + +#### FSdioSetClkFreq + +```c +void FSdioSetClkFreq(FSdio *const instance_p, u32 input_clk_hz) +``` + +Note: + +- Set the Card clock freqency + +Input: + +- {FSdio} *instance_p, SDIO controller instance +- {u32} input_clk_hz, Card clock freqency in Hz + +Return: + +- {None} + +#### FSdioDMATransfer + +```c +FError FSdioDMATransfer(FSdio *const instance_p, FSdioCmdData *const cmd_data_p) +``` + +Note: + +- Start command and data transfer in DMA mode + +Input: + +- {FSdio} *instance_p, SDIO controller instance +- {FSdioCmdData} *cmd_data_p, contents of transfer command and data + +Return: + +- {FError} FSDIO_SUCCESS if transfer success, otherwise failed + +#### FSdioPollWaitDMAEnd + +```c +FError FSdioPollWaitDMAEnd(FSdio *const instance_p, FSdioCmdData *const cmd_data_p, FSdioRelaxHandler relax) +``` + +Note: + +- Wait DMA transfer finished by poll + +Input: + +- {FSdio} *instance_p, SDIO controller instance +- {FSdioCmdData} *cmd_data_p, contents of transfer command and data +- {FSdioRelaxHandler} relax, handler of relax when wait busy + +Return: + +- {FError} FSDIO_SUCCESS if wait success, otherwise wait failed + +#### FSdioGetInterruptMask + + +```c +u32 FSdioGetInterruptMask(FSdio *const instance_p, FSdioIntrType type) +``` + +Note: + +- Get SDIO controller interrupt mask + +Input: + +- {FSdio} *instance_p, SDIO controller instance +- {FSdioIntrType} type, Type of interrupt, controller/DMA interrupt + +Return: + +- {u32} interrupt mask bits + +#### FSdioSetInterruptMask + + +```c +void FSdioSetInterruptMask(FSdio *const instance_p, FSdioIntrType type, u32 set_mask, boolean enable) +``` + +Note: + +- Enable/Disable SDIO controller interrupt + +Input: + +- {FSdio} *instance_p, SDIO controller instance +- {FSdioIntrType} type, Type of interrupt, controller/DMA interrupt +- {u32} set_mask, interrupt mask bits +- {boolean} enable, TRUE: enable interrupt mask bits + +Return: + +- {NONE} + +#### FSdioInterruptHandler + +```c +void FSdioInterruptHandler(s32 vector, void *param) +``` + +Note: + +- Interrupt handler for SDIO instance + +Input: + +- {s32} vector, Interrupt id +- {void} *param, Interrupt params, is SDIO instance + +Return: + +- {NONE} + +#### FSdioRegisterEvtHandler + +```c +void FSdioRegisterEvtHandler(FSdio *const instance_p, FSdioEvtType evt, FSdioEvtHandler handler, void *handler_arg) +``` + +Note: + +- Register event call-back function as handler for interrupt events + +Input: + +- {FSdio} *instance_p, SDIO controller instance +- {FSdioEvtType} evt, interrupt event +- {FSdioEvtHandler} handler, event call-back function +- {void} *handler_arg, argument of event call-back function + +Return: + +- {NONE} + + +#### FSdioPIOTransfer + +```c +FError FSdioPIOTransfer(FSdio *const instance_p, FSdioCmdData *const cmd_data_p) +``` + +Note: + +- Start command and data transfer in PIO mode + +Input: + +- {FSdio} *instance_p, SDIO controller instance +- {FSdioCmdData} *cmd_data_p, contents of transfer command and data + +Return: + +- {FError} FSDIO_SUCCESS if transfer success, otherwise failed + +#### FSdioPollWaitPIOEnd + +```c +FError FSdioPollWaitPIOEnd(FSdio *const instance_p, FSdioCmdData *const cmd_data_p, FSdioRelaxHandler relax); +``` + +Note: + +- Wait PIO transfer finished by poll + +Input: + +- {FSdio} *instance_p, SDIO controller instance +- {FSdioCmdData} *cmd_data_p, contents of transfer command and data +- {FSdioRelaxHandler} relax, handler of relax when wait busy + +Return: + +- {FError} FSDIO_SUCCESS if wait success, otherwise wait failed + +#### FSdioGetCmdResponse + +```c +FError FSdioGetCmdResponse(FSdio *const instance_p, FSdioCmdData *const cmd_data_p) +``` + +Note: + +- Get cmd response and received data after wait poll status or interrupt signal + +Input: + +- {FSdio} *instance_p, SDIO controller instance +- {FSdioCmdData} *cmd_data_p, contents of transfer command and data + +Return: + +- {FError} FSDIO_SUCCESS if get success + +#### FSdioRestart + +```c +FError FSdioRestart(FSdio *const instance_p) +``` + +Note: + +- reset controller from error state + +Input: + +- {FSdio} *instance_p, instance of controller + +Return: + +- {FError} FSDIO_SUCCESS if restart success diff --git a/bsp/phytium/libraries/standalone/doc/reference/driver/fsdmmc.md b/bsp/phytium/libraries/standalone/doc/reference/driver/fsdmmc.md new file mode 100644 index 0000000000..e4805188bf --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/driver/fsdmmc.md @@ -0,0 +1,383 @@ +# FSDMMC 驱动程序 + +## 1. 概述 + +SD/MMC控制器主要提供对固态非易失性存储的内存卡的访问能力,包括多媒体存储卡(MMC,MultiMedia Card)和安全和数据保护卡(SD,Secure Digital Card)。 + +## 2. 功能 + +SD/MMC控制器驱动提供了SD/MMC卡的控制访问方法, +- 初始化SD/MMC控制器 +- 以轮询方式发送/接收数据和命令 +- 以中断方式发送/接收数据和命令 +- 设置SD/MMC控制器的中断工作模式和中断响应函数 + +访问SD/MMC卡需要兼容一系列协议命令,这一部分驱动不提供,可以通过第三方框架sdmmc使用 + +驱动相关的源文件包括, +``` +fsdmmc + ├── fsdmmc.c + ├── fsdmmc.h + ├── fsdmmc_dma.c + ├── fsdmmc_dma.h + ├── fsdmmc_g.c + ├── fsdmmc_hw.c + ├── fsdmmc_hw.h + ├── fsdmmc_intr.c + └── fsdmmc_sinit.c +``` + +## 3. 配置方法 + +以下部分将指导您完成 FSDMMC 驱动的软件配置: + +- 初始化FSDMMC控制器 +- 通过协议命令完成SD/MMC卡初始化 +- 通过协议命令读写SD/MMC卡数据 + +## 4 应用示例 + +### [检测SD卡](../../../baremetal/example/peripheral/mmc/fsdmmc_probe) + +### [SD/MMC卡协议实现](../../../third-party/sdmmc) + +### [通过协议命令读写SD卡](../../../baremetal/example/storage/sdmmc_cmds) + +### [通过文件系统使用SD卡](../../../baremetal/example/storage/sdmmc_fatfs) + +## 5. API参考 + +### 5.1. 用户数据结构 + +- FSDMMC控制数据 + +```c +typedef struct +{ + FSdmmcConfig config; /* Current active configs */ + u32 is_ready; /* Device is initialized and ready */ + FSdmmcEventHandler evt_handler[FSDMMC_EVT_NUM]; +} FSdmmc; /* Device instance */ +``` + +- FSDMMC配置数据 + +```c +typedef struct +{ + u32 instance_id; /* Device instance id */ + uintptr base_addr; /* Device base address */ + u32 irq_num[FSDMMC_INTR_NUM]; +} FSdmmcConfig; +``` + +- FSDMMC命令结构 +```c +typedef struct +{ + u32 cmdidx; + u32 cmdarg; + u32 resptype; + u32 response[4]; + u32 flag; +#define FSDMMC_CMD_FLAG_NEED_STOP BIT(0) +#define FSDMMC_CMD_FLAG_NEED_INIT BIT(1) +#define FSDMMC_CMD_FLAG_EXP_RESP BIT(2) +#define FSDMMC_CMD_FLAG_EXP_LONG_RESP BIT(3) +#define FSDMMC_CMD_FLAG_NEED_RESP_CRC BIT(4) +#define FSDMMC_CMD_FLAG_EXP_DATA BIT(5) +#define FSDMMC_CMD_FLAG_WRITE_DATA BIT(6) +#define FSDMMC_CMD_FLAG_READ_DATA BIT(7) +#define FSDMMC_CMD_FLAG_NEED_AUTO_STOP BIT(8) +#define FSDMMC_CMD_FLAG_ADTC BIT(9) + FSdmmcData *data_p; +} FSdmmcCmd; +``` + +- FSDMMC数据结构 +```c +typedef struct +{ + u8 *buf; + u32 blksz; + u32 blkcnt; + u32 datalen; +} FSdmmcData; +``` + +- FSDMMC中断类型和中断事件 +```c +enum +{ + FSDMMC_DMA_BD_INTR = 0, + FSDMMC_CMD_INTR, + FSDMMC_ERROR_INTR, + + FSDMMC_INTR_NUM +}; /* 中断类型 */ + +enum +{ + FSDMMC_EVT_CARD_REMOVED = 0, + FSDMMC_EVT_CMD_DONE, + FSDMMC_EVT_CMD_ERROR, + FSDMMC_EVT_CMD_RESP_ERROR, + FSDMMC_EVT_DATA_ERROR, + FSDMMC_EVT_DATA_READ_DONE, + FSDMMC_EVT_DATA_WRITE_DONE, + + FSDMMC_EVT_NUM +}; /* 事件类型 */ +``` + +### 5.2 错误码定义 + +- [0x0] FSDMMC_SUCCESS : success + +- [0x10c0001] FSDMMC_ERR_NOT_READY : FSDMMC控制器未初始化 + +- [0x10c0001] FSDMMC_ERR_TIMEOUT : 数据或者命令传输等待超时 + +- [0x10c0001] FSDMMC_ERR_CMD_FAILED : 命令传输失败 + +- [0x10c0001] FSDMMC_ERR_DATA_FAILED : 数据传输失败 + +- [0x10c0001] FSDMMC_ERR_CARD_NO_FOUND : 卡未检测到 + +- [0x10c0001] FSDMMC_ERR_INVALID_BUF : 数据缓冲区不合法 + +### 5.3. 用户API接口 + +#### FSdmmcLookupConfig + +- 获取FSDMMC控制器默认配置 + +```c +const FSdmmcConfig *FSdmmcLookupConfig(u32 instance_id); +``` + +Note: + +- instance_id从0开始,取决于FSDMMC控制器的个数 + +Input: + +- {u32} instance_id 驱动控制器ID + +Return: + +- {const FSdmmcConfig *} FSDMMC默认配置,返回NULL如果找不到默认配置 + +#### FSdmmcCfgInitialize + +- 初始化FSDMMC控制器, 使之可以使用 + +```c +FError FSdmmcCfgInitialize(FSdmmc *instance_p, const FSdmmcConfig *input_config_p); +``` + +Note: + +- 输入配置通过FSdmmcLookupConfig获取,用户按照需要修改后传入此函数 + +Input: + +- {FSdmmc} *instance_p FSDMMC驱动控制数据 +- {FSdmmcConfig} *input_config_p FSDMMC用户输入配置 + +Return: + +- {FError} 驱动初始化的错误码信息,FSDMMC_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + +#### FSdmmcDeInitialize + +- 去使能FSDMMC控制器, 清零实例数据 + +```c +void FSdmmcDeInitialize(FSdmmc *instance_p); +``` + +Note: + +- 无 + +Input: + +- {FSdmmc} *instance_p FSDMMC驱动控制数据 + +Return: + +- 无 + +#### FSdmmcPollTransfer + +- 通过FSDMMC轮询方式发送/接收数据和命令 + +```c +FError FSdmmcPollTransfer(FSdmmc *instance_p, FSdmmcCmd *cmd_data_p); +``` + +Note: + +- FSDMMC控制器初始化后才能调用此函数 + +Input: + +- {FSdmmc} *instance_p FSDMMC驱动控制数据 +- {FSdmmcCmd} *cmd_data_p FSDMMC数据和命令 + +Return: + +- {FError} 驱动初始化的错误码信息,FSDMMC_SUCCESS 表示发送/接收成功,其它返回值表示发送/接收失败 + +#### FSdmmcInterruptTransfer + +- 通过FSDMMC中断方式发送/接收数据和命令 + +```c +FError FSdmmcInterruptTransfer(FSdmmc *instance_p, FSdmmcCmd *cmd_data_p); +``` + +Note: + +- FSDMMC控制器初始化后才能调用此函数,使用前需要确保FSDMMC中断设置完成 + +Input: + +- {FSdmmc} *instance_p FSDMMC驱动控制数据 +- {FSdmmcCmd} *cmd_data_p FSDMMC数据和命令 + +Return: + +- {FError} 驱动初始化的错误码信息,FSDMMC_SUCCESS 表示发送/接收成功,其它返回值表示发送/接收失败 + +#### FSdmmcGetInterruptMask + +- 获取FSDMMC的中断掩码 + +```c +u32 FSdmmcGetInterruptMask(uintptr base_addr, u32 intr_type); +``` + +Note: + +- FSDMMC控制器初始化后才能调用此函数 + +Input: + +- {uintptr} base_addr FSDMMC控制器基地址 +- {u32} intr_type FSDMMC中断类型, 参考FSDMMC_INTR_NUM + +Return: + +- {u32} 中断掩码 + +#### FSdmmcSetInterruptMask + +- 设置FSDMMC的中断掩码 + +```c +void FSdmmcSetInterruptMask(uintptr base_addr, u32 intr_type, u32 mask, boolean enable); +``` + +Note: + +- FSDMMC控制器初始化后才能调用此函数 + +Input: + +- {uintptr} base_addr FSDMMC控制器基地址 +- {u32} intr_type FSDMMC中断类型, 参考FSDMMC_INTR_NUM +- {u32} mask 中断掩码 +- {boolean} enable TRUE:打开中断, FALSE:关闭中断 + +Return: + +- 无 + +#### FSdmmcCmdInterrupHandler + +- 命令中断响应函数 + +```c +void FSdmmcCmdInterrupHandler(s32 vector, void *param); +``` + +Note: + +- 此函数用于设置FSDMMC中断时注册,用户可以自定义一个中断响应函数替换此函数 + +Input: + +- vector 中断向量号 +- {void} *param 中断响应输入参数 + +Return: + +- 无 + +#### FSdmmcDmaInterrupHandler + +- DMA中断响应函数 + +```c +void FSdmmcDmaInterrupHandler(s32 vector, void *param); +``` + +Note: + +- 此函数用于设置FSDMMC中断时注册,用户可以自定义一个中断响应函数替换此函数 + +Input: + +- {s32} vector 中断向量号 +- {void} *param 中断响应输入参数 + +Return: + +- 无 + +#### FSdmmcErrInterrupHandler + +- 错误中断响应函数 + +```c +void FSdmmcErrInterrupHandler(s32 vector, void *param); +``` + +Note: + +- 此函数用于设置FSDMMC中断时注册,用户可以自定义一个中断响应函数替换此函数 + +Input: + +- {s32} vector 中断向量号 +- {void} *param 中断响应输入参数 + +Return: + +- 无 + +#### FSdmmcRegisterInterruptHandler + +- 注册中断事件响应函数 + +```c +void FSdmmcRegisterInterruptHandler(FSdmmc *instance_p, u32 event, FSdmmcEventHandler handler); +``` + +Note: + +- 此函数用于设置FSDMMC中断时注册,被注册的函数被FSdmmcCmdInterrupHandler、FSdmmcErrInterrupHandler + * 和FSdmmcDmaInterrupHandler调用 + +Input: + +- {FSdmmc} *instance_p FSDMMC驱动控制数据 +- {u32} event FSDMMC中断事件类型,参考FSDMMC_EVT_NUM +- {FSdmmcEventHandler} handler, FSDMMC中断事件响应函数 + +Return: + +- 无 \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/doc/reference/driver/fsemaphore.md b/bsp/phytium/libraries/standalone/doc/reference/driver/fsemaphore.md new file mode 100644 index 0000000000..8419c8d613 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/driver/fsemaphore.md @@ -0,0 +1,254 @@ +# FSemaphore 驱动程序 + +## 1. 概述 + +硬件信号量(Semaphore)是E2000提供的一个二值信号量模块,支持 32 个硬件信号量。每个信号量包括 UNLOCK 和 LOCKED 两个状态 + +## 2. 功能 + +FSemaphore 驱动程序主要完成 Semaphore 模块的初始化,锁的分配、获取与释放, +相关源文件为: +``` +fsemaphore + . + ├── fsemaphore.c + ├── fsemaphore.h + ├── fsemaphore_g.c + ├── fsemaphore_hw.h + └── fsemaphore_sinit.c +``` + +## 3. 配置方法 + +以下部分将指导您完成 FSemaphore 驱动的软件配置: + +- 初始化 Semaphore 控制器 +- 获取 Semaphore 锁 +- 加锁/解锁操作 + +## 4. 应用示例 + +### [Semaphore加锁解锁测试](../../../baremetal/example/peripheral/ipc/fsemaphore_test) + +## 5. API参考 + + +### 5.1. 用户数据结构 + +#### FSemaConfig + +- Semaphore控制器配置 + +```c +typedef struct +{ + u32 id; /* Semaphore控制器id */ + uintptr base_addr; /* Semaphore控制器基地址 */ +} FSemaConfig; /* Semaphore控制器配置 */ +``` + +#### FSemaLocker + +- Semaphore锁实例 + +```c +typedef struct +{ + u32 index; /* Semaphore锁id */ +#define FSEMA_LOCKER_NAME_LEN 32U + char name[FSEMA_LOCKER_NAME_LEN]; /* Semaphore锁的名字 */ + u32 owner; /* Semaphore锁的拥有者 */ + FSema *sema; /* Semaphore控制器实例 */ +} FSemaLocker; /* Semaphore锁实例 */ +``` + +#### FSema + +- Semaphore控制器实例 + +```c +typedef struct _FSema +{ + FSemaConfig config; /* Semaphore控制器配置 */ + u32 is_ready; /* Semaphore控制器初始化是否完成 */ + FSemaLocker *locker[FSEMA_NUM_OF_LOCKER]; /* Semaphore锁实例,locker[i] == NULL 表示锁尚未分配 */ +} FSema; /* Semaphore控制器实例 */ +``` +### 5.2 错误码定义 + +FSEMA_SUCCESS :成功 +FSEMA_ERR_NOT_INIT :控制器未初始化 +FSEMA_ERR_NO_AVAILABLE_LOCKER :没有空闲的锁可以分配 +FSEMA_ERR_LOCK_TIMEOUT :锁被占用,获取锁失败 +FSEMA_ERR_NO_PERMISSION :当前角色没有权限操作 + +### 5.3. 用户API接口 + +#### FSemaLoopkupConfig + +```c +const FSemaConfig *FSemaLoopkupConfig(u32 instance_id) +``` + +Note: + +- 获取Semaphore的默认配置 + +Input: + +- {u32} instance_id, Semaphore的实例id + +Return: + +- {const FSemaConfig *} Semaphore的默认配置 + +#### FSemaCfgInitialize + +```c +FError FSemaCfgInitialize(FSema *const instance, const FSemaConfig *input_config) +``` + +Note: + +- 初始化Semaphore控制器 + +Input: + +- {FSema} *instance, Semaphore控制器实例 +- {FSemaConfig} *input_config, Semaphore控制器配置 + +Return: + +- {FError} FSEMA_SUCCESS 表示初始化成功 + +#### FSemaDeInitialize + +```c +void FSemaDeInitialize(FSema *const instance) +``` + +Note: + +- 去初始化Semaphore控制器 + +Input: + +- {FSema} *instance, Semaphore控制器实例 + +Return: + +- 无 + +#### FSemaCreateLocker + +```c +FError FSemaCreateLocker(FSema *const instance, FSemaLocker *const locker) +``` + +Note: + +- 分配和创建Semaphore锁 + +Input: + +- {FSema} *instance, Semaphore控制器实例 +- {FSemaLocker} *locker, Semaphore锁的实例 + +Return: + +- {FError} FSEMA_SUCCESS 表示分配成功 + +#### FSemaDeleteLocker + +```c +FError FSemaDeleteLocker(FSemaLocker *const locker) +``` + +Note: + +- 强制解除Semaphore锁并删除锁实例 + +Input: + +- {FSemaLocker} *locker, Semaphore锁实例 + +Return: + +- {FError} FSEMA_SUCCESS 表示删除锁成功 + +#### FSemaTryLock + +```c +FError FSemaTryLock(FSemaLocker *const locker, u32 owner, u32 try_times, FSemaRelaxHandler relax_handler) +``` + +Note: + +- 尝试获取Semaphore锁 + +Input: + +- {FSemaLocker} *locker, Semaphore锁的实例 +- {u32} owner, 当前尝试获取锁的是谁 +- {u32} try_times, 尝试获取的次数 +- {FSemaRelaxHandler} relax_handler, 每次尝试获取锁失败后的relax函数 + +Return: + +- {FError} FSEMA_SUCCESS 表示成功获取锁,FSEMA_ERR_LOCK_TIMEOUT 表示锁已经被占用 + +#### FSemaUnlock + +```c +FError FSemaUnlock(FSemaLocker *const locker, u32 owner) +``` + +Note: + +- 尝试释放Semaphore锁 + +Input: + +- {FSemaLocker} *locker, Semaphore锁实例 +- {u32} owner, 当前尝试释放锁的身份 + +Return: + +- {FError} FSEMA_SUCCESS释放锁成功 + +#### FSemaUnlockAll + +```c +FError FSemaUnlockAll(FSema *const instance) +``` + +Note: + +- 强制解除所有Semaphore锁 + +Input: + +- {FSema} *instance, Semaphore控制器实例 + +Return: + +- {FError} FSEMA_SUCCESS表示强制解锁成功 + +#### FSemaIsLocked + +```c +boolean FSemaIsLocked(FSemaLocker *locker) +``` + +Note: + +- 检查指定Semaphore锁是否处于锁定状态 + +Input: + +- {FSemaLocker} *locker, Semaphore锁实例 + +Return: + +- {boolean} TRUE: 处于锁定状态 + diff --git a/bsp/phytium/libraries/standalone/doc/reference/driver/fspim.md b/bsp/phytium/libraries/standalone/doc/reference/driver/fspim.md new file mode 100644 index 0000000000..909cd4dfe5 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/driver/fspim.md @@ -0,0 +1,374 @@ +# FSPIM 驱动程序 + +## 1. 概述 + + +- SPI 总线是一种4线总线,提供了一种高速、高效率的串行通信技术。SPI 通信通常有一个主设备和一个或多个从设备,需要至少4根线,它们是 MISO(主设备数据输入)、MOSI(主设备数据输出)、SCLK(时钟)、CS(片选) + +- 其中,CS是从芯片是否被主芯片选中的控制信号,SCLK提供时钟脉冲,SDI,SDO则基于此脉冲完成数据传输 + +- 本驱动程序提供了FT2000/4、D2000平台的SPI主设备功能 + +- FT2000/4、D2000上包含 2 个通用 SPI 接口,仅作为通用 SPI 主设备使用,外部最多挂载 4 个 SPI 从设备 + + +## 2. 功能 + + +- 驱动相关的源文件如下, +- drivers/spi/fspim + +``` +. +├── fspim.c +├── fspim.h +├── fspim_g.c +├── fspim_hw.c +├── fspim_hw.h +├── fspim_intr.c +├── fspim_options.c +└── fspim_sinit.c +``` + +- FSPIM 常用于读写Nor-flash,其应用可以参考以下结构,最底层的硬件是 SPI 主机控制器和 Nor-flash, 其上由 FSPIM 驱动和 SFUD 提供 Spi Nor-flash的读写接口,面向SPI文件系统,如 LittleFS 和 SPIFFS,文件系统提供负载均衡、坏块检测和断电保护等特性,并为上层应用提供文件创建/读写等接口,使用方法参考`4 应用示例` + +![spi_app](./figs/spi_app.png) + +## 3. 配置方法 + + +以下部分将指导您完成 FSPIM 驱动的软件配置: + +### 3.1 使用 SFUD 通用SPI协议框架 + +- 使能 CONFIG_USE_FSPIM 和 CONFIG_USE_SFUD 配置 +- 初始化 SFUD 框架 +- 调用 SFUD 提供的 API 读写 SPI 从设备 + +关于 SFUD 框架的使用,可以参考[sfud.md](./sfud.md) + +### 3.2 不使用 SFUD 通用SPI协议框架 + +- 使能 CONFIG_USE_FSPIM 配置 +- 初始 FSPIM 驱动 +- 调用 FSPIM 提供的 API 读写 SPI 从设备,需要按照 SPI 从设备的手册实现相关的命令和协议 + +## 4 应用示例 + +### [SPI 回环测试](../../../baremetal/example/peripheral/spi/fspim_loopback/README.md) + +### [SPI Norflash读写测试](../../../baremetal/example/storage/spi_sfud/README.md) + +### [SPI Norflash文件系统测试-LittleFS](../../../baremetal/example/storage/littlefs_test/README.md) + +### [SPI Norflash文件系统测试-SPIFFS](../../../baremetal/example/storage/spiffs_test/README.md) + +## 4. API参考 + +### 4.1 用户数据结构 + +- SPIM 驱动配置数据 +```c +typedef struct +{ + u32 instance_id; /* Device instance id */ + uintptr base_addr; /* Device base address */ + u32 irq_num; /* Device intrrupt id */ + u32 irq_prority; /* Device intrrupt priority */ + FSpimSlaveDevice slave_dev_id; /* Slave device id */ + u32 max_freq_hz; /* Clock frequency in Hz */ + FSpimTransByte n_bytes; /* Bytes in transfer */ + FSpimCpolType cpol; /* Polarity of the clock */ + FSpimCphaType cpha; /* Phase of the clock */ + boolean en_test; /* Enable test mode */ +} FSpimConfig; +``` + +- SPIM 驱动控制数据 +```c +typedef struct +{ + FSpimConfig config; /* Current active configs */ + u32 is_ready; /* Device is initialized and ready */ + u32 length; /* Data length in transfer */ + const void *tx_buff; /* Tx buffer beg */ + void *rx_buff; /* Rx buffer beg */ + const void *tx_buff_end; /* Tx buffer end */ + void *rx_buff_end; /* Rx buffer end */ + u32 tx_fifo_len; /* Depth of tx fifo */ + u32 rx_fifo_len; /* Depth of rx fifo */ + FSpimEvtHandler evt_handler[FSPIM_INTR_EVT_NUM]; /* event handler for interrupt */ + void *evt_param[FSPIM_INTR_EVT_NUM]; /* parameters ptr of event handler */ +} FSpim; +``` + +- SPI 从设备ID,最多支持4个从设备 +```c +enum +{ + FSPIM_SLAVE_DEV_0 = 0, + FSPIM_SLAVE_DEV_1, + FSPIM_SLAVE_DEV_2, + FSPIM_SLAVE_DEV_3, + + FSPIM_NUM_OF_SLAVE_DEV +}; +``` + +- 一次 SPI 传输的数据量,一个字节或者两个字节 +```c +enum +{ + FSPIM_1_BYTE = 1, + FSPIM_2_BYTE = 2, + + FSPIM_MAX_BYTES_NUM +}; +``` + +- SPI传输,选择接收/发送 +```c +enum +{ + FSPIM_TRANS_MODE_RX_TX = 0x0, + FSPIM_TRANS_MODE_TX_ONLY = 0x1, + FSPIM_TRANS_MODE_RX_ONLY = 0x2, + FSPIM_TRANS_MODE_READ_EEPROM = 0x3, + + FSPIM_TRANS_MODE_MAX +}; +``` + +- SPI的时钟极性和相位 +```c +enum +{ + FSPIM_CPOL_LOW = 0, + FSPIM_CPOL_HIGH +}; + +enum +{ + FSPIM_CPHA_1_EDGE = 0, + FSPIM_CPHA_2_EDGE +}; +``` + +### 4.2 错误码定义 + +- 模块错误码编号:0x1060000 +- [0x0] FSPIM_SUCCESS : fspim success +- [0x1060000] FSPIM_SUCCESS : fspim success +- [0x1060001] FSPIM_ERR_INVAL_STATE : fspim invalid state +- [0x1060002] FSPIM_ERR_NOT_READY : fspim driver not ready +- [0x1060003] FSPIM_ERR_INVAL_PARAM : fspim invalid input parameters +- [0x1060004] FSPIM_ERR_BUS_BUSY : fspim bus is busy +- [0x1060005] FSPIM_ERR_NOT_SUPPORT : fspim not support operation +- [0x1060006] FSPIM_ERR_TIMEOUT : fspim wait timeout + +### 4.3 用户API接口 + +#### FSpimLookupConfig + +- 获取FSPIM驱动的默认配置参数 + +```c +const FSpimConfig *FSpimLookupConfig(u32 instance_id) +``` + +Note: + +- 用户可以通过此接口获取驱动默认配置的副本,进行修改后,作为`FSpimCfgInitialize`函数的入参使用 + +Input: + +- u32 instance_id, 选择的FSPIM控制器实例号 + +Return: + +- const FSpimConfig *, 返回的默认驱动配置,返回NULL表示失败 + + +#### FSpimCfgInitialize + +- 完成FSPIM驱动实例的初始化,使之可以使用 + +```c +FError FSpimCfgInitialize(FSpim *instance_p, const FSpimConfig *cofig_p); +``` + +Note: + +- 此函数会重置FSPIM控制器和FSPIM控制数据 + +Input: + +- FSpim *instance_p, FSPIM驱动控制数据 + +- const FSpimConfig *input_config_p, FSPIM驱动配置数据 + +Return: + +- FError, 驱动初始化的错误码信息,FSPIM_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + +#### FSpimDeInitialize + +- 完成FSPIM驱动实例去初始化,之后不能使用 + +```c +void FSpimDeInitialize(FSpim *instance_p) +``` + +Note: + +- 此函数会重置FSPIM控制数据 + +Input: + +- FSpim *instance_p, FSPIM驱动控制数据 + +Return: + +无 + +#### FSpimTransferPollFifo +- 先发送后接收数据 (阻塞处理),利用Fifo进行处理 + +```c +FError FSpimTransferPollFifo(FSpim *instance_p, const void *tx_buf, void *rx_buf, fsize_t len); +``` + +Note: + +- 使用此函数前需要确保FSPIM驱动初始化成功 +- 从函数不会使用中断,会按照TX FIFO的深度进行传输,每次发送填满TX FIFO后触发发送/接收动作 + +Input: + +- FSpim *instance_p, FSPIM驱动控制数据 + +- const void *tx_buf, 写缓冲区,可以为空,为空时表示只关注读数据,此时驱动会发送0xff读数据 + +- void *rx_buf, 读缓冲区, 可以为空,为空时表示值关注写数据,此时SPI总线上返回的数据会被抛弃 + +- fsize_t len, 进行传输的长度,如果tx_buf或者rx_buf不为空,则两个buf的长度必须为len + +Return: + +- FError, 驱动初始化的错误码信息,FSPIM_SUCCESS 表示数据交换成功,其它返回值表示交换失败 + +#### FSpimTransferByInterrupt + +- 先发送后接收数据 (中断处理),利用Fifo进行处理 + +```c +FError FSpimTransferByInterrupt(FSpim *instance_p, const void *tx_buf, void *rx_buf, fsize_t len); +``` + +Note: + +- 使用此函数前需要确保FSPIM驱动初始化成功 +- 此函数会安装TX FIFO的深度进行传输,每次发送填满TX FIFO后在中断中处理发送/接收动作,用户需要注册FSPIM_INTR_EVT_RX_DONE事件,传输过程中用户处理其它任务,等待传输完成后从tx_buf和rx_buf中读数据 + +Input: + +- FSpim *instance_p, FSPIM驱动控制数据 + +- const void *tx_buf, 写缓冲区,可以为空,为空时表示只关注读数据,此时驱动会发送0xff读数据 + +- void *rx_buf, 读缓冲区, 可以为空,为空时表示值关注写数据,此时SPI总线上返回的数据会被抛弃 + +- fsize_t len, 进行传输的长度,如果tx_buf或者rx_buf不为空,则两个buf的长度必须为len + +Return: + +- FError, 驱动初始化的错误码信息,FSPIM_SUCCESS 表示数据交换成功,其它返回值表示交换失败 + +#### FSpimInterruptHandler + +- SPIM中断处理函数 + +```c +void FSpimInterruptHandler(s32 vector, void *param); +``` + +Note: + +- 此函数给用户注册在中断处理模块中,从而在中断触发时进行处理 +- 使用此函数前,用户需要打开部分中断屏蔽位 + +Input: + +- s32 vector, 中断向量号,此处不关心此参数 + +- void *param, 中断输入参数, 指向FSPIM的驱动控制实例 + +Return: + +无 + +#### FSpimRegisterIntrruptHandler +- SPIM中断事件注册函数 + +```c +void FSpimRegisterIntrruptHandler(FSpim *instance_p, u32 evt, FSpimEvtHandler handler, void *param) +``` + +Note: + +- 此函数与FSpimInterruptHandler配套使用,为中断处理注册事件回调函数 + +Input: + +- FSpim *instance_p, FSPIM驱动控制数据 + +- u32 evt, 中断事件号码,参考FSPIM_INTR_EVT_NUM + +- FSpimEvtHandler handler, 中断事件回调函数 + +- void *param, 中断事件回调函数的入参,取决于中断事件回调函数的实现 + +Return: + +无 + +#### FSpimErrorToMessage + +- 获取FSPIM驱动错误码 + +```c +const char *FSpimErrorToMessage(FError error); +``` + +Note: + +无 + +Input: + +- FError error, FSPIM模块错误码 + +Return: + +const char *, FSPIM模块错误码对应的信息 + +#### FSpimSetChipSelection + +- 获取FSPIM驱动错误码 + +```c +void FSpimSetChipSelection(FSpim *instance_p, boolean on); +``` + +Note: + +无 + +Input: + +- {FSpim} *instance_p, 驱动控制数据 +- {boolean} on, TRUE: 片选打开, FALSE: 片选关闭 + +Return: + +- 无 diff --git a/bsp/phytium/libraries/standalone/doc/reference/driver/fusb.md b/bsp/phytium/libraries/standalone/doc/reference/driver/fusb.md new file mode 100644 index 0000000000..16c74266ae --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/driver/fusb.md @@ -0,0 +1,535 @@ + +# FUSB 驱动程序 + +## 1. 概述 + +USB是通用串行总线(Universal Serial Bus)的缩写, USB是一种简易、双向、快速、同步、即插即用(Plug and Play,PnP)且支持热插拔功能的串行接口。USB设备现在已经非常普及,如U盘、鼠标、键盘等。USB协议曾经出现过多种版本,如USB1.0、USB1.1、USB2.0、USB3.0等 + +USB总线体系中, USB主机(Host)是系统的主人, 负责USB通信过程中数据的控制和处理, USB设备(Device)是系统的从机,用于实现特定的功能,如常用的U盘、移动硬盘、鼠标、键盘、游戏手柄等,在USB传输过程中,USB主机处于主导地位,由USB主机发起数据和命令的传输,USB设备被动响应USB主机发来的请求命令,USB总线中的主要角色包括USB主机、根Hub、USB Hub和USB设备等 + +USB主机和USB设备间的通信是通过管道(Pipe)进行的,管道是指在USB主机端的一组缓冲区,用于管道中数据的收发;在USB设备端,有一个特定端点(Endpoint,ENDP)与管道对接 + +USB可靠传输(具有反馈机制)的最小单位是事务,事务就是利用令牌包、数据包和握手包实现一个带有错误反馈机制的通信,使USB传输更加安全可靠,按照令牌包的类型可以分为,Setup事务、IN事务、OUT事务等。基于事务,USB协议定义了传输(Transfer)用于完成一组具有特定目的的事务,包括控制传输、中断传输、批量传输和同步传输 + +## 2. 功能 + +- 驱动相关的源文件如下, +- drivers/usb +``` +. +├── Kconfig +├── fusb.c +├── fusb.h +├── fusb_debug.c +├── fusb_def.h +├── fusb_dev.c +├── fusb_g.c +├── fusb_generic_hub.c +├── fusb_generic_hub.h +├── fusb_hid.c +├── fusb_hid.h +├── fusb_hub.c +├── fusb_hub.h +├── fusb_msc.c +├── fusb_msc.h +├── fusb_private.h +├── fusb_sinit.c +``` + +- 关于 [XHCI](./fxhci.md) + +## 3. 配置方法 + +参考以下步骤完成 FUSB 硬件配置, + +- 1. FUSB驱动支持 E2000,在 E2000 上完成测试 + +参考以下步骤完成 FUSB 软件配置, + +- 1. 选择USB控制器类型为 XHCI +- 2. 配置 FUSB 的内存池空间,推荐在1Mb以上 +- 3. 注册USB设备驱动 +- 4. 调用USB控制器驱动接口启动FUSB实例 +- 5. 调用USB控制器驱动接口轮询FUSB,更新USB设备状态 +- 6. 调用USB设备驱动操作USB设备 + +## 4 应用示例 + +- USB主机操作和USB设备发现 +### [fxhci_host](../../../baremetal/example/peripheral/usb/fxhci_host) + +- USB大容量存储器读写 +### [fusb_fatfs](../../../baremetal/example/storage/fusb_fatfs) + +## 5. API参考 + +### 5.1. 用户数据结构 + +#### 5.1.1 USB端点 + +```c +typedef struct +{ + FUsbDev *dev; /* device instance of this endpoint */ + int endpoint; /* endpoint address ep0 = 0, epn = n */ + FUsbDirection direction; /* type or direction of ep */ + int toggle; /* ep state for some device to toggle */ + int maxpacketsize; /* max packet size for ep transfer */ + FUsbEpType type; /* transfer type of ep, control, bulk or so on */ + int interval; /* expressed as binary logarithm of the number + of microframes (i.e. t = 125us * 2^interval) */ +} FUsbEndpoint; /* encapsulates a single endpoint of an USB device */ +``` + +#### 5.1.2 USB设备 + +```c +typedef struct _FUsbDev +{ + FUsbHc *controller; /* Hc instance where device attached */ + FUsbEndpoint endpoints[FUSB_MAX_EP_NUM]; /* all Ep instance of device */ + int num_endp; /* num of Ep in use */ + FUsbDevAddr address; /* USB address */ + FUsbDevClass class; /* USB device class, e.g hid */ + int hub; /* hub where device is attached to */ + int port; /* port where device is attached */ + FUsbSpeed speed; /* speed type of device */ + void *data; /* private data for specific type of device */ + FUsbDeviceDescriptor *descriptor; /* device descriptor ever get from device hw */ + FUsbConfigurationDescriptor *configuration; /* configure descriptor followed with interface descriptor ever get from device hw */ + FUsbConfigParser config_parser; /* parser for configure descriptor */ + FUsbStringParser string_parser; /* parser for string descriptor */ + void (*init)(FUsbDev *dev); /* device init function of specific device type for register */ + void (*destroy) (FUsbDev *dev); /* device deinit function of specific device type for register */ + void (*poll) (FUsbDev *dev); /* device poll function of specific device type for register */ +} FUsbDev; /* encapsulates a single USB device */ +``` + +#### 5.1.3 USB主机 + +```c +typedef struct _FUsbHc +{ + FUsbHc *next; /* next Hc instance in the list */ + uintptr reg_base; /* base address of Hc register */ + FUsb *usb; /* instance of USB system */ + FUsbHcType type; /* type of Hc, e.g XHCI */ + FUsbDev *devices[FUSB_MAX_DEV_NUM]; /* dev 0 is root hub, 127 is last addressable */ + + /* start(): Resume operation. */ + void (*start) (FUsbHc *controller); + /* stop(): Stop operation but keep controller initialized. */ + void (*stop) (FUsbHc *controller); + /* reset(): Perform a controller reset. The controller needs to + be (re)initialized afterwards to work (again). */ + void (*reset) (FUsbHc *controller); + /* init(): Initialize a (previously reset) controller + to a working state. */ + void (*init) (FUsbHc *controller); + /* shutdown(): Stop operation, detach host controller and shutdown + this driver instance. After calling shutdown() any + other usage of this hci_t* is invalid. */ + void (*shutdown) (FUsbHc *controller); + + FUsbTransCode (*bulk) (FUsbEndpoint *ep, int size, u8 *data, int finalize); + FUsbTransCode (*control) (FUsbDev *dev, FUsbDirection pid, int dr_length, + void *devreq, int data_length, u8 *data); + void* (*create_intr_queue) (FUsbEndpoint *ep, int reqsize, int reqcount, int reqtiming); + void (*destroy_intr_queue) (FUsbEndpoint *ep, void *queue); + u8* (*poll_intr_queue) (void *queue); + void *instance; /* instance to specific Hc implementation, e.g XHCI */ + + /* set_address(): Tell the USB device its address (xHCI + controllers want to do this by + themselves). Also, allocate the FUsbDev + structure, initialize enpoint 0 + (including MPS) and return it. */ + FUsbDev *(*set_address) (FUsbHc *controller, FUsbSpeed speed, + int hubport, int hubaddr); + /* finish_device_config(): Another hook for xHCI, returns 0 on success. */ + int (*finish_device_config) (FUsbDev *dev); + /* destroy_device(): Finally, destroy all structures that + were allocated during set_address() + and finish_device_config(). */ + void (*destroy_device) (FUsbHc *controller, int devaddr); +} FUsbHc; /* encapsulates a single USB host */ +``` + +#### 5.1.4 USB系统配置 + +```c +typedef struct +{ + void *(*malloc_align)(size_t size, size_t align); + void (*free)(void *mem); +} FUsbMemAllocator; /* memory allocator used in USB system */ + +typedef struct +{ + u32 instance_id; /* id for this USB system */ + uintptr base_addr; /* base addr of Hc register, set as 0 for pci-usb */ + u32 irq_num; + u32 irq_priority; + FUsbMemAllocator allocator; /* memory allocator to support dynamic memory */ +} FUsbConfig; /* configure data of the USB system */ +``` + +#### 5.1.4 USB系统 + +```c +typedef struct _FUsb +{ + FUsbConfig config; /* configuration of USB system */ + void *pcie_instance; /* NULL if unused */ + void *pcie_info[FUSB_MAX_CTRL_NUM]; /* NULL if unused */ + FUsbHc *hc; /* first hc, there might have multiple hc in pcie-mode */ + /* hook to set init function for specific device type */ + FUsbDevInitFunc dev_init[FUSB_MAX_DEV_TYPE_NUM]; + u32 dev_init_num; /* number of init function in used */ + u32 is_ready; /* indicator of system okay */ +} FUsb; /* instance of the USB system */ +``` + +### 5.2 错误码定义 + + +- 模块错误码编号 `0x1110000` + +- [0x0] FUSB_SUCCESS : success +- [0x1110000] FUSB_ERR_WAIT_TIMEOUT : wait for status timeout +- [0x1110001] FUSB_ERR_INVALID_PARA : invalid input parameters +- [0x1110002] FUSB_ERR_NOT_SUPPORT : parameters or feature not supported +- [0x1110003] FUSB_ERR_NON_INSTANCE : cannot find instance +- [0x1110004] FUSB_ERR_INVALID_DATA : invalid input data +- [0x1110005] FUSB_ERR_DESC_PARSE_ERR : failed to parse descriptor +- [0x1110006] FUSB_ERR_ALLOCATE_FAIL : failed to allocate memory from memory pool +- [0x1110007] FUSB_ERR_TRANS_FAIL : failed to transfer data + + +- 传输过程完成错误码 + +- [0] FUSB_CC_ZERO_BYTES : failed, transfer zero bytes +- [1] FUSB_CC_SUCCESS : transfer success with bytes unkonwn + +### 5.3. 用户API接口 + +#### FUsbLookupConfig + +```c +const FUsbConfig *FUsbLookupConfig(u32 instance_id) +``` + +Note: + +- 获取USB的默认配置 + +Input: + +- {u32} instance_id USB实例号 + +Return: + +- {const FUsbConfig *} USB默认配置 + +#### FUsbCfgInitialize + +```c +FError FUsbCfgInitialize(FUsb *instance, const FUsbConfig *input_config) +``` + +Note: + +- 初始化USB实例 +- 在PCIE模式下,USB Hc实例在PCIE总线发现控制器后创建 + +Input: + +- {FUsb} *instance, USB实例 +- {const FUsbConfig} *input_config, USB输入配置 + +Return: + +- {FError} 初始化错误码 + +#### FUsbDeInitialize + +```c +void FUsbDeInitialize(FUsb *instance); +``` + +Note: + +- 去初始化USB实例 + +Input: + +- {FUsb} *instance, USB实例 + +Return: + +- 无 + +#### FUsbPoll + +```c +void FUsbPoll(FUsb *instance) +``` + +Note: + +- 轮询USB控制器连接的所有设备, 更新设备拓扑 + +Input: + +- {FUsb} *instance, USB实例 + +Return: + +- 无 + +#### FUsbExit + +```c +void FUsbExit(FUsb *instance) +``` + +Note: + +- 关闭所有的USB控制器,移除所有连接的设备 + +Input: + +- {FUsb} *instance, USB实例 + +Return: + +- 无 + + +#### FUsbAssignDevInitFunc + +```c +FError FUsbAssignDevInitFunc(FUsb *instance, const FUsbDevIndex *index, FUsbDevInitHandler handler) +``` + +Note: + +- 指定特定USB设备的初始化函数,供创建USB设备实例时使用 + +Input: + +- {FUsb} *instance, USB实例 +- {FUsbDevIndex} *index, 特定USB设备的索引 +- {FUsbDevInitHandler} handler, 特定USB设备的初始化函数 + +Return: + +- {FError} 处理返回错误码 + +#### FUsbGetAllDevEntries + +```c +size_t FUsbGetAllDevEntries(FUsbHc *controller, FUsbDev *devs[], size_t max_dev_num) +``` + +Note: + +- 获取USB控制器上连接的所有USB设备实例 + +Input: + +- {FUsbHc} *controller, USB控制器实例 +- {FUsbDev} *devs, 放置USB设备实例的缓冲区 +- {size_t} max_dev_num, 最多可以获取的USB设备实例数目 + +Return: + +- {size_t} 实际获取的USB设备实例数目 + +#### FUsbSetFeature + +```c +FUsbTransCode FUsbSetFeature(FUsbDev *dev, int endp, int feature, int rtype) +``` + +Note: + +- 标准USB主机请求,使能设备/接口/端点的某个特性 + +Input: + +- {FUsbDev} *dev, USB设备实例 +- {int} endp, 设备号(0x00)/接口号/端点号 +- {int} feature, 待使能的特性 +- {int} rtype, 请求类型,由FUsbGenerateReqType生成 + +Return: + +- {FUsbTransCode} 控制传输的返回值,小于0表示失败,大于0表示成功传输的字节数目 + +#### FUsbGetStatus + +```c +FUsbTransCode FUsbGetStatus(FUsbDev *dev, int intf, int rtype, int len, void *data) +``` + +Note: + +- 标准USB主机请求,获取设备/接口/端点的状态 + +Input: + +- {FUsbDev} *dev, USB设备实例 +- {int} intf,设备号(0x00)/接口号/端点号 +- {int} rtype, 请求类型,由FUsbGenerateReqType生成 +- {int} len, Data Stage的数据长度 +- {void} *data, Data Stage的数据缓冲区 + +Return: + +- {FUsbTransCode} 控制传输的返回值,小于0表示失败,大于0表示成功传输的字节数目 + +#### FUsbGetDescriptor + +```c +FUsbTransCode FUsbGetDescriptor(FUsbDev *dev, int rtype, FUsbDescriptorType desc_type, int desc_idx, void *data, size_t len) +``` + +Note: + +- 标准USB主机请求,获取指定描述符 + +Input: + +- {FUsbDev} *dev, USB设备实例 +- {int} rtype, 请求类型,由FUsbGenerateReqType生成 +- {FUsbDescriptorType} desc_type, 描述符类型 +- {int} desc_idx, 描述符索引 +- {void} *data, Data Stage的数据缓冲区 +- {size_t} len, Data Stage的数据长度 + +Return: + +- {FUsbTransCode} 控制传输的返回值,小于0表示失败,大于0表示成功传输的字节数目 + +#### FUsbGetStringDescriptor + +```c +FUsbTransCode FUsbGetStringDescriptor(FUsbDev *dev, int rtype, FUsbDescriptorType desc_type, int desc_idx, int lang_id, void *data, size_t len) +``` + +Note: + +- USB主机请求,获取字符串描述符 + +Input: + +- {FUsbDev} *dev, USB设备实例 +- {int} rtype, 请求类型,由FUsbGenerateReqType生成 +- {int} desc_type, 描述符类型 +- {int} desc_idx, 描述符索引 +- {int} lang_id, 语言类型 +- {void} *data, Data Stage的数据缓冲区 +- {size_t} len, Data Stage的数据长度 + +Return: + +- {int} 控制传输的返回值,小于0表示失败,大于0表示成功传输的字节数目 + +#### FUsbSetConfiguration + +```c +FUsbTransCode FUsbSetConfiguration(FUsbDev *dev) +``` + +Note: + +- 标准USB主机请求,设置配置值 + +Input: + +- {FUsbDev} *dev, USB设备实例 + +Return: + +- {FUsbTransCode} 控制传输的返回值,小于0表示失败,大于0表示成功传输的字节数目 + +#### FUsbClearFeature + +```c +FUsbTransCode FUsbClearFeature(FUsbDev *dev, int endp, int feature, int rtype) +``` + +Note: + +- 标准USB主机请求,去使能设备/接口/端点的某个特性 + +Input: + +- {FUsbDev} *dev, USB设备实例 +- {int} endp, 设备号(0x00)/接口号/端点号 +- {int} feature,待去除的特性 +- {int} rtype, 请求类型,由FUsbGenerateReqType生成 + +Return: + +- {FUsbTransCode} 控制传输的返回值,小于0表示失败,大于0表示成功传输的字节数目 + +#### FUsbDumpAllDescriptors + +```c +void FUsbDumpAllDescriptors(FUsbDev *dev) +``` + +Note: + +- 打印USB设备的描述符信息(设备描述符,配置描述符和接口描述符) + +Input: + +- {FUsbDev} *dev, USB设备实例,已完成初始化 + +Return: + +- 无 + +#### FUsbDetachDev + +```c +void FUsbDetachDev(FUsbHc *controller, int devno) +``` + +Note: + +- 从USB主机移除指定USB设备(USB设备驱动使用) + +Input: + +- {FUsbHc} *controller, USB控制器实例 +- {int} devno, USB设备地址 + +Return: + +- 无 + + +#### FUsbAttachDev + +```c +FUsbDevAddr FUsbAttachDev(FUsbHc *controller, int hubaddress, int port, FUsbSpeed speed) +``` + +Note: + +- 向USB主机添加USB设备(USB设备驱动使用) + +Input: + +- {FUsbHc} *controller, USB控制器实例 +- {int} hubaddress, Hub地址 +- {int} port, 连接的Port +- {FUsbSpeed} speed, USB设备的设置速度类型 + +Return: + +- {FUsbDevAddr} 分配的USB设备地址 diff --git a/bsp/phytium/libraries/standalone/doc/reference/driver/fwdt.md b/bsp/phytium/libraries/standalone/doc/reference/driver/fwdt.md new file mode 100644 index 0000000000..c24f78e303 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/driver/fwdt.md @@ -0,0 +1,171 @@ +# WDT 驱动程序 + +## 1. 概述 + +- WDT(watchdog timer)看门狗定时器是一种监控系统运行状况的手段,软件需在运行过程中定时向看门狗发送喂狗信号。 + +- 若看门狗定时器在一段时间内没有收到来自软件的喂狗信号,则认为系统故障,会强制系统复位。 + +- WDT 驱动支持的平台包括 FT2000/4、D2000、E2000。 + +## 2. 功能 + +WDT 驱动程序主要完成WDT模块的初始化、超时时间设置和超时中断函数,该驱动程序具备以下功能: + +- 模块初始化 +- 超时时间设置 +- 超时中断函数设置 + +相关源文件为: +``` +fwdt + ├── fwdt.c + ├── fwdt.h + ├── fwdt_g.c + ├── fwdt_hw.c + ├── fwdt_hw.h + ├── fwdt_intr.c + └── fwdt_sinit.c +``` + +## 3. 配置方法 + +以下部分将指导您完成 WDT 驱动的软件配置: + +- 配置驱动程序,新建应用工程,使能WDT驱动模块 +- 设置配置参数 +- 设置超时时间 +- 设置超时中断函数喂狗 + +## 4. 应用示例 + + +### [wdt_test](../../../baremetal/example/peripheral/timer/wdt_test/README.md) + + +## 5. API参考 + + +### 5.1. 用户数据结构 + +- drivers/wdt/fwdt/fwdt.h + +- wdt实例配置 + +```c +typedef struct +{ + u32 instance_id;/* wdt id */ + uintptr refresh_base_addr;/* wdt refresh base addr */ + uintptr control_base_addr;/* wdt control base addr */ + u32 irq_num; /* wdt ir num */ + u32 irq_prority;/* wdt irq priority */ + const char *instance_name;/* instance name */ +}FWdtConfig;/* wdt config */ +``` + +### 5.2 错误码定义 + +- FWDT_SUCCESS : success +- FWDT_ERR_INVAL_PARM : invalid input parameters +- FWDT_NOT_READY : driver not ready +- FWDT_NOT_SUPPORT : not support operation +- FWDT_TIMEOUT : wait timeout + +### 5.3. 用户API接口 + +- 获取wdt驱动的默认配置参数 + +```c +const FWdtConfig *FWdtLookupConfig(u32 instance_id); +``` + + Note: + + - 用户需要修改配置参数时,可以通过修改返回的FWdtConfig副本,作为后续使用函数的入参, + + Input: + + - u32 instance_id, 当前控制的WDT控制器实例号 + + Return: + + - const FWdtConfig *, 返回驱动默认参数, NULL表示失败 + + +- 设置wdt超时时间 + +```c +u32 FWdtSetTimeout(FWdtCtrl *pCtrl, u32 timeout); +``` + + Note: + + - 此函数会根据传入的超时时间初始化WDT寄存器; + - WDT两次超时后,才执行系统复位操作;例如希望WDT 6s后复位,则应设置timeout=3; + + Input: + + - FWdtCtrl *pCtrl, WDT驱动实例数据 + + - u32 timeout, 设置的WDT超时时间,最大不超过89 + + Return: + + - u32, 参考5.2章错误码定义 + + +- WDT喂狗函数 + +```c +u32 FWdtRefresh(FWdtCtrl *pCtrl); +``` + + Note: + + - 此函数会刷新wdt寄存器,重新开始计时 + + Input: + + - FWdtCtrl *pCtrl, WDT驱动实例数据 + + Return: + + - u32, 参考5.2章错误码定义 + +- WDT使能函数 + +```c +u32 FWdtStart(FWdtCtrl *pCtrl); +``` + + Note: + + - 此函数会使能wdt + + Input: + + - FWdtCtrl *pCtrl, WDT驱动实例数据 + + Return: + + - u32, 参考5.2章错误码定义 + + +- WDT停止函数 + +```c +u32 FWdtStop(FWdtCtrl *pCtrl); +``` + + Note: + + - 此函数会停止wdt + + Input: + + - FWdtCtrl *pCtrl, WDT驱动实例数据 + + Return: + + - u32, 参考5.2章错误码定义 \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/doc/reference/driver/fxhci.md b/bsp/phytium/libraries/standalone/doc/reference/driver/fxhci.md new file mode 100644 index 0000000000..6be1857604 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/driver/fxhci.md @@ -0,0 +1,104 @@ +# FXHCI 驱动程序 + +## 1. 概述 + +XHCI,即可扩展的主机控制器接口,是英特尔公司开发的一个USB主机控制器接口,它主要是面向USB 3.0的,同时它也支持USB 2.0及以下的设备,包括所有种类速度的USB设备(USB 3.0 SuperSpeed, USB 2.0 Low-, Full-, and High-speed, USB 1.1 Low- and Full-speed),是UHCI/OHCI/EHCI等接口标准的升级版本 + +XHCI接口架构主要包括三大部分, + +1. 主机配置空间(Host Configuration Space) + +- 每个xHC实现都应包括一种通过系统软件识别和枚举主机控制器的方法。本规范提供了一个主机配置空间的PCI示例,它被称为PCI配置空间。PCI配置空间定义提供了一个关于系统xHC枚举和资源(中断、电源、虚拟化等)的配置空间使用的工作示例 + +2. 寄存器空间(MMIO Space) + +- 寄存器空间表示xHC向驻留在内存地址空间中的系统软件提供的硬件寄存器。寄存器空间提供了在xHCI正常和扩展功能寄存器中定义的实现特定参数、操作和运行时控制和状态寄存器,以及用于标记对单个USB设备的访问的门钟阵列。该空间,通常被称为I/O空间,被实现为内存映射的I/O(MMIO)空间 + +3. 主机空间(Host Memory) + +- 主机空间由控制数据结构(设备上下文基地址阵列、设备上下文、传输环等)定义。以及由xHC驱动程序分配和管理的数据缓冲区,以启用单个设备的端点流量。此空间将在内存地址空间的内核和用户区域中分配 + +## 2. 功能 + +- 驱动相关的源文件如下, + +``` +drivers/usb/fxhci +├── fxhci.c +├── fxhci.h +├── fxhci_cmd.c +├── fxhci_debug.c +├── fxhci_dev.c +├── fxhci_evt.c +├── fxhci_hw.c +├── fxhci_hw.h +├── fxhci_private.h +└── fxhci_roothub.c +``` + +## 3. 配置方法 + +- 1. 通过 PCIe 总线连接或者板载外设, 连接 XHCI 控制器 +- 2. 如果使用 PCIe 总线连接的 XHCI 控制器,先调用 FPCIE 驱动完成设备枚举和 PCIe 配置 +- 3. 初始化 FUSB 驱动,通过 FUSB 驱动框架,初始化 FXHCI 控制器驱动 +- 4. 通过 FUSB 驱动完成控制器和设备初始化 +- 5. 调用 FUSB 轮询接口更新设备状态 + +## 4 应用示例 + +- USB主机操作和USB设备发现 +### [fxhci_host_pcie](../../../baremetal/example/peripheral/usb/fxhci_host_pcie) + +- USB大容量存储器读写 +### [fusb_fatfs](../../../baremetal/example/storage/fusb_fatfs) + +## 5. API参考 + +### 5.1. 用户数据结构 + +无 + +### 5.2 错误码定义 + +- [1] FXHCI_CC_SUCCESS : success +- [-65] FXHCI_CC_TIMEOUT : wait transfer timeout +- [-66] FXHCI_CC_CONTROLLER_ERROR : usb controller in wrong state +- [-67] FXHCI_CC_COMMUNICATION_ERROR : communication error +- [-68] FXHCI_CC_OUT_OF_MEMORY : memory used up +- [-69] FXHCI_CC_DRIVER_ERROR : driver in wrong state +- [-1] FXHCI_CC_GENERAL_ERROR : general error +- [0] FXHCI_CC_ZERO_BYTES : failed, transfer zero bytes + +>下列错误码来自XHCI标准 +- [5] FXHCI_CC_TRB_ERROR : TRB error (XHCI spec.) +- [6] FXHCI_CC_STALL_ERROR : stall error (XHCI spec.) +- [7] FXHCI_CC_RESOURCE_ERROR : resource error (XHCI spec.) +- [8] FXHCI_CC_BANDWIDTH_ERROR : bandwidth error (XHCI spec.) +- [9] FXHCI_CC_NO_SLOTS_AVAILABLE : No available slot error (XHCI spec.) +- [13] FXHCI_CC_SHORT_PACKET : short packet error (XHCI spec.) +- [21] FXHCI_CC_EVENT_RING_FULL_ERROR : event ring full error (XHCI spec.) +- [24] FXHCI_CC_COMMAND_RING_STOPPED : command ring stopped error (XHCI spec.) +- [25] FXHCI_CC_STOPPED : command abort error (XHCI spec.) +- [26] FXHCI_CC_TRB_ERROR : stopped error (XHCI spec.) +- [27] FXHCI_CC_STOPPED_LENGTH_INVALID : stopped invalid length error (XHCI spec.) + +### 5.3. 用户API接口 + +#### FXhciHcInit + +```c +FUsbHc *FXhciHcInit (FUsb *instance, uintptr base_addr) +``` + +Note: + +- 创建XHCI USB 控制器实例,完成初始化 + +Input: + +- {FUsb} *instance, USB实例 +- {uintptr} base_addr, XHCI控制器基地址 + +Return: + +- {FUsbHc *} 类型为XHCI的USB控制器实例 diff --git a/bsp/phytium/libraries/standalone/doc/reference/driver/fxmac.md b/bsp/phytium/libraries/standalone/doc/reference/driver/fxmac.md new file mode 100644 index 0000000000..d3d6fe56fe --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/driver/fxmac.md @@ -0,0 +1,496 @@ + +# FXMAC 驱动程序 + +## 1. 概述 + +以太网控制器(XMAC)的主要功能是在兼容 IEEE802.3 standard 标准的以太网中发送和接收数据,当前支持 SGMII/RGMII 的 PHY 接口 + +XMAC 接口特点包括 +- 支持速率 1000Mbps/100Mbps/10Mbps +- 支持 Reduced Gigabit Media Independent Interface (RGMII) +- 支持 SGMII Serial Gigabit Media-Independent Interface (SGMII) + +## 2. 功能 + +XMAC 驱动提供了以太网控制器的初始化,DMA 环形队列使用函数,外部PHY 接口相关的配置功能 + +XMAC 驱动程序的源文件包括, + +``` +. +├── fxmac_bd.h +├── fxmac_bdring.c +├── fxmac_bdring.h +├── fxmac.c +├── fxmac_g.c +├── fxmac.h +├── fxmac_hw.h +├── fxmac_intr.c +├── fxmac_options.c +├── fxmac_phy.c +├── fxmac_phy.h +├── fxmac_sinit.c +├── Kconfig +└── phy + ├── eth_ieee_reg.h + └── yt + ├── phy_yt.c + └── phy_yt.h +``` + +- 其中fxmac.h/fxmac.c 为开发者提供以下功能: +1. mac 控制器实例初始化 +2. 设置每个控制器实例中4个mac地址的接口 +3. 设置每个控制器实例中4个mac匹配地址的接口 +4. 外置phy 芯片交互接口 +5. 中断相关接口 + +- 其中fxmac_bdring.h/fxmac_bdring.c 为开发者提供了以下功能: +1. 创建dma 环形队列 +2. 环形队列数据拷贝 +3. 环形队列描述符分配 +4. 环形队列描述符释放 + + + +## 4 应用示例 + +### [fgmac_lwip_echo](../../../baremetal/example/fgmac_lwip_echo/README.md) + +- 启动LWIP网络协议栈,通过FXMAC驱动,支持开发板和网络主机的ping通 + +### [lwip port](../../../third-party/lwip-2.1.2/ports/fxmac/) + +- fxmac 耦合lwip 功能 + +## 5. API参考 + +### 5.1. 用户数据结构 + +- FXMAC 驱动配置数据 +```c + typedef struct + { + u32 instance_id; /* Id of device*/ + volatile uintptr_t base_address; + volatile uintptr_t extral_mode_base; + volatile uintptr_t extral_loopback_base; + FXmacPhyInterface interface; /* 接口类型,提供SGMII/RGMII 选择 */ + u32 speed; /* FXMAC_SPEED_XXX */ + u32 duplex; /* 1 is full-duplex , 0 is half-duplex */ + u32 auto_neg; /* Enable auto-negotiation - when set active high, autonegotiation operation is enabled. */ + u32 pclk_hz; + u32 max_queue_num; /* Number of Xmac Controller Queues */ + u32 tx_queue_id; /* 0 ~ FT_XMAC_QUEUE_MAX_NUM ,Index queue number */ + u32 rx_queue_id; /* 0 ~ FT_XMAC_QUEUE_MAX_NUM ,Index queue number */ + u32 hotplug_irq_num; + u32 dma_brust_length; /* burst length */ + u32 network_default_config; /* mac 控制器默认配置 */ + u32 queue_irq_num[FT_XMAC_QUEUE_MAX_NUM]; /* mac0 8个 ,其他的 4个 */ + } FXmacConfig; +``` + +- FGMAC 驱动控制数据 +```c + typedef struct + { + FXmacConfig config; + u32 is_ready; /* Device is ininitialized and ready*/ + u32 is_started; + u32 link_status; /* indicates link status ,FXMAC_LINKUP is link up ,FXMAC_LINKDOWN is link down,FXMAC_NEGOTIATING is need to negotiating*/ + u32 options; + + FXmacQueue tx_bd_queue; /* Transmit Queue */ + FXmacQueue rx_bd_queue; /* Receive Queue */ + + FXmacIrqHandler send_irq_handler; + void *send_args; + + FXmacIrqHandler recv_irq_handler; + void *recv_args; + + FXmacErrorIrqHandler error_irq_handler; + void *error_args; + + FXmacIrqHandler link_change_handler; + void *link_change_args; + + FXmacIrqHandler restart_handler; + void *restart_args; + + u32 moudle_id; /* Module identification number */ + u32 max_mtu_size; + u32 max_frame_size; + + u32 phy_address; /* phy address */ + u32 rxbuf_mask; /* Filter length */ /* 1000,100,10 */ + + } FXmac; +``` + +- FGMAC DMA描述符 + +```c + typedef struct + { + uintptr phys_base_addr; /* Physical address of 1st BD in list */ + uintptr base_bd_addr; /* Virtual address of 1st BD in list */ + uintptr high_bd_addr; /* Virtual address of last BD in the list */ + u32 length; /* Total size of ring in bytes */ + u32 run_state; /* Flag to indicate DMA is started */ + u32 separation; /* Number of bytes between the starting address + of adjacent BDs */ + FXmacBd *free_head; + /* First BD in the free group */ + FXmacBd *pre_head; /* First BD in the pre-work group */ + FXmacBd *hw_head; /* First BD in the work group */ + FXmacBd *hw_tail; /* Last BD in the work group */ + FXmacBd *post_head; + /* First BD in the post-work group */ + FXmacBd *bda_restart; + /* BDA to load when channel is started */ + + volatile u32 hw_cnt; /* Number of BDs in work group */ + u32 pre_cnt; /* Number of BDs in pre-work group */ + u32 free_cnt; /* Number of allocatable BDs in the free group */ + u32 post_cnt; /* Number of BDs in post-work group */ + u32 all_cnt; /* Total Number of BDs for channel */ + } FXmacBdRing; +``` + +- FGMAC DMA描述符表(链式)相关数据 +```c +typedef struct +{ + u32 desc_idx; /* For Current Desc position */ + u32 desc_buf_idx; /* For Current Desc buffer buf position */ + u32 desc_max_num; /* Max Number for Desc and Desc buffer */ + u8 *desc_buf_base; /* Desc buffer Base */ +} FGmacRingDescData; +``` + + +### 5.2 错误码定义 + +- 模块错误码编号:0x1070000 +- [0x0] FGMAC: Success +- FXMAC_ERR_INVALID_PARAM : Invalid parameter +- FXMAC_ERR_SG_LIST : dma ring out of sequence +- FXMAC_ERR_GENERAL :the number of BDs to allocate greater that the number of BDs in the preprocessing state. +- FXMAC_ERR_SG_NO_LIST : dma ring is not allocated +- FXMAC_ERR_PHY_BUSY : if there is another PHY operation in progress +- FXMAC_PHY_IS_NOT_FOUND : phy is not found +- FXMAC_PHY_AUTO_AUTONEGOTIATION_FAILED : PHY autonegotiation is error +- FXMAC_ERR_MAC_IS_PROCESSING : MAC controllers are enabled together. As a result, some operations cannot be mirrored + + +### 5.3 初始化流程 + +1. FXmacLookupConfig 获取默认配置 +2. 修改默认配置 中 phy interface 类型、协商模式 +3. 初始化mac 模块 +4. 初始化phy 模块 +5. 初始化mac 中断 +6. 初始化dma 模块 +7. 根据mac 默认配置启动mac 功能 + + +### 5.4. 用户API接口 + +#### FXmacLookupConfig + +- 获取FXMAC驱动的默认配置参数 + +```c +const FXmacConfig *FXmacLookupConfig(u32 instance_id); +``` + +Note: + +- 返回FXMAC的默认配置,复制后修改配置 +- 需要确认当前平台支持输入的instance_id + +Input: + +- {u32} instance_id, 驱动控制器号 + +Return: + +- {const FXmacConfig *}, 驱动默认配置 +#### FXmacCfgInitialize + +- 完成FGMAC驱动实例的初始化,使之可以使用 + +```c +FError FXmacCfgInitialize(FXmac *instance_p, const FXmacConfig *config_p) +``` + +Note: + +- 此函数会重置FGMAC控制器和FGMAC控制数据 + +Input: + +- {FXmac} *instance_p MAC 控制器实例指针 + +- {FXmacConfig} *cofig_p 控制器驱动配置数据 + +Return: + +- {FError} 驱动初始化的错误码信息,FGMAC_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + +#### FXmacInitInterface + +- 根据phy 接口类型 ,初始化mac 控制器配置 + +```c +void FXmacInitInterface(FXmac *instance_p) +``` + +Note: +- 此函数一般用于 PHY 芯片协商完成之后被调用,与PHY配置进行适配 + +Input: +- {FXmac} *instance_p MAC 控制器实例指针 + +#### FXmacGetMacAddress + +- 根据index 获取mac 地址 + +```c +void FXmacGetMacAddress(FXmac *instance_p, u8 *address_ptr, u8 index) +``` + +Input : + +- {FGmac} *instance_p MAC 控制器实例指针 +- {u8} index MAC(0-3)地址的索引 + +Output : +- {u8} *address_ptr 是指向缓冲区的指针当前MAC地址将被复制。 + + +#### FXmacSetMacAddress + +- 根据index 写入mac 地址 + +```c +FError FXmacSetMacAddress(FXmac *instance_p, u8 *address_ptr, u8 index); +``` + +Input : + +- {FGmac} *instance_p MAC 控制器实例指针 +- {u8} *address_ptr 是指向缓冲区的指针当前MAC地址将被复制。 +- {u8} index MAC(0-3)地址的索引 + +Output : + +- {FError} FT_SUCCESS 如果MAC地址设置成功 + +#### FXmacSetOptions + +- 设置FXmac 中的相关配置信息 + +```c +FError FXmacSetOptions(FXmac *instance_p, u32 options, u32 queue_num) +``` + +Note: +- 必须在mac 控制器关闭的情况被调用 + +Input: +- {FGmac} *instance_p MAC 控制器实例指针 +- {u32} options 是要设置的选项。 选项参数位于 fxmac.h 中的 FXMAC_****_OPTION +- {u32} queue_num mac控制器中队列的选项,仅在 FXMAC_JUMBO_ENABLE_OPTION 配置时被使用 + +Return: +- {FError} FT_SUCCESS 设置成功 + +#### FXmacClearOptions + +- 清除FXmac 中的相关配置信息 + +```c +FError FXmacClearOptions(FXmac *instance_p, u32 options, u32 queue_num) +``` + +Note: +- 必须在mac 控制器关闭的情况被调用 + +Input: +- {FGmac} *instance_p MAC 控制器实例指针 +- {u32} options 是要设置的选项。 选项参数位于 fxmac.h 中的 FXMAC_****_OPTION +- {u32} queue_num mac控制器中队列的选项,仅在 FXMAC_JUMBO_ENABLE_OPTION 配置时被使用 + +Return: +- {FError} FT_SUCCESS 清除成功 + +#### FXmacStart + +- 启动以太网控制器 + +```c +void FXmacStart(FXmac *instance_p) +``` + +note: + +- 根据 network_default_config 中的 FXMAC_TRANSMIT_ENABLE_OPTION 与 +FXMAC_RECEIVER_ENABLE_OPTION ,决定是否开启控制器的接收与发送功能。并且默认开启接收与发送相关中断 + +Input: + +- {FGmac} *instance_p MAC 控制器实例指针 + + +#### FXmacStop + +- 关闭以太网控制器 + +```c +void FXmacStop(FXmac *instance_p) +``` + +note: +- 关闭所有中断,关闭接收与发送功能 + +Input: + +- {FGmac} *instance_p MAC 控制器实例指针 + + +#### FXmacSetQueuePtr + +- 设置mac 控制器中接收/发送缓冲区 的描述符环形队列的首地址 + +```c +void FXmacSetQueuePtr(FXmac *instance_p, uintptr queue_p, u8 queue_num, + u32 direction) +``` + +Note: +- 描述符环形队列的首地址按照128bit 对其 + +Input: + +- {FXmac} *instance_p MAC 控制器实例指针 +- {uintptr} queue_p 写入队列的地址 +- {u8} queue_num 缓冲队列索引 +- {u32} direction 当为 FXMAC_SEND 表示方向为发送,当为 FXMAC_RECV 表示方向为接收 + + +#### FXmacPhyWrite + +- 将数据写入指定的PHY寄存器。 + +```c +FError FXmacPhyWrite(FXmac *instance_p, u32 phy_address, + u32 register_num, u16 phy_data) +``` + +Note: +- 这个函数不是线程安全的。 用户必须提供互斥的如果有多个线程可以调用该函数,则访问该函数。 + +Input: +- {FXmac} *instance_p MAC 控制器实例指针 +- {u32} phy_address 要写入的PHY的地址 +- {u32} register_num 要写入的PHY的地址,特定PHY寄存器的寄存器号0-31 +- {u16} phy_data 需要写入对应PHY 芯片中 对应register_num 的参数 + +Return: + +- {FError} FT_SUCCESS PHY 写入成功 + + +#### FXmacPhyRead + +- 指定PHY 芯片中对应的寄存器号,读出其中对应的参数 + +```c +FError FXmacPhyRead(FXmac *instance_p, u32 phy_address, + u32 register_num, u16 *phydat_aptr) +``` + +Note: + +- 这个函数不是线程安全的。 用户必须提供互斥的如果有多个线程可以调用该函数,则访问该函数。 + +Input: +- {FXmac} *instance_p MAC 控制器实例指针 +- {u32} phy_address 要写入的PHY的地址 +- {u32} register_num 要写入的PHY的地址,特定PHY寄存器的寄存器号0-31 + +Output: +- {u16} *phydat_aptr 需要读出对应PHY 芯片中 对应register_num中值的指针 + +Return: + +- {FError} FT_SUCCESS PHY 读入成功 + + +#### FXmacPhyInit + +- 初始化PHY 芯片 ,首先检查出当前已连接的PHY 芯片地址,然后根据协商方式,确定 + +```c +FError FXmacPhyInit(FXmac *instance_p, u32 speed,u32 duplex_mode, u32 autonegotiation_en); +``` + +Input: +- {FXmac} *instance_p MAC 控制器实例指针 +- {u32} speed 需要设置的速度 +- {u32} duplex_mode 双工模式配置,1为全双工,0 为半双工 +- {u32} autonegotiation_en 为1 时,PHY 会进行自协商操作。为0时 ,将根据配置项进行协商 + +Return: +- FError FT_SUCCESS 初始化成功 + + +#### FXmacSelectClk + +- 根据MAC 与 PHY 芯片连接的情况,设置相关时钟参数 + +```c +void FXmacSelectClk(FXmac *instance_p ) +``` + +Input: +- {FXmac} *instance_p MAC 控制器实例指针 + + +#### FXmacSetHandler + +- 设置中断回调函数 + +```c +FError FXmacSetHandler(FXmac *instance_p, u32 handler_type, void *func_pointer, void *call_back_ref) +``` + +Input: +- {FXmac} *instance_p MAC 控制器实例指针 +- {u32} handler_type 指示中断处理程序类型 ,具体参数参考 FXMAC_HANDLER_*** +- {void } *func_pointer 回调函数接口 +- {void } *call_back_ref 回调函数的传入参数 + diff --git a/bsp/phytium/libraries/standalone/doc/reference/driver/sfud.md b/bsp/phytium/libraries/standalone/doc/reference/driver/sfud.md new file mode 100644 index 0000000000..1d4d042466 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/driver/sfud.md @@ -0,0 +1,42 @@ +# SFUD 驱动框架 + +## 1. 概述 + + +- [SFUD Gitee](https://gitee.com/Armink/SFUD) +- SFUD 是一款开源的串行 SPI Flash 通用驱动库 + +## 2. 功能 + +- third-party/sfud-1.1.0 +- 目前SFUD框架实现对接FSPIM驱动的对接,用于读写SPI Flash +``` +├── Kconfig +├── inc +│   ├── sfud.h +│   ├── sfud_cfg.h +│   ├── sfud_def.h +│   └── sfud_flash_def.h +├── library.json +├── ports +│   └── f_spim +│   └── sfud_port.c +└── src + ├── sfud.c + └── sfud_sfdp.c +``` + +## 3. 配置方法 + +- 使能 CONFIG_USE_SFUD 配置 +- 选择底层的SPI驱动,如CONFIG_USE_FSPIM +- 调用 SFUD 提供的 API 读写 SPI 从设备 + +## 4 应用示例 + +### [fspim_nor_flash](../../../baremetal/example/fspim_nor_flash/README.md) + +## 4. API参考 + +- sfud.h 和 sfud_port.c 是主要需要对接的源文件 +- 参考[SFUD API指南](https://gitee.com/Armink/SFUD/) \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/doc/reference/driver/template.md b/bsp/phytium/libraries/standalone/doc/reference/driver/template.md new file mode 100644 index 0000000000..37a6cf3ec8 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/driver/template.md @@ -0,0 +1,22 @@ +# XX 驱动程序 + +## 1. 概述 + +`XX的基本概念, XX驱动支持的硬件平台` + +## 2. 功能 + +`XX驱动程序支持的功能,关联的源文件` + +## 3. 配置方法 + +`XX驱动使用的一般过程和方法` +`XX驱动的配置方法和参数设置` + +## 4 应用示例 + +`XX驱动的example使用` + +## 5. API参考 + +`XX驱动用户API说明` \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/doc/reference/driver/timer_tacho.md b/bsp/phytium/libraries/standalone/doc/reference/driver/timer_tacho.md new file mode 100644 index 0000000000..5941cf2064 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/driver/timer_tacho.md @@ -0,0 +1,454 @@ +# TIMER_TACHO 驱动程序 + +## 1. 概述 + + +- TIMER_TACHO包含了2种功能,定时器和输入捕获。其中定时计数作为通用定时器供软件使用,输入捕获功能主要用来测试输入方波的高电平持续时间,该方波是由风扇设备设备发出,通过PAD直接连接到控制器模块输入端。 +- 对于定时功能,有若干可配置参数,例如产生中断次数,是否对当前计数值进行清空,是否使能计数器,计数器位宽选择(32/64),或者是restart/free模式,以及当进行了初值配置是否需要强制更新,以及可以通过配置寄存器对控制器模块进行复位操作。 +- 对于输入捕获,控制器模块设置了上下阈值,也就是当捕获到的高电平时间低于或者高于阈值都会发出中断(此时分别对应风扇不同的故障)。同时对输入信号进行消抖处理,也可以对消抖级数进行配置。 +- 本驱动程序提供了E2000(D、Q、S)平台的定时器和输入捕获功能 +- E2000(D、Q、S)上包含了38个定时器控制器,(D、Q)上包含了4个tacho输入捕获接口,(S)上包含了16个输入捕获接口 + +## 2. 功能 + +- 驱动相关的源文件如下: +- drivers/timer/ftimer_tacho + +``` +. +├── ftacho.c --> 转速计功能实现 +├── ftimer_tacho_g.c --> 相关配置和全局变量 +├── ftimer_tacho_hw.h --> 寄存器操作 +├── ftimer_tacho_intr.c --> 中断相关处理 +├── ftimer_tacho.h --> 用户接口 +└── ftimer.c --> 定时器功能实现 +``` + +## 3. 配置方法 + +以下部分将指导您完成ftimer_tacho驱动的软件配置: + +- 初始化timer_tacho控制器,使用timer功能还是tacho功能 +- 分别配置两个功能的参数,配置使用timer功能的id,定时时间,计数方式等;配置使用tacho功能的id,阈值,输入消抖级数等。 +- 注册中断处理函数,使能中断 + +## 4. 应用示例 + +### [timer定时器](../../../baremetal/example/peripheral/timer/timer_tacho/README.md) + +#### timer +- FTimerFunctionInit 获取cmd传递进来的最基本参数 +- FTimerCfgInit 对传递的参数以及默认参数进行配置,并启动中断 +- FTimerStartTest 定时器开始工作,如果是单次计时,则可以传递新的参数并计时,循环模式则仅仅进行启动 +- 在中断中进行回调 + +#### tacho +- FTachoFunctionInit 获取cmd传递进来的参数,使用计时器的id以及工作模式 +- FTachoCfgInit 配置工作参数 +- TachoEnableIntr 启用事件中断 +- FTimerStart tacho开始工作 +- FTachoGetRPM 可以获取RPM参数了 + +#### timer与tacho的切换 +- 注意需要对定时器控制器进行disable,可以调用各自的F****DeInit函数,也可以FTimerSwithMode进行操作 + +## 5. API参考 + +### 5.1 用户数据结构 + +- ftimer_tacho控制数据 + +```c +typedef struct +{ + FTimerTachoConfig config; /* Current active configs */ + boolean isready; /* Device is initialized and ready */ + FTimerEventHandler evt_handlers[FMAX_TIMER_TACHO_EVENT];/* event handler for interrupt */ +}FTimerTachoCtrl; +``` + +- ftimer_tacho配置数据 + +```c +typedef struct +{ + u32 id; /* id of timer tacho */ + char name[12]; /* instance name */ + u32 irq_priority; /* intr priority */ + u32 work_mode; /* timer/tacho/capture mode */ + /* for timer function */ + u32 timer_mode; /* free-run/restart */ + u32 timer_bits; /* 32/64 bits */ + u32 cmp_type; /* once/cycle cmp */ + boolean clear_cnt; /* clear timer counts */ + boolean force_load; /* start count from start val */ + /* for tacho function */ + u32 edge_mode; /* rising/falling/double */ + u32 jitter_level; /* jitter level */ + u32 plus_num; /* plus_num of period to calculate rpm */ + u32 captue_cnt; /* in capture mode, when cnt reach this val, intr asserted */ +}FTimerTachoConfig; +``` + +- ftimer_tacho工作模式 +```c +typedef enum +{ + /*TimerTacho mode */ + FTIMER_WORK_MODE_TIMER = 0, + FTIMER_WORK_MODE_TACHO, + FTIMER_WORK_MODE_CAPTURE +}FTimerTachoModeType; +``` + +- timer计数模式 +```c +typedef enum +{ + /*Timer count mode*/ + FTIMER_FREE_RUN = 0, + FTIMER_RESTART +}FTimerCntModeType; +``` + +- ftimer_tacho中断事件类型 +```c +typedef enum +{ + FTACHO_EVENT_OVER = 0, /*tacho超速事件*/ + FTACHO_EVENT_UNDER, /*tacho低速事件*/ + FTIMER_EVENT_ROLL_OVER, /*计数器翻转事件*/ + FTIMER_EVENT_ONCE_CMP, /*单次定时输出事件*/ + FTIMER_EVENT_CYC_CMP, /*重复定时输出事件*/ + FTACHO_EVENT_CAPTURE, /*tacho输入捕获事件*/ + + FMAX_TIMER_TACHO_EVENT +}FTimerTachoEventType; +``` + +- tacho输入模式选择 +```c +typedef enum +{ + FTACHO_FALLING_EDGE = 0, + FTACHO_RISING_EDGE, + FTACHO_DOUBLE_EDGE +}FTachoEdgeType; +``` + +- tacho消抖级数选择 +```c +typedef enum +{ + FTACHO_JITTER_LEVEL0 = 0, + FTACHO_JITTER_LEVEL1, + FTACHO_JITTER_LEVEL2, + FTACHO_JITTER_LEVEL3, +}FTachoJitterLevelType; +``` + +### 5.2 错误码定义 + +- FTIMER_TACHO_ERR_IS_READ 已经初始化 +- FTIMER_TACHO_ERR_NOT_READY 未初始化 +- FTIMER_TACHO_ERR_INVAL_PARM 参数错误 +- FTIMER_TACHO_ERR_INIT_FAILED 初始化错误 +- FTIMER_TACHO_ERR_ABORT 运行中止 +- FTIMER_TACHO_ERR_FAILED 运行错误 +- FTIMER_TACHO_ERR_NOT_SUPPORT 不支持此配置 + +### 5.3 用户API接口 + +#### Time & Tacho API + +##### FTimerSoftwareReset + +- 将控制器复位 + +```c +FError FTimerSoftwareReset(FTimerTachoCtrl *instance_p); +``` + +Note: + +- 复位控制器 + +Input: + +- {FTimerTachoCtrl} *instance_p,timer_tacho驱动控制数据 + +Return: + +- {FError} 驱动的错误码信息 + +##### FTimerSetInterruptMask + +- 设置中断 + +```c +void FTimerSetInterruptMask(FTimerTachoCtrl *instance_p, + FTimerTachoEventType intrType, + boolean enable); +``` + +Note: + +- 设置中断,根据不同的intrType,将对于的中断mask置位 + +Input: + +- {FTimerTachoCtrl} *instance_p,timer_tacho驱动控制数据 +- {FTimerTachoEventType} intrType,timer_tacho中断类型 +- {boolean} enable,开启还是关闭中断 + +Return: + +- void 无 + +##### FTimerStart + +- 启动timer_tacho + +```c +FError FTimerStart(FTimerTachoCtrl *instance_p); +``` + +Note: + +- 启动已经初始化完成的timer_tacho外设,根据不同的功能,开启使能位 + +Input: + +- {FTimerTachoCtrl} *instance_p,timer_tacho驱动控制数据 + +Return: + +- {FError} 驱动的错误码信息 + +##### FTimerStop + +- 停止timer外设 + +```c +FError FTimerStop(FTimerTachoCtrl *instance_p); +``` + +Note: + +- 停止timer外设,根据不同的功能,关闭使能位,计数值停止并冻结 + +Input: + +- {FTimerTachoCtrl} *instance_p,timer_tacho驱动控制数据 + +Return: + +- {FError} 驱动的错误码信息 + +##### FTimerSwithMode + +- timer 与 tacho-capture两种模式的切换 + +```c +FError FTimerSwithMode(FTimerTachoCtrl *instance_p, FTimerTachoConfig *new_config_p); +``` + +Note: + +- 用于timer 与 tacho-capture两种模式的切换,切换需要失能和清除计数器 + +Input: + +- {FTimerTachoCtrl} *instance_p,timer_tacho驱动控制数据 +- {FTimerTachoConfig} *new_config_p,timer_tacho配置数据 + +Return: + +- {FError} 驱动的错误码信息 + +##### FTimerRegisterEvtCallback + +- 注册中断事件处理回调函数 + +```c +void FTimerRegisterEvtCallback(FTimerTachoCtrl *instance_p, + FTimerTachoEventType evt, + FTimerEventHandler callback); +``` + +Note: + +- 注册中断事件处理回调函数,根据不同的中断事件类型,当发生中断后,会跳转到注册的函数中 + +Input: + +- {FTimerTachoCtrl} *instance_p,timer_tacho驱动控制数据 +- {FTimerTachoEventType} evt,中断事件类型 +- {FTimerEventHandler} callback,用户自己定义的中断回调函数 + +Return: + +- void 无 + +##### FTimeSettingDump + +- 打印寄存器信息 + +```c +FError FTimeSettingDump(const FTimerTachoCtrl *instance_p); +``` + +Note: + +- 打印已经初始化成功的timer_tacho控制器寄存器信息 + +Input: + +- {FTimerTachoCtrl} *instance_p,timer_tacho驱动控制数据 + +Return: + +- {FError} 驱动的错误码信息 + +##### FTimerTachoIntrHandler + +- TimerTacho中断处理函数 + +```c +void FTimerTachoIntrHandler(s32 vector, void *param); +``` + +Note: + +- TimerTacho中断处理函数,如果注册回调函数,则跳转到回调函数 + +Input: + +- {s32} vector,中断向量号 +- {void} *param, 中断输入参数, 指向FTimerTachoCtrl的驱动控制实例 + +Return: + +- void 无 + +##### FTimerTachoSetIntr + +- 根据工作模式和状态设置相应的中断 + +```c +void FTimerTachoSetIntr(FTimerTachoCtrl *instance_p); +``` + +Note: + +- 根据工作模式和状态设置相应的中断,此函数根据设置的模式自动配置中断,特殊需求可更改 + +Input: + +- {FTimerTachoCtrl} *instance_p,timer_tacho驱动控制数据 + +Return: + +- void 无 + +##### FTimerInit + +- TimerTacho驱动实例的初始化 + +```c +FError FTimerInit(FTimerTachoCtrl *instance_p, const FTimerTachoConfig *config_p); +``` + +Note: + +- 完成TimerTacho驱动实例的初始化,使之在就绪状态,配合FTimerStart使用 + +Input: + +- {FTimerTachoCtrl} *instance_p,timer_tacho驱动控制数据 +- {FTimerTachoConfig} *config_p,timer_tacho配置数据 + +Return: + +- {FError} 驱动的错误码信息 + +##### FTimerDeInit + +- Timer驱动实例去使能,清零实例数据 + +```c +void FTimerDeInit(FTimerTachoCtrl *instance_p); +``` + +Note: + +- 对已经完成初始化的实例,进行Timer驱动实例去使能,清零实例数据 + +Input: + +- {FTimerTachoCtrl} *instance_p,timer_tacho驱动控制数据 + +Return: + +- void 无 + +##### FTachoInit + +- 完成Tacho驱动实例的初始化 + +```c +FError FTachoInit(FTimerTachoCtrl *instance_p, const FTimerTachoConfig *config_p); +``` + +Note: + +- 完成Tacho驱动实例的初始化,使之在就绪状态 + +Input: + +- {FTimerTachoCtrl} *instance_p,timer_tacho驱动控制数据 + +Return: + +- void 无 + +##### FTachoGetFanRPM + +- 获取风扇的转速值 + +```c +FError FTachoGetFanRPM(FTimerTachoCtrl *instance_p,u32 *rpm); +``` + +Note: + +- 根据预设采样周期的值,来获取风扇的转速值 + +Input: + +- {FTimerTachoCtrl} *instance_p,timer_tacho驱动控制数据 +- {u32} *rpm,传入保存转数数据的指针 + +Return: + +- {FError} 驱动的错误码信息 + +##### FTachoDeInit + +- Tacho驱动实例去使能,清零实例数据 + +```c +void FTachoDeInit(FTimerTachoCtrl *instance_p); +``` + +Note: + +- 完成Tacho驱动实例去使能,清零实例数据 + +Input: + +- {FTimerTachoCtrl} *instance_p,timer_tacho驱动控制数据 + +Return: + +- void 无 diff --git a/bsp/phytium/libraries/standalone/doc/reference/hw/template.md b/bsp/phytium/libraries/standalone/doc/reference/hw/template.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bsp/phytium/libraries/standalone/doc/reference/sdk/fmemory_pool.md b/bsp/phytium/libraries/standalone/doc/reference/sdk/fmemory_pool.md new file mode 100644 index 0000000000..3af23ce41b --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/sdk/fmemory_pool.md @@ -0,0 +1,279 @@ + +# Memory pool + +## 1. 概述 + +内存池 (Memory Pool)是一种内存分配方式,是为了减少频繁使用 malloc/free new/delete 等系统调用而造成的性能损耗而设计的,具有效率高、内存碎片少和防止内存泄漏等特性。内存池根据实际需要,在初始状态获取一大块内存(堆区或者静态内存),然后划分成若干内存页,进行管理,将内存页中的块传递给申请者使用。SDK中采用TLSF内存分配器管理,专门设计用于满足实时要求。 + +TLSF (Two-Level Segregated Fit memory allocator), 是一款通用的动态内存分配器,具有以下特点, + +- malloc,free,realloc,memalign的算法复杂度变为O(1) +- 每次分配的开销极低(4字节) +- 低碎片化 +- 支持动态添加和删除内存池区域 + +TLSF主要采用两级位图(Two-Level Bitmap)与分级空闲块链表(Segregated Free List)的数据结构管理动态内存池(memory pool)以及其中的空闲块(free blocks),用Good-Fit的策略进行分配。 + +TLSF算法分配速度不一定快,只是说能保证分配的时间是个常数(malloc不能保证)。 + +TLSF也叫多内存堆管理算法,支持动态增加或者删除多块不连续的内存,将它们作为一个内存堆使用。 + +## 2. 功能 + +FMemory Pool主要完成内存池的初始化,为用户提供多种内存申请方法,支持内存池的使用情况监测, +- 初始化内存池 +- 删除内存池 +- 申请一段空间(不要求对齐) +- 按指定字节数申请一段对齐的内存 +- 申请一段数组,返回被清零的空间 +- 回收原来申请的空间并重新分配 +- 跟踪当前内存池的使用情况 + +相关源文件为: +``` +fmempory_pool + ├── fmempory_pool.c + └── fmempory_pool.h +``` + +``` +tlsf + ├── tlsf.c + └── tlsf.h +``` + +## 3. 配置方法 + +以下部分将指导您完成 FMemory Pool 的软件配置: +- 使能TLSF组件 + +## 4. 应用示例 + +### [memory pool](../../../baremetal/example/system/memory_pool_test) + +## 5. API参考 + +### 5.1. 用户数据结构 + +- common/fmempory_pool.h + +```c +typedef struct +{ + pool_t pool_addr; + FSListNode list; +} FMempPoolList; /* 内存池控制数据 */ + +typedef struct +{ + FMempPoolList *pools_list; /* 内存池链表 */ + tlsf_t tlsf_ptr; /* tlsf内存池 */ + u32 is_ready; /* 内存池初始化完成标志 */ +} FMemp; /* 内存池控制数据 */ +``` + +### 5.2 错误码定义 + +- 模块错误码编号 `0x0010000` + +- [0x0] FMEMP_SUCCESS : success + +- [0x0010000] FMEMP_ERR_INVALID_BUF : 输入的内存池缓存区不合法 + +- [0x0010001] FMEMP_ERR_INIT_TLFS : 初始化TLFS内存池失败 + +- [0x0010002] FMEMP_ERR_BAD_MALLOC : 从TLFS内存池分配空间失败 + +### 5.3. 用户API接口 + +#### FMempInit +- 初始化内存池, 分配内存池的内存空间 + +```c +FError FMempInit(FMemp *memp, void *begin_addr, void *end_addr); +``` + +Note: + +- begin_addr end_addr 指向为内存池指定的缓冲区的起止地址 + +Input: + +- {FMemp} *memp, 内存池的控制数据 +- {void} *begin_addr, 分配给内存池的空间起始地址 +- {void} *end_addr, 分配给内存池的空间结束地址 + +Return: + +- {FError} FMEMP_SUCCESS表示初始化成功,返回其它值表示初始化失败 + +#### FMempRemove + +- 释放所有分配的内存,删除内存池 + +```c +void FMempRemove(FMemp *memp); +``` + +Note: + +- 需要初始化后才能调用,调用此函数后,内存池分配的空间不再能使用 + +Input: + +- {FMemp} *memp 内存池控制数据 + +Return: + +- 无 + +#### FMempMalloc + +- 从内存池申请一段空间 + +```c +void *FMempMalloc(FMemp *memp, fsize_t nbytes); +``` + +Note: + +- 需要初始化后才能调用,申请的空间再不再使用后需要调用FMempFree释放 + +Input: + +- {FMemp} *memp 内存池控制数据 +- {fsize_t} nbytes 申请的字节数 + +Return: + +- {void *} 申请到的空间,如果申请失败,返回NULL + +#### FMempCalloc + +- 从内存池申请一段数组空间并清零 + +```c +void *FMempCalloc(FMemp *memp, fsize_t count, fsize_t size) +``` + +Note: + +- 需要初始化后才能调用,申请的空间再不再使用后需要调用FMempFree释放 + +Input: + +- {FMemp} *memp 内存池控制数据 +- {fsize_t} count 数据成员格式 +- {fsize_t} size 单个数据成员的字节数 + +Return: + +- {void *} 申请到的空间,如果申请失败,返回NULL + +#### FMempMallocAlign + +- 按指定对齐方式申请一段空间 + +```c +void *FMempMallocAlign(FMemp *memp, fsize_t size, fsize_t align); +``` + +Note: + +- 需要初始化后才能调用,申请的空间再不再使用后需要调用FMempFree释放 + +Input: + +- {FMemp} *memp 内存池控制数据 +- {fsize_t} size 申请的字节数 +- {fsize_t} align 对齐字节数 + +Return: + +- {void *} 申请到的空间,如果申请失败,返回NULL + +#### FMempRealloc + +- 回收原来申请的空间并重新分配 + +```c +void *FMempRealloc(FMemp *memp, void *ptr, fsize_t nbytes); +``` + +Note: + +- 需要初始化后才能调用,申请的空间再不再使用后需要调用FMempFree释放,调用函数后,原来的空间不再能使用,原空间的数据被移动到返回指针指向的空间 + +Input: + +- {FMemp} *memp 内存池控制数据 +- {void} *ptr 原来的空间 +- {fsize_t} nbytes 新申请的字节数 + +Return: + +- {void *} 替换后空间,如果替换失败,返回NULL + +#### FMempFree + +- 释放一段从内存池申请的空间 + +```c +void FMempFree(FMemp *memp, void *ptr); +``` + +Note: + +- 需要初始化后才能调用,传入的指针需要是FMempMalloc/FMempCalloc/FMempMallocAlign/FMempRealloc返回的 + +Input: + +- {FMemp} *memp 内存池控制数据 +- {void} *ptr 待释放的空间地址 + +Return: + +- 无 + +#### FMemProbe + +- 跟踪当前内存池的使用情况 + +```c +void FMemProbe(FMemp *memp, u32 *total, u32 *used, u32 *max_used); +``` + +Note: + +- 需要初始化后才能调用 + +Input: + +- {FMemp} *memp 内存池控制数据 +- {u32} *total 总可用字节数 +- {u32} *used 已使用字节数 +- {u32} *max_used 已使用字节数的峰值 + +Return: + +- 无 + +#### FMemListAll + +- 打印当前分配的内存块信息 + +```c +void FMemListAll(FMemp *memp); +``` + +Note: + +- 需要初始化后才能调用 + +Input: + +- {FMemp} *memp 内存池控制数据 + +Return: + +- 无 \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/doc/reference/sdk/fpinctrl.md b/bsp/phytium/libraries/standalone/doc/reference/sdk/fpinctrl.md new file mode 100644 index 0000000000..958156f695 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/sdk/fpinctrl.md @@ -0,0 +1,266 @@ +# 芯片引脚控制 + +## 1. 概述 + +- 芯片引脚控制 (Pin Ctrl)用于引脚功能配置和引脚延迟配置,开发板上常存在某些 IO 之间存在共用同一物理引脚情况,即引脚复用,同时,通用 IO 引脚还可以配置引脚延迟 +- FPinCtrl 支持芯片引脚控制功能,支持 FT2000/4、D2000 和 E2000 + +## 2. 功能 + +- FPinCtrl 主要提供引脚控制相关的功能配置,包括 +- 1. 控制复用功能 +- 2. 控制引脚驱动能力,只支持E2000平台 +- 3. 配置引脚的上下拉电阻 +- 4. 调节引脚的输入输出延迟 + +- 相关的源文件包括 +``` +fpinctrl.h + fiopad.h --> E2000 + fiopad.c + + fioctrl.h --> FT2000/4, D2000 + fioctrl.c +``` + +## 3. 配置方法 + +- 不需要特别配置 + +## 4. 应用示例 + +- 设置引脚的复用,驱动能力和上下拉电阻等 + +### [fpin_test](../../../baremetal/example/peripheral/pin/fioctrl_test) + +- IO 引脚延时配置索引,参考 D2000/FT2000-4 数据手册的"表 5-32 通用 IO 引脚延时配置寄存 + +> 调用 FIOCTRL_INDEX 生成PAD对应的索引,e.g. + +| 偏移 | 位域 | 说明 | +| -------- | ------- | ------- | +|0x0208 | [27 : 26] | 控制 i2c_0_scl_pad 的上下拉 2'b10 | +| | [25 : 24] | 控制 i2c_0_scl_pad 的复用功能 2'b00 | + +```c + #define FIOCTRL_I2C0_SCL_PAD (FPinIndex)FIOCTRL_INDEX(0x208, 24) /* i2c0-scl: func 0 */ +``` +> 定义控制域为 i2c0_scl_pad 的PAD,其中延时配置寄存器偏移量为0x208, 输入延时配置位从第24位开始 + +## 5. API参考 + +Note: + +- FT2000/4 和 D2000 平台,使用FIOCTRL_INDEX 宏定义的 index 作为引脚索引 +- E2000 平台,使用 FIOPAD_INDEX 宏定义的 index 作为引脚索引 + +#### FPinGetFunc + +```c +FPinFunc FPinGetFunc(const FPinIndex pin); +``` + +Note: + +- 获取IO引脚当前的复用功能 + +Input: + +- {FPinIndex} pin IO引脚索引 + +Return: + +- {FPinFunc} 当前的复用功能 + +#### FPinSetFunc + +```c +void FPinSetFunc(const FPinIndex pin, FPinFunc func); +``` + +Note: + +- 设置IO引脚复用功能 + +Input: + +- {FPinIndex} pin IO引脚索引 +- {FPinFunc} func IO复用功能 + +Return: + +- 无 + +#### FPinGetPull + +```c +FPinPull FPinGetPull(const FPinIndex pin); +``` + +Note: + +- 获取IO引脚当前的上下拉设置 + +Input: + +- {FPinIndex} pin IO引脚索引 + +Return: + +- {FPinPull} 当前的上下拉设置 + +#### FPinSetPull + +```c +void FPinSetPull(const FPinIndex pin, FPinPull pull); +``` + +Note: + +- 设置IO引脚当前的上下拉 + +Input: + +- {FPinIndex} pin IO引脚索引 +- {FPinPull} pull 上下拉设置 + +Return: + +- 无 + +#### FPinGetDelay + +```c +FPinDelay FPinGetDelay(const FPinIndex pin, FPinDelayDir dir, FPinDelayType type); +``` + +Note: + +- 获取IO引脚当前的延时设置 + +Input: + +- {FPinIndex} pin IO引脚延时设置索引 +- {FPinDelayDir} dir 输入/输出延时 +- {FPinDelayType} type 精调/粗调延时 + +Return: + +- {FPinDelay} 当前的延时设置 + +#### FPinGetDelayEn + +```c +boolean FPinGetDelayEn(const FPinIndex pin, FPinDelayDir dir); +``` + +Note: + +- 获取IO引脚当前的延时使能标志位 + +Input: + +- {FPinIndex} pin IO引脚延时设置索引 +- {FPinDelayDir} dir 输入/输出延时 + +Return: + +- {boolean} TRUE: 使能延时, FALSE: 去使能延时 + +#### FPinSetDelay + +```c +void FPinSetDelay(const FPinIndex pin, FPinDelayDir dir, FPinDelayType type, FPinDelay delay); +``` + +Note: + +- 设置IO引脚延时 + +Input: + +- {FPinIndex} pin IO引脚延时设置索引 +- {FPinDelayDir} dir 输入/输出延时 +- {FPinDelayType} type 精调/粗调延时 +- {FPinDelay} delay 延时设置 + +Return: + +- 无 + +#### FPinSetDelayEn + +```c +void FPinSetDelayEn(const FPinIndex pin, FPinDelayDir dir, boolean enable); +``` + +Note: + +- 使能/去使能IO引脚延时 + +Input: + +- {FPinIndex} pin IO引脚延时设置索引 +- {FPinDelayDir} dir 输入/输出延时 +- {boolean} enable TRUE: 使能, FALSE: 去使能 + +Return: + +- 无 + +#### FPinGetDrive + +```c +FPinDrive FPinGetDrive(const FPinIndex pin); +``` + +Note: + +- 获取IO引脚的驱动能力 +- 只支持 E2000 平台 + +Input: + +- {FPinIndex} pin IO引脚索引 + +Return: + +- {FPinDrive} 引脚的当前的驱动能力 + +#### FPinSetDrive + +```c +void FPinSetDrive(const FPinIndex pin, FPinDrive drive); +``` + +Note: + +- 设置IO引脚的驱动能力 +- 只支持 E2000 平台 + +Input: + +- {FPinIndex} pin, IO引脚索引 +- {FPinDrive} drive, 引脚驱动能力设置 + +Return: + +- 无 + +#### + +```c + +``` + +Note: + +- + +Input: + +- + +Return: + +- \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/doc/reference/sdk_reference.md b/bsp/phytium/libraries/standalone/doc/reference/sdk_reference.md new file mode 100644 index 0000000000..d8096d7960 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/sdk_reference.md @@ -0,0 +1,42 @@ +# SDK参考手册 + +`SDK参考手册模板` + +- 版本:v0.1 + +## 1. SDK用户指南 + +`提供使用API过程中可能遇到的问题,按照用户使用的一般顺序排列` + +### 1.1 配套目标平台 + +### 1.2 使用构建系统 + +### 1.3 使用驱动组件 + +### 1.4 生成链接器脚本 + +### 1.5 完成引导加载 + +### 1.6 处理API错误码 + +## 2. 组件使用指南 + +`提供分模块的具体API说明,按照模块分类说明,模块内按照字母顺序排列,方便检索` + +### I2C + +(./driver_reference/i2c.md) + + + +## 3. 硬件使用指南 + +`提供硬件平台的参考资料,按字母顺序排列` + +### D2000 + +### E2000 + +### FT2000/4 + diff --git a/bsp/phytium/libraries/standalone/doc/reference/usr/install_linux_aarch64.md b/bsp/phytium/libraries/standalone/doc/reference/usr/install_linux_aarch64.md new file mode 100644 index 0000000000..e5deacef74 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/usr/install_linux_aarch64.md @@ -0,0 +1,77 @@ +# 1. Linux arm aarch64 SDK安装方法 + +Linux arm aarch64下通过通过下载SDK开发工具包完成安装,运行脚本`./setup_dev.py`完成安装,安装前请先确认当前设备属于`Linux arm aarch64` + +![linux-aarch64](../../fig/is_aarch64.png) + +Linux arm aarch64安装包集成了: +- `gcc-arm-10.3-2021.07-aarch64-aarch64-none-elf.tar.xz`和`gcc-arm-10.3-2021.07-aarch64-arm-none-eabi.tar.xz`,aarch64交叉编译链,SDK安装过程中会被解压到DEV目录的`cross_tool`目录下 +- `setup_dev.py`, 安装脚本,主要的功能包括创建sdk的profile文件,创建`PHYTIUM_DEV_PATH`环境变量,通过git拉取SDK源码,以及完成SDK安装 + + +## 1.1 获取SDK的开发环境 + +- [Linux arm aarch64](https://pan.baidu.com/s/1lQC4n8wRDSLAMTXvzPn98g) + +> 提取码:LA64 + +- Linux环境下可以用unzip命令解压 +- Linux环境下需要通过`sudo apt-get install build-essential`安装git, make和python3等工具 + +## 1.2 安装SDK开发环境 + +- (1). 解压开发环境压缩包,形成DEV目录 + +- (2). 进入DEV目录,运行`./setup_dev.py`,安装开发环境 + +![安装开发环境](../../fig/setup_aarch64_dev.png) + +- (3). 通过git拉取Phytium Standalone SDK的代码,如 + +``` +git clone https://gitee.com/phytium_embedded/phytium-standalone-sdk.git ./phytium-standalone-sdk +``` + +- (4). 进入Phytium Standalone SDK代码目录,运行`./install.py` + +``` +cd ./phytium-standalone-sdk +./install.py +``` + +![输入图片说明](../../fig/install_for_aarch64.png) + +- (5). 安装完成后重启系统 + +### Q: 如果当前环境无法连接互联网 + +- 在执行第(2)步前,需要手动下载Phytium Standalone SDK,放置在DEV目录下 +- https://gitee.com/phytium_embedded/phytium-standalone-sdk + +## 1.3 检查安装是否成功 + +- 打印下列环境变量,观察各变量是否存在,指向的目录是否正确 +> `PHYTIUM_DEV_PATH`指向DEV目录 + +> `STANDALONE_SDK_ROOT`指向SDK源文件目录 + +> `AARCH32_CROSS_PATH`指向32位交叉编译链目录 + +> `AARCH64_CROSS_PATH`指向64位交叉编译链目录 + +``` +echo $PHYTIUM_DEV_PATH +echo $STANDALONE_SDK_ROOT +echo $AARCH32_CROSS_PATH +echo $AARCH64_CROSS_PATH +``` +![检查环境变量](../../fig/check_env_for_aarch64.png) + +- 环境变量打印正确无误,表示**安装成功** +## 1.4 卸载开发环境 + +- 在DEV目录运行`./uninstall.py`完成SDK卸载 + +- 运行`rm /etc/profile.d/phytium_standalone_sdk.sh`,删除SDK配置文件 + +- 重启系统完成卸载 \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/doc/reference/usr/install_linux_x86_64.md b/bsp/phytium/libraries/standalone/doc/reference/usr/install_linux_x86_64.md new file mode 100644 index 0000000000..d90077e33a --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/usr/install_linux_x86_64.md @@ -0,0 +1,75 @@ + +# 1. Linux x86_64 SDK安装方法 + +Linux x86_64下通过通过下载SDK开发工具包完成安装,运行脚本`./setup_dev.py`完成安装,安装前请先确认当前设备属于`Linux x86_64` + +![linux-x86_64](../../fig/is_x86_64.png) + +Linux x86_64安装包集成了, +- `gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf.tar.xz`和`gcc-arm-10.3-2021.07-x86_64-arm-none-eabi.tar.xz`,aarch64交叉编译链,SDK安装过程中会被解压到DEV目录的`cross_tool`目录下 +- `setup_dev.py`, 安装脚本,主要的功能包括创建sdk的profile文件,创建`PHYTIUM_DEV_PATH`环境变量,通过git拉取SDK源码,以及完成SDK安装 + +## 1.1 获取SDK的开发环境 + +- [Linux x86_64](https://pan.baidu.com/s/1KsGcHoqOJ8nv4G1G-L5gtQ ) + +>提取码:LX64 + +- Linux环境下可以用unzip命令解压 +- Linux环境下需要通过`sudo apt-get install build-essential`安装git, make和python3等工具 + +## 1.2 安装SDK开发环境 + +- (1). 解压开发环境压缩包,形成DEV目录 + +![解压DEV](../../fig/uncompress_for_x86.png) + +- (2). 进入DEV目录,运行`./setup_dev.py`,安装开发环境 + +![安装开发环境](../../fig/setup_x86_dev.png) + +-(3). 通过git拉取Phytium Standalone SDK的代码,如 + +``` +git clone https://gitee.com/phytium_embedded/phytium-standalone-sdk.git ./phytium-standalone-sdk +``` + +- (4). 进入Phytium Standalone SDK代码目录,运行`./install.py` + +``` +cd ./phytium-standalone-sdk +./install.py +``` +![安装完成](../../fig/install_for_x86.png) + +- (3). 安装完成后重启系统 + +### Q: 如果当前环境无法连接互联网 + +- 在执行第(2)步前,需要手动下载Phytium Standalone SDK,放置在DEV目录下 +- https://gitee.com/phytium_embedded/phytium-standalone-sdk + +## 1.3 检查安装是否成功 + +- 打印下列环境变量,观察各变量是否存在,指向的目录是否正确 +> `PHYTIUM_DEV_PATH`指向DEV目录 + +> `STANDALONE_SDK_ROOT`指向SDK源文件目录 + +> `AARCH32_CROSS_PATH`指向32位交叉编译链目录 + +> `AARCH64_CROSS_PATH`指向64位交叉编译链目录 + +``` +echo $PHYTIUM_DEV_PATH $STANDALONE_SDK_ROOT $AARCH32_CROSS_PATH $AARCH64_CROSS_PATH +``` +![检查环境变量](../../fig/check_env_for_x86.png) + +- 环境变量打印正确无误,表示**安装成功** +## 1.4 卸载开发环境 + +- 在DEV目录运行`./uninstall.py`完成SDK卸载 + +- 运行`rm /etc/profile.d/phytium_standalone_sdk.sh`,删除SDK配置文件 + +- 重启系统完成卸载 diff --git a/bsp/phytium/libraries/standalone/doc/reference/usr/install_windos_wsl.md b/bsp/phytium/libraries/standalone/doc/reference/usr/install_windos_wsl.md new file mode 100644 index 0000000000..98ee0ab9df --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/usr/install_windos_wsl.md @@ -0,0 +1,113 @@ + +# 1. Windows下WSL Linux子系统 SDK安装方法 + + +在Windows下,可以通过WSL Linux子系统来获得Linux环境,并且,这种环境下的Linux系统与Windows系统的文件是完全共享的 +Linux x86_64下通过通过下载SDK开发工具包完成安装,安装前请先确认当前设备属于`Linux x86_64` +``` +uname -a +``` +![linux-x86_64](../../fig/is_x86_64.png) + +接下来正式开始安装SDK开发环境 + +## 1.1 系统安装与获取SDK的开发环境 + +- [Linux x86_64](https://pan.baidu.com/s/1KsGcHoqOJ8nv4G1G-L5gtQ ) + +>提取码:LX64 + +Windows用户可以参考此链接,安装WSL Linux子系统,并做好相应准备工作: +- [WSL Linux子系统安装教程](https://zhuanlan.zhihu.com/p/146545159) + +安装完毕后,启动Ubuntu,如果出现运行问题,可参考 +- [WSL 无法安装解决方法](https://blog.csdn.net/qq_18625805/article/details/109732122) + +在cmd上输入如下指令,查看WSL版本 +``` +wsl -l -v +``` +由于WSL2不支持本地网线连接,如果发现目前版本是WSL2,需要退回到WSL1 +``` +wsl --set-version Ubuntu-20.04 1 +``` + +>这里的20.04是Ubuntu的版本号,根据实际情况调整 + +正确结果如下 +![wsl_l_v](../../fig/wsl_l_v.png) +通过下面的指令完成对Linux系统的更新 +``` +apt update +apt upgrade +``` +Linux环境下需要通过代码安装git, make和python3等工具 +``` +sudo apt-get install build-essential +``` + +## 1.2 安装SDK开发环境 + +- (1). 解压开发环境压缩包,形成DEV目录 + +![解压DEV](../../fig/uncompress_for_x86.png) + +Linux x86_64安装包集成了: +- `gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf.tar.xz`和`gcc-arm-10.3-2021.07-x86_64-arm-none-eabi.tar.xz`,aarch64交叉编译链,在SDK安装过程中会被解压到DEV目录的`cross_tool`目录下 +- `setup_dev.py`, 一种安装脚本,主要的功能包括创建sdk的profile文件,创建`PHYTIUM_DEV_PATH`环境变量,通过git拉取SDK源码,以及完成SDK安装 + + +- (2). 进入DEV目录,运行`./setup_dev.py`,安装开发环境 + +![安装开发环境](../../fig/setup_x86_dev.png) +*如果无法正常运行请使用如下代码或者重启系统* +``` +python3 setup_dev.py +``` +>指令执行完毕后,请关注回执信息中[5],按要求输入`source /etc/profile.d/phytium_dev.sh`指令获取环境变量或者重启系统 + +- (3). 通过git拉取Phytium Standalone SDK的代码,如 + +``` +git clone https://gitee.com/phytium_embedded/phytium-standalone-sdk.git ./phytium-standalone-sdk +``` + +- (4). 进入Phytium Standalone SDK代码目录,运行`./install.py` + +``` +cd ./phytium-standalone-sdk +./install.py +``` +![安装完成](../../fig/install_for_x86.png) + +- (5). 安装完成后重启系统 + +### Q: 如果当前环境无法连接互联网 + +- 在执行第(2)步前,需要手动下载Phytium Standalone SDK,放置在DEV目录下 +- https://gitee.com/phytium_embedded/phytium-standalone-sdk + +## 1.3 检查安装是否成功 + +- 打印下列环境变量,观察各变量是否存在,指向的目录是否正确 +> `PHYTIUM_DEV_PATH`指向DEV目录 + +> `STANDALONE_SDK_ROOT`指向SDK源文件目录 + +> `AARCH32_CROSS_PATH`指向32位交叉编译链目录 + +> `AARCH64_CROSS_PATH`指向64位交叉编译链目录 + +``` +echo $PHYTIUM_DEV_PATH $STANDALONE_SDK_ROOT $AARCH32_CROSS_PATH $AARCH64_CROSS_PATH +``` +![检查环境变量](../../fig/check_env_for_x86.png) + +- 环境变量打印正确无误,表示**安装成功** +## 1.4 如何卸载开发环境 + +- 在DEV目录运行`./uninstall.py`完成SDK卸载 + +- 运行`rm /etc/profile.d/phytium_standalone_sdk.sh`,删除SDK配置文件 + +- 重启系统完成卸载 diff --git a/bsp/phytium/libraries/standalone/doc/reference/usr/install_windows.md b/bsp/phytium/libraries/standalone/doc/reference/usr/install_windows.md new file mode 100644 index 0000000000..e73ae689ed --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/usr/install_windows.md @@ -0,0 +1,142 @@ +# 1. Windows10 SDK安装方法 + +Windows 10 SDK开发环境集成了, +- `msys64`,Msys2 portable环境, 主要提供Windows上的shell命令行开发环境,包括了Cygwin (POSIX 兼容性层) 和 MinGW-w64(从"MinGW-生成") +- `setup_dev.py`, 安装脚本,主要的功能包括创建sdk的profile文件,创建`PHYTIUM_DEV_PATH`环境变量,通过git拉取SDK源码,以及完成SDK安装 +- `gcc-arm-10.3-2021.07-mingw-w64-i686-aarch64-none-elf.tar.xz`和`gcc-arm-10.3-2021.07-mingw-w64-i686-arm-none-eabi.tar.xz`, mingw64交叉编译链, SDK安装过程中会被解压到DEV目录的`cross_tool`目录下 +- `tftp`, tftp工具, 提供tftp服务,用于开发板下载二进制镜像文件,主要的功能包括安装tftp32服务和配置tftp目录 +- `run_msys2.cmd`,用于打开和配置msys2 shell的脚本,需要设置`PHYTIUM_DEV_PATH`后才能使用 +- `run_tftd.cmd`,用于打开tftpd工具的脚本,需要设置`PHYTIUM_DEV_PATH`后才能使用 +- `tftp/reinstall.cmd`, 用于安装和配置tftpd服务的脚本 +- `tftp/uninstall.cmd`, 用于卸载tftpd服务的脚本 + +## 1.1 获取SDK的开发环境 + +- [Windows10](https://pan.baidu.com/s/1V96isNcPq4F7nKi3_8GoGg) +>提取码:WX64 + +- Windows环境下可以用通用的unzip工具解压,如7zip和winrar + +## 1.2 安装SDK开发环境 + +- (1). 添加Windows环境变量,`PHYTIUM_DEV_PATH`(环境变量名**不能自定义**),例如,指向文件夹`E:\phytium-dev-windows-nt`(可以自定义) + +![配置环境变量](../../fig/add_path_for_win.png) + +- (2). 进入DEV目录,双击脚本`run_msys2.cmd`, 进入msys2控制台, 运行`./setup_dev.py`,注册开发环境 + +![安装开发环境](../../fig/install_for_mingw.png) + +-(3). DEV目录注册完成后,通过git拉取Phytium Standalone SDK的代码,如 + +``` +git clone https://gitee.com/phytium_embedded/phytium-standalone-sdk.git ./phytium-standalone-sdk +``` + +- (4). 进入Phytium Standalone SDK代码目录,运行`./install.py` + +``` +cd ./phytium-standalone-sdk +./install.py +``` + +![开发环境安装完成](../../fig/setup_win.png) + +- (5). 安装完成后重启系统 + +### Q: 如果当前环境无法连接互联网 + +- 在执行第(2)步前,需要手动下载Phytium Standalone SDK,放置在DEV目录下 +- https://gitee.com/phytium_embedded/phytium-standalone-sdk + +![手动下载SDK](../../fig/git_url.png) + +## 1.3 检查安装是否成功 + +- 打印下列环境变量,观察各变量是否存在,指向的目录是否正确 +> `PHYTIUM_DEV_PATH`指向DEV目录 + +> `STANDALONE_SDK_ROOT`指向SDK源文件目录 + +> `AARCH32_CROSS_PATH`指向32位交叉编译链目录 + +> `AARCH64_CROSS_PATH`指向64位交叉编译链目录 + +``` +echo $PHYTIUM_DEV_PATH $STANDALONE_SDK_ROOT $AARCH32_CROSS_PATH $AARCH64_CROSS_PATH +``` + +- 环境变量打印正确无误,表示**安装成功** +## 1.4 Windows 10安装Msys2(可选) + +- (1). 获取[Windows环境安装包](https://pan.baidu.com/s/17WX5hec7t8_ubAKzFCwQAA) + +> 提取码:MGW6 + +- msys2, `msys2-x86_64-20210725.exe` +- mingw64-arm交叉编译链, `gcc-arm-10.3-2021.07-mingw-w64-i686-arm-none-eabi.tar.xz`, `gcc-arm-10.3-2021.07-mingw-w64-i686-aarch64-none-elf.tar.xz` +- tftp工具,`tftp.zip` + +- (2). 创建Windows集成开发环境(DEV目录),如`D:/phytium-dev`, 将DEV目录添加在Windows环境变量中,变量名为`PHYTIUM_DEV_PATH`,如下图所示,保存环境变量, + +> 对于Windows 10,在桌面左下角系统搜索框中输入“环境变量”即可进入环境变量编辑界面 + +> DEV目录中不要留空格 + +![输入图片说明](../../fig/add_path_for_win.png) + +- (3). 保存DEV环境变量后,打开一个控制台,输入`echo %PHYTIUM_DEV_PATH%`,检查环境变量是否设置成功, + +![输入图片说明](../../fig/check_env_for_win.png) + +- (4). 双击`msys2-x86_64-20210725.exe`,设置`Msys2`的安装路径在DEV路径下,其余设置按默认安装,注意安装完成后不要马上启动,最后一步取消勾选“马上启动Msys2” + +![输入图片说明](../../fig/install_msys2.png) + +![Msys2安装过程](../../fig/installing_msys2.png) + +- (5). `Msys2`安装完成后,需要添加国内软件源,否则下载速度会很慢,进入`D:\phytium-dev\msys64\etc\pacman.d`目录下,找到以下三个文件,在文件末尾分别添加对应的软件源 + +- mirrorlist.mingw32 +``` +Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/mingw/i686 +``` + +- mirrorlist.mingw64 +``` +Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/mingw/x86_64 +``` + +- mirrorlist.msys +``` +Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/msys/$arch +``` + +- (6). 进入`D:\phytium-dev\msys64`目录,双击`msys2_shell.cmd`启动运行`Msys2`,输入以下命令,更新`Msys2`软件,安装必要组件 + +> 如果执行失败,可以多次尝试,直到没有报错 + +> 安装过程全部选用默认方式`default`或选择`y`,注意如果没有`default = all`,需要输入`y` + +``` +$ pacman -Syu +$ pacman -S mingw-w64-x86_64-toolchain +$ pacman -S base-devel git python3 python3-pip +``` + +![更新Msys2软件源](../../fig/update_packman.png) + +- (7). 运行以下命令,检查组件是否安装完全 +``` +$ pacman -Q make git wget python3 python3-pip +``` + +## 1.5 卸载开发环境 + +- 在DEV目录下双击`run_msys2.cmd`, 启动`Msys2`控制台,在控制台输入`./uninstall.py`完成SDK卸载 + +- 在`Msys2`控制台运行`rm /etc/profile.d/phytium_standalone_sdk.sh`,删除SDK配置文件 + +- 在DEV目录`D:\phytium-dev\tftp`下以**管理员权限**打开Windows命令行中断,运行`uninstall.cmd`完成Tftd卸载 + +- 重启系统完成卸载 \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/doc/reference/usr/usage.md b/bsp/phytium/libraries/standalone/doc/reference/usr/usage.md new file mode 100644 index 0000000000..2e03a76df1 --- /dev/null +++ b/bsp/phytium/libraries/standalone/doc/reference/usr/usage.md @@ -0,0 +1,224 @@ +# 使用方法 +## 1.1前期配置 +在正式调用SDK实例前,需要对tftp进行一系列配置,Windows下与Linux下的方法有很大的区别,下面我们正式开始 +>对于权限不够的问题,普通用户可以在指令前加上`sudo`来暂时获得root权限 + +### 1.1.1 在 host 侧(Ubuntu 20.04)配置 tftp 服务 + +- 在开发环境`host`侧安装`tftp`服务 + +``` +sudo apt-get install tftp-hpa tftpd-hpa +sudo apt-get install xinetd +``` + +- 新建 `tftboot`目录, `/mnt/d/tftboot`, 此目录应与项目编译脚本makefile中的USR_BOOT_DIR一致, 并确保 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 +} +``` +--- +在配置上述文件时,如果之前没有使用过Linux文件编写,可以使用自带的vim编辑器 +``` +vim /etc/xinetd.d/tftp +``` +--- +- 启动主机`tftp`服务,生成默认配置 + +``` +$ sudo service tftpd-hpa start +``` + +- 修改主机`tftp`配置,指向`tftboot`目录 + 修改/etc/default/tftpd-hpa +``` +vim /etc/default/tftpd-hpa +``` + +``` +$ sudo vim /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 +``` +>注意,每次进入Linux系统时都需要使用该代码重启主机`tftp`服务 +- 测试主机`tftp`服务的可用性 +登录`tftp`服务,获取`tftboot`目录下的一个文件 + +``` +$ tftp 192.168.4.50 +tftp> get test1234 +tftp> q +``` + +### 1.1.2 在 host 侧(Windows)配置 tftp 服务 + +- 将`tftp.zip`解压到开发环境,如`D:\**\phytium-dev-windows-nt\tftp` + >如果`phytium-dev-windows-nt\tftp`已经存在,则直接进行下一步 +- 以**管理员权限**打开Windows cmd,进入`D:\**\phytium-dev-windows-nt\tftp`,运行`.\reinstall.cmd`完成Tftpd服务安装 +![输入图片说明](../../fig/admin_rights.png) +![输入图片说明](../../fig/tftp32_srv.png) + >注意每次使用前,都需要进入Windows服务,手动将tftp服务打开 + +### 1.1.3 配置以太网ipv4设置 +- 为了连接串口,打开windows下的以太网设置界面,选择手动设置,配置相关地址 +![输入图片说明](../../fig/ipv4_setting.png) + +## 1.2 连接开发板,着手跑通用例 +- 在完成前面的步骤后,就可以着手连接开发板。连接之前,需要下载Windows下与的开发板配套软件 +[MobaXterm_Portable_v22.0](https://pan.baidu.com/s/1IjDG2j5YwK9IhpBR4ChoYA ) + >提取码:ROOT + +- 如果是不用内置Linux子系统的windows用户还需要在`tftp`中配置。配置完成后,进入SDK,双击`D:\phytium-dev\phytium-standalone-sdk`目录下的`run_tftd.cmd`,启动tftp工具,设置tftp目录和ip +![输入图片说明](../../fig/config_tftp32.png) + +### 1.2.1 编译获取.elf与.bin文件 +以WSL为例: +- 进入SDK所在目录,右键打开Windows终端,输入以下指令 + ``` + wsl + ``` + ![输入图片说明](../../fig/wsl_teiminal.png) +- 进入`baremetal`文件夹,选取一个希望执行的用例。以`uart测试`为例,在wsl下输入如下指令,进入测试用例所在文件夹 + ``` + cd baremetal/example/peripheral/serial/fpl011_test + ``` +- 根据需求,配置在不同芯片下32位或64位的编译模式,这里以`飞腾新四核芯片`的32位为例,输入以下指令 + ``` + make load_ft2004_aarch32 + ``` +- 编译测试用例,输入以下指令 + ``` + make clean boot + ``` +- 出现如下信息,表示编译成功,编译结果.elf与.bin文件已经保存至tftboot文件夹内 +![输入图片说明](../../fig/wsl_make_success.png) +![输入图片说明](../../fig/bin_show.png) + +### 1.2.2 配置开发板ip,连通host下载启动镜像 +- 连通开发板串口,打开windows的设备管理器,在端口栏确认串口是否成果接入 +![输入图片说明](../../fig/Serial_inform.png) + +- 打开MobaXterm_Portable软件,选择`Session`,然后选择`Serial`,选择串口(如上图所示串口号为`COM3`),设置波特率`bps`为115200 + +- 进入`u-boot`界面,输入如下指令,配置开发板ip,`host`侧ip和网关地址 + ``` + setenv ipaddr 192.168.4.20 + setenv serverip 192.168.4.50 + setenv gatewayip 192.168.4.1 + ``` +- 随后烧录`tftboot`文件夹下的文件到开发板,输入以下指令 + ``` + tftpboot 0x90100000 baremetal.elf + bootelf -p 0x90100000 + ``` + + > 镜像启动的地址为`0x80100000`, 对于`BIN`文件,需要直接加载到`0x80100000`,对于`ELF`文件,启动地址会自动获取,需要加载到`DRAM`中一段可用的地址,这里选择`0x90100000` + +- 见到如下结果,表示成功进入测试环境,可以自行浏览每个测试用例文件夹下对应的`README.md`文件,根据其中的提示,进行用例调试 +![输入图片说明](../../fig/letter_shell.png) + + + + + +## 1.3 新建一个baremetal应用工程 +如果您希望自己建立一个应用工程,可以参考下面的流程 +### 1.3.1 选择工程模板 + +- 复制`~/standalone-sdk/example/template`目录,作为 baremetal 应用工程 + + > `*` 表示可选文件/目录 + ``` + $ ls + Kconfig --> 应用工程配置menu文件 + makefile --> makefile + main.c --> 包含main函数 + sdkconfig --> 配置输出 + sdkconfig.h --> 配置输出 + inc --> 用户头文件* + src --> 用户源文件* + ``` + >请注意使用小写makefile,使用Makefile在部分平台不能被识别 + +### 1.3.2 选择目标平台 + +- 切换目标平台, e.g `FT2000/4 AARCH32`, 加载默认配置 + + ``` + make config_ft2004_aarch32 + ``` + + > 使用`FT2000-4`作为目标编译平台,通过`make config_ft2004_aarch32`和`make config_ft2004_aarch64`加载默认配置 + + > 使用`D2000`作为目标编译平台,通过`make config_d2000_aarch32`和`make config_d2000_aarch64`加载默认配置 + +- 编译应用工程, 生成`*.bin`文件用于下载到开发板 + ``` + $ make + $ ls + template.bin --> 二进制文件 + template.dis --> 反汇编文件 + template.elf --> ELF文件 + template.map --> 内存布局文件 + ``` + ![输入图片说明](../../fig/compiling.png) + +### 1.3.3 快速使用例程 + +- 将`BIN`文件或者`ELF`文件复制到`tftpboot`目录 + ``` + $ cp ./baremetal.bin /mnt/d/tftboot + $ cp ./baremetal.elf /mnt/d/tftboot + ``` + > ~/standalone-sdk/example/aarch32_hello_world + ![输入图片说明](../../fig/load_image.png) +- 结合1.2节中的操作,即可快速使用自己创建的例程 \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/Kconfig b/bsp/phytium/libraries/standalone/drivers/Kconfig new file mode 100644 index 0000000000..69f79376af --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/Kconfig @@ -0,0 +1,237 @@ +menu "Components Configuration" +config USE_SPI + bool + prompt "Use Spi" + default n + help + Include SPI modules and enable SPI + + if USE_SPI + source "$STANDALONE_DIR/drivers/spi/Kconfig" + endif + +config USE_QSPI + bool + prompt "Use QSpi" + default n + help + Include QSPI modules and enable QSPI + + if USE_QSPI + source "$STANDALONE_DIR/drivers/qspi/Kconfig" + endif + +config USE_GIC + bool + prompt "Use Gic" + default y + help + Include Generic Interrupt Controllor + + if USE_GIC + source "$STANDALONE_DIR/drivers/gic/Kconfig" + endif + +config USE_SERIAL + bool + prompt "Use SERIAL" + default n + help + Include serial modules and enable serial + + if USE_SERIAL + source "$STANDALONE_DIR/drivers/serial/Kconfig" + endif + + +config USE_GPIO + bool + prompt "Use Gpio" + default n + help + Include GPIO modules and enable GPIO + + if USE_GPIO + source "$STANDALONE_DIR/drivers/pin/fgpio/Kconfig" + endif + +config USE_ETH + bool + prompt "Use Eth" + default n + help + Include eth + + if USE_ETH + source "$STANDALONE_DIR/drivers/eth/Kconfig" + endif + +config USE_CAN + bool + prompt "Use Can" + default n + help + Include can + + if USE_CAN + source "$STANDALONE_DIR/drivers/can/Kconfig" + endif + +config USE_I2C + bool + prompt "Use I2C" + default n + help + Include I2C modules and enable I2C + + if USE_I2C + source "$STANDALONE_DIR/drivers/i2c/Kconfig" + endif + + +config USE_TIMER + bool + prompt "Use Timer" + default n + help + Include Timer modules and enable Timer + + if USE_TIMER + source "$STANDALONE_DIR/drivers/timer/Kconfig" + endif + +config USE_MIO + bool + prompt "Use Mio" + default n + help + Include Mio modules and enable Mio + + if USE_MIO + source "$STANDALONE_DIR/drivers/mio/Kconfig" + endif + +config USE_SDMMC + bool + prompt "Use SD/MMC" + default n + help + Include SD/MMC modules and enable Timer + + if USE_SDMMC + source "$STANDALONE_DIR/drivers/mmc/Kconfig" + endif + +config USE_PCIE + bool + prompt "Use PCIE" + default n + help + Include PCIE + + if USE_PCIE + source "$STANDALONE_DIR/drivers/pcie/Kconfig" + endif + +config USE_WDT + bool + prompt "Use WDT" + default n + help + Include watchdog timer modules and enable watchdog timer + + if USE_WDT + source "$STANDALONE_DIR/drivers/watchdog/Kconfig" + endif + + +config USE_DMA + bool + prompt "Use DMA" + default n + help + Include DMA + + if USE_DMA + source "$STANDALONE_DIR/drivers/dma/Kconfig" + endif + +config USE_NAND + bool + prompt "Use NAND" + help + Include NAND + + if USE_NAND + source "$STANDALONE_DIR/drivers/nand/Kconfig" + endif + +config USE_RTC + bool + prompt "Use RTC" + default n + help + Include system rtc service + + if USE_RTC + source "$STANDALONE_DIR/drivers/rtc/Kconfig" + endif + +config USE_SATA + bool + prompt "Use SATA" + default n + help + Include system sata service + + if USE_SATA + source "$STANDALONE_DIR/drivers/sata/Kconfig" + endif + +config USE_USB + bool + prompt "Use USB" + default n + help + Include USB Subsystem + + if USE_USB + source "$STANDALONE_DIR/drivers/usb/Kconfig" + endif + +config USE_ADC + bool + prompt "Use ADC" + default n + help + Include adc modules and enable adc + + if USE_ADC + source "$STANDALONE_DIR/drivers/adc/Kconfig" + endif + +config USE_PWM + bool + prompt "Use PWM" + default n + help + Include system pwm + + if USE_PWM + source "$STANDALONE_DIR/drivers/pwm/Kconfig" + endif + +config USE_IPC + bool + prompt "Use IPC" + default n + help + Include IPC Service + + if USE_IPC + source "$STANDALONE_DIR/drivers/ipc/Kconfig" + endif + + +endmenu + diff --git a/bsp/phytium/libraries/standalone/drivers/adc/Kconfig b/bsp/phytium/libraries/standalone/drivers/adc/Kconfig new file mode 100644 index 0000000000..58f7dd8a6f --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/adc/Kconfig @@ -0,0 +1,10 @@ + +menu "ADC Configuration" + config USE_FADC + bool + prompt "Use FADC" + default n + +endmenu + + diff --git a/bsp/phytium/libraries/standalone/drivers/adc/fadc/fadc.c b/bsp/phytium/libraries/standalone/drivers/adc/fadc/fadc.c new file mode 100644 index 0000000000..83247b60e6 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/adc/fadc/fadc.c @@ -0,0 +1,535 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fadc.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:28:45 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ +#include +#include "fgeneric_timer.h" +#include "fkernel.h" +#include "ftypes.h" +#include "ferror_code.h" +#include "fdebug.h" +#include "fadc.h" +#include "fadc_hw.h" +#include "fparameters.h" +#include "fassert.h" +#include "fsleep.h" + +#define FADC_DEBUG_TAG "FT_ADC" +#define FADC_DEBUG(format, ...) FT_DEBUG_PRINT_D(FADC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FADC_INFO(format, ...) FT_DEBUG_PRINT_I(FADC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FADC_WARN(format, ...) FT_DEBUG_PRINT_W(FADC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FADC_ERROR(format, ...) FT_DEBUG_PRINT_E(FADC_DEBUG_TAG, format, ##__VA_ARGS__) + +#define FADC_MAX_CLOCK_PRESC 16 + +#define FADC_MAX_THRESHOLD 0x400 + + +/** + * @name: FAdcPowerDownControl + * @msg: Set power down signal + * @param {FAdcCtrl} *pctrl, pointer to a FAdcCtrl structure that contains + * the configuration information for the specified adc module. + * @param {u8} power_state, this parameter must be enable or disable. + * @return err code information, FADC_SUCCESS indicates success,others indicates failed + */ +static FError FAdcPowerDownControl(FAdcCtrl *pctrl, u8 power_state) +{ + + FASSERT(pctrl != NULL); + FASSERT(FT_COMPONENT_IS_READY == pctrl->is_ready); + u32 reg_val = 0; + uintptr base_addr = pctrl->config.base_addr; + reg_val = FADC_READ_REG32(base_addr, FADC_CTRL_REG_OFFSET); + if (power_state == FADC_CTRL_PD_ENABLE) + { + reg_val |= FADC_CTRL_REG_PD_EN; + } + else + { + reg_val &= ~(FADC_CTRL_REG_PD_EN); + } + + FADC_WRITE_REG32(base_addr, FADC_CTRL_REG_OFFSET, reg_val); + + return FADC_SUCCESS; +} + +/** + * @name: FAdcChannelEnable + * @msg: enable channel, corresponding to fix channel mode or multi channel mode. + * @param {FAdcCtrl} *pctrl, pointer to a FAdcCtrl structure that contains + * the configuration information for the specified adc module. + * @param {FAdcChannel} channel, adc channel number. + * @param {boolean} state, TRUE-enable, FALSE-disable + * @return void + */ +void FAdcChannelEnable(FAdcCtrl *pctrl, FAdcChannel channel, boolean state) +{ + FASSERT(pctrl != NULL); + FASSERT(FT_COMPONENT_IS_READY == pctrl->is_ready); + FASSERT(channel < FADC_CHANNEL_NUM); + u32 reg_val = 0; + uintptr base_addr = pctrl->config.base_addr; + reg_val = FADC_READ_REG32(base_addr, FADC_CTRL_REG_OFFSET); + + if (state == TRUE) + { + if (reg_val & FADC_CTRL_REG_FIX_CHANNEL) + { + reg_val &= ~(FADC_CTRL_REG_FIX_CHANNEL_NUM_MASK); + reg_val |= FADC_CTRL_REG_FIX_CHANNEL_NUM(channel); + } + else + { + reg_val |= FADC_CTRL_REG_CHANNEL_EN(channel); + } + } + else + { + /* fix channel mode, disable means stop convert */ + if (reg_val & FADC_CTRL_REG_FIX_CHANNEL) + { + reg_val &= ~(FADC_CTRL_REG_SOC_EN); + + } + else + { + reg_val &= ~(FADC_CTRL_REG_CHANNEL_EN(channel)); + } + } + + + FADC_WRITE_REG32(base_addr, FADC_CTRL_REG_OFFSET, reg_val); + +} + +/** + * @name: FAdcChannelThresholdSet + * @msg: Set adc channel high_threshold and low_threshold. + * you need use this function after FAdcConvertSet. If you want to use this function to + * add other channel enable when the adc conversion is started, you need to restart the + * adc convert start signal(adc_soc_en) after use to make the operation valid. + * @param {FAdcCtrl} *pctrl, pointer to a FAdcCtrl structure that contains + * the configuration information for the specified adc module. + * @param {FAdcChannel} channel, adc channel number. + * @param {FAdcThresholdConfig} *threshold_config, pointer to adc channel threshold value struct. + * @return err code information, FADC_SUCCESS indicates success,others indicates failed + */ +FError FAdcChannelThresholdSet(FAdcCtrl *pctrl, FAdcChannel channel, FAdcThresholdConfig *threshold_config) +{ + FASSERT(pctrl != NULL); + FASSERT(FT_COMPONENT_IS_READY == pctrl->is_ready); + FASSERT(channel < FADC_CHANNEL_NUM); + + u16 low_threshold = threshold_config->low_threshold; + u16 high_threshold = threshold_config->high_threshold; + + FASSERT(high_threshold < FADC_MAX_THRESHOLD); + FASSERT(low_threshold < high_threshold); + + uintptr base_addr = pctrl->config.base_addr; + + u32 threshold = (FADC_LEVEL_REG_HIGH_LEVEL(high_threshold)) | + (FADC_LEVEL_REG_LOW_LEVEL(low_threshold)); + FADC_WRITE_REG32(base_addr, (FADC_LEVEL_REG_OFFSET(channel)), threshold); + + return FADC_SUCCESS; +} + +/** + * @name: FAdcConvertSet + * @msg: config adc convert parameters. + * @param {FAdcCtrl} *pctrl, pointer to a FAdcCtrl structure that contains + * the configuration information for the specified adc module. + * @param {FAdcConvertConfig} *convert_config, include convert mode,channel mode, + * clock divider and convert_interval. + * @return err code information, FADC_SUCCESS indicates success,others indicates failed + */ +FError FAdcConvertSet(FAdcCtrl *pctrl, FAdcConvertConfig *convert_config) +{ + FASSERT(pctrl != NULL); + FASSERT(FT_COMPONENT_IS_READY == pctrl->is_ready); + + u32 reg_val = 0; + uintptr base_addr = pctrl->config.base_addr; + reg_val = FADC_READ_REG32(base_addr, FADC_CTRL_REG_OFFSET); + + /* clk_div config */ + u32 clk_div = convert_config->clk_div; + FASSERT(clk_div < FADC_MAX_CLOCK_PRESC); + if (clk_div % 2 == 1) + { + FADC_ERROR("clk_div is not even."); + return FADC_ERR_INVAL_PARM; + } + reg_val &= (~FADC_CTRL_REG_CLK_DIV_MASK); + reg_val |= FADC_CTRL_REG_CLK_DIV(clk_div); + + /* config convert mode */ + FAdcConvertMode convert_mode = convert_config->convert_mode; + FASSERT(convert_mode < FADC_CONVERT_MODE_NUM); + if (convert_mode == FADC_SINGLE_CONVERT) + { + reg_val |= FADC_CTRL_REG_SINGLE_CONVERT; + } + else + { + reg_val &= ~(FADC_CTRL_REG_SINGLE_CONVERT); + } + + /* config channel mode */ + FAdcChannelMode channel_mode = convert_config->channel_mode; + FASSERT(channel_mode < FADC_CHANNEL_MODE_NUM); + + if (channel_mode == FADC_FIXED_CHANNEL) + { + reg_val |= FADC_CTRL_REG_FIX_CHANNEL; + } + else + { + reg_val &= ~(FADC_CTRL_REG_FIX_CHANNEL); + } + FADC_WRITE_REG32(base_addr, FADC_CTRL_REG_OFFSET, reg_val); + + /* config time interval between two converts */ + FADC_WRITE_REG32(base_addr, FADC_INTER_REG_OFFSET, convert_config->convert_interval); + + return FADC_SUCCESS; +} + + +/** + * @name: FAdcInterruptEnable + * @msg: enable channel interrupt. + * @param {FAdcCtrl} *pctrl, pointer to a FAdcCtrl structure that contains + * the configuration information for the specified adc module. + * @param {FAdcChannel} channel, adc channel number. + * @param {FAdcIntrEvtType} event_type, interrupt event type + * @return err code information, FADC_SUCCESS indicates success,others indicates failed + */ +FError FAdcInterruptEnable(FAdcCtrl *pctrl, FAdcChannel channel, FAdcIntrEventType event_type) +{ + FASSERT(pctrl != NULL); + FASSERT(FT_COMPONENT_IS_READY == pctrl->is_ready); + + uintptr base_addr = pctrl->config.base_addr; + u32 reg_val = 0; + reg_val = FADC_READ_REG32(base_addr, FADC_INTRMASK_REG_OFFSET); + switch (event_type) + { + case FADC_INTR_EVENT_COVFIN: /* enable channel convert complete irq */ + reg_val &= ~(FADC_INTRMASK_REG_COVFIN_MASK(channel)); + break; + + case FADC_INTR_EVENT_DLIMIT: + reg_val &= ~(FADC_INTRMASK_REG_DLIMIT_MASK(channel)); + break; + + case FADC_INTR_EVENT_ULIMIT: + reg_val &= ~(FADC_INTRMASK_REG_ULIMIT_MASK(channel)); + break; + + case FADC_INTR_EVENT_ERROR: + reg_val &= ~(FADC_INTRMASK_REG_ERR_MASK); + break; + + default: + break; + } + + FADC_WRITE_REG32(base_addr, FADC_INTRMASK_REG_OFFSET, reg_val); + + return FADC_SUCCESS; +} + +/** + * @name: FAdcInterruptDisable + * @msg: disable channel interrupt. + * @param {FAdcCtrl} *pctrl, pointer to a FAdcCtrl structure that contains + * the configuration information for the specified adc module. + * @param {FAdcChannel} channel, adc channel number. + * @param {FAdcIntrEvtType} event_type, interrupt event type + * @return err code information, FADC_SUCCESS indicates success,others indicates failed + */ +FError FAdcInterruptDisable(FAdcCtrl *pctrl, FAdcChannel channel, FAdcIntrEventType event_type) +{ + FASSERT(pctrl != NULL); + FASSERT(FT_COMPONENT_IS_READY == pctrl->is_ready); + FASSERT(channel < FADC_CHANNEL_NUM); + + uintptr base_addr = pctrl->config.base_addr; + u32 reg_val = 0; + reg_val = FADC_READ_REG32(base_addr, FADC_INTRMASK_REG_OFFSET); + switch (event_type) + { + case FADC_INTR_EVENT_COVFIN: /* enable channel convert complete irq */ + reg_val |= (FADC_INTRMASK_REG_COVFIN_MASK(channel)); + break; + + case FADC_INTR_EVENT_DLIMIT: + reg_val |= (FADC_INTRMASK_REG_DLIMIT_MASK(channel)); + break; + + case FADC_INTR_EVENT_ULIMIT: + reg_val |= (FADC_INTRMASK_REG_ULIMIT_MASK(channel)); + break; + + case FADC_INTR_EVENT_ERROR: + reg_val |= (FADC_INTRMASK_REG_ERR_MASK); + break; + + default: + break; + } + + FADC_WRITE_REG32(base_addr, FADC_INTRMASK_REG_OFFSET, reg_val); + + return FADC_SUCCESS; +} + +/** + * @name: FAdcConvertStart + * @msg: Start adc convert. + * @param {FAdcCtrl} *pctrl, pointer to a FAdcCtrl structure that contains + * the configuration information for the specified adc module. + * @return + */ +void FAdcConvertStart(FAdcCtrl *pctrl) +{ + FASSERT(pctrl != NULL); + FASSERT(FT_COMPONENT_IS_READY == pctrl->is_ready); + u32 reg_val = 0; + uintptr base_addr = pctrl->config.base_addr; + reg_val = FADC_READ_REG32(base_addr, FADC_CTRL_REG_OFFSET); + reg_val |= FADC_CTRL_REG_SOC_EN; + FADC_WRITE_REG32(base_addr, FADC_CTRL_REG_OFFSET, reg_val); + +} + +/** + * @name: FAdcConvertStop + * @msg: Stop adc convert. + * @param {FAdcCtrl} *pctrl, pointer to a FAdcCtrl structure that contains + * the configuration information for the specified adc module. + * @return + */ +void FAdcConvertStop(FAdcCtrl *pctrl) +{ + FASSERT(pctrl != NULL); + FASSERT(FT_COMPONENT_IS_READY == pctrl->is_ready); + u32 reg_val = 0; + uintptr base_addr = pctrl->config.base_addr; + reg_val = FADC_READ_REG32(base_addr, FADC_CTRL_REG_OFFSET); + reg_val &= (~FADC_CTRL_REG_SOC_EN); + FADC_WRITE_REG32(base_addr, FADC_CTRL_REG_OFFSET, reg_val); + +} + +/** + * @name: FAdcInit + * @msg: init adc variable configuration. + * @param {FAdcCtrl} *pctrl, pointer to a FAdcCtrl structure that contains + * the configuration information for the specified adc module. + * @param {FAdcConvertConfig} *convert_config, pointer to adc convert configuration + * @return err code information, FADC_SUCCESS indicates success,others indicates failed + */ +FError FAdcVariableConfig(FAdcCtrl *pctrl, FAdcConvertConfig *convert_config) +{ + FASSERT(pctrl != NULL); + FASSERT(convert_config != NULL); + + FError ret = FADC_SUCCESS; + + /* disable power down signal */ + ret = FAdcPowerDownControl(pctrl, FADC_CTRL_PD_DISABLE); + if (ret != FADC_SUCCESS) + { + FADC_ERROR("FAdcPowerDownControl failed."); + return FADC_ERR_CMD_FAILED; + } + + /* set time interval between two converts */ + ret = FAdcConvertSet(pctrl, convert_config); + if (ret != FADC_SUCCESS) + { + FADC_ERROR("FAdcConvertSet failed."); + return FADC_ERR_CMD_FAILED; + } + + return ret; +} + +/** + * @name: FAdcSingleConvertEnable + * @msg: Enable single convert signal, when convert mode is set to single conversion. + * @param {FAdcCtrl} *pctrl, pointer to a FAdcCtrl structure that contains + * the configuration information for the specified adc module. + * @return err code information, FADC_SUCCESS indicates success,others indicates failed + */ +static FError FAdcSingleConvertEnable(FAdcCtrl *pctrl) +{ + FASSERT(pctrl != NULL); + FASSERT(FT_COMPONENT_IS_READY == pctrl->is_ready); + u32 reg_val = 0; + uintptr base_addr = pctrl->config.base_addr; + reg_val = FADC_READ_REG32(base_addr, FADC_CTRL_REG_OFFSET); + reg_val |= FADC_CTRL_REG_SINGLE_CONVERT_EN; + FADC_WRITE_REG32(base_addr, FADC_CTRL_REG_OFFSET, reg_val); + + return FADC_SUCCESS; +} + +/** + * @name: FAdcReadConvertResult + * @msg: read adc channel convert result value. + * @param {FAdcCtrl} *pctrl, pointer to a FAdcCtrl structure that contains + * the configuration information for the specified adc module. + * @param {FAdcChannel} channel, adc channel number. + * @param {u16} *val, pointer to adc convert result value. + * @return err code information, FADC_SUCCESS indicates success,others indicates failed. + */ +FError FAdcReadConvertResult(FAdcCtrl *pctrl, FAdcChannel channel, u16 *val) +{ + FASSERT(pctrl != NULL); + FASSERT(channel < FADC_CHANNEL_NUM); + FASSERT(FT_COMPONENT_IS_READY == pctrl->is_ready); + int timeout = FADC_READ_DELAY; + uintptr base_addr = pctrl->config.base_addr; + + u32 reg_val = FADC_READ_REG32(base_addr, FADC_CTRL_REG_OFFSET); + /* single conversion */ + if (reg_val & FADC_CTRL_REG_SINGLE_CONVERT) + { + FAdcSingleConvertEnable(pctrl); + } + + do + { + fsleep_millisec(10); + } + while ((!pctrl->convert_complete[channel]) && (0 <= --timeout)); + + if (0 >= timeout) + { + FADC_ERROR("timeout when read adc data, convert is not completed."); + *val = 0; + return FADC_ERR_TIMEOUT; + } + + FADC_CONVERT_UNCOMPLETE(pctrl->convert_complete[channel]); + *val = pctrl->value[channel]; + + return FADC_SUCCESS; +} + +/** + * @name: FAdcReadFinishCnt + * @msg: read adc channel convert finish count. + * @param {FAdcCtrl} *pctrl, pointer to a FAdcCtrl structure that contains + * the configuration information for the specified adc module. + * @param {FAdcChannel} channel, adc channel number. + * @param {u32} *count, pointer to adc convert finish count. + * @return err code information, FADC_SUCCESS indicates success,others indicates failed. + */ +FError FAdcReadFinishCnt(FAdcCtrl *pctrl, FAdcChannel channel, u32 *count) +{ + FASSERT(pctrl != NULL); + FASSERT(FT_COMPONENT_IS_READY == pctrl->is_ready); + + uintptr base_addr = pctrl->config.base_addr; + *count = FADC_READ_REG32(base_addr, FADC_FINISH_CNT_REG_OFFSET(channel)); + return FADC_SUCCESS; +} + +/** + * @name: FAdcReadHisLimit + * @msg: read adc channel history limit value, include upper limit and lower limit. + * @param {FAdcCtrl} *pctrl, pointer to a FAdcCtrl structure that contains + * the configuration information for the specified adc module. + * @param {FAdcChannel} channel, adc channel number. + * @param {u16} *u_his_limit, pointer to adc convert history upper limit value. + * @param {u16} *d_his_limit, pointer to adc convert history lower limit value. + * @return err code information, FADC_SUCCESS indicates success,others indicates failed. + */ +FError FAdcReadHisLimit(FAdcCtrl *pctrl, FAdcChannel channel, u16 *u_his_limit, u16 *d_his_limit) +{ + FASSERT(pctrl != NULL); + FASSERT(FT_COMPONENT_IS_READY == pctrl->is_ready); + u32 reg_val = 0; + uintptr base_addr = pctrl->config.base_addr; + + reg_val = FADC_READ_REG32(base_addr, FADC_HIS_LIMIT_REG_OFFSET(channel)); + + *u_his_limit = (reg_val & FADC_HIS_LIMIT_REG_UMASK) >> 16; + *d_his_limit = (reg_val & FADC_HIS_LIMIT_REG_DMASK) ; + + return FADC_SUCCESS; +} + +/** + * @name: FAdcDeInitialize + * @msg: DeInitialization function for the device instance + * @param {FAdcCtrl} *pctrl, instance of FADC controller + * @return {*} + */ +void FAdcDeInitialize(FAdcCtrl *pctrl) +{ + FASSERT(pctrl); + + pctrl->is_ready = 0; + memset(pctrl, 0, sizeof(*pctrl)); + + return; +} + +/** + * @name: FAdcCfgInitialize + * @msg: Initializes a specific instance such that it is ready to be used. + * @param {FAdcCtrl} *pctrl, instance of FADC controller + * @param {FAdcConfig} *input_config_p, Default configuration parameters of FADC + * @return err code information, FADC_SUCCESS indicates success,others indicates failed + */ +FError FAdcCfgInitialize(FAdcCtrl *pctrl, const FAdcConfig *input_config_p) +{ + FASSERT(pctrl && input_config_p); + + FError ret = FADC_SUCCESS; + /* + * If the device is started, disallow the initialize and return a Status + * indicating it is started. This allows the user to de-initialize the device + * and reinitialize, but prevents a user from inadvertently + * initializing. + */ + if (FT_COMPONENT_IS_READY == pctrl->is_ready) + { + FADC_WARN("device is already initialized!!!"); + } + + /*Set default values and configuration data */ + FAdcDeInitialize(pctrl); + + pctrl->config = *input_config_p; + + pctrl->is_ready = FT_COMPONENT_IS_READY; + + return ret; +} diff --git a/bsp/phytium/libraries/standalone/drivers/adc/fadc/fadc.h b/bsp/phytium/libraries/standalone/drivers/adc/fadc/fadc.h new file mode 100644 index 0000000000..a7c96131f9 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/adc/fadc/fadc.h @@ -0,0 +1,179 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fadc.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:29:10 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef FT_ADC_H +#define FT_ADC_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ftypes.h" +#include "fdebug.h" +#include "ferror_code.h" +#include "fkernel.h" +#include "fparameters.h" + + +#define FADC_SUCCESS FT_SUCCESS +#define FADC_ERR_INVAL_PARM FT_MAKE_ERRCODE(ErrModBsp, ErrBspAdc, 1) +#define FADC_ERR_NOT_READY FT_MAKE_ERRCODE(ErrModBsp, ErrBspAdc, 2) +#define FADC_ERR_TIMEOUT FT_MAKE_ERRCODE(ErrModBsp, ErrBspAdc, 3) +#define FADC_ERR_CMD_FAILED FT_MAKE_ERRCODE(ErrModBsp, ErrBspAdc, 4) +#define FADC_ERR_NOT_SUPPORT FT_MAKE_ERRCODE(ErrModBsp, ErrBspAdc, 5) + +#define FADC_CTRL_PD_DISABLE 0 +#define FADC_CTRL_PD_ENABLE 1 + +#define FADC_CONVERT_COMPLETE(x) (x=TRUE) +#define FADC_CONVERT_UNCOMPLETE(x) (x=FALSE) +#define FADC_READ_TIMEOUT (600) +#define FADC_READ_DELAY (10) + +/* adc interrupt event type */ +typedef enum +{ + FADC_INTR_EVENT_COVFIN = 0, /**< Handler type for convert finish interrupt */ + FADC_INTR_EVENT_DLIMIT = 1, /**< Handler type for low limit interrupt*/ + FADC_INTR_EVENT_ULIMIT = 2, /**< Handler type for high limit interrupt*/ + FADC_INTR_EVENT_ERROR = 3, /**< Handler type for error interrupt*/ + + FADC_INTR_EVENT_NUM +} FAdcIntrEventType; + +/* adc convert mode */ +typedef enum +{ + FADC_CONTINUOUS_CONVERT = 0,/* continuous conversion*/ + FADC_SINGLE_CONVERT = 1, /* single conversion*/ + + FADC_CONVERT_MODE_NUM + +} FAdcConvertMode; + +/* adc channel mode */ +typedef enum +{ + FADC_MULTI_CHANNEL = 0, /* multi channel conversion*/ + FADC_FIXED_CHANNEL = 1, /* fixed channel conversion*/ + + FADC_CHANNEL_MODE_NUM +} FAdcChannelMode; + +/* adc base configuration */ +typedef struct +{ + u32 instance_id;/* adc id */ + uintptr base_addr;/* adc control register base address*/ + u32 irq_num;/* adc interrupt number */ + u32 irq_prority;/* adc interrupt priority*/ + const char *instance_name;/* instance name */ + +} FAdcConfig; + +typedef struct +{ + u32 convert_interval; /* convert interval time */ + u32 clk_div; /* clock divider, must be even*/ + FAdcConvertMode convert_mode;/*!< convert mode */ + FAdcChannelMode channel_mode;/*!< channel mode */ + +} FAdcConvertConfig; + +/* adc variable config */ +typedef struct +{ + u16 high_threshold; /*!< Configures the ADC analog high threshold value. + This parameter must be a 10-bit value. */ + u16 low_threshold; /*!< Configures the ADC analog low threshold value. + This parameter must be a 10-bit value. */ +} FAdcThresholdConfig; + + +typedef void (*FAdcIntrEventHandler)(void *param); + +typedef struct +{ + FAdcConfig config;/* adc config */ + u32 is_ready;/* adc init ready flag */ + u16 value[FADC_CHANNEL_NUM]; /* adc value */ + boolean convert_complete[FADC_CHANNEL_NUM]; /*!< Specifies whether the conversion is complete> */ + FAdcIntrEventHandler event_handler[FADC_INTR_EVENT_NUM]; /* event handler for interrupt */ + void *event_param[FADC_INTR_EVENT_NUM]; /* parameters of event handler */ + +} FAdcCtrl; + +/* get default configuration of specific adc id */ +const FAdcConfig *FAdcLookupConfig(FAdcInstance instance_id); + +/* DeInitialization function for the device instance */ +void FAdcDeInitialize(FAdcCtrl *pctrl); + +/* Initializes a specific instance such that it is ready to be used */ +FError FAdcCfgInitialize(FAdcCtrl *pctrl, const FAdcConfig *input_config_p); + +/* config adc convert parameters */ +FError FAdcConvertSet(FAdcCtrl *pctrl, FAdcConvertConfig *convert_config); + +/* Set adc channel high_threshold and low_threshold */ +FError FAdcChannelThresholdSet(FAdcCtrl *pctrl, FAdcChannel channel, FAdcThresholdConfig *threshold_config); + +/* init adc variable configuration */ +FError FAdcVariableConfig(FAdcCtrl *pctrl, FAdcConvertConfig *convert_config); + +/* enable channel interrupt */ +FError FAdcInterruptEnable(FAdcCtrl *pctrl, FAdcChannel channel, FAdcIntrEventType event_type); + +/* disable channel interrupt */ +FError FAdcInterruptDisable(FAdcCtrl *pctrl, FAdcChannel channel, FAdcIntrEventType event_type); + +void FAdcChannelEnable(FAdcCtrl *pctrl, FAdcChannel channel, boolean state); + +/* Start adc convert */ +void FAdcConvertStart(FAdcCtrl *pctrl); + +/* Stop adc convert */ +void FAdcConvertStop(FAdcCtrl *pctrl); + +/* read adc channel convert result value */ +FError FAdcReadConvertResult(FAdcCtrl *pctrl, FAdcChannel channel, u16 *val); + +/* read adc channel convert finish count */ +FError FAdcReadFinishCnt(FAdcCtrl *pctrl, FAdcChannel channel, u32 *count); + +/* read adc channel history limit value, include high limit and low limit */ +FError FAdcReadHisLimit(FAdcCtrl *pctrl, FAdcChannel channel, u16 *u_his_limit, u16 *d_his_limit); + +/* interrupt handler for the driver */ +void FAdcIntrHandler(s32 vector, void *args); + +/* register FAdc interrupt handler function */ +void FAdcRegisterInterruptHandler(FAdcCtrl *instance_p, FAdcIntrEventType event_type, + FAdcIntrEventHandler handler, void *param); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/adc/fadc/fadc_g.c b/bsp/phytium/libraries/standalone/drivers/adc/fadc/fadc_g.c new file mode 100644 index 0000000000..aae6f24981 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/adc/fadc/fadc_g.c @@ -0,0 +1,45 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fadc_g.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:28:45 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ +#include "fparameters.h" +#include "fadc.h" + +/* default configs of wdt ctrl */ +const FAdcConfig FAdcConfigTbl[FADC_INSTANCE_NUM] = +{ + { + .instance_id = FADC_INSTANCE_0, + .base_addr = FADC0_CONTROL_BASE, + .irq_num = FADC0_INTR_IRQ, + .irq_prority = 0, + .instance_name = "ADC-0" + + }, + + { + .instance_id = FADC_INSTANCE_1, + .base_addr = FADC1_CONTROL_BASE, + .irq_num = FADC1_INTR_IRQ, + .irq_prority = 0, + .instance_name = "ADC-1" + } +}; \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/adc/fadc/fadc_hw.c b/bsp/phytium/libraries/standalone/drivers/adc/fadc/fadc_hw.c new file mode 100644 index 0000000000..c1e5cc63f7 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/adc/fadc/fadc_hw.c @@ -0,0 +1,48 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fadc_hw.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-25 11:45:05 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "fparameters.h" +#include "fadc_hw.h" +#include "stdio.h" + +/** + * @name: FAdcDump + * @msg: debug register value for adc channel. + * @param {uintptr} base_addr, base address of FADC controller + * @param {u8} channel, adc channel number + * @return {*} + */ +void FAdcDump(uintptr base_addr, u8 channel) +{ + printf("Off[0x%02x]: = 0x%08x\r\n", FADC_CTRL_REG_OFFSET, FADC_READ_REG32(base_addr, FADC_CTRL_REG_OFFSET)); + printf("Off[0x%02x]: = 0x%08x\r\n", FADC_INTER_REG_OFFSET, FADC_READ_REG32(base_addr, FADC_INTER_REG_OFFSET)); + printf("Off[0x%02x]: = 0x%08x\r\n", FADC_STATE_REG_OFFSET, FADC_READ_REG32(base_addr, FADC_STATE_REG_OFFSET)); + printf("Off[0x%02x]: = 0x%08x\r\n", FADC_INTRMASK_REG_OFFSET, FADC_READ_REG32(base_addr, FADC_INTRMASK_REG_OFFSET)); + printf("Off[0x%02x]: = 0x%08x\r\n", FADC_INTR_REG_OFFSET, FADC_READ_REG32(base_addr, FADC_INTR_REG_OFFSET)); + printf("Off[0x%02x]: = 0x%08x\r\n", FADC_COV_RESULT_REG_OFFSET(channel), FADC_READ_REG32(base_addr, FADC_COV_RESULT_REG_OFFSET(channel))); + printf("Off[0x%02x]: = 0x%08x\r\n", FADC_FINISH_CNT_REG_OFFSET(channel), FADC_READ_REG32(base_addr, FADC_FINISH_CNT_REG_OFFSET(channel))); + printf("Off[0x%02x]: = 0x%08x\r\n", FADC_HIS_LIMIT_REG_OFFSET(channel), FADC_READ_REG32(base_addr, FADC_HIS_LIMIT_REG_OFFSET(channel))); + + printf("\r\n"); + +} diff --git a/bsp/phytium/libraries/standalone/drivers/adc/fadc/fadc_hw.h b/bsp/phytium/libraries/standalone/drivers/adc/fadc/fadc_hw.h new file mode 100644 index 0000000000..e9abff56b2 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/adc/fadc/fadc_hw.h @@ -0,0 +1,121 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fadc_hw.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:28:45 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ +#ifndef BSP_DRIVERS_ADC_HW_H +#define BSP_DRIVERS_ADC_HW_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "fkernel.h" +#include "ftypes.h" +#include "fio.h" + +/* Generic ADC register definitions */ + +/* FADC register */ +#define FADC_CTRL_REG_OFFSET 0x00 +#define FADC_INTER_REG_OFFSET 0x04 +#define FADC_STATE_REG_OFFSET 0x08 +#define FADC_ERRCLR_REG_OFFSET 0x0c +#define FADC_LEVEL_REG_OFFSET(x) (0x10+(x)*4) +#define FADC_INTRMASK_REG_OFFSET 0x30 +#define FADC_INTR_REG_OFFSET 0x34 +#define FADC_COV_RESULT_REG_OFFSET(x) (0x38+(x)*4) +#define FADC_FINISH_CNT_REG_OFFSET(x) (0x58+(x)*4) +#define FADC_HIS_LIMIT_REG_OFFSET(x) (0x78+(x)*4) + +#define FADC_CTRL_REG_PD_EN BIT(31) +#define FADC_CTRL_REG_FIX_CHANNEL_NUM_MASK GENMASK(18, 16) +#define FADC_CTRL_REG_FIX_CHANNEL_NUM(x) ((x)<<16) +#define FADC_CTRL_REG_CLK_DIV(x) ((x)<<12) +#define FADC_CTRL_REG_CLK_DIV_MASK GENMASK(15, 12) +#define FADC_CTRL_REG_CHANNEL_EN(x) BIT((x)+4) +#define FADC_CTRL_REG_FIX_CHANNEL BIT(3) +#define FADC_CTRL_REG_SINGLE_CONVERT_EN BIT(2) +#define FADC_CTRL_REG_SINGLE_CONVERT BIT(1) +#define FADC_CTRL_REG_SOC_EN BIT(0) + +#define FADC_STATE_REG_B_STA(x) ((x)<<8) +#define FADC_STATE_REG_EOC_STA BIT(7) +#define FADC_STATE_REG_S_STA(x) ((x)<<4) +#define FADC_STATE_REG_SOC_STA BIT(3) +#define FADC_STATE_REG_ERR_STA BIT(2) +#define FADC_STATE_REG_COV_FINISH_STA BIT(1) +#define FADC_STATE_REG_CTL_BUSY_STA BIT(0) + +#define FADC_LEVEL_REG_HIGH_LEVEL(x) ((x)<<16) +#define FADC_LEVEL_REG_LOW_LEVEL(x) ((x)<<0) + +#define FADC_INTRMASK_REG_ERR_MASK BIT(24) +#define FADC_INTRMASK_REG_ULIMIT_MASK(x) BIT((x)*2+9) +#define FADC_INTRMASK_REG_DLIMIT_MASK(x) BIT((x)*2+8) +#define FADC_INTRMASK_REG_COVFIN_MASK(x) BIT((x)) + +#define FADC_INTR_REG_ERR BIT(24) +#define FADC_INTR_REG_ULIMIT(x) BIT((x)*2+9) +#define FADC_INTR_REG_DLIMIT(x) BIT((x)*2+8) +#define FADC_INTR_REG_COVFIN(x) BIT((x)) +#define FADC_INTR_REG_COVFIN_MASK GENMASK(7, 0) +#define FADC_INTR_REG_LIMIT_MASK GENMASK(23, 8) + +/* convert result range */ +#define FADC_COV_RESULT_REG_MASK GENMASK(9, 0) + +#define FADC_HIS_LIMIT_REG_UMASK GENMASK(25, 16) +#define FADC_HIS_LIMIT_REG_DMASK GENMASK(9, 0) + +/***************** Macros (Inline Functions) Definitions *********************/ + +/** + * @name: FADC_READ_REG32 + * @msg: 读取FADC寄存器 + * @param {u32} addr 定时器的基地址 + * @param {u32} reg_offset 定时器的寄存器的偏移 + * @return {u32} 寄存器参数 + */ +#define FADC_READ_REG32(addr, reg_offset) FtIn32(addr + (u32)(reg_offset)) + +/** + * @name: FADC_WRITE_REG32 + * @msg: 写入FADC寄存器 + * @param {u32} addr 定时器的基地址 + * @param {u32} reg_offset 定时器的寄存器的偏移 + * @param {u32} reg_value 写入寄存器参数 + * @return {void} + */ +#define FADC_WRITE_REG32(addr, reg_offset, reg_value) FtOut32((addr) + (u32)(reg_offset), (u32)(reg_value)) + +#define FADC_SETBIT(base_addr, reg_offset, data) FtSetBit32((base_addr) + (u32)(reg_offset), (u32)(data)) + +#define FADC_CLEARBIT(base_addr, reg_offset, data) FtClearBit32((base_addr) + (u32)(reg_offset), (u32)(data)) + +/* debug register value for adc channel */ +void FAdcDump(uintptr base_addr, u8 channel); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/adc/fadc/fadc_intr.c b/bsp/phytium/libraries/standalone/drivers/adc/fadc/fadc_intr.c new file mode 100644 index 0000000000..6a6ac7ba60 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/adc/fadc/fadc_intr.c @@ -0,0 +1,133 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fadc_intr.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:28:45 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ +#include "fparameters.h" +#include "fassert.h" +#include "finterrupt.h" +#include "fadc.h" +#include "fadc_hw.h" + +#define FADC_DEBUG_TAG "FT_ADC_INTR" +#define FADC_DEBUG(format, ...) FT_DEBUG_PRINT_D(FADC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FADC_INFO(format, ...) FT_DEBUG_PRINT_I(FADC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FADC_WARN(format, ...) FT_DEBUG_PRINT_W(FADC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FADC_ERROR(format, ...) FT_DEBUG_PRINT_E(FADC_DEBUG_TAG, format, ##__VA_ARGS__) + +#define FADC_CALL_INTR_EVENT_HANDLDER(instance_p, event) \ + if (instance_p->event_handler[event]) \ + instance_p->event_handler[event](instance_p->event_param[event]) + +/** + * @name: FAdcRegisterInterruptHandler + * @msg: register FAdc interrupt handler function + * @param {FAdc} *instance_p, pointer to the adc instance + * @param {FAdcIntrEvtType} event_type, interrupt event type + * @param {FAdcEvtHandler} handler, interrupt event handler + * @param {void} *param, contains a pointer to the driver instance + * @return {*} + */ +void FAdcRegisterInterruptHandler(FAdcCtrl *instance_p, FAdcIntrEventType event_type, + FAdcIntrEventHandler handler, void *param) +{ + FASSERT(instance_p); + FASSERT(event_type < FADC_INTR_EVENT_NUM); + instance_p->event_handler[event_type] = handler; + instance_p->event_param[event_type] = param; +} + +/** + * @name: FAdcIntrHandler + * @msg: This function is the interrupt handler for the driver. + * It must be connected to an interrupt system by the application such that it + * can be called when an interrupt occurs. + * @param vector Irq num ,Don't need attention . + * @param args contains a pointer to the driver instance + */ +void FAdcIntrHandler(s32 vector, void *args) +{ + FASSERT(args != NULL); + FAdcCtrl *pctrl = (FAdcCtrl *)args; + u32 status = 0; + u32 intrmask = 0; + u32 cfg = 0; + u32 channel = 0; + uintptr base_addr = pctrl->config.base_addr; + + status = FADC_READ_REG32(base_addr, FADC_INTR_REG_OFFSET); + /* channel convert complete irq mask */ + intrmask = FADC_READ_REG32(base_addr, FADC_INTRMASK_REG_OFFSET); + + /* adc error interrupt */ + if (status & FADC_INTR_REG_ERR) + { + /* clear error interrupt status */ + FADC_SETBIT(base_addr, FADC_INTR_REG_OFFSET, FADC_INTR_REG_ERR); + + /* write error clear register, adc_errclr_reg=0 */ + FADC_WRITE_REG32(base_addr, FADC_ERRCLR_REG_OFFSET, 0); + + FADC_CALL_INTR_EVENT_HANDLDER(pctrl, FADC_INTR_EVENT_ERROR); + } + + if (status & FADC_INTR_REG_LIMIT_MASK) + { + for (channel = 0; channel < FADC_CHANNEL_NUM; channel++) + { + if (status & FADC_INTR_REG_DLIMIT(channel)) + { + /* clear dlimit interrupt status */ + FADC_SETBIT(base_addr, FADC_INTR_REG_OFFSET, FADC_INTR_REG_DLIMIT(channel)); + FADC_CALL_INTR_EVENT_HANDLDER(pctrl, FADC_INTR_EVENT_DLIMIT); + } + if (status & FADC_INTR_REG_ULIMIT(channel)) + { + /* clear ulimit interrupt status */ + FADC_SETBIT(base_addr, FADC_INTR_REG_OFFSET, FADC_INTR_REG_ULIMIT(channel)); + FADC_CALL_INTR_EVENT_HANDLDER(pctrl, FADC_INTR_EVENT_ULIMIT); + } + } + } + + /* 有中断转换完成的情况下,根据adc_intr_reg寄存器的通道转换完成中断标志位bit0~7,读取转换结果 */ + if (status & FADC_INTR_REG_COVFIN_MASK) + { + for (channel = 0; channel < FADC_CHANNEL_NUM; channel++) + { + if (status & FADC_INTR_REG_COVFIN(channel)) + { + pctrl->value[channel] = FADC_READ_REG32(base_addr, FADC_COV_RESULT_REG_OFFSET(channel)) & FADC_COV_RESULT_REG_MASK; + FADC_CONVERT_COMPLETE(pctrl->convert_complete[channel]); + /* clear convert finish interrupt status */ + FADC_SETBIT(base_addr, FADC_INTR_REG_OFFSET, FADC_INTR_REG_COVFIN(channel)); + } + } + + FADC_CALL_INTR_EVENT_HANDLDER(pctrl, FADC_INTR_EVENT_COVFIN); + } + else + { + + } + + return; +} + diff --git a/bsp/phytium/libraries/standalone/drivers/adc/fadc/fadc_sinit.c b/bsp/phytium/libraries/standalone/drivers/adc/fadc/fadc_sinit.c new file mode 100644 index 0000000000..b6f415db2a --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/adc/fadc/fadc_sinit.c @@ -0,0 +1,67 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fadc_sinit.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-25 11:45:05 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +/***************************** Include Files *********************************/ + + +#include "fparameters.h" +#include "fadc.h" +#include "fassert.h" + +extern FAdcConfig FAdcConfigTbl[FADC_INSTANCE_NUM]; + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/*****************************************************************************/ + +/** + * @name: FAdcLookupConfig + * @msg: get default configuration of specific adc id. + * @param {FAdcInstance} instance_id, instance id of FADC controller + * @return {FAdcConfig*} Default configuration parameters of FADC + */ +const FAdcConfig *FAdcLookupConfig(FAdcInstance instance_id) +{ + const FAdcConfig *pconfig = NULL; + FASSERT(instance_id < FADC_INSTANCE_NUM); + + u32 index = 0; + + for (index = 0; index < (u32)FADC_INSTANCE_NUM; index++) + { + if (FAdcConfigTbl[index].instance_id == instance_id) + { + pconfig = &FAdcConfigTbl[index]; + break; + } + } + + return (FAdcConfig *)pconfig; +} diff --git a/bsp/phytium/libraries/standalone/drivers/can/Kconfig b/bsp/phytium/libraries/standalone/drivers/can/Kconfig new file mode 100644 index 0000000000..a6b59e8753 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/can/Kconfig @@ -0,0 +1,18 @@ + +menu "CAN Configuration" + config USE_FCAN + bool + prompt "Use FCAN" + default n + if USE_FCAN + config FCAN_USE_CANFD + depends on TARGET_E2000 + bool + prompt "Use CanFD" + default n + help + use canfd protocol + endif + +endmenu + diff --git a/bsp/phytium/libraries/standalone/drivers/can/fcan/fcan.c b/bsp/phytium/libraries/standalone/drivers/can/fcan/fcan.c new file mode 100644 index 0000000000..9de3f128f4 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/can/fcan/fcan.c @@ -0,0 +1,1060 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fcan.c + * Date: 2021-04-29 10:21:53 + * LastEditTime: 2022-02-18 08:29:20 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "string.h" +#include +#include +#include "fkernel.h" +#include "fcan.h" +#include "fcan_hw.h" +#include "fassert.h" +#include "fdebug.h" +#include "fswap.h" +#include "fparameters.h" +#include "fsleep.h" + + +#define FT_CAN_DEBUG_TAG "FT_CAN" +#define FCAN_DEBUG(format, ...) FT_DEBUG_PRINT_D(FT_CAN_DEBUG_TAG, format, ##__VA_ARGS__) +#define FCAN_INFO(format, ...) FT_DEBUG_PRINT_I(FT_CAN_DEBUG_TAG, format, ##__VA_ARGS__) +#define FCAN_WARN(format, ...) FT_DEBUG_PRINT_W(FT_CAN_DEBUG_TAG, format, ##__VA_ARGS__) +#define FCAN_ERROR(format, ...) FT_DEBUG_PRINT_E(FT_CAN_DEBUG_TAG, format, ##__VA_ARGS__) + +typedef struct +{ + 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; +} FCanBittimingConst; + +/* 仲裁段速率默认值 */ +static const FCanBittimingConst FCanArbBitConst = +{ + .tseg1_min = FCAN_ARB_TSEG1_MIN, /* Time segement 1 = prop_seg + phase_seg1 */ + .tseg1_max = FCAN_ARB_TSEG1_MAX, + .tseg2_min = FCAN_ARB_TSEG2_MIN, /* Time segement 2 = phase_seg2 */ + .tseg2_max = FCAN_ARB_TSEG2_MAX, + .sjw_max = FCAN_ARB_SJW_MAX, /* Synchronisation jump width */ + .brp_min = FCAN_ARB_BRP_MIN, /* Bit-rate prescaler */ + .brp_max = FCAN_ARB_BRP_MAX, + .brp_inc = FCAN_ARB_BRP_INC, +}; + +/* 数据段速率默认值 */ +static const FCanBittimingConst FCanDataBitConst = +{ + .tseg1_min = FCAN_DATA_TSEG1_MIN, /* Time segement 1 = prop_seg + phase_seg1 */ + .tseg1_max = FCAN_DATA_TSEG1_MAX, + .tseg2_min = FCAN_DATA_TSEG2_MIN, /* Time segement 2 = phase_seg2 */ + .tseg2_max = FCAN_DATA_TSEG2_MAX, + .sjw_max = FCAN_DATA_SJW_MAX, /* Synchronisation jump width */ + .brp_min = FCAN_DATA_BRP_MIN, /* Bit-rate prescaler */ + .brp_max = FCAN_DATA_BRP_MAX, + .brp_inc = FCAN_DATA_BRP_INC, +}; + +/* calculate the can sample point */ +static s32 FCanUpdateSamplePoint(const FCanBittimingConst *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 = UINT_MAX; + 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; +} + +/** + * @name: FCanCalcBittiming + * @msg: This routine calculate Bit timing + * @param {structFCanBittiming} *bt_p is is a structure that contains the CAN baud rate configuration parameter , The user needs to fill in the baudrate first + * @param {u32} target_baudrate, parameters of target baudrate + * @param {u32} target_sample_point, parameters of target sample point, 0 means the general configuration is used + * @param {FCanSegmentType} target_segment, specifies which target is to be selected. followed by FCAN_ARB_SEGMENT or FCAN_DATA_SEGMENT + * @return err code information, FCAN_SUCCESS indicates success,others indicates failed + */ +static FError FCanCalcBittiming(FCanBaudrateConfig *bt_p, u32 target_baudrate, u32 target_sample_point, FCanSegmentType target_segment) +{ + u32 baudrate; /* current baudrate */ + u32 baudrate_error; /* difference between current and nominal value */ + u32 best_baudrate_error = UINT_MAX; + u32 sample_point_error; /* difference between current and nominal value */ + u32 best_sample_point_error = UINT_MAX; + 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; + + u32 reg_val; + + const FCanBittimingConst *btc; + FCanBaudrateConfig *bt = bt_p; + FASSERT(bt_p != NULL); + FASSERT(target_segment < FCAN_SEGMENT_TYPE_NUM); + + if (target_segment == FCAN_DATA_SEGMENT) + { + btc = &FCanDataBitConst; + } + else + { + btc = &FCanArbBitConst; + } + + if (target_sample_point) + { + sample_point_nominal = target_sample_point; + } + else + { + if (target_baudrate > 1000000) + sample_point_nominal = 650; + else if (target_baudrate > 800000) + sample_point_nominal = 750; + else if (target_baudrate > 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 = FCAN_REF_CLOCK / (tsegall * target_baudrate) + 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; + + baudrate = FCAN_REF_CLOCK / (brp * tsegall); + baudrate_error = abs(target_baudrate - baudrate); + + /* tseg brp biterror */ + if (baudrate_error > best_baudrate_error) + continue; + + /* reset sample point error if we have a better baudrate */ + if (baudrate_error < best_baudrate_error) + best_sample_point_error = UINT_MAX; + + FCanUpdateSamplePoint(btc, sample_point_nominal, tseg / 2, &tseg1, &tseg2, &sample_point_error); + + FCAN_DEBUG("target_segment=%d, brp=%d, tseg=%d, tseg1=%d, tseg2=%d, sample_point_nominal=%d", + target_segment, brp, tseg, tseg1, tseg2, sample_point_nominal); + + u32 prop_seg = tseg1 / 2; + u32 phase_seg1 = tseg1 - prop_seg; + u32 phase_seg2 = tseg2; + u32 sjw = 1; + + /* Setting Baud Rate prescalar value in BRPR Register */ + reg_val = (brp - 1) << 16; + reg_val |= (prop_seg - 1) << 2; + reg_val |= (phase_seg1 - 1) << 5; + reg_val |= (phase_seg2 - 1) << 8; + reg_val |= (sjw - 1); + FCAN_DEBUG("reg_val=%#x\n", reg_val); + + if (sample_point_error > best_sample_point_error) + continue; + + best_sample_point_error = sample_point_error; + best_baudrate_error = baudrate_error; + best_tseg = tseg / 2; + best_brp = brp; + + if (baudrate_error == 0 && sample_point_error == 0) + break; + } + + if (best_baudrate_error) + { + /* Error in one-tenth of a percent */ + v64 = (u64)best_baudrate_error * 1000; + do_div(v64, target_baudrate); + baudrate_error = (u32)v64; + if (baudrate_error > CAN_CALC_MAX_ERROR) + { + FCAN_ERROR("baudrate error"); + return FCAN_FAILURE; + } + } + + /* real sample point */ + FCanUpdateSamplePoint(btc, sample_point_nominal, best_tseg, + &tseg1, &tseg2, NULL); + FCAN_DEBUG("tseg1=%d, tseg2=%d, sample_point_nominal=%d", tseg1, tseg2, sample_point_nominal); + + 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 baudrate */ + if (target_baudrate != FCAN_REF_CLOCK / (bt->brp * (CAN_CALC_SYNC_SEG + tseg1 + tseg2))) + { + FCAN_ERROR("target baudrate calculate timing failed"); + return FCAN_FAILURE; + } + + FCAN_DEBUG("bt->prop_seg=%d, bt->phase_seg1=%d, bt->phase_seg2=%d, bt->sjw=%d, bt->brp=%d", + bt->prop_seg, bt->phase_seg1, bt->phase_seg2, bt->sjw, bt->brp); + + return FCAN_SUCCESS; +} + +static u32 FCanGetDlcLen(u32 dlc) +{ + u32 dlc_len = 0; + if (dlc == 0) + { + dlc_len = 8; + } + + switch (dlc) + { + case 1: + dlc_len = 1; + break; + case 2: + dlc_len = 2; + break; + case 3: + dlc_len = 3; + break; + case 4: + dlc_len = 4; + break; + case 5: + dlc_len = 5; + break; + case 6: + dlc_len = 6; + break; + case 7: + dlc_len = 7; + break; + case 8: + dlc_len = 8; + break; + case 9: + dlc_len = 12; + break; + case 10: + dlc_len = 16; + break; + case 11: + dlc_len = 20; + break; + case 12: + dlc_len = 24; + break; + case 13: + dlc_len = 32; + break; + case 14: + dlc_len = 48; + break; + case 15: + dlc_len = 64; + break; + default : + dlc_len = 0; + break; + } + + return dlc_len; + +} + +static u32 FCanSetDlcLen(u32 len) +{ + if (len <= 8) + { + return len; + } + else if (len <= 12) + { + return 9; + } + else if (len <= 16) + { + return 10; + } + else if (len <= 20) + { + return 11; + } + else if (len <= 24) + { + return 12; + } + else if (len <= 32) + { + return 13; + } + else if (len <= 48) + { + return 14; + } + else if (len <= 64) + { + return 15; + } + else + { + return 0; + } +} + +/** + * @name: FCanReset + * @msg: reset a specific can instance + * @param {FCanCtrl} *pctrl, instance of FCan controller + * @return {*} + */ +void FCanReset(FCanCtrl *instance_p) +{ + u32 reg_value; + FCanConfig *config_p; + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + config_p = &instance_p->config; + + FCAN_WRITE_REG32(config_p->base_address, FCAN_CTRL_OFFSET, 0); + reg_value = FCAN_READ_REG32(config_p->base_address, FCAN_CTRL_OFFSET); + if (reg_value & FCAN_CTRL_XFER_MASK) + { + FCAN_ERROR("FT can is not in configration mode\n"); + return; + } + + /* reset can */ + FCAN_WRITE_REG32(config_p->base_address, FCAN_CTRL_OFFSET, FCAN_CTRL_RST_MASK); +} + +/** + * @name: FCanDeInitialize + * @msg: Deinitializes a specific can instance + * @param {FCanCtrl} *pctrl, instance of FCan controller + * @return {*} + */ +void FCanDeInitialize(FCanCtrl *instance_p) +{ + FASSERT(instance_p); + instance_p->is_ready = 0; + memset(instance_p, 0, sizeof(*instance_p)); + return; +} + +/** + * @name: FCanCfgInitialize + * @msg: Initializes a specific instance such that it is ready to be used. + * @param {FCanCtrl} *instance_p, instance of FCanCtrl controller + * @param {FCanConfig} *input_config_p, configuration parameters of FCanCtrl + * @return err code information, FCAN_SUCCESS indicates success,others indicates failed + */ +FError FCanCfgInitialize(FCanCtrl *instance_p, const FCanConfig *input_config_p) +{ + FASSERT(instance_p != NULL); + FASSERT(input_config_p != NULL); + + FError ret = FCAN_SUCCESS; + /* + * If the device is started, disallow the initialize and return a Status + * indicating it is started. This allows the user to de-initialize the device + * and reinitialize, but prevents a user from inadvertently + * initializing. + */ + if (FT_COMPONENT_IS_READY == instance_p->is_ready) + { + FCAN_WARN("device is already initialized!!!"); + } + + /*Set default values and configuration data */ + FCanDeInitialize(instance_p); + + instance_p->config = *input_config_p; + + instance_p->is_ready = FT_COMPONENT_IS_READY; + + FCanReset(instance_p); + + return ret; +} + +/** + * @name: FCanStatusGet + * @msg: read can status, include transfer status, error and fifo count. + * @param {FCanCtrl} *instance_p, pointer to a FCanCtrl structure that contains + * the configuration information for the specified can module. + * @param {FCanStatus} *status_p, pointer to can status, include send and receive, error and fifo count . + * @return err code information, FCAN_SUCCESS indicates success,others indicates failed. + */ +FError FCanStatusGet(FCanCtrl *instance_p, FCanStatus *status_p) +{ + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + FASSERT(status_p != NULL); + uintptr base_address = instance_p->config.base_address; + + u32 reg_val = 0; + reg_val = FCAN_READ_REG32(base_address, FCAN_XFER_STS_OFFSET); + + status_p->xfer_status.xfers = FCAN_XFER_STS_XFERS_GET(reg_val); + status_p->xfer_status.rs = FCAN_XFER_STS_RS_GET(reg_val); + status_p->xfer_status.ts = FCAN_XFER_STS_TS_GET(reg_val); + status_p->xfer_status.fies = FCAN_XFER_STS_FIES_GET(reg_val); + status_p->xfer_status.fras = FCAN_XFER_STS_FRAS_GET(reg_val); + + reg_val = FCAN_READ_REG32(base_address, FCAN_ERR_CNT_OFFSET); + status_p->rx_err_cnt = FCAN_ERR_CNT_RFN_GET(reg_val); + status_p->tx_err_cnt = FCAN_ERR_CNT_TFN_GET(reg_val); + + reg_val = FCAN_READ_REG32(base_address, FCAN_FIFO_CNT_OFFSET); + status_p->tx_fifo_cnt = FCAN_FIFO_CNT_TFN_GET(reg_val); + status_p->rx_fifo_cnt = FCAN_FIFO_CNT_RFN_GET(reg_val); + + return FCAN_SUCCESS; +} + +/** + * @name: FCanRecv + * @msg: receive can message by specific can instance. + * @param {FCanCtrl} *instance_p, pointer to a FCanCtrl structure that contains + * the configuration information for the specific can module. + * @param {FCanFrame} *frame_p, can message receive struct. + * @return err code information, FCAN_SUCCESS indicates success,others indicates failed. + */ +FError FCanRecv(FCanCtrl *instance_p, FCanFrame *frame_p) +{ + u32 canid; + u32 dlc; + int i = 0, j = 0; + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + FASSERT(frame_p != NULL); + uintptr base_address = instance_p->config.base_address; + + memset(frame_p, 0, sizeof(FCanFrame)); + + /* Read a frame from Phytium CAN */ + canid = FCAN_READ_REG32(base_address, FCAN_RX_FIFO_OFFSET); + /* if canid is big-endian ,use swap change to little-endian */ + canid = be32_to_cpu(canid); + + FCAN_DEBUG("FCanRecv canid = %#x\n", canid); + + /* identifier extension */ + if (canid & FCAN_IDR_IDE_MASK) + { + dlc = FCAN_READ_REG32(base_address, FCAN_RX_FIFO_OFFSET); + dlc = be32_to_cpu(dlc); + FCAN_DEBUG("FCanRecv dlc = %#x\n", dlc); + + if (dlc & FTCANFD_ID2_FDL_MASK) + { + if (dlc & FTCANFD_ID2_BRS_MASK) + { + frame_p->flags |= CANFD_BRS; + } + + if (dlc & FTCANFD_ID2_ESI_MASK) + { + frame_p->flags |= CANFD_ESI; + } + dlc = FTCANFD_ID2_EDLC_GET(dlc); + } + else + { + dlc = FCAN_IDR_EDLC_GET(dlc); + } + + frame_p->canid = FCAN_IDR_ID1_GET(canid) << FCAN_ACC_IDN_SHIFT; + frame_p->canid |= FCAN_IDR_ID2_GET(canid); + frame_p->canid |= CAN_EFF_FLAG; + + if (canid & FCAN_IDR_RTR_MASK) + { + frame_p->canid |= CAN_RTR_FLAG; + } + } + else + { + if (canid & FTCANFD_ID1_FDL_MASK) + { + if (canid & FTCANFD_ID1_BRS_MASK) + { + frame_p->flags |= CANFD_BRS; + } + + if (canid & FTCANFD_ID1_ESI_MASK) + { + frame_p->flags |= CANFD_ESI; + } + dlc = FTCANFD_ID1_SDLC_GET(canid); + } + else + { + dlc = FCAN_IDR_DLC_GET(canid); + } + + /* The received frame is a standard format frame */ + frame_p->canid = FCAN_IDR_ID1_GET(canid); + if (canid & FCAN_IDR_SRR_MASK) + { + frame_p->canid |= CAN_RTR_FLAG; + } + } + + frame_p->candlc = FCanGetDlcLen(dlc); + FCAN_DEBUG("FCanRecv frame_p->candlc = %d\n", frame_p->candlc); + + if (!(frame_p->canid & CAN_RTR_FLAG)) + { + j = 0; + for (i = frame_p->candlc; i > 0; i -= 4) + { + *(u32 *)(frame_p->data + j) = FCAN_READ_REG32(base_address, FCAN_RX_FIFO_OFFSET); + j += 4; + } + + if (i > 0) + { + *(u32 *)(frame_p->data + j) = FCAN_READ_REG32(base_address, FCAN_RX_FIFO_OFFSET); + } + } + + return FCAN_SUCCESS; +} + +/** + * @name: FCanSend + * @msg: send can message by specific can instance. + * @param {FCanCtrl} *instance_p, pointer to a FCanCtrl structure that contains + * the configuration information for the specific can module. + * @param {FCanFrame} *frame_p, can message send struct. + * @return err code information, FCAN_SUCCESS indicates success,others indicates failed. + */ +FError FCanSend(FCanCtrl *instance_p, FCanFrame *frame_p) +{ + u32 id, dlc; + + int i = 0, j = 0; + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + uintptr base_address = instance_p->config.base_address; + + while (FCAN_TX_FIFO_FULL(instance_p)); + + u32 can_send_dlc = FCanSetDlcLen(frame_p->candlc); + + if (frame_p->canid & CAN_EFF_FLAG) + { + /* Extended CAN id format */ + id = FCAN_IDR_ID2_GET(frame_p->canid & CAN_EFF_MASK); + id |= FCAN_IDR_ID1_GET((frame_p->canid & CAN_EFF_MASK) >> + (CAN_EFF_ID_BITS - CAN_SFF_ID_BITS)); + + /* The substibute remote TX request bit should be "1" + * for extended frames as in the Phytium CAN datasheet + */ + id |= FCAN_IDR_IDE_MASK | FCAN_IDR_SRR_MASK; + if (frame_p->canid & CAN_RTR_FLAG) + { + id |= FCAN_IDR_RTR_MASK; + } + + if (instance_p->use_canfd == TRUE) + { + dlc = can_send_dlc << FCANFD_IDR_EDLC_SHIFT; + dlc |= FTCANFD_ID2_FDL_MASK; + + /* enable brs-Bit Rate Switch */ + frame_p->flags |= CANFD_BRS; + + if (frame_p->flags & CANFD_BRS) + { + dlc |= FTCANFD_ID2_BRS_MASK; + } + + if (frame_p->flags & CANFD_ESI) + { + dlc |= FTCANFD_ID2_ESI_MASK; + } + } + else + { + dlc = can_send_dlc << FCAN_IDR_EDLC_SHIFT; + } + + FCAN_DEBUG("FCanSend id = %#x\n", id); + FCAN_DEBUG("FCanSend dlc = %#x\n", dlc); + FCAN_WRITE_REG32(base_address, FCAN_TX_FIFO_OFFSET, be32_to_cpu(id)); + FCAN_WRITE_REG32(base_address, FCAN_TX_FIFO_OFFSET, be32_to_cpu(dlc)); + } + else + { + /* Standard CAN id format */ + id = FCAN_IDR_ID1_GET(frame_p->canid & CAN_SFF_MASK); + + if (frame_p->canid & CAN_RTR_FLAG) + id |= FCAN_IDR_SRR_MASK; + + FCAN_DEBUG("instance_p->use_canfd = %d, can_send_dlc = %d \n", + instance_p->use_canfd, can_send_dlc); + + if (instance_p->use_canfd == TRUE) + { + dlc = ((can_send_dlc << FCANFD_IDR1_SDLC_SHIFT) | FTCANFD_IDR_PAD_MASK); + + dlc |= FTCANFD_ID1_FDL_MASK; + + /* enable brs-Bit Rate Switch */ + frame_p->flags |= CANFD_BRS; + + if (frame_p->flags & CANFD_BRS) + { + dlc |= FTCANFD_ID1_BRS_MASK; + } + + if (frame_p->flags & CANFD_ESI) + { + dlc |= FTCANFD_ID1_ESI_MASK; + } + } + else + { + dlc = ((can_send_dlc << FCAN_IDR_SDLC_SHIFT) | FCAN_IDR_PAD_MASK); + } + + id |= dlc; + FCAN_DEBUG("can_send id = %#x\n", id); + FCAN_WRITE_REG32(base_address, FCAN_TX_FIFO_OFFSET, be32_to_cpu(id)); + + } + + if (!(frame_p->canid & CAN_RTR_FLAG)) + { + j = 0; + FCAN_DEBUG("frame_p->canid=%#x, frame_p->candlc = %d\n", frame_p->canid, frame_p->candlc); + for (i = frame_p->candlc; i > 0; i -= 4) + { + FCAN_WRITE_REG32(base_address, FCAN_TX_FIFO_OFFSET, *(u32 *)(frame_p->data + j)); + j += 4; + } + + if (i > 0) + { + FCAN_WRITE_REG32(base_address, FCAN_TX_FIFO_OFFSET, *(u32 *)(frame_p->data + j)); + } + + } + + /* triggers tranmission */ + FCAN_CLEARBIT(base_address, FCAN_CTRL_OFFSET, FCAN_CTRL_XFER_MASK); + FCAN_SETBIT(base_address, FCAN_CTRL_OFFSET, FCAN_CTRL_TXREQ_MASK | FCAN_CTRL_XFER_MASK); + + return FCAN_SUCCESS; +} + +/** + * @name: FCan_SetTiming + * @msg: This routine sets Bit time + * @param {FCanCtrl} *instance_p is a pointer to the FCanCtrl instance. + * @param {FCanBaudrateConfig} *bittiming_p, parameters of arbitration or data segment baudrate + * @param {FCanSegmentType} target_segment, specifies which target is to be selected. followed by FCAN_ARB_SEGMENT or FCAN_DATA_SEGMENT + * @out param: + * @return {*} + */ +static FError FCanSetTiming(FCanCtrl *instance_p, FCanBaudrateConfig *bittiming_p, FCanSegmentType target_segment) +{ + u32 reg_val = 0; + u32 transfer_enable; + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + FASSERT(target_segment < FCAN_SEGMENT_TYPE_NUM); + uintptr base_address = instance_p->config.base_address; + + FASSERT(bittiming_p->brp != 0); + FASSERT(bittiming_p->prop_seg != 0); + FASSERT(bittiming_p->phase_seg1 != 0); + FASSERT(bittiming_p->phase_seg2 != 0); + + FCAN_DEBUG("brp=%d, prop_seg=%d, phase_seg1=%d, phase_seg2=%d, sjw=%d", + bittiming_p->brp, bittiming_p->prop_seg, bittiming_p->phase_seg1, + bittiming_p->phase_seg2, bittiming_p->sjw); + + /* Setting Baud Rate prescalar value in BRPR Register */ + reg_val = (bittiming_p->brp - 1) << 16; + reg_val |= (bittiming_p->prop_seg - 1) << 2; + reg_val |= (bittiming_p->phase_seg1 - 1) << 5; + reg_val |= (bittiming_p->phase_seg2 - 1) << 8; + reg_val |= (bittiming_p->sjw - 1); + + transfer_enable = (FCAN_READ_REG32(base_address, FCAN_CTRL_OFFSET) & FCAN_CTRL_XFER_MASK); + if (transfer_enable) + { + FCAN_CLEARBIT(base_address, FCAN_CTRL_OFFSET, FCAN_CTRL_XFER_MASK); + } + + if (reg_val) + { + if (target_segment == FCAN_DATA_SEGMENT) + FCAN_WRITE_REG32(base_address, FCAN_DAT_RATE_CTRL_OFFSET, reg_val); + else + FCAN_WRITE_REG32(base_address, FCAN_ARB_RATE_CTRL_OFFSET, reg_val); + } + else + { + FCAN_ERROR("FCanSetTiming reg_val failed"); + return FCAN_FAILURE; + } + + return FCAN_SUCCESS; +} + +/** + * @name: FCanBaudrateSet + * @msg: Set the fcan baudrate by FCanBaudrateConfig parameters. + * @param {FCanCtrl} *instance_p, instance of FCanCtrl controller + * @param {FCanBaudrateConfig} *baudrate_p, parameters of arbitration or data segment baudrate + * include baudrate, parameters of target baudrate + * include sample_point, parameters of target sample point, 0 means the general configuration is used. + * The value is the percentage of sampling points multiplied by 1000. + * For example, if sample point is 0.75, set target_sample_point = 750. + * Or manual config baudrate parameters. + * @param {FCanSegmentType} segment, specifies data segment or arbitration segment is selected. followed by FCAN_ARB_SEGMENT or FCAN_DATA_SEGMENT + * @return err code information, FQSPI_SUCCESS indicates success,others indicates failed + * @note this function is to set arb and data segment baudrate, according to the prop_seg, + * phase_seg1, phase_seg2 ,brp and sjw parameters, users can use this function to set can baudrate. + * A formula to calculate baudrate is: + * baudrate = FCAN_REF_CLOCK/(brp*(sjw+prop_seg+phase_seg1++phase_seg2)) + * sample point = (sjw+prop_seg+phase_seg1)/(sjw+prop_seg+phase_seg1++phase_seg2) + * Recommended sample point : + * 75.0% : baudrate > 800000 + * 80.0% : baudrate > 500000 + * 87.5% : baudrate <= 500000 + */ +FError FCanBaudrateSet(FCanCtrl *instance_p, FCanBaudrateConfig *baudrate_p) +{ + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + FCanSegmentType segment = baudrate_p->segment; + FASSERT(segment < FCAN_SEGMENT_TYPE_NUM); + u32 baudrate = baudrate_p->baudrate; + u32 sample_point = baudrate_p->sample_point; + if (baudrate_p->auto_calc == TRUE) + { +#if defined(CONFIG_TARGET_F2000_4) || defined(CONFIG_TARGET_D2000) + if ((segment == FCAN_ARB_SEGMENT) && ((baudrate > FCAN_BAUDRATE_1000K) || (baudrate < FCAN_BAUDRATE_50K))) + { + FCAN_ERROR("FCanBaudrateSet FCAN_ARB_SEGMENT baudrate = %d invalid", baudrate); + return FCAN_INVAL_PARAM; + } + if ((segment == FCAN_DATA_SEGMENT) && ((baudrate > FCAN_BAUDRATE_1000K) || (baudrate < FCAN_BAUDRATE_50K))) + { + FCAN_ERROR("FCanBaudrateSet FCAN_DATA_SEGMENT baudrate = %d invalid", baudrate); + return FCAN_INVAL_PARAM; + } +#elif defined(CONFIG_TARGET_E2000) + if ((segment == FCAN_ARB_SEGMENT) && ((baudrate > FCAN_BAUDRATE_1000K) || (baudrate < FCAN_BAUDRATE_10K))) + { + FCAN_ERROR("FCanBaudrateSet FCAN_ARB_SEGMENT baudrate = %d invalid", baudrate); + return FCAN_INVAL_PARAM; + } + if ((segment == FCAN_DATA_SEGMENT) && ((baudrate > FCAN_BAUDRATE_5000K) || (baudrate < FCAN_BAUDRATE_10K))) + { + FCAN_ERROR("FCanBaudrateSet FCAN_DATA_SEGMENT baudrate = %d invalid", baudrate); + return FCAN_INVAL_PARAM; + } +#endif + } + + FError ret = FCAN_SUCCESS; + FCanBaudrateConfig timing_config; + memset(&timing_config, 0, sizeof(timing_config)); + + /* Automatically calculate parameters based on baudrate and sample point */ + if (baudrate_p->auto_calc == TRUE) + { + ret = FCanCalcBittiming(&timing_config, baudrate, sample_point, segment); + if (ret != FCAN_SUCCESS) + { + FCAN_ERROR("FCanCalcBittiming %d failed", segment); + return FCAN_FAILURE; + } + + ret = FCanSetTiming(instance_p, &timing_config, segment); + if (ret != FCAN_SUCCESS) + { + FCAN_ERROR("FCanSetTiming %d failed", segment); + return FCAN_FAILURE; + } + } + else + { + ret = FCanSetTiming(instance_p, baudrate_p, segment); + if (ret != FCAN_SUCCESS) + { + FCAN_ERROR("FCanSetTiming failed"); + return FCAN_FAILURE; + } + } + + return ret; +} + +/** + * @name: FCanFdEnable + * @msg: Enable or disable can. + * @param {FCanCtrl} *instance_p, instance of FCanCtrl controller + * @param {boolean} enable, TRUE-enable canfd, FALSE-disable canfd. + * @return err code information, FCAN_SUCCESS indicates success,others indicates failed + */ +FError FCanEnable(FCanCtrl *instance_p, boolean enable) +{ + FASSERT(instance_p); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + + uintptr base_addr = instance_p->config.base_address; + + if (enable == TRUE) + { + FCAN_SETBIT(base_addr, FCAN_CTRL_OFFSET, FCAN_CTRL_XFER_MASK); + } + else + { + FCAN_CLEARBIT(base_addr, FCAN_CTRL_OFFSET, FCAN_CTRL_XFER_MASK); + } + return FCAN_SUCCESS; +} + +/** + * @name: FCanIdMaskFilterSet + * @msg: Set the can mask and umask id. + * @param {FCanCtrl} *instance_p, instance of FCanCtrl controller + * @param {FCanIdMaskConfig} *id_mask_p, include filter register index, umask id and mask id + * id indicates a specific ID can receive + * mask indicates mask the corresponding bit of the id + * @return err code information, FCAN_SUCCESS indicates success,others indicates failed + */ +FError FCanIdMaskFilterSet(FCanCtrl *instance_p, FCanIdMaskConfig *id_mask_p) +{ + FASSERT(instance_p); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + FASSERT(id_mask_p); + u32 filter_index = id_mask_p->filter_index; + FASSERT(filter_index < FCAN_ACC_ID_REG_NUM); + uintptr base_address = instance_p->config.base_address; + u32 id_reg_offset = 0; + u32 mask_reg_offset = 0; + u32 id = 0; + u32 mask = 0; + + switch (filter_index) + { + case 0: + id_reg_offset = FCAN_ACC_ID0_OFFSET; + mask_reg_offset = FCAN_ACC_ID0_MASK_OFFSET; + break; + case 1: + id_reg_offset = FCAN_ACC_ID1_OFFSET; + mask_reg_offset = FCAN_ACC_ID1_MASK_OFFSET; + break; + case 2: + id_reg_offset = FCAN_ACC_ID2_OFFSET; + mask_reg_offset = FCAN_ACC_ID2_MASK_OFFSET; + break; + case 3: + id_reg_offset = FCAN_ACC_ID3_OFFSET; + mask_reg_offset = FCAN_ACC_ID3_MASK_OFFSET; + break; + default: + return FCAN_FAILURE; + } + + if (id_mask_p->type == FCAN_STANDARD_FRAME) + { + id = id_mask_p->id << FCAN_ACC_IDN_SHIFT; + mask = id_mask_p->mask << FCAN_ACC_IDN_SHIFT; + } + + FCAN_WRITE_REG32(base_address, id_reg_offset, id); + FCAN_WRITE_REG32(base_address, mask_reg_offset, mask); + + return FCAN_SUCCESS; +} + +/** + * @name: FCanIdMaskFilterEnable + * @msg: Set the can id mask filter enable. + * @param {FCanCtrl} *instance_p, instance of FCanCtrl controller + * @return {*} + */ +void FCanIdMaskFilterEnable(FCanCtrl *instance_p) +{ + FCanConfig *config_p; + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + config_p = &instance_p->config; + FCAN_SETBIT(config_p->base_address, FCAN_CTRL_OFFSET, FCAN_CTRL_AIME_MASK); +} + +/** + * @name: FCanIdMaskFilterDisable + * @msg: Set the can id mask filter disable. + * @param {FCanCtrl} *instance_p, instance of FCanCtrl controller + * @return {*} + */ +void FCanIdMaskFilterDisable(FCanCtrl *instance_p) +{ + FCanConfig *config_p; + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + config_p = &instance_p->config; + FCAN_CLEARBIT(config_p->base_address, FCAN_CTRL_OFFSET, FCAN_CTRL_AIME_MASK); +} + +/** + * @name: FCanInterruptEnable + * @msg: Enable can interrupt. + * @param {FCanCtrl} *instance_p, instance of FCanCtrl controller + * @param {FCanIntrEventType} event_type, interrupt event type + * @return err code information, FCAN_SUCCESS indicates success,others indicates failed + */ +FError FCanInterruptEnable(FCanCtrl *instance_p, FCanIntrEventType event_type) +{ + FASSERT(instance_p != NULL); + FASSERT(FT_COMPONENT_IS_READY == instance_p->is_ready); + + uintptr base_addr = instance_p->config.base_address; + u32 reg_val = 0; + reg_val = FCAN_READ_REG32(base_addr, FCAN_INTR_OFFSET); + + switch (event_type) + { + case FCAN_INTR_EVENT_SEND: + reg_val |= FCAN_INTR_TEIE_MASK; + break; + + case FCAN_INTR_EVENT_RECV: + reg_val |= FCAN_INTR_REIE_MASK; + break; + + case FCAN_INTR_EVENT_ERROR: + reg_val |= FCAN_INTR_EIE_MASK; + break; + + default: + break; + } + + FCAN_WRITE_REG32(base_addr, FCAN_INTR_OFFSET, reg_val); + + return FCAN_SUCCESS; +} + +/** + * @name: FCanFdEnable + * @msg: Enable or disable can fd. + * @param {FCanCtrl} *instance_p, instance of FCanCtrl controller + * @param {boolean} enable, TRUE-enable canfd, FALSE-disable canfd. + * @return err code information, FCAN_SUCCESS indicates success,others indicates failed + */ +FError FCanFdEnable(FCanCtrl *instance_p, boolean enable) +{ + FASSERT(instance_p != NULL); + FASSERT(FT_COMPONENT_IS_READY == instance_p->is_ready); + uintptr base_addr = instance_p->config.base_address; + + if (enable == TRUE) + { + instance_p->use_canfd = TRUE; + FCAN_SETBIT(base_addr, FCAN_CTRL_OFFSET, FCAN_CTRL_FDCRC_MASK | FCAN_CTRL_IOF_MASK); + } + else + { + instance_p->use_canfd = FALSE; + FCAN_SETBIT(base_addr, FCAN_CTRL_OFFSET, FCAN_CTRL_IOF_MASK); + FCAN_CLEARBIT(base_addr, FCAN_CTRL_OFFSET, FCAN_CTRL_FDCRC_MASK); + } + + return FCAN_SUCCESS; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/can/fcan/fcan.h b/bsp/phytium/libraries/standalone/drivers/can/fcan/fcan.h new file mode 100644 index 0000000000..d35076fd74 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/can/fcan/fcan.h @@ -0,0 +1,249 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fcan.h + * Date: 2021-04-27 15:08:44 + * LastEditTime: 2022-02-18 08:29:25 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#ifndef FT_CAN_H +#define FT_CAN_H + +#include "ftypes.h" +#include "ferror_code.h" +#include "fparameters.h" +#include "fkernel.h" +#include "fcan_hw.h" + +typedef enum +{ + FCAN_INTR_EVENT_SEND = 0, /* Handler type for frame sending interrupt */ + FCAN_INTR_EVENT_RECV = 1, /* Handler type for frame reception interrupt */ + FCAN_INTR_EVENT_ERROR, /* Handler type for error interrupt */ + FCAN_INTR_EVENT_NUM +} FCanIntrEventType; + +#define FCAN_SUCCESS FT_SUCCESS /* SUCCESS */ +#define FCAN_NOT_READY FT_MAKE_ERRCODE(ErrModBsp, ErrBspCan, 1) +#define FCAN_FAILURE FT_MAKE_ERRCODE(ErrModBsp, ErrBspCan, 2) /* failed */ +#define FCAN_INVAL_PARAM FT_MAKE_ERRCODE(ErrModBsp, ErrBspCan, 3) /* invalid parameters */ + +#if defined(CONFIG_FCAN_USE_CANFD) + #define FCAN_DATA_LENGTH 64U +#else + #define FCAN_DATA_LENGTH 8U +#endif + +/* 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 */ + +/* Bit timing calculate */ +#define CAN_CALC_MAX_ERROR 50 /* in one-tenth of a percent */ +#define CAN_CALC_SYNC_SEG 1 + +/* can segment type */ +typedef enum +{ + FCAN_ARB_SEGMENT = 0, /* Arbitration segment */ + FCAN_DATA_SEGMENT = 1, /* Data segment */ + FCAN_SEGMENT_TYPE_NUM +} FCanSegmentType; + +/* Can error status bit mask */ +#define FCAN_BUS_ERROR_MASK 1 +#define FCAN_PASSIVE_ERROR_MASK 2 +#define FCAN_PASSIVE_WARNING_MASK 4 +#define FCAN_FIFO_RX_OVERFLOW_MASK 8 + +/* Can frame select */ +#define FCAN_STANDARD_FRAME 0 +#define FCAN_EXTENDARD_FRAME 1 + +/* can baudrate */ +#define FCAN_BAUDRATE_10K 10000 +#define FCAN_BAUDRATE_50K 50000 +#define FCAN_BAUDRATE_100K 100000 +#define FCAN_BAUDRATE_200K 200000 +#define FCAN_BAUDRATE_250K 250000 +#define FCAN_BAUDRATE_500K 500000 +#define FCAN_BAUDRATE_1000K 1000000 +#define FCAN_BAUDRATE_2000K 2000000 +#define FCAN_BAUDRATE_3000K 3000000 +#define FCAN_BAUDRATE_4000K 4000000 +#define FCAN_BAUDRATE_5000K 5000000 + +/* + * defined bits for FCanFrame.flags + * + * The use of struct FCanFrame implies the Extended Data Length (EDL) bit to + * be set in the CAN frame bitstream on the wire. The EDL bit switch turns + * the CAN controllers bitstream processor into the CAN FD mode which creates + * two new options within the CAN FD frame specification: + * + * Bit Rate Switch - to indicate a second baudrate is/was used for the payload + * Error State Indicator - represents the error state of the transmitting node + * + * As the CANFD_ESI bit is internally generated by the transmitting CAN + * controller only the CANFD_BRS bit is relevant for real CAN controllers when + * building a CAN FD frame for transmission. Setting the CANFD_ESI bit can make + * sense for virtual CAN interfaces to test applications with echoed frames. + */ +#define CANFD_BRS 0x02 /* bit rate switch (second baudrate for payload data) */ +#define CANFD_ESI 0x04 /* error state indicator of the transmitting node */ + +typedef void (*FCanIntrEventHandler)(void *param); + +typedef struct +{ + FCanIntrEventType type; + FCanIntrEventHandler handler; + void *param; +} FCanIntrEventConfig; + +typedef struct +{ + u32 filter_index;/* filter register index*/ + u32 id; /* id bit to receive */ + u32 mask;/* id mask bit to receive */ + u32 type;/* frame type, standard or extended*/ +} FCanIdMaskConfig; + +typedef struct +{ + u32 canid;/* can frame id */ + u8 candlc;/* can frame length */ + u8 flags; /* additional flags for CAN FD */ + u8 data[FCAN_DATA_LENGTH] __attribute__((aligned(8))); +} FCanFrame; + +typedef struct +{ + u32 instance_id; /* Id of device */ + uintptr base_address; /* Can base Address */ + u32 irq_num; /* interrupt number */ + u32 irq_prority;/* interrupt priority*/ +} FCanConfig; + +typedef struct +{ + FCanSegmentType segment; + boolean auto_calc; /* if auto calculate baudrate parameters */ + u32 baudrate; /* baudrate */ + u32 sample_point; /* sample point */ + 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; /* Baudrate prescaler */ +} FCanBaudrateConfig; + +typedef struct +{ + u8 xfers;/* transfer status */ + u8 rs; /* receive status */ + u8 ts; /* transmit status */ + u8 fies; /* Current status of the controller state machine */ + u8 fras; /* Frame tagging status */ +} FCanXferStatus; + +typedef struct +{ + FCanXferStatus xfer_status; + u32 tx_err_cnt; + u32 rx_err_cnt; + u32 tx_fifo_cnt; + u32 rx_fifo_cnt; +} FCanStatus; + +typedef struct +{ + FCanConfig config; + u32 is_ready; /* Device is initialized and ready */ + boolean use_canfd; /* if use canfd function */ + + FCanIntrEventConfig intr_event[FCAN_INTR_EVENT_NUM];/* event handler and parameters for interrupt */ +} FCanCtrl; + +/* get default configuration of specific can id */ +const FCanConfig *FCanLookupConfig(FCanInstance instance_id); + +/* reset a specific can instance */ +void FCanReset(FCanCtrl *instance_p); + +/* Deinitializes a specific can instance */ +void FCanDeInitialize(FCanCtrl *instance_p); + +/* Initializes a specific can instance */ +FError FCanCfgInitialize(FCanCtrl *instance_p, const FCanConfig *input_config_p); + +/* Set the fcan baudrate */ +FError FCanBaudrateSet(FCanCtrl *instance_p, FCanBaudrateConfig *baudrate_p); + +/* interrupt handler for the driver */ +void FCanIntrHandler(s32 vector, void *args); + +/* register FCanCtrl interrupt handler function */ +void FCanRegisterInterruptHandler(FCanCtrl *instance_p, FCanIntrEventConfig *intr_event_p); + +/* receive can message by specific can instance */ +FError FCanRecv(FCanCtrl *instance_p, FCanFrame *frame_p); + +/* send can message by specific can instance */ +FError FCanSend(FCanCtrl *instance_p, FCanFrame *frame_p); + +/* Enable the specific can instance */ +FError FCanEnable(FCanCtrl *instance_p, boolean enable); + +/* read can status, include send and receive error count */ +FError FCanStatusGet(FCanCtrl *instance_p, FCanStatus *status_p); + +/* Set the can mask and umask id */ +FError FCanIdMaskFilterSet(FCanCtrl *instance_p, FCanIdMaskConfig *id_mask_p); + +/* Set the can id mask filter enable */ +void FCanIdMaskFilterEnable(FCanCtrl *instance_p); + +/* Set the can id mask filter disable */ +void FCanIdMaskFilterDisable(FCanCtrl *instance_p); + +/* Enable can interrupt */ +FError FCanInterruptEnable(FCanCtrl *instance_p, FCanIntrEventType event_type); + +/* Enable or disable can fd */ +FError FCanFdEnable(FCanCtrl *instance_p, boolean enable); + +#endif // !FT_CAN_H diff --git a/bsp/phytium/libraries/standalone/drivers/can/fcan/fcan_g.c b/bsp/phytium/libraries/standalone/drivers/can/fcan/fcan_g.c new file mode 100644 index 0000000000..62d126a570 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/can/fcan/fcan_g.c @@ -0,0 +1,48 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fcan_g.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:28:45 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#include "fcan.h" +#include "fparameters.h" + +const FCanConfig FCanConfigTbl[FCAN_INSTANCE_NUM] = +{ + { + .instance_id = FCAN_INSTANCE_0, /* Id of device */ + .base_address = FCAN0_BASEADDR, /* Can base Address */ + .irq_num = FCAN0_IRQNUM, + }, + { + .instance_id = FCAN_INSTANCE_1, /* Id of device */ + .base_address = FCAN1_BASEADDR, /* Can base Address */ + .irq_num = FCAN1_IRQNUM, + }, +#if defined(CONFIG_TARGET_F2000_4) || defined(CONFIG_TARGET_D2000) + { + .instance_id = FCAN_INSTANCE_2, /* Id of device */ + .base_address = FCAN2_BASEADDR, /* Can base Address */ + .irq_num = FCAN2_IRQNUM, + } +#endif + +}; diff --git a/bsp/phytium/libraries/standalone/drivers/can/fcan/fcan_hw.c b/bsp/phytium/libraries/standalone/drivers/can/fcan/fcan_hw.c new file mode 100644 index 0000000000..dedaa33077 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/can/fcan/fcan_hw.c @@ -0,0 +1,67 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fcan_hw.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:28:50 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#include "fcan_hw.h" +#include "fparameters.h" +#include "fassert.h" +#include "fdebug.h" + +#define CAN_HW_DEBUG_TAG "CAN_HW" +#define FCAN_DEBUG(format, ...) FT_DEBUG_PRINT_D(CAN_HW_DEBUG_TAG, format, ##__VA_ARGS__) +#define FCAN_INFO(format, ...) FT_DEBUG_PRINT_I(CAN_HW_DEBUG_TAG, format, ##__VA_ARGS__) +#define FCAN_WARN(format, ...) FT_DEBUG_PRINT_W(CAN_HW_DEBUG_TAG, format, ##__VA_ARGS__) +#define FCAN_ERROR(format, ...) FT_DEBUG_PRINT_E(CAN_HW_DEBUG_TAG, format, ##__VA_ARGS__) + + +void FCanDump(uintptr base_addr) +{ + + printf("Off[0x%x]: FCAN_CTRL_OFFSET = 0x%08x\r\n", base_addr + FCAN_CTRL_OFFSET, FCAN_READ_REG32(base_addr, FCAN_CTRL_OFFSET)); + printf("Off[0x%x]: FCAN_INTR_OFFSET = 0x%08x\r\n", base_addr + FCAN_INTR_OFFSET, FCAN_READ_REG32(base_addr, FCAN_INTR_OFFSET)); + printf("Off[0x%x]: FCAN_ARB_RATE_CTRL_OFFSET = 0x%08x\r\n", base_addr + FCAN_ARB_RATE_CTRL_OFFSET, FCAN_READ_REG32(base_addr, FCAN_ARB_RATE_CTRL_OFFSET)); + printf("Off[0x%x]: FCAN_DAT_RATE_CTRL_OFFSET = 0x%08x\r\n", base_addr + FCAN_DAT_RATE_CTRL_OFFSET, FCAN_READ_REG32(base_addr, FCAN_DAT_RATE_CTRL_OFFSET)); + printf("Off[0x%x]: FCAN_ACC_ID0_OFFSET = 0x%08x\r\n", base_addr + FCAN_ACC_ID0_OFFSET, FCAN_READ_REG32(base_addr, FCAN_ACC_ID0_OFFSET)); + printf("Off[0x%x]: FCAN_ACC_ID1_OFFSET = 0x%08x\r\n", base_addr + FCAN_ACC_ID1_OFFSET, FCAN_READ_REG32(base_addr, FCAN_ACC_ID1_OFFSET)); + printf("Off[0x%x]: FCAN_ACC_ID2_OFFSET = 0x%08x\r\n", base_addr + FCAN_ACC_ID2_OFFSET, FCAN_READ_REG32(base_addr, FCAN_ACC_ID2_OFFSET)); + printf("Off[0x%x]: FCAN_ACC_ID3_OFFSET = 0x%08x\r\n", base_addr + FCAN_ACC_ID3_OFFSET, FCAN_READ_REG32(base_addr, FCAN_ACC_ID3_OFFSET)); + printf("Off[0x%x]: FCAN_ACC_ID0_MASK_OFFSET = 0x%08x\r\n", base_addr + FCAN_ACC_ID0_MASK_OFFSET, FCAN_READ_REG32(base_addr, FCAN_ACC_ID0_MASK_OFFSET)); + printf("Off[0x%x]: FCAN_ACC_ID1_MASK_OFFSET = 0x%08x\r\n", base_addr + FCAN_ACC_ID1_MASK_OFFSET, FCAN_READ_REG32(base_addr, FCAN_ACC_ID1_MASK_OFFSET)); + printf("Off[0x%x]: FCAN_ACC_ID2_MASK_OFFSET = 0x%08x\r\n", base_addr + FCAN_ACC_ID2_MASK_OFFSET, FCAN_READ_REG32(base_addr, FCAN_ACC_ID2_MASK_OFFSET)); + printf("Off[0x%x]: FCAN_ACC_ID3_MASK_OFFSET = 0x%08x\r\n", base_addr + FCAN_ACC_ID3_MASK_OFFSET, FCAN_READ_REG32(base_addr, FCAN_ACC_ID3_MASK_OFFSET)); + printf("Off[0x%x]: FCAN_XFER_STS_OFFSET = 0x%08x\r\n", base_addr + FCAN_XFER_STS_OFFSET, FCAN_READ_REG32(base_addr, FCAN_XFER_STS_OFFSET)); + printf("Off[0x%x]: FCAN_ERR_CNT_OFFSET = 0x%08x\r\n", base_addr + FCAN_ERR_CNT_OFFSET, FCAN_READ_REG32(base_addr, FCAN_ERR_CNT_OFFSET)); + + printf("Off[0x%x]: FCAN_FIFO_CNT_OFFSET = 0x%08x\r\n", base_addr + FCAN_FIFO_CNT_OFFSET, FCAN_READ_REG32(base_addr, FCAN_FIFO_CNT_OFFSET)); + printf("Off[0x%x]: FCAN_DMA_CTRL_OFFSET = 0x%08x\r\n", base_addr + FCAN_DMA_CTRL_OFFSET, FCAN_READ_REG32(base_addr, FCAN_DMA_CTRL_OFFSET)); + + printf("Off[0x%x]: FCAN_XFER_EN_OFFSET = 0x%08x\r\n", base_addr + FCAN_XFER_EN_OFFSET, FCAN_READ_REG32(base_addr, FCAN_XFER_EN_OFFSET)); + + printf("Off[0x%x]: FCAN_FRM_INFO_OFFSET = 0x%08x\r\n", base_addr + FCAN_FRM_INFO_OFFSET, FCAN_READ_REG32(base_addr, FCAN_FRM_INFO_OFFSET)); + + printf("Off[0x%x]: FCAN_TX_FIFO_OFFSET = 0x%08x\r\n", base_addr + FCAN_TX_FIFO_OFFSET, FCAN_READ_REG32(base_addr, FCAN_TX_FIFO_OFFSET)); + printf("Off[0x%x]: FCAN_RX_FIFO_OFFSET = 0x%08x\r\n", base_addr + FCAN_RX_FIFO_OFFSET, FCAN_READ_REG32(base_addr, FCAN_RX_FIFO_OFFSET)); + + printf("\r\n"); + +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/can/fcan/fcan_hw.h b/bsp/phytium/libraries/standalone/drivers/can/fcan/fcan_hw.h new file mode 100644 index 0000000000..1117d962bc --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/can/fcan/fcan_hw.h @@ -0,0 +1,246 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fcan_hw.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:29:05 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#ifndef FT_CAN_HW_H +#define FT_CAN_HW_H + +#include "ftypes.h" +#include "fio.h" +#include "sdkconfig.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_XFER_EN_OFFSET 0x40 /* Transfer enable register */ +#define FCAN_FRM_INFO_OFFSET 0x48 /* Frame valid number 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 BIT(0) /*Transfer enable*/ +#define FCAN_CTRL_TXREQ_MASK BIT(1) /*Transmit request*/ +#define FCAN_CTRL_AIME_MASK BIT(2) /*Acceptance identifier mask enable*/ +#define FCAN_CTRL_RST_MASK BIT(7) /* Soft rest 1:reset and auto clear */ +#define FCAN_CTRL_RFEIDF_MASK BIT(8) /* whether generates frame recv completion interrupt when filtering frames */ +#define FCAN_CTRL_IRFEDT_MASK BIT(9) /* whether generates frame recv completion interrupt when sending frame */ +#define FCAN_CTRL_IOF_MASK BIT(10) /* send overload frame */ +#define FCAN_CTRL_FDCRC_MASK BIT(11) /* Stuff count, crc mode */ + +/* FCAN_INTR mask */ +#define FCAN_INTR_BOIS_MASK BIT(0) /* Bus off interrupt status*/ +#define FCAN_INTR_PWIS_MASK BIT(1) /* Passive warning interrupt status*/ +#define FCAN_INTR_PEIS_MASK BIT(2) /* Passive error interrupt status*/ +#define FCAN_INTR_RFIS_MASK BIT(3) /* RX FIFO full interrupt status*/ +#define FCAN_INTR_TFIS_MASK BIT(4) /* TX FIFO empty interrupt status*/ +#define FCAN_INTR_REIS_MASK BIT(5) /* RX frame end interrupt status*/ +#define FCAN_INTR_TEIS_MASK BIT(6) /* TX frame end interrupt status*/ +#define FCAN_INTR_EIS_MASK BIT(7) /* Error interrupt status*/ +#define FCAN_INTR_BOIE_MASK BIT(8) /* Bus off interrupt enable*/ +#define FCAN_INTR_PWIE_MASK BIT(9) /* Passive warning interrupt enable*/ +#define FCAN_INTR_PEIE_MASK BIT(10) /* Passive error interrupt enable*/ +#define FCAN_INTR_RFIE_MASK BIT(11) /* RX FIFO full interrupt enable*/ +#define FCAN_INTR_TFIE_MASK BIT(12) /* TX FIFO empty interrupt enable*/ +#define FCAN_INTR_REIE_MASK BIT(13) /* RX frame end interrupt enable*/ +#define FCAN_INTR_TEIE_MASK BIT(14) /* TX frame end interrupt enable*/ +#define FCAN_INTR_EIE_MASK BIT(15) /* Error interrupt enable*/ +#define FCAN_INTR_BOIC_MASK BIT(16) /* Bus off interrupt clear*/ +#define FCAN_INTR_PWIC_MASK BIT(17) /* Passive warning interrupt clear*/ +#define FCAN_INTR_PEIC_MASK BIT(18) /* Passive error interrupt clear*/ +#define FCAN_INTR_RFIC_MASK BIT(19) /* RX FIFO full interrupt clear*/ +#define FCAN_INTR_TFIC_MASK BIT(20) /* TX FIFO empty interrupt clear*/ +#define FCAN_INTR_REIC_MASK BIT(21) /* RX frame end interrupt clear*/ +#define FCAN_INTR_TEIC_MASK BIT(22) /* TX frame end interrupt clear*/ +#define FCAN_INTR_EIC_MASK BIT(23) /* Error interrupt clear*/ +#define FCAN_INTR_BORIS_MASK BIT(24) +#define FCAN_INTR_PWRIS_MASK BIT(25) +#define FCAN_INTR_PERIS_MASK BIT(26) +#define FCAN_INTR_RFRIS_MASK BIT(27) +#define FCAN_INTR_TFRIS_MASK BIT(28) +#define FCAN_INTR_RERIS_MASK BIT(29) +#define FCAN_INTR_TERIS_MASK BIT(30) +#define FCAN_INTR_ERIS_MASK BIT(31) + +/* FCAN_DAT_RATE_CTRL mask */ + +/* FCAN_ACC_ID(0-3)_MASK mask */ +#define FCAN_ACC_IDN_MASK GENMASK(28, 0)/*don’t care the matching */ + +#define FCAN_ACC_ID_REG_NUM 4 + +/* FCAN_XFER_STS mask */ +#define FCAN_XFER_STS_XFERS_GET(x) GET_REG32_BITS((x), 10, 10) +#define FCAN_XFER_STS_RS_GET(x) GET_REG32_BITS((x), 9, 9) +#define FCAN_XFER_STS_TS_GET(x) GET_REG32_BITS((x), 8, 8) +#define FCAN_XFER_STS_FIES_GET(x) GET_REG32_BITS((x), 7, 3) +#define FCAN_XFER_STS_FRAS_GET(x) GET_REG32_BITS((x), 2, 0) + +/* FCAN_ERR_CNT_OFFSET mask */ +#define FCAN_ERR_CNT_RFN_MASK GENMASK(8, 0) /*Receive error counter*/ +#define FCAN_ERR_CNT_RFN_GET(x) GET_REG32_BITS((x), 8, 0) +#define FCAN_ERR_CNT_RFN_SET(x) SET_REG32_BITS((x), 8, 0) + +#define FCAN_ERR_CNT_TFN_MASK GENMASK(24, 16) /*Transmit error counter*/ +#define FCAN_ERR_CNT_TFN_GET(x) GET_REG32_BITS((x), 24, 16) +#define FCAN_ERR_CNT_TFN_SET(x) SET_REG32_BITS((x), 24, 16) + +/* FCAN_FIFO_CNT_OFFSET mask */ +#define FCAN_FIFO_CNT_RFN_MASK GENMASK(6, 0) /*Receive FIFO valid data number*/ +#define FCAN_FIFO_CNT_RFN_GET(x) GET_REG32_BITS((x), 6, 0) +#define FCAN_FIFO_CNT_RFN_SET(x) SET_REG32_BITS((x), 6, 0) + +#define FCAN_FIFO_CNT_TFN_MASK GENMASK(6, 0) /*Transmit FIFO valid data number*/ +#define FCAN_FIFO_CNT_TFN_GET(x) GET_REG32_BITS((x), 22, 16) +#define FCAN_FIFO_CNT_TFN_SET(x) SET_REG32_BITS((x), 22, 16) + +#define FCAN_IDR_ID1_SHIFT 21 /* Standard Messg Identifier */ +#define FCAN_IDR_SDLC_SHIFT 14 +#define FCANFD_IDR_EDLC_SHIFT 24 +#define FCAN_IDR_EDLC_SHIFT 26 +#define FCAN_ACC_IDN_SHIFT 18 /*Standard ACC ID shift*/ +#define FCANFD_IDR_GET_EDLC_SHIFT 12 +#define FCANFD_IDR1_SDLC_SHIFT 11 + +/* can */ +#define FCAN_IDR_ID2_GET(x) GET_REG32_BITS((x), 18, 1) /* Get extended message ident */ +#define FCAN_IDR_ID2_SET(x) SET_REG32_BITS((x), 18, 1) /* Set extended message ident */ +#define FCAN_IDR_ID1_GET(x) GET_REG32_BITS((x), 31, 21) /* Get standard msg identifier */ +#define FCAN_IDR_ID1_SET(x) SET_REG32_BITS((x), 31, 21) /* Set standard msg identifier */ +#define FCAN_IDR_IDE_MASK BIT(19) /* Identifier extension */ +#define FCAN_IDR_SRR_MASK BIT(20) /* Substitute remote TXreq */ +#define FCAN_IDR_RTR_MASK BIT(0) /* Extended frames remote TX request */ +#define FCAN_IDR_PAD_MASK GENMASK(13, 0) /* Standard msg padding 1 */ +#define FCAN_IDR_DLC_GET(x) GET_REG32_BITS((x), 17, 14) /* Standard msg dlc */ +#define FCAN_IDR_EDLC_GET(x) GET_REG32_BITS((x), 29, 26) /* Extended msg dlc */ + +/* canfd */ +#define FTCANFD_ID1_FDL_MASK BIT(18) /* CANFD Standard FDF */ +#define FTCANFD_ID1_BRS_MASK BIT(16) /* CANFD Standard BRS */ +#define FTCANFD_ID1_ESI_MASK BIT(15) /* CANFD Standard ESI */ +#define FTCANFD_ID1_SDLC_GET(x) GET_REG32_BITS((x),14, 11) /* CANFD Standard msg dlc */ +#define FTCANFD_IDR_PAD_MASK GENMASK(10, 0) /* CANFD Standard msg padding 1 */ + +#define FTCANFD_ID2_FDL_MASK BIT(31) /* CANFD Extended FDF */ +#define FTCANFD_ID2_BRS_MASK BIT(29) /* CANFD Extended BRS */ +#define FTCANFD_ID2_ESI_MASK BIT(28) /* CANFD Extended ESI */ +#define FTCANFD_ID2_EDLC_GET(x) GET_REG32_BITS((x), 27, 24) /* CANFD Extended msg dlc */ + +#define FTCAN_INTR_EN (FTCAN_INTR_TEIE_MASK | FTCAN_INTR_REIE_MASK | FTCAN_INTR_RFIE_MASK) + +/* Can timming */ +#if defined(CONFIG_TARGET_F2000_4) || defined(CONFIG_TARGET_D2000) + + #define FCAN_ARB_TSEG1_MIN 1 + #define FCAN_ARB_TSEG1_MAX 16 + #define FCAN_ARB_TSEG2_MIN 1 + #define FCAN_ARB_TSEG2_MAX 8 + #define FCAN_ARB_SJW_MAX 4 + #define FCAN_ARB_BRP_MIN 1 + #define FCAN_ARB_BRP_MAX 512 + #define FCAN_ARB_BRP_INC 1 + + #define FCAN_DATA_TSEG1_MIN 1 + #define FCAN_DATA_TSEG1_MAX 16 + #define FCAN_DATA_TSEG2_MIN 1 + #define FCAN_DATA_TSEG2_MAX 8 + #define FCAN_DATA_SJW_MAX 4 + #define FCAN_DATA_BRP_MIN 1 + #define FCAN_DATA_BRP_MAX 512 + #define FCAN_DATA_BRP_INC 1 + +#elif defined(CONFIG_TARGET_E2000) + + #define FCAN_ARB_TSEG1_MIN 1 + #define FCAN_ARB_TSEG1_MAX 16 + #define FCAN_ARB_TSEG2_MIN 1 + #define FCAN_ARB_TSEG2_MAX 8 + #define FCAN_ARB_SJW_MAX 4 + #define FCAN_ARB_BRP_MIN 1 + #define FCAN_ARB_BRP_MAX 8192 + #define FCAN_ARB_BRP_INC 1 + + #define FCAN_DATA_TSEG1_MIN 1 + #define FCAN_DATA_TSEG1_MAX 16 + #define FCAN_DATA_TSEG2_MIN 1 + #define FCAN_DATA_TSEG2_MAX 8 + #define FCAN_DATA_SJW_MAX 4 + #define FCAN_DATA_BRP_MIN 1 + #define FCAN_DATA_BRP_MAX 8192 + #define FCAN_DATA_BRP_INC 1 + +#endif + + +#define FCAN_FIFO_DEPTH 64 + +/** +* This macro reads the given register. +* @param base_addr is the base address of the device. +* @param reg_offset is the register offset to be read. +* @return The 32-bit value of the register +* @note None. +*****************************************************************************/ +#define FCAN_READ_REG32(addr, reg_offset) FtIn32((addr) + (u32)(reg_offset)) + +/****************************************************************************/ +/** +* This macro writes the given register. +* @param base_addr is the base address of the device. +* @param reg_offset 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_WRITE_REG32(addr, reg_offset, reg_value) FtOut32((addr) + (u32)reg_offset, (u32)reg_value) + +#define FCAN_SETBIT(base_addr, reg_offset, data) FtSetBit32((base_addr) + (u32)(reg_offset), (u32)(data)) + +#define FCAN_CLEARBIT(base_addr, reg_offset, data) FtClearBit32((base_addr) + (u32)(reg_offset), (u32)(data)) + + +#define FCAN_TX_FIFO_FULL(instance_p) (FCAN_FIFO_DEPTH == FCAN_FIFO_CNT_TFN_GET(FCAN_READ_REG32(instance_p->config.base_address, FCAN_FIFO_CNT_OFFSET))) + +#define FCAN_RX_FIFO_EMPTY(instance_p) (0 == FCAN_FIFO_CNT_RFN_GET(FCAN_READ_REG32(instance_p->config.base_address, FCAN_FIFO_CNT_OFFSET))) + + +void FCanDump(uintptr base_addr); + +#endif // ! diff --git a/bsp/phytium/libraries/standalone/drivers/can/fcan/fcan_intr.c b/bsp/phytium/libraries/standalone/drivers/can/fcan/fcan_intr.c new file mode 100644 index 0000000000..a792448e73 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/can/fcan/fcan_intr.c @@ -0,0 +1,128 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fcan_intr.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:29:10 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "finterrupt.h" +#include "fcan.h" +#include "fcan_hw.h" +#include "fassert.h" +#include "ftypes.h" +#include "fdebug.h" + +#define FT_CAN_DEBUG_TAG "FT_CAN_INTR" +#define FCAN_DEBUG(format, ...) FT_DEBUG_PRINT_D(FT_CAN_DEBUG_TAG, format, ##__VA_ARGS__) +#define FCAN_INFO(format, ...) FT_DEBUG_PRINT_I(FT_CAN_DEBUG_TAG, format, ##__VA_ARGS__) +#define FCAN_WARN(format, ...) FT_DEBUG_PRINT_W(FT_CAN_DEBUG_TAG, format, ##__VA_ARGS__) +#define FCAN_ERROR(format, ...) FT_DEBUG_PRINT_E(FT_CAN_DEBUG_TAG, format, ##__VA_ARGS__) + +#define FCAN_CALL_INTR_EVENT_HANDLDER(instance_p, event) \ + if (instance_p->intr_event[event].handler) \ + instance_p->intr_event[event].handler(instance_p->intr_event[event].param) + +/** + * @name: FCanRegisterInterruptHandler + * @msg: register FCanCtrl interrupt handler function + * @param {FCanCtrl} *instance_p, pointer to the Can instance + * @param {FCanIntrEventConfig} *intr_event_p, interrupt event type, handler and parameters + * @return {*} + */ +void FCanRegisterInterruptHandler(FCanCtrl *instance_p, FCanIntrEventConfig *intr_event_p) +{ + FASSERT(instance_p); + FASSERT(intr_event_p); + FCanIntrEventType type = intr_event_p->type; + FASSERT(intr_event_p->type < FCAN_INTR_EVENT_NUM); + instance_p->intr_event[type].type = type; + instance_p->intr_event[type].handler = intr_event_p->handler; + instance_p->intr_event[type].param = intr_event_p->param; +} + +/** + * @name: FCanIntrHandler + * @msg: This function is the interrupt handler for the driver. + * It must be connected to an interrupt system by the application such that it + * can be called when an interrupt occurs. + * @param vector Irq num, Don't need attention. + * @param args contains a pointer to the driver instance + * @return {*} + */ +void FCanIntrHandler(s32 vector, void *args) +{ + u32 irq_status; + FCanCtrl *instance_p = (FCanCtrl *)args; + FCanConfig *config_p; + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + config_p = &instance_p->config; + irq_status = FCAN_READ_REG32(config_p->base_address, FCAN_INTR_OFFSET); + + if (0 == irq_status) + { + return; + } + + /* Check for the type of error interrupt and Processing it */ + if (irq_status & FCAN_INTR_TEIS_MASK) + { + irq_status &= ~FCAN_INTR_REIS_MASK; + FCAN_SETBIT(config_p->base_address, FCAN_INTR_OFFSET, + FCAN_INTR_TEIC_MASK | FCAN_INTR_REIC_MASK); + FCAN_CLEARBIT(config_p->base_address, FCAN_CTRL_OFFSET, FCAN_CTRL_XFER_MASK); + FCAN_SETBIT(config_p->base_address, FCAN_CTRL_OFFSET, FCAN_CTRL_TXREQ_MASK); + FCAN_SETBIT(config_p->base_address, FCAN_CTRL_OFFSET, FCAN_CTRL_XFER_MASK); + + FCAN_CALL_INTR_EVENT_HANDLDER(instance_p, FCAN_INTR_EVENT_SEND); + } + + if (irq_status & (FCAN_INTR_EIS_MASK | FCAN_INTR_RFIS_MASK | FCAN_INTR_TFIS_MASK | + FCAN_INTR_BOIS_MASK | FCAN_INTR_PEIS_MASK | FCAN_INTR_PWIS_MASK)) + { + FCAN_CALL_INTR_EVENT_HANDLDER(instance_p, FCAN_INTR_EVENT_ERROR); + + FCAN_SETBIT(config_p->base_address, FCAN_INTR_OFFSET, + (FCAN_INTR_EIC_MASK | FCAN_INTR_RFIC_MASK | FCAN_INTR_BOIC_MASK | + FCAN_INTR_PEIC_MASK | FCAN_INTR_PWIC_MASK)); + + /* Check for rx fifo full interrupt and output error information */ + if (irq_status & FCAN_INTR_RFIS_MASK) + { + FCAN_ERROR("rx_fifo is full!!!"); + /* disable rx fifo full interrupt */ + FCAN_CLEARBIT(config_p->base_address, FCAN_INTR_OFFSET, FCAN_INTR_RFIE_MASK); + } + + if (irq_status & FCAN_INTR_TFIS_MASK) + { + FCAN_ERROR("tx_fifo is empty!!!"); + /* disable tx fifo empty interrupt */ + FCAN_CLEARBIT(config_p->base_address, FCAN_INTR_OFFSET, FCAN_INTR_TFIE_MASK); + } + } + + if (irq_status & FCAN_INTR_REIS_MASK) + { + FCAN_SETBIT(config_p->base_address, FCAN_INTR_OFFSET, FCAN_INTR_REIE_MASK); + FCAN_SETBIT(config_p->base_address, FCAN_INTR_OFFSET, FCAN_INTR_REIC_MASK); + FCAN_SETBIT(config_p->base_address, FCAN_INTR_OFFSET, FCAN_INTR_REIE_MASK); + FCAN_CALL_INTR_EVENT_HANDLDER(instance_p, FCAN_INTR_EVENT_RECV); + } +} diff --git a/bsp/phytium/libraries/standalone/drivers/can/fcan/fcan_sinit.c b/bsp/phytium/libraries/standalone/drivers/can/fcan/fcan_sinit.c new file mode 100644 index 0000000000..15dbb27ba0 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/can/fcan/fcan_sinit.c @@ -0,0 +1,52 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fcan_sinit.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:29:15 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#include "fcan.h" +#include "fparameters.h" +#include "fassert.h" + +extern const FCanConfig FCanConfigTbl[FCAN_INSTANCE_NUM]; + +/** + * @name: FCanLookupConfig + * @msg: get default configuration of specific can instance_id. + * @param {FCanInstance} instance_id, instance id of Can controller + * @return {FCanConfig*} Default configuration parameters of Can + */ +const FCanConfig *FCanLookupConfig(FCanInstance instance_id) +{ + FASSERT(instance_id < FCAN_INSTANCE_NUM); + const FCanConfig *pconfig = NULL; + u32 index; + + for (index = 0; index < (u32)FCAN_INSTANCE_NUM; index++) + { + if (FCanConfigTbl[index].instance_id == instance_id) + { + pconfig = &FCanConfigTbl[index]; + break; + } + } + return (FCanConfig *)pconfig; +} diff --git a/bsp/phytium/libraries/standalone/drivers/dma/Kconfig b/bsp/phytium/libraries/standalone/drivers/dma/Kconfig new file mode 100644 index 0000000000..9ed7f94848 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/dma/Kconfig @@ -0,0 +1,10 @@ + +config ENABLE_FGDMA + bool + prompt "Use FGDMA" + default n + +config ENABLE_FDDMA + bool + prompt "Use FDDMA" + default n \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/dma/fddma/fddma.c b/bsp/phytium/libraries/standalone/drivers/dma/fddma/fddma.c new file mode 100644 index 0000000000..494af32fde --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/dma/fddma/fddma.c @@ -0,0 +1,380 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fddma.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:24:47 + * Description:  This files is for ddma interface implementation + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/5/13 init commit + */ + +/***************************** Include Files *********************************/ +#include + +#include "fkernel.h" +#include "fparameters.h" +#include "fassert.h" +#include "fdebug.h" + +#include "fddma_hw.h" +#include "fddma.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FDDMA_DEBUG_TAG "DDMA" +#define FDDMA_ERROR(format, ...) FT_DEBUG_PRINT_E(FDDMA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FDDMA_WARN(format, ...) FT_DEBUG_PRINT_W(FDDMA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FDDMA_INFO(format, ...) FT_DEBUG_PRINT_I(FDDMA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FDDMA_DEBUG(format, ...) FT_DEBUG_PRINT_D(FDDMA_DEBUG_TAG, format, ##__VA_ARGS__) + +/************************** Function Prototypes ******************************/ +static FError FDdmaReset(FDdma *const instance); + +/****************************************************************************/ +/** + * @name: FDdmaCfgInitialization + * @msg: 初始化DDMA控制器 + * @return {FError} FDDMA_SUCCESS表示初始化成功,其它返回值表示初始化失败 + * @param {FDdma} *instance, DDMA控制器实例 + * @param {FDdmaConfig} *input_config, DDMA控制器配置 + */ +FError FDdmaCfgInitialization(FDdma *const instance, const FDdmaConfig *input_config) +{ + FASSERT(instance && input_config); + uintptr base_addr = input_config->base_addr; + FError ret = FDDMA_SUCCESS; + + if (FT_COMPONENT_IS_READY == instance->is_ready) + { + FDDMA_WARN("device is already initialized!!!"); + } + + FDdmaDeInitialization(instance); + instance->config = *input_config; + + ret = FDdmaReset(instance); + if (FDDMA_SUCCESS == ret) + { + instance->is_ready = FT_COMPONENT_IS_READY; + FDDMA_INFO("ddma@0x%x init success !!!", base_addr); + } + + return ret; +} + +/** + * @name: FDdmaStart + * @msg: 启动DDMA控制器,开始传输 + * @return {FError} FDDMA_SUCCESS表示启动成功,其它返回值表示启动失败 + * @param {FDdma} *instance, DDMA控制器实例 + */ +FError FDdmaStart(FDdma *const instance) +{ + FASSERT(instance); + FError ret = FDDMA_SUCCESS; + uintptr base_addr = instance->config.base_addr; + + if (FT_COMPONENT_IS_READY != instance->is_ready) + { + FDDMA_ERROR("dma instance not init !!!"); + return FDDMA_ERR_NOT_INIT; + } + + FDdmaEnableGlobalIrq(base_addr); /* enable ddma irq */ + FDdmaEnable(base_addr); + return FDDMA_SUCCESS; +} + +/** + * @name: FDdmaStop + * @msg: 停止DDMA控制器 + * @return {FError} FDDMA_SUCCESS表示停止成功,其它返回值表示停止失败 + * @param {FDdma} *instance, DDMA控制器实例 + */ +FError FDdmaStop(FDdma *const instance) +{ + FASSERT(instance); + FError ret = FDDMA_SUCCESS; + uintptr base_addr = instance->config.base_addr; + + if (FT_COMPONENT_IS_READY != instance->is_ready) + { + FDDMA_ERROR("dma instance not init !!!"); + return FDDMA_ERR_NOT_INIT; + } + + FDdmaDisableGlobalIrq(base_addr); /* enable ddma irq */ + FDdmaDisable(base_addr); + return FDDMA_SUCCESS; +} + +/** + * @name: FDdmaDeInitialization + * @msg: 去初始化DDMA控制器 + * @return {无} + * @param {FDdma} *instance, DDMA控制器实例 + */ +void FDdmaDeInitialization(FDdma *const instance) +{ + FASSERT(instance); + u32 chan; + + for (chan = 0; chan < FDDMA_NUM_OF_CHAN; chan++) + { + if (instance->bind_status & BIT(chan)) + { + FDDMA_WARN("channel %d has not been unbind", chan); + } + } + + memset(instance, 0, sizeof(*instance)); + return; +} + +/** + * @name: FDdmaAllocateChan + * @msg: 按照配置分配DDMA通道 + * @return {FError} FDDMA_SUCCESS表示分配成功,其它返回值表示分配失败 + * @param {FDdma} *instance, DDMA控制器实例 + * @param {FDdmaChan} *dma_chan, DDMA通道实例 + * @param {FDdmaChanConfig} *dma_chan_config, DDMA通道配置 + */ +FError FDdmaAllocateChan(FDdma *const instance, FDdmaChan *const dma_chan, const FDdmaChanConfig *dma_chan_config) +{ + FASSERT(instance && dma_chan && dma_chan_config); + FError ret = FDDMA_SUCCESS; + const FDdmaChanIndex chan_idx = dma_chan_config->id; + u32 reg_val; + uintptr base_addr = instance->config.base_addr; + + if (FT_COMPONENT_IS_READY != instance->is_ready) + { + FDDMA_ERROR("dma instance not init !!!"); + return FDDMA_ERR_NOT_INIT; + } + + if ((TRUE == dma_chan->is_used) || (BIT(chan_idx) & instance->bind_status)) + { + FDDMA_ERROR("chan-%d is already is use !!!", chan_idx); + return FDDMA_ERR_CHAN_BINDED; + } + + if (FDdmaIsChanRunning(base_addr, chan_idx)) + { + FDDMA_ERROR("chan-%d is already running !!!", chan_idx); + return FDDMA_ERR_CHAN_BINDED; + } + + if (dma_chan_config->ddr_addr % FDDMA_DDR_ADDR_ALIGMENT) + { + FDDMA_ERROR("ddr addr 0x%x must align with %d bytes", + dma_chan_config->ddr_addr, FDDMA_DDR_ADDR_ALIGMENT); + return FDDMA_ERR_INVALID_DDR_ADDR; + } + + if ((FDDMA_MAX_TRANSFER_LEN < dma_chan_config->trans_len) || + (FDDMA_MIN_TRANSFER_LEN > dma_chan_config->trans_len) || + (0 != dma_chan_config->trans_len % FDDMA_MIN_TRANSFER_LEN)) + { + FDDMA_ERROR("invalid transfer size %d Bytes !!!", dma_chan_config->trans_len); + return FDDMA_ERR_INVALID_TRANS_SIZE; + } + + dma_chan->dma = instance; + instance->chan[chan_idx] = dma_chan; + + if (&(dma_chan->config) != dma_chan_config) + dma_chan->config = *dma_chan_config; + + FDdmaStop(instance); /* disable irq */ + + if (FDDMA_SUCCESS != FDdmaDisableChan(base_addr, chan_idx)) + { + FDDMA_ERROR("disable DDMA@0x%x channel %d failed !!!", base_addr, chan_idx); + return FDDMA_ERR_WAIT_TIMEOUT; + } + + FDdmaResetChan(base_addr, chan_idx); /* reset channel */ + FDdmaSetChanSelection(base_addr, chan_idx, dma_chan->config.slave_id); /* select channel */ + FDdmaSetChanBind(base_addr, chan_idx, TRUE); /* bind channel */ + + /* setup transfer src and dst */ + /* dma_tx_req: ddr --> dev 从内存中读取数据,写入外设 */ + /* dma_rx_req: dev --> ddr 从外设读取数据到内存 */ +#ifdef __aarch64___ + FDdmaWriteReg(base_addr, FDDMA_CHAN_DDR_LOW_ADDR_OFFSET(chan_idx), LOWER_32_BITS(dma_chan_config->ddr_addr)); + FDdmaWriteReg(base_addr, FDDMA_CHAN_DDR_UP_ADDR_OFFSET(chan_idx), UPPER_32_BITS(dma_chan_config->ddr_addr)); +#else + FDdmaWriteReg(base_addr, FDDMA_CHAN_DDR_LOW_ADDR_OFFSET(chan_idx), (u32)(dma_chan_config->ddr_addr)); + FDdmaWriteReg(base_addr, FDDMA_CHAN_DDR_UP_ADDR_OFFSET(chan_idx), 0); +#endif + + FDdmaWriteReg(base_addr, FDDMA_CHAN_DEV_ADDR_OFFSET(chan_idx), dma_chan_config->dev_addr); + FDdmaWriteReg(base_addr, FDDMA_CHAN_TS_OFFSET(chan_idx), dma_chan_config->trans_len); /* block size */ + + /* set channel request direction */ + FDdmaSetChanDirection(base_addr, chan_idx, + (FDDMA_CHAN_REQ_RX == dma_chan->config.req_mode) ? TRUE : FALSE); + + FDDMA_INFO("chan-%d ddr @0x%x", chan_idx, FDDMA_CHAN_DDR_LOW_ADDR_OFFSET(chan_idx)); + FDDMA_INFO("ddr addr: 0x%x", FDdmaReadReg(base_addr, FDDMA_CHAN_DDR_LOW_ADDR_OFFSET(chan_idx))); + FDDMA_INFO("dev addr: 0x%x", FDdmaReadReg(base_addr, FDDMA_CHAN_DEV_ADDR_OFFSET(chan_idx))); + FDDMA_INFO("trans len: %d", FDdmaReadReg(base_addr, FDDMA_CHAN_TS_OFFSET(chan_idx))); + + FDdmaSetChanTimeout(base_addr, chan_idx, 0xffff); + FDdmaEnableChanIrq(base_addr, chan_idx); + + if (FDDMA_SUCCESS == ret) + { + instance->bind_status |= BIT(chan_idx); + dma_chan->is_used = TRUE; + FDDMA_INFO("allocate channel %d", chan_idx); + } + + return ret; +} + +/** + * @name: FDdmaDellocateChan + * @msg: 释放之前分配的DDMA通道 + * @return {FError} FDDMA_SUCCESS表示释放成功,其它返回值表示释放失败 + * @param {FDdmaChan} *dma_chan, DDMA控制器实例 + */ +FError FDdmaDellocateChan(FDdmaChan *const dma_chan) +{ + FASSERT(dma_chan && dma_chan->dma); + FDdma *const instance = dma_chan->dma; + const FDdmaChanIndex chan_idx = dma_chan->config.id; + uintptr base_addr = instance->config.base_addr; + FError ret = FDDMA_SUCCESS; + + if (FT_COMPONENT_IS_READY != instance->is_ready) + { + FDDMA_ERROR("dma instance not init !!!"); + return FDDMA_ERR_NOT_INIT; + } + + if (FDDMA_SUCCESS != FDdmaDisableChan(base_addr, chan_idx)) + { + FDDMA_ERROR("disable DDMA@0x%x channel %d failed !!!", base_addr, chan_idx); + return FDDMA_ERR_WAIT_TIMEOUT; + } + + FDdmaResetChan(base_addr, chan_idx); + + FDdmaSetChanBind(base_addr, chan_idx, FALSE); /* unbind channel */ + ret = FDdmaDisableChan(base_addr, chan_idx); + if (FDDMA_SUCCESS != ret) /* disable channel */ + { + FDDMA_ERROR("disable ddma@%p channel %d failed !!!", base_addr, chan_idx); + return ret; + } + + FDdmaDisableChanIrq(base_addr, chan_idx); /* disable channel irq */ + + instance->bind_status &= ~BIT(chan_idx); /* set bind status */ + instance->chan[chan_idx] = NULL; + + FDDMA_INFO("deallocate channel %d", chan_idx); + memset(dma_chan, 0, sizeof(*dma_chan)); + + return ret; +} + +/** + * @name: FDdmaActiveChan + * @msg: 使能指定的DDMA通道 + * @note: 调用FDdmaAllocateChan后无需调用此函数 + * @return {FError} 返回FDDMA_SUCCESS表示成功,返回其它表示失败 + * @param FDdmaChan *const dma_chan, DDMA通道实例 + */ +FError FDdmaActiveChan(FDdmaChan *const dma_chan) +{ + FASSERT(dma_chan && dma_chan->dma); + FDdma *const instance = dma_chan->dma; + uintptr base_addr = instance->config.base_addr; + + if (FT_COMPONENT_IS_READY != instance->is_ready) + { + FDDMA_ERROR("dma instance not init !!!"); + return FDDMA_ERR_NOT_INIT; + } + + FDdmaEnableChan(base_addr, dma_chan->config.id); + FDdmaClearChanIrq(base_addr, dma_chan->config.id); /* clear interrupt status */ + return FDDMA_SUCCESS; +} + +FError FDdmaDeactiveChan(FDdmaChan *const dma_chan) +{ + FASSERT(dma_chan && dma_chan->dma); + FDdma *const instance = dma_chan->dma; + uintptr base_addr = instance->config.base_addr; + + if (FT_COMPONENT_IS_READY != instance->is_ready) + { + FDDMA_ERROR("dma instance not init !!!"); + return FDDMA_ERR_NOT_INIT; + } + + return FDdmaDisableChan(base_addr, dma_chan->config.id); +} + +/** + * @name: FDdmaReset + * @msg: 重置DDMA控制器 + * @return {FError} FDDMA_SUCCESS表示重置成功,其它返回值表示失败 + * @param {FDdma} *instance, DDMA控制器实例 + */ +static FError FDdmaReset(FDdma *const instance) +{ + FASSERT(instance); + uintptr base_addr = instance->config.base_addr; + FError ret = FDDMA_SUCCESS; + u32 reg_val; + u32 chan; + + if (0 != instance->bind_status) + { + FDDMA_WARN("some channel not yet un-bind !!!"); + } + + FDdmaDisable(base_addr); /* disable ddma */ + FDdmaSoftwareReset(base_addr); /* do software reset */ + FDdmaDisableGlobalIrq(base_addr); + + /* disable channel and its irq */ + for (u32 chan = FDDMA_CHAN_0; chan < FDDMA_NUM_OF_CHAN; chan++) + { + /* disable channel */ + ret = FDdmaDisableChan(base_addr, chan); + if (FDDMA_SUCCESS != ret) + { + FDDMA_ERROR("disable ddma@%p channel %d failed !!!", base_addr, chan); + break; + } + } + + FDdmaDumpRegisters(base_addr); + + return ret; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/dma/fddma/fddma.h b/bsp/phytium/libraries/standalone/drivers/dma/fddma/fddma.h new file mode 100644 index 0000000000..486876a698 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/dma/fddma/fddma.h @@ -0,0 +1,169 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fddma.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:24:52 + * Description:  This files is for ddma interface definition + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/5/13 init commit + */ + +#ifndef DRIVER_FDDMA_H +#define DRIVER_FDDMA_H + +#ifdef __cplusplus +extern "C" +{ +#endif +/***************************** Include Files *********************************/ + +#include "ftypes.h" +#include "ferror_code.h" + +/************************** Constant Definitions *****************************/ +typedef enum +{ + FDDMA_CHAN_0 = 0, + FDDMA_CHAN_1, + FDDMA_CHAN_2, + FDDMA_CHAN_3, + FDDMA_CHAN_4, + FDDMA_CHAN_5, + FDDMA_CHAN_6, + FDDMA_CHAN_7, + + FDDMA_NUM_OF_CHAN +} FDdmaChanIndex; /* DDMA channel index */ + +typedef enum +{ + FDDMA_CHAN_REQ_RX = 0, + FDDMA_CHAN_REQ_TX, +} FDdmaChanRequst; /* DDMA channel direction */ + +typedef enum +{ + FDDMA_CHAN_EVT_REQ_DONE = 0, + FDDMA_CHAN_EVT_FIFO_EMPTY, + FDDMA_CHAN_EVT_FIFO_FULL, + + FDDMA_NUM_OF_CHAN_EVT +} FDdmaChanEvt; /* DDMA channel interrupt event */ + +#define FDDMA_SUCCESS FT_SUCCESS +#define FDDMA_ERR_NOT_INIT FT_MAKE_ERRCODE(ErrModBsp, ErrDdma, 0) +#define FDDMA_ERR_CHAN_BINDED FT_MAKE_ERRCODE(ErrModBsp, ErrDdma, 1) +#define FDDMA_ERR_CHAN_RUNNING FT_MAKE_ERRCODE(ErrModBsp, ErrDdma, 2) +#define FDDMA_ERR_INVALID_TRANS_SIZE FT_MAKE_ERRCODE(ErrModBsp, ErrDdma, 3) +#define FDDMA_ERR_WAIT_TIMEOUT FT_MAKE_ERRCODE(ErrModBsp, ErrDdma, 4) +#define FDDMA_ERR_INVALID_DDR_ADDR FT_MAKE_ERRCODE(ErrModBsp, ErrDdma, 5) + +/**************************** Type Definitions *******************************/ +typedef struct FDdma_ FDdma; +typedef struct FDdmaChan_ FDdmaChan; + +typedef void (*FDdmaChanEvtHandler)(FDdmaChan *const dma_chan, void *arg); /* DMA interrupt event handler */ + +typedef struct +{ + u32 id; /* DDMA ctrl id */ + uintptr base_addr; /* DDMA ctrl base address */ + u32 irq_num; /* DDMA ctrl interrupt id */ + u32 irq_prority; /* DDMA ctrl interrupt priority */ +} FDdmaConfig; /* DDMA instance configuration */ + +typedef struct +{ + FDdmaChanIndex id; /* DMA channel index */ + u32 slave_id; /* Perpherial slave id for DDMA */ + FDdmaChanRequst req_mode; /* DMA transfer direction */ + uintptr ddr_addr; /* DMA channel DDR address, could be source or destination */ + u32 dev_addr; /* DMA channel Perpherial, could be source or destination */ + u32 trans_len; /* DMA channel transfer length */ +#define FDDMA_MAX_TRANSFER_LEN 64 /* max bytes in transfer */ +#define FDDMA_MIN_TRANSFER_LEN 4 /* min bytes in transfer */ + u32 timeout; /* timeout = 0 means no use DMA timeout */ +} FDdmaChanConfig; /* DDMA channel instance */ + +typedef struct FDdmaChan_ +{ + FDdmaChanConfig config; /* DDMA channel configuration */ + boolean is_used; /* TRUE means channel is in use */ + FDdma *dma; /* DMA instance of this channel */ + FDdmaChanEvtHandler evt_handler[FDDMA_NUM_OF_CHAN_EVT]; /* interrupt evt */ + void *evt_handler_args[FDDMA_NUM_OF_CHAN_EVT]; /* interrupt evt args */ +} FDdmaChan; /* DDMA channel instance */ + +typedef struct FDdma_ +{ + FDdmaConfig config; /* DDMA instance configuration */ + FDdmaChan *chan[FDDMA_NUM_OF_CHAN]; /* DDMA channel instance, NULL means channel not yet allocate */ + u32 is_ready; /* TRUE means DDMA init ok */ + u32 bind_status; /* channel bind status, BIT(n) = 1 means channel n is allocated */ +} FDdma; /* DDMA instance */ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FDDMA_DDR_ADDR_ALIGMENT 128 /* DMA DDR Buffer need align wiht 128 bytes */ + +/************************** Function Prototypes ******************************/ +/* 获取DDMA实例默认配置 */ +const FDdmaConfig *FDdmaLookupConfig(u32 instance_id); + +/* 初始化DDMA控制器 */ +FError FDdmaCfgInitialization(FDdma *const instance, const FDdmaConfig *input_config); + +/* 去初始化DDMA控制器 */ +void FDdmaDeInitialization(FDdma *const instance); + +/* 按照配置分配DDMA通道 */ +FError FDdmaAllocateChan(FDdma *const instance, FDdmaChan *const dma_chan, const FDdmaChanConfig *dma_chan_config); + +/* 释放之前分配的DDMA通道 */ +FError FDdmaDellocateChan(FDdmaChan *const dma_chan); + +/* 使能指定的DDMA通道,调用FDdmaAllocateChan后无需调用此函数 */ +FError FDdmaActiveChan(FDdmaChan *const dma_chan); + +/* 去使能DDMA通道 */ +FError FDdmaDeactiveChan(FDdmaChan *const dma_chan); + +/* 启动DDMA控制器并开始传输 */ +FError FDdmaStart(FDdma *const instance); + +/* 停止DDMA控制器 */ +FError FDdmaStop(FDdma *const instance); + +/* DDMA中断处理函数 */ +void FDdmaIrqHandler(s32 vector, void *args); + +/* 注册DDMA通道中断响应事件函数 */ +void FDdmaRegisterChanEvtHandler(FDdmaChan *const dma_chan, + FDdmaChanEvt evt, + FDdmaChanEvtHandler handler, + void *handler_arg); +/* DDMA控制器寄存器自检测试 */ +void FDdmaDumpRegisters(uintptr base_addr); + +void FDdmaDumpChanRegisters(uintptr base_addr, FDdmaChanIndex chan); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/dma/fddma/fddma_g.c b/bsp/phytium/libraries/standalone/drivers/dma/fddma/fddma_g.c new file mode 100644 index 0000000000..4204d9d23e --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/dma/fddma/fddma_g.c @@ -0,0 +1,57 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fddma_g.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:24:47 + * Description:  This files is for ddma static configuration + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/5/13 init commit + */ + +/***************************** Include Files *********************************/ +#include "fparameters.h" + +#include "fddma_hw.h" +#include "fddma.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ +const FDdmaConfig fddma_cfg_tbl[FDDMA_INSTANCE_NUM] = +{ + [FDDMA0_ID] = + { + .id = FDDMA0_ID, + .base_addr = FDDMA0_BASE_ADDR, + .irq_num = FDDMA0_IRQ_NUM, + .irq_prority = 0 + }, + + [FDDMA1_ID] = + { + .id = FDDMA1_ID, + .base_addr = FDDMA1_BASE_ADDR, + .irq_num = FDDMA1_IRQ_NUM, + .irq_prority = 0 + } +}; /* DDMA控制器默认配置 */ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/dma/fddma/fddma_hw.c b/bsp/phytium/libraries/standalone/drivers/dma/fddma/fddma_hw.c new file mode 100644 index 0000000000..dfbe76576e --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/dma/fddma/fddma_hw.c @@ -0,0 +1,372 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fddma_hw.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:24:47 + * Description:  This files is for ddma register rw operations + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/5/13 init commit + */ + +/***************************** Include Files *********************************/ +#include + +#include "fkernel.h" +#include "fparameters.h" +#include "fassert.h" +#include "fdebug.h" + +#include "fddma_hw.h" +#include "fddma.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FDDMA_DEBUG_TAG "DDMA-HW" +#define FDDMA_ERROR(format, ...) FT_DEBUG_PRINT_E(FDDMA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FDDMA_WARN(format, ...) FT_DEBUG_PRINT_W(FDDMA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FDDMA_INFO(format, ...) FT_DEBUG_PRINT_I(FDDMA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FDDMA_DEBUG(format, ...) FT_DEBUG_PRINT_D(FDDMA_DEBUG_TAG, format, ##__VA_ARGS__) + +/************************** Function Prototypes ******************************/ + +/****************************************************************************/ +/** + * @name: FDdmaDisable + * @msg: 去使能DDMA控制器 + * @return {*} + * @param {uintptr} base_addr, DDMA控制器基地址 + */ +void FDdmaDisable(uintptr base_addr) +{ + u32 reg_val = FDdmaReadReg(base_addr, FDDMA_CTL_OFFSET); + reg_val &= ~FDDMA_CTL_ENABLE; + FDdmaWriteReg(base_addr, FDDMA_CTL_OFFSET, reg_val); + FDDMA_DEBUG("ddma @%p disabled : 0x%x", base_addr, FDdmaReadReg(base_addr, FDDMA_CTL_OFFSET)); + return; +} + +/** + * @name: FDdmaEnable + * @msg: 使能DDMA控制器 + * @return {*} + * @param {uintptr} base_addr, DDMA控制器基地址 + */ +void FDdmaEnable(uintptr base_addr) +{ + u32 reg_val = FDdmaReadReg(base_addr, FDDMA_CTL_OFFSET); + reg_val |= FDDMA_CTL_ENABLE; + FDdmaWriteReg(base_addr, FDDMA_CTL_OFFSET, reg_val); + FDDMA_DEBUG("ddma @%p enabled : 0x%x", base_addr, FDdmaReadReg(base_addr, FDDMA_CTL_OFFSET)); + return; +} + +/** + * @name: FDdmaSoftwareReset + * @msg: 复位DDMA控制器 + * @return {*} + * @param {uintptr} base_addr, DDMA控制器基地址 + */ +void FDdmaSoftwareReset(uintptr base_addr) +{ + int delay = 10000; + u32 reg_val = FDdmaReadReg(base_addr, FDDMA_CTL_OFFSET); + reg_val |= FDDMA_CTL_SRST; + FDdmaWriteReg(base_addr, FDDMA_CTL_OFFSET, reg_val); + FDDMA_DEBUG("ddma @%p software reset start : 0x%x", base_addr, FDdmaReadReg(base_addr, FDDMA_CTL_OFFSET)); + + while (--delay > 0) /* wait a while to do reset */ + ; + + reg_val &= ~FDDMA_CTL_SRST; + FDdmaWriteReg(base_addr, FDDMA_CTL_OFFSET, reg_val); /* exit from software reset */ + FDDMA_DEBUG("ddma @%p software reset end : 0x%x", base_addr, FDdmaReadReg(base_addr, FDDMA_CTL_OFFSET)); + return; +} + +/** + * @name: FDdmaDisableGlobalIrq + * @msg: 关闭DDMA全局中断 + * @return {*} + * @param {uintptr} base_addr, DDMA控制器基地址 + */ +void FDdmaDisableGlobalIrq(uintptr base_addr) +{ + u32 reg_val = FDdmaReadReg(base_addr, FDDMA_MASK_INTR_OFFSET); + reg_val |= FDDMA_MASK_EN_GLOBAL_INTR; /* write 1 and disable interrupt */ + FDdmaWriteReg(base_addr, FDDMA_MASK_INTR_OFFSET, reg_val); + return; +} + +/** + * @name: FDdmaEnableGlobalIrq + * @msg: 打开DDMA全局中断 + * @return {*} + * @param {uintptr} base_addr, DDMA控制器基地址 + */ +void FDdmaEnableGlobalIrq(uintptr base_addr) +{ + u32 reg_val = FDdmaReadReg(base_addr, FDDMA_MASK_INTR_OFFSET); + reg_val &= ~FDDMA_MASK_EN_GLOBAL_INTR; /* write 0 and enable interrupt */ + FDdmaWriteReg(base_addr, FDDMA_MASK_INTR_OFFSET, reg_val); + return; +} + +/** + * @name: FDdmaDisableChanIrq + * @msg: 关闭DDMA通道中断 + * @return {*} + * @param {uintptr} base_addr, DDMA控制器基地址 + * @param {u32} chan, DDMA通道号 + */ +void FDdmaDisableChanIrq(uintptr base_addr, u32 chan) +{ + FASSERT_MSG((FDDMA_NUM_OF_CHAN > chan), "chan %d not support", chan); + u32 reg_val = FDdmaReadReg(base_addr, FDDMA_MASK_INTR_OFFSET); + reg_val |= FDDMA_MASK_EN_CHAN_INTR(chan); + FDdmaWriteReg(base_addr, FDDMA_MASK_INTR_OFFSET, reg_val); + return; +} + +/** + * @name: FDdmaEnableChanIrq + * @msg: 打开DDMA通道中断 + * @return {*} + * @param {uintptr} base_addr, DDMA控制器基地址 + * @param {u32} chan, DDMA通道号 + */ +void FDdmaEnableChanIrq(uintptr base_addr, u32 chan) +{ + FASSERT_MSG((FDDMA_NUM_OF_CHAN > chan), "chan %d not support", chan); + u32 reg_val = FDdmaReadReg(base_addr, FDDMA_MASK_INTR_OFFSET); + reg_val &= ~FDDMA_MASK_EN_CHAN_INTR(chan); /* write 0 and enable */ + FDdmaWriteReg(base_addr, FDDMA_MASK_INTR_OFFSET, reg_val); + return; +} + +/** + * @name: FDdmaDisableChan + * @msg: 去使能DDMA通道 + * @return {FError} FDDMA_SUCCESS 表示去使能成功 + * @param {uintptr} base_addr, DDMA控制器基地址 + * @param {u32} chan, DDMA通道号 + */ +FError FDdmaDisableChan(uintptr base_addr, u32 chan) +{ + FASSERT_MSG((FDDMA_NUM_OF_CHAN > chan), "chan %d not support", chan); + int delay = 1000; + u32 reg_val = FDdmaReadReg(base_addr, FDDMA_CHAN_CTL_OFFSET(chan)); + reg_val &= ~FDDMA_CHAN_CTL_EN; + FDdmaWriteReg(base_addr, FDDMA_CHAN_CTL_OFFSET(chan), reg_val); + + /* 先写该位 1’b0,随后读取到该位是 1’b0 的时候,才能复位该通道 */ + do + { + reg_val = FDdmaReadReg(base_addr, FDDMA_CHAN_CTL_OFFSET(chan)); + if (delay-- <= 0) + break; + } + while (reg_val & FDDMA_CHAN_CTL_EN); + + FDDMA_DEBUG("ddma @%p chan %d disabled : 0x%x", base_addr, chan, FDdmaReadReg(base_addr, FDDMA_CHAN_CTL_OFFSET(chan))); + return (delay > 0) ? FDDMA_SUCCESS : FDDMA_ERR_WAIT_TIMEOUT; +} + +/** + * @name: FDdmaEnableChan + * @msg: 使能DDMA通道 + * @return {*} + * @param {uintptr} base_addr, DDMA控制器基地址 + * @param {u32} chan, DDMA通道号 + */ +void FDdmaEnableChan(uintptr base_addr, u32 chan) +{ + FASSERT_MSG((FDDMA_NUM_OF_CHAN > chan), "chan %d not support", chan); + u32 reg_val = FDdmaReadReg(base_addr, FDDMA_CHAN_CTL_OFFSET(chan)); + reg_val |= FDDMA_CHAN_CTL_EN; + FDdmaWriteReg(base_addr, FDDMA_CHAN_CTL_OFFSET(chan), reg_val); + FDDMA_DEBUG("ddma @%p chan %d enabled : 0x%x", base_addr, chan, FDdmaReadReg(base_addr, FDDMA_CHAN_CTL_OFFSET(chan))); + return; +} + +/** + * @name: FDdmaClearChanIrq + * @msg: 清除DDMA通道中断状态 + * @return {*} + * @param {uintptr} base_addr, DDMA控制器基地址 + * @param {u32} chan, DDMA通道号 + */ +void FDdmaClearChanIrq(uintptr base_addr, u32 chan) +{ + FASSERT_MSG((FDDMA_NUM_OF_CHAN > chan), "chan %d not support", chan); + /* write 1 to clear irq status of channel */ + FDdmaWriteReg(base_addr, FDDMA_STA_OFFSET, FDDMA_STA_CHAN_REQ_DONE(chan)); +} + +/** + * @name: FDdmaResetChan + * @msg: 重置DDMA通道 + * @return {*} + * @param {uintptr} base_addr, DDMA控制器基地址 + * @param {u32} chan, DDMA通道号 + */ +void FDdmaResetChan(uintptr base_addr, u32 chan) +{ + FASSERT_MSG((FDDMA_NUM_OF_CHAN > chan), "chan %d not support", chan); + int delay = 1000; + u32 reg_val; + + if (FDdmaIsChanRunning(base_addr, chan)) /* disable channel if running */ + (void)FDdmaDisableChan(base_addr, chan); + + reg_val = FDdmaReadReg(base_addr, FDDMA_CHAN_CTL_OFFSET(chan)); + reg_val |= FDDMA_CHAN_CTL_SRST; + FDdmaWriteReg(base_addr, FDDMA_CHAN_CTL_OFFSET(chan), reg_val); + + while (--delay > 0) /* wait a while to do reset */ + ; + + reg_val &= ~FDDMA_CHAN_CTL_SRST; + FDdmaWriteReg(base_addr, FDDMA_CHAN_CTL_OFFSET(chan), reg_val); + FDDMA_DEBUG("chan reset done, ctrl: 0x%x", FDdmaReadReg(base_addr, FDDMA_CHAN_CTL_OFFSET(chan))); + return; +} + +/** + * @name: FDdmaIsChanRunning + * @msg: 检查通道是否在工作中 + * @return {boolean} TRUE: 在工作中 + * @param {uintptr} base_addr, DDMA控制器基地址 + * @param {u32} chan, DDMA通道号 + */ +boolean FDdmaIsChanRunning(uintptr base_addr, u32 chan) +{ + FASSERT_MSG((FDDMA_NUM_OF_CHAN > chan), "chan %d not support", chan); + u32 reg_val = FDdmaReadReg(base_addr, FDDMA_CHAN_CTL_OFFSET(chan)); + return (FDDMA_CHAN_CTL_EN & reg_val) ? TRUE : FALSE; +} + +/** + * @name: FDdmaSetChanSelection + * @msg: 将DDMA通道与外设绑定 + * @return {*} + * @param {uintptr} base_addr, DDMA控制器基地址 + * @param {u32} chan, DDMA通道号 + * @param {u32} slave_id, 外设对应的slave id + */ +void FDdmaSetChanSelection(uintptr base_addr, u32 chan, u32 slave_id) +{ + FASSERT_MSG((FDDMA_NUM_OF_CHAN > chan), "chan %d not support", chan); + FASSERT_MSG((FDDMA_MAX_SLAVE_ID >= slave_id), "invalid slave id %d", slave_id); + u32 reg_val; + + if (FDDMA_CHAN_4 > chan) + { + reg_val = FDdmaReadReg(base_addr, FDDMA_CHAN_0_3_CFG_OFFSET); + reg_val &= ~FDDMA_CHAN_0_3_SEL_MASK(chan); + reg_val |= FDDMA_CHAN_0_3_SEL(chan, slave_id); + reg_val |= FDDMA_CHAN_0_3_SEL_EN(chan); + FDdmaWriteReg(base_addr, FDDMA_CHAN_0_3_CFG_OFFSET, reg_val); + FDDMA_DEBUG("ddma@%p set chan-%d slave id-%d, 0x%x", base_addr, chan, slave_id, FDdmaReadReg(base_addr, FDDMA_CHAN_0_3_CFG_OFFSET)); + } + else + { + reg_val = FDdmaReadReg(base_addr, FDDMA_CHAN_4_7_CFG_OFFSET); + reg_val &= ~FDDMA_CHAN_4_7_SEL_MASK(chan); + reg_val |= FDDMA_CHAN_4_7_SEL(chan, slave_id); + reg_val |= FDDMA_CHAN_4_7_SEL_EN(chan); + FDdmaWriteReg(base_addr, FDDMA_CHAN_4_7_CFG_OFFSET, reg_val); + FDDMA_DEBUG("ddma@%p set chan-%d slave id-%d, 0x%x", base_addr, chan, slave_id, FDdmaReadReg(base_addr, FDDMA_CHAN_4_7_CFG_OFFSET)); + } + + return; +} + +/** + * @name: FDdmaSetChanBind + * @msg: 修改通道的绑定状态 + * @return {*} + * @param {uintptr} base_addr, DDMA控制器基地址 + * @param {u32} chan, DDMA通道号 + * @param {boolean} bind, TRUE: 绑定,FALSE: 解除绑定 + */ +void FDdmaSetChanBind(uintptr base_addr, u32 chan, boolean bind) +{ + FASSERT_MSG((FDDMA_NUM_OF_CHAN > chan), "chan %d not support", chan); + u32 reg_val = FDdmaReadReg(base_addr, FDDMA_CHAN_BIND_OFFSET); + + if (bind) + reg_val |= BIT(chan); + else + reg_val &= ~BIT(chan); + + FDDMA_DEBUG("ddma@%p %s chan-%d, 0x%x", base_addr, bind ? "bind" : "unbind", chan, FDdmaReadReg(base_addr, FDDMA_CHAN_BIND_OFFSET)); + FDdmaWriteReg(base_addr, FDDMA_CHAN_BIND_OFFSET, reg_val); + return; +} + +/** + * @name: FDdmaSetChanDirection + * @msg: 设置通道的方向 + * @return {*} + * @param {uintptr} base_addr, DDMA控制器基地址 + * @param {u32} chan, DDMA通道号 + * @param {boolean} is_rx, TRUE: 接收, FALSE: 发送 + */ +void FDdmaSetChanDirection(uintptr base_addr, u32 chan, boolean is_rx) +{ + FASSERT_MSG((FDDMA_NUM_OF_CHAN > chan), "chan %d not support", chan); + u32 reg_val = FDdmaReadReg(base_addr, FDDMA_CHAN_CTL_OFFSET(chan)); + if (is_rx) + reg_val |= FDDMA_CHAN_CTL_RX_MODE; /* device to memory */ + else + reg_val &= ~FDDMA_CHAN_CTL_RX_MODE; /* memory to device */ + FDdmaWriteReg(base_addr, FDDMA_CHAN_CTL_OFFSET(chan), reg_val); + return; +} + +/** + * @name: FDdmaSetChanTimeout + * @msg: 设置通道超时 + * @return {*} + * @param {uintptr} base_addr, DDMA控制器基地址 + * @param {u32} chan, DDMA通道号 + * @param {u32} timeout, 超时设置,0表示不启用超时 + */ +void FDdmaSetChanTimeout(uintptr base_addr, u32 chan, u32 timeout) +{ + FASSERT_MSG((FDDMA_NUM_OF_CHAN > chan), "chan %d not support", chan); + u32 reg_val = FDdmaReadReg(base_addr, FDDMA_CHAN_TIMEOUT_CNT_OFFSET(chan)); + + if (0 < timeout) + { + reg_val &= ~FDDMA_CHAN_TIMEOUT_CNT_MASK; + reg_val |= FDDMA_CHAN_TIMEOUT_CNT_SET(timeout); + reg_val |= FDDMA_CHAN_TIMEOUT_EN; + } + else + { + reg_val &= ~FDDMA_CHAN_TIMEOUT_EN; + } + + FDdmaWriteReg(base_addr, FDDMA_CHAN_TIMEOUT_CNT_OFFSET(chan), reg_val); + return; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/dma/fddma/fddma_hw.h b/bsp/phytium/libraries/standalone/drivers/dma/fddma/fddma_hw.h new file mode 100644 index 0000000000..2334abd184 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/dma/fddma/fddma_hw.h @@ -0,0 +1,223 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fddma_hw.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:24:52 + * Description:  This files is for register definition + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/5/13 init commit + */ + +#ifndef DRIVERS_FDDMA_HW_H +#define DRIVERS_FDDMA_HW_H + +#ifdef __cplusplus +extern "C" +{ +#endif +/***************************** Include Files *********************************/ + +#include "fassert.h" +#include "ftypes.h" +#include "fio.h" +#include "fkernel.h" + +/************************** Constant Definitions *****************************/ +/** @name Register Map + * + * Register offsets from the base address of an GPIO device. + * @{ + */ +#define FDDMA_CTL_OFFSET 0x0 /* 全局控制类寄存器 */ +#define FDDMA_CHAN_0_3_CFG_OFFSET 0x4 /* DMA通道0~3选择配置寄存器 */ +#define FDDMA_STA_OFFSET 0x8 /* 中断状态寄存器 */ +#define FDDMA_MASK_INTR_OFFSET 0xC /* 中断掩码寄存器 */ +/* AXI读写相关的配置硬件暂未开放 */ +#define FDDMA_UP_AXI_AW_CFG_OFFSET 0x10 /* 上行AXI写通道配置寄存器 */ +#define FDDMA_UP_AXI_AR_CFG_OFFSET 0x14 /* 上行AXI读通道配置寄存器 */ +#define FDDMA_DOWN_AXI_AW_CFG_OFFSET 0x18 /* 下行AXI写通道配置寄存器 */ +#define FDDMA_DOWN_AXI_AR_CFG_OFFSET 0x1C /* 下行AXI读通道配置寄存器 */ + +#define FDDMA_CHAN_BIND_OFFSET 0x20 /* DMA通道绑定寄存器 */ +#define FDDMA_GCAP_OFFSET 0x24 /* DMA通道数寄存器(只读) */ +#define FDDMA_CHAN_4_7_CFG_OFFSET 0x28 /* DMA通道4~7选择配置寄存器 */ + +#define FDDMA_CHAN_OFFSET(chan) (0x40 * (chan)) +#define FDDMA_CHAN_DDR_UP_ADDR_OFFSET(chan) (0x40 + FDDMA_CHAN_OFFSET(chan)) /* 内存中源/目的地址高32位 */ +#define FDDMA_CHAN_DDR_LOW_ADDR_OFFSET(chan) (0x44 + FDDMA_CHAN_OFFSET(chan)) /* 内存中源/目的地址低32位 */ +#define FDDMA_CHAN_DEV_ADDR_OFFSET(chan) (0x48 + FDDMA_CHAN_OFFSET(chan)) /* 设备中源/目的地址32位 */ +#define FDDMA_CHAN_TS_OFFSET(chan) (0x4C + FDDMA_CHAN_OFFSET(chan)) /* 需要传输的总数据字节数 */ +#define FDDMA_CHAN_CRT_UP_ADDR_OFFSET(chan) (0x50 + FDDMA_CHAN_OFFSET(chan)) /* 当前内存需要读写数据的高32位 */ +#define FDDMA_CHAN_CRT_LOW_ADDR_OFFSET(chan) (0x54 + FDDMA_CHAN_OFFSET(chan)) /* 当前内存需要读写数据的低32位 */ +#define FDDMA_CHAN_CTL_OFFSET(chan) (0x58 + FDDMA_CHAN_OFFSET(chan)) /* 通道控制寄存器 */ +#define FDDMA_CHAN_STS_OFFSET(chan) (0x5C + FDDMA_CHAN_OFFSET(chan)) /* 通道状态寄存器 */ +#define FDDMA_CHAN_TIMEOUT_CNT_OFFSET(chan) (0x60 + FDDMA_CHAN_OFFSET(chan)) /* 超时等待阈值 */ + +/** @name FDDMA_CTL_OFFSET Register + */ +#define FDDMA_CTL_SRST BIT(1) /* 1: 全局软复位 */ +#define FDDMA_CTL_ENABLE BIT(0) /* 1: 全局使能控制 */ + +/** @name FDDMA_CHAN_0_3_CFG_OFFSET Register + */ +#define FDDMA_CHAN_0_3_SEL_EN(chan) BIT((chan) * 8 + 7) /* 1: 通道chan信号源选择有效 */ +#define FDDMA_CHAN_0_3_SEL(chan, sel) ((sel) << ((chan) * 8)) /* 通道chan信号源选择sel项 */ +#define FDDMA_CHAN_0_3_SEL_MASK(chan) (GENMASK(7, 0) << ((chan) * 8)) + +/** @name FDDMA_STA_OFFSET Register + */ +#define FDDMA_STA_CHAN_REQ_DONE(chan) BIT((chan) * 4) /* 通道chan的block请求完成时置1,写1后清0 */ + +/** @name FDDMA_MASK_INTR_OFFSET Register + */ +#define FDDMA_MASK_EN_GLOBAL_INTR BIT(31) /* 全局中断使能输出控制位 */ +#define FDDMA_MASK_EN_CHAN_INTR(chan) BIT(chan) /* 通道chan的中断输出控制位 */ + +/** @name FDDMA_CHAN_BIND_OFFSET Register + */ +#define FDDMA_CHAN_BIND(chan) BIT(chan) /* 1: 通道已绑定了外设DMA请求信号线 */ + +/** @name FDDMA_CHAN_4_7_CFG_OFFSET Register + */ +#define FDDMA_CHAN_4_7_SEL_EN(chan) BIT(((chan) - 4) * 8 + 7) /* 1: 通道chan信号源选择有效 */ +#define FDDMA_CHAN_4_7_SEL(chan, sel) ((sel) << (((chan) - 4) * 8)) /* 通道chan信号源选择sel项 */ +#define FDDMA_CHAN_4_7_SEL_MASK(chan) (GENMASK(7, 0) << (((chan) - 4) * 8)) + +/** @name FDDMA_CHAN_CTL_OFFSET Register + */ +#define FDDMA_CHAN_CTL_RX_MODE BIT(2) /* 1:接收外设 dma_rx_req, 0:接收外设 dma_tx_req */ +#define FDDMA_CHAN_CTL_SRST BIT(1) /* 1: 复位通道 */ +#define FDDMA_CHAN_CTL_EN BIT(0) /* 1: 使能通道 */ + +/** @name FDDMA_CHAN_STS_OFFSET Register + */ +#define FDDMA_CHAN_STS_FIFO_EMPTY BIT(1) /* 1: FIFO空状态 */ +#define FDDMA_CHAN_STS_FIFO_FULL BIT(0) /* 1: FIFO满状态 */ + +/** @name FDDMA_CHAN_TIMEOUT_CNT_OFFSET Register + */ +#define FDDMA_CHAN_TIMEOUT_EN BIT(32) /* 1: 使能超时机制 */ +#define FDDMA_CHAN_TIMEOUT_CNT_SET(cnt) SET_REG32_BITS((cnt), 27, 0) /* 超时阈值 */ +#define FDDMA_CHAN_TIMEOUT_CNT_MASK GENMASK(27, 0) +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +/** + * @name: FDdmaReadReg + * @msg: 读DDMA寄存器 + * @return {u32} 获取的寄存器值 + * @param {uintptr} base_addr, DDMA寄存器基地址 + * @param {u32} reg_off, DDMA寄存器偏移量 + */ +static inline u32 FDdmaReadReg(uintptr base_addr, u32 reg_off) +{ + return FtIn32(base_addr + reg_off); +} + +/** + * @name: FDdmaWriteReg + * @msg: 写DDMA寄存器 + * @return {无} + * @param {uintptr} base_addr, DDMA寄存器基地址 + * @param {u32} reg_off, DDMA寄存器偏移量 + * @param {u32} reg_val, 设置的寄存器值 + */ +static inline void FDdmaWriteReg(uintptr base_addr, u32 reg_off, u32 reg_val) +{ + FtOut32(base_addr + reg_off, reg_val); + return; +} + +/** + * @name: FDdmaReadStatus + * @msg: 获取DDMA中断状态 + * @return {u32} 中断状态 + * @param {uintptr} base_addr, DDMA寄存器基地址 + */ +static inline u32 FDdmaReadStatus(uintptr base_addr) +{ + return FDdmaReadReg(base_addr, FDDMA_STA_OFFSET); +} + +/** + * @name: FDdmaReadChanStatus + * @msg: 获取DDMA通道中断状态 + * @return {u32} 中断状态 + * @param {uintptr} base_addr, DDMA寄存器基地址 + * @param {u32} chan, DDMA通道号 + */ +static inline u32 FDdmaReadChanStatus(uintptr base_addr, u32 chan) +{ + return FDdmaReadReg(base_addr, FDDMA_CHAN_STS_OFFSET(chan)); +} + +/************************** Function Prototypes ******************************/ +/* 复位DDMA控制器 */ +void FDdmaSoftwareReset(uintptr base_addr); + +/* 去使能DDMA控制器 */ +void FDdmaDisable(uintptr base_addr); + +/* 使能DDMA控制器 */ +void FDdmaEnable(uintptr base_addr); + +/* 重置DDMA通道 */ +void FDdmaResetChan(uintptr base_addr, u32 chan); + +/* 去使能DDMA通道 */ +FError FDdmaDisableChan(uintptr base_addr, u32 chan); + +/* 使能DDMA通道 */ +void FDdmaEnableChan(uintptr base_addr, u32 chan); + +/* 关闭DDMA全局中断 */ +void FDdmaDisableGlobalIrq(uintptr base_addr); + +/* 打开DDMA全局中断 */ +void FDdmaEnableGlobalIrq(uintptr base_addr); + +/* 关闭DDMA通道中断 */ +void FDdmaDisableChanIrq(uintptr base_addr, u32 chan); + +/* 打开DDMA通道中断 */ +void FDdmaEnableChanIrq(uintptr base_addr, u32 chan); + +/* 清除DDMA通道中断状态 */ +void FDdmaClearChanIrq(uintptr base_addr, u32 chan); + +/* 检查通道是否在工作中 */ +boolean FDdmaIsChanRunning(uintptr base_addr, u32 chan); + +/* 将DDMA通道与外设绑定 */ +void FDdmaSetChanSelection(uintptr base_addr, u32 chan, u32 slave_id); + +/* 修改通道的绑定状态 */ +void FDdmaSetChanBind(uintptr base_addr, u32 chan, boolean bind); + +/* 设置通道的方向 */ +void FDdmaSetChanDirection(uintptr base_addr, u32 chan, boolean is_rx); + +/* 设置通道超时 */ +void FDdmaSetChanTimeout(uintptr base_addr, u32 chan, u32 timeout); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/dma/fddma/fddma_intr.c b/bsp/phytium/libraries/standalone/drivers/dma/fddma/fddma_intr.c new file mode 100644 index 0000000000..c71ba37018 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/dma/fddma/fddma_intr.c @@ -0,0 +1,141 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fddma_intr.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:24:47 + * Description:  This files is for ddma interrupt implementation + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/5/13 init commit + */ + +/***************************** Include Files *********************************/ +#include + +#include "fkernel.h" +#include "fparameters.h" +#include "fassert.h" +#include "fdebug.h" + +#include "fddma_hw.h" +#include "fddma.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FDDMA_DEBUG_TAG "DDMA-INTR" +#define FDDMA_ERROR(format, ...) FT_DEBUG_PRINT_E(FDDMA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FDDMA_WARN(format, ...) FT_DEBUG_PRINT_W(FDDMA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FDDMA_INFO(format, ...) FT_DEBUG_PRINT_I(FDDMA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FDDMA_DEBUG(format, ...) FT_DEBUG_PRINT_D(FDDMA_DEBUG_TAG, format, ##__VA_ARGS__) + +#define FDDMA_CALL_EVT_HANDLER(express, dma_chan, args) \ + if (express) \ + { \ + express(dma_chan, args); \ + } + +/************************** Function Prototypes ******************************/ + +/****************************************************************************/ +/** + * @name: FDdmaChanIrqHandler + * @msg: DDMA通道中断处理函数 + * @return {*} + * @param {FDdma} *instance, DDMA实例 + * @param {FDdmaChanIndex} chan_idx, DDMA通道号 + */ +static void FDdmaChanIrqHandler(FDdma *const instance, FDdmaChanIndex chan_idx) +{ + FASSERT(instance && instance->chan[chan_idx]); + FDdmaChan *const dma_chan = instance->chan[chan_idx]; + uintptr base_addr = instance->config.base_addr; + u32 status = FDdmaReadChanStatus(base_addr, chan_idx); + + FDDMA_INFO("chan-%d irq status: 0x%x", chan_idx, status); + + FDDMA_CALL_EVT_HANDLER(dma_chan->evt_handler[FDDMA_CHAN_EVT_REQ_DONE], dma_chan, + dma_chan->evt_handler_args[FDDMA_CHAN_EVT_REQ_DONE]); + + if (FDDMA_CHAN_STS_FIFO_EMPTY & status) + { + FDDMA_CALL_EVT_HANDLER(dma_chan->evt_handler[FDDMA_CHAN_EVT_FIFO_EMPTY], dma_chan, + dma_chan->evt_handler_args[FDDMA_CHAN_EVT_FIFO_EMPTY]); + } + + if (FDDMA_CHAN_STS_FIFO_FULL & status) + { + FDDMA_CALL_EVT_HANDLER(dma_chan->evt_handler[FDDMA_CHAN_EVT_FIFO_FULL], dma_chan, + dma_chan->evt_handler_args[FDDMA_CHAN_EVT_FIFO_FULL]); + } + + /* submit queued descriptor after processing the completed ones */ + return; +} + +/** + * @name: FDdmaIrqHandler + * @msg: DDMA中断处理函数 + * @return {无} + * @param {s32} vector + * @param {void} *param, 输入参数 + */ +void FDdmaIrqHandler(s32 vector, void *args) +{ + FASSERT(NULL != args); + FDdma *const instance = (FDdma * const)args; + uintptr base_addr = instance->config.base_addr; + u32 status = FDdmaReadStatus(base_addr); + u32 chan; + + FDDMA_INFO("ddma irq 0x%x", status); + FDdmaDisableGlobalIrq(base_addr); /* disable interrupt from occur again */ + + /* poll, clear and process every chanel interrupt status */ + for (chan = FDDMA_CHAN_0; chan < FDDMA_NUM_OF_CHAN; chan++) + { + if (0 == (FDDMA_STA_CHAN_REQ_DONE(chan) & status)) + continue; + + FDDMA_INFO("handle chan %d", chan); + FDdmaClearChanIrq(base_addr, chan); /* clear interrupt status */ + FDdmaChanIrqHandler(instance, chan); /* channel interrupt handle */ + } + + FDdmaEnableGlobalIrq(base_addr); /* re-enable interrupt */ + return; +} + +/** + * @name: FDdmaRegisterChanEvtHandler + * @msg: 注册DDMA通道中断响应事件函数 + * @return {无} + * @param {FDdmaChan} *dma_chan, DDMA通道 + * @param {FDdmaChanEvt} evt, 中断事件 + * @param {FDdmaChanEvtHandler} handler, 中断响应事件函数 + * @param {void} *handler_arg, 中断响应事件函数输入参数 + */ +void FDdmaRegisterChanEvtHandler(FDdmaChan *const dma_chan, FDdmaChanEvt evt, FDdmaChanEvtHandler handler, void *handler_arg) +{ + FASSERT(dma_chan); + dma_chan->evt_handler[evt] = handler; + dma_chan->evt_handler_args[evt] = handler_arg; + return; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/dma/fddma/fddma_selftest.c b/bsp/phytium/libraries/standalone/drivers/dma/fddma/fddma_selftest.c new file mode 100644 index 0000000000..dfe9dea6a5 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/dma/fddma/fddma_selftest.c @@ -0,0 +1,85 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fddma_selftest.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:24:47 + * Description:  This files is for ddma self test + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/5/13 init commit + */ + +/***************************** Include Files *********************************/ +#include + +#include "fkernel.h" +#include "fparameters.h" +#include "fassert.h" +#include "fdebug.h" + +#include "fddma_hw.h" +#include "fddma.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FDDMA_DEBUG_TAG "DDMA-TEST" +#define FDDMA_ERROR(format, ...) FT_DEBUG_PRINT_E(FDDMA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FDDMA_WARN(format, ...) FT_DEBUG_PRINT_W(FDDMA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FDDMA_INFO(format, ...) FT_DEBUG_PRINT_I(FDDMA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FDDMA_DEBUG(format, ...) FT_DEBUG_PRINT_D(FDDMA_DEBUG_TAG, format, ##__VA_ARGS__) + +#define FDDMA_DUMPER(base_addr, reg_off, reg_name) \ + FDDMA_DEBUG("\t\t[%s]@0x%x\t=\t0x%x", reg_name, (reg_off), FDdmaReadReg((base_addr), (reg_off))) +/************************** Function Prototypes ******************************/ + +/****************************************************************************/ + + +void FDdmaDumpRegisters(uintptr base_addr) +{ + FDDMA_DEBUG("ddma@0x%x", base_addr); + FDDMA_DUMPER(base_addr, FDDMA_CTL_OFFSET, "ctl"); + FDDMA_DUMPER(base_addr, FDDMA_CHAN_0_3_CFG_OFFSET, "chan_0_3"); + FDDMA_DUMPER(base_addr, FDDMA_STA_OFFSET, "sta"); + FDDMA_DUMPER(base_addr, FDDMA_MASK_INTR_OFFSET, "mask_intr"); + FDDMA_DUMPER(base_addr, FDDMA_UP_AXI_AW_CFG_OFFSET, "up_axi_aw"); + FDDMA_DUMPER(base_addr, FDDMA_UP_AXI_AR_CFG_OFFSET, "up_axi_ar"); + FDDMA_DUMPER(base_addr, FDDMA_DOWN_AXI_AW_CFG_OFFSET, "dw_axi_aw"); + FDDMA_DUMPER(base_addr, FDDMA_DOWN_AXI_AR_CFG_OFFSET, "dw_axi_ar"); + FDDMA_DUMPER(base_addr, FDDMA_CHAN_BIND_OFFSET, "chan_bind"); + FDDMA_DUMPER(base_addr, FDDMA_GCAP_OFFSET, "gcap"); + FDDMA_DUMPER(base_addr, FDDMA_CHAN_4_7_CFG_OFFSET, "chan_4_7"); +} + +void FDdmaDumpChanRegisters(uintptr base_addr, FDdmaChanIndex chan) +{ + FDDMA_DEBUG("\tchan-%d", chan); + FDDMA_DUMPER(base_addr, FDDMA_CHAN_DDR_UP_ADDR_OFFSET(chan), "ddr_up"); + FDDMA_DUMPER(base_addr, FDDMA_CHAN_DDR_LOW_ADDR_OFFSET(chan), "ddr_low"); + FDDMA_DUMPER(base_addr, FDDMA_CHAN_DEV_ADDR_OFFSET(chan), "dev"); + FDDMA_DUMPER(base_addr, FDDMA_CHAN_TS_OFFSET(chan), "ts"); + FDDMA_DUMPER(base_addr, FDDMA_CHAN_CRT_UP_ADDR_OFFSET(chan), "crt_up"); + FDDMA_DUMPER(base_addr, FDDMA_CHAN_CRT_LOW_ADDR_OFFSET(chan), "crt_low"); + FDDMA_DUMPER(base_addr, FDDMA_CHAN_CTL_OFFSET(chan), "ctl"); + FDDMA_DUMPER(base_addr, FDDMA_CHAN_STS_OFFSET(chan), "sts"); + FDDMA_DUMPER(base_addr, FDDMA_CHAN_TIMEOUT_CNT_OFFSET(chan), "cnt"); +} + diff --git a/bsp/phytium/libraries/standalone/drivers/dma/fddma/fddma_sinit.c b/bsp/phytium/libraries/standalone/drivers/dma/fddma/fddma_sinit.c new file mode 100644 index 0000000000..0a7ad7f430 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/dma/fddma/fddma_sinit.c @@ -0,0 +1,63 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fddma_sinit.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:24:47 + * Description:  This files is for static initialization + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/5/13 init commit + */ + +/***************************** Include Files *********************************/ +#include "fparameters.h" +#include "fassert.h" + +#include "fddma_hw.h" +#include "fddma.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ +extern const FDdmaConfig fddma_cfg_tbl[FDDMA_INSTANCE_NUM]; + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ +/** + * @name: FDdmaLookupConfig + * @msg: 获取DDMA实例默认配置 + * @return {const FDdmaConfig *} DDMA控制器默认配置 + * @param {u32} instance_id, DDMA实例号 + */ +const FDdmaConfig *FDdmaLookupConfig(u32 instance_id) +{ + const FDdmaConfig *ptr = NULL; + u32 index; + + for (index = 0; index < (u32)FDDMA_INSTANCE_NUM; index++) + { + if (fddma_cfg_tbl[index].id == instance_id) + { + ptr = &fddma_cfg_tbl[index]; + break; + } + } + + return (const FDdmaConfig *)ptr; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/dma/fgdma/fgdma.c b/bsp/phytium/libraries/standalone/drivers/dma/fgdma/fgdma.c new file mode 100644 index 0000000000..f43a56bd27 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/dma/fgdma/fgdma.c @@ -0,0 +1,652 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgdma.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:25:29 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 huanghe 2021-11-5 init commit + * 1.1 zhugengyu 2022-5-16 support chan alloc. and qos setting + */ + + +/***************************** Include Files *********************************/ +#include + +#include "fdebug.h" + +#include "fgdma_hw.h" +#include "fgdma.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FGDMA_DEBUG_TAG "GDMA" +#define FGDMA_ERROR(format, ...) FT_DEBUG_PRINT_E(FGDMA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGDMA_WARN(format, ...) FT_DEBUG_PRINT_W(FGDMA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGDMA_INFO(format, ...) FT_DEBUG_PRINT_I(FGDMA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGDMA_DEBUG(format, ...) FT_DEBUG_PRINT_D(FGDMA_DEBUG_TAG, format, ##__VA_ARGS__) + +/************************** Function Prototypes ******************************/ +static void FGdmaReset(FGdma *const instance_p); + +/************************** Variable Definitions *****************************/ + +/*****************************************************************************/ + +/** + * @name: FGdmaCfgInitialize + * @msg: 初始化GDMA控制器实例 + * @return {FError} 返回FGDMA_SUCCESS表示初始化成功,返回其它表示失败 + * @param FGdma *const instance_p, GDMA控制器实例 + * @param const FGdmaConfig *input_config, GDMA控制器配置 + */ +FError FGdmaCfgInitialize(FGdma *const instance_p, const FGdmaConfig *input_config) +{ + FASSERT(instance_p && input_config); + uintptr base_addr = input_config->base_addr; + FError ret = FGDMA_SUCCESS; + + if (FT_COMPONENT_IS_READY == instance_p->is_ready) + { + FGDMA_WARN("device is already initialized!!!"); + } + + FGdmaDeInitialize(instance_p); + + if (&instance_p->config != input_config) + instance_p->config = *input_config; + + FASSERT_MSG((0 != base_addr), "invalid device base address"); + FGdmaReset(instance_p); + + if (FGDMA_SUCCESS == ret) + { + instance_p->is_ready = FT_COMPONENT_IS_READY; + } + + return ret; +} + +/** + * @name: FGdmaDeInitialize + * @msg: 去初始化GDMA控制器实例 + * @return {void} 无 + * @param FGdma *const instance_p, GDMA控制器实例 + */ +void FGdmaDeInitialize(FGdma *const instance_p) +{ + FASSERT(instance_p); + u32 chan; + + for (chan = FGDMA_CHAN0_INDEX; chan < FGDMA_NUM_OF_CHAN; chan++) + { + if (NULL != instance_p->chans[chan]) + { + FGDMA_WARN("chan-%d might be in use !!!", chan); + } + } + + memset(instance_p, 0, sizeof(*instance_p)); + return; +} + +/** + * @name: FGdmaSetChanQos + * @msg: 设置GDMA通道的Qos配置 + * @return {void} 无 + * @param {uintptr} base_addr, GDMA控制器基地址 + * @param {FGdmaChan} *dma_chan, GDMA通道实例 + */ +static void FGdmaSetChanQos(uintptr base_addr, FGdmaChan *const dma_chan) +{ + FASSERT(dma_chan); + FGdmaChanIndex chan_id = dma_chan->config.chan_id; + u32 reg_val = FGDMA_READREG(base_addr, FGDMA_CHX_MODE_OFFSET(chan_id)); + FGdmaOperPriority prev_rd_qos = FGDMA_CHX_MODE_RD_QOS_GET(reg_val); + FGdmaOperPriority prev_wr_qos = FGDMA_CHX_MODE_WR_QOS_GET(reg_val); + + FGDMA_INFO("prev rd qos: 0x%x, set rd qos: 0x%x", prev_rd_qos, dma_chan->config.rd_qos); + FGDMA_INFO("prev wr qos: 0x%x, set wr qos: 0x%x", prev_wr_qos, dma_chan->config.wr_qos); + + if (prev_rd_qos != dma_chan->config.rd_qos) /* need to update rd qos */ + { + /* replace with new rd qos config */ + reg_val &= ~FGDMA_CHX_MODE_RD_QOS_MASK; + reg_val |= FGDMA_CHX_MODE_RD_QOS_EN | + FGDMA_CHX_MODE_RD_QOS_SET(dma_chan->config.rd_qos); + } + + if (prev_wr_qos != dma_chan->config.wr_qos) /* need to update wr qos */ + { + /* replace with new wr qos config */ + reg_val &= ~FGDMA_CHX_MODE_WR_QOS_MASK; + reg_val |= FGDMA_CHX_MODE_WR_QOS_EN | + FGDMA_CHX_MODE_WR_QOS_SET(dma_chan->config.wr_qos); + } + + FGDMA_WRITEREG(base_addr, FGDMA_CHX_MODE_OFFSET(chan_id), reg_val); + return; +} + +/** + * @name: FGdmaAllocateChan + * @msg: 分配指定GDMA通道 + * @return {FError} FGDMA_SUCCESS表示分配成功,返回其它值表示分配失败 + * @param FGdma *const instance_p, GDMA控制器实例 + * @param FGdmaChan *const dma_chan, GDMA通道实例 + * @param const FGdmaChanConfig *dma_chan_config, GDMA通道配置 + */ +FError FGdmaAllocateChan(FGdma *const instance_p, FGdmaChan *const dma_chan, + const FGdmaChanConfig *dma_chan_config) +{ + FASSERT(instance_p); + FASSERT(dma_chan); + FASSERT(dma_chan_config); + uintptr base_addr = instance_p->config.base_addr; + FError ret = FGDMA_SUCCESS; + FGdmaChanIndex chan_idx = dma_chan_config->chan_id; + u32 reg_val; + + if (FT_COMPONENT_IS_READY != instance_p->is_ready) + { + FGDMA_ERROR("dma instance not init !!!"); + return FGDMA_ERR_NOT_INIT; + } + + if (NULL != instance_p->chans[chan_idx]) + { + FGDMA_ERROR("chan %d is in use !!!", chan_idx); + return FGDMA_ERR_CHAN_IN_USE; + } + + if (&dma_chan->config != dma_chan_config) + dma_chan->config = *dma_chan_config; + + /* disable and reset chan */ + FGdmaChanDisable(base_addr, chan_idx); + FGdmaChanReset(base_addr, chan_idx); + + /* enable channel clock */ + FGdmaSetChanClock(base_addr, chan_idx, TRUE); + + /* set chan mode */ + reg_val = FGDMA_READREG(base_addr, FGDMA_CHX_MODE_OFFSET(chan_idx)); + if (FGDMA_OPER_BDL == dma_chan->config.trans_mode) + { + FGDMA_INFO("set as BDL mode"); + reg_val |= FGDMA_CHX_MODE_BDL_EN; + + if (dma_chan->config.roll_back) + { + reg_val |= FGDMA_CHX_MODE_BDL_ROLL_EN; /* run BDL in roll-back mode */ + } + else + { + reg_val &= ~FGDMA_CHX_MODE_BDL_ROLL_EN; + } + } + else + { + FGDMA_INFO("set as Direct mode"); + reg_val &= ~FGDMA_CHX_MODE_BDL_EN; + reg_val &= ~FGDMA_CHX_MODE_BDL_ROLL_EN; + } + FGDMA_WRITEREG(base_addr, FGDMA_CHX_MODE_OFFSET(chan_idx), reg_val); + + FGdmaSetChanQos(base_addr, dma_chan); + FGDMA_INFO("mode: 0x%x", FGDMA_READREG(base_addr, FGDMA_CHX_MODE_OFFSET(chan_idx))); + + /* set xfer config */ + reg_val = 0; + reg_val |= FGDMA_CHX_XFER_CFG_AR_LEN_SET(FGDMA_MAX_BURST_LEN) | /* burst length configed as max 8, which adapted when trans bytes less than 8 */ + FGDMA_CHX_XFER_CFG_AR_SIZE_SET((u32)dma_chan->config.rd_align) | + FGDMA_CHX_XFER_CFG_AR_BRUST_SET(FGDMA_INCR); /* mem to mem trans work in incr mode */ + + reg_val |= FGDMA_CHX_XFER_CFG_AW_LEN_SET(FGDMA_MAX_BURST_LEN) | + FGDMA_CHX_XFER_CFG_AW_SIZE_SET((u32)dma_chan->config.wr_align) | + FGDMA_CHX_XFER_CFG_AW_BRUST_SET(FGDMA_INCR); /* mem to mem trans work in incr mode */ + FGDMA_WRITEREG(base_addr, FGDMA_CHX_XFER_CFG_OFFSET(chan_idx), reg_val); + FGDMA_INFO("xfer cfg: 0x%x", FGDMA_READREG(base_addr, FGDMA_CHX_XFER_CFG_OFFSET(chan_idx))); + + instance_p->chans[chan_idx] = dma_chan; + dma_chan->gdma = instance_p; + + return ret; +} + +/** + * @name: FGdmaDellocateChan + * @msg: 释放GDMA通道 + * @return {FError} FGDMA_SUCCESS表示处理成功 + * @param FGdmaChan *const dma_chan, GDMA通道实例 + */ +FError FGdmaDellocateChan(FGdmaChan *const dma_chan) +{ + FASSERT(dma_chan); + FASSERT(dma_chan->gdma); + FGdma *const instance_p = dma_chan->gdma; + uintptr base_addr = instance_p->config.base_addr; + FError ret = FGDMA_SUCCESS; + FGdmaChanIndex chan_idx = dma_chan->config.chan_id; + u32 reg_val; + + if (FT_COMPONENT_IS_READY != instance_p->is_ready) + { + FGDMA_ERROR("dma instance not init !!!"); + return FGDMA_ERR_NOT_INIT; + } + + if (dma_chan != instance_p->chans[chan_idx]) + { + FGDMA_ERROR("chan %d not bind !!!", chan_idx); + return FGDMA_ERR_CHAN_NOT_INIT; + } + + /* disable chan and it irq */ + FGdmaChanIrqDisable(base_addr, chan_idx); + FGdmaChanDisable(base_addr, chan_idx); + + /* disable channel clock */ + FGdmaSetChanClock(base_addr, chan_idx, FALSE); + + instance_p->chans[chan_idx] = NULL; + memset(dma_chan, 0, sizeof(*dma_chan)); + + return ret; +} + +/** + * @name: FGdmaDirectTransfer + * @msg: 直接操作模式下发起DMA传输 + * @return {FError} FGDMA_SUCCESS表示传输成功 + * @param FGdmaChan *const chan_p, GDMA通道实例 + * @param uintptr src_addr, 传输源地址 + * @param uintptr dst_addr, 传输目的地址 + */ +FError FGdmaDirectTransfer(FGdmaChan *const chan_p, uintptr src_addr, uintptr dst_addr, fsize_t data_len) +{ + FASSERT(chan_p); + FASSERT(chan_p->gdma); + u32 reg_val; + FGdma *const instance_p = chan_p->gdma; + uintptr base_addr = instance_p->config.base_addr; + FGdmaChanIndex chan_idx = chan_p->config.chan_id; + + if (FT_COMPONENT_IS_READY != instance_p->is_ready) + { + FGDMA_ERROR("dma instance not init !!!"); + return FGDMA_ERR_NOT_INIT; + } + + if ((src_addr % FGDMA_GET_BURST_SIZE(chan_p->config.rd_align)) || + (dst_addr % FGDMA_GET_BURST_SIZE(chan_p->config.wr_align))) + { + FGDMA_ERROR("src addr 0x%x or dst addr 0x%x not aligned with %d bytes", + src_addr, dst_addr, FGDMA_ADDR_ALIGMENT); + return FGDMA_ERR_INVALID_ADDR; + } + + if (0 != (data_len % chan_p->config.wr_align)) + { + FGDMA_ERROR("data length %d must be N times of burst size %d !!!", + data_len, chan_p->config.wr_align); + return FGDMA_ERR_INVALID_SIZE; + } + + /* src address */ +#ifdef __aarch64__ + FGDMA_WRITEREG(base_addr, FGDMA_CHX_UPSADDR_OFFSET(chan_idx), UPPER_32_BITS(src_addr)); + FGDMA_WRITEREG(base_addr, FGDMA_CHX_LWSADDR_OFFSET(chan_idx), LOWER_32_BITS(src_addr)); +#else + FGDMA_WRITEREG(base_addr, FGDMA_CHX_UPSADDR_OFFSET(chan_idx), 0x0U); + FGDMA_WRITEREG(base_addr, FGDMA_CHX_LWSADDR_OFFSET(chan_idx), (u32)(src_addr)); +#endif + + FGDMA_INFO("src: 0x%x-0x%x", + FGDMA_READREG(base_addr, FGDMA_CHX_UPSADDR_OFFSET(chan_idx)), + FGDMA_READREG(base_addr, FGDMA_CHX_LWSADDR_OFFSET(chan_idx))); + + /* dest address */ +#ifdef __aarch64__ + FGDMA_WRITEREG(base_addr, FGDMA_CHX_UPDADDR_OFFSET(chan_idx), UPPER_32_BITS(dst_addr)); + FGDMA_WRITEREG(base_addr, FGDMA_CHX_LWDADDR_OFFSET(chan_idx), LOWER_32_BITS(dst_addr)); +#else + FGDMA_WRITEREG(base_addr, FGDMA_CHX_UPDADDR_OFFSET(chan_idx), 0x0U); + FGDMA_WRITEREG(base_addr, FGDMA_CHX_LWDADDR_OFFSET(chan_idx), (u32)(dst_addr)); +#endif + + FGDMA_INFO("dst: 0x%x-0x%x", + FGDMA_READREG(base_addr, FGDMA_CHX_UPDADDR_OFFSET(chan_idx)), + FGDMA_READREG(base_addr, FGDMA_CHX_LWDADDR_OFFSET(chan_idx))); + + /* num of BDL entry not used in direct mode */ + FGDMA_WRITEREG(base_addr, FGDMA_CHX_LVI_OFFSET(chan_idx), 0x0U); + + /* length of data to transfer */ + FGDMA_WRITEREG(base_addr, FGDMA_CHX_TS_OFFSET(chan_idx), data_len); + + FGDMA_INFO("ts: 0x%x", FGDMA_READREG(base_addr, FGDMA_CHX_TS_OFFSET(chan_idx))); + + /* enable channel interrupt */ + FGdmaChanIrqEnable(base_addr, chan_idx, FGDMA_CHX_INT_CTRL_TRANS_END_ENABLE); + + /* enable channel and start transfer */ + FGdmaChanEnable(base_addr, chan_idx); + + return FGDMA_SUCCESS; +} + +/** + * @name: FGdmaSetupBDLEntry + * @msg: 设置BDL描述符的一个条目 + * @return {FError} FGDMA_SUCCESS 表示设置成功 + * @param FGdmaBdlDesc *desc_entry, 一条BDL描述符 + * @param uintptr src_addr, 传输源地址 + * @param uintptr dst_addr, 传输目的地址 + * @param fsize_t data_len, 传输数据长度 + */ +FError FGdmaAppendBDLEntry(FGdmaChan *const chan_p, uintptr src_addr, uintptr dst_addr, fsize_t data_len) +{ + FASSERT(chan_p); + FASSERT_MSG((chan_p->config.descs) && (chan_p->config.total_desc_num > 0), "BDL descriptor list not yet assign !!!"); + u32 desc_idx = chan_p->config.valid_desc_num; + FGdmaBdlDesc *desc_entry = &(chan_p->config.descs[desc_idx]); + + if (chan_p->config.valid_desc_num >= chan_p->config.total_desc_num) + { + FGDMA_ERROR("total BDL descriptor num is %d, already used up", chan_p->config.total_desc_num); + return FGDMA_ERR_BDL_NOT_ENOUGH; + } + + if ((0U != (dst_addr % FGDMA_GET_BURST_SIZE(chan_p->config.wr_align))) || + (0U != (src_addr % FGDMA_GET_BURST_SIZE(chan_p->config.rd_align)))) + { + FGDMA_ERROR("src addr 0x%x or dst addr 0x%x not aligned with %d bytes", + src_addr, dst_addr, FGDMA_GET_BURST_SIZE(chan_p->config.wr_align)); + return FGDMA_ERR_INVALID_ADDR; + } + + if (0U != (data_len % chan_p->config.wr_align)) + { + FGDMA_ERROR("data length %d must be N times of burst size %d !!!", + data_len, chan_p->config.wr_align); + return FGDMA_ERR_INVALID_SIZE; + } + +#ifdef __aarch64___ + desc_entry->src_addr_h = UPPER_32_BITS(src_addr); + desc_entry->src_addr_l = LOWER_32_BITS(src_addr); + desc_entry->dst_addr_h = UPPER_32_BITS(dst_addr); + desc_entry->dst_addr_l = LOWER_32_BITS(dst_addr); +#else + desc_entry->src_addr_h = 0U; + desc_entry->src_addr_l = src_addr; + desc_entry->dst_addr_h = 0U; + desc_entry->dst_addr_l = dst_addr; +#endif + + /* rd = src */ + desc_entry->src_tc = FGDMA_SRC_TC_BDL_BURST_SET(FGDMA_INCR) | + FGDMA_SRC_TC_BDL_SIZE_SET((u32)chan_p->config.rd_align) | + FGDMA_SRC_TC_BDL_LEN_SET(FGDMA_MAX_BURST_LEN); + + /* wr = dst */ + desc_entry->dst_tc = FGDMA_DST_TC_BDL_BURST_SET(FGDMA_INCR) | + FGDMA_DST_TC_BDL_SIZE_SET((u32)chan_p->config.wr_align) | + FGDMA_DST_TC_BDL_LEN_SET(FGDMA_MAX_BURST_LEN); + + desc_entry->total_bytes = data_len; + desc_entry->ioc = 0U; + + chan_p->config.valid_desc_num++; + + return FGDMA_SUCCESS; +} + +/** + * @name: FGdmaBDLTransfer + * @msg: BDL操作模式下发起DMA传输 + * @return {FError} FGDMA_SUCCESS 表示传输成功 + * @param FGdmaChan *const chan_p, DMA通道实例 + */ +FError FGdmaBDLTransfer(FGdmaChan *const chan_p) +{ + FASSERT(chan_p); + FASSERT(chan_p->gdma); + FASSERT_MSG((chan_p->config.descs) && (chan_p->config.total_desc_num > 0), "BDL descriptor list not yet assign !!!"); + u32 reg_val; + FGdma *const instance_p = chan_p->gdma; + uintptr base_addr = instance_p->config.base_addr; + FGdmaChanIndex chan_idx = chan_p->config.chan_id; + + if (FT_COMPONENT_IS_READY != instance_p->is_ready) + { + FGDMA_ERROR("dma instance not init !!!"); + return FGDMA_ERR_NOT_INIT; + } + + if (0 == chan_p->config.valid_desc_num) + { + FGDMA_WARN("need to assign BDL entry fisrt !!!"); + return FGDMA_SUCCESS; + } + + if (((uintptr)(void *)chan_p->config.descs) % FGDMA_ADDR_ALIGMENT) + { + FGDMA_ERROR("BDL addr %p not aligned with %d bytes", chan_p->config.descs, FGDMA_ADDR_ALIGMENT); + return FGDMA_ERR_INVALID_ADDR; + } + + u32 desc_idx = chan_p->config.valid_desc_num - 1; + FGdmaBdlDesc *descs = chan_p->config.descs; /* get the first BDL entry */ + + chan_p->config.descs[desc_idx].ioc = 1U; /* set as the last BDL entry */ + + /* src address, and dst address has been defined in BDL */ +#ifdef __aarch64__ + FGDMA_WRITEREG(base_addr, FGDMA_CHX_UPSADDR_OFFSET(chan_idx), UPPER_32_BITS((uintptr)descs)); + FGDMA_WRITEREG(base_addr, FGDMA_CHX_LWSADDR_OFFSET(chan_idx), LOWER_32_BITS((uintptr)descs)); +#else + FGDMA_WRITEREG(base_addr, FGDMA_CHX_UPSADDR_OFFSET(chan_idx), 0x0U); + FGDMA_WRITEREG(base_addr, FGDMA_CHX_LWSADDR_OFFSET(chan_idx), (u32)((uintptr)descs)); +#endif + + /* dst address not used in BDL mode */ + FGDMA_WRITEREG(base_addr, FGDMA_CHX_UPDADDR_OFFSET(chan_idx), 0x0U); + FGDMA_WRITEREG(base_addr, FGDMA_CHX_LWDADDR_OFFSET(chan_idx), 0x0U); + + /* ts not used in BDL mode */ + FGDMA_WRITEREG(base_addr, FGDMA_CHX_TS_OFFSET(chan_idx), 0x0U); + + /* num of BDL entry */ + FGDMA_WRITEREG(base_addr, FGDMA_CHX_LVI_OFFSET(chan_idx), FGDMA_CHX_LVI_SET(chan_p->config.valid_desc_num)); + + /* enable channel interrupt */ + FGdmaChanIrqEnable(base_addr, chan_idx, FGDMA_CHX_INT_CTRL_TRANS_END_ENABLE); + + /* enable channel and start transfer */ + FGdmaChanEnable(base_addr, chan_idx); + + return FGDMA_SUCCESS; +} + +/** + * @name: FGdmaStart + * @msg: 使能启动GDMA控制器 + * @return {FError} FGDMA_SUCCESS表示启动成功 + * @param FGdma *const instance_p, GDMA控制器实例 + * @note: 先调用此函数,后调用FGdmaAllocateChan配置特定通道 + */ +FError FGdmaStart(FGdma *const instance_p) +{ + FASSERT(instance_p); + uintptr base_addr = instance_p->config.base_addr; + u32 reg_val; + + if (FT_COMPONENT_IS_READY != instance_p->is_ready) + { + FGDMA_ERROR("dma instance not init !!!"); + return FGDMA_ERR_NOT_INIT; + } + + FGdmaIrqEnable(base_addr); + + reg_val = FGDMA_READREG(base_addr, FGDMA_CTL_OFFSET); + reg_val &= ~FGDMA_CTL_OT_MASK; + reg_val |= FGDMA_CTL_OT_SET(FGDMA_OUTSTANDING); /* 设置传输outstanding数 */ + reg_val |= FGDMA_CTL_ENABLE; /* 使能DMA传输 */ + FGDMA_WRITEREG(base_addr, FGDMA_CTL_OFFSET, reg_val); + + return FGDMA_SUCCESS; // 放到初始化 +} + +/** + * @name: FGdmaStop + * @msg: 停止GDMA控制器 + * @return {FError} FGDMA_SUCCESS表示处理成功 + * @param FGdma *const instance_p, GDMA控制器实例 + */ +FError FGdmaStop(FGdma *const instance_p) +{ + FASSERT(instance_p); + uintptr base_addr = instance_p->config.base_addr; + u32 reg_val; + u32 chan_id; + u32 chan_status; + + if (FT_COMPONENT_IS_READY != instance_p->is_ready) + { + FGDMA_ERROR("dma instance not init !!!"); + return FGDMA_ERR_NOT_INIT; + } + + /* Abort 流程 */ + for (chan_id = FGDMA_CHAN0_INDEX; chan_id < FGDMA_NUM_OF_CHAN; chan_id++) + { + if (NULL == instance_p->chans[chan_id]) + continue; /* skip un-allocate channel */ + + chan_status = FGdmaReadChanStatus(base_addr, chan_id); + if (FGDMA_CHX_INT_STATE_BUSY & chan_status) + { + FGDMA_WARN("chan-%d has abort unfinished request !!!", chan_id); + FGdmaChanDisable(base_addr, chan_id); /* 关闭通道 */ + FGdmaChanReset(base_addr, chan_id); /* 需要进行软复位,否则再次使能通道时,仍然会执行之前的请求 */ + } + else + { + FGdmaChanDisable(base_addr, chan_id); /* 关闭通道 */ + } + + FGdmaClearChanStatus(base_addr, chan_id, chan_status); /* 清除通道状态 */ + } + + FGdmaDisable(base_addr); + + return FGDMA_SUCCESS; +} + +/** + * @name: FGdmaSetQos + * @msg: 设置GDMA控制器的全局Qos配置 + * @return {void} 无 + * @param {FGdma} *instance_p, GDMA控制器实例 + */ +static void FGdmaSetQos(FGdma *const instance_p) +{ + uintptr base_addr = instance_p->config.base_addr; + u32 reg_val; + + /* enable/disable Qos */ + reg_val = FGDMA_READREG(base_addr, FGDMA_CTL_OFFSET); + if (FGDMA_OPER_NONE_PRIORITY_POLL == instance_p->config.rd_qos) /* Poll mode */ + { + reg_val &= ~FGDMA_CTL_RD_ARB; + } + else /* Qos mode */ + { + reg_val |= FGDMA_CTL_RD_ARB; + } + + if (FGDMA_OPER_NONE_PRIORITY_POLL == instance_p->config.wr_qos) /* Poll mode */ + { + reg_val &= ~FGDMA_CTL_WR_ARB; + } + else /* Qos mode */ + { + reg_val |= FGDMA_CTL_WR_ARB; + } + FGDMA_WRITEREG(base_addr, FGDMA_CTL_OFFSET, reg_val); + + /* set Qos configure */ + reg_val = FGDMA_READREG(base_addr, FGDMA_QOS_CFG_OFFSET); + if (FGDMA_OPER_NONE_PRIORITY_POLL == instance_p->config.rd_qos) /* Poll mode */ + { + reg_val &= ~FGDMA_QOS_CFG_ARGOS_MASK; + } + else /* Qos value */ + { + reg_val |= FGDMA_QOS_CFG_ARGOS_SET((u32)instance_p->config.rd_qos); + } + + if (FGDMA_OPER_NONE_PRIORITY_POLL == instance_p->config.wr_qos) /* Poll mode */ + { + reg_val &= ~FGDMA_QOS_CFG_AWQOS_MASK; + } + else /* Qos value */ + { + reg_val |= FGDMA_QOS_CFG_AWQOS_SET((u32)instance_p->config.wr_qos); + } + FGDMA_WRITEREG(base_addr, FGDMA_QOS_CFG_OFFSET, reg_val); + + return; +} + +/** + * @name: FGdmaReset + * @msg: 重置GDMA控制器 + * @return {void} 无 + * @param {FGdma} *instance_p, GDMA控制器实例 + */ +static void FGdmaReset(FGdma *const instance_p) +{ + uintptr base_addr = instance_p->config.base_addr; + u32 chan; + u32 reg_val; + + FGDMA_INFO("reset ctrl @0x%x ...", base_addr); + + FGdmaDisable(base_addr); + FGdmaSoftwareReset(base_addr); + + FGdmaSetQos(instance_p); + + FGDMA_INFO("reset chan ..."); + + for (chan = FGDMA_CHAN0_INDEX; chan < FGDMA_NUM_OF_CHAN; chan++) + { + FGdmaChanDisable(base_addr, chan); + FGdmaChanIrqDisable(base_addr, chan); + FGdmaChanReset(base_addr, chan); + FGdmaSetChanClock(base_addr, chan, FALSE); + } + + return; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/dma/fgdma/fgdma.h b/bsp/phytium/libraries/standalone/drivers/dma/fgdma/fgdma.h new file mode 100644 index 0000000000..0319af3618 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/dma/fgdma/fgdma.h @@ -0,0 +1,265 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgdma.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:25:35 + * Description:  This files is for gdma user api implementation + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 huanghe 2021-11-5 init commit + * 1.1 zhugengyu 2022-5-16 modify according to tech manual. + */ + +#ifndef DRIVERS_FGDMA_H +#define DRIVERS_FGDMA_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ + +#include "ftypes.h" +#include "fassert.h" +#include "ferror_code.h" +#include "fkernel.h" + +/************************** Constant Definitions *****************************/ +typedef enum +{ + FGDMA_CHAN0_INDEX = 0, + FGDMA_CHAN1_INDEX = 1, + FGDMA_CHAN2_INDEX = 2, + FGDMA_CHAN3_INDEX = 3, + FGDMA_CHAN4_INDEX = 4, + FGDMA_CHAN5_INDEX = 5, + FGDMA_CHAN6_INDEX = 6, + FGDMA_CHAN7_INDEX = 7, + FGDMA_CHAN8_INDEX = 8, + FGDMA_CHAN9_INDEX = 9, + FGDMA_CHAN10_INDEX = 10, + FGDMA_CHAN11_INDEX = 11, + FGDMA_CHAN12_INDEX = 12, + FGDMA_CHAN13_INDEX = 13, + FGDMA_CHAN14_INDEX = 14, + FGDMA_CHAN15_INDEX = 15, + + FGDMA_NUM_OF_CHAN +} FGdmaChanIndex; /* 16个独立通道, 0 ~ 15 */ + +typedef enum +{ + FGDMA_OPER_NONE_PRIORITY_POLL = -1, /* Priority = -1 表示读写请求仲裁模式为Poll */ + FGDMA_OPER_PRIORITY0 = 0, /* Priority >= 0 表示读写请求仲裁模式为Qos */ + FGDMA_OPER_PRIORITY1 = 1, + FGDMA_OPER_PRIORITY2 = 2, + FGDMA_OPER_PRIORITY3 = 3, + FGDMA_OPER_PRIORITY4 = 4, + FGDMA_OPER_PRIORITY5 = 5, + FGDMA_OPER_PRIORITY6 = 6, + FGDMA_OPER_PRIORITY7 = 7, + FGDMA_OPER_PRIORITY8 = 8, + FGDMA_OPER_PRIORITY9 = 9, + FGDMA_OPER_PRIORITY10 = 10, + FGDMA_OPER_PRIORITY11 = 11, + FGDMA_OPER_PRIORITY12 = 12, + FGDMA_OPER_PRIORITY13 = 13, + FGDMA_OPER_PRIORITY14 = 14, + FGDMA_OPER_PRIORITY15 = 15 +} FGdmaOperPriority; /* Qos配置,值越高,优先级越高 */ + +typedef enum +{ + FGDMA_OPER_DIRECT = 0, /* 直接操作模式 */ + FGDMA_OPER_BDL /* BDL操作模式 */ +} FGdmaOperMode; /* 支持的操作模式 */ + +typedef enum +{ + FGDMA_BURST_SIZE_1_BYTE = 0, + FGDMA_BURST_SIZE_2_BYTE = 1, + FGDMA_BURST_SIZE_4_BYTE = 2, + FGDMA_BURST_SIZE_8_BYTE = 3, + FGDMA_BURST_SIZE_16_BYTE = 4 +} FGdmaBurstSize; /* 支持的读写请求size大小 */ + +#define FGDMA_GET_BURST_SIZE(brust_align) (1U << brust_align) + +typedef enum +{ + FGDMA_CHAN_EVT_FIFO_EMPTY = 0, /* 通道Fifo空事件 */ + FGDMA_CHAN_EVT_FIFO_FULL, /* 通道Fifo满事件 */ + FGDMA_CHAN_EVT_BDL_END, /* BDL模式下一个BDL条目传输完成 */ + FGDMA_CHAN_EVT_TRANS_END, /* 所有传输数据完成 */ + FGDMA_CHAN_EVT_BUSY, /* 前一次传输未完成,当前还处于传输中 */ + + FGDMA_CHAN_NUM_OF_EVT +} FGdmaChanEvtType; /* 通道中断事件 */ + +#define FGDMA_SUCCESS FT_SUCCESS +#define FGDMA_ERR_NOT_INIT FT_MAKE_ERRCODE(ErrModBsp, ErrGdma, 0) +#define FGDMA_ERR_CHAN_IN_USE FT_MAKE_ERRCODE(ErrModBsp, ErrGdma, 1) +#define FGDMA_ERR_CHAN_NOT_INIT FT_MAKE_ERRCODE(ErrModBsp, ErrGdma, 2) +#define FGDMA_ERR_INVALID_ADDR FT_MAKE_ERRCODE(ErrModBsp, ErrGdma, 3) +#define FGDMA_ERR_INVALID_SIZE FT_MAKE_ERRCODE(ErrModBsp, ErrGdma, 4) +#define FGDMA_ERR_BDL_NOT_ENOUGH FT_MAKE_ERRCODE(ErrModBsp, ErrGdma, 5) + +#define FGDMA_ADDR_ALIGMENT 128U /* 直接模式和BDL模式的地址需要按128位对齐 */ + +/**************************** Type Definitions *******************************/ +typedef struct _FGdma FGdma; +typedef struct _FGdmaChan FGdmaChan; + +typedef struct +{ + u32 instance_id; /* GDMA控制器ID */ + u32 irq_num; /* GDMA控制器中断号 */ + u32 irq_prority; /* GDMA控制器中断优先级 */ + volatile uintptr_t base_addr; /* GDMA控制器基地址 */ + FGdmaOperPriority rd_qos; /* 读操作优先级 */ + FGdmaOperPriority wr_qos; /* 写操作优先级 */ +} FGdmaConfig; /* GDMA控制器配置 */ + +typedef struct +{ + u32 src_addr_l; /* 0x0, 数据源地址低32位 */ + u32 src_addr_h; /* 0x4, 数据源地址高32位 */ + u32 dst_addr_l; /* 0x8, 数据目的地址低32位 */ + u32 dst_addr_h; /* 0xc, 数据目的地址高32位 */ +#define FGDMA_SRC_TC_BDL_BURST_SET(x) SET_REG32_BITS((x), 1U, 0U) +#define FGDMA_SRC_TC_BDL_SIZE_SET(x) SET_REG32_BITS((x), 6U, 4U) +#define FGDMA_SRC_TC_BDL_LEN_SET(x) SET_REG32_BITS((x), 15U, 8U) + u32 src_tc; /* 0x10, 源传输控制位 */ +#define FGDMA_DST_TC_BDL_BURST_SET(x) SET_REG32_BITS((x), 1U, 0U) +#define FGDMA_DST_TC_BDL_SIZE_SET(x) SET_REG32_BITS((x), 6U, 4U) +#define FGDMA_DST_TC_BDL_LEN_SET(x) SET_REG32_BITS((x), 15U, 8U) + u32 dst_tc; /* 0x14, 目的传输控制 */ + u32 total_bytes;/* 0x18, 传输数据总量,以Byte为单位 */ + u32 ioc; /* 0x1c, 该条目传输完成中断产生控制位 */ +} __attribute__((__packed__)) FGdmaBdlDesc; /* BDL描述符 */ + +FASSERT_STATIC(0x20U == sizeof(FGdmaBdlDesc)); + +typedef struct +{ + FGdmaChanIndex chan_id; /* DMA通道ID */ + FGdmaOperPriority rd_qos; /* DMA通道读Qos配置 */ + FGdmaOperPriority wr_qos; /* DMA通道写Qos配置 */ + FGdmaOperMode trans_mode; /* DMA通道的操作模式,直接模式或者BDL模式 */ + /* Direct模式有效 */ + FGdmaBurstSize rd_align; /* DMA读请求的Burst对齐方式 */ + FGdmaBurstSize wr_align; /* DMA写请求的Burst对齐方式 */ + /* BDL模式有效 */ + boolean roll_back; /* 循环模式,TRUE: 当前BDL列表完成后,从第一个BDL项从新开始传输 */ + FGdmaBdlDesc *descs; + u32 total_desc_num; + u32 valid_desc_num; +} FGdmaChanConfig; /* DMA通道配置 */ + +typedef void (*FGdmaChanEvtHandler)(FGdmaChan *const chan, void *args); + +typedef struct _FGdmaChan +{ + FGdmaChanConfig config; /* DMA通道配置 */ + FGdma *gdma; /* DMA控制器实例 */ + FGdmaChanEvtHandler evt_handlers[FGDMA_CHAN_NUM_OF_EVT]; /* DMA通道事件回调函数 */ + void *evt_handler_args[FGDMA_CHAN_NUM_OF_EVT]; /* DMA通道事件回调函数入参 */ +} FGdmaChan; /* GDMA通道实例 */ + +typedef struct _FGdma +{ + FGdmaConfig config; /* GDMA控制器配置 */ + u32 is_ready; /* GDMA控制器初始化是否完成 */ + FGdmaChan *chans[FGDMA_NUM_OF_CHAN]; /* GDMA通道实例,如果通道没有分配,值为NULL */ +} FGdma; /* GDMA控制器实例 */ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +/* 获取默认的通道配置 */ +#define FGDMA_DEFAULT_DIRECT_CHAN_CONFIG(_chan_id)\ +(FGdmaChanConfig){ \ + .chan_id = (_chan_id),\ + .rd_align = FGDMA_BURST_SIZE_4_BYTE,\ + .wr_align = FGDMA_BURST_SIZE_4_BYTE,\ + .rd_qos = FGDMA_OPER_NONE_PRIORITY_POLL,\ + .wr_qos = FGDMA_OPER_NONE_PRIORITY_POLL,\ + .trans_mode = FGDMA_OPER_DIRECT,\ + .roll_back = FALSE\ +} + +#define FGDMA_DEFAULT_BDL_CHAN_CONFIG(_chan_id, _bdl_descs, _bdl_desc_num)\ +(FGdmaChanConfig){ \ + .chan_id = (_chan_id),\ + .rd_align = FGDMA_BURST_SIZE_4_BYTE,\ + .wr_align = FGDMA_BURST_SIZE_4_BYTE,\ + .rd_qos = FGDMA_OPER_NONE_PRIORITY_POLL,\ + .wr_qos = FGDMA_OPER_NONE_PRIORITY_POLL,\ + .trans_mode = FGDMA_OPER_BDL,\ + .roll_back = FALSE,\ + .descs = _bdl_descs,\ + .total_desc_num = _bdl_desc_num,\ + .valid_desc_num = 0U\ +} + +/************************** Function Prototypes ******************************/ +/* 获取GDMA控制器默认配置 */ +const FGdmaConfig *FGdmaLookupConfig(u32 instance_id); + +/* 初始化GDMA控制器实例 */ +FError FGdmaCfgInitialize(FGdma *const instance_p, const FGdmaConfig *config_p); + +/* 去初始化GDMA控制器实例 */ +void FGdmaDeInitialize(FGdma *const instance_p); + +/* 分配指定GDMA通道 */ +FError FGdmaAllocateChan(FGdma *const instance_p, FGdmaChan *const chan_p, + const FGdmaChanConfig *config_p); + +/* 释放GDMA通道 */ +FError FGdmaDellocateChan(FGdmaChan *const chan_p); + +/* 直接操作模式下发起DMA传输 */ +FError FGdmaDirectTransfer(FGdmaChan *const chan_p, uintptr src_addr, uintptr dst_addr, fsize_t data_len); + +/* 设置BDL描述符的一个条目 */ +FError FGdmaAppendBDLEntry(FGdmaChan *const chan_p, uintptr src_addr, uintptr dst_addr, fsize_t data_len); + +/* BDL操作模式下发起DMA传输 */ +FError FGdmaBDLTransfer(FGdmaChan *const chan_p); + +/* 使能启动GDMA控制器 */ +FError FGdmaStart(FGdma *const instance_p); + +/* 停止GDMA控制器 */ +FError FGdmaStop(FGdma *const instance_p); + +/* GDMA中断处理函数 */ +void FGdmaIrqHandler(s32 vector, void *args); + +/* 注册GDMA通道事件回调函数 */ +void FGdmaChanRegisterEvtHandler(FGdmaChan *const chan_p, FGdmaChanEvtType evt, + FGdmaChanEvtHandler handler, void *handler_arg); + +/* 打印当前的GDMA寄存器值, DEBUG模式下有效 */ +void FGdmaDumpRegisterVals(uintptr base_addr, u32 max_chan); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsp/phytium/libraries/standalone/drivers/dma/fgdma/fgdma_g.c b/bsp/phytium/libraries/standalone/drivers/dma/fgdma/fgdma_g.c new file mode 100644 index 0000000000..8cb1ba77f8 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/dma/fgdma/fgdma_g.c @@ -0,0 +1,58 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgdma_g.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:25:09 + * Description:  This files is for static variables definition + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 huanghe 2021-11-5 init commit + * 1.1 zhugengyu 2022-5-16 modify according to tech manual. + */ + +/***************************** Include Files *********************************/ + +#include "ftypes.h" +#include "fparameters.h" + +#include "fgdma.h" +#include "fgdma_hw.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + +const FGdmaConfig fgdma_cfg_tbl[FGDMA_INSTANCE_NUM] = +{ + [FGDMA0_ID] = + { + .instance_id = FGDMA0_ID, + .base_addr = FGDMA0_BASE_ADDR, + .irq_num = FGDMA0_IRQ_NUM, + .irq_prority = 0, + .rd_qos = FGDMA_OPER_NONE_PRIORITY_POLL, + .wr_qos = FGDMA_OPER_NONE_PRIORITY_POLL + } +}; + + +/*****************************************************************************/ diff --git a/bsp/phytium/libraries/standalone/drivers/dma/fgdma/fgdma_hw.h b/bsp/phytium/libraries/standalone/drivers/dma/fgdma/fgdma_hw.h new file mode 100644 index 0000000000..14e45c451e --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/dma/fgdma/fgdma_hw.h @@ -0,0 +1,447 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgdma_hw.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:24:52 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 huanghe 2021-11-5 init commit + * 1.1 zhugengyu 2022-5-16 modify according to tech manual. + */ + +#ifndef DRIVERS_FGDMA_HW_H +#define DRIVERS_FGDMA_HW_H + +#ifdef __cplusplus +extern "C" +{ +#endif +/***************************** Include Files *********************************/ + +#include "fparameters.h" +#include "fio.h" +#include "fkernel.h" + +/************************** Constant Definitions *****************************/ + +/** @name Register Map + * + * Register offsets from the base address of an GDMA device. + * @{ + */ +#define FGDMA_CTL_OFFSET 0x0U /* 全局控制类寄存器 */ +#define FGDMA_INTR_STATE_OFFSET 0x4U /* 中断状态寄存器 */ +#define FGDMA_INTR_CTRL_OFFSET 0x8U /* 中断使能控制寄存器 */ +#define FGDMA_LP_OFFSET 0xCU /* 每一位对应一个通道时钟开启和关断 */ +#define FGDMA_QOS_CFG_OFFSET 0x10U /* 读写请求的 QoS 配置 */ + +/* Channel register */ +#define FGDMA_CHX_OFFSET(x) ((x) * 0x60U) +#define FGDMA_CHX_CTL_OFFSET(x) (0x20 + FGDMA_CHX_OFFSET(x)) /* 软复位信号与使能控制信号 */ +#define FGDMA_CHX_MODE_OFFSET(x) (0x24 + FGDMA_CHX_OFFSET(x)) /* 模式寄存器 */ +#define FGDMA_CHX_INT_CTRL_OFFSET(x) (0x28 + FGDMA_CHX_OFFSET(x)) /* 中断输出控制寄存器 */ +#define FGDMA_CHX_INT_STATE_OFFSET(x) (0x2C + FGDMA_CHX_OFFSET(x)) /* 状态寄存器 */ +#define FGDMA_CHX_LVI_OFFSET(x) (0x30 + FGDMA_CHX_OFFSET(x)) /* 链表模式下 BDL 有效条目 */ +#define FGDMA_CHX_TS_OFFSET(x) (0x34 + FGDMA_CHX_OFFSET(x)) /* 直接操作模式下总 Byte 数据量 */ +#define FGDMA_CHX_UPSADDR_OFFSET(x) (0x38 + FGDMA_CHX_OFFSET(x)) /* 源地址寄存器(高32位) */ +#define FGDMA_CHX_LWSADDR_OFFSET(x) (0x3C + FGDMA_CHX_OFFSET(x)) /* 源地址寄存器(低32位) */ +#define FGDMA_CHX_UPDADDR_OFFSET(x) (0x40 + FGDMA_CHX_OFFSET(x)) /* 目的地址寄存器(高32位) */ +#define FGDMA_CHX_LWDADDR_OFFSET(x) (0x44 + FGDMA_CHX_OFFSET(x)) /* 目的地址寄存器(低32位) */ +#define FGDMA_CHX_XFER_CFG_OFFSET(x) (0x48 + FGDMA_CHX_OFFSET(x)) /* 读写请求 Burst 信息 */ +#define FGDMA_CHX_LCP_OFFSET(x) (0x4C + FGDMA_CHX_OFFSET(x)) /* 当前操作了多少个 BDL 列表 */ +#define FGDMA_CHX_SECCTL_OFFSET(x) (0x50 + FGDMA_CHX_OFFSET(x)) /* 安全控制寄存器 */ +#define FGDMA_CHX_SEC_ATST_OFFSET(x) (0x54 + FGDMA_CHX_OFFSET(x)) /* Secssid 和 Atst信号控制寄存器 */ +#define FGDMA_CHX_NSID_STRMID_OFFSET(x) (0x58 + FGDMA_CHX_OFFSET(x)) /* NSAID 和 StreamID 控制寄存器 */ +#define FGDMA_CHX_AW_CFG_OFFSET(x) (0x5C + FGDMA_CHX_OFFSET(x)) /* 控制 AXI AW 通道配置寄存器 */ +#define FGDMA_CHX_AR_CFG_OFFSET(x) (0x60 + FGDMA_CHX_OFFSET(x)) /* 控制 AXI AR 通道配置寄存器 */ +#define FGDMA_CHX_SECRSP_OFFSET(x) (0x64 + FGDMA_CHX_OFFSET(x)) /* 通道 Response 安全控制寄存器 */ + +/** @name FGDMA_CTL_OFFSET Register + */ +#define FGDMA_CTL_OT_MASK GENMASK(11, 8) +#define FGDMA_CTL_OT_SET(ot) SET_REG32_BITS(ot, 11, 8) /* dma 传输outstanding 控制,实际数量为该寄存器值+1 */ +#define FGDMA_CTL_RD_ARB BIT(5) /* dma 读请求仲裁模式: 0:轮询模式,1:采用Qos判断模式 */ +#define FGDMA_CTL_WR_ARB BIT(4) /* dma 写请求仲裁模式: 0:轮询模式,1:采用Qos判断模式 */ +#define FGDMA_CTL_SOFT_RESET BIT(1) /* dma 软件复位信号,1表示进行复位,写0退出 */ +#define FGDMA_CTL_ENABLE BIT(0) /* dma 使能信号,1表示使能,写0表示disable */ + +/** @name FGDMA_INTR_STATE_OFFSET Register + */ +#define FGDMA_CHX_INTR_STATE(x) BIT(x) /* channel-x 中断状态标志位 */ + +/** @name FGDMA_INTR_CTRL_OFFSET Register + */ +#define FGDMA_CHX_INTR_MASK(x) BIT(x) /* channel-x 中断使能控制,1 表示使能,0 表示不使能 */ +#define FGDMA_CHX_INTR_GLOBAL_MASK BIT(31) /* 全局中断输出 mask 控制信号,1表示允许中断输出,0 表示不允许 */ + +/** @name FGDMA_LP_OFFSET Register + */ +#define FGDMA_CHX_LP_CTL(x) BIT(x) /* channel-x 时钟开启和关断,1 表示关断,默认开启 */ + +/** @name FGDMA_QOS_CFG_OFFSET Register + */ +#define FGDMA_QOS_CFG_ARGOS_MASK GENMASK(7, 4) +#define FGDMA_QOS_CFG_ARGOS_SET(qos) SET_REG32_BITS((qos), 7, 4) /* 读请求的 QoS 配置 */ +#define FGDMA_QOS_CFG_AWQOS_MASK GENMASK(3, 0) +#define FGDMA_QOS_CFG_AWQOS_SET(qos) SET_REG32_BITS((qos), 3, 0) /* 写请求的 QoS 配置 */ + +/** @name FGDMA_CHX_CTL_OFFSET Register + */ +#define FGDMA_CHX_CTL_SOFT_RESET BIT(4) /* 软复位信号,1 表示进行软复位,写 0 退出 */ +#define FGDMA_CHX_CTL_ENABLE BIT(0) /* 使能控制信号,1 表示使能该通道,0 表示不使能 */ + +/** @name FGDMA_CHX_MODE_OFFSET Register + */ +#define FGDMA_CHX_MODE_RD_QOS_MASK GENMASK(23, 20) +#define FGDMA_CHX_MODE_RD_QOS_SET(qos) SET_REG32_BITS((qos), 23, 20) /* CHX 读请求 Qos 配置 */ +#define FGDMA_CHX_MODE_RD_QOS_GET(qos) GET_REG32_BITS((qos), 23, 20) +/* 是否用 cd_rd_qos 的值替换该通道的去请求 QoSS,1 表示替换, 0 不替换 */ +#define FGDMA_CHX_MODE_RD_QOS_EN BIT(16) + +#define FGDMA_CHX_MODE_WR_QOS_MASK GENMASK(15, 12) +#define FGDMA_CHX_MODE_WR_QOS_SET(qos) SET_REG32_BITS((qos), 15, 12) /* CHX 写请求 Qos 配置 */ +#define FGDMA_CHX_MODE_WR_QOS_GET(qos) GET_REG32_BITS((qos), 15, 12) +/* 是否用 cx_wr_qos 中的值替换该通道写请求的 QoS:1 表示替换, 0 不替换 */ +#define FGDMA_CHX_MODE_WR_QOS_EN BIT(8) + +/* 是否用CHX qos cfg 中的值替换该读请求的 Qos:1 表示替换 ,0 不替换 */ +#define FGDMA_CHX_MODE_BDL_ROLL_EN BIT(4) +#define FGDMA_CHX_MODE_BDL_EN BIT(0) /* 配置当前采用direct 或者BDL 链表模式, 0 采用Direct 模式, 1 采用BDL 模式 */ + +/** @name FGDMA_CHX_INT_CTRL_OFFSET Register + */ +#define FGDMA_CHX_INT_CTRL_TRANS_END_ENABLE BIT(3) /* CHX 所以数据传输完成中断输出控制,1表示允许输出,0表示不允许 */ +#define FGDMA_CHX_INT_CTRL_BDL_END_ENABLE BIT(2) /* CHX bdl 条目数据传输完成中断输出控制,1表示允许输出,0表示不允许 */ +#define FGDMA_CHX_INT_CTRL_FIFO_FULL_ENABLE BIT(1) /* CHX ARM满中断 1表示允许输出,0表示不允许 */ +#define FGDMA_CHX_INT_CTRL_FIFO_EMPTY_ENABLE BIT(0) /* CHX ARM空中断 1表示允许输出,0表示不允许 */ + +/** @name FGDMA_CHX_INT_STATE_OFFSET Register + */ +#define FGDMA_CHX_INT_STATE_BUSY BIT(4) /* CHX 处于数据传输中,写1 清0 */ +#define FGDMA_CHX_INT_STATE_TRANS_END BIT(3) /* CHX 处于传输完成状态,direct 模式时,表示所有数据传输完成,BDL 模式表示当前所有BDL条目传输完成: 写1清0 */ +#define FGDMA_CHX_INT_STATE_BDL_END BIT(2) /* CHX 在BDL模式下,表示当前一个BDL 条目数据传输完成;Direct 模式下该位始终为0 , 写1清0 */ +#define FGDMA_CHX_INT_STATE_FIFO_FULL BIT(1) /* CHX FIFO 满状态,写1清0 */ +#define FGDMA_CHX_INT_STATE_FIFO_EMPTY BIT(0) /* CHX FIFO 空状态,写1清0 */ +#define FGDMA_CHX_INT_STATE_ALL GENMASK(4, 0) + +/** @name FGDMA_CHX_LVI_OFFSET Register + */ +#define FGDMA_CHX_LVI_SET(lvi) SET_REG32_BITS((lvi - 1), 31, 0) /* CHX last vaild index, 即链表末模式下BDL有效条目,实际有效条目=该寄存器值+1 */ + +/** @name FGDMA_CHX_TS_OFFSET Register + */ +#define FGDMA_CHX_TS_SET(ts) SET_REG32_BITS((ts), 31, 0) /* CHX 在direct 模式下操作的的总Byte数量 */ + +/** @name FGDMA_CHX_UPSADDR_OFFSET Register + */ +#define FGDMA_CHX_UPSADDR_SET(addr) SET_REG32_BITS((addr), 31, 0) /* CHX 源地址高32bits */ + +/** @name FGDMA_CHX_LWSADDR_OFFSET Register + */ +#define FGDMA_CHX_LWSADDR_SET(addr) SET_REG32_BITS((addr), 31, 0) /* CHX 源地址低32bits */ + +/** @name FGDMA_CHX_UPDADDR_OFFSET Register + */ +#define FGDMA_CHX_UPDADDR_SET(addr) SET_REG32_BITS((addr), 31, 0) /* CHX 目标地址高32bits */ + +/** @name FGDMA_CHX_LWDADDR_OFFSET Register + */ +#define FGDMA_CHX_LWDADDR_SET(addr) SET_REG32_BITS((addr), 31, 0) /* CHX 目标地址低32bits */ + +/** @name FGDMA_CHX_XFER_CFG_OFFSET Register + */ +#define FGDMA_CHX_XFER_CFG_AR_LEN_SET(len) SET_REG32_BITS((len), 31, 24) /* CHX 读请求Burst length 大小 */ +#define FGDMA_CHX_XFER_CFG_AR_SIZE_SET(size) SET_REG32_BITS((size), 22, 20) /* CHX 读请求Size 大小 , 支持 1、2、8、16 Byte */ +#define FGDMA_CHX_XFER_CFG_AR_BRUST_SET(type) SET_REG32_BITS((type), 17, 16) /* CHX 读请求Brust 类型: 0:fix ,1:incr */ +#define FGDMA_CHX_XFER_CFG_AW_LEN_SET(len) SET_REG32_BITS((len), 15, 8) /* CHX 写请求Burst length 大小 */ +#define FGDMA_CHX_XFER_CFG_AW_SIZE_SET(size) SET_REG32_BITS((size), 6, 4) /* CHX 写请求Size 大小 , 支持 1、2、8、16 Byte */ +#define FGDMA_CHX_XFER_CFG_AW_BRUST_SET(type) SET_REG32_BITS((type), 1, 0) /* CHX 写请求Brust 类型: 0:fix ,1:incr */ + +#define FGDMA_INCR 1U +#define FGDMA_FIX 0U + +#define FGDMA_MAX_BURST_LEN 8U + +/** @name FGDMA_CHX_LCP_OFFSET Register + */ +#define FGDMA_CHX_LCP_GET(reg_val) GET_REG32_BITS((reg_val), 31, 0) /* 当前操作了多少个 BDL 列表 */ + +/** @name FGDMA_CHX_SECCTL_OFFSET Register + */ +#define FGDMA_CHX_SECCTL_SET(val) GET_REG32_BITS((val), 31, 0) /* CHX 安全控制寄存器,仅安全状态可访问 */ + +/** @name FGDMA_CHX_SEC_ATST_OFFSET Register + */ +#define FGDMA_CHX_SEC_ATST_SET(val) SET_REG32_BITS((val), 31, 0) + +/** @name FGDMA_CHX_NSID_STRMID_OFFSET Register + */ +#define FGDMA_CHX_NSID_STRMID_SET(val) SET_REG32_BITS((val), 31, 0) + +/** @name FGDMA_CHX_AW_CFG_OFFSET Register + */ +#define FGDMA_CHX_AW_CFG_AWCACHE_SET(val) SET_REG32_BITS((val), 3, 0) /* CHX dma arcache */ +#define FGDMA_CHX_AW_CFG_AWREGION_MASK_SET(val) SET_REG32_BITS((val), 7, 4) /* CHX dma arregion */ +#define FGDMA_CHX_AW_CFG_AWPROT_SET(val) SET_REG32_BITS((val), 9, 8) +#define FGDMA_CHX_AW_CFG_AWDOMAIN_SET(val) SET_REG32_BITS((val), 13, 12) /* chx ardomain */ +#define FGDMA_CHX_AW_CFG_AWSNOOP_SET(val) SET_REG32_BITS((val), 18, 16) +#define FGDMA_CHX_AW_CFG_AWBAR_SET(val) SET_REG32_BITS((val), 21, 20) + +/** @name FGDMA_CHX_AR_CFG_OFFSET Register + */ +#define FGDMA_CHX_AR_CFG_AWCACHE_SET(val) SET_REG32_BITS((val), 3, 0) /* CHX dma arcache */ +#define FGDMA_CHX_AR_CFG_AWREGION_MASK_SET(val) SET_REG32_BITS((val), 7, 4) /* CHX dma arregion */ +#define FGDMA_CHX_AR_CFG_AWPROT_SET(val) SET_REG32_BITS((val), 9, 8) +#define FGDMA_CHX_AR_CFG_AWDOMAIN_SET(val) SET_REG32_BITS((val), 13, 12) /* chx ardomain */ +#define FGDMA_CHX_AR_CFG_AWSNOOP_SET(val) SET_REG32_BITS((val), 19, 16) +#define FGDMA_CHX_AR_CFG_AWBAR_SET(val) SET_REG32_BITS((val), 21, 20) + +/** @name FGDMA_CHX_SECRSP_OFFSET Register + */ +#define FGDMA_CHX_SECRSP BIT(0) /* response 安全控制位 */ + +#define FGDMA_OUTSTANDING 0xfU /* 实际outstanding数目为0xf + 1 */ + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FGDMA_READREG(base_addr, reg_offset) \ + FtIn32((base_addr) + (u32)(reg_offset)) + +#define FGDMA_WRITEREG(base_addr, reg_offset, data) \ + FtOut32((base_addr) + (u32)(reg_offset), (u32)(data)) + +/** + * @name: FGdmaDisable + * @msg: 去使能GDMA控制器 + * @return {void} 无 + * @param {uintptr} base_addr, GDMA控制器基地址 + */ +static inline void FGdmaDisable(uintptr base_addr) +{ + u32 reg_val = FGDMA_READREG(base_addr, FGDMA_CTL_OFFSET); + reg_val &= ~FGDMA_CTL_ENABLE; + FGDMA_WRITEREG(base_addr, FGDMA_CTL_OFFSET, reg_val); +} + +/** + * @name: FGdmaEnable + * @msg: 使能GDMA控制器 + * @return {void} 无 + * @param {uintptr} base_addr, GDMA控制器基地址 + */ +static inline void FGdmaEnable(uintptr base_addr) +{ + u32 reg_val = FGDMA_READREG(base_addr, FGDMA_CTL_OFFSET); + reg_val |= FGDMA_CTL_ENABLE; + FGDMA_WRITEREG(base_addr, FGDMA_CTL_OFFSET, reg_val); +} + +/** + * @name: FGdmaSoftwareReset + * @msg: 完成GDMA控制器软复位 + * @return {void} 无 + * @param {uintptr} base_addr, GDMA控制器基地址 + */ +static inline void FGdmaSoftwareReset(uintptr base_addr) +{ + int delay = 100; + u32 reg_val = FGDMA_READREG(base_addr, FGDMA_CTL_OFFSET); + reg_val |= FGDMA_CTL_SOFT_RESET; + FGDMA_WRITEREG(base_addr, FGDMA_CTL_OFFSET, reg_val); + + while (--delay > 0) + ; + + reg_val = FGDMA_READREG(base_addr, FGDMA_CTL_OFFSET); + reg_val &= ~FGDMA_CTL_SOFT_RESET; + FGDMA_WRITEREG(base_addr, FGDMA_CTL_OFFSET, reg_val); +} + +/** + * @name: FGdmaIrqEnable + * @msg: 开启GDMA控制器中断 + * @return {void} 无 + * @param {uintptr} base_addr, GDMA控制器基地址 + */ +static inline void FGdmaIrqEnable(uintptr base_addr) +{ + u32 reg_val = FGDMA_READREG(base_addr, FGDMA_INTR_CTRL_OFFSET); + reg_val |= FGDMA_CHX_INTR_GLOBAL_MASK; + FGDMA_WRITEREG(base_addr, FGDMA_INTR_CTRL_OFFSET, reg_val); +} + +/** + * @name: FGdmaIrqDisable + * @msg: 关闭GDMA控制器中断 + * @return {void} 无 + * @param {uintptr} base_addr, GDMA控制器基地址 + */ +static inline void FGdmaIrqDisable(uintptr base_addr) +{ + u32 reg_val = FGDMA_READREG(base_addr, FGDMA_INTR_CTRL_OFFSET); + reg_val &= ~FGDMA_CHX_INTR_GLOBAL_MASK; + FGDMA_WRITEREG(base_addr, FGDMA_INTR_CTRL_OFFSET, reg_val); +} + +/** + * @name: FGdmaChanDisable + * @msg: 去使能GDMA通道 + * @return {void} 无 + * @param {uintptr} base_addr, GDMA控制器基地址 + * @param {u32} chan, GDMA通道号 + */ +static inline void FGdmaChanDisable(uintptr base_addr, u32 chan) +{ + u32 reg_val = FGDMA_READREG(base_addr, FGDMA_CHX_CTL_OFFSET(chan)); + reg_val &= ~FGDMA_CHX_CTL_ENABLE; /* 禁用通道 */ + FGDMA_WRITEREG(base_addr, FGDMA_CHX_CTL_OFFSET(chan), reg_val); +} + +/** + * @name: FGdmaChanEnable + * @msg: 使能GDMA通道 + * @return {void} 无 + * @param {uintptr} base_addr, GDMA控制器基地址 + * @param {u32} chan, GDMA通道号 + */ +static inline void FGdmaChanEnable(uintptr base_addr, u32 chan) +{ + u32 reg_val = FGDMA_READREG(base_addr, FGDMA_CHX_CTL_OFFSET(chan)); + reg_val |= FGDMA_CHX_CTL_ENABLE; /* 使能通道 */ + FGDMA_WRITEREG(base_addr, FGDMA_CHX_CTL_OFFSET(chan), reg_val); +} + +/** + * @name: FGdmaChanIrqDisable + * @msg: 关闭GDMA通道中断 + * @return {void} 无 + * @param {uintptr} base_addr, GDMA控制器基地址 + * @param {u32} chan, GDMA通道号 + */ +static inline void FGdmaChanIrqDisable(uintptr base_addr, u32 chan) +{ + u32 reg_val = FGDMA_READREG(base_addr, FGDMA_INTR_CTRL_OFFSET); + reg_val &= ~FGDMA_CHX_INTR_MASK(chan); /* 禁用通道中断 */ + FGDMA_WRITEREG(base_addr, FGDMA_INTR_CTRL_OFFSET, reg_val); + FGDMA_WRITEREG(base_addr, FGDMA_CHX_INT_CTRL_OFFSET(chan), 0x0U); /* 禁用通道所有中断位 */ +} + +/** + * @name: FGdmaChanIrqEnable + * @msg: 打开GDMA通道中断 + * @return {void} 无 + * @param {uintptr} base_addr, GDMA控制器基地址 + * @param {u32} chan, GDMA通道号 + * @param {u32} umask, 使能的通道中断位 + */ +static inline void FGdmaChanIrqEnable(uintptr base_addr, u32 chan, u32 umask) +{ + u32 reg_val = FGDMA_READREG(base_addr, FGDMA_INTR_CTRL_OFFSET); + reg_val |= FGDMA_CHX_INTR_MASK(chan); /* 使能通道中断 */ + FGDMA_WRITEREG(base_addr, FGDMA_INTR_CTRL_OFFSET, reg_val); + FGDMA_WRITEREG(base_addr, FGDMA_CHX_INT_CTRL_OFFSET(chan), umask); /* 使能通道指定中断位 */ +} + +/** + * @name: FGdmaChanReset + * @msg: 完成GDMA通道软复位 + * @return {void} 无 + * @param {uintptr} base_addr, GDMA控制器基地址 + * @param {u32} chan, GDMA通道号 + */ +static inline void FGdmaChanReset(uintptr base_addr, u32 chan) +{ + int delay = 100; + u32 reg_val = FGDMA_READREG(base_addr, FGDMA_CHX_CTL_OFFSET(chan)); + reg_val |= FGDMA_CHX_CTL_SOFT_RESET; + FGDMA_WRITEREG(base_addr, FGDMA_CHX_CTL_OFFSET(chan), reg_val); + + while (--delay > 0) + ; + + reg_val = FGDMA_READREG(base_addr, FGDMA_CHX_CTL_OFFSET(chan)); + reg_val &= ~FGDMA_CHX_CTL_SOFT_RESET; + FGDMA_WRITEREG(base_addr, FGDMA_CHX_CTL_OFFSET(chan), reg_val); +} + +/** + * @name: FGdmaReadStatus + * @msg: 获取GDMA控制器中断状态 + * @return {u32} 中断状态位 + * @param {uintptr} base_addr, GDMA控制器基地址 + */ +static inline u32 FGdmaReadStatus(uintptr base_addr) +{ + return FGDMA_READREG(base_addr, FGDMA_INTR_STATE_OFFSET); +} + +/** + * @name: FGdmaReadChanStatus + * @msg: 获取GDMA通道中断状态 + * @return {u32} 中断状态位 + * @param {uintptr} base_addr, GDMA控制器基地址 + * @param {u32} chan, 通道号 + */ +static inline u32 FGdmaReadChanStatus(uintptr base_addr, u32 chan) +{ + return FGDMA_READREG(base_addr, FGDMA_CHX_INT_STATE_OFFSET(chan)); +} + +/** + * @name: FGdmaClearChanStatus + * @msg: 清除GDMA通道中断状态 + * @return {void} 无 + * @param {uintptr} base_addr, GDMA控制器基地址 + * @param {u32} chan, 通道号 + * @param {u32} status, 待清除的中断状态 + */ +static inline void FGdmaClearChanStatus(uintptr base_addr, u32 chan, u32 status) +{ + FGDMA_WRITEREG(base_addr, FGDMA_CHX_INT_STATE_OFFSET(chan), status); /* 写1清0 */ +} + +/** + * @name: FGdmaSetChanClock + * @msg: 打开/关闭GDMA通道时钟 + * @return {void} 无 + * @param {uintptr} base_addr, GDMA控制器基地址 + * @param {u32} chan, 通道号 + * @param {boolean} enable,TRUE: 打开时钟, FALSE: 关闭时钟 + */ +static inline void FGdmaSetChanClock(uintptr base_addr, u32 chan, boolean enable) +{ + u32 reg_val = FGDMA_READREG(base_addr, FGDMA_LP_OFFSET); + if (enable) + reg_val &= ~FGDMA_CHX_LP_CTL(chan); /* 写0开启通道时钟 */ + else + reg_val |= FGDMA_CHX_LP_CTL(chan); /* 写1关断通道时钟 */ + FGDMA_WRITEREG(base_addr, FGDMA_LP_OFFSET, reg_val); + + return; +} + +/************************** Function Prototypes ******************************/ + + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/dma/fgdma/fgdma_intr.c b/bsp/phytium/libraries/standalone/drivers/dma/fgdma/fgdma_intr.c new file mode 100644 index 0000000000..80ba4ea4d4 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/dma/fgdma/fgdma_intr.c @@ -0,0 +1,156 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgdma_intr.c + * Date: 2022-05-16 17:01:48 + * LastEditTime: 2022-05-16 17:01:49 + * Description:  This files is for interrupt api implmentation + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 huanghe 2021-11-5 init commit + * 1.1 zhugengyu 2022-5-16 modify according to tech manual. + */ + +/***************************** Include Files *********************************/ +#include + +#include "fdebug.h" +#include "fassert.h" + +#include "fgdma_hw.h" +#include "fgdma.h" +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FGDMA_DEBUG_TAG "GDMA-INTR" +#define FGDMA_ERROR(format, ...) FT_DEBUG_PRINT_E(FGDMA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGDMA_WARN(format, ...) FT_DEBUG_PRINT_W(FGDMA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGDMA_INFO(format, ...) FT_DEBUG_PRINT_I(FGDMA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGDMA_DEBUG(format, ...) FT_DEBUG_PRINT_D(FGDMA_DEBUG_TAG, format, ##__VA_ARGS__) + +#define FGDMA_CALL_EVT_HANDLER(express, dma_chan, args) \ + do \ + { \ + if (express) \ + { \ + express(dma_chan, args); \ + } \ + } while (0) + +/************************** Function Prototypes ******************************/ +/** + * @name: FGdmaChanIrqHandler + * @msg: GDMA通道中断处理函数 + * @return {void} 无 + * @param {FGdmaChan} *chan_p, GDMA通道实例 + */ +static void FGdmaChanIrqHandler(FGdmaChan *const chan_p) +{ + FGdma *const instance_p = chan_p->gdma; + FASSERT(instance_p); + uintptr base_addr = instance_p->config.base_addr; + u32 chan_status = FGdmaReadChanStatus(base_addr, chan_p->config.chan_id); + + if (FGDMA_CHX_INT_STATE_BUSY & chan_status) + { + FGDMA_CALL_EVT_HANDLER(chan_p->evt_handlers[FGDMA_CHAN_EVT_BUSY], + chan_p, chan_p->evt_handler_args[FGDMA_CHAN_EVT_BUSY]); + } + + if (FGDMA_CHX_INT_STATE_TRANS_END & chan_status) + { + FGDMA_CALL_EVT_HANDLER(chan_p->evt_handlers[FGDMA_CHAN_EVT_TRANS_END], + chan_p, chan_p->evt_handler_args[FGDMA_CHAN_EVT_TRANS_END]); + } + + if (FGDMA_CHX_INT_STATE_BDL_END & chan_status) + { + FGDMA_CALL_EVT_HANDLER(chan_p->evt_handlers[FGDMA_CHAN_EVT_BDL_END], + chan_p, chan_p->evt_handler_args[FGDMA_CHAN_EVT_BDL_END]); + } + + if (FGDMA_CHX_INT_STATE_FIFO_FULL & chan_status) + { + FGDMA_CALL_EVT_HANDLER(chan_p->evt_handlers[FGDMA_CHAN_EVT_FIFO_FULL], + chan_p, chan_p->evt_handler_args[FGDMA_CHAN_EVT_FIFO_FULL]); + } + + if (FGDMA_CHX_INT_STATE_FIFO_EMPTY & chan_status) + { + FGDMA_CALL_EVT_HANDLER(chan_p->evt_handlers[FGDMA_CHAN_EVT_FIFO_EMPTY], + chan_p, chan_p->evt_handler_args[FGDMA_CHAN_EVT_FIFO_EMPTY]); + } + + FGdmaClearChanStatus(base_addr, chan_p->config.chan_id, chan_status); + return; +} + +/** + * @name: FGdmaIrqHandler + * @msg: GDMA中断处理函数 + * @return {void} 无 + * @param {s32} vector, 中断号 + * @param {void} *args, 中断参数 + */ +void FGdmaIrqHandler(s32 vector, void *args) +{ + FASSERT(args); + FGdma *const instance_p = (FGdma * const)args; + FASSERT(FT_COMPONENT_IS_READY == instance_p->is_ready); + uintptr base_addr = instance_p->config.base_addr; + u32 chan_id; + + u32 status = FGdmaReadStatus(base_addr); + FGDMA_INFO("status: 0x%x", status); + + FGdmaIrqDisable(base_addr); + for (chan_id = FGDMA_CHAN0_INDEX; chan_id < FGDMA_NUM_OF_CHAN; chan_id++) + { + if (!(FGDMA_CHX_INTR_STATE(chan_id) & status)) + continue; + + /* channel interrupt happens */ + FASSERT_MSG((NULL != instance_p->chans[chan_id]), "invalid chan interrupt event !!!"); + FGdmaChanIrqHandler(instance_p->chans[chan_id]); + } + FGdmaIrqEnable(base_addr); + + return; +} + +/** + * @name: FGdmaChanRegisterEvtHandler + * @msg: 注册GDMA通道事件回调函数 + * @return {void} 无 + * @param {FGdmaChan} *chan_p, GDMA通道实例 + * @param {FGdmaChanEvtType} evt, 通道事件 + * @param {FGdmaChanEvtHandler} handler, 事件回调函数 + * @param {void} *handler_arg, 事件回调函数输入参数 + */ +void FGdmaChanRegisterEvtHandler(FGdmaChan *const chan_p, FGdmaChanEvtType evt, + FGdmaChanEvtHandler handler, void *handler_arg) +{ + FASSERT(chan_p); + FASSERT(FGDMA_CHAN_NUM_OF_EVT > evt); + + chan_p->evt_handlers[evt] = handler; + chan_p->evt_handler_args[evt] = handler_arg; + + return; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/dma/fgdma/fgdma_selftest.c b/bsp/phytium/libraries/standalone/drivers/dma/fgdma/fgdma_selftest.c new file mode 100644 index 0000000000..fe16b3e525 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/dma/fgdma/fgdma_selftest.c @@ -0,0 +1,87 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgdma_selftest.c + * Date: 2022-05-20 13:39:27 + * LastEditTime: 2022-05-20 13:39:27 + * Description:  This files is for self test implementation + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 huanghe 2021-11-5 init commit + * 1.1 zhugengyu 2022-5-16 modify according to tech manual. + */ +/***************************** Include Files *********************************/ +#include + +#include "fdebug.h" + +#include "fgdma_hw.h" +#include "fgdma.h" +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FGDMA_DEBUG_TAG "GDMA-TEST" +#define FGDMA_ERROR(format, ...) FT_DEBUG_PRINT_E(FGDMA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGDMA_WARN(format, ...) FT_DEBUG_PRINT_W(FGDMA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGDMA_INFO(format, ...) FT_DEBUG_PRINT_I(FGDMA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGDMA_DEBUG(format, ...) FT_DEBUG_PRINT_D(FGDMA_DEBUG_TAG, format, ##__VA_ARGS__) + +/************************** Function Prototypes ******************************/ + +/****************************************************************************/ +/** + * @name: FGdmaDumpRegisterVals + * @msg: 打印当前的GDMA寄存器值 + * @return {void} 无 + * @param {uintptr} base_addr, GDMA控制器基地址 + * @param {u32} max_chan, 打印从 0 ~ max_chan 的通道寄存器值 + */ +void FGdmaDumpRegisterVals(uintptr base_addr, u32 max_chan) +{ + FGDMA_DEBUG("ctrl[0x%x]: 0x%x", FGDMA_CTL_OFFSET, FGDMA_READREG(base_addr, FGDMA_CTL_OFFSET)); + FGDMA_DEBUG("state[0x%x]: 0x%x", FGDMA_INTR_STATE_OFFSET, FGDMA_READREG(base_addr, FGDMA_INTR_STATE_OFFSET)); + FGDMA_DEBUG("intr[0x%x]: 0x%x", FGDMA_INTR_CTRL_OFFSET, FGDMA_READREG(base_addr, FGDMA_INTR_CTRL_OFFSET)); + FGDMA_DEBUG("lp[0x%x] : 0x%x", FGDMA_LP_OFFSET, FGDMA_READREG(base_addr, FGDMA_LP_OFFSET)); + FGDMA_DEBUG("qos[0x%x] : 0x%x", FGDMA_QOS_CFG_OFFSET, FGDMA_READREG(base_addr, FGDMA_QOS_CFG_OFFSET)); + + for (u32 chan = FGDMA_CHAN0_INDEX; chan <= max_chan; chan++) + { + FGDMA_DEBUG("chan-%d", chan); + FGDMA_DEBUG(" ctrl[0x%x]: 0x%x", FGDMA_CHX_CTL_OFFSET(chan), FGDMA_READREG(base_addr, FGDMA_CHX_CTL_OFFSET(chan))); + FGDMA_DEBUG(" mode[0x%x]: 0x%x", FGDMA_CHX_MODE_OFFSET(chan), FGDMA_READREG(base_addr, FGDMA_CHX_MODE_OFFSET(chan))); + FGDMA_DEBUG(" intr[0x%x]: 0x%x", FGDMA_CHX_INT_CTRL_OFFSET(chan), FGDMA_READREG(base_addr, FGDMA_CHX_INT_CTRL_OFFSET(chan))); + FGDMA_DEBUG(" state[0x%x]: 0x%x", FGDMA_CHX_INT_STATE_OFFSET(chan), FGDMA_READREG(base_addr, FGDMA_CHX_INT_STATE_OFFSET(chan))); + FGDMA_DEBUG(" lvi[0x%x]: 0x%x", FGDMA_CHX_LVI_OFFSET(chan), FGDMA_READREG(base_addr, FGDMA_CHX_LVI_OFFSET(chan))); + FGDMA_DEBUG(" ts[0x%x]: 0x%x", FGDMA_CHX_TS_OFFSET(chan), FGDMA_READREG(base_addr, FGDMA_CHX_TS_OFFSET(chan))); + FGDMA_DEBUG(" src-up[0x%x]: 0x%x", FGDMA_CHX_UPSADDR_OFFSET(chan), FGDMA_READREG(base_addr, FGDMA_CHX_UPSADDR_OFFSET(chan))); + FGDMA_DEBUG(" src-low[0x%x]: 0x%x", FGDMA_CHX_LWSADDR_OFFSET(chan), FGDMA_READREG(base_addr, FGDMA_CHX_LWSADDR_OFFSET(chan))); + FGDMA_DEBUG(" dst-up[0x%x]: 0x%x", FGDMA_CHX_UPDADDR_OFFSET(chan), FGDMA_READREG(base_addr, FGDMA_CHX_UPDADDR_OFFSET(chan))); + FGDMA_DEBUG(" dst-low[0x%x]: 0x%x", FGDMA_CHX_LWDADDR_OFFSET(chan), FGDMA_READREG(base_addr, FGDMA_CHX_LWDADDR_OFFSET(chan))); + FGDMA_DEBUG(" xfer[0x%x]: 0x%x", FGDMA_CHX_XFER_CFG_OFFSET(chan), FGDMA_READREG(base_addr, FGDMA_CHX_XFER_CFG_OFFSET(chan))); + FGDMA_DEBUG(" lpc[0x%x]: 0x%x", FGDMA_CHX_LCP_OFFSET(chan), FGDMA_READREG(base_addr, FGDMA_CHX_LCP_OFFSET(chan))); + FGDMA_DEBUG(" sec[0x%x]: 0x%x", FGDMA_CHX_SECCTL_OFFSET(chan), FGDMA_READREG(base_addr, FGDMA_CHX_SECCTL_OFFSET(chan))); + FGDMA_DEBUG(" atst[0x%x]: 0x%x", FGDMA_CHX_SEC_ATST_OFFSET(chan), FGDMA_READREG(base_addr, FGDMA_CHX_SEC_ATST_OFFSET(chan))); + FGDMA_DEBUG(" nsid[0x%x]: 0x%x", FGDMA_CHX_NSID_STRMID_OFFSET(chan), FGDMA_READREG(base_addr, FGDMA_CHX_NSID_STRMID_OFFSET(chan))); + FGDMA_DEBUG(" aw[0x%x]: 0x%x", FGDMA_CHX_AW_CFG_OFFSET(chan), FGDMA_READREG(base_addr, FGDMA_CHX_AW_CFG_OFFSET(chan))); + FGDMA_DEBUG(" ar[0x%x]: 0x%x", FGDMA_CHX_AR_CFG_OFFSET(chan), FGDMA_READREG(base_addr, FGDMA_CHX_AR_CFG_OFFSET(chan))); + FGDMA_DEBUG(" resp[0x%x]: 0x%x", FGDMA_CHX_SECRSP_OFFSET(chan), FGDMA_READREG(base_addr, FGDMA_CHX_SECRSP_OFFSET(chan))); + } + + return; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/dma/fgdma/fgdma_sinit.c b/bsp/phytium/libraries/standalone/drivers/dma/fgdma/fgdma_sinit.c new file mode 100644 index 0000000000..bb315ae0c0 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/dma/fgdma/fgdma_sinit.c @@ -0,0 +1,64 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgdma_sinit.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:25:15 + * Description:  This files is for gdma static init + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 huanghe 2021-11-5 init commit + * 1.1 zhugengyu 2022-5-16 modify according to tech manual. + */ + +/***************************** Include Files *********************************/ + +#include "ftypes.h" +#include "fparameters.h" + +#include "fgdma.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Variable Definitions *****************************/ +extern const FGdmaConfig fgdma_cfg_tbl[FGDMA_INSTANCE_NUM]; + +/************************** Function Prototypes ******************************/ +/** + * @name: FGdmaLookupConfig + * @msg: 获取GDMA控制器默认配置 + * @return {const FGdmaConfig *} 控制器默认配置 + * @param {u32} instance_id, GDMA控制器ID + */ +const FGdmaConfig *FGdmaLookupConfig(u32 instance_id) +{ + const FGdmaConfig *ptr = NULL; + u32 index; + + for (index = 0; index < (u32)FGDMA_INSTANCE_NUM; index++) + { + if (fgdma_cfg_tbl[index].instance_id == instance_id) + { + ptr = &fgdma_cfg_tbl[index]; + break; + } + } + + return (const FGdmaConfig *)ptr; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/eth/Kconfig b/bsp/phytium/libraries/standalone/drivers/eth/Kconfig new file mode 100644 index 0000000000..f041d27764 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/eth/Kconfig @@ -0,0 +1,22 @@ + +menu "Eth Configuration" + config ENABLE_FXMAC + bool + prompt "Use FXMAC" + default n + + config ENABLE_FGMAC + bool + prompt "Use FGMAC" + default n + + if ENABLE_FGMAC + source "$STANDALONE_DIR/drivers/eth/fgmac/Kconfig" + endif + + if ENABLE_FXMAC + source "$STANDALONE_DIR/drivers/eth/fxmac/Kconfig" + endif + +endmenu + diff --git a/bsp/phytium/libraries/standalone/drivers/eth/fgmac/Kconfig b/bsp/phytium/libraries/standalone/drivers/eth/fgmac/Kconfig new file mode 100644 index 0000000000..af211a4c2d --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/eth/fgmac/Kconfig @@ -0,0 +1,13 @@ +choice FGMAC_PHY_TYPE + prompt "PHY Type" + default FGMAC_PHY_COMMON + help + Select PHY for FGMAC + + config FGMAC_PHY_COMMON + bool "Common" + config FGMAC_PHY_AR803X + bool "AR803X" + +endchoice # FGMAC_PHY_TYPE + diff --git a/bsp/phytium/libraries/standalone/drivers/eth/fgmac/fgmac.c b/bsp/phytium/libraries/standalone/drivers/eth/fgmac/fgmac.c new file mode 100644 index 0000000000..bef00ec931 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/eth/fgmac/fgmac.c @@ -0,0 +1,497 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgmac.c + * Date: 2022-04-06 14:46:52 + * LastEditTime: 2022-04-06 14:46:58 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +/***************************** Include Files *********************************/ +#include + +#include "fio.h" +#include "ferror_code.h" +#include "ftypes.h" +#include "fdebug.h" +#include "fassert.h" +#include "fgmac_hw.h" +#include "fgmac_phy.h" +#include "fgmac.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FGMAC_DEBUG_TAG "FGMAC" +#define FGMAC_ERROR(format, ...) FT_DEBUG_PRINT_E(FGMAC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGMAC_WARN(format, ...) FT_DEBUG_PRINT_W(FGMAC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGMAC_INFO(format, ...) FT_DEBUG_PRINT_I(FGMAC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGMAC_DEBUG(format, ...) FT_DEBUG_PRINT_D(FGMAC_DEBUG_TAG, format, ##__VA_ARGS__) + + +/************************** Function Prototypes ******************************/ +static FError FGmacReset(FGmac *instance_p); +static FError FGmacDmaConfigure(FGmac *instance_p); +static FError FGmacControllerConfigure(FGmac *instance_p); +/************************** Variable Definitions *****************************/ + +/*****************************************************************************/ + +/* 此文件主要为了完成用户对外接口,用户可以使用这些接口直接开始工作 */ + +/* - 包括用户API的定义和实现 + - 同时包含必要的OPTION方法,方便用户进行配置 + - 如果驱动可以直接进行I/O操作,在此源文件下可以将API 进行实现 */ + +/* - 包括用户API的定义和实现 + - 同时包含必要的OPTION方法,方便用户进行配置 + - 如果驱动可以直接进行I/O操作,在此源文件下可以将API 进行实现 */ + +/* + * @name: FGmacStop + * @msg: 停止FGMAC控制器寄存器 + * @return {*} + * @param {FGmac} *instance_p 驱动控制数据 + */ +void FGmacStop(FGmac *instance_p) +{ + FASSERT(instance_p); + uintptr base_addr = instance_p->config.base_addr; + u32 reg_val; + + /* disable dma tx and rx */ + reg_val = FGMAC_READ_REG32(base_addr, FGMAC_DMA_OP_OFFSET); + reg_val &= (~FGMAC_DMA_OP_ST); + reg_val &= (~FGMAC_DMA_OP_SR); + FGMAC_WRITE_REG32(base_addr, FGMAC_DMA_OP_OFFSET, reg_val); + + /* disable gmac tx and rx */ + reg_val = FGMAC_READ_REG32(base_addr, FGMAC_CONF_OFFSET); + reg_val &= (~FGMAC_CONF_TX_EN); + reg_val &= (~FGMAC_CONF_RX_EN); + FGMAC_WRITE_REG32(base_addr, FGMAC_CONF_OFFSET, reg_val); + +} + + +/** + * @name: FGmacCfgInitialize + * @msg: init FGMAC controller + * @param {FGmac} *instance_p, instance of FGmac controller + * @param {FGmacConfig} *cofig_p, input configuration parameters + * @return err code information, FGMAC_SUCCESS indicates success,others indicates failed + */ +FError FGmacCfgInitialize(FGmac *instance_p, const FGmacConfig *input_config_p) +{ + FASSERT(instance_p && input_config_p); + FError ret = FGMAC_SUCCESS; + + /* indicating device is started */ + if (FT_COMPONENT_IS_READY == instance_p->is_ready) + { + FGMAC_WARN("device is already initialized!!!"); + } + + /* de-initialize device instance */ + FGmacDeInitialize(instance_p); + instance_p->config = *input_config_p; + + + /*Phy Awaken*/ + + ret = FGmacPhyAwaken(instance_p); + if (FGMAC_SUCCESS != ret) + { + FGMAC_ERROR("phy awaken failed!"); + return ret; + } + + + /* initialize the gmac controller */ + ret = FGmacReset(instance_p); // permmit failed + if (FGMAC_SUCCESS != ret) + { + printf("FGmacReset failed \r\n"); + } + + + ret = FGmacControllerConfigure(instance_p); + if (FGMAC_SUCCESS != ret) + return ret; + + /* initialize the gmac dma controller */ + ret = FGmacDmaConfigure(instance_p); + if (FGMAC_SUCCESS == ret) + { + instance_p->is_ready = FT_COMPONENT_IS_READY; + } + + return ret; +} + +/** + * @name: FGmacDeInitialize + * @msg: deinit FGMAC controller + * @param {FGmac} *instance_p, instance of FGmac controller + * @return err code information, FGMAC_SUCCESS indicates success,others indicates failed + */ +FError FGmacDeInitialize(FGmac *instance_p) +{ + FASSERT(instance_p); + FError ret = FGMAC_SUCCESS; + + instance_p->is_ready = 0; + memset(instance_p, 0, sizeof(*instance_p)); + + return ret; +} + +/** + * @name: FGmacReset + * @msg: reset FGMAC controller + * @param {FGmac} *instance_p, instance of FGmac controller + * @return err code information, FGMAC_SUCCESS indicates success,others indicates failed + */ +static FError FGmacReset(FGmac *instance_p) +{ + FASSERT(instance_p); + FGmacMacAddr mac_addr; + uintptr base_addr = instance_p->config.base_addr; + FError ret = FGMAC_SUCCESS; + u32 reg_val; + + + /* backup mac address before software reset */ + memset(mac_addr, 0, sizeof(mac_addr)); + FGmacGetMacAddr(base_addr, mac_addr); + + /* disable all gmac & dma intr */ + FGmacSetInterruptMask(instance_p, FGMAC_CTRL_INTR, FGMAC_ISR_MASK_ALL_BITS); + FGmacSetInterruptMask(instance_p, FGMAC_DMA_INTR, FGMAC_DMA_INTR_ENA_ALL_MASK); + + /* stop gmac/dma tx and rx */ + FGmacStop(instance_p); + + /* do software reset per init */ + ret = FGmacSoftwareReset(base_addr, FGMAC_RETRY_TIMES); + + + /* disable gmac & dma interrupts */ + FGmacSetInterruptMask(instance_p, FGMAC_CTRL_INTR, FGMAC_ISR_MASK_ALL_BITS); + FGmacSetInterruptMask(instance_p, FGMAC_DMA_INTR, FGMAC_DMA_INTR_ENA_ALL_MASK); + + /* recover mac address after softwate reset */ + FGmacSetMacAddr(base_addr, mac_addr); + + return ret;// may return error +} + +/** + * @name: FGmacControllerSpeedConfig + * @msg: fgmac speed configuration + * @param {FGmac} *instance_p, instance of FGmac controller + * @param {u32} speed, speed value + * @return {*} + */ +void FGmacControllerSpeedConfig(FGmac *instance_p, u32 speed) +{ + FASSERT(instance_p); + uintptr base_addr = instance_p->config.base_addr; + u32 reg_val = 0; + + /* MAC配置寄存器 */ + reg_val = FGMAC_READ_REG32(base_addr, FGMAC_CONF_OFFSET); + + /* 设置通信速度FES=1000Mbps */ + if (speed == FGMAC_PHY_SPEED_1000) + { + reg_val &= (~FGMAC_CONF_PORTSELECT); + reg_val &= (~FGMAC_CONF_FES); + } + + /* 设置通信速度FES=100Mbps */ + if (speed == FGMAC_PHY_SPEED_100) + { + reg_val |= FGMAC_CONF_PORTSELECT; + reg_val |= FGMAC_CONF_FES; + } + + /* 设置通信速度FES=10Mbps */ + if (speed == FGMAC_PHY_SPEED_10) + { + reg_val |= FGMAC_CONF_PORTSELECT; + reg_val &= (~FGMAC_CONF_FES); + } + + FGMAC_WRITE_REG32(base_addr, FGMAC_CONF_OFFSET, reg_val); +} + +/** + * @name: FGmacControllerDuplexConfig + * @msg: fgmac deplex mode configuration + * @param {FGmac} *instance_p, instance of FGmac controller + * @param {u32} duplex, duplex mode + * @return {*} + */ +void FGmacControllerDuplexConfig(FGmac *instance_p, u32 duplex) +{ + FASSERT(instance_p); + uintptr base_addr = instance_p->config.base_addr; + u32 reg_val = 0; + + /* MAC配置寄存器 */ + reg_val = FGMAC_READ_REG32(base_addr, FGMAC_CONF_OFFSET); + + /* 设置双工模式 */ + if (duplex == FGMAC_PHY_MODE_FULLDUPLEX) + reg_val |= FGMAC_CONF_DUPLEX_MODE; + else + reg_val &= ~FGMAC_CONF_DUPLEX_MODE; + + FGMAC_WRITE_REG32(base_addr, FGMAC_CONF_OFFSET, reg_val); + +} + +/** + * @name: FGmacControllerConfigure + * @msg: config FGMAC controller + * @param {FGmac} *instance_p, instance of FGmac controller + * @return err code information, FGMAC_SUCCESS indicates success,others indicates failed + */ +static FError FGmacControllerConfigure(FGmac *instance_p) +{ + FASSERT(instance_p); + FGmacMacAddr mac_addr; + uintptr base_addr = instance_p->config.base_addr; + FError ret = FGMAC_SUCCESS; + u32 reg_val = 0; + + /********gmac ctrl reg init*********/ + /* 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 */ + + /* MAC配置寄存器 */ + reg_val = FGMAC_READ_REG32(base_addr, FGMAC_CONF_OFFSET); + + /* 使能载波侦听DCRS、失能环回模式LM */ + reg_val &= ~(FGMAC_CONF_DCRS | FGMAC_CONF_LOOPBACK_MODE); + + /* 设置帧内间隔IFG */ + reg_val |= FGMAC_CONF_IFG(0); + + /* 1000Mbps default */ + reg_val &= (~FGMAC_CONF_PORTSELECT); + reg_val &= (~FGMAC_CONF_FES); + + /* 双工模式 */ + if (instance_p->config.duplex_mode) + reg_val |= FGMAC_CONF_DUPLEX_MODE; + else + reg_val &= ~FGMAC_CONF_DUPLEX_MODE; + + /* 使能校验和卸载IPS */ + if (FGMAC_CHECKSUM_BY_HARDWARE == instance_p->config.cheksum_mode) + reg_val |= FGMAC_CONF_IPC; + else + reg_val &= ~FGMAC_CONF_IPC; + + /* 重发DR=1, 重发一次 */ + reg_val |= FGMAC_CONF_DISABLE_RETRY; + + /* 自动 PAD/ CRC 剥线, 全双工模式保留 */ + reg_val |= FGMAC_CONF_ACS; + + /* 后退限制, 全双工模式保留 */ + reg_val |= FGMAC_CONF_BL(0); + + /* 延期检查禁用 */ + reg_val &= ~FGMAC_CONF_DC; + + /* 使能类型帧的CRC剥离、禁用看门狗WD、禁用Jabber JD、帧突发启用BE、不能自接收DO(全双工保留)*/ + reg_val |= (FGMAC_CONF_CST | FGMAC_CONF_WD | FGMAC_CONF_JD | FGMAC_CONF_BE | FGMAC_CONF_DO); + + FGMAC_WRITE_REG32(base_addr, FGMAC_CONF_OFFSET, reg_val); + + /********gmac filter reg init*********/ + /* 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 */ + + /* MAC帧过滤寄存器 */ + reg_val = FGMAC_READ_REG32(base_addr, FGMAC_FRAME_FILTER_OFFSET); + + /* 全部接收RA */ + reg_val |= FGMAC_FRAME_FILTER_RA; + + /* 通过控制帧PCF 10b */ + reg_val |= FGMAC_FRAME_FILTER_PCF(2); + + /* 失能禁用广播帧DBF */ + reg_val &= ~FGMAC_FRAME_FILTER_DBF; + + /* 失能通过所有多播PM */ + reg_val &= ~FGMAC_FRAME_FILTER_PM; + + /* 失能目的地址反向过滤DAIF */ + reg_val &= ~FGMAC_FRAME_FILTER_DAIF; + + /* 失能哈希单播PR */ + reg_val &= ~FGMAC_FRAME_FILTER_PR; + FGMAC_WRITE_REG32(base_addr, FGMAC_FRAME_FILTER_OFFSET, reg_val); + + /********hash reg*********/ + FGMAC_WRITE_REG32(base_addr, FGMAC_HASH_HIGH_OFFSET, 0x0); + FGMAC_WRITE_REG32(base_addr, FGMAC_HASH_LOW_OFFSET, 0x0); + + /********gmac flow ctrl reg init*********/ + + /* 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 */ + reg_val = FGMAC_READ_REG32(base_addr, FGMAC_FLOW_CTRL_OFFSET); + /* 禁用自动零暂停帧生成DZPQ */ + reg_val |= FGMAC_FLOW_DZPQ; + + /* 暂停低阈值PLT */ + reg_val |= FGMAC_FLOW_PLT(0); + + /* 禁用接收流控制RFE */ + reg_val &= ~FGMAC_FLOW_RFE; + + /* 禁用传输流控制TFE */ + reg_val &= ~FGMAC_FLOW_TFE; + FGMAC_WRITE_REG32(base_addr, FGMAC_FLOW_CTRL_OFFSET, reg_val); + + /********vlan tag reg*********/ + /* Set the ETV bit according to ETH VLANTagComparison value */ + /* Set the VL bit according to ETH VLANTagIdentifier value */ + /* 接收帧的 VLAN 标记标识符 VL */ + reg_val = FGMAC_VLAN_TAG_VL(0); + /* 设置VLAN 标记比较的位数ETV 1-12bit 0-16bit */ + reg_val &= ~FGMAC_VLAN_TAG_ETV; + FGMAC_WRITE_REG32(base_addr, FGMAC_VLAN_TAG_OFFSET, reg_val); + + return ret; +} + +static FError FGmacDmaConfigure(FGmac *instance_p) +{ + FASSERT(instance_p); + u32 reg_val; + FError ret = FGMAC_SUCCESS; + uintptr base_addr = instance_p->config.base_addr; + + /********DMA总线模式寄存器*********/ + /* 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 */ + reg_val = FGMAC_READ_REG32(base_addr, FGMAC_DMA_BUS_MODE_OFFSET); + reg_val |= FGMAC_DMA_BUS_AAL; + /* 使用单独的 PBL USP */ + reg_val |= FGMAC_DMA_BUS_USP; + /* RxDMA PBL RPBL */ + reg_val |= FGMAC_DMA_BUS_RPBL(32); + + /* 固定突发 FB */ + reg_val |= FGMAC_DMA_BUS_FB; + + /* 控制 RxDMA 和 TxDMA 之间的加权循环仲裁中的优先级比率 PR */ + reg_val |= FGMAC_DMA_BUS_PR(0); + + /* 可编程突发长度 PBL */ + reg_val |= FGMAC_DMA_BUS_PBL(32); + + /* 交替描述表大小 ATDS */ + reg_val |= FGMAC_DMA_BUS_ATDS; + + + FGMAC_WRITE_REG32(base_addr, FGMAC_DMA_BUS_MODE_OFFSET, reg_val); + + /* dma set bus mode */ + // FGMAC_WRITE_REG32(base_addr, FGMAC_DMA_BUS_MODE_OFFSET, FGMAC_DMA_BUS_INIT); + + /********DMA操作模式寄存器*********/ + /* 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 */ + reg_val = FGMAC_READ_REG32(base_addr, FGMAC_DMA_OP_OFFSET); + reg_val &= FGMAC_DMA_OP_CLEAR_MASK; + /* 丢弃 TCP / IP 校验和错误帧 DT */ + reg_val &= ~FGMAC_DMA_OP_DT; + + /* 接收存储转发 RSF */ + reg_val |= FGMAC_DMA_OP_RSF; + + /* 刷新正在接收的帧 DFF */ + reg_val &= ~FGMAC_DMA_OP_DFF; + + /* 发送存储和转发 TSF */ + reg_val |= FGMAC_DMA_OP_TSF; + + /* 传输阈值控制 TTC */ + reg_val |= FGMAC_DMA_OP_TTC(7); + + /* 在第二帧上操作 OSF */ + reg_val |= FGMAC_DMA_OP_OSF; + FGMAC_WRITE_REG32(base_addr, FGMAC_DMA_OP_OFFSET, reg_val); + + /* 刷新DMA发送FIFO FTF */ + ret = FGmacFlushTxFifo(base_addr, FGMAC_RETRY_TIMES); + + if (FGMAC_SUCCESS != ret) + { + printf("FGmac Flush Failed\r\n"); + } + + /* AXI 突发长度 BLEN 16,8,4 */ + reg_val = (FGMAC_DMA_AXI_BUS_MOD_BLEN16 | FGMAC_DMA_AXI_BUS_MOD_BLEN8 | FGMAC_DMA_AXI_BUS_MOD_BLEN4); + FGMAC_WRITE_REG32(base_addr, FGMAC_DMA_AXI_BUS_MOD_OFFSET, reg_val); + + return ret; +} diff --git a/bsp/phytium/libraries/standalone/drivers/eth/fgmac/fgmac.h b/bsp/phytium/libraries/standalone/drivers/eth/fgmac/fgmac.h new file mode 100644 index 0000000000..e4dc25d5fc --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/eth/fgmac/fgmac.h @@ -0,0 +1,259 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgmac.h + * Date: 2022-04-06 14:46:52 + * LastEditTime: 2022-04-06 14:46:58 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#ifndef DRIVERS_ETH_FGMAC_H +#define DRIVERS_ETH_FGMAC_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ + +#include "ftypes.h" +#include "fassert.h" +#include "ferror_code.h" +#include "fkernel.h" + +#define FGMAC_PHY_MAX_NUM 32U + +/************************** Constant Definitions *****************************/ +#define FGMAC_SUCCESS FT_SUCCESS +#define FGMAC_ERR_TIMEOUT FT_MAKE_ERRCODE(ErrModBsp, ErrBspEth, 1) +#define FGMAC_ERR_INVALID_DMA_MEM FT_MAKE_ERRCODE(ErrModBsp, ErrBspEth, 2) +#define FGMAC_ERR_NOT_READY FT_MAKE_ERRCODE(ErrModBsp, ErrBspEth, 3) +#define FGMAC_ERR_TRANS_FAILED FT_MAKE_ERRCODE(ErrModBsp, ErrBspEth, 4) +#define FGMAC_ERR_PHY_NOT_SUPPORT FT_MAKE_ERRCODE(ErrModBsp, ErrBspEth, 5) +#define FGMAC_ERR_PHY_IS_NOT_FOUND FT_MAKE_ERRCODE(ErrModBsp, ErrBspEth, 6) +#define FGMAC_ERR_FAILED FT_MAKE_ERRCODE(ErrModBsp, ErrBspEth, 7) +#define FGMAC_ERR_PHY_AUTO_FAILED FT_MAKE_ERRCODE(ErrModBsp, ErrBspEth, 8) + + +#define FGMAC_ERR_CODE_PREFIX FGMAC_ERR_TIMEOUT & (FT_ERRCODE_SYS_MODULE_MASK | FT_ERRCODE_SUB_MODULE_MASK) +#define FGMAC_NUM_OF_ERR_CODE 5 + +/********发送描述符********/ + +/* TDES0 包含发送的帧状态和描述符所有权信息 */ +#define FGMAC_DMA_TDES0_DEFERRED BIT(0) +#define FGMAC_DMA_TDES0_UNDERFLOW_ERROR BIT(1) +#define FGMAC_DMA_TDES0_EXCESSIVE_DEFERRAL BIT(2) +#define FGMAC_DMA_TDES0_COLLISION_COUNT_MASK GENMASK(6, 3) +#define FGMAC_DMA_TDES0_VLAN_FRAME BIT(7) +#define FGMAC_DMA_TDES0_EXCESSIVE_COLLISIONS BIT(8) +#define FGMAC_DMA_TDES0_LATE_COLLISION BIT(9) +#define FGMAC_DMA_TDES0_NO_CARRIER BIT(10) +#define FGMAC_DMA_TDES0_LOSS_CARRIER BIT(11) +#define FGMAC_DMA_TDES0_PAYLOAD_ERROR BIT(12) +#define FGMAC_DMA_TDES0_FRAME_FLUSHED BIT(13) +#define FGMAC_DMA_TDES0_JABBER_TIMEOUT BIT(14) +#define FGMAC_DMA_TDES0_ERROR_SUMMARY BIT(15) +#define FGMAC_DMA_TDES0_IP_HEADER_ERROR BIT(16) +#define FGMAC_DMA_TDES0_TIME_STAMP_STATUS BIT(17) /* 指示已捕获相应发送帧的时间戳 */ +#define FGMAC_DMA_TDES0_OWN BIT(31) /* 该位表示描述符归 DMA 所有 */ + +/* TDES1 包含缓冲区大小和控制描述符链/环和正在传输的帧的其他位 */ +#define FGMAC_DMA_TDES1_BUFFER1_SIZE_MASK GENMASK(10, 0) +#define FGMAC_DMA_TDES1_BUFFER2_SIZE_MASK GENMASK(21, 11) +#define FGMAC_DMA_TDES1_BUFFER2_SIZE_SHIFT 11 +#define FGMAC_DMA_TDES1_TIME_STAMP_ENABLE BIT(22) +#define FGMAC_DMA_TDES1_DISABLE_PADDING BIT(23) +#define FGMAC_DMA_TDES1_SECOND_ADDRESS_CHAINED BIT(24) +#define FGMAC_DMA_TDES1_END_RING BIT(25) +#define FGMAC_DMA_TDES1_CRC_DISABLE BIT(26) +#define FGMAC_DMA_TDES1_CHECKSUM_INSERTION_MASK GENMASK(28, 27) +#define FGMAC_DMA_TDES1_CHECKSUM_INSERTION_SHIFT 27 +#define FGMAC_DMA_TDES1_FIRST_SEGMENT BIT(29) +#define FGMAC_DMA_TDES1_LAST_SEGMENT BIT(30) +#define FGMAC_DMA_TDES1_INTERRUPT BIT(31) + +/********接收描述符********/ + +/* RDES0 包含接收的帧状态,帧长度和描述符所有权信息 */ +#define FGMAC_DMA_RDES0_PAYLOAD_CSUM_ERR BIT(0) +#define FGMAC_DMA_RDES0_CRC_ERROR BIT(1) +#define FGMAC_DMA_RDES0_DRIBBLING BIT(2) +#define FGMAC_DMA_RDES0_MII_ERROR BIT(3) +#define FGMAC_DMA_RDES0_RECEIVE_WATCHDOG BIT(4) +#define FGMAC_DMA_RDES0_FRAME_TYPE BIT(5) +#define FGMAC_DMA_RDES0_COLLISION BIT(6) +#define FGMAC_DMA_RDES0_IPC_CSUM_ERROR BIT(7) +#define FGMAC_DMA_RDES0_LAST_DESCRIPTOR BIT(8) +#define FGMAC_DMA_RDES0_FIRST_DESCRIPTOR BIT(9) +#define FGMAC_DMA_RDES0_VLAN_TAG BIT(10) +#define FGMAC_DMA_RDES0_OVERFLOW_ERROR BIT(11) +#define FGMAC_DMA_RDES0_LENGTH_ERROR BIT(12) +#define FGMAC_DMA_RDES0_SA_FILTER_FAIL BIT(13) +#define FGMAC_DMA_RDES0_DESCRIPTOR_ERROR BIT(14) +#define FGMAC_DMA_RDES0_ERROR_SUMMARY BIT(15) +#define FGMAC_DMA_RDES0_FRAME_LEN_MASK (0x3FFF << 16) /* GENMASK(29, 16)*/ +#define FGMAC_DMA_RDES0_FRAME_LEN_SHIFT 16 +#define FGMAC_DMA_RDES0_DA_FILTER_FAIL BIT(30) +#define FGMAC_DMA_RDES0_OWN BIT(31) + +/* RDES1 包含缓冲区大小和控制描述符链/环的其他位 */ +#define FGMAC_DMA_RDES1_BUFFER1_SIZE_MASK GENMASK(10, 0) +#define FGMAC_DMA_RDES1_BUFFER2_SIZE_MASK GENMASK(21, 11) +#define FGMAC_DMA_RDES1_BUFFER2_SIZE_SHIFT 11 +#define FGMAC_DMA_RDES1_SECOND_ADDRESS_CHAINED BIT(24) +#define FGMAC_DMA_RDES1_END_RING BIT(25) +#define FGMAC_DMA_RDES1_DISABLE_IC BIT(31) + +/* Operational Mode */ +enum +{ + FGMAC_CHECKSUM_BY_SOFTWARE = 0, + FGMAC_CHECKSUM_BY_HARDWARE +}; /* software checksum or hardware checksum */ + +enum +{ + FGMAC_TX_COMPLETE_EVT = 0, + FGMAC_RX_COMPLETE_EVT, + FGMAC_LINK_STATUS_EVT, + FGMAC_PHY_STATUS_EVT, + FGMAC_DMA_ERR_EVT, + + FGMAC_INTR_EVT_NUM +}; /* interrupt event type */ + + +/**************************** Type Definitions *******************************/ + +/** + * This typedef contains configuration information for the device. + */ +typedef struct +{ + u32 instance_id; /* device instance id */ + uintptr base_addr; /* device base address */ + u32 irq_num; /* irq num */ + u32 irq_prority; /* device intrrupt priority */ + u32 cheksum_mode; /* hardware or software checksum */ + u32 duplex_mode; /* selects the MAC duplex mode: Half-Duplex or Full-Duplex mode */ + u32 max_packet_size; /* max num of bytes in frame transfer */ + u32 mdc_clk_hz; /* MDC clock access PHY. [1.0MHz ~2.5MHz] */ + boolean en_auto_negtiation; /* auto-negotiation or not */ + u32 speed; /* sets the Ethernet speed: 10/100/1000 Mbps. */ + +} FGmacConfig; /* FGMAC 驱动配置数据 */ + +/** + * This typedef contains driver instance data. The user is required to allocate a + * variable of this type for every device in the system. A pointer + * to a variable of this type is then passed to the driver API functions. + */ + +typedef struct +{ + volatile u32 status; + u32 ctrl; + u32 buf_addr; + u32 next; +} FGmacDmaDesc; /* FGMAC DMA描述符 */ + +typedef struct +{ + u32 desc_idx; /* For Current Desc position */ + u32 desc_buf_idx; /* For Current Desc buffer buf position */ + u32 desc_max_num; /* Max Number for Desc and Desc buffer */ + u8 *desc_buf_base; /* Desc buffer Base */ +} FGmacRingDescData; /* FGMAC DMA描述符表(链式)相关数据 */ + +typedef void (*FGmacEvtHandler)(void *pCtrl); + +typedef struct +{ + FGmacConfig config; /* Current active configs */ + u32 is_ready; /* Device is initialized and ready */ + FGmacRingDescData rx_ring; /* RX DMA descriptor data (idx, length) */ + volatile FGmacDmaDesc *rx_desc; /* RX DMA descriptor table in ring */ + FGmacRingDescData tx_ring; /* TX DMA descriptor data (idx, length) */ + volatile FGmacDmaDesc *tx_desc; /* TX DMA descriptor table in ring */ + FGmacEvtHandler evt_handler[FGMAC_INTR_EVT_NUM]; /* User registered interrupt handler */ + u32 phy_valid_mask; /* phy valid addr sequence mask */ + u32 phy_speed; + u32 phy_addr; /* phy max valid addr, or the unique value */ + u16 phy_id1; /*phy tag,only value to identify phy*/ +} FGmac; /* FGMAC 驱动控制数据 */ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ +/* 获取FGMAC驱动的默认配置参数 */ +const FGmacConfig *FGmacLookupConfig(u32 instance_id); + +/* 完成FGMAC驱动实例的初始化,使之可以使用 */ +FError FGmacCfgInitialize(FGmac *instance_p, const FGmacConfig *cofig_p); + +/* 完成FGMAC驱动实例去使能,清零实例数据 */ +FError FGmacDeInitialize(FGmac *instance_p); + +/* 配置FGMAC的发送DMA描述符和缓冲区 */ +FError FGmacSetupTxDescRing(FGmac *instance_p, volatile FGmacDmaDesc *tx_desc_tbl, + u8 *tx_buf, const fsize_t tx_pre_buf_len, const fsize_t tx_buf_num); + +/* 配置FGMAC的接收DMA描述符和缓冲区 */ +FError FGmacSetupRxDescRing(FGmac *instance_p, volatile FGmacDmaDesc *rx_desc_tbl, + u8 *rx_buf, const fsize_t rx_pre_buf_len, const fsize_t rx_buf_num); + +/* FGMAC中断处理函数 */ +void FGmacInterruptHandler(s32 vector, void *param); + +/* 注册FGMAC中断事件处理函数 */ +void FGmacRegisterEvtHandler(FGmac *instance_p, u32 evt, FGmacEvtHandler handler); + +/* 使能FGMAC DMA,使之可以接收/发送数据 */ +FError FGmacStartTrans(FGmac *instance_p); + +/* 去使能FGMAC DMA, 使之不再能接收/发送数据 */ +FError FGmacStopTrans(FGmac *instance_p); + +/* 通过FGMAC接收数据帧 */ +FError FGmacRecvFrame(FGmac *instance_p); + +/* 通过FGMAC发送数据帧 */ +FError FGmacSendFrame(FGmac *instance_p, u32 frame_len); + +/* 设置FGMAC中断屏蔽位 */ +void FGmacSetInterruptMask(FGmac *instance_p, u32 intr_type, u32 mask); + +/* 设置FGMAC中断使能位 */ +void FGmacSetInterruptUmask(FGmac *instance_p, u32 intr_type, u32 mask); + +/*fgmac deplex mode configuration */ +void FGmacControllerDuplexConfig(FGmac *instance_p, u32 duplex); + +/*fgmac speed configuration */ +void FGmacControllerSpeedConfig(FGmac *instance_p, u32 speed); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/bsp/phytium/libraries/standalone/drivers/eth/fgmac/fgmac_dma.c b/bsp/phytium/libraries/standalone/drivers/eth/fgmac/fgmac_dma.c new file mode 100644 index 0000000000..019c4f659a --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/eth/fgmac/fgmac_dma.c @@ -0,0 +1,342 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgmac_dma.c + * Date: 2022-04-06 14:46:52 + * LastEditTime: 2022-04-06 14:46:58 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +/***************************** Include Files *********************************/ + +#include +#include "fassert.h" +#include "fkernel.h" +#include "fcache.h" +#include "fdebug.h" +#include "fgmac.h" +#include "fgmac_hw.h" + +/************************** Constant Definitions *****************************/ +#if defined(__aarch64__) + #define FGMAC_DMA_IS_64_BIT_MEMORY(addr) (GENMASK_ULL(63, 32) & (uintptr)(addr)) +#else + #define FGMAC_DMA_IS_64_BIT_MEMORY(addr) (FALSE) +#endif + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FGMAC_DEBUG_TAG "FGMAC-DMA" +#define FGMAC_ERROR(format, ...) FT_DEBUG_PRINT_E(FGMAC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGMAC_WARN(format, ...) FT_DEBUG_PRINT_W(FGMAC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGMAC_INFO(format, ...) FT_DEBUG_PRINT_I(FGMAC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGMAC_DEBUG(format, ...) FT_DEBUG_PRINT_D(FGMAC_DEBUG_TAG, format, ##__VA_ARGS__) + +/************************** Variable Definitions *****************************/ + +/************************** Function Prototypes ******************************/ + +/** + * @name: FGmacSetupTxDescRing + * @msg: 配置FGMAC的接收DMA描述符和缓冲区 + * @param {FGmac *}instance_p 驱动控制数据 + * {volatile FGmacDmaDesc *} rx_desc_tbl 接收DMA描述符表(数组) + * {u8} *rx_buf 接收DMA缓冲区(数组,每一个描述符对应一个缓冲区) + * {const fsize_t} rx_pre_buf_len 单个DMA缓冲区的字节数 + * {const fsize_t} rx_buf_num DMA描述符或者DMA缓存区的数目 + * @return {FError} RX DMA初始化的错误码信息,FGMAC_SUCCESS 表示RX DMA初始化成功,其它返回值表示RX DMA初始化失败 + * @note 传入的rx_desc_tbl和rx_buf必须为32位空间地址 + */ +FError FGmacSetupRxDescRing(FGmac *instance_p, volatile FGmacDmaDesc *rx_desc_tbl, + u8 *rx_buf, const fsize_t rx_pre_buf_len, const fsize_t rx_buf_num) +{ + FASSERT(instance_p && rx_desc_tbl && rx_buf); + u32 i; + volatile FGmacDmaDesc *cur_rx_desc; + FGmacRingDescData *rx_ring_p = &instance_p->rx_ring; + uintptr base_addr = instance_p->config.base_addr; + void *desc_end = (void *)(rx_desc_tbl + rx_buf_num * sizeof(FGmacDmaDesc)); + void *buf_end = (void *)(rx_buf + rx_buf_num * rx_pre_buf_len); + + /* check if end address of descriptor or buffer is in 64 bit memory, + if TRUE, return error because DMA register can only hold 32 bit memory address */ + if ((FGMAC_DMA_IS_64_BIT_MEMORY(desc_end)) || (FGMAC_DMA_IS_64_BIT_MEMORY(buf_end))) + { + FGMAC_ERROR("invalid rx descriptor memory %p or rx dma buf memory %p", + desc_end, buf_end); + return FGMAC_ERR_INVALID_DMA_MEM; + } + + /* init rx dma ring data */ + memset(rx_ring_p, 0, sizeof(*rx_ring_p)); + rx_ring_p->desc_max_num = rx_buf_num; /* total num of rx desc and rx buf */ + rx_ring_p->desc_idx = 0; /* idx of rx desc */ + rx_ring_p->desc_buf_idx = 0; /* idx of rx buf */ + rx_ring_p->desc_buf_base = rx_buf; /* base addr of rx buf */ + + /* init rx dma descriptor table */ + memset((void *)rx_desc_tbl, 0, sizeof(FGmacDmaDesc) * rx_buf_num); + + for (i = 0; i < rx_buf_num; i++) + { + cur_rx_desc = rx_desc_tbl + i; + cur_rx_desc->status = FGMAC_DMA_RDES0_OWN; + cur_rx_desc->ctrl = (FGMAC_DMA_RDES1_BUFFER1_SIZE_MASK & rx_pre_buf_len); + FCacheDCacheInvalidateRange((uintptr)&rx_buf[i * rx_pre_buf_len], rx_pre_buf_len); + cur_rx_desc->buf_addr = (u32)((uintptr)&rx_buf[i * rx_pre_buf_len]); + + if ((rx_buf_num - 1) == i) + { + cur_rx_desc->ctrl |= FGMAC_DMA_RDES1_END_RING; + } + } + + /* flush descriptor */ + instance_p->rx_desc = rx_desc_tbl; + FCacheDCacheInvalidateRange((uintptr)instance_p->rx_desc, sizeof(FGmacDmaDesc) * rx_buf_num); + + FGMAC_WRITE_REG32(base_addr, FGMAC_DMA_RX_LIST_BASE_OFFSET, (u32)(uintptr)rx_desc_tbl); + return FGMAC_SUCCESS; +} + +/** + * @name: FGmacSetupTxDescRing + * @msg: 配置FGMAC的发送DMA描述符和缓冲区 + * @param {FGmac *}instance_p 驱动控制数据 + * {volatile FGmacDmaDesc *} tx_desc_tbl 发送DMA描述符表(数组) + * {u8} *tx_buf 发送DMA缓冲区(数组,每一个描述符对应一个缓冲区) + * {const fsize_t} tx_pre_buf_len 单个DMA缓冲区的字节数 + * {const fsize_t} tx_buf_num DMA描述符或者DMA缓存区的数目 + * @return {FError} TX DMA初始化的错误码信息,FGMAC_SUCCESS 表示TX DMA初始化成功,其它返回值表示TX DMA初始化失败 + * @note 传入的tx_desc_tbl和tx_buf必须为32位空间地址 + */ +FError FGmacSetupTxDescRing(FGmac *instance_p, volatile FGmacDmaDesc *tx_desc_tbl, + u8 *tx_buf, const fsize_t tx_pre_buf_len, const fsize_t tx_buf_num) +{ + FASSERT(instance_p && tx_desc_tbl && tx_buf); + u32 i; + volatile FGmacDmaDesc *cur_tx_desc; + FGmacRingDescData *tx_ring_p = &instance_p->tx_ring; + uintptr base_addr = instance_p->config.base_addr; + void *desc_end = (void *)(tx_desc_tbl + tx_buf_num * sizeof(FGmacDmaDesc)); + void *buf_end = (void *)(tx_buf + tx_buf_num * tx_pre_buf_len); + + /* check if end address of descriptor or buffer is in 64 bit memory, + if TRUE, return error because DMA register can only hold 32 bit memory address */ + if ((FGMAC_DMA_IS_64_BIT_MEMORY(desc_end)) || (FGMAC_DMA_IS_64_BIT_MEMORY(buf_end))) + { + FGMAC_ERROR("invalid rx descriptor memory %p or rx dma buf memory %p", + desc_end, buf_end); + return FGMAC_ERR_INVALID_DMA_MEM; + } + + /* setup DMA descriptor ring data */ + memset(tx_ring_p, 0, sizeof(*tx_ring_p)); + tx_ring_p->desc_max_num = tx_buf_num; + tx_ring_p->desc_idx = 0; + tx_ring_p->desc_buf_idx = 0; + tx_ring_p->desc_buf_base = tx_buf; + + /* setup DMA descriptor */ + memset((void *)tx_desc_tbl, 0, tx_buf_num * sizeof(FGmacDmaDesc)); + tx_desc_tbl[tx_buf_num - 1].ctrl |= FGMAC_DMA_TDES1_END_RING; + for (i = 0; i < tx_buf_num; i++) + { + cur_tx_desc = tx_desc_tbl + i; + FCacheDCacheInvalidateRange((uintptr)&tx_buf[i * tx_pre_buf_len], tx_pre_buf_len); + cur_tx_desc->buf_addr = (u32)((uintptr)&tx_buf[i * tx_pre_buf_len]); + cur_tx_desc->status = 0; + } + + /* flush descriptor */ + instance_p->tx_desc = tx_desc_tbl; + FCacheDCacheInvalidateRange((uintptr)instance_p->tx_desc, tx_buf_num * sizeof(FGmacDmaDesc)); + + FGMAC_WRITE_REG32(base_addr, FGMAC_DMA_TX_LIST_BASE_OFFSET, (u32)(uintptr)tx_desc_tbl); + return FGMAC_SUCCESS; +} + +/** + * @name: FGmacStartTrans + * @msg: 使能FGMAC DMA,使之可以接收/发送数据 + * @return {FError} FGMAC_SUCCESS 表示启动成功,其它返回值表示启动失败 + * @param {FGmac} *instance_p 驱动控制数据 + * @note 调用函数前需要确保FGMAC驱动初始化成功 + */ +FError FGmacStartTrans(FGmac *instance_p) +{ + FASSERT(instance_p); + if (FT_COMPONENT_IS_READY != instance_p->is_ready) + { + FGMAC_ERROR("device is already initialized!!!"); + return FGMAC_ERR_NOT_READY; + } + + FGmacStartDmaTrans(instance_p->config.base_addr); + return FGMAC_SUCCESS; +} + +/** + * @name: FGmacStopTrans + * @msg: 去使能FGMAC DMA, 使之不再能接收/发送数据 + * @return {FError} FGMAC_SUCCESS 表示去启动成功,其它返回值表示去启动失败 + * @param {FGmac} *instance_p 驱动控制数据 + * @note 调用函数前需要确保FGMAC驱动初始化成功 + */ +FError FGmacStopTrans(FGmac *instance_p) +{ + FASSERT(instance_p); + if (FT_COMPONENT_IS_READY != instance_p->is_ready) + { + FGMAC_ERROR("device is already initialized!!!"); + return FGMAC_ERR_NOT_READY; + } + + FGmacStopDmaTrans(instance_p->config.base_addr); + return FGMAC_SUCCESS; +} + +/** + * @name: FGmacRecvFrame + * @msg: 通过FGMAC接收数据帧 + * @return {FError} FGMAC_SUCCESS 表示接收数据帧成功,其它返回值表示接收数据帧失败 + * @param {FGmac} *instance_p 驱动控制数据 + * @note 调用函数前需要确保FGMAC驱动初始化成功 + */ +FError FGmacRecvFrame(FGmac *instance_p) +{ + FASSERT(instance_p); + FGmacRingDescData *rx_ring = &instance_p->rx_ring; + volatile FGmacDmaDesc *cur_rx_desc = &instance_p->rx_desc[rx_ring->desc_idx]; + u32 desc_cnt = 0; + u32 flag = (FGMAC_DMA_RDES0_FIRST_DESCRIPTOR | FGMAC_DMA_RDES0_LAST_DESCRIPTOR); + + while ((0 == (FGMAC_DMA_RDES0_OWN & cur_rx_desc->status)) && + (desc_cnt < rx_ring->desc_max_num)) + { + desc_cnt++; + + if (FGMAC_DMA_RDES0_FIRST_DESCRIPTOR == (flag & cur_rx_desc->status)) + { + rx_ring->desc_buf_idx = rx_ring->desc_idx; + FGMAC_DMA_INC_DESC(rx_ring->desc_idx, rx_ring->desc_max_num); + cur_rx_desc = &instance_p->rx_desc[rx_ring->desc_idx]; + } + else if (0 == (flag & cur_rx_desc->status)) + { + FGMAC_DMA_INC_DESC(rx_ring->desc_idx, rx_ring->desc_max_num); + cur_rx_desc = &instance_p->rx_desc[rx_ring->desc_idx]; + } + else + { + rx_ring->desc_buf_idx = rx_ring->desc_idx; + FGMAC_DMA_INC_DESC(rx_ring->desc_idx, rx_ring->desc_max_num); + return FGMAC_SUCCESS; + } + } + + return FGMAC_ERR_TRANS_FAILED; +} + +/** + * @name: FGmacSendFrame + * @msg: 通过FGMAC发送数据帧 + * @return {FError} FGMAC_SUCCESS 表示发送数据帧成功,其它返回值表示发送数据帧失败 + * @param {FGmac} *instance_p 驱动控制数据 + * @param {u32} frame_len 数据帧长度 + * @note 调用函数前需要确保FGMAC驱动初始化成功 + */ +FError FGmacSendFrame(FGmac *instance_p, u32 frame_len) +{ + FASSERT(instance_p); + u32 size = 0U; + u32 i = 0U; + u32 buf_cnt = 0U; + FError ret = FGMAC_SUCCESS; + volatile FGmacDmaDesc *tx_desc; + FGmacRingDescData *tx_ring = &instance_p->tx_ring; + const u32 max_packet_size = instance_p->config.max_packet_size; + + if (0U == frame_len) + { + return FGMAC_SUCCESS; + } + + if (max_packet_size < frame_len) + { + buf_cnt = frame_len / max_packet_size; + if (frame_len % max_packet_size) + buf_cnt++; + } + else + { + buf_cnt = 1U; + } + + if (1U == buf_cnt) + { + tx_desc = &instance_p->tx_desc[tx_ring->desc_idx]; + + /* Set LAST and FIRST segment */ + tx_desc->ctrl |= (FGMAC_DMA_TDES1_FIRST_SEGMENT | FGMAC_DMA_TDES1_LAST_SEGMENT); + + /* Set frame size */ + tx_desc->ctrl &= ~(FGMAC_DMA_TDES1_BUFFER1_SIZE_MASK); + tx_desc->ctrl |= (frame_len & FGMAC_DMA_TDES1_BUFFER1_SIZE_MASK); + + /* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */ + tx_desc->status |= FGMAC_DMA_TDES0_OWN; + FGMAC_DMA_INC_DESC(tx_ring->desc_idx, tx_ring->desc_max_num); + + } + else + { + for (i = 0U; i < buf_cnt; i++) + { + tx_desc = &instance_p->tx_desc[tx_ring->desc_idx]; + + /* Clear FIRST and LAST segment bits */ + tx_desc->ctrl &= ~(FGMAC_DMA_TDES1_FIRST_SEGMENT | FGMAC_DMA_TDES1_LAST_SEGMENT); + + if (0U == i) + { + tx_desc->ctrl |= FGMAC_DMA_TDES1_FIRST_SEGMENT; /* Setting the first segment bit */ + } + + /* Program size */ + tx_desc->ctrl &= ~(FGMAC_DMA_TDES1_BUFFER1_SIZE_MASK); + tx_desc->ctrl |= (max_packet_size & FGMAC_DMA_TDES1_BUFFER1_SIZE_MASK); + + if ((buf_cnt - 1) == i) + { + /* Setting the last segment bit */ + tx_desc->ctrl |= FGMAC_DMA_TDES1_LAST_SEGMENT; + size = frame_len - (buf_cnt - 1U) * max_packet_size; + tx_desc->ctrl &= ~(FGMAC_DMA_TDES1_BUFFER1_SIZE_MASK); + tx_desc->ctrl |= (size & FGMAC_DMA_TDES1_BUFFER1_SIZE_MASK); + } + + /* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */ + tx_desc->status |= FGMAC_DMA_TDES0_OWN; + FGMAC_DMA_INC_DESC(tx_ring->desc_idx, tx_ring->desc_max_num); + } + } + + FGmacResumeDmaSend(instance_p->config.base_addr); + return ret; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/eth/fgmac/fgmac_g.c b/bsp/phytium/libraries/standalone/drivers/eth/fgmac/fgmac_g.c new file mode 100644 index 0000000000..a72ab1b34b --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/eth/fgmac/fgmac_g.c @@ -0,0 +1,65 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgmac_g.c + * Date: 2022-04-06 14:46:52 + * LastEditTime: 2022-04-06 14:46:58 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +/* - This file contains a configuration table that specifies the configuration +- 驱动全局变量定义,包括静态配置参数 */ + +/***************************** Include Files *********************************/ + +#include "fparameters.h" +#include "fgmac.h" + +/************************** Constant Definitions *****************************/ + +const FGmacConfig FGMAC_CONFIG_TBL[GMAC_INSTANCE_NUM] = +{ + [GMAC_INSTANCE_0] = + { + .instance_id = GMAC_INSTANCE_0, + .base_addr = GMAC_INSTANCE_0_BASE_ADDR, + .irq_num = GMAC_INSTANC_0_IRQ, + .irq_prority = 0, + .cheksum_mode = FGMAC_CHECKSUM_BY_SOFTWARE, + .max_packet_size = GMAC_MAX_PACKET_SIZE + }, + + [GMAC_INSTANCE_1] = + { + .instance_id = GMAC_INSTANCE_1, + .base_addr = GMAC_INSTANCE_1_BASE_ADDR, + .irq_num = GMAC_INSTANC_1_IRQ, + .irq_prority = 0, + .cheksum_mode = FGMAC_CHECKSUM_BY_SOFTWARE, + .max_packet_size = GMAC_MAX_PACKET_SIZE + } +}; + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + +/*****************************************************************************/ diff --git a/bsp/phytium/libraries/standalone/drivers/eth/fgmac/fgmac_hw.c b/bsp/phytium/libraries/standalone/drivers/eth/fgmac/fgmac_hw.c new file mode 100644 index 0000000000..b4a493a2dd --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/eth/fgmac/fgmac_hw.c @@ -0,0 +1,166 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgmac_hw.c + * Date: 2022-04-06 14:46:52 + * LastEditTime: 2022-04-06 14:46:58 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "fassert.h" +#include "fdebug.h" + +#include "fgmac.h" +#include "fgmac_hw.h" + + +/***************************** Include Files *********************************/ + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FGMAC_DEBUG_TAG "FGMAC-HW" +#define FGMAC_ERROR(format, ...) FT_DEBUG_PRINT_E(FGMAC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGMAC_WARN(format, ...) FT_DEBUG_PRINT_W(FGMAC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGMAC_INFO(format, ...) FT_DEBUG_PRINT_I(FGMAC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGMAC_DEBUG(format, ...) FT_DEBUG_PRINT_D(FGMAC_DEBUG_TAG, format, ##__VA_ARGS__) + +/************************** Function Prototypes ******************************/ + +/** + * @name: FGmacGetMacAddr + * @msg: 获取FGMAC控制器配置的MAC地址 + * @return {*} + * @param {uintptr} base_addr 控制器寄存器基地址 + * @param {FGmacMacAddr} mac_addr 配置的MAC地址 + */ +void FGmacGetMacAddr(uintptr base_addr, FGmacMacAddr mac_addr) +{ + u32 mac_high = FGMAC_READ_REG32(base_addr, FGMAC_MAC_ADDR0_UPPER16BIT_OFFSET); + u32 mac_low = FGMAC_READ_REG32(base_addr, FGMAC_MAC_ADDR0_LOWER32BIT_OFFSET); + + /* get lower 32 bits of mac addr */ + mac_addr[0] = (u8)(mac_low & 0xff); + mac_addr[1] = (u8)((mac_low >> 8) & 0xff); + mac_addr[2] = (u8)((mac_low >> 16) & 0xff); + mac_addr[3] = (u8)((mac_low >> 24) & 0xff); + + /* get upper 16 bits of mac addr */ + mac_addr[4] = (u8)(mac_high & 0xff); + mac_addr[5] = (u8)((mac_high >> 8) & 0xff); + + return; +} + +/** + * @name: FGmacSetMacAddr + * @msg: 设置FGMAC控制器的MAC地址 + * @return {*} + * @param {uintptr} base_addr 控制器寄存器基地址 + * @param {FGmacMacAddr} mac_addr 配置的MAC地址 + */ +void FGmacSetMacAddr(uintptr base_addr, const FGmacMacAddr mac_addr) +{ + u32 reg_val; + + reg_val = ((u32)mac_addr[5] << 8) | (u32)mac_addr[4]; + FGMAC_WRITE_REG32(base_addr, FGMAC_MAC_ADDR0_UPPER16BIT_OFFSET, reg_val); + reg_val = ((u32)mac_addr[3] << 24) | ((u32)mac_addr[2] << 16) | ((u32)mac_addr[1] << 8) | mac_addr[0]; + FGMAC_WRITE_REG32(base_addr, FGMAC_MAC_ADDR0_LOWER32BIT_OFFSET, reg_val); + + return; +} + +/** + * @name: FGmacSoftwareReset + * @msg: 触发FGMAC控制器软件复位 GMac DMA寄存器列表 和 控制寄存器列表 + * @return {*} + * @param {uintptr} base_addr 控制器寄存器基地址 + * @param {int} timeout 等待复位完成的状态检测周期数目 + */ +FError FGmacSoftwareReset(uintptr base_addr, int timeout) +{ + FASSERT(timeout > 1); /* 至少等待一个周期 */ + u32 reg_val; + + FGMAC_SET_REG32(base_addr, FGMAC_DMA_BUS_MODE_OFFSET, FGMAC_DMA_BUS_SWR); /*最后一位写为1 此时MAC DMA 控制器将复位所有 GMAC子系统内部寄存器和逻辑。 完成后会自动清0*/ + do + { + reg_val = FGMAC_READ_REG32(base_addr, FGMAC_DMA_BUS_MODE_OFFSET); + } + while ((reg_val & FGMAC_DMA_BUS_SWR) && (--timeout > 0)); /*判断swr位是否为1,当读到0时此时判断 复位操作已完成 软件复位成功*/ + + if ((0 >= timeout) && (reg_val & FGMAC_DMA_BUS_SWR)) + { + FGMAC_ERROR("reset timeout, please check phy connection!!!"); + return FGMAC_ERR_TIMEOUT; + } + + return FGMAC_SUCCESS; +} + +FError FGmacFlushTxFifo(uintptr base_addr, int timeout) +{ + FASSERT(timeout > 1); /* 至少等待一个周期 */ + u32 reg_val; + FGMAC_SET_REG32(base_addr, FGMAC_DMA_OP_OFFSET, FGMAC_DMA_OP_FTF); + + do + { + reg_val = FGMAC_READ_REG32(base_addr, FGMAC_DMA_OP_OFFSET); + } + while ((reg_val & FGMAC_DMA_OP_FTF) && (--timeout > 0)); + + if ((0 >= timeout) && (reg_val & FGMAC_DMA_OP_FTF)) + { + FGMAC_ERROR("flush tx fifo timeout !!!"); + return FGMAC_ERR_TIMEOUT; + } + + return FGMAC_SUCCESS; +} + +/** + * @name: FGmacPhyWaitBusBusy + * @msg: wait phy gmii is not busy + * @param {uintptr} base_addr, base address of FGmac controller register + * @param {int} timeout, wait timeout + * @return err code information, FGMAC_SUCCESS indicates success,others indicates failed + */ +FError FGmacPhyWaitBusBusy(uintptr base_addr, int timeout) +{ + u32 reg_val; + + /* Check for the Busy flag */ + do + { + reg_val = FGMAC_READ_REG32(base_addr, FGMAC_GMII_ADDR_OFFSET); + } + while ((FGMAC_MII_ADDR_GB & reg_val) && (0 <= --timeout)); + + if (0 >= timeout) + { + FGMAC_ERROR("timeout when wait GMII timeout"); + return FGMAC_ERR_TIMEOUT; + } + + return FGMAC_SUCCESS; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/eth/fgmac/fgmac_hw.h b/bsp/phytium/libraries/standalone/drivers/eth/fgmac/fgmac_hw.h new file mode 100644 index 0000000000..d192d81f4c --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/eth/fgmac/fgmac_hw.h @@ -0,0 +1,554 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgmac_hw.h + * Date: 2022-04-06 14:46:52 + * LastEditTime: 2022-04-06 14:46:58 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef DRIVERS_ETH_FGMAC_HW_H +#define DRIVERS_ETH_FGMAC_HW_H + + +/* - 传入模块基地址,不能复杂结构体 +- hardware interface of device || low-level driver function prototypes + +- 包括驱动寄存器参数和low-level操作定义 +1. 定义寄存器偏移 +2. 对上提供该模块寄存器操作的接口 +3. 一些简单外设提供直接操作接口 +4. 可以定义一些状态的接口,用于响应驱动状态的变化 + +note: 本文件不能引用fooxx.h +*/ + +#ifdef __cplusplus +extern "C" +{ +#endif +/***************************** Include Files *********************************/ + +#include "fkernel.h" +#include "fio.h" +#include "ftypes.h" + +/************************** Constant Definitions *****************************/ + +/** @name Register Map + * + * Register offsets from the base address of an SD device. + * @{ + */ +/**** FGMAC CTRL ****/ +#define FGMAC_CONF_OFFSET 0x00U /* MAC 配置寄存器 */ +#define FGMAC_FRAME_FILTER_OFFSET 0x04U /* Mac 帧过滤 */ +#define FGMAC_HASH_HIGH_OFFSET 0x08U /* 哈希表高位寄存器 */ +#define FGMAC_HASH_LOW_OFFSET 0x0cU /* 哈希表低位寄存器 */ +#define FGMAC_GMII_ADDR_OFFSET 0x10U /* GMII 地址寄存器 */ +#define FGMAC_GMII_DATA_OFFSET 0x14U /* GMII 数据寄存器 */ +#define FGMAC_FLOW_CTRL_OFFSET 0x18U /* 流控寄存器 */ +#define FGMAC_VLAN_TAG_OFFSET 0x1cU /* VLAN 标记寄存器 */ +#define FGMAC_VERSION_OFFSET 0x20U /* 版本寄存器 */ +#define FGMAC_DEBUG_STATUS_OFFSET 0x24U /* 调试寄存器 */ +#define FGMAC_LPI_CTRL_STATUS_OFFSET 0x30U /* LPI (低功耗空闲) 控制和状态寄存器 */ +#define FGMAC_LPI_TIMER_CTRL_OFFSET 0x34U /* LPI 定时器控制寄存器 */ +#define FGMAC_INTR_STATUS_OFFSET 0x38U /* 中断状态寄存器 */ +#define FGMAC_INTR_MASK_OFFSET 0x3CU /* 中断屏蔽寄存器 */ +#define FGMAC_MAC_ADDR0_UPPER16BIT_OFFSET 0x40U /* 第一个 MAC 地址的高 16 位。 */ +#define FGMAC_MAC_ADDR0_LOWER32BIT_OFFSET 0x44U /* 第一个 MAC 地址的低 32 位。。 */ +#define FGMAC_MAC_ADDR1_UPPER16BIT_OFFSET 0x48U /* 第二个 MAC 地址的高 16 位。 */ +#define FGMAC_MAC_ADDR1_LOWER32BIT_OFFSET 0x4CU /* 第二个 MAC 地址的低 32 位。。 */ +#define FGMAC_MAC_PHY_STATUS 0xD8U /* MAC PHY 状态 */ + +/**** FGMAC DMA CTRL ****/ +#define FGMAC_DMA_BUS_MODE_OFFSET 0x1000U /* 总线模式寄存器 */ +#define FGMAC_DMA_TX_POLL_REQ_OFFSET 0x1004U /* 发送轮询请求寄存器 */ +#define FGMAC_DMA_RX_POLL_REQ_OFFSET 0x1008U /* 接收轮询请求寄存器 */ +#define FGMAC_DMA_RX_LIST_BASE_OFFSET 0x100cU /* 接收描述符列表地址寄存器 */ +#define FGMAC_DMA_TX_LIST_BASE_OFFSET 0x1010U /* 发送描述符列表地址寄存器 */ +#define FGMAC_DMA_STATUS_OFFSET 0x1014U /* 状态寄存器 */ +#define FGMAC_DMA_OP_OFFSET 0x1018U /* 操作模式寄存器 */ +#define FGMAC_DMA_INTR_OFFSET 0x101cU /* 中断使能寄存器 */ +#define FGMAC_DMA_MISSED_FRAME_CNT_OFFSET 0x1020U /* 丢帧和缓冲区溢出计数器寄存器 */ +#define FGMAC_DMA_RX_WATCHDOG_OFFSET 0x1024U /* 接收中断看门狗定时器寄存器 */ +#define FGMAC_DMA_AXI_BUS_MOD_OFFSET 0x1028U /* AXI 总线模式寄存器*/ +#define FGMAC_DMA_AXI_BUS_STATUS_OFFSET 0x102CU /* AXI 状态寄存器 */ +#define FGMAC_DMA_HOST_TX_DESC_OFFSET 0x1048U /* 当前主机发送描述符寄存器 */ +#define FGMAC_DMA_HOST_RX_DESC_OFFSET 0x104CU /* 当前主机接收描述符寄存器 */ +#define FGMAC_DMA_HOST_TX_BUF_ADDR_OFFSET 0x1050U /* 当前主机发送缓冲地址寄存器 */ +#define FGMAC_DMA_HOST_RX_BUF_ADDR_OFFSET 0x1054U /* 当前主机接收缓冲地址寄存器 */ +#define FGMAC_DMA_HW_FUNC_OFFSET 0x1058U /* 硬件功能寄存器 */ + +/** @name FGMAC_CONF_OFFSET Register + */ +#define FGMAC_CONF_RX_EN BIT(2) /* 接收器启用 */ +#define FGMAC_CONF_TX_EN BIT(3) /* 发送器启用 */ +#define FGMAC_CONF_DC BIT(4) /* 延期检查 */ +#define FGMAC_CONF_BL_MASK GENMASK(6, 5) /* 后退限制 */ +#define FGMAC_CONF_BL(x) (FGMAC_CONF_BL_MASK & ((x) << 5)) +#define FGMAC_CONF_ACS BIT(7) /* 自动 PAD/ CRC 剥线 */ +#define FGMAC_CONF_LINK_UPDOWN BIT(8) /* 链接 up/down */ +#define FGMAC_CONF_DISABLE_RETRY BIT(9) /* 禁用重试 */ +#define FGMAC_CONF_IPC BIT(10) /* 校验和卸载 */ +#define FGMAC_CONF_DUPLEX_MODE BIT(11) /* 双工模式 */ +#define FGMAC_CONF_LOOPBACK_MODE BIT(12) /* 环回模式 */ +#define FGMAC_CONF_DO BIT(13) /* 不能自接收 */ +#define FGMAC_CONF_FES BIT(14) /* 速度选择 0: 10Mbps, 1: 100Mbps d2000 ft2004手册有误*/ +#define FGMAC_CONF_PORTSELECT BIT(15) /* 端口选择 0: GMII(1000Mbps)1: MII(10/100Mbps) */ +#define FGMAC_CONF_DCRS BIT(16) /* 传输过程中禁用载波侦听 */ +#define FGMAC_CONF_IFG_MASK GENMASK(19, 17) /* 帧内间隔 */ +#define FGMAC_CONF_IFG(x) (FGMAC_CONF_IFG_MASK & ((x) << 17)) +#define FGMAC_CONF_JE BIT(20) /* 使用Jumbo帧 */ +#define FGMAC_CONF_BE BIT(21) /* 帧突发启用 */ +#define FGMAC_CONF_JD BIT(22) /* Jabber 禁用 */ +#define FGMAC_CONF_WD BIT(23) /* 看门狗禁用 */ +#define FGMAC_CONF_TC BIT(24) /* 在 RGMII 中传输配置 */ +#define FGMAC_CONF_CST BIT(25) /* 类型帧的 CRC 剥离 */ + +/* 使能类型帧的CRC剥离、禁用看门狗WD、禁用Jabber JD、帧突发启用BE、不能自接收DO(全双工保留)*/ +#define FGMAC_CONF_INIT (FGMAC_CONF_CST| FGMAC_CONF_WD| FGMAC_CONF_JD| FGMAC_CONF_BE| FGMAC_CONF_DO) + +/** @name FGMAC_FRAME_FILTER_OFFSET Register + */ +#define FGMAC_FRAME_FILTER_PR BIT(0) +#define FGMAC_FRAME_FILTER_HUC BIT(1) /* 哈希单播 */ +#define FGMAC_FRAME_FILTER_HMC BIT(2) /* 哈希多播 */ +#define FGMAC_FRAME_FILTER_DAIF BIT(3) /* 目的地址反向过滤 */ +#define FGMAC_FRAME_FILTER_PM BIT(4) /* 通过所有多播 */ +#define FGMAC_FRAME_FILTER_DBF BIT(5) /* 禁用广播帧 */ +#define FGMAC_FRAME_FILTER_PCF_MASK (GENMASK(7, 6)) /* 通过控制帧 */ +#define FGMAC_FRAME_FILTER_PCF(x) ((x) << 6) +#define FGMAC_FRAME_FILTER_SAIF BIT(8) /* 源地址反相过滤 */ +#define FGMAC_FRAME_FILTER_SAF BIT(9) /* 源地址过滤使能 */ +#define FGMAC_FRAME_FILTER_HPF BIT(10) /* hash 或 perfect 过滤器 */ +#define FGMAC_FRAME_FILTER_RA BIT(31) /* 全部接收 */ + +/** @name FGMAC_HASH_HIGH_OFFSET Register + */ +#define FGMAC_HASH_HIGH_HTH_MASK GENMASK(31, 0) /* 该字段包含 Hash 表的高 32 位。 */ + +/** @name FGMAC_HASH_LOW_OFFSET Register + */ +#define FGMAC_HASH_LOW_HTH_MASK GENMASK(31, 0) /* 该字段包含 Hash 表的低 32 位。 */ + +/** @name FGMAC_GMII_ADDR_OFFSET Register + */ +#define FGMAC_MII_ADDR_GB BIT(0) /* GMII 忙 */ +#define FGMAC_MII_ADDR_GW BIT(1) /* GMII 写 */ +#define FGMAC_MII_ADDR_CR_MASK GENMASK(5, 2) /* CSR 时钟范围 */ +#define FGMAC_MII_ADDR_CR(x) (FGMAC_MII_ADDR_CR_MASK & ((x) << 2)) +#define FGMAC_MII_ADDR_GR_MASK GENMASK(10, 6) /* GMII 寄存器 */ +#define FGMAC_MII_ADDR_GR(x) (FGMAC_MII_ADDR_GR_MASK & ((x) << 6)) +#define FGMAC_MII_ADDR_PA_MASK GENMASK(15, 11) /* 物理层地址 */ +#define FGMAC_MII_ADDR_PA(x) (FGMAC_MII_ADDR_PA_MASK & ((x) << 11)) + +/** @name FGMAC_GMII_DATA_OFFSET Register + */ +#define FGMAC_MII_DATA_GD_MASK GENMASK(15, 0) + +/** @name FGMAC_FLOW_CTRL_OFFSET Register + */ +#define FGMAC_FLOW_FCB BIT(0) +#define FGMAC_FLOW_BPA BIT(0) +#define FGMAC_FLOW_TFE BIT(1) +#define FGMAC_FLOW_RFE BIT(2) /* 接收流控制启用 */ +#define FGMAC_FLOW_UP BIT(3) /* 单播暂停帧检测 */ +#define FGMAC_FLOW_PLT_MASK GENMASK(5, 4) /* 暂停低阈值 */ +#define FGMAC_FLOW_PLT(x) ((x) << 3) +#define FGMAC_FLOW_DZPQ BIT(7) +#define FGMAC_FLOW_PT_MASK GENMASK(31, 16) /* 暂停时间 */ + +/** @name FGMAC_VLAN_TAG_OFFSET Register + */ +#define FGMAC_VLAN_TAG_VL_MASK GENMASK(15, 0) /* 启用 12 位 VLAN 标记比较 */ +#define FGMAC_VLAN_TAG_VL(x) (FGMAC_VLAN_TAG_VL_MASK & ((x) << 15)) +#define FGMAC_VLAN_TAG_ETV BIT(16) /* 接收帧的 VLAN 标记标识符 */ + +/** @name FGMAC_VERSION_OFFSET Register + */ +#define FGMAC_VERSION_UDV_MASK GENMASK(15, 8) /* 用户定义版本号 */ +#define FGMAC_VERSION_SDV_MASK GENMASK(7, 0) /* 硬件定议版本号 */ + +/** @name FGMAC_LPI_CTRL_STATUS_OFFSET Register + */ +#define FGMAC_LPI_CTRL_STATS_TLPIEN BIT(0) /* 发送 LPI 进入 */ +#define FGMAC_LPI_CTRL_STATS_TLPIEX BIT(1) /* 发送 LPI 退出 */ +#define FGMAC_LPI_CTRL_STATS_RLPIEN BIT(2) /* 接收 LPI 进入 */ +#define FGMAC_LPI_CTRL_STATS_RLPIEX BIT(3) /* 接收 LPI 退出 */ +#define FGMAC_LPI_CTRL_STATS_TLPIST BIT(8) /* 发送 LPI 状态 */ +#define FGMAC_LPI_CTRL_STATS_RLPIST BIT(9) /* 接收 LPI 状态 */ +#define FGMAC_LPI_CTRL_STATS_LPIEN BIT(16) /* LPI 使能 */ +#define FGMAC_LPI_CTRL_STATS_PLS BIT(17) /* PHY 链路状态 */ +#define FGMAC_LPI_CTRL_STATS_PLSEN BIT(18) /* 物理链路状态使能 */ +#define FGMAC_LPI_CTRL_STATS_LPITXA BIT(19) /* LPI 发送自动化 */ + +/** @name FGMAC_LPI_TIMER_CTRL_OFFSET Register + */ +#define FGMAC_LPI_TIMER_TWT_MASK GENMASK(15, 0) +#define FGMAC_LPI_TIMER_LIT_MASK GENMASK(25, 16) + +/** @name FGMAC_INTR_STATUS_OFFSET Register + */ +#define FGMAC_ISR_STATUS_RSIS BIT(0) /* RGMII/SMII Interrupt Status */ +#define FGMAC_ISR_STATUS_PCSLSC BIT(1) /* PCS 链路状态改变 */ +#define FGMAC_ISR_STATUS_PCSANC BIT(2) /* PCS 自协商完成 */ +#define FGMAC_ISR_STATUS_PMTIS BIT(3) +#define FGMAC_ISR_STATUS_MMCIS BIT(4) +#define FGMAC_ISR_STATUS_MMCRIS BIT(5) /* MMC 接收中断状态 */ +#define FGMAC_ISR_STATUS_MMCTIS BIT(6) /* MMC 发送中断状态 */ +#define FGMAC_ISR_STATUS_MMCRCOIS BIT(7) /* 接收校验和卸载中断状态 */ +#define FGMAC_ISR_STATUS_TIS BIT(9) /* 时间戳中断状态 */ +#define FGMAC_ISR_STATUS_LPIIS BIT(10) /* LPI 中断状态 */ +#define FGMAC_ISR_STATUS_ALL_MASK GENMASK(10, 0) + +/** @name FGMAC_INTR_MASK_OFFSET Register + */ +#define FGMAC_ISR_MASK_RSIM BIT(0) /* RGMII/SMII 中断屏蔽 */ +#define FGMAC_ISR_MASK_PCSLSIM BIT(1) /* PCS 链路状态中断屏蔽 */ +#define FGMAC_ISR_MASK_PCSANCIM BIT(2) /* PCS AN 完成中断屏蔽 */ +#define FGMAC_ISR_MASK_PMTIM BIT(3) /* PMT 中断屏蔽 */ +#define FGMAC_ISR_MASK_TIM BIT(9) /* 时间戳中断屏蔽 */ +#define FGMAC_ISR_MASK_LPIIM BIT(10) /* LPI 中断屏蔽 */ +#define FGMAC_ISR_MASK_ALL_BITS (GENMASK(3, 0) | GENMASK(10, 9)) + +#define FGMAC_8BIT_ADDR GENMASK(7, 0) + +/** @name FGMAC_MAC_ADDR0_UPPER16BIT_OFFSET Register + */ +#define FGMAC_MAC_ADDR0_UPPER16BIT_A GENMASK(15, 0) /* MAC 地址 0[47:32] */ + +/** @name FGMAC_MAC_ADDR0_LOWER32BIT_OFFSET Register + */ +#define FGMAC_MAC_ADDR0_LOWER32BIT_A GENMASK(31, 0) /* MAC 地址 0[31:0] */ + +/** @name FGMAC_MAC_ADDR1_UPPER16BIT_OFFSET Register + */ +#define FGMAC_MAC_ADDR1_UPPER16BIT_A GENMASK(15, 0) /* MAC 地址 1[47:32] */ +#define FGMAC_MAC_ADDR1_UPPER16BIT_MBC GENMASK(29, 24) +#define FGMAC_MAC_ADDR1_UPPER16BIT_SA BIT(30) +#define FGMAC_MAC_ADDR1_UPPER16BIT_AE BIT(31) + +/** @name FGMAC_MAC_ADDR1_LOWER32BIT_OFFSET Register + */ +#define FGMAC_MAC_ADDR1_LOWER16BIT_A GENMASK(31, 0) /* MAC 地址 1[31:0] */ + +/** @name FGMAC_MAC_PHY_STATUS Register + */ +#define FGMAC_RGSMIIIS_LNKMODE BIT(0) /* 指示链路的当前操作模式 */ +#define FGMAC_RGSMIIIS_LNKMODE_HALF (0b0 << 0) +#define FGMAC_RGSMIIIS_LNKMODE_FULL (0b1 << 0) +#define FGMAC_RGSMIIIS_SPEED GENMASK(2, 1) +#define FGMAC_RGSMIIIS_SPEED_2_5MHZ (0b00 << 1) /* 链路速度 2.5MHz */ +#define FGMAC_RGSMIIIS_SPEED_25MHZ (0b01 << 1) /* 25MHz */ +#define FGMAC_RGSMIIIS_SPEED_125MHZ (0b10 << 1) /* 125MHz */ +#define FGMAC_RGSMIIIS_LNKSTS BIT(3) +#define FGMAC_RGSMIIIS_LNKSTS_UP (0b1 << 3) +#define FGMAC_RGSMIIIS_LNKSTS_DOWN (0b0 << 3) +#define FGMAC_RGSMIIIS_JAB_TIMEOUT BIT(4) +#define FGMAC_RGSMIIIS_FALSECARDET BIT(5) + +/****************FGMAC DMA Register*******************/ +/* Bus mode register definitions */ +/** @name FGMAC_DMA_BUS_MODE_OFFSET Register + */ +#define FGMAC_DMA_BUS_SWR BIT(0) /* 软件复位 */ +#define FGMAC_DMA_BUS_DA BIT(1) /* 设置 8xPBL 模式 */ +#define FGMAC_DMA_BUS_DSL_MASK GENMASK(6, 2) /* 描述符跳跃长度 */ +#define FGMAC_DMA_BUS_ATDS BIT(7) +#define FGMAC_DMA_BUS_PBL_MASK GENMASK(13, 8) /* 可编程突发长度 */ +#define FGMAC_DMA_BUS_PBL(x) ((x) << 8) +enum +{ + FGMAC_DMA_BUS_PBL_1 = 1, + FGMAC_DMA_BUS_PBL_2 = 2, + FGMAC_DMA_BUS_PBL_4 = 4, + FGMAC_DMA_BUS_PBL_8 = 8, + FGMAC_DMA_BUS_PBL_16 = 16, + FGMAC_DMA_BUS_PBL_32 = 32 +}; +#define FGMAC_DMA_BUS_PR_MASK GENMASK(15, 14) /* 加权循环仲裁中的优先级比率 */ +#define FGMAC_DMA_BUS_PR(x) ((x) << 14) +#define FGMAC_DMA_BUS_FB BIT(16) /* 固定突发 */ +#define FGMAC_DMA_BUS_RPBL_MASK GENMASK(22, 17) /* RxDMA 事务中要传输的最大节拍数 */ +#define FGMAC_DMA_BUS_RPBL(x) ((x) << 17) +enum +{ + FGMAC_DMA_BUS_RPBL_1 = 1, + FGMAC_DMA_BUS_RPBL_2 = 2, + FGMAC_DMA_BUS_RPBL_4 = 4, + FGMAC_DMA_BUS_RPBL_16 = 16, + FGMAC_DMA_BUS_RPBL_32 = 32 +}; +#define FGMAC_DMA_BUS_USP BIT(23) +#define FGMAC_DMA_BUS_8XPBL BIT(24) +#define FGMAC_DMA_BUS_AAL BIT(25) +#define FGMAC_DMA_BUS_MB BIT(26) /* 混合突发 */ +#define FGMAC_DMA_BUS_TXPR BIT(27) /* 发送优先级 */ +#define FGMAC_DMA_BUS_PRWG GENMASK(29, 18) /* 通道优先权重 */ + +#define FGMAC_DMA_BUS_INIT (FGMAC_DMA_BUS_FB | FGMAC_DMA_BUS_PBL(16) | FGMAC_DMA_BUS_RPBL(16)) + +/* 这些位控制 RxDMA 和 TxDMA 之间的加权循环仲裁中的优先级比率, + FGMAC_DMA_BUS_DA=1时有效 */ +#define FGMAC_DMA_BUS_PRIORXTX_41 (3 << 14) /* 4:1 */ +#define FGMAC_DMA_BUS_PRIORXTX_31 (2 << 14) /* 3:1 */ +#define FGMAC_DMA_BUS_PRIORXTX_21 (1 << 14) /* 2:1 */ +#define FGMAC_DMA_BUS_PRIORXTX_11 (0 << 14) /* 1:1 */ + +/** @name FGMAC_DMA_TX_POLL_REQ_OFFSET Register + */ +#define FGMAC_DMA_XMT_POLL_DEMAND_TPD GENMASK(31, 0) + +/** @name FGMAC_DMA_RX_POLL_REQ_OFFSET Register + */ +#define FGMAC_DMA_RCV_POLL_DEMAND_RPD GENMASK(31, 0) + +/** @name FGMAC_DMA_RX_LIST_BASE_OFFSET Register + */ +#define FGMAC_DMA_RCV_BASE_ADDR_START_REC_LIST GENMASK(31, 4) + +/** @name FGMAC_DMA_TX_LIST_BASE_OFFSET Register + */ +#define FGMAC_DMA_TX_BASE_ADDR_START_TRA_LIST GENMASK(31, 4) + +/** @name FGMAC_DMA_STATUS_OFFSET Register + */ +#define FGMAC_DMA_STATUS_GLPII BIT(30) /* FGMAC LPI interrupt */ +#define FGMAC_DMA_STATUS_TTI BIT(29) /* 时间戳触发中断 */ +#define FGMAC_DMA_STATUS_GPI BIT(28) /* PMT interrupt */ +#define FGMAC_DMA_STATUS_GMI BIT(27) /* MMC interrupt */ +#define FGMAC_DMA_STATUS_GLI BIT(26) /* FGMAC Line interface int */ +#define FGMAC_DMA_STATUS_EB_MASK GENMASK(25, 23) /* Error Bits Mask */ +#define FGMAC_DMA_STATUS_TS_MASK GENMASK(22, 20) /* Transmit Process State */ +#define FGMAC_DMA_STATUS_TS_STOPPED (0b000 << 20) +#define FGMAC_DMA_STATUS_TS_GET_DESC (0b001 << 20) +#define FGMAC_DMA_STATUS_TS_WAIT (0b010 << 20) +#define FGMAC_DMA_STATUS_TS_QUEUE (0b011 << 20) +#define FGMAC_DMA_STATUS_TS_PAUSE (0b110 << 20) +#define FGMAC_DMA_STATUS_TS_CLOSE_DESC (0b111 << 20) +#define FGMAC_DMA_STATUS_RS_MASK GENMASK(19, 17) /* Receive Process State */ +#define FGMAC_DMA_STATUS_NIS BIT(16) /* Normal Interrupt Summary */ +#define FGMAC_DMA_STATUS_AIS BIT(15) /* Abnormal Interrupt Summary */ +#define FGMAC_DMA_STATUS_ERI BIT(14) /* Early Receive Interrupt */ +#define FGMAC_DMA_STATUS_FBI BIT(13) /* Fatal Bus Error Interrupt */ +#define FGMAC_DMA_STATUS_ETI BIT(10) /* Early Transmit Interrupt */ +#define FGMAC_DMA_STATUS_RWT BIT(9) /* Receive Watchdog Timeout */ +#define FGMAC_DMA_STATUS_RPS BIT(8) /* Receive Process Stopped */ +#define FGMAC_DMA_STATUS_RU BIT(7) /* Receive Buffer Unavailable */ +#define FGMAC_DMA_STATUS_RI BIT(6) /* Receive Interrupt */ +#define FGMAC_DMA_STATUS_UNF BIT(5) /* Transmit Underflow */ +#define FGMAC_DMA_STATUS_OVF BIT(4) /* Receive Overflow */ +#define FGMAC_DMA_STATUS_TJT BIT(3) /* Transmit Jabber Timeout */ +#define FGMAC_DMA_STATUS_TU BIT(2) /* Transmit Buffer Unavailable */ +#define FGMAC_DMA_STATUS_TPS BIT(1) /* Transmit Process Stopped */ +#define FGMAC_DMA_STATUS_TI BIT(0) /* Transmit Interrupt */ + +#define FGMAC_DMA_STATUS_CLR_ABLE (GENMASK(5, 0) | GENMASK(10, 7)| FGMAC_DMA_STATUS_FBI | FGMAC_DMA_STATUS_AIS) /* BIT [16 : 0] write 1 to clear */ + +/** @name FGMAC_DMA_OP_OFFSET Register + */ +#define FGMAC_DMA_OP_DT BIT(26) /* No Dropping of TCP/IP csum Err Frame */ +#define FGMAC_DMA_OP_RSF BIT(25) /* Rx Store and Forward */ +#define FGMAC_DMA_OP_DFF BIT(24) /* */ +#define FGMAC_DMA_OP_RFA_2 BIT(23) +#define FGMAC_DMA_OP_RFD_2 BIT(22) +#define FGMAC_DMA_OP_TSF BIT(21) /* Tx Store and Forward */ +#define FGMAC_DMA_OP_FTF BIT(20) /* Flush Tx FIFO */ +#define FGMAC_DMA_OP_TTC_MASK GENMASK(16, 14) +#define FGMAC_DMA_OP_TTC(x) ((x) << 14) /* Tx Threshold Control */ +enum +{ + FGMAC_DMA_OP_TTC_64 = 0b000, + FGMAC_DMA_OP_TTC_128 = 0b001, + FGMAC_DMA_OP_TTC_192 = 0b010, + FGMAC_DMA_OP_TTC_256 = 0b011, + FGMAC_DMA_OP_TTC_40 = 0b100, + FGMAC_DMA_OP_TTC_32 = 0b101, + FGMAC_DMA_OP_TTC_24 = 0b110, + FGMAC_DMA_OP_TTC_16 = 0b111 +}; + +#define FGMAC_DMA_OP_ST BIT(13) /* Start/Stop Tx */ +#define FGMAC_DMA_OP_RFD_MASK GENMASK(12, 11) +#define FGMAC_DMA_OP_RFD(x) ((x) << 11) /* Threshold for DeActive Flow Control */ +#define FGMAC_DMA_OP_RFA_MASK GENMASK(10, 9) /* Threshold for Active Flow Control */ +#define FGMAC_DMA_OP_EFC BIT(8) /* Enable HW Flow control */ +#define FGMAC_DMA_OP_FEF BIT(7) /* Forward Error Frame */ +#define FGMAC_DMA_OP_FUF BIT(6) /* Forward Undersize Good Frame */ +#define FGMAC_DMA_OP_RTC_MASK GENMASK(4, 3) /* Rx Threshold Control */ +#define FGMAC_DMA_OP_OSF BIT(2) /* Operate On Second Mode */ +#define FGMAC_DMA_OP_SR BIT(1) /* Start/Stop Rx */ +#define FGMAC_DMA_OP_CLEAR_MASK GENMASK(31, 0) +#define FGMAC_DMA_OP_INIT (FGMAC_DMA_OP_SR | FGMAC_DMA_OP_RSF) + +/** @name FGMAC_DMA_INTR_OFFSET Register + */ +#define FGMAC_DMA_INTR_ENA_TIE BIT(0) /* Transmit Interrupt */ +#define FGMAC_DMA_INTR_ENA_TSE BIT(1) /* 传输停止启用 */ +#define FGMAC_DMA_INTR_ENA_TUE BIT(2) /* Transmit Buffer Unavailable */ +#define FGMAC_DMA_INTR_ENA_THE BIT(3) /* 发送 Jabber 超时启用 */ +#define FGMAC_DMA_INTR_ENA_OVE BIT(4) /* 溢出中断使能 */ +#define FGMAC_DMA_INTR_ENA_UNE BIT(5) /* 下溢中断使能 */ +#define FGMAC_DMA_INTR_ENA_RIE BIT(6) /* Receive Interrupt */ +#define FGMAC_DMA_INTR_ENA_RUE BIT(7) /* 接收缓冲区不可用启用 */ +#define FGMAC_DMA_INTR_ENA_RSE BIT(8) /* 接收已停止启用 */ +#define FGMAC_DMA_INTR_ENA_RWE BIT(9) /* 接收看门狗超时使能 */ +#define FGMAC_DMA_INTR_ENA_ETE BIT(10) /* 早期发送中断使能 */ +#define FGMAC_DMA_INTR_ENA_FBE BIT(13) /* Fatal Bus Error */ +#define FGMAC_DMA_INTR_ENA_ERE BIT(14) /* Early Receive */ +#define FGMAC_DMA_INTR_ENA_AIE BIT(15) /* Abnormal Summary */ +#define FGMAC_DMA_INTR_ENA_NIE BIT(16) /* Normal Summary */ +#define FGMAC_DMA_INTR_ENA_ALL_MASK (GENMASK(10, 0) | GENMASK(16, 13)) + +/** @name FGMAC_DMA_MISSED_FRAME_CNT_OFFSET Register + */ +#define FGMAC_DMA_MISSED_FRAME_CTR_CMIS GENMASK(15, 0) /* 由于主机接收缓冲区不可用而导致控制器丢失的帧数 */ +#define FGMAC_DMA_MISSED_FRAME_CTR_OVMIS BIT(16) +#define FGMAC_DMA_MISSED_FRAME_CTR_CFIFO GENMASK(27, 17) +#define FGMAC_DMA_MISSED_FRAME_CTR_OVFIFO BIT(28) + +/** @name FGMAC_DMA_RX_WATCHDOG_OFFSET Register + */ +#define FGMAC_DMA_RX_WATCHDOG_RIWT GENMASK(7, 0) + +/** @name FGMAC_DMA_AXI_BUS_MOD_OFFSET Register + */ +#define FGMAC_DMA_AXI_BUS_MOD_UNDEF BIT(0) /* AXI 未定义的突发长度 */ +#define FGMAC_DMA_AXI_BUS_MOD_BLEN4 BIT(1) /* AXI 突发长度 4 */ +#define FGMAC_DMA_AXI_BUS_MOD_BLEN8 BIT(2) /* AXI 突发长度 8 */ +#define FGMAC_DMA_AXI_BUS_MOD_BLEN16 BIT(3) /* AXI 突发长度 16 */ +#define FGMAC_DMA_AXI_BUS_MOD_BLEN32 BIT(4) /* AXI 突发长度 32 */ +#define FGMAC_DMA_AXI_BUS_MOD_BLEN64 BIT(5) /* AXI 突发长度 64 */ +#define FGMAC_DMA_AXI_BUS_MOD_BLEN128 BIT(6) /* AXI 突发长度 128 */ +#define FGMAC_DMA_AXI_BUS_MOD_BLEN256 BIT(7) /* AXI 突发长度 256 */ +#define FGMAC_DMA_AXI_BUS_MOD_AXI_AAL BIT(12) /* 地址对齐的节拍 */ +#define FGMAC_DMA_AXI_BUS_MOD_RD_OSR_LMT_MASK GENMASK(19, 16) +#define FGMAC_DMA_AXI_BUS_MOD_RD_OSR_LMT(x) ((x) << 16) /* XI 最大读取未决请求限制此值限 制 AXI 读取接口上的最大未完成请求。 */ +#define FGMAC_DMA_AXI_BUS_MOD_WR_OSR_LMT_MASK GENMASK(23, 20) +#define FGMAC_DMA_AXI_BUS_MOD_WR_OSR_LMT(x) ((x) << 20) /* AXI 最大写入未决请求限制此值 限制 AXI 写入接口上的最大未完成请求。 */ +#define FGMAC_DMA_AXI_BUS_MOD_UNLCK_ON_MGK_RWK BIT(30) +#define FGMAC_DMA_AXI_BUS_MOD_EN_LPI BIT(31) + +#define FGMAC_DMA_DESC_ADDR_MASK GENMASK(31, 4) + +#define FGMAC_MAC_ADDR_LEN 6 +#define FGMAC_RETRY_TIMES 500 +#define FGMAC_DELAY_US 100 +/**************************** Type Definitions *******************************/ + +typedef u8 FGmacMacAddr[FGMAC_MAC_ADDR_LEN]; + +enum +{ + FGMAC_CTRL_INTR = 0, + FGMAC_DMA_INTR, + + FGMAC_MAX_INTR_TYPE +}; + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +/* 读FGMAC控制器寄存器,以u32返回整个寄存器值 */ +#define FGMAC_READ_REG32(addr, reg_offset) FtIn32((addr) + (u32)(reg_offset)) + +/* 写FGMAC控制器寄存器,以u32覆盖写入整个寄存器值 */ +#define FGMAC_WRITE_REG32(addr, reg_offset, reg_value) FtOut32((addr) + (u32)(reg_offset), (u32)(reg_value)) + +/* 将FGMAC控制器寄存器的特定位置为1, 不改变其它位 */ +#define FGMAC_SET_REG32(addr, reg_offset, set_bits) FtSetBit32((addr) + (u32)(reg_offset), (u32)(set_bits)) + +/* 将FGMAC控制器寄存器的特定位置为0, 不改变其它位 */ +#define FGMAC_CLR_REG32(addr, reg_offset, clr_bits) FtClearBit32((addr) + (u32)(reg_offset), (u32)(clr_bits)) + +/* 在一个总长度为num的环内将索引idx增加1 */ +#define FGMAC_DMA_INC_DESC(idx, num) (idx) = (((idx) + 1) % (num)) + +static inline void FGmacResumeDmaSend(uintptr base_addr) +{ + if (FGMAC_READ_REG32(base_addr, FGMAC_DMA_STATUS_OFFSET) & FGMAC_DMA_STATUS_TU) + { + FGMAC_SET_REG32(base_addr, FGMAC_DMA_STATUS_OFFSET, FGMAC_DMA_STATUS_TU); /* clear TBUS GMAC DMA flag */ + FGMAC_WRITE_REG32(base_addr, FGMAC_DMA_TX_POLL_REQ_OFFSET, 0xff); /* resume DMA transmission */ + } + else + { + FGMAC_WRITE_REG32(base_addr, FGMAC_DMA_TX_POLL_REQ_OFFSET, 0xff); /* resume DMA transmission */ + } +} + +static inline void FGmacResumeDmaRecv(uintptr base_addr) +{ + if (FGMAC_READ_REG32(base_addr, FGMAC_DMA_STATUS_OFFSET) & FGMAC_DMA_STATUS_RU) + { + FGMAC_SET_REG32(base_addr, FGMAC_DMA_STATUS_OFFSET, FGMAC_DMA_STATUS_RU); /* Clear RBUS GMAC DMA flag */ + FGMAC_WRITE_REG32(base_addr, FGMAC_DMA_RX_POLL_REQ_OFFSET, 0xff); /* Resume DMA transmission*/ + } + else + { + FGMAC_WRITE_REG32(base_addr, FGMAC_DMA_RX_POLL_REQ_OFFSET, 0xff); + } +} + +static inline void FGmacStartDmaTrans(uintptr base_addr) +{ + FGMAC_SET_REG32(base_addr, FGMAC_DMA_OP_OFFSET, (FGMAC_DMA_OP_SR | FGMAC_DMA_OP_ST)); /* enable dma tx and rx */ + FGMAC_SET_REG32(base_addr, FGMAC_CONF_OFFSET, (FGMAC_CONF_RX_EN | FGMAC_CONF_TX_EN)); /* enable gmac tx and rx */ + + /* clear Tx and Rx process stopped flags */ + FGMAC_WRITE_REG32(base_addr, FGMAC_DMA_INTR_OFFSET, + (FGMAC_DMA_INTR_ENA_RIE | FGMAC_DMA_INTR_ENA_AIE | FGMAC_DMA_INTR_ENA_NIE)); +} + +static inline void FGmacStopDmaTrans(uintptr base_addr) +{ + FGMAC_CLR_REG32(base_addr, FGMAC_DMA_OP_OFFSET, (FGMAC_DMA_OP_SR | FGMAC_DMA_OP_ST)); /* disable dma tx and rx */ + FGMAC_CLR_REG32(base_addr, FGMAC_CONF_OFFSET, (FGMAC_CONF_RX_EN | FGMAC_CONF_TX_EN)); /* disable gmac tx and rx */ +} + +static inline void FGmacResmuDmaUnderflow(uintptr base_addr) +{ + if (FGMAC_DMA_STATUS_UNF & FGMAC_READ_REG32(base_addr, FGMAC_DMA_STATUS_OFFSET)) + { + FGMAC_SET_REG32(base_addr, FGMAC_DMA_STATUS_OFFSET, FGMAC_DMA_STATUS_UNF); /* clear TBUS GMAC DMA flag */ + FGMAC_WRITE_REG32(base_addr, FGMAC_DMA_TX_POLL_REQ_OFFSET, 0xff); /* resume DMA transmission*/ + } +} + +/************************** Function Prototypes ******************************/ +/* 获取FGMAC控制器配置的MAC地址 */ +void FGmacGetMacAddr(uintptr base_addr, FGmacMacAddr mac_addr); + +/* 设置FGMAC控制器的MAC地址 */ +void FGmacSetMacAddr(uintptr base_addr, const FGmacMacAddr mac_addr); + +/* 触发FGMAC控制器软件复位 */ +FError FGmacSoftwareReset(uintptr base_addr, int timeout); + +FError FGmacFlushTxFifo(uintptr base_addr, int timeout); + +void FGmacStopDmaTrans(uintptr base_addr); + +/* wait fgmac mii not busy */ +FError FGmacPhyWaitBusBusy(uintptr base_addr, int timeout); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsp/phytium/libraries/standalone/drivers/eth/fgmac/fgmac_intr.c b/bsp/phytium/libraries/standalone/drivers/eth/fgmac/fgmac_intr.c new file mode 100644 index 0000000000..e23aabfb44 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/eth/fgmac/fgmac_intr.c @@ -0,0 +1,177 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgmac_intr.c + * Date: 2022-04-06 14:46:52 + * LastEditTime: 2022-04-06 14:46:58 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +/***************************** Include Files *********************************/ + +#include "fgmac.h" +#include "fgmac_hw.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FGMAC_CALL_EVT_HANDLER(instance_p, evt) \ + {\ + if (NULL != (instance_p)->evt_handler[(evt)]) \ + { \ + (instance_p)->evt_handler[evt]((void *)(instance_p)); \ + }\ + } + +/************************** Variable Definitions *****************************/ + +/************************** Function Prototypes ******************************/ + +/** + * @name: FGmacInterruptHandler + * @msg: FGMAC中断处理函数 + * @return {*} + * @param {s32} vector, 中断向量号,此处没有用到 + {void} *param, 中断输入参数,此处传入的是FGMAC的驱动控制数据 + * @note 此函数运行在中断上下文 + */ +void FGmacInterruptHandler(s32 vector, void *param) +{ + FASSERT(param); + FGmac *instance_p = (FGmac *)param; + uintptr base_addr = instance_p->config.base_addr; + u32 status = 0; + + /* dma interrupt */ + status = FGMAC_READ_REG32(base_addr, FGMAC_DMA_STATUS_OFFSET); + + if (FGMAC_DMA_STATUS_GLI & status) + { + FGMAC_CALL_EVT_HANDLER(instance_p, FGMAC_PHY_STATUS_EVT); + } + + if (FGMAC_DMA_STATUS_RI & status) + { + FGMAC_CALL_EVT_HANDLER(instance_p, FGMAC_RX_COMPLETE_EVT); + FGMAC_SET_REG32(base_addr, FGMAC_DMA_STATUS_OFFSET, FGMAC_DMA_STATUS_RI); /* write to clear */ + } + + if (FGMAC_DMA_STATUS_TI & status) + { + FGMAC_CALL_EVT_HANDLER(instance_p, FGMAC_TX_COMPLETE_EVT); + FGMAC_SET_REG32(base_addr, FGMAC_DMA_STATUS_OFFSET, FGMAC_DMA_STATUS_TI); /* write to clear */ + } + + if (FGMAC_DMA_STATUS_AIS & status) + { + FGMAC_CALL_EVT_HANDLER(instance_p, FGMAC_DMA_ERR_EVT); + FGMAC_SET_REG32(base_addr, FGMAC_DMA_STATUS_OFFSET, FGMAC_DMA_STATUS_CLR_ABLE); + } + + FGMAC_WRITE_REG32(base_addr, FGMAC_DMA_STATUS_OFFSET, status); /* write to clear */ + + /* RGMII/SGMII Interrupt */ + status = FGMAC_READ_REG32(base_addr, FGMAC_INTR_STATUS_OFFSET); + if (status & FGMAC_ISR_STATUS_RSIS) + { + /* status changed, read SGMII register to clear */ + FGMAC_READ_REG32(base_addr, FGMAC_MAC_PHY_STATUS); + } + + return; +} + +/** + * @name: FGmacRegisterEvtHandler + * @msg: 注册FGMAC中断事件响应函数 + * @return {*} + * @param {FGmac} *instance_p 驱动控制数据 + * @param {u32} evt 中断事件类型 + * @param {FGmacEvtHandler} handler 中断事件响应函数 + * @note 注册的函数handler会在中断上下文执行 + */ +void FGmacRegisterEvtHandler(FGmac *instance_p, u32 evt, FGmacEvtHandler handler) +{ + FASSERT((NULL != instance_p) && (FGMAC_INTR_EVT_NUM > evt)); + instance_p->evt_handler[evt] = handler; +} + +/** + * @name: FGmacSetInterruptMask + * @msg: 屏蔽FGMAC中断 + * @return {*} + * @param {FGmac} *instance_p 驱动控制数据 + * @param {u32} intr_type 中断类型 GMAC中断/DMA中断 + * @param {u32} mask 中断屏蔽位 + * @note 在FGMAC驱动初始化成功后调用此函数 + */ +void FGmacSetInterruptMask(FGmac *instance_p, u32 intr_type, u32 mask) +{ + FASSERT(instance_p); + FASSERT(FGMAC_MAX_INTR_TYPE > intr_type); + u32 cur_mask = 0; + uintptr base_addr = instance_p->config.base_addr; + + if (FGMAC_CTRL_INTR == intr_type) + { + cur_mask = FGMAC_READ_REG32(base_addr, FGMAC_INTR_MASK_OFFSET); + cur_mask |= mask; + FGMAC_WRITE_REG32(base_addr, FGMAC_INTR_MASK_OFFSET, cur_mask); + } + else + { + cur_mask = FGMAC_READ_REG32(base_addr, FGMAC_DMA_INTR_OFFSET); + cur_mask &= (~mask); + FGMAC_WRITE_REG32(base_addr, FGMAC_DMA_INTR_OFFSET, cur_mask); + } + + return; +} + +/** + * @name: FGmacSetInterruptUmask + * @msg: 使能FGMAC中断 + * @return {*} + * @param {FGmac} *instance_p 驱动控制数据 + * @param {u32} intr_type 中断类型 GMAC中断/DMA中断 + * @param {u32} mask 中断使能标志位 + * @note 在FGMAC驱动初始化成功后调用此函数 + */ +void FGmacSetInterruptUmask(FGmac *instance_p, u32 intr_type, u32 mask) +{ + FASSERT(instance_p); + FASSERT(FGMAC_MAX_INTR_TYPE > intr_type); + u32 cur_mask = 0; + uintptr base_addr = instance_p->config.base_addr; + + if (FGMAC_CTRL_INTR == intr_type) + { + cur_mask = FGMAC_READ_REG32(base_addr, FGMAC_INTR_MASK_OFFSET); + cur_mask &= (~mask); + FGMAC_WRITE_REG32(base_addr, FGMAC_INTR_MASK_OFFSET, cur_mask); + } + else + { + cur_mask = FGMAC_READ_REG32(base_addr, FGMAC_DMA_INTR_OFFSET); + cur_mask |= mask; + FGMAC_WRITE_REG32(base_addr, FGMAC_DMA_INTR_OFFSET, cur_mask); + } + + return; +} diff --git a/bsp/phytium/libraries/standalone/drivers/eth/fgmac/fgmac_sinit.c b/bsp/phytium/libraries/standalone/drivers/eth/fgmac/fgmac_sinit.c new file mode 100644 index 0000000000..87822e48b0 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/eth/fgmac/fgmac_sinit.c @@ -0,0 +1,68 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgmac_sinit.c + * Date: 2022-04-06 14:46:52 + * LastEditTime: 2022-04-06 14:46:58 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +/* - This file contains the implementation of driver's static initialization functionality. +- 驱动静态初始化 */ + +/***************************** Include Files *********************************/ + +#include "ftypes.h" +#include "fparameters.h" +#include "fgmac.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Variable Definitions *****************************/ + +extern const FGmacConfig FGMAC_CONFIG_TBL[GMAC_INSTANCE_NUM]; + +/************************** Function Prototypes ******************************/ +/** + * @name: FGmacLookupConfig + * @msg: 获取FGMAC驱动的默认配置参数 + * @return {const FGmacConfig *}, 驱动默认配置 + * @param {u32} instance_id, 驱动控制器号 + * @note 返回FGMAC的默认配置,复制后修改配置 + * 需要确认当前平台支持输入的instance_id + */ +const FGmacConfig *FGmacLookupConfig(u32 instance_id) +{ + const FGmacConfig *ptr = NULL; + u32 index; + + for (index = 0; index < (u32)GMAC_INSTANCE_NUM; index++) + { + if (FGMAC_CONFIG_TBL[index].instance_id == instance_id) + { + ptr = &FGMAC_CONFIG_TBL[index]; + break; + } + } + + return (const FGmacConfig *)ptr; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/eth/fgmac/phy/ar803x/fgmac_ar803x.c b/bsp/phytium/libraries/standalone/drivers/eth/fgmac/phy/ar803x/fgmac_ar803x.c new file mode 100644 index 0000000000..038afa1ede --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/eth/fgmac/phy/ar803x/fgmac_ar803x.c @@ -0,0 +1,198 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgmac_ar803x.c + * Date: 2022-04-06 14:46:52 + * LastEditTime: 2022-04-06 14:46:58 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +/***************************** Include Files *********************************/ + +#include "fio.h" +#include "ferror_code.h" +#include "ftypes.h" +#include "fdebug.h" + +#include "fgmac_hw.h" +#include "fgmac.h" +#include "fgmac_ar803x.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FGMAC_DEBUG_TAG "FGMAC-AR803X" +#define FGMAC_ERROR(format, ...) FT_DEBUG_PRINT_E(FGMAC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGMAC_WARN(format, ...) FT_DEBUG_PRINT_W(FGMAC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGMAC_INFO(format, ...) FT_DEBUG_PRINT_I(FGMAC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGMAC_DEBUG(format, ...) FT_DEBUG_PRINT_D(FGMAC_DEBUG_TAG, format, ##__VA_ARGS__) + +/************************** Function Prototypes ******************************/ +/* write phy register */ +extern FError FGmacWritePhyReg(FGmac *instance_p, u32 phy_address, u16 phy_reg, u16 phy_reg_val); +/* read phy register */ +extern FError FGmacReadPhyReg(FGmac *instance_p, u32 phy_address, u16 phy_reg, u16 *phy_reg_val_p); + + +/************************** Variable Definitions *****************************/ + +/*****************************************************************************/ + +/* 此文件主要为了完成用户对外接口,用户可以使用这些接口直接开始工作 */ + +/* - 包括用户API的定义和实现 + - 同时包含必要的OPTION方法,方便用户进行配置 + - 如果驱动可以直接进行I/O操作,在此源文件下可以将API 进行实现 */ + + +/** + * @name: FGmacAr803xDebugRegRead + * @msg: read phy debug register value + * @param {FGmac} *instance_p, instance of FGmac controller + * @param {u32} phy_address, phy address connect to fgmac + * @param {u16} debug_reg, phy debug register offset to read + * @param {u16} *reg_data_p, phy register value pointer + * @return err code information, FGMAC_SUCCESS indicates success,others indicates failed + */ +static FError FGmacAr803xDebugRegRead(FGmac *instance_p, u32 phy_address, u16 debug_reg, u16 *reg_data_p) +{ + FASSERT(instance_p && reg_data_p); + FError ret = FGMAC_SUCCESS; + + ret = FGmacWritePhyReg(instance_p, phy_address, FGMAC_AR803X_DEBUG_ADDR, debug_reg & FGMAC_AR803X_DEBUG_DATA_MASK); + if (FGMAC_SUCCESS != ret) + return ret; + + ret = FGmacReadPhyReg(instance_p, phy_address, FGMAC_AR803X_DEBUG_DATA, reg_data_p); + return ret; +} + +static FError FGmacAr803xMaskReg(FGmac *instance_p, u32 phy_address, u16 reg, u32 clear, u32 set) +{ + FASSERT(instance_p); + FError ret = FGMAC_SUCCESS; + u16 val = 0; + + ret = FGmacReadPhyReg(instance_p, phy_address, reg, &val); + if (FGMAC_SUCCESS != ret) + return ret; + + val &= ~clear; + val |= set; + + ret = FGmacWritePhyReg(instance_p, phy_address, FGMAC_AR803X_DEBUG_DATA, val); + return ret; +} + +/** + * @name: FGmacAr803xDisableHibernate + * @msg: disable phy Power hibernate control + * @param {FGmac} *instance_p, instance of FGmac controller + * @return err code information, FGMAC_SUCCESS indicates success,others indicates failed + */ +FError FGmacAr803xDisableHibernate(FGmac *instance_p) +{ + FASSERT(instance_p); + u32 ret = FGMAC_SUCCESS; + u16 reg_val = 0; + + ret = FGmacAr803xDebugRegRead(instance_p, instance_p->phy_addr, FGMAC_AR803X_DEBUG_HIB_CTRL_REG, ®_val); + if (FGMAC_SUCCESS != ret) + return ret; + + reg_val &= ~FGMAC_AR803X_PS_HIB_EN; + ret = FGmacWritePhyReg(instance_p, instance_p->phy_addr, FGMAC_AR803X_DEBUG_DATA, reg_val); + + reg_val = 0; + FGmacAr803xDebugRegRead(instance_p, instance_p->phy_addr, FGMAC_AR803X_DEBUG_HIB_CTRL_REG, ®_val); + FGMAC_INFO("debug reg: 0x%lx, ret: 0x%lx", reg_val, ret); + + return ret; +} + +/** + * @name: FFmacAr803xRxClockDelayControl + * @msg: control phy debug register0 for rx clock delay + * @param {FGmac} *instance_p, instance of FGmac controller + * @param {u32} enable_setting, 1-enable, else disable + * @return err code information, FGMAC_SUCCESS indicates success,others indicates failed + */ +FError FFmacAr803xRxClockDelayControl(FGmac *instance_p, u32 enable_setting) +{ + FASSERT(instance_p); + u32 ret = FGMAC_SUCCESS; + u16 reg_val = 0; + + ret = FGmacAr803xDebugRegRead(instance_p, instance_p->phy_addr, FGMAC_AR803X_RX_CLOCK_CTRL_REG, ®_val); + if (FGMAC_SUCCESS != ret) + return ret; + + if (enable_setting == FGMAC_RX_CLOCK_ENABLE) + { + reg_val |= FGMAC_AR803X_RX_CLOCK_DELAY; + } + else + { + reg_val &= ~FGMAC_AR803X_RX_CLOCK_DELAY; + } + + ret = FGmacWritePhyReg(instance_p, instance_p->phy_addr, FGMAC_AR803X_DEBUG_DATA, reg_val); + + reg_val = 0; + FGmacAr803xDebugRegRead(instance_p, instance_p->phy_addr, FGMAC_AR803X_RX_CLOCK_CTRL_REG, ®_val); + FGMAC_INFO("debug reg: 0x%lx, ret: 0x%lx", reg_val, ret); + + return ret; +} + +/** + * @name: FFmacAr803xTxClockDelayControl + * @msg: control phy debug register5 for tx clock delay + * @param {FGmac} *instance_p, instance of FGmac controller + * @param {u32} enable_setting, 1-enable, else disable + * @return err code information, FGMAC_SUCCESS indicates success,others indicates failed + */ +FError FFmacAr803xTxClockDelayControl(FGmac *instance_p, u32 enable_setting) +{ + FASSERT(instance_p); + u32 ret = FGMAC_SUCCESS; + u16 reg_val = 0; + + ret = FGmacAr803xDebugRegRead(instance_p, instance_p->phy_addr, FGMAC_AR803X_TX_CLOCK_CTRL_REG, ®_val); + if (FGMAC_SUCCESS != ret) + return ret; + + if (enable_setting == FGMAC_TX_CLOCK_ENABLE) + { + reg_val |= FGMAC_AR803X_TX_CLOCK_DELAY; + } + else + { + reg_val &= ~FGMAC_AR803X_TX_CLOCK_DELAY; + } + + ret = FGmacWritePhyReg(instance_p, instance_p->phy_addr, FGMAC_AR803X_DEBUG_DATA, reg_val); + + reg_val = 0; + FGmacAr803xDebugRegRead(instance_p, instance_p->phy_addr, FGMAC_AR803X_TX_CLOCK_CTRL_REG, ®_val); + FGMAC_INFO("debug reg: 0x%lx, ret: 0x%lx", reg_val, ret); + + return ret; +} diff --git a/bsp/phytium/libraries/standalone/drivers/eth/fgmac/phy/ar803x/fgmac_ar803x.h b/bsp/phytium/libraries/standalone/drivers/eth/fgmac/phy/ar803x/fgmac_ar803x.h new file mode 100644 index 0000000000..324583dca3 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/eth/fgmac/phy/ar803x/fgmac_ar803x.h @@ -0,0 +1,93 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgmac_ar803x.h + * Date: 2022-04-06 14:46:52 + * LastEditTime: 2022-04-06 14:46:58 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#ifndef DRIVERS_FGMAC_PHY_AR803X_H +#define DRIVERS_FGMAC_PHY_AR803X_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ + +#include "ftypes.h" +#include "fassert.h" + +#include "fgmac_phy.h" + +/************************** Constant Definitions *****************************/ + +/* phy id */ +#define FGMAC_AR803X_PHY_ID1 0x4D /*803xPhy芯片 id1值为4d 8035芯片 id1 为0x004d id2 为0xd072 */ +#define FGMAC_AR803X_PHY_ID2 0x4D + +/* address and data port */ +#define FGMAC_AR803X_DEBUG_ADDR 0x1DU +#define FGMAC_AR803X_DEBUG_DATA 0x1EU + +/* debug register offset */ +#define FGMAC_AR803X_DEBUG_HIB_CTRL_REG 0xBU +#define FGMAC_AR803X_RX_CLOCK_CTRL_REG 0x0U +#define FGMAC_AR803X_TX_CLOCK_CTRL_REG 0x5U + +#define FGMAC_AR803X_DEBUG_DATA_MASK GENMASK(5, 0) + +/* rx clock delay setting */ +#define FGMAC_RX_CLOCK_ENABLE 1 +#define FGMAC_RX_CLOCK_DISABLE 0 + +/* tx clock delay setting */ +#define FGMAC_TX_CLOCK_ENABLE 1 +#define FGMAC_TX_CLOCK_DISABLE 0 + +/* hib ctrl and auto-negotiation register */ +#define FGMAC_AR803X_PS_HIB_EN BIT(15) + +#define FGMAC_AR803X_RX_CLOCK_DELAY BIT(15) +#define FGMAC_AR803X_TX_CLOCK_DELAY BIT(8) + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/* disable phy Power hibernate control */ +FError FGmacAr803xDisableHibernate(FGmac *instance_p); + +/* control phy debug register0 for rx clock delay */ +FError FFmacAr803xRxClockDelayControl(FGmac *instance_p, u32 enable_setting); + +/* control phy debug register5 for tx clock delay */ +FError FFmacAr803xTxClockDelayControl(FGmac *instance_p, u32 enable_setting); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/bsp/phytium/libraries/standalone/drivers/eth/fgmac/phy/fgmac_phy.c b/bsp/phytium/libraries/standalone/drivers/eth/fgmac/phy/fgmac_phy.c new file mode 100644 index 0000000000..53b0d65ada --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/eth/fgmac/phy/fgmac_phy.c @@ -0,0 +1,523 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgmac_phy.c + * Date: 2022-04-06 14:46:52 + * LastEditTime: 2022-04-06 14:46:53 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +/***************************** Include Files *********************************/ + +#include "fassert.h" +#include "fio.h" +#include "ferror_code.h" +#include "ftypes.h" +#include "fdebug.h" + +#include "fparameters.h" + +#include "fgmac_hw.h" +#include "fgmac_phy.h" +#include "fgmac.h" +#ifdef CONFIG_FGMAC_PHY_AR803X + #include "fgmac_ar803x.h" +#endif +#include "fsleep.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FGMAC_DEBUG_TAG "FGMAC-PHY" +#define FGMAC_ERROR(format, ...) FT_DEBUG_PRINT_E(FGMAC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGMAC_WARN(format, ...) FT_DEBUG_PRINT_W(FGMAC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGMAC_INFO(format, ...) FT_DEBUG_PRINT_I(FGMAC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGMAC_DEBUG(format, ...) FT_DEBUG_PRINT_D(FGMAC_DEBUG_TAG, format, ##__VA_ARGS__) + +/************************** Function Prototypes ******************************/ + + +/************************** Variable Definitions *****************************/ + +/*****************************************************************************/ + +/* 此文件主要为了完成用户对外接口,用户可以使用这些接口直接开始工作 */ + +/* - 包括用户API的定义和实现 + - 同时包含必要的OPTION方法,方便用户进行配置 + - 如果驱动可以直接进行I/O操作,在此源文件下可以将API 进行实现 */ + +/* - 包括用户API的定义和实现 + - 同时包含必要的OPTION方法,方便用户进行配置 + - 如果驱动可以直接进行I/O操作,在此源文件下可以将API 进行实现 */ + +/** + * @name: FGmacWaitPhyAutoNegotiationEnd + * @msg: wait fgmac phy auto negotiation complete + * @param {FGmac} *instance_p, instance of FGmac controller + * @param {u32} phy_address, phy address connect to fgmac + * @return err code information, FGMAC_SUCCESS indicates success,others indicates failed + */ +static FError FGmacWaitPhyAutoNegotiationEnd(FGmac *instance_p, u32 phy_address) +{ + u16 reg_val; + FError ret = FGMAC_SUCCESS; + int timeout = FGMAC_RETRY_TIMES; + do + { + reg_val = 0; + ret = FGmacReadPhyReg(instance_p, phy_address, FGMAC_PHY_MII_STATUS_REG, ®_val); + if (FGMAC_SUCCESS != ret) + break; + fsleep_millisec(20); + } + while ((FGMAC_PHY_MII_SR_AUTO_NEGOT_COMPLETE != (FGMAC_PHY_MII_SR_AUTO_NEGOT_COMPLETE & reg_val)) && + (0 < --timeout)); + + if (FGMAC_SUCCESS != ret) + return ret; + + if (0 >= timeout) + { + FGMAC_ERROR("auto negotiation timeout, reg_val: %#x", reg_val); + ret = FGmacReadPhyReg(instance_p, phy_address, FGMAC_PHY_MII_CTRL_REG, ®_val); + FGMAC_ERROR("auto negotiation timeout, FGMAC_PHY_MII_CTRL_REG reg_val: %#x", reg_val); + ret = FGMAC_ERR_TIMEOUT; + } + + return ret; +} + +/** + * @name: FGmacPhyAutoNegotiation + * @msg: fgmac phy auto negotiation configuration + * @param {FGmac} *instance_p, instance of FGmac controller + * @param {u32} phy_address, phy address connect to fgmac + * @return err code information, FGMAC_SUCCESS indicates success,others indicates failed + */ +static FError FGmacPhyAutoNegotiation(FGmac *instance_p, u32 phy_address) +{ + FASSERT(instance_p); + u16 reg_val; + FError ret = FGMAC_SUCCESS; + int timeout = FGMAC_RETRY_TIMES; + /* check link state */ + do + { + reg_val = 0; + ret = FGmacReadPhyReg(instance_p, phy_address, FGMAC_PHY_MII_STATUS_REG, ®_val); + if (FGMAC_SUCCESS != ret) + break; + fsleep_millisec(20); + + } + while (!(reg_val & FGMAC_PHY_MII_SR_LSTATUS) && (0 <= --timeout)); + + if (0 >= timeout) + { + FGMAC_ERROR("timeout when wait phy auto negotiation "); + return FGMAC_ERR_TIMEOUT; + } + + if (FGMAC_SUCCESS != ret) + return ret; + + ret = FGmacReadPhyReg(instance_p, phy_address, FGMAC_PHY_MII_CTRL_REG, ®_val); + if (FGMAC_SUCCESS != ret) + { + FGMAC_ERROR("auto negotiation failed"); + return ret; + } + + reg_val |= FGMAC_PHY_MII_CR_AUTO_NEGOT; + + ret = FGmacWritePhyReg(instance_p, phy_address, FGMAC_PHY_MII_CTRL_REG, reg_val); + if (FGMAC_SUCCESS != ret) + { + FGMAC_ERROR("auto negotiation failed"); + return ret; + } + + ret = FGmacWaitPhyAutoNegotiationEnd(instance_p, phy_address); + if (FGMAC_SUCCESS != ret) + return ret; + + return ret; +} + +/** + * @name: FGmacPhyNoneNegotiation + * @msg: fgmac phy not negotiation configuration + * @param {FGmac} *instance_p, instance of FGmac controller + * @param {u32} phy_address, phy address connect to fgmac + * @return err code information, FGMAC_SUCCESS indicates success,others indicates failed + */ +static FError FGmacPhyNoneNegotiation(FGmac *instance_p, u32 phy_address) +{ + FASSERT(instance_p); + u16 control_reg = 0; + FError ret = FGMAC_SUCCESS; + + /* read phy control register */ + ret = FGmacReadPhyReg(instance_p, phy_address, FGMAC_PHY_MII_CTRL_REG, &control_reg); + if (FGMAC_SUCCESS != ret) + return ret; + + /* 设置半双工模式 */ + if (FGMAC_PHY_MODE_FULLDUPLEX == instance_p->config.duplex_mode) + control_reg |= FGMAC_PHY_MII_CR_DUPLEX_MODE; + else + control_reg &= ~(FGMAC_PHY_MII_CR_DUPLEX_MODE); + + /* 设置速度bit6|bit13, 10b-1000M, 01b-100M, 00b-10M */ + switch (instance_p->config.speed) + { + case FGMAC_PHY_SPEED_1000: + control_reg |= FGMAC_PHY_MII_CR_SPEED_SEL_MSB; + control_reg &= ~(FGMAC_PHY_MII_CR_SPEED_SEL_LSB); + break; + case FGMAC_PHY_SPEED_100: + control_reg &= ~(FGMAC_PHY_MII_CR_SPEED_SEL_MSB); + control_reg |= FGMAC_PHY_MII_CR_SPEED_SEL_LSB; + break; + case FGMAC_PHY_SPEED_10: + control_reg &= ~(FGMAC_PHY_MII_CR_SPEED_SEL_MSB); + control_reg &= ~(FGMAC_PHY_MII_CR_SPEED_SEL_LSB); + break; + default: + FASSERT(0); + break; + } + + /* disable auto-negotiation */ + control_reg &= ~(FGMAC_PHY_MII_CR_AUTO_NEGOT); + control_reg &= ~(FGMAC_PHY_MII_CR_RESTART_AUTO_NEGO); + + /* write phy control register */ + ret = FGmacWritePhyReg(instance_p, phy_address, FGMAC_PHY_MII_CTRL_REG, control_reg); + if (FGMAC_SUCCESS != ret) + { + FGMAC_ERROR("disable auto-negotiation failed"); + return ret; + } + + return ret; +} + +/** + * @name: FGmacWritePhyReg + * @msg: write phy register value + * @param {FGmac} *instance_p, instance of FGmac controller + * @param {u32} phy_address, phy address connect to fgmac + * @param {u16} phy_reg, phy register offset to write + * @param {u16} phy_reg_val, value write to phy register + * @return err code information, FGMAC_SUCCESS indicates success,others indicates failed + */ +FError FGmacWritePhyReg(FGmac *instance_p, u32 phy_address, u16 phy_reg, u16 phy_reg_val) +{ + FASSERT(instance_p); + u32 cmd_reg_val; + uintptr base_addr = instance_p->config.base_addr; + FError ret = FGMAC_SUCCESS; + + cmd_reg_val = FGMAC_MII_ADDR_CR(instance_p->config.mdc_clk_hz); + cmd_reg_val |= FGMAC_MII_ADDR_PA(phy_address); + cmd_reg_val |= FGMAC_MII_ADDR_GR(phy_reg); + cmd_reg_val |= FGMAC_MII_ADDR_GW; + cmd_reg_val |= FGMAC_MII_ADDR_GB; + + FGMAC_WRITE_REG32(base_addr, FGMAC_GMII_DATA_OFFSET, phy_reg_val); + FGMAC_WRITE_REG32(base_addr, FGMAC_GMII_ADDR_OFFSET, cmd_reg_val); + + ret = FGmacPhyWaitBusBusy(base_addr, FGMAC_RETRY_TIMES); + return ret; +} + +/** + * @name: FGmacReadPhyReg + * @msg: read phy register value + * @param {FGmac} *instance_p, instance of FGmac controller + * @param {u32} phy_address, phy address connect to fgmac + * @param {u16} phy_reg, phy register offset to read + * @param {u16} *phy_reg_val_p, phy register value pointer + * @return err code information, FGMAC_SUCCESS indicates success,others indicates failed + */ +FError FGmacReadPhyReg(FGmac *instance_p, u32 phy_address, u16 phy_reg, u16 *phy_reg_val_p) +{ + FASSERT(instance_p && phy_reg_val_p); + u32 cmd_reg_val; + uintptr base_addr = instance_p->config.base_addr; + FError ret = FGMAC_SUCCESS; + + cmd_reg_val = FGMAC_MII_ADDR_CR(instance_p->config.mdc_clk_hz); + cmd_reg_val |= FGMAC_MII_ADDR_PA(phy_address); + cmd_reg_val |= FGMAC_MII_ADDR_GR(phy_reg); + cmd_reg_val &= ~FGMAC_MII_ADDR_GW; + cmd_reg_val |= FGMAC_MII_ADDR_GB; + + ret = FGmacPhyWaitBusBusy(base_addr, FGMAC_RETRY_TIMES); + if (FGMAC_SUCCESS != ret) + return ret; + + FGMAC_WRITE_REG32(base_addr, FGMAC_GMII_ADDR_OFFSET, cmd_reg_val); + + ret = FGmacPhyWaitBusBusy(base_addr, FGMAC_RETRY_TIMES); + if (FGMAC_SUCCESS != ret) + return ret; + + *phy_reg_val_p = FGMAC_MII_DATA_GD_MASK & FGMAC_READ_REG32(base_addr, FGMAC_GMII_DATA_OFFSET); + return ret; +} + +/** + * @name: FGmacPhyDetect + * @msg: detect fgmac phy, and get phy addr + * @param {FGmac} *instance_p, instance of FGmac controller + * @return err code information, FGMAC_SUCCESS indicates success,others indicates failed + */ +FError FGmacPhyDetect(FGmac *instance_p) +{ + u32 phy_addr = 0; + u16 phy_reg = 0, phy_id1_reg, phy_id2_reg; + FError ret = FGMAC_SUCCESS; + u32 invalid_count = 0; + + for (phy_addr = 0; phy_addr < FGMAC_PHY_MAX_NUM; phy_addr++) + { + ret = FGmacReadPhyReg(instance_p, phy_addr, FGMAC_PHY_MII_STATUS_REG, &phy_reg); + if (ret != FGMAC_SUCCESS) + { + FGMAC_ERROR("%s, PHY operation is busy", __func__); + return ret; + } + + if (phy_reg != 0xffff) + { + ret = FGmacReadPhyReg(instance_p, phy_addr, FGMAC_PHY_MII_PHYSID1_REG, &phy_id1_reg); + ret |= FGmacReadPhyReg(instance_p, phy_addr, FGMAC_PHY_MII_PHYSID2_REG, &phy_id2_reg); + + if ((ret == FGMAC_SUCCESS) && (phy_id1_reg != 0xffff) && (phy_id2_reg != 0xffff)) + { + /* assign the max valid phy address to instance_p->phy_addr */ + instance_p->phy_addr = phy_addr; + instance_p->phy_valid_mask |= (1 << phy_addr); + instance_p->phy_id1 = phy_id1_reg; + + FGMAC_INFO("phy_addr: [%d], phy_valid_mask: 0x%x, phy id: [0x%08x][0x%08x], phy_reg:0x%x", + phy_addr, instance_p->phy_valid_mask, phy_id1_reg, phy_id2_reg, phy_reg); + + return ret; + } + } + else + { + invalid_count++; + } + } + + if (invalid_count == FGMAC_PHY_MAX_NUM) + { + FGMAC_ERROR("phy detect failed, phy address is not found!"); + return FGMAC_ERR_PHY_IS_NOT_FOUND; + } + + return FGMAC_SUCCESS; +} + +/** + * @name: FGmacPhyReset + * @msg: detect fgmac phy, and get phy addr + * @param {FGmac} *instance_p, instance of FGmac controller + * @param {u32} phy_address, phy address connect to fgmac + * @return err code information, FGMAC_SUCCESS indicates success,others indicates failed + */ +FError FGmacPhyReset(FGmac *instance_p, u32 phy_address) +{ + FError ret = FGMAC_ERR_PHY_NOT_SUPPORT; + uintptr base_addr = instance_p->config.base_addr; + ret = FGmacWritePhyReg(instance_p, phy_address, FGMAC_PHY_MII_CTRL_REG, FGMAC_PHY_MII_CR_RESET); + if (FGMAC_SUCCESS != ret) + { + FGMAC_ERROR("reset phy failed"); + return ret; + } + return FGMAC_SUCCESS; +} + +/** + * @name: FGmacGetPhySpecialStatus + * @msg: read phy special status register to get speed and duplex mode + * @param {FGmac} *instance_p, instance of FGmac controller + * @param {u32} phy_address, phy address connect to fgmac + * @return err code information, FGMAC_SUCCESS indicates success,others indicates failed + */ +static FError FGmacGetPhySpecialStatus(FGmac *instance_p, u32 phy_address) +{ + u16 phy_special_status = 0; + u32 ret = FGMAC_SUCCESS; + + ret = FGmacReadPhyReg(instance_p, phy_address, FGMAC_PHY_MII_PHY_SPECIAL_REG, &phy_special_status); + if (FGMAC_SUCCESS != ret) + { + return ret; + } + + switch (phy_special_status & FGMAC_PHY_SPECIFIC_STATUS_SPEED_MASK) + { + case FGMAC_PHY_SPECIFIC_STATUS_SPEED_1000M: + instance_p->config.speed = FGMAC_PHY_SPEED_1000; + break; + case FGMAC_PHY_SPECIFIC_STATUS_SPEED_100M: + instance_p->config.speed = FGMAC_PHY_SPEED_100; + break; + case FGMAC_PHY_SPECIFIC_STATUS_SPEED_10M: + instance_p->config.speed = FGMAC_PHY_SPEED_10; + break; + default: + break; + } + + if (phy_special_status & FGMAC_PHY_SPECIFIC_STATUS_DUPLEX_MASK) + instance_p->config.duplex_mode = FGMAC_PHY_MODE_FULLDUPLEX; + else + instance_p->config.duplex_mode = FGMAC_PHY_MODE_HALFDUPLEX; + + return ret; +} + +/** + * @name: FGmacPhyCfgInitialize + * @msg: fgmac phy configuration + * @param {FGmac} *instance_p, instance of FGmac controller + * @return err code information, FGMAC_SUCCESS indicates success,others indicates failed + */ +FError FGmacPhyCfgInitialize(FGmac *instance_p) +{ + FASSERT(instance_p); + uintptr base_addr = instance_p->config.base_addr; + FError ret = FGMAC_SUCCESS; + u32 phy_addr; + + /* detect phy address, and assigned the minimum valid address to phy_addr */ + ret = FGmacPhyDetect(instance_p); + if (FGMAC_SUCCESS != ret) + { + FGMAC_ERROR("phy detect failed!"); + return ret; + } + + for (phy_addr = 0; phy_addr <= instance_p->phy_addr; phy_addr++) + { + /* 计算出当前位置 */ + if (instance_p->phy_valid_mask & (1 << phy_addr)) + { + /* set phy power down and set phy Normal operation */ + FGmacPhyReset(instance_p, phy_addr); + + /* auto negotiation */ + if (instance_p->config.en_auto_negtiation) + { + ret = FGmacPhyAutoNegotiation(instance_p, phy_addr); + if (FGMAC_SUCCESS != ret) + { + FGMAC_ERROR("auto negotiation phy failed"); + return ret; + } + } + else + { + /* if gmac is disable auto negotiation, we need set speed */ + ret = FGmacPhyNoneNegotiation(instance_p, phy_addr); + if (FGMAC_SUCCESS != ret) + { + FGMAC_ERROR("negotiation phy failed"); + return ret; + } + } + + /* read phy special status register to get speed and duplex mode */ + ret = FGmacGetPhySpecialStatus(instance_p, phy_addr); + if (FGMAC_SUCCESS != ret) + { + FGMAC_ERROR("get phy special status failed"); + return ret; + } + + FGMAC_DEBUG("instance_p->config.speed: %d", instance_p->config.speed); + FGMAC_DEBUG("instance_p->config.duplex_mode: 0x%x", instance_p->config.duplex_mode); + /* update mac controller speed setting */ + FGmacControllerSpeedConfig(instance_p, instance_p->config.speed); + + /* update mac controller duplex mode setting */ + FGmacControllerDuplexConfig(instance_p, instance_p->config.duplex_mode); + } + } + return ret; +} + +FError FGmacPhyCfgDeInitialize(FGmac *instance_p) +{ + FASSERT(instance_p); + uintptr base_addr = instance_p->config.base_addr; + FError ret = FGMAC_SUCCESS; + u32 phy_addr; + + for (phy_addr = 0; phy_addr <= instance_p->phy_addr; phy_addr++) + { + /* 计算出当前位置 */ + if (instance_p->phy_valid_mask & (1 << phy_addr)) + { + /* set phy power down and set phy Normal operation */ + FGmacPhyReset(instance_p, phy_addr); + } + } + return ret; +} + +/** + * @name: FGmacPhyAwaken + * @msg: fgmac phy awaken + * @param {FGmac} *instance_p, instance of FGmac controller + * @return err code information, FGMAC_SUCCESS indicates success,others indicates failed + */ +FError FGmacPhyAwaken(FGmac *instance_p) +{ + + FError ret = FGMAC_SUCCESS; + +#ifdef CONFIG_FGMAC_PHY_AR803X + ret = FGmacPhyDetect(instance_p); + if (FGMAC_SUCCESS != ret) + { + FGMAC_ERROR("phy detect failed!"); + return ret; + } + + u16 phy_id1; + phy_id1 = instance_p->phy_id1; + + if (phy_id1 == FGMAC_AR803X_PHY_ID1) + { + + ret = FGmacAr803xDisableHibernate(instance_p); + + } +#endif + return ret; +} diff --git a/bsp/phytium/libraries/standalone/drivers/eth/fgmac/phy/fgmac_phy.h b/bsp/phytium/libraries/standalone/drivers/eth/fgmac/phy/fgmac_phy.h new file mode 100644 index 0000000000..6287745cf3 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/eth/fgmac/phy/fgmac_phy.h @@ -0,0 +1,213 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgmac_phy.h + * Date: 2022-04-06 14:46:52 + * LastEditTime: 2022-04-06 14:46:58 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#ifndef DRIVERS_ETH_FGMAC_PHY_H +#define DRIVERS_ETH_FGMAC_PHY_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ + +#include "ftypes.h" +#include "fassert.h" +#include "fkernel.h" +#include "fgmac.h" + +/************************** Constant Definitions *****************************/ +#define FGMAC_PHY_MAX_NUM 32U + +/* Generic MII registers. */ +#define FGMAC_PHY_MII_CTRL_REG 0x00 /* Basic mode control register */ +#define FGMAC_PHY_MII_STATUS_REG 0x01 /* Basic mode status register */ +#define FGMAC_PHY_MII_PHYSID1_REG 0x02 /* PHYS ID 1 */ +#define FGMAC_PHY_MII_PHYSID2_REG 0x03 /* PHYS ID 2 */ +#define FGMAC_PHY_MII_AUTONEG_REG 0x04 /* Advertisement control reg */ +#define FGMAC_PHY_MII_LP_REG 0x05 /* Link partner ability reg */ +#define FGMAC_PHY_MII_AUTONEG_EX_REG 0x06 /* Expansion register */ +#define FGMAC_PHY_MII_NEXT_PAGE_REG 0x07 /* Next Page Transmit Register */ +#define FGMAC_PHY_MII_LP_NEXT_PAGE_REG 0x08 /* Link Partner Next Page Register */ +#define FGMAC_PHY_MII_CTRL1000_REG 0x09 /* 1000BASE-T control */ +#define FGMAC_PHY_MII_STAT1000_REG 0x0a /* 1000BASE-T status */ +#define FGMAC_PHY_MII_MMD_CTRL_REG 0x0d /* MMD Access Control Register */ +#define FGMAC_PHY_MII_MMD_DATA_REG 0x0e /* MMD Access Data Register */ +#define FGMAC_PHY_MII_ESTATUS_REG 0x0f /* Extended Status */ +#define FGMAC_PHY_MII_FUNC_CONTROL_REG 0x10 /* Function control */ +#define FGMAC_PHY_MII_PHY_SPECIAL_REG 0x11 /* PHY-specific status */ +#define FGMAC_PHY_MII_DCOUNTER_REG 0x12 /* Disconnect counter */ +#define FGMAC_PHY_MII_FCSCOUNTER_REG 0x13 /* False carrier counter */ +#define FGMAC_PHY_MII_NWAYTEST_REG 0x14 /* N-way auto-neg test reg */ +#define FGMAC_PHY_MII_RERRCOUNTER_REG 0x15 /* Receive error counter */ +#define FGMAC_PHY_MII_SREVISION_REG 0x16 /* Silicon revision */ +#define FGMAC_PHY_MII_RESV1_REG 0x17 /* Reserved... */ +#define FGMAC_PHY_MII_LBRERROR_REG 0x18 /* Lpback, rx, bypass error */ +#define FGMAC_PHY_MII_PHYADDR_REG 0x19 /* PHY address */ +#define FGMAC_PHY_MII_RESV2_REG 0x1a /* Reserved... */ +#define FGMAC_PHY_MII_TPISTATUS_REG 0x1b /* TPI status for 10mbps */ +#define FGMAC_PHY_MII_NCONFIG_REG 0x1c /* Network interface config */ + +#define FGMAC_PHY_ID1 (0xffff) +#define FGMAC_PHY_ID2 (0xffff) + +/* Basic mode control register */ +#define FGMAC_PHY_MII_CR_RES GENMASK(5, 0) /* Unused... */ +#define FGMAC_PHY_MII_CR_SPEED_SEL_MSB BIT(6) /* MSB of Speed (1000) */ +#define FGMAC_PHY_MII_CR_COLLISION_TEST BIT(7) /* Collision test */ +#define FGMAC_PHY_MII_CR_DUPLEX_MODE BIT(8) /* Full duplex */ +#define FGMAC_PHY_MII_CR_RESTART_AUTO_NEGO BIT(9) /* Auto negotiation restart */ +#define FGMAC_PHY_MII_CR_ISOLATE BIT(10) /* Isolate data paths from MII */ +#define FGMAC_PHY_MII_CR_POWER_DOWN BIT(11) /* Enable low power state */ +#define FGMAC_PHY_MII_CR_AUTO_NEGOT BIT(12) /* Enable auto negotiation */ +#define FGMAC_PHY_MII_CR_SPEED_SEL_LSB BIT(13) /* Select 100Mbps */ +#define FGMAC_PHY_MII_CR_LOOPBACK BIT(14) /* TXD loopback bits */ +#define FGMAC_PHY_MII_CR_RESET BIT(15) /* Reset to default state */ + +#define FGMAC_PHY_MII_CR_FULLDUPLEX_1000M ((u16)0x2140U) /* Set the full-duplex mode at 1000 Mb/s */ +#define FGMAC_PHY_MII_CR_HALFDUPLEX_1000M ((u16)0x2040U) /* Set the half-duplex mode at 1000 Mb/s */ +#define FGMAC_PHY_MII_CR_FULLDUPLEX_100M ((u16)0x2100U) /* Set the full-duplex mode at 100 Mb/s */ +#define FGMAC_PHY_MII_CR_HALFDUPLEX_100M ((u16)0x2000U) /* Set the half-duplex mode at 100 Mb/s */ +#define FGMAC_PHY_MII_CR_FULLDUPLEX_10M ((u16)0x0100U) /* Set the full-duplex mode at 10 Mb/s */ +#define FGMAC_PHY_MII_CR_HALFDUPLEX_10M ((u16)0x0000U) /* Set the half-duplex mode at 10 Mb/s */ + +/* Basic mode status register. */ +#define FGMAC_PHY_MII_SR_EXT_CAP BIT(0) /* Ext-reg capability */ +#define FGMAC_PHY_MII_SR_JCD BIT(1) /* Jabber detected */ +#define FGMAC_PHY_MII_SR_LSTATUS BIT(2) /* Link status */ +#define FGMAC_PHY_MII_SR_AUTO_NEGOT BIT(3) /* Able to do auto-negotiation */ +#define FGMAC_PHY_MII_SR_REMOTE_FAULT BIT(4) /* Remote fault detected */ +#define FGMAC_PHY_MII_SR_AUTO_NEGOT_COMPLETE BIT(5) /* Auto-negotiation complete */ +#define FGMAC_PHY_MII_SR_MF_PREAM BIT(6) /* MF Preamble Suppression */ +#define FGMAC_PHY_MII_SR_EXT_STATUS BIT(8) /* Extended Status in R15 */ +#define FGMAC_PHY_MII_SR_100HALF2 BIT(9) /* Can do 100BASE-T2 HDX */ +#define FGMAC_PHY_MII_SR_100FULL2 BIT(10) /* Can do 100BASE-T2 FDX */ +#define FGMAC_PHY_MII_SR_10HALF BIT(11) /* Can do 10mbps, half-duplex */ +#define FGMAC_PHY_MII_SR_10FULL BIT(12) /* Can do 10mbps, full-duplex */ +#define FGMAC_PHY_MII_SR_100HALF BIT(13) /* Can do 100mbps, half-duplex */ +#define FGMAC_PHY_MII_SR_100FULL BIT(14) /* Can do 100mbps, full-duplex */ +#define FGMAC_PHY_MII_SR_100BASE4 BIT(15) /* Can do 100mbps, 4k packets */ + +/* MII_STAT1000 masks */ +#define FGMAC_PHY_MII_1000BTSR_MSCF BIT(15) /* Master/Slave Configuration Fault */ +#define FGMAC_PHY_MII_1000BTSR_MSCR BIT(14) /* Master/Slave Configuration Resolution */ +#define FGMAC_PHY_MII_1000BTSR_LRS BIT(13) /* Local Receiver Status */ +#define FGMAC_PHY_MII_1000BTSR_RRS BIT(12) /* Remote Receiver Status */ +#define FGMAC_PHY_MII_1000BTSR_1000FD BIT(11) /* Full Duplex Capability */ +#define FGMAC_PHY_MII_1000BTSR_1000HD BIT(10) /* Half Duplex Capability */ +#define FGMAC_PHY_MII_1000BTSR_IDLE_ERR_CNT GENMASK(7, 0) /* MSB of Idle Error Counter */ + +/* Link partner ability register. */ +#define FGMAC_PHY_MII_LPA_SELECT GENMASK(4, 0) /* Selector Field */ +#define FGMAC_PHY_MII_LPA_10HALF BIT(5) /* 10BASE-T half-duplex capable */ +#define FGMAC_PHY_MII_LPA_10FULL BIT(6) /* 10BASE-T full-duplex capable */ +#define FGMAC_PHY_MII_LPA_1000TXHALF BIT(7) /* 100BASE-TX half-duplex capable */ +#define FGMAC_PHY_MII_LPA_1000TXFULL BIT(8) /* 100BASE-TX full-duplex capable */ +#define FGMAC_PHY_MII_LPA_T4 BIT(9) /* 100BASE-T4 capable */ +#define FGMAC_PHY_MII_LPA_PAUSE BIT(10) /* capable of pause operation */ +#define FGMAC_PHY_MII_LPA_ASY_PAUSE BIT(11) /* asymmetric pause */ +#define FGMAC_PHY_MII_LPA_REMOTE_FAULT BIT(13) /* Remote Fault */ +#define FGMAC_PHY_MII_LPA_ACK BIT(14) /* Acknowledge */ +#define FGMAC_PHY_MII_LPA_NEXT_PAGE BIT(15) /* capable of next page */ + +/* Expansion register for auto-negotiation. */ +#define FGMAC_PHY_MII_ESTATUS_1000_XFULL BIT(15) /* Can do 1000BX Full */ +#define FGMAC_PHY_MII_ESTATUS_1000_XHALF BIT(14) /* Can do 1000BX Half */ +#define FGMAC_PHY_MII_ESTATUS_1000_TFULL BIT(13) /* Can do 1000BT Full */ +#define FGMAC_PHY_MII_ESTATUS_1000_THALF BIT(12) /* Can do 1000BT Half */ +#define FGMAC_PHY_MII_ESTATUS_MASK (FGMAC_PHY_MII_ESTATUS_1000_XFULL | FGMAC_PHY_MII_ESTATUS_1000_XHALF | \ + FGMAC_PHY_MII_ESTATUS_1000_TFULL | FGMAC_PHY_MII_ESTATUS_1000_THALF) +#define FGMAC_PHY_MII_ESTATUS_FULL_MASK (FGMAC_PHY_MII_ESTATUS_1000_XFULL | FGMAC_PHY_MII_ESTATUS_1000_TFULL) + + +#define FGMAC_PHY_SPECIFIC_STATUS_SPEED_MASK GENMASK(15, 14) + +#define FGMAC_PHY_SPECIFIC_STATUS_SPEED_1000M (2L << 14) +#define FGMAC_PHY_SPECIFIC_STATUS_SPEED_100M (1L << 14) +#define FGMAC_PHY_SPECIFIC_STATUS_SPEED_10M (0L << 14) + +#define FGMAC_PHY_SPECIFIC_STATUS_DUPLEX_MASK BIT(13) + +enum +{ + FGMAC_PHY_AUTONEGOTIATION_DISABLE = 0, + FGMAC_PHY_AUTONEGOTIATION_ENABLE +}; + +enum +{ + FGMAC_PHY_MODE_HALFDUPLEX = 0, + FGMAC_PHY_MODE_FULLDUPLEX = 1 +}; + +/* The forced speed, 10Mb, 100Mb, gigabit, 2.5Gb, 10GbE. */ +enum +{ + FGMAC_PHY_SPEED_10 = 10, + FGMAC_PHY_SPEED_100 = 100, + FGMAC_PHY_SPEED_1000 = 1000, + FGMAC_PHY_SPEED_2500 = 2500, + FGMAC_PHY_SPEED_10000 = 10000 +}; + +enum +{ + FGMAC_PHY_MII_ADDR_CR_60_100MHZ = (0b0000), + FGMAC_PHY_MII_ADDR_CR_100_150MHZ = (0b0001), + FGMAC_PHY_MII_ADDR_CR_23_35MHZ = (0b0010), + FGMAC_PHY_MII_ADDR_CR_35_60MHZ = (0b0011), + FGMAC_PHY_MII_ADDR_CR_150_250MHZ = (0b0100), + FGMAC_PHY_MII_ADDR_CR_250_300MHZ = (0b0101) +}; + +/**************************** Type Definitions *******************************/ +/** + * This typedef contains driver instance data. The user is required to allocate a + * variable of this type for every device in the system. A pointer + * to a variable of this type is then passed to the driver API functions. + */ + + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/* init phy device */ +FError FGmacPhyCfgInitialize(FGmac *instance_p); +/* write phy register */ +FError FGmacWritePhyReg(FGmac *instance_p, u32 phy_address, u16 phy_reg, u16 phy_reg_val); +/* read phy register */ +FError FGmacReadPhyReg(FGmac *instance_p, u32 phy_address, u16 phy_reg, u16 *phy_reg_val_p); + +FError FGmacPhyCfgDeInitialize(FGmac *instance_p); + +FError FGmacPhyAwaken(FGmac *instance_p); +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/bsp/phytium/libraries/standalone/drivers/eth/fxmac/Kconfig b/bsp/phytium/libraries/standalone/drivers/eth/fxmac/Kconfig new file mode 100644 index 0000000000..90655a6e74 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/eth/fxmac/Kconfig @@ -0,0 +1,13 @@ +choice FXMAC_PHY_TYPE + prompt "PHY Type" + default FXMAC_PHY_COMMON + help + Select PHY for FGMAC + + config FXMAC_PHY_COMMON + bool "Common" + config FXMAC_PHY_YT + bool "YT" + +endchoice # FXMAC_PHY_TYPE + diff --git a/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac.c b/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac.c new file mode 100644 index 0000000000..ffdca1ffbf --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac.c @@ -0,0 +1,874 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fxmac.c + * Date: 2022-04-06 14:46:52 + * LastEditTime: 2022-04-06 14:46:58 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "fxmac.h" +#include "ftypes.h" +#include "fxmac_hw.h" +#include "stdio.h" + +#include "fdebug.h" + + +#define FXMAC_DEBUG_TAG "FXMAC" +#define FXMAC_PRINT_E(format, ...) FT_DEBUG_PRINT_E(FXMAC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FXMAC_PRINT_I(format, ...) FT_DEBUG_PRINT_I(FXMAC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FXMAC_PRINT_D(format, ...) FT_DEBUG_PRINT_D(FXMAC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FXMAC_PRINT_W(format, ...) FT_DEBUG_PRINT_W(FXMAC_DEBUG_TAG, format, ##__VA_ARGS__) + + +static void FXmacReset(FXmac *instance_p); +extern FError FXmacSetTypeIdCheck(FXmac *instance_p, u32 id_check, u8 index); + +/** + * @name: FXmacSelectClk + * @msg: Determine the driver clock configuration based on the media independent interface + * @param {FXmac} *instance_p is a pointer to the instance to be worked on. + * @param {u32} speed interface speed + * @return {*} + */ +void FXmacSelectClk(FXmac *instance_p) +{ + u32 reg_value; + s32 set_speed = 0; + u32 speed = instance_p->config.speed; + FASSERT(instance_p != NULL); + FASSERT((speed == FXMAC_SPEED_10) || (speed == FXMAC_SPEED_100) || (speed == FXMAC_SPEED_1000) || (speed == FXMAC_SPEED_2500) || (speed == FXMAC_SPEED_10000)); + + if ((instance_p->config.interface == FXMAC_PHY_INTERFACE_MODE_USXGMII) || (instance_p->config.interface == FXMAC_PHY_INTERFACE_MODE_XGMII)) + { + if (speed == FXMAC_SPEED_10000) + { + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_SRC_SEL_LN, 0x1); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_DIV_SEL0_LN, 0x4); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_DIV_SEL1_LN, 0x1); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_PMA_XCVR_POWER_STATE, 0x1); + } + } + else if (instance_p->config.interface == FXMAC_PHY_INTERFACE_MODE_SGMII) + { + FXMAC_PRINT_I("FXMAC_PHY_INTERFACE_MODE_SGMII init"); + if (speed == FXMAC_SPEED_2500) + { + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_SRC_SEL_LN, 0); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_DIV_SEL0_LN, 0x1); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_DIV_SEL1_LN, 0x2); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_PMA_XCVR_POWER_STATE, 0x1); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_TX_CLK_SEL0, 0); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_TX_CLK_SEL1, 0x1); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_TX_CLK_SEL2, 0x1); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_TX_CLK_SEL3, 0x1); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_RX_CLK_SEL0, 0x1); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_RX_CLK_SEL1, 0x0); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_TX_CLK_SEL3_0, 0x0); /*0x1c70*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_TX_CLK_SEL4_0, 0x0); /*0x1c74*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_RX_CLK_SEL3_0, 0x0); /*0x1c78*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_RX_CLK_SEL4_0, 0x0); /*0x1c7c*/ + } + else if (speed == FXMAC_SPEED_1000) + { + FXMAC_PRINT_I("sgmii FXMAC_SPEED_1000 \r\n "); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_SRC_SEL_LN, 1); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_DIV_SEL0_LN, 0x4); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_DIV_SEL1_LN, 0x8); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_PMA_XCVR_POWER_STATE, 0x1); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_TX_CLK_SEL0, 0x0); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_TX_CLK_SEL1, 0x0); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_TX_CLK_SEL2, 0x0); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_TX_CLK_SEL3, 0x1); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_RX_CLK_SEL0, 0x1); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_RX_CLK_SEL1, 0x0); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_TX_CLK_SEL3_0, 0x0); /*0x1c70*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_TX_CLK_SEL4_0, 0x0); /*0x1c74*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_RX_CLK_SEL3_0, 0x0); /*0x1c78*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_RX_CLK_SEL4_0, 0x0); /*0x1c7c*/ + } + else if ((speed == FXMAC_SPEED_100) || (speed == FXMAC_SPEED_10)) + { + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_DIV_SEL0_LN, 0x4); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_DIV_SEL1_LN, 0x8); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_PMA_XCVR_POWER_STATE, 0x1); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_TX_CLK_SEL0, 0x0); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_TX_CLK_SEL1, 0x0); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_TX_CLK_SEL2, 0x1); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_TX_CLK_SEL3, 0x1); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_RX_CLK_SEL0, 0x1); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_RX_CLK_SEL1, 0x0); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_TX_CLK_SEL3_0, 0x1); /*0x1c70*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_TX_CLK_SEL4_0, 0x0); /*0x1c74*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_RX_CLK_SEL3_0, 0x0); /*0x1c78*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_RX_CLK_SEL4_0, 0x1); /*0x1c7c*/ + } + } + else if (instance_p->config.interface == FXMAC_PHY_INTERFACE_MODE_RGMII) + { + FXMAC_PRINT_I("FXMAC_PHY_INTERFACE_MODE_RGMII init"); + if (speed == FXMAC_SPEED_1000) + { + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_MII_SELECT, 0x1); /*0x1c18*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_SEL_MII_ON_RGMII, 0x0); /*0x1c1c*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_TX_CLK_SEL0, 0x0); /*0x1c20*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_TX_CLK_SEL1, 0x1); /*0x1c24*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_TX_CLK_SEL2, 0x0); /*0x1c28*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_TX_CLK_SEL3, 0x0); /*0x1c2c*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_RX_CLK_SEL0, 0x0); /*0x1c30*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_RX_CLK_SEL1, 0x1); /*0x1c34*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_CLK_250M_DIV10_DIV100_SEL, + 0x0); /*0x1c38*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_RX_CLK_SEL5, 0x1); /*0x1c48*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_RGMII_TX_CLK_SEL0, 0x1); /*0x1c80*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_RGMII_TX_CLK_SEL1, 0x0); /*0x1c84*/ + } + else if (speed == FXMAC_SPEED_100) + { + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_SEL_MII_ON_RGMII, 0x0); /*0x1c1c*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_TX_CLK_SEL0, 0x0); /*0x1c20*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_TX_CLK_SEL1, 0x1); /*0x1c24*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_TX_CLK_SEL2, 0x0); /*0x1c28*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_TX_CLK_SEL3, 0x0); /*0x1c2c*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_RX_CLK_SEL0, 0x0); /*0x1c30*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_RX_CLK_SEL1, 0x1); /*0x1c34*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_CLK_250M_DIV10_DIV100_SEL, + 0x0); /*0x1c38*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_RX_CLK_SEL5, 0x1); /*0x1c48*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_RGMII_TX_CLK_SEL0, 0x0); /*0x1c80*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_RGMII_TX_CLK_SEL1, 0x0); /*0x1c84*/ + } + else + { + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_SEL_MII_ON_RGMII, 0x0); /*0x1c1c*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_TX_CLK_SEL0, 0x0); /*0x1c20*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_TX_CLK_SEL1, 0x1); /*0x1c24*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_TX_CLK_SEL2, 0x0); /*0x1c28*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_TX_CLK_SEL3, 0x0); /*0x1c2c*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_RX_CLK_SEL0, 0x0); /*0x1c30*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_RX_CLK_SEL1, 0x1); /*0x1c34*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_CLK_250M_DIV10_DIV100_SEL, + 0x1); /*0x1c38*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_RX_CLK_SEL5, 0x1); /*0x1c48*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_RGMII_TX_CLK_SEL0, 0x0); /*0x1c80*/ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_RGMII_TX_CLK_SEL1, 0x0); /*0x1c84*/ + } + } + else if (instance_p->config.interface == FXMAC_PHY_INTERFACE_MODE_RMII) + { + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_RX_CLK_SEL5, 0x1); /*0x1c48*/ + } + + + switch (speed) + { + case FXMAC_SPEED_25000: + set_speed = 2; + break; + case FXMAC_SPEED_10000: + set_speed = 4; + break; + case FXMAC_SPEED_5000: + set_speed = 3; + break; + case FXMAC_SPEED_2500: + set_speed = 2; + break; + case FXMAC_SPEED_1000: + set_speed = 1; + break; + default: + set_speed = 0; + break; + } + /*GEM_HSMAC(0x0050) provide rate to the external*/ + reg_value = FXMAC_READREG32(instance_p->config.base_address, FXMAC_GEM_HSMAC); + reg_value &= ~FXMAC_GEM_HSMACSPEED_MASK; + reg_value |= (set_speed) &FXMAC_GEM_HSMACSPEED_MASK; + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_GEM_HSMAC, reg_value); + + reg_value = FXMAC_READREG32(instance_p->config.base_address, FXMAC_GEM_HSMAC); + + FXMAC_PRINT_I("FXMAC_GEM_HSMAC is %x \r\n ", reg_value); +} + +/** + * Start the Ethernet controller as follows: + * - Enable transmitter if FXMAC_TRANSMIT_ENABLE_OPTION is set + * - Enable receiver if FXMAC_RECEIVER_ENABLE_OPTION is set + * - Start the SG DMA send and receive channels and enable the device + * interrupt + * + * @param instance_p is a pointer to the instance to be worked on. + * + * @return N/A + * + * @note + * Hardware is configured with scatter-gather DMA, the driver expects to start + * the scatter-gather channels and expects that the user has previously set up + * the buffer descriptor lists. + * + * This function makes use of internal resources that are shared between the + * Start, Stop, and Set/ClearOptions functions. So if one task might be setting + * device options while another is trying to start the device, the user is + * required to provide protection of this shared data (typically using a + * semaphore). + * + * This function must not be preempted by an interrupt that may service the + * device. + * + */ +void FXmacStart(FXmac *instance_p) +{ + u32 reg_val; + u32 reg = 0; + + /* Assert bad arguments and conditions */ + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == (u32)FT_COMPONENT_IS_READY); + + /* Start DMA */ + /* When starting the DMA channels, both transmit and receive sides + * need an initialized BD list. + */ + + FASSERT(instance_p->rx_bd_queue.bdring.base_bd_addr != 0); + + reg = FXMAC_READREG32(instance_p->config.base_address, FXMAC_RXQBASE_OFFSET); + reg = FXMAC_READREG32(instance_p->config.base_address, FXMAC_TXQBASE_OFFSET); + + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_RXQBASE_OFFSET, + instance_p->rx_bd_queue.bdring.base_bd_addr); + + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_TXQBASE_OFFSET, + instance_p->tx_bd_queue.bdring.base_bd_addr); + + reg = FXMAC_READREG32(instance_p->config.base_address, FXMAC_RXQBASE_OFFSET); + reg = FXMAC_READREG32(instance_p->config.base_address, FXMAC_TXQBASE_OFFSET); + + /* clear any existed int status */ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_ISR_OFFSET, + FXMAC_IXR_ALL_MASK); + + /* Enable transmitter if not already enabled */ + if ((instance_p->config.network_default_config & (u32)FXMAC_TRANSMITTER_ENABLE_OPTION) != 0x00000000U) + { + reg_val = FXMAC_READREG32(instance_p->config.base_address, + FXMAC_NWCTRL_OFFSET); + if ((!(reg_val & FXMAC_NWCTRL_TXEN_MASK)) == TRUE) + { + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_NWCTRL_OFFSET, + reg_val | (u32)FXMAC_NWCTRL_TXEN_MASK); + } + } + + /* Enable receiver if not already enabled */ + if ((instance_p->config.network_default_config & FXMAC_RECEIVER_ENABLE_OPTION) != 0x00000000U) + { + + reg_val = FXMAC_READREG32(instance_p->config.base_address, + FXMAC_NWCTRL_OFFSET); + FXMAC_PRINT_I("endable receiver 0x%x \r\n ", reg_val); + if ((!(reg_val & FXMAC_NWCTRL_RXEN_MASK)) == TRUE) + { + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_NWCTRL_OFFSET, + reg_val | (u32)FXMAC_NWCTRL_RXEN_MASK); + } + } + FXMAC_PRINT_I("FXMAC_NWCTRL_OFFSET is 0x%x \r\n", FXMAC_READREG32(instance_p->config.base_address, + FXMAC_NWCTRL_OFFSET)); + + /* Enable TX and RX interrupt */ + FXMAC_INT_ENABLE(instance_p, FXMAC_IXR_LINKCHANGE_MASK | FXMAC_IXR_TX_ERR_MASK | FXMAC_IXR_RX_ERR_MASK | FXMAC_IXR_RXCOMPL_MASK | FXMAC_IXR_TXCOMPL_MASK); + + /* Mark as started */ + instance_p->is_started = FT_COMPONENT_IS_STARTED; + + return; +} + + +/** + * Gracefully stop the Ethernet MAC as follows: + * - Disable all interrupts from this device + * - Stop DMA channels + * - Disable the tansmitter and receiver + * + * Device options currently in effect are not changed. + * + * This function will disable all interrupts. Default interrupts settings that + * had been enabled will be restored when FXmacStart() is called. + * + * @param instance_p is a pointer to the instance to be worked on. + * + * @note + * This function makes use of internal resources that are shared between the + * Start, Stop, Setoptions, and Clearoptions functions. So if one task might be + * setting device options while another is trying to start the device, the user + * is required to provide protection of this shared data (typically using a + * semaphore). + * + * Stopping the DMA channels causes this function to block until the DMA + * operation is complete. + * + */ +void FXmacStop(FXmac *instance_p) +{ + u32 reg_val; + + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == (u32)FT_COMPONENT_IS_READY); + + /* Disable all interrupts */ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_IDR_OFFSET, + FXMAC_IXR_ALL_MASK); + + + + /* Disable the receiver & transmitter */ + reg_val = FXMAC_READREG32(instance_p->config.base_address, + FXMAC_NWCTRL_OFFSET); + reg_val &= (u32)(~FXMAC_NWCTRL_RXEN_MASK); + reg_val &= (u32)(~FXMAC_NWCTRL_TXEN_MASK); + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_NWCTRL_OFFSET, reg_val); + + /* Mark as stopped */ + instance_p->is_started = 0U; +} + +static u32 FXmacClkDivGet(FXmac *instance_p) +{ + FXmacConfig *config_p; + + config_p = &instance_p->config; + + if (config_p->pclk_hz <= 20000000) + { + return FXMAC_NWCFG_CLOCK_DIV8_MASK; + } + else if (config_p->pclk_hz <= 40000000) + { + return FXMAC_NWCFG_CLOCK_DIV16_MASK; + } + else if (config_p->pclk_hz <= 80000000) + { + return FXMAC_NWCFG_CLOCK_DIV32_MASK; + } + else if (instance_p->moudle_id >= 2) + { + if (config_p->pclk_hz <= 120000000) + { + return FXMAC_NWCFG_CLOCK_DIV48_MASK; + } + else if (config_p->pclk_hz <= 160000000) + { + return FXMAC_NWCFG_CLOCK_DIV64_MASK; + } + else if (config_p->pclk_hz <= 240000000) + { + return FXMAC_NWCFG_CLOCK_DIV96_MASK; + } + else if (config_p->pclk_hz <= 320000000) + { + return FXMAC_NWCFG_CLOCK_DIV128_MASK; + } + else + { + return FXMAC_NWCFG_CLOCK_DIV224_MASK; + } + } + else + { + return FXMAC_NWCFG_CLOCK_DIV64_MASK; + } +} + +static u32 FXmacDmaWidth(FXmac *instance_p) +{ + u32 read_regs = 0; + FXmacConfig *config_p; + config_p = &instance_p->config; + + if (instance_p->moudle_id < 2) + { + return FXMAC_NWCFG_BUS_WIDTH_32_MASK; + } + + read_regs = FXMAC_READREG32(config_p->base_address, FXMAC_DESIGNCFG_DEBUG1_OFFSET); + + switch ((read_regs & FXMAC_DESIGNCFG_DEBUG1_BUS_WIDTH_MASK) >> 25) + { + case 4: + FXMAC_PRINT_I("bus width is 128"); + return FXMAC_NWCFG_BUS_WIDTH_128_MASK; + case 2: + FXMAC_PRINT_I("bus width is 64"); + return FXMAC_NWCFG_BUS_WIDTH_64_MASK; + default: + FXMAC_PRINT_I("bus width is 32"); + return FXMAC_NWCFG_BUS_WIDTH_32_MASK; + } +} + +static void FXmacDmaReset(FXmac *instance_p) +{ + u32 queue = 0; + FXmacConfig *config_p; + config_p = &instance_p->config; + u32 dmacfg = 0; + u32 rx_buf_size = 0; + + rx_buf_size = instance_p->max_frame_size / FXMAC_RX_BUF_UNIT; + rx_buf_size += ((instance_p->max_frame_size % FXMAC_RX_BUF_UNIT) != 0) ? 1 : 0; /* roundup */ + + if (instance_p->moudle_id >= 2) + { + for (queue = 0; queue < config_p->max_queue_num; queue++) + { + dmacfg = 0; + FXmacSetQueuePtr(instance_p, (uintptr)NULL, queue, (u16)FXMAC_SEND); + FXmacSetQueuePtr(instance_p, (uintptr)NULL, queue, (u16)FXMAC_RECV); + + if (queue) + { + FXMAC_WRITEREG32(config_p->base_address, FXMAC_RXBUFQX_SIZE_OFFSET(queue), rx_buf_size); + } + else /* queue is 0 */ + { + dmacfg |= ((u32)FXMAC_DMACR_RXBUF_MASK & (rx_buf_size << FXMAC_DMACR_RXBUF_SHIFT)); + } + } + + dmacfg |= (config_p->dma_brust_length & FXMAC_DMACR_BLENGTH_MASK); + + dmacfg &= ~FXMAC_DMACR_ENDIAN_MASK; + dmacfg &= ~FXMAC_DMACR_SWAP_MANAGEMENT_MASK; /* 选择小端 */ + + dmacfg &= ~FXMAC_DMACR_TCPCKSUM_MASK; /* close transmitter checksum generation engine */ + + dmacfg &= ~FXMAC_DMACR_ADDR_WIDTH_64; + dmacfg |= FXMAC_DMACR_RXSIZE_MASK | FXMAC_DMACR_TXSIZE_MASK; +#if defined(__aarch64__) || defined(__arch64__) + dmacfg |= FXMAC_DMACR_ADDR_WIDTH_64; +#endif + } + else + { + FXmacSetQueuePtr(instance_p, (uintptr)NULL, 0, (u16)FXMAC_SEND); + FXmacSetQueuePtr(instance_p, (uintptr)NULL, 0, (u16)FXMAC_RECV); + dmacfg |= ((u32)FXMAC_DMACR_RXBUF_MASK & (rx_buf_size << FXMAC_DMACR_RXBUF_SHIFT)); + dmacfg |= (config_p->dma_brust_length & FXMAC_DMACR_BLENGTH_MASK); + + dmacfg &= ~FXMAC_DMACR_ENDIAN_MASK; + dmacfg &= ~FXMAC_DMACR_SWAP_MANAGEMENT_MASK; /* 选择小端 */ + + dmacfg &= ~FXMAC_DMACR_TCPCKSUM_MASK; /* close transmitter checksum generation engine */ + + dmacfg &= ~FXMAC_DMACR_ADDR_WIDTH_64; + dmacfg |= FXMAC_DMACR_RXSIZE_MASK | FXMAC_DMACR_TXSIZE_MASK; +#if defined(__aarch64__) || defined(__arch64__) + dmacfg |= FXMAC_DMACR_ADDR_WIDTH_64; +#endif + } + + FXMAC_WRITEREG32(config_p->base_address, FXMAC_DMACR_OFFSET, dmacfg); +} + + +/** + * Perform a graceful reset of the Ethernet MAC. Resets the DMA channels, the + * transmitter, and the receiver. + * + * Steps to reset + * - Stops transmit and receive channels + * - Stops DMA + * - Configure transmit and receive buffer size to default + * - Clear transmit and receive status register and counters + * - Clear all interrupt sources + * - Clear phy (if there is any previously detected) address + * - Clear MAC addresses (1-4) as well as Type IDs and hash value + * + * All options are placed in their default state. Any frames in the + * descriptor lists will remain in the lists. The side effect of doing + * this is that after a reset and following a restart of the device, frames + * were in the list before the reset may be transmitted or received. + * + * The upper layer software is responsible for re-configuring (if necessary) + * and restarting the MAC after the reset. Note also that driver statistics + * are not cleared on reset. It is up to the upper layer software to clear the + * statistics if needed. + * + * When a reset is required, the driver notifies the upper layer software of + * this need through the ErrorHandler callback and specific status codes. + * The upper layer software is responsible for calling this Reset function + * and then re-configuring the device. + * + * @param instance_p is a pointer to the instance to be worked on. + * + */ +static void FXmacReset(FXmac *instance_p) +{ + u32 reg_val, write_reg = 0; + u8 i; + s8 mac_addr[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; + u32 rx_buf_num; + + FASSERT(instance_p != NULL); + + /* Stop the device and reset hardware */ + FXmacStop(instance_p); + + instance_p->moudle_id = (FXMAC_READREG32(instance_p->config.base_address, FXMAC_REVISION_REG_OFFSET) & FXMAC_IDENTIFICATION_MASK) >> 16; + FXMAC_PRINT_I("instance_p->moudle_id is %d \r\n", instance_p->moudle_id); + instance_p->max_mtu_size = FXMAC_MTU; + instance_p->max_frame_size = FXMAC_MTU + FXMAC_HDR_SIZE + FXMAC_TRL_SIZE; + + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_NWCTRL_OFFSET, + ((FXMAC_NWCTRL_STATCLR_MASK) & (u32)(~FXMAC_NWCTRL_LOOPEN_MASK)) | FXMAC_NWCTRL_MDEN_MASK); + + write_reg = FXmacClkDivGet(instance_p); /* mdio clock division */ + write_reg |= FXmacDmaWidth(instance_p); /* 位宽 */ + + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_NWCFG_OFFSET, write_reg); + + FXmacDmaReset(instance_p); + + /* This register, when read provides details of the status of the receive path. */ + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_RXSR_OFFSET, FXMAC_SR_ALL_MASK); + + /* write 1 ro the relavant bit location disable that particular interrupt */ + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_IDR_OFFSET, FXMAC_IXR_ALL_MASK); + + reg_val = FXMAC_READREG32(instance_p->config.base_address, + FXMAC_ISR_OFFSET); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_ISR_OFFSET, + reg_val); + + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_TXSR_OFFSET, FXMAC_SR_ALL_MASK); + + FXmacClearHash(instance_p); + + /* set default mac address */ + for (i = 0U; i < 4U; i++) + { + (void)FXmacSetMacAddress(instance_p, mac_addr, i); + (void)FXmacGetMacAddress(instance_p, mac_addr, i); + (void)FXmacSetTypeIdCheck(instance_p, 0x00000000U, i); + } + + /* clear all counters */ + for (i = 0U; i < (u8)((FXMAC_LAST_OFFSET - FXMAC_OCTTXL_OFFSET) / 4U); + i++) + { + (void)FXMAC_READREG32(instance_p->config.base_address, + FXMAC_OCTTXL_OFFSET + (u32)(((u32)i) * ((u32)4))); + } + + /* Sync default options with hardware but leave receiver and + * transmitter disabled. They get enabled with FXmacStart() if + * FXMAC_TRANSMITTER_ENABLE_OPTION and + * FXMAC_RECEIVER_ENABLE_OPTION are set. + */ + FXmacSetOptions(instance_p, instance_p->config.network_default_config & ~((u32)FXMAC_TRANSMITTER_ENABLE_OPTION | (u32)FXMAC_RECEIVER_ENABLE_OPTION), 0); + FXmacClearOptions(instance_p, ~instance_p->config.network_default_config, 0); +} + +/** + * @name: FXmacInitInterface + * @msg: Initialize the MAC controller configuration based on the PHY interface type + * @note: + * @param {FXmac} *instance_p is a pointer to the instance to be worked on. + */ +void FXmacInitInterface(FXmac *instance_p) +{ + u32 config, control; + FXmacConfig *config_p; + config_p = &instance_p->config; + + if (config_p->interface == FXMAC_PHY_INTERFACE_MODE_XGMII) + { + config = FXMAC_READREG32(config_p->base_address, FXMAC_NWCFG_OFFSET); + config &= ~FXMAC_NWCFG_PCSSEL_MASK; + FXMAC_WRITEREG32(config_p->base_address, FXMAC_NWCFG_OFFSET, config); + + control = FXMAC_READREG32(config_p->base_address, FXMAC_NWCTRL_OFFSET); + control |= FXMAC_NWCTRL_ENABLE_HS_MAC_MASK; /* Use high speed MAC */ + FXMAC_WRITEREG32(config_p->base_address, FXMAC_NWCTRL_OFFSET, control); + + config_p->duplex = 1; + } + else if (config_p->interface == FXMAC_PHY_INTERFACE_MODE_USXGMII) + { + config = FXMAC_READREG32(config_p->base_address, FXMAC_NWCFG_OFFSET); + config |= FXMAC_NWCFG_PCSSEL_MASK; + FXMAC_WRITEREG32(config_p->base_address, FXMAC_NWCFG_OFFSET, config); + + control = FXMAC_READREG32(config_p->base_address, FXMAC_NWCTRL_OFFSET); + control |= FXMAC_NWCTRL_ENABLE_HS_MAC_MASK; /* Use high speed MAC */ + FXMAC_WRITEREG32(config_p->base_address, FXMAC_NWCTRL_OFFSET, control); + + control = FXMAC_READREG32(config_p->base_address, FXMAC_GEM_USX_CONTROL_OFFSET); + control &= ~(FXMAC_GEM_USX_TX_SCR_BYPASS | FXMAC_GEM_USX_RX_SCR_BYPASS); + control |= FXMAC_GEM_USX_RX_SYNC_RESET; + FXMAC_WRITEREG32(config_p->base_address, FXMAC_GEM_USX_CONTROL_OFFSET, control); + + control = FXMAC_READREG32(config_p->base_address, FXMAC_GEM_USX_CONTROL_OFFSET); + control &= ~FXMAC_GEM_USX_RX_SYNC_RESET; + control |= FXMAC_GEM_USX_TX_DATAPATH_EN; + control |= FXMAC_GEM_USX_SIGNAL_OK; + + if (config_p->speed == FXMAC_SPEED_10000) + { + control |= FXMAC_GEM_USX_HS_MAC_SPEED_10G; + } + else if (config_p->speed == FXMAC_SPEED_25000) + { + control |= FXMAC_GEM_USX_HS_MAC_SPEED_2_5G; + } + else if (config_p->speed == FXMAC_SPEED_1000) + { + control |= FXMAC_GEM_USX_HS_MAC_SPEED_1G; + } + else if (config_p->speed == FXMAC_SPEED_100) + { + control |= FXMAC_GEM_USX_HS_MAC_SPEED_100M; + } + + FXMAC_WRITEREG32(config_p->base_address, FXMAC_GEM_USX_CONTROL_OFFSET, control); + config_p->duplex = 1; + } + else if (config_p->interface == FXMAC_PHY_INTERFACE_MODE_SGMII) + { + config = FXMAC_READREG32(config_p->base_address, FXMAC_NWCFG_OFFSET); + config |= FXMAC_NWCFG_PCSSEL_MASK | FXMAC_NWCFG_SGMII_MODE_ENABLE_MASK; + + config &= ~(FXMAC_NWCFG_100_MASK | FXMAC_NWCFG_FDEN_MASK); + + if (instance_p->moudle_id >= 2) + { + config &= ~FXMAC_NWCFG_1000_MASK; + } + + if (config_p->duplex) + { + config |= FXMAC_NWCFG_FDEN_MASK; + } + + if (config_p->speed == FXMAC_SPEED_100) + { + config |= FXMAC_NWCFG_100_MASK; + } + else if (config_p->speed == FXMAC_SPEED_1000) + { + config |= FXMAC_NWCFG_1000_MASK; + } + + FXMAC_WRITEREG32(config_p->base_address, FXMAC_NWCFG_OFFSET, config); + + if (config_p->speed == FXMAC_SPEED_2500) + { + control = FXMAC_READREG32(config_p->base_address, FXMAC_NWCTRL_OFFSET); + control |= FXMAC_NWCTRL_TWO_PT_FIVE_GIG_MASK; + FXMAC_WRITEREG32(config_p->base_address, FXMAC_NWCTRL_OFFSET, control); + } + else + { + control = FXMAC_READREG32(config_p->base_address, FXMAC_NWCTRL_OFFSET); + control &= ~FXMAC_NWCTRL_TWO_PT_FIVE_GIG_MASK; + FXMAC_WRITEREG32(config_p->base_address, FXMAC_NWCTRL_OFFSET, control); + } + + control = FXMAC_READREG32(config_p->base_address, FXMAC_NWCTRL_OFFSET); + control &= ~FXMAC_NWCTRL_ENABLE_HS_MAC_MASK; + FXMAC_WRITEREG32(config_p->base_address, FXMAC_NWCTRL_OFFSET, control); + + control = FXMAC_READREG32(config_p->base_address, FXMAC_PCS_CONTROL_OFFSET); + control |= FXMAC_PCS_CONTROL_ENABLE_AUTO_NEG; + FXMAC_WRITEREG32(config_p->base_address, FXMAC_PCS_CONTROL_OFFSET, control); + } + else + { + config = FXMAC_READREG32(config_p->base_address, FXMAC_NWCFG_OFFSET); + + FXMAC_PRINT_I("select rgmii \r\n"); + + config &= ~FXMAC_NWCFG_PCSSEL_MASK; + config &= ~(FXMAC_NWCFG_100_MASK | FXMAC_NWCFG_FDEN_MASK); + + if (instance_p->moudle_id >= 2) + { + config &= ~FXMAC_NWCFG_1000_MASK; + } + + if (config_p->duplex) + { + config |= FXMAC_NWCFG_FDEN_MASK; + } + + if (config_p->speed == FXMAC_SPEED_100) + { + config |= FXMAC_NWCFG_100_MASK; + } + else if (config_p->speed == FXMAC_SPEED_1000) + { + config |= FXMAC_NWCFG_1000_MASK; + } + + if (config_p->duplex) + { + config |= FXMAC_NWCFG_FDEN_MASK; + } + + FXMAC_WRITEREG32(config_p->base_address, FXMAC_NWCFG_OFFSET, config); + + control = FXMAC_READREG32(config_p->base_address, FXMAC_NWCTRL_OFFSET); + control &= ~FXMAC_NWCTRL_ENABLE_HS_MAC_MASK; /* Use high speed MAC */ + FXMAC_WRITEREG32(config_p->base_address, FXMAC_NWCTRL_OFFSET, control); + } +} + + +static void FXmacIrqStubHandler(void) +{ + FASSERT_MSG(0, "Please register the interrupt callback function"); +} + +/** + * @name: FXmacCfgInitialize + * @msg: Initialize a specific fxmac instance/driver. + * @note: + * @param {FXmac} *instance_p is a pointer to the instance to be worked on. + * @param {FXmacConfig} *config_p is the device configuration structure containing required +* hardware build data. + * @return {FT_SUCCESS} if initialization was successful + */ +FError FXmacCfgInitialize(FXmac *instance_p, const FXmacConfig *config_p) +{ + /* Verify arguments */ + FASSERT(instance_p != NULL); + FASSERT(config_p != NULL); + + instance_p->config = *config_p; + instance_p->link_status = FXMAC_LINKDOWN; + /* Reset the hardware and set default options */ + instance_p->is_ready = FT_COMPONENT_IS_READY; + FXmacReset(instance_p); + + instance_p->send_irq_handler = (FXmacIrqHandler)FXmacIrqStubHandler; + instance_p->send_args = NULL; + + instance_p->recv_irq_handler = (FXmacIrqHandler)FXmacIrqStubHandler; + instance_p->recv_args = NULL; + + instance_p->error_irq_handler = (FXmacErrorIrqHandler)FXmacIrqStubHandler; + instance_p->error_args = NULL; + + instance_p->link_change_handler = (FXmacIrqHandler)FXmacIrqStubHandler; + instance_p->link_change_args = NULL; + + instance_p->restart_handler = (FXmacIrqHandler)FXmacIrqStubHandler; + instance_p->restart_args = NULL; + + return FT_SUCCESS; +} + + +/** + * This function sets the start address of the transmit/receive buffer queue. + * + * @param instance_p is a pointer to the instance to be worked on. + * @param queue_p is the address of the Queue to be written + * @param queue_num is the Buffer Queue Index + * @param direction indicates Transmit/Receive + * + * @note + * The buffer queue addresses has to be set before starting the transfer, so + * this function has to be called in prior to FXmacStart() + * + */ +void FXmacSetQueuePtr(FXmac *instance_p, uintptr queue_p, u8 queue_num, + u32 direction) +{ + /* Assert bad arguments and conditions */ + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == (u32)FT_COMPONENT_IS_READY); + + /* If already started, then there is nothing to do */ + if (instance_p->is_started == (u32)FT_COMPONENT_IS_STARTED) + { + return; + } + + if (queue_num == 0x00U) + { + if (direction == FXMAC_SEND) + { + /* set base start address of TX buffer queue (tx buffer descriptor list) */ + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_TXQBASE_OFFSET, + (queue_p & ULONG64_LO_MASK) | (((queue_p == (uintptr)0)) ? 1 : 0)); + } + else + { + /* set base start address of RX buffer queue (rx buffer descriptor list) */ + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_RXQBASE_OFFSET, + (queue_p & ULONG64_LO_MASK) | (((queue_p == (uintptr)0)) ? 1 : 0)); + } + } + else + { + if (direction == FXMAC_SEND) + { + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_TXQ1BASE_OFFSET, queue_num), + (queue_p & ULONG64_LO_MASK) | (((queue_p == (uintptr)0)) ? 1 : 0)); + } + else + { + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_TXQ1BASE_OFFSET, queue_num), + (queue_p & ULONG64_LO_MASK) | (((queue_p == (uintptr)0)) ? 1 : 0)); + } + } +#ifdef __aarch64__ + if (direction == FXMAC_SEND) + { + /* Set the MSB of TX Queue start address */ + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_MSBBUF_TXQBASE_OFFSET, + (u32)((queue_p & ULONG64_HI_MASK) >> 32U)); + } + else + { + /* Set the MSB of RX Queue start address */ + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_MSBBUF_RXQBASE_OFFSET, + (u32)((queue_p & ULONG64_HI_MASK) >> 32U)); + } +#endif +} diff --git a/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac.h b/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac.h new file mode 100644 index 0000000000..19678d929c --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac.h @@ -0,0 +1,332 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fxmac.h + * Date: 2022-04-06 14:46:52 + * LastEditTime: 2022-04-06 14:46:58 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef DRIVERS_ETH_F_XMAC_H +#define DRIVERS_ETH_F_XMAC_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ftypes.h" +#include "fassert.h" +#include "ferror_code.h" + +#include "fxmac_hw.h" +#include "fxmac_bdring.h" +#include "fparameters.h" + +#define FXMAC_ERR_INVALID_PARAM FT_MAKE_ERRCODE(ErrModBsp, ErrBspEth, 0x1u) +#define FXMAC_ERR_SG_LIST FT_MAKE_ERRCODE(ErrModBsp, ErrBspEth, 0x2u) +#define FXMAC_ERR_GENERAL FT_MAKE_ERRCODE(ErrModBsp, ErrBspEth, 0x4u) +#define FXMAC_ERR_SG_NO_LIST FT_MAKE_ERRCODE(ErrModBsp, ErrBspEth, 0x5u) +#define FXMAC_ERR_PHY_BUSY FT_MAKE_ERRCODE(ErrModBsp, ErrBspEth, 0x6u) +#define FXMAC_PHY_IS_NOT_FOUND FT_MAKE_ERRCODE(ErrModBsp, ErrBspEth, 0x7u) +#define FXMAC_PHY_AUTO_AUTONEGOTIATION_FAILED FT_MAKE_ERRCODE(ErrModBsp, ErrBspEth, 0x8u) +#define FXMAC_ERR_MAC_IS_PROCESSING FT_MAKE_ERRCODE(ErrModBsp, ErrBspEth, 0x9u) + +/** @name Configuration options + * + * Device configuration options. See the FXMAC_SetOptions(), + * FXMACClearOptions() and FXMAC_GetOptions() for information on how to + * use options. + * + * The default state of the options are noted and are what the device and + * driver will be set to after calling FXMAC_Reset() or + * FXMAC_Initialize(). + * + * @{ + */ + +#define FXMAC_PROMISC_OPTION 0x00000001U +/* Accept all incoming packets. + * This option defaults to disabled (cleared) */ + +#define FXMAC_FRAME1536_OPTION 0x00000002U +/* Frame larger than 1516 support for Tx & Rx.x + * This option defaults to disabled (cleared) */ + +#define FXMAC_VLAN_OPTION 0x00000004U +/* VLAN Rx & Tx frame support. + * This option defaults to disabled (cleared) */ + +#define FXMAC_FLOW_CONTROL_OPTION 0x00000010U +/* Enable recognition of flow control frames on Rx + * This option defaults to enabled (set) */ + +#define FXMAC_FCS_STRIP_OPTION 0x00000020U +/* Strip FCS and PAD from incoming frames. Note: PAD from VLAN frames is not + * stripped. + * This option defaults to enabled (set) */ + +#define FXMAC_FCS_INSERT_OPTION 0x00000040U +/* Generate FCS field and add PAD automatically for outgoing frames. + * This option defaults to disabled (cleared) */ + +#define FXMAC_LENTYPE_ERR_OPTION 0x00000080U +/* Enable Length/Type error checking for incoming frames. When this option is + * set, the MAC will filter frames that have a mismatched type/length field + * and if FXMAC_REPORT_RXERR_OPTION is set, the user is notified when these + * types of frames are encountered. When this option is cleared, the MAC will + * allow these types of frames to be received. + * + * This option defaults to disabled (cleared) */ + +#define FXMAC_TRANSMITTER_ENABLE_OPTION 0x00000100U +/* Enable the transmitter. + * This option defaults to enabled (set) */ + +#define FXMAC_RECEIVER_ENABLE_OPTION 0x00000200U +/* Enable the receiver + * This option defaults to enabled (set) */ + +#define FXMAC_BROADCAST_OPTION 0x00000400U +/* Allow reception of the broadcast address + * This option defaults to enabled (set) */ + +#define FXMAC_MULTICAST_OPTION 0x00000800U +/* Allows reception of multicast addresses programmed into hash + * This option defaults to disabled (clear) */ + +#define FXMAC_RX_CHKSUM_ENABLE_OPTION 0x00001000U +/* Enable the RX checksum offload + * This option defaults to enabled (set) */ + +#define FXMAC_TX_CHKSUM_ENABLE_OPTION 0x00002000U +/* Enable the TX checksum offload + * This option defaults to enabled (set) */ + +#define FXMAC_JUMBO_ENABLE_OPTION 0x00004000U +#define FXMAC_SGMII_ENABLE_OPTION 0x00008000U + +#define FXMAC_LOOPBACK_NO_MII_OPTION 0x00010000U +#define FXMAC_LOOPBACK_USXGMII_OPTION 0x00020000U + +#define FXMAC_GET_TXRING(instance) (instance.tx_bd_queue.bdring) +#define FXMAC_GET_RXRING(instance) (instance.rx_bd_queue.bdring) + +#define FXMAC_DEFAULT_OPTIONS \ + ((u32)FXMAC_FLOW_CONTROL_OPTION | \ + (u32)FXMAC_FCS_INSERT_OPTION | \ + (u32)FXMAC_FCS_STRIP_OPTION | \ + (u32)FXMAC_BROADCAST_OPTION | \ + (u32)FXMAC_LENTYPE_ERR_OPTION | \ + (u32)FXMAC_TRANSMITTER_ENABLE_OPTION | \ + (u32)FXMAC_RECEIVER_ENABLE_OPTION | \ + (u32)FXMAC_RX_CHKSUM_ENABLE_OPTION | \ + (u32)FXMAC_TX_CHKSUM_ENABLE_OPTION) + +typedef enum +{ + FXMAC_LINKDOWN = 0, + FXMAC_LINKUP = 1, + FXMAC_NEGOTIATING = 2 +} FXmacLinkStatus; + +/* The next few constants help upper layers determine the size of memory + * pools used for Ethernet buffers and descriptor lists. + */ +#define FXMAC_MAC_ADDR_SIZE 6U /* size of Ethernet header */ + +#define FXMAC_MTU 1500U /* max MTU size of Ethernet frame */ +#define FXMAC_MTU_JUMBO 10240U /* max MTU size of jumbo frame */ +#define FXMAC_HDR_SIZE 14U /* size of Ethernet header , DA + SA + TYPE*/ +#define FXMAC_HDR_VLAN_SIZE 18U /* size of Ethernet header with VLAN */ +#define FXMAC_TRL_SIZE 4U /* size of Ethernet trailer (FCS) */ +#define FXMAC_MAX_FRAME_SIZE (FXMAC_MTU + FXMAC_HDR_SIZE + \ + FXMAC_TRL_SIZE) +#define FXMAC_MAX_VLAN_FRAME_SIZE (FXMAC_MTU + FXMAC_HDR_SIZE + \ + FXMAC_HDR_VLAN_SIZE + FXMAC_TRL_SIZE) +#define FXMAC_MAX_VLAN_FRAME_SIZE_JUMBO (FXMAC_MTU_JUMBO + FXMAC_HDR_SIZE + \ + FXMAC_HDR_VLAN_SIZE + FXMAC_TRL_SIZE) + +#define FXMAC_MAX_FRAME_SIZE_JUMBO (FXMAC_MTU_JUMBO + FXMAC_HDR_SIZE + FXMAC_TRL_SIZE) + +/** @name Callback identifiers + * + * These constants are used as parameters to FXMAC_SetHandler() + * @{ + */ +#define FXMAC_HANDLER_DMASEND 1U /* 发送中断 */ +#define FXMAC_HANDLER_DMARECV 2U /* 接收中断 */ +#define FXMAC_HANDLER_ERROR 3U /* 异常中断 */ +#define FXMAC_HANDLER_LINKCHANGE 4U /* 连接状态 */ +#define FXMAC_HANDLER_RESTART 5U /* 发送描述符队列发生异常 */ +/*@}*/ + +#define FXMAC_DMA_SG_IS_STARTED 0 +#define FXMAC_DMA_SG_IS_STOPED 1 + +#define FXMAC_SPEED_10 10U +#define FXMAC_SPEED_100 100U +#define FXMAC_SPEED_1000 1000U +#define FXMAC_SPEED_2500 2500U +#define FXMAC_SPEED_5000 5000U +#define FXMAC_SPEED_10000 10000U +#define FXMAC_SPEED_25000 25000U + +/** @name Direction identifiers + * + * These are used by several functions and callbacks that need + * to specify whether an operation specifies a send or receive channel. + * @{ + */ +#define FXMAC_SEND 1U /* send direction */ +#define FXMAC_RECV 2U /* receive direction */ + +/****************************************************************************/ +/** + * + * This macro triggers trasmit circuit to send data currently in TX buffer(s). + * + * @param instance_p is a pointer to the FXmac instance to be worked on. + * + * @return + * + * @note + * + * Signature: void FXmacTransmit(FXmac *instance_p) + * + *****************************************************************************/ +#define FXmacTransmit(instance_p) \ + FXMAC_WRITEREG32((instance_p)->config.base_address, \ + FXMAC_NWCTRL_OFFSET, \ + (FXMAC_READREG32((instance_p)->config.base_address, \ + FXMAC_NWCTRL_OFFSET) | \ + FXMAC_NWCTRL_STARTTX_MASK)) + +typedef void (*FXmacIrqHandler)(void *args); +typedef void (*FXmacErrorIrqHandler)(void *args, u32 direction, u32 error_word); +/* Interface Mode definitions */ +typedef enum +{ + FXMAC_PHY_INTERFACE_MODE_SGMII, + FXMAC_PHY_INTERFACE_MODE_RMII, + FXMAC_PHY_INTERFACE_MODE_RGMII, + FXMAC_PHY_INTERFACE_MODE_XGMII, + FXMAC_PHY_INTERFACE_MODE_USXGMII, +} FXmacPhyInterface; + +typedef struct +{ + u32 instance_id; /* Id of device*/ + volatile uintptr_t base_address; + volatile uintptr_t extral_mode_base; + volatile uintptr_t extral_loopback_base; + FXmacPhyInterface interface; + u32 speed; /* FXMAC_SPEED_XXX */ + u32 duplex; /* 1 is full-duplex , 0 is half-duplex */ + u32 auto_neg; /* Enable auto-negotiation - when set active high, autonegotiation operation is enabled. */ + u32 pclk_hz; + u32 max_queue_num; /* Number of Xmac Controller Queues */ + u32 tx_queue_id; /* 0 ~ FT_XMAC_QUEUE_MAX_NUM ,Index queue number */ + u32 rx_queue_id; /* 0 ~ FT_XMAC_QUEUE_MAX_NUM ,Index queue number */ + u32 hotplug_irq_num; + u32 dma_brust_length; /* burst length */ + u32 network_default_config; + u32 queue_irq_num[FT_XMAC_QUEUE_MAX_NUM]; /* mac0 8个 ,其他的 4个 */ +} FXmacConfig; + +typedef struct +{ + u32 queue_id; + FXmacBdRing bdring; +} FXmacQueue; + +typedef struct +{ + FXmacConfig config; + u32 is_ready; /* Device is ininitialized and ready*/ + u32 is_started; + u32 link_status; /* indicates link status ,FXMAC_LINKUP is link up ,FXMAC_LINKDOWN is link down,FXMAC_NEGOTIATING is need to negotiating*/ + u32 options; + + FXmacQueue tx_bd_queue; /* Transmit Queue */ + FXmacQueue rx_bd_queue; /* Receive Queue */ + + FXmacIrqHandler send_irq_handler; + void *send_args; + + FXmacIrqHandler recv_irq_handler; + void *recv_args; + + FXmacErrorIrqHandler error_irq_handler; + void *error_args; + + FXmacIrqHandler link_change_handler; + void *link_change_args; + + FXmacIrqHandler restart_handler; + void *restart_args; + + u32 moudle_id; /* Module identification number */ + u32 max_mtu_size; + u32 max_frame_size; + + u32 phy_address; /* phy address */ + u32 rxbuf_mask; /* 1000,100,10 */ + +} FXmac; + +/* fxmac_sinit.c */ +const FXmacConfig *FXmacLookupConfig(u32 instance_id); + +/* fgmac.c */ +FError FXmacCfgInitialize(FXmac *instance_p, const FXmacConfig *config_p); + +void FXmacInitInterface(FXmac *instance_p); + +void FXmacGetMacAddress(FXmac *instance_p, u8 *address_ptr, u8 index); +FError FXmacSetMacAddress(FXmac *instance_p, u8 *address_ptr, u8 index); + +FError FXmacSetOptions(FXmac *instance_p, u32 options, u32 queue_num); +FError FXmacClearOptions(FXmac *instance_p, u32 options, u32 queue_num); + +void FXmacStart(FXmac *instance_p); +void FXmacStop(FXmac *instance_p); +void FXmacSetQueuePtr(FXmac *instance_p, uintptr QPtr, u8 QueueNum, + u32 direction); + +/* phy interface */ +FError FXmacPhyWrite(FXmac *instance_p, u32 phy_address, + u32 register_num, u16 phy_data); + +FError FXmacPhyRead(FXmac *instance_p, u32 phy_address, + u32 register_num, u16 *phydat_aptr); + +FError FXmacPhyInit(FXmac *instance_p, u32 speed, u32 duplex_mode, u32 autonegotiation_en); + +void FXmacSelectClk(FXmac *instance_p); + +FError FXmacSetHandler(FXmac *instance_p, u32 handler_type, void *func_pointer, void *call_back_ref); +/* interrupt */ +void FXmacIntrHandler(s32 vector, void *args); + +void FXmacClearHash(FXmac *instance_p); + +#ifdef __cplusplus +} +#endif + +#endif // ! diff --git a/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_bd.h b/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_bd.h new file mode 100644 index 0000000000..bbe982340d --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_bd.h @@ -0,0 +1,272 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fxmac_bd.h + * Date: 2022-04-06 14:46:52 + * LastEditTime: 2022-04-06 14:46:58 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef DRIVERS_ETH_F_XMAC_BD_H +#define DRIVERS_ETH_F_XMAC_BD_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ftypes.h" +#include "string.h" + + +/** + * @name: FXMAC_BD_READ + * @msg: Read the given Buffer Descriptor word. + * @param bd_ptr is the base address of the BD to read + * @param offset is the word offset to be read + * @return The 32-bit value of the field + */ +#define FXMAC_BD_READ(bd_ptr, offset) \ + (*(u32 *)((uintptr)((void *)(bd_ptr)) + (u32)(offset))) + + +/** + * @name: FXMAC_BD_WRITE + * @msg: Write the given Buffer Descriptor word. + * @param bd_ptr is the base address of the BD to write + * @param Offset is the word offset to be written + * @param data is the 32-bit value to write to the field + * @return {*} + */ +#define FXMAC_BD_WRITE(bd_ptr, Offset, data) \ + (*(u32 *)((uintptr)(void *)(bd_ptr) + (u32)(Offset)) = (u32)(data)) + + +/** + * @name: FXMAC_BD_SET_STATUS + * @msg: Set the BD's Status field (word 1). + * @param bd_ptr is the BD pointer to operate on + * @param data is the value to write to BD's status field. + */ +#define FXMAC_BD_SET_STATUS(bd_ptr, data) \ + FXMAC_BD_WRITE((bd_ptr), FXMAC_BD_STAT_OFFSET, \ + FXMAC_BD_READ((bd_ptr), FXMAC_BD_STAT_OFFSET) | (data)) + + +/** + * @name: FXMAC_BD_IS_RX_NEW + * @msg: Determine the new bit of the receive BD. + * @param bd_ptr is the BD pointer to operate on + */ +#define FXMAC_BD_IS_RX_NEW(bd_ptr) \ + ((FXMAC_BD_READ((bd_ptr), FXMAC_BD_ADDR_OFFSET) & \ + FXMAC_RXBUF_NEW_MASK) != 0U \ + ? TRUE \ + : FALSE) + + +/** + * @name: FXMAC_BD_IS_TX_WRAP + * @msg: Determine the wrap bit of the transmit BD which indicates end of the + * BD list. + * @param bd_ptr is the BD pointer to operate on + */ +#define FXMAC_BD_IS_TX_WRAP(bd_ptr) \ + ((FXMAC_BD_READ((bd_ptr), FXMAC_BD_STAT_OFFSET) & \ + FXMAC_TXBUF_WRAP_MASK) != 0U \ + ? TRUE \ + : FALSE) + + +/** + * @name: FXMAC_BD_IS_RX_WRAP + * @msg: Determine the wrap bit of the receive BD which indicates end of the + * BD list. + * @param: bd_ptr is the BD pointer to operate on + */ +#define FXMAC_BD_IS_RX_WRAP(bd_ptr) \ + ((FXMAC_BD_READ((bd_ptr), FXMAC_BD_ADDR_OFFSET) & \ + FXMAC_RXBUF_WRAP_MASK) != 0U \ + ? TRUE \ + : FALSE) + + + +/** + * @name: FXMAC_BD_SET_ADDRESS_TX + * @msg: Set the BD's address field (word 0). + * @param: bd_ptr is the BD pointer to operate on + * @param: addr is the value to write to BD's status field. + */ +#if defined(__aarch64__) || defined(__arch64__) +#define FXMAC_BD_SET_ADDRESS_TX(bd_ptr, addr) \ + FXMAC_BD_WRITE((bd_ptr), FXMAC_BD_ADDR_OFFSET, \ + (u32)((addr)&ULONG64_LO_MASK)); \ + FXMAC_BD_WRITE((bd_ptr), FXMAC_BD_ADDR_HI_OFFSET, \ + (u32)(((addr)&ULONG64_HI_MASK) >> 32U)); +#else +#define FXMAC_BD_SET_ADDRESS_TX(bd_ptr, addr) \ + FXMAC_BD_WRITE((bd_ptr), FXMAC_BD_ADDR_OFFSET, (u32)(addr)) +#endif + + +/** + * @name: FXMAC_BD_SET_ADDRESS_RX + * @msg: Set the BD's address field (word 0). + * @param: bd_ptr is the BD pointer to operate on + * @param: addr is the value to write to BD's status field. + * @return {*} + */ +#ifdef __aarch64__ +#define FXMAC_BD_SET_ADDRESS_RX(bd_ptr, addr) \ + FXMAC_BD_WRITE((bd_ptr), FXMAC_BD_ADDR_OFFSET, \ + ((FXMAC_BD_READ((bd_ptr), FXMAC_BD_ADDR_OFFSET) & \ + ~FXMAC_RXBUF_ADD_MASK) | \ + ((u32)((addr)&ULONG64_LO_MASK)))); \ + FXMAC_BD_WRITE((bd_ptr), FXMAC_BD_ADDR_HI_OFFSET, \ + (u32)(((addr)&ULONG64_HI_MASK) >> 32U)); +#else +#define FXMAC_BD_SET_ADDRESS_RX(bd_ptr, addr) \ + FXMAC_BD_WRITE((bd_ptr), FXMAC_BD_ADDR_OFFSET, \ + ((FXMAC_BD_READ((bd_ptr), FXMAC_BD_ADDR_OFFSET) & \ + ~FXMAC_RXBUF_ADD_MASK) | \ + (u32)(addr))) +#endif + + +/** + * @name: FXMAC_BD_SET_LENGTH + * @msg: Set transfer length in bytes for the given BD. The length must be set each + * time a BD is submitted to hardware. + * @param: bd_ptr is the BD pointer to operate on + * @param: len_bytes is the number of bytes to transfer. + * @return {*} + */ +#define FXMAC_BD_SET_LENGTH(bd_ptr, len_bytes) \ + FXMAC_BD_WRITE((bd_ptr), FXMAC_BD_STAT_OFFSET, \ + ((FXMAC_BD_READ((bd_ptr), FXMAC_BD_STAT_OFFSET) & \ + ~FXMAC_TXBUF_LEN_MASK) | \ + (len_bytes))) + + +/** + * @name: FXMAC_BD_GET_LENGTH + * @msg: For Tx channels, the returned value is the same as that written with + * FXMAC_BD_SET_LENGTH(). For Rx channels, the returned value is the size of the received packet. + * @param: bd_ptr is the BD pointer to operate on + * @return {*} + */ +#define FXMAC_BD_GET_LENGTH(bd_ptr) \ + (FXMAC_BD_READ((bd_ptr), FXMAC_BD_STAT_OFFSET) & \ + FXMAC_RXBUF_LEN_MASK) + + +/** + * @name: FXMAC_GET_RX_FRAME_SIZE + * @msg: The returned value is the size of the received packet. + * This API supports jumbo frame sizes if enabled. + * @param instance_p is the pointer to xmac instance + * @param bd_ptr is the BD pointer to operate on + * + * @return Length field processed by hardware or set by + * FXMAC_BD_SET_LENGTH(). + */ + +#define FXMAC_BD_JUMBO_LENGTH_MASK + +#define FXMAC_GET_RX_FRAME_SIZE(instance_p, bd_ptr) \ + (FXMAC_BD_READ((bd_ptr), FXMAC_BD_STAT_OFFSET) & \ + 0x00003FFFU) + + + +/** + * @name: FXMAC_BD_CLEAR_TX_USED + * @msg: Software clears this bit to enable the buffer to be read by the hardware. + * Hardware sets this bit for the first buffer of a frame once it has been + * successfully transmitted. This macro clears this bit of transmit BD. + * @param: bd_ptr is the BD pointer to operate on + * @return {*} + */ +#define FXMAC_BD_CLEAR_TX_USED(bd_ptr) \ + (FXMAC_BD_WRITE((bd_ptr), FXMAC_BD_STAT_OFFSET, \ + FXMAC_BD_READ((bd_ptr), FXMAC_BD_STAT_OFFSET) & \ + (~FXMAC_TXBUF_USED_MASK))) + +#define FXMAC_BD_SET_CRC(bd_ptr) \ + (FXMAC_BD_WRITE((bd_ptr), FXMAC_BD_STAT_OFFSET, \ + FXMAC_BD_READ((bd_ptr), FXMAC_BD_STAT_OFFSET) & \ + (~FXMAC_TXBUF_NOCRC_MASK))) + + +/** + * @name: FXMAC_BD_SET_LAST + * @msg: Tell the DMA engine that the given transmit BD marks the end of the current + * packet to be processed. + * @param bd_ptr is the BD pointer to operate on + * @return {*} + */ +#define FXMAC_BD_SET_LAST(bd_ptr) \ + (FXMAC_BD_WRITE((bd_ptr), FXMAC_BD_STAT_OFFSET, \ + FXMAC_BD_READ((bd_ptr), FXMAC_BD_STAT_OFFSET) | \ + FXMAC_TXBUF_LAST_MASK)) + + +/** + * @name: FXMAC_BD_CLEAR_LAST + * @msg: Tell the DMA engine that the current packet does not end with the given + * BD. + * @param bd_ptr is the BD pointer to operate on + * @return {*} + */ +#define FXMAC_BD_CLEAR_LAST(bd_ptr) \ + (FXMAC_BD_WRITE((bd_ptr), FXMAC_BD_STAT_OFFSET, \ + FXMAC_BD_READ((bd_ptr), FXMAC_BD_STAT_OFFSET) & \ + ~FXMAC_TXBUF_LAST_MASK)) + +/** + * @name: FXMAC_BD_CLEAR + * @msg: Zero out BD fields + * @param bd_ptr is the BD pointer to operate on + * @return {*} + */ +#define FXMAC_BD_CLEAR(bd_ptr) \ + memset((bd_ptr), 0, sizeof(FXmacBd)) + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ +#ifdef __aarch64__ +/* Minimum BD alignment */ +#define FXMAC_DMABD_MINIMUM_ALIGNMENT 64U +#define FXMAC_BD_NUM_WORDS 4U +#else +/* Minimum BD alignment */ +#define FXMAC_DMABD_MINIMUM_ALIGNMENT 4U +#define FXMAC_BD_NUM_WORDS 2U +#endif + +/** + * The FXMAC_Bd is the type for buffer descriptors (BDs). + */ +typedef u32 FXmacBd[FXMAC_BD_NUM_WORDS]; + +#ifdef __cplusplus +} +#endif + +#endif // ! diff --git a/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_bdring.c b/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_bdring.c new file mode 100644 index 0000000000..5459203c18 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_bdring.c @@ -0,0 +1,964 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fxmac_bdring.c + * Date: 2022-04-06 14:46:52 + * LastEditTime: 2022-04-06 14:46:58 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "fxmac_hw.h" +#include "fxmac.h" +#include "fxmac_bdring.h" +#include "fxmac_bd.h" +#include "ftypes.h" +#include "fxmac.h" +#include "string.h" +#include "fprintk.h" +#include "fdebug.h" + +static void FXmacBdSetRxWrap(uintptr bdptr); +static void FXmacBdSetTxWrap(uintptr bdptr); + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/** + * @name: FXMAC_RING_SEEKAHEAD + * @msg: Move the bdptr argument ahead an arbitrary number of BDs wrapping around + * to the beginning of the ring if needed. + * @param ring_ptr is the ring bdptr appears in + * @param bdptr on input is the starting BD position and on output is the final BD position + * @param num_bd is the number of BD spaces to increment + */ +#define FXMAC_RING_SEEKAHEAD(ring_ptr, bdptr, num_bd) \ + { \ + uintptr addr = (uintptr)(void *)(bdptr); \ + \ + addr += ((ring_ptr)->separation * (num_bd)); \ + if ((addr > (ring_ptr)->high_bd_addr) || ((uintptr)(void *)(bdptr) > addr)) \ + { \ + addr -= (ring_ptr)->length; \ + } \ + \ + (bdptr) = (FXmacBd *)(void *)addr; \ + } + +/** + * @name: FXMAC_RING_SEEKBACK + * @msg: Move the bdptr argument backwards an arbitrary number of BDs wrapping + * around to the end of the ring if needed. + * @param ring_ptr is the ring bdptr appears in + * @param bdptr on input is the starting BD position and on output is the + * final BD position + * @param num_bd is the number of BD spaces to increment + * @return {*} + */ +#define FXMAC_RING_SEEKBACK(ring_ptr, bdptr, num_bd) \ + { \ + uintptr addr = (uintptr)(void *)(bdptr); \ + \ + addr -= ((ring_ptr)->separation * (num_bd)); \ + if ((addr < (ring_ptr)->base_bd_addr) || ((uintptr)(void *)(bdptr) < addr)) \ + { \ + addr += (ring_ptr)->length; \ + } \ + \ + (bdptr) = (FXmacBd *)(void *)addr; \ + } + + + +/** + * @name: FXmacBdRingCreate + * @msg: Using a memory segment allocated by the caller, create and setup the BD list + * for the given DMA channel. + * @param ring_ptr is the instance to be worked on. + * @param Physaddr is the physical base address of user memory region. + * @param Virtaddr is the virtual base address of the user memory region. If + * address translation is not being utilized, then Virtaddr should be + * equivalent to Physaddr. + * @param Alignment governs the byte alignment of individual BDs. This function + * will enforce a minimum alignment of 4 bytes with no maximum as long + * as it is specified as a power of 2. + * @param bd_count is the number of BDs to setup in the user memory region. It + * is assumed the region is large enough to contain the BDs. + * @return + * FT_SUCCESS if initialization was successful + * FXMAC_ERR_INVALID_PARAM under any of the following conditions: + * 1) Physaddr and/or Virtaddr are not aligned to the given Alignment + * parameter. + * 2) Alignment parameter does not meet minimum requirements or is not a + * power of 2 value. + * 3) bd_count is 0. + */ +FError FXmacBdRingCreate(FXmacBdRing *ring_ptr, uintptr phys_addr, + uintptr virt_addr, u32 alignment, u32 bd_count) +{ + u32 i; + uintptr bd_virt_addr; + uintptr bd_phy_addr; + uintptr virt_addr_loc = virt_addr; + + /* In case there is a failure prior to creating list, make sure the + * following attributes are 0 to prevent calls to other functions + * from doing anything. + */ + ring_ptr->all_cnt = 0U; + ring_ptr->free_cnt = 0U; + ring_ptr->hw_cnt = 0U; + ring_ptr->pre_cnt = 0U; + ring_ptr->post_cnt = 0U; + + /* Make sure alignment parameter meets minimum requirements */ + if (alignment < (u32)FXMAC_DMABD_MINIMUM_ALIGNMENT) + { + return (FError)(FXMAC_ERR_INVALID_PARAM); + } + + /* Make sure alignment is a power of 2 */ + if (((alignment - 0x00000001U) & alignment) != 0x00000000U) + { + return (FError)(FXMAC_ERR_INVALID_PARAM); + } + + /* Make sure phys_addr and virt_addr are on same alignment */ + if (((phys_addr % alignment) != (u32)0) || ((virt_addr_loc % alignment) != (u32)0)) + { + return (FError)(FXMAC_ERR_INVALID_PARAM); + } + + /* Is bd_count reasonable? */ + if (bd_count == 0x00000000U) + { + return (FError)(FXMAC_ERR_INVALID_PARAM); + } + + /* Figure out how many bytes will be between the start of adjacent BDs */ + ring_ptr->separation = ((u32)sizeof(FXmacBd)); + + /* Must make sure the ring doesn't span address 0x00000000. If it does, + * then the next/prev BD traversal macros will fail. + */ + if (virt_addr_loc > ((virt_addr_loc + (ring_ptr->separation * bd_count)) - (u32)1)) + { + return (FError)(FXMAC_ERR_SG_LIST); + } + + /* Initial ring setup: + * - Clear the entire space + * - Setup each BD's BDA field with the physical address of the next BD + */ + (void)memset((void *)virt_addr_loc, 0, (ring_ptr->separation * bd_count)); + + bd_virt_addr = virt_addr_loc; + bd_phy_addr = phys_addr + ring_ptr->separation; + for (i = 1U; i < bd_count; i++) + { + bd_virt_addr += ring_ptr->separation; + bd_phy_addr += ring_ptr->separation; + } + + /* Setup and initialize pointers and counters */ + ring_ptr->run_state = (u32)(FXMAC_DMA_SG_IS_STOPED); + ring_ptr->base_bd_addr = virt_addr_loc; + ring_ptr->phys_base_addr = phys_addr; + ring_ptr->high_bd_addr = bd_virt_addr; + ring_ptr->length = + ((ring_ptr->high_bd_addr - ring_ptr->base_bd_addr) + ring_ptr->separation); + ring_ptr->all_cnt = (u32)bd_count; + ring_ptr->free_cnt = (u32)bd_count; + ring_ptr->free_head = (FXmacBd *)(void *)virt_addr_loc; + ring_ptr->pre_head = (FXmacBd *)virt_addr_loc; + ring_ptr->hw_head = (FXmacBd *)virt_addr_loc; + ring_ptr->hw_tail = (FXmacBd *)virt_addr_loc; + ring_ptr->post_head = (FXmacBd *)virt_addr_loc; + ring_ptr->bda_restart = (FXmacBd *)(void *)phys_addr; + + return (FError)(FT_SUCCESS); +} + +/** + * @name: FXmacBdRingClone + * @msg: Clone the given BD into every BD in the list. + * every field of the source BD is replicated in every BD of the list. + * @param ring_ptr is the instance to be worked on. + * @param src_bd_ptr is the source BD template to be cloned into the list. This + * BD will be modified. + * @param direction is either FXMAC_SEND or FXMAC_RECV that indicates + * which direction. + * @return {*} + */ +FError FXmacBdRingClone(FXmacBdRing *ring_ptr, FXmacBd *src_bd_ptr, + u8 direction) +{ + u32 i; + uintptr cur_bd; + + /* Can't do this function if there isn't a ring */ + if (ring_ptr->all_cnt == 0x00000000U) + { + return (FError)(FXMAC_ERR_SG_NO_LIST); + } + + /* Can't do this function with the channel running */ + if (ring_ptr->run_state == (u32)FXMAC_DMA_SG_IS_STARTED) + { + return (FError)(FT_COMPONENT_IS_STARTED); + } + + /* Can't do this function with some of the BDs in use */ + if (ring_ptr->free_cnt != ring_ptr->all_cnt) + { + return (FError)(FXMAC_ERR_SG_LIST); + } + + if ((direction != (u8)FXMAC_SEND) && (direction != (u8)FXMAC_RECV)) + { + return (FError)(FXMAC_ERR_INVALID_PARAM); + } + + /* Starting from the top of the ring, save bd.next, overwrite the entire + * BD with the template, then restore bd.next + */ + cur_bd = ring_ptr->base_bd_addr; + for (i = 0U; i < ring_ptr->all_cnt; i++) + { + memcpy((void *)cur_bd, src_bd_ptr, sizeof(FXmacBd)); + cur_bd += ring_ptr->separation; + } + + cur_bd -= ring_ptr->separation; + + if (direction == FXMAC_RECV) + { + FXmacBdSetRxWrap(cur_bd); + } + else + { + FXmacBdSetTxWrap(cur_bd); + } + + return (FError)(FT_SUCCESS); +} + + +/** + * @name: FXmacBdRingAlloc + * @msg: Reserve locations in the BD list. The set of returned BDs may be modified + * in preparation for future DMA transaction(s). Once the BDs are ready to be + * submitted to hardware, the user must call FXmacBdRingToHw() in the same + * order which they were allocated here. + * @param ring_ptr is a pointer to the BD ring instance to be worked on. + * @param num_bd is the number of BDs to allocate + * @param bd_set_ptr is an output parameter, it points to the first BD available + * for modification. + * @return FT_SUCCESS if the requested number of BDs was returned in the bd_set_ptr + * parameter. + * - FXMAC_ERR_GENERAL if there were not enough free BDs to satisfy the request. + */ +FError FXmacBdRingAlloc(FXmacBdRing *ring_ptr, u32 num_bd, + FXmacBd **bd_set_ptr) +{ + FError status; + /* Enough free BDs available for the request? */ + if (ring_ptr->free_cnt < num_bd) + { + status = (FError)(FXMAC_ERR_GENERAL); + } + else + { + /* Set the return argument and move free_head forward */ + *bd_set_ptr = ring_ptr->free_head; + FXMAC_RING_SEEKAHEAD(ring_ptr, ring_ptr->free_head, num_bd); + ring_ptr->free_cnt -= num_bd; + ring_ptr->pre_cnt += num_bd; + status = (FError)(FT_SUCCESS); + } + return status; +} + + +/** + * @name: FXmacBdRingUnAlloc + * @msg: * Fully or partially undo an FXmacBdRingAlloc() operation. Use this + * function if all the BDs allocated by FXmacBdRingAlloc() could not be + * transferred to hardware with FXmacBdRingToHw(). + * + * This function helps out in situations when an unrelated error occurs after + * BDs have been allocated but before they have been given to hardware. + * An example of this type of error would be an OS running out of resources. + * + * This function is not the same as FXmacBdRingFree(). The Free function + * returns BDs to the free list after they have been processed by hardware, + * while UnAlloc returns them before being processed by hardware. + * + * There are two scenarios where this function can be used. Full UnAlloc or + * Partial UnAlloc. A Full UnAlloc means all the BDs Alloc'd will be returned: + * + *
    + *    status = FXmacBdRingAlloc(Myring_ptr, 10, &bdptr),
    + *        ...
    + *    if (Error)
    + *    {
    + *        status = FXmacBdRingUnAlloc(Myring_ptr, 10, &bdptr),
    + *    }
    + * 
    + * + * A partial UnAlloc means some of the BDs Alloc'd will be returned: + * + *
    + *    status = FXmacBdRingAlloc(Myring_ptr, 10, &bdptr),
    + *    BdsLeft = 10,
    + *    cur_bd_ptr = bdptr,
    + *
    + *    while (BdsLeft)
    + *    {
    + *       if (Error)
    + *       {
    + *          status = FXmacBdRingUnAlloc(Myring_ptr, BdsLeft, cur_bd_ptr),
    + *       }
    + *
    + *       cur_bd_ptr = FXMAC_BD_RING_NEXT(Myring_ptr, cur_bd_ptr),
    + *       BdsLeft--,
    + *    }
    + * 
    + * + * A partial UnAlloc must include the last BD in the list that was Alloc'd. + * + * @param ring_ptr is a pointer to the instance to be worked on. + * @param num_bd is the number of BDs to allocate + * @param bd_set_ptr is an output parameter, it points to the first BD available + * for modification. + * + * @return + * - FT_SUCCESS if the BDs were unallocated. + * - FXMAC_ERR_GENERAL if num_bd parameter was greater that the number of BDs in + * the preprocessing state. + * + * @return {*} + */ +FError FXmacBdRingUnAlloc(FXmacBdRing *ring_ptr, u32 num_bd, + FXmacBd *bd_set_ptr) +{ + FError status; + (void)bd_set_ptr; + FASSERT(ring_ptr != NULL); + FASSERT(bd_set_ptr != NULL); + + /* Enough BDs in the free state for the request? */ + if (ring_ptr->pre_cnt < num_bd) + { + status = (FError)(FXMAC_ERR_GENERAL); + } + else + { + /* Set the return argument and move free_head backward */ + FXMAC_RING_SEEKBACK(ring_ptr, (ring_ptr->free_head), num_bd); + ring_ptr->free_cnt += num_bd; + ring_ptr->pre_cnt -= num_bd; + status = (FError)(FT_SUCCESS); + } + return status; +} + + +/** + * @name: FXmacBdRingToHw + * @msg: Enqueue a set of BDs to hardware that were previously allocated by + * FXmacBdRingAlloc(). Once this function returns, the argument BD set goes + * under hardware control. Any changes made to these BDs after this point will + * corrupt the BD list leading to data corruption and system instability. + * + * @param ring_ptr is a pointer to the instance to be worked on. + * @param num_bd is the number of BDs in the set. + * @param bd_set_ptr is the first BD of the set to commit to hardware. + * @return FT_SUCCESS if the set of BDs was accepted and enqueued to hardware. + * XST_FAILURE if the set of BDs was rejected because the last BD of the set + * did not have its "last" bit set. + * FXMAC_ERR_SG_LIST if this function was called out of sequence with + * FXmacBdRingAlloc(). + */ +FError FXmacBdRingToHw(FXmacBdRing *ring_ptr, u32 num_bd, + FXmacBd *bd_set_ptr) +{ + FXmacBd *cur_bd_ptr; + u32 i; + FError status; + /* if no bds to process, simply return. */ + if (0U == num_bd) + { + status = (FError)(FT_SUCCESS); + } + else + { + /* Make sure we are in sync with FXmacBdRingAlloc() */ + if ((ring_ptr->pre_cnt < num_bd) || (ring_ptr->pre_head != bd_set_ptr)) + { + status = (FError)(FXMAC_ERR_SG_LIST); + } + else + { + cur_bd_ptr = bd_set_ptr; + for (i = 0U; i < num_bd; i++) + { + cur_bd_ptr = (FXmacBd *)((void *)FXMAC_BD_RING_NEXT(ring_ptr, cur_bd_ptr)); + } + /* Adjust ring pointers & counters */ + FXMAC_RING_SEEKAHEAD(ring_ptr, ring_ptr->pre_head, num_bd); + ring_ptr->pre_cnt -= num_bd; + ring_ptr->hw_tail = cur_bd_ptr; + ring_ptr->hw_cnt += num_bd; + + status = (FError)(FT_SUCCESS); + } + } + return status; +} + + +/** + * @name: FXmacBdRingFromHwTx + * @msg: Returns a set of BD(s) that have been processed by hardware. The returned + * BDs may be examined to determine the outcome of the DMA transaction(s). + * Once the BDs have been examined, the user must call FXmacBdRingFree() + * in the same order which they were retrieved here. Example: + * + *
    + *        num_bd = FXmacBdRingFromHwTx(Myring_ptr, MaxBd, &MyBdSet),
    + *        if (num_bd == 0)
    + *        {
    + *           * hardware has nothing ready for us yet*
    + *        }
    + *
    + *        cur_bd = MyBdSet,
    + *        for (i=0; i
    + *
    + * A more advanced use of this function may allocate multiple sets of BDs.
    + * They must be retrieved from hardware and freed in the correct sequence:
    + * 
    + *        * Legal *
    + *        FXmacBdRingFromHwTx(Myring_ptr, num_bd1, &MySet1),
    + *        FXmacBdRingFree(Myring_ptr, num_bd1, MySet1),
    + *
    + *        * Legal *
    + *        FXmacBdRingFromHwTx(Myring_ptr, num_bd1, &MySet1),
    + *        FXmacBdRingFromHwTx(Myring_ptr, num_bd2, &MySet2),
    + *        FXmacBdRingFree(Myring_ptr, num_bd1, MySet1),
    + *        FXmacBdRingFree(Myring_ptr, num_bd2, MySet2),
    + *
    + *        * Not legal *
    + *        FXmacBdRingFromHwTx(Myring_ptr, num_bd1, &MySet1),
    + *        FXmacBdRingFromHwTx(Myring_ptr, num_bd2, &MySet2),
    + *        FXmacBdRingFree(Myring_ptr, num_bd2, MySet2),
    + *        FXmacBdRingFree(Myring_ptr, num_bd1, MySet1),
    + * 
    + * + * If hardware has only partially completed a packet spanning multiple BDs, + * then none of the BDs for that packet will be included in the results. + * + * @param ring_ptr is a pointer to the instance to be worked on. + * @param bd_limit is the maximum number of BDs to return in the set. + * @param bd_set_ptr is an output parameter, it points to the first BD available + * for examination. + * +* @return + * The number of BDs processed by hardware. A value of 0 indicates that no + * data is available. No more than bd_limit BDs will be returned. + * + * @return + * The number of BDs processed by hardware. A value of 0 indicates that no + * data is available. No more than bd_limit BDs will be returned. + */ +u32 FXmacBdRingFromHwTx(FXmacBdRing *ring_ptr, u32 bd_limit, + FXmacBd **bd_set_ptr) +{ + FXmacBd *cur_bd_ptr; + u32 bd_str = 0U; + u32 bd_count; + u32 bd_partial_count; + u32 Sop = 0U; + u32 status; + u32 bd_limitLoc = bd_limit; + cur_bd_ptr = ring_ptr->hw_head; + bd_count = 0U; + bd_partial_count = 0U; + + /* If no BDs in work group, then there's nothing to search */ + if (ring_ptr->hw_cnt == 0x00000000U) + { + *bd_set_ptr = NULL; + status = 0U; + } + else + { + + if (bd_limitLoc > ring_ptr->hw_cnt) + { + bd_limitLoc = ring_ptr->hw_cnt; + } + /* Starting at hw_head, keep moving forward in the list until: + * - A BD is encountered with its new/used bit set which means + * hardware has not completed processing of that BD. + * - ring_ptr->hw_tail is reached and ring_ptr->hw_cnt is reached. + * - The number of requested BDs has been processed + */ + while (bd_count < bd_limitLoc) + { + /* Read the status */ + if (cur_bd_ptr != NULL) + { + bd_str = FXMAC_BD_READ(cur_bd_ptr, FXMAC_BD_STAT_OFFSET); + } + + if ((Sop == 0x00000000U) && ((bd_str & FXMAC_TXBUF_USED_MASK) != 0x00000000U)) + { + Sop = 1U; + } + if (Sop == 0x00000001U) + { + bd_count++; + bd_partial_count++; + } + + /* hardware has processed this BD so check the "last" bit. + * If it is clear, then there are more BDs for the current + * packet. Keep a count of these partial packet BDs. + */ + if ((Sop == 0x00000001U) && ((bd_str & FXMAC_TXBUF_LAST_MASK) != 0x00000000U)) + { + Sop = 0U; + bd_partial_count = 0U; + } + + /* Move on to next BD in work group */ + cur_bd_ptr = FXMAC_BD_RING_NEXT(ring_ptr, cur_bd_ptr); + } + + /* Subtract off any partial packet BDs found */ + bd_count -= bd_partial_count; + + /* If bd_count is non-zero then BDs were found to return. Set return + * parameters, update pointers and counters, return success + */ + if (bd_count > 0x00000000U) + { + *bd_set_ptr = ring_ptr->hw_head; + ring_ptr->hw_cnt -= bd_count; + ring_ptr->post_cnt += bd_count; + FXMAC_RING_SEEKAHEAD(ring_ptr, ring_ptr->hw_head, bd_count); + status = (bd_count); + } + else + { + *bd_set_ptr = NULL; + status = 0U; + } + } + return status; +} + +/** + * @name: FXmacBdRingFromHwRx + * @msg: Returns a set of BD(s) that have been processed by hardware. The returned + * BDs may be examined to determine the outcome of the DMA transaction(s). + * Once the BDs have been examined, the user must call FXmacBdRingFree() + * in the same order which they were retrieved here. Example: + * + *
    + *        num_bd = FXmacBdRingFromHwRx(Myring_ptr, MaxBd, &MyBdSet),
    + *
    + *        if (num_bd == 0)
    + *        {
    + *           *hardware has nothing ready for us yet*
    + *        }
    + *
    + *        cur_bd = MyBdSet,
    + *        for (i=0; i
    + *
    + * A more advanced use of this function may allocate multiple sets of BDs.
    + * They must be retrieved from hardware and freed in the correct sequence:
    + * 
    + *        * Legal *
    + *        FXmacBdRingFromHwRx(Myring_ptr, num_bd1, &MySet1),
    + *        FXmacBdRingFree(Myring_ptr, num_bd1, MySet1),
    + *
    + *        * Legal *
    + *        FXmacBdRingFromHwRx(Myring_ptr, num_bd1, &MySet1),
    + *        FXmacBdRingFromHwRx(Myring_ptr, num_bd2, &MySet2),
    + *        FXmacBdRingFree(Myring_ptr, num_bd1, MySet1),
    + *        FXmacBdRingFree(Myring_ptr, num_bd2, MySet2),
    + *
    + *        * Not legal *
    + *        FXmacBdRingFromHwRx(Myring_ptr, num_bd1, &MySet1),
    + *        FXmacBdRingFromHwRx(Myring_ptr, num_bd2, &MySet2),
    + *        FXmacBdRingFree(Myring_ptr, num_bd2, MySet2),
    + *        FXmacBdRingFree(Myring_ptr, num_bd1, MySet1),
    + * 
    + * + * If hardware has only partially completed a packet spanning multiple BDs, + * then none of the BDs for that packet will be included in the results. + * + * @param ring_ptr is a pointer to the instance to be worked on. + * @param bd_limit is the maximum number of BDs to return in the set. + * @param bd_set_ptr is an output parameter, it points to the first BD available + * for examination. + * + * @return + * The number of BDs processed by hardware. A value of 0 indicates that no + * data is available. No more than bd_limit BDs will be returned. + */ +u32 FXmacBdRingFromHwRx(FXmacBdRing *ring_ptr, u32 bd_limit, + FXmacBd **bd_set_ptr) +{ + FXmacBd *cur_bd_ptr; + u32 bd_str = 0U; + u32 bd_count; + u32 bd_partial_count; + u32 status; + + cur_bd_ptr = ring_ptr->hw_head; + bd_count = 0U; + bd_partial_count = 0U; + + /* If no BDs in work group, then there's nothing to search */ + if (ring_ptr->hw_cnt == 0x00000000U) + { + *bd_set_ptr = NULL; + status = 0U; + } + else + { + /* Starting at hw_head, keep moving forward in the list until: + * - A BD is encountered with its new/used bit set which means + * hardware has completed processing of that BD. + * - ring_ptr->hw_tail is reached and ring_ptr->hw_cnt is reached. + * - The number of requested BDs has been processed + */ + while (bd_count < bd_limit) + { + + /* Read the status */ + if (cur_bd_ptr != NULL) + { + bd_str = FXMAC_BD_READ(cur_bd_ptr, FXMAC_BD_STAT_OFFSET); + } + if ((!(FXMAC_BD_IS_RX_NEW(cur_bd_ptr))) == TRUE) + { + break; + } + + bd_count++; + + /* hardware has processed this BD so check the "last" bit. If + * it is clear, then there are more BDs for the current packet. + * Keep a count of these partial packet BDs. + */ + if ((bd_str & FXMAC_RXBUF_EOF_MASK) != 0x00000000U) + { + bd_partial_count = 0U; + } + else + { + bd_partial_count++; + } + + /* Move on to next BD in work group */ + cur_bd_ptr = FXMAC_BD_RING_NEXT(ring_ptr, cur_bd_ptr); + // if((bd_str & FXMAC_RXBUF_EOF_MASK) != 0x00000000U) + // { + // if(bd_str &FXMAC_RXBUF_FCS_STATUS_MASK) + // { + // f_printk("********** error fcs data is appear ************* \r\n"); + // FtDumpHexWord(FXMAC_BD_READ(cur_bd_ptr,0) &(0xfffffff8),bd_str&FXMAC_RXBUF_LEN_MASK); + // f_printk("********** end ************* \r\n"); + // } + // } + } + + /* Subtract off any partial packet BDs found */ + bd_count -= bd_partial_count; + + /* If bd_count is non-zero then BDs were found to return. Set return + * parameters, update pointers and counters, return success + */ + if (bd_count > 0x00000000U) + { + *bd_set_ptr = ring_ptr->hw_head; + ring_ptr->hw_cnt -= bd_count; + ring_ptr->post_cnt += bd_count; + FXMAC_RING_SEEKAHEAD(ring_ptr, ring_ptr->hw_head, bd_count); + status = (bd_count); + } + else + { + *bd_set_ptr = NULL; + status = 0U; + } + } + return status; +} + +/** + * @name: FXmacBdRingFree + * @msg: Frees a set of BDs that had been previously retrieved with + * FXmacBdRingFromHw(). + * + * @param ring_ptr is a pointer to the instance to be worked on. + * @param num_bd is the number of BDs to free. + * @param bd_set_ptr is the head of a list of BDs returned by + * FXmacBdRingFromHw(). + * + * @return + * FT_SUCCESS if the set of BDs was freed. + * FXMAC_ERR_SG_LIST if this function was called out of sequence with + * FXmacBdRingFromHw(). + */ +FError FXmacBdRingFree(FXmacBdRing *ring_ptr, u32 num_bd, + FXmacBd *bd_set_ptr) +{ + FError status; + /* if no bds to process, simply return. */ + if (0x00000000U == num_bd) + { + status = (FError)(FT_SUCCESS); + } + else + { + /* Make sure we are in sync with FXmacBdRingFromHw() */ + if ((ring_ptr->post_cnt < num_bd) || (ring_ptr->post_head != bd_set_ptr)) + { + status = (FError)(FXMAC_ERR_SG_LIST); + } + else + { + /* Update pointers and counters */ + ring_ptr->free_cnt += num_bd; + ring_ptr->post_cnt -= num_bd; + FXMAC_RING_SEEKAHEAD(ring_ptr, ring_ptr->post_head, num_bd); + status = (FError)(FT_SUCCESS); + } + } + return status; +} + +/** + * @name: FXmacBdRingCheck + * @msg: Check the internal data structures of the BD ring for the provided channel. + * The following checks are made: + * + * - Is the BD ring linked correctly in physical address space. + * - Do the internal pointers point to BDs in the ring. + * - Do the internal counters add up. + * + * The channel should be stopped prior to calling this function. + * + * @param {FXmacBdRing} ring_ptr is a pointer to the instance to be worked on. + * @param {u8} direction is either FXMAC_SEND or FXMAC_RECV that indicates + * which direction. + * @return {*} + * FT_SUCCESS if the set of BDs was freed. + * XST_DMA_SG_NO_LIST if the list has not been created. + * FT_COMPONENT_IS_STARTED if the channel is not stopped. + * FXMAC_ERR_SG_LIST if a problem is found with the internal data + * structures. If this value is returned, the channel should be reset to + * avoid data corruption or system instability. + */ +FError FXmacBdRingCheck(FXmacBdRing *ring_ptr, u8 direction) +{ + uintptr addr_v, addr_p; + u32 i; + + if ((direction != (u8)FXMAC_SEND) && (direction != (u8)FXMAC_RECV)) + { + return (FError)(FXMAC_ERR_INVALID_PARAM); + } + + /* Is the list created */ + if (ring_ptr->all_cnt == 0x00000000U) + { + return (FError)(FXMAC_ERR_SG_NO_LIST); + } + + /* Can't check if channel is running */ + if (ring_ptr->run_state == (u32)FXMAC_DMA_SG_IS_STARTED) + { + return (FError)(FT_COMPONENT_IS_STARTED); + } + + /* run_state doesn't make sense */ + if (ring_ptr->run_state != (u32)FXMAC_DMA_SG_IS_STOPED) + { + return (FError)(FXMAC_ERR_SG_LIST); + } + + /* Verify internal pointers point to correct memory space */ + addr_v = (uintptr)ring_ptr->free_head; + if ((addr_v < ring_ptr->base_bd_addr) || (addr_v > ring_ptr->high_bd_addr)) + { + return (FError)(FXMAC_ERR_SG_LIST); + } + + addr_v = (uintptr)ring_ptr->pre_head; + if ((addr_v < ring_ptr->base_bd_addr) || (addr_v > ring_ptr->high_bd_addr)) + { + return (FError)(FXMAC_ERR_SG_LIST); + } + + addr_v = (uintptr)ring_ptr->hw_head; + if ((addr_v < ring_ptr->base_bd_addr) || (addr_v > ring_ptr->high_bd_addr)) + { + return (FError)(FXMAC_ERR_SG_LIST); + } + + addr_v = (uintptr)ring_ptr->hw_tail; + if ((addr_v < ring_ptr->base_bd_addr) || (addr_v > ring_ptr->high_bd_addr)) + { + return (FError)(FXMAC_ERR_SG_LIST); + } + + addr_v = (uintptr)ring_ptr->post_head; + if ((addr_v < ring_ptr->base_bd_addr) || (addr_v > ring_ptr->high_bd_addr)) + { + return (FError)(FXMAC_ERR_SG_LIST); + } + + /* Verify internal counters add up */ + if ((ring_ptr->hw_cnt + ring_ptr->pre_cnt + ring_ptr->free_cnt + + ring_ptr->post_cnt) != ring_ptr->all_cnt) + { + return (FError)(FXMAC_ERR_SG_LIST); + } + + /* Verify BDs are linked correctly */ + addr_v = ring_ptr->base_bd_addr; + addr_p = ring_ptr->phys_base_addr + ring_ptr->separation; + + for (i = 1U; i < ring_ptr->all_cnt; i++) + { + /* Check BDA for this BD. It should point to next physical addr */ + if (FXMAC_BD_READ(addr_v, FXMAC_BD_ADDR_OFFSET) != addr_p) + { + return (FError)(FXMAC_ERR_SG_LIST); + } + + /* Move on to next BD */ + addr_v += ring_ptr->separation; + addr_p += ring_ptr->separation; + } + + /* Last BD should have wrap bit set */ + if (FXMAC_SEND == direction) + { + if ((!FXMAC_BD_IS_TX_WRAP(addr_v)) == TRUE) + { + return (FError)(FXMAC_ERR_SG_LIST); + } + } + else + { + /* FXMAC_RECV */ + if ((!FXMAC_BD_IS_RX_WRAP(addr_v)) == TRUE) + { + return (FError)(FXMAC_ERR_SG_LIST); + } + } + + /* No problems found */ + return (FError)(FT_SUCCESS); +} + + +/** + * @name: FXmacBdSetRxWrap + * @msg: Set this bit to mark the last descriptor in the receive buffer descriptor + * list. + * @param {uintptr} bdptr is the BD pointer to operate on + */ +static void FXmacBdSetRxWrap(uintptr bdptr) +{ + u32 data_value_rx; + u32 *temp_ptr; + + bdptr += (u32)(FXMAC_BD_ADDR_OFFSET); + temp_ptr = (u32 *)bdptr; + if (temp_ptr != NULL) + { + data_value_rx = *temp_ptr; + data_value_rx |= FXMAC_RXBUF_WRAP_MASK; + *temp_ptr = data_value_rx; + } +} + +/** + * @name: FXmacBdSetTxWrap + * @msg: Sets this bit to mark the last descriptor in the transmit buffer + * descriptor list. + * @param {uintptr} bdptr is the BD pointer to operate on + */ +static void FXmacBdSetTxWrap(uintptr bdptr) +{ + u32 data_value_tx; + u32 *temp_ptr; + + bdptr += (u32)(FXMAC_BD_STAT_OFFSET); + temp_ptr = (u32 *)bdptr; + if (temp_ptr != NULL) + { + data_value_tx = *temp_ptr; + data_value_tx |= FXMAC_TXBUF_WRAP_MASK; + *temp_ptr = data_value_tx; + } +} + + +/** + * @name: FXmacBdringPtrReset + * @msg: Reset BD ring head and tail pointers. + * @return {*} + * @param {FXmacBdRing} *ring_ptr is the instance to be worked on. + * @param {void} *virtaddrloc is the virtual base address of the user memory region. + */ +void FXmacBdringPtrReset(FXmacBdRing *ring_ptr, void *virtaddrloc) +{ + ring_ptr->free_head = virtaddrloc; + ring_ptr->pre_head = virtaddrloc; + ring_ptr->hw_head = virtaddrloc; + ring_ptr->hw_tail = virtaddrloc; + ring_ptr->post_head = virtaddrloc; +} diff --git a/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_bdring.h b/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_bdring.h new file mode 100644 index 0000000000..b03269e3f4 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_bdring.h @@ -0,0 +1,160 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fxmac_bdring.h + * Date: 2022-04-06 14:46:52 + * LastEditTime: 2022-04-06 14:46:58 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef DRIVERS_ETH_F_XMAC_BDRING_H +#define DRIVERS_ETH_F_XMAC_BDRING_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "fxmac_bd.h" +#include "ftypes.h" + +/**************************** Type Definitions *******************************/ + +/** This is an internal structure used to maintain the DMA list */ +typedef struct +{ + uintptr phys_base_addr; /* Physical address of 1st BD in list */ + uintptr base_bd_addr; /* Virtual address of 1st BD in list */ + uintptr high_bd_addr; /* Virtual address of last BD in the list */ + u32 length; /* Total size of ring in bytes */ + u32 run_state; /* Flag to indicate DMA is started */ + u32 separation; /* Number of bytes between the starting address + of adjacent BDs */ + FXmacBd *free_head; + /* First BD in the free group */ + FXmacBd *pre_head; /* First BD in the pre-work group */ + FXmacBd *hw_head; /* First BD in the work group */ + FXmacBd *hw_tail; /* Last BD in the work group */ + FXmacBd *post_head; + /* First BD in the post-work group */ + FXmacBd *bda_restart; + /* BDA to load when channel is started */ + + volatile u32 hw_cnt; /* Number of BDs in work group */ + u32 pre_cnt; /* Number of BDs in pre-work group */ + u32 free_cnt; /* Number of allocatable BDs in the free group */ + u32 post_cnt; /* Number of BDs in post-work group */ + u32 all_cnt; /* Total Number of BDs for channel */ +} FXmacBdRing; + +/** + * @name: FXMAC_BD_RING_NEXT + * @msg: Return the next BD from bd_ptr in a list. + * + * @param ring_ptr is the DMA channel to operate on. + * @param bd_ptr is the BD to operate on. + * @return The next BD in the list relative to the bd_ptr parameter. + */ +#define FXMAC_BD_RING_NEXT(ring_ptr, bd_ptr) \ + (((uintptr)((void *)(bd_ptr)) >= (ring_ptr)->high_bd_addr) ? (FXmacBd *)((void *)(ring_ptr)->base_bd_addr) : (FXmacBd *)((uintptr)((void *)(bd_ptr)) + (ring_ptr)->separation)) + +/** + * @name: FXMAC_BD_RING_CNT_CALC + * @msg: Use this macro at initialization time to determine how many BDs will fit + * in a BD list within the given memory constraints. + * + * @param alignment specifies what byte alignment the BDs must fall on and + * must be a power of 2 to get an accurate calculation (32, 64, 128,...) + * @param Bytes is the number of bytes to be used to store BDs. + * @return Number of BDs that can fit in the given memory area + */ +#define FXMAC_BD_RING_CNT_CALC(alignment, Bytes) \ + (u32)((Bytes) / (sizeof(FXmacBd))) + +/** + * @name: FXMAC_BD_RING_MEM_CALC + * @msg: Use this macro at initialization time to determine how many bytes of memory + * is required to contain a given number of BDs at a given alignment. + * @param alignment specifies what byte alignment the BDs must fall on. This + * parameter must be a power of 2 to get an accurate calculation (32, 64, + * 128,...) + * @param num_bd is the number of BDs to calculate memory size requirements for + * @return The number of bytes of memory required to create a BD list with the + * given memory constraints. + */ +#define FXMAC_BD_RING_MEM_CALC(alignment, num_bd) \ + (u32)(sizeof(FXmacBd) * (num_bd)) + +/** + * @name: FXMAC_BD_RING_GET_CNT + * @msg: Return the total number of BDs allocated by this channel with + * FXmacBdRingCreate(). + * @param ring_ptr is the DMA channel to operate on. + * @return The total number of BDs allocated for this channel. + */ +#define FXMAC_BD_RING_GET_CNT(ring_ptr) ((ring_ptr)->all_cnt) + +/** + * @name: FXMAC_BD_RING_GET_FREE_CNT + * @msg: Return the number of BDs allocatable with FXmacBdRingAlloc() for pre- + * processing. + * + * @param ring_ptr is the DMA channel to operate on. + * @return The number of BDs currently allocatable. + */ +#define FXMAC_BD_RING_GET_FREE_CNT(ring_ptr) ((ring_ptr)->free_cnt) + +/** + * @name: FXMAC_BD_RING_PREV + * @msg: Return the previous BD from bd_ptr in the list. + * @param ring_ptr is the DMA channel to operate on. + * @param bd_ptr is the BD to operate on + * @return The previous BD in the list relative to the bd_ptr parameter. + */ +#define FXMAC_BD_RING_PREV(ring_ptr, bd_ptr) \ + (((uintptr)(bd_ptr) <= (ring_ptr)->base_bd_addr) ? (FXmacBd *)(ring_ptr)->high_bd_addr : (FXmacBd *)((uintptr)(bd_ptr) - (ring_ptr)->separation)) + +/************************** Function Prototypes ******************************/ + +/* + * Scatter gather DMA related functions in FXmacbdring.c + */ +FError FXmacBdRingCreate(FXmacBdRing *ring_ptr, uintptr phys_addr, + uintptr virt_addr, u32 alignment, u32 bd_count); +FError FXmacBdRingClone(FXmacBdRing *ring_ptr, FXmacBd *src_bd_ptr, + u8 direction); +FError FXmacBdRingAlloc(FXmacBdRing *ring_ptr, u32 num_bd, + FXmacBd **bd_set_ptr); +FError FXmacBdRingUnAlloc(FXmacBdRing *ring_ptr, u32 num_bd, + FXmacBd *bd_set_ptr); +FError FXmacBdRingToHw(FXmacBdRing *ring_ptr, u32 num_bd, + FXmacBd *bd_set_ptr); +FError FXmacBdRingFree(FXmacBdRing *ring_ptr, u32 num_bd, + FXmacBd *bd_set_ptr); +u32 FXmacBdRingFromHwTx(FXmacBdRing *ring_ptr, u32 bd_limit, + FXmacBd **bd_set_ptr); +u32 FXmacBdRingFromHwRx(FXmacBdRing *ring_ptr, u32 bd_limit, + FXmacBd **bd_set_ptr); +FError FXmacBdRingCheck(FXmacBdRing *ring_ptr, u8 direction); + +void FXmacBdringPtrReset(FXmacBdRing *ring_ptr, void *virt_addrloc); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_g.c b/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_g.c new file mode 100644 index 0000000000..b8f1b053f3 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_g.c @@ -0,0 +1,152 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fxmac_g.c + * Date: 2022-04-06 14:46:52 + * LastEditTime: 2022-04-06 14:46:58 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "fxmac.h" +#include "fparameters.h" +#include "ftypes.h" +/************************** Constant Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/************************** Variable Prototypes ******************************/ + +/* + * The configuration table for emacps device + */ + +const FXmacConfig fxmac_cfg_tbl[FT_XMAC_NUM] = +{ + [FT_XMAC0_ID] = + { + .instance_id = (u32)FT_XMAC0_ID, /* Device ID */ + .base_address = (uintptr)FT_XMAC0_BASEADDRESS, /* Device base address */ + .extral_mode_base = FT_XMAC0_MODE_SEL_BASEADDRESS, + .extral_loopback_base = FT_XMAC0_LOOPBACK_SEL_BASEADDRESS, + .interface = FXMAC_PHY_INTERFACE_MODE_SGMII, + .speed = 1000, + .duplex = 1, + .auto_neg = 1, + .pclk_hz = FT_XMAC0_PCLK, + .max_queue_num = 16, + .tx_queue_id = 0, + .rx_queue_id = 0, + .hotplug_irq_num = FT_XMAC0_HOTPLUG_IRQ_NUM, + .dma_brust_length = 16, + .network_default_config = FXMAC_DEFAULT_OPTIONS, + .queue_irq_num = + { + FT_XMAC0_QUEUE0_IRQ_NUM, + FT_XMAC0_QUEUE1_IRQ_NUM, + FT_XMAC0_QUEUE2_IRQ_NUM, + FT_XMAC0_QUEUE3_IRQ_NUM, + FT_XMAC0_QUEUE4_IRQ_NUM, + FT_XMAC0_QUEUE5_IRQ_NUM, + FT_XMAC0_QUEUE6_IRQ_NUM, + FT_XMAC0_QUEUE7_IRQ_NUM + } + }, +#ifdef FT_XMAC1_ID + [FT_XMAC1_ID] = + { + .instance_id = (u32)FT_XMAC1_ID, /* Device ID */ + .base_address = (uintptr)FT_XMAC1_BASEADDRESS, /* Device base address */ + .extral_mode_base = FT_XMAC1_MODE_SEL_BASEADDRESS, + .extral_loopback_base = FT_XMAC1_LOOPBACK_SEL_BASEADDRESS, + .interface = FXMAC_PHY_INTERFACE_MODE_SGMII, + .speed = 1000, + .duplex = 1, + .auto_neg = 1, + .pclk_hz = FT_XMAC1_PCLK, + .max_queue_num = 4, + .tx_queue_id = 0, + .rx_queue_id = 0, + .hotplug_irq_num = FT_XMAC1_HOTPLUG_IRQ_NUM, + .dma_brust_length = 16, + .network_default_config = FXMAC_DEFAULT_OPTIONS, + .queue_irq_num = + { + FT_XMAC1_QUEUE0_IRQ_NUM, + FT_XMAC1_QUEUE1_IRQ_NUM, + FT_XMAC1_QUEUE2_IRQ_NUM, + FT_XMAC1_QUEUE3_IRQ_NUM + } + }, +#endif +#ifdef FT_XMAC2_ID + [FT_XMAC2_ID] = + { + .instance_id = (u32)FT_XMAC2_ID, /* Device ID */ + .base_address = (uintptr)FT_XMAC2_BASEADDRESS, /* Device base address */ + .extral_mode_base = FT_XMAC2_MODE_SEL_BASEADDRESS, + .extral_loopback_base = FT_XMAC2_LOOPBACK_SEL_BASEADDRESS, + .interface = FXMAC_PHY_INTERFACE_MODE_RGMII, + .speed = 1000, + .duplex = 1, + .auto_neg = 1, + .pclk_hz = FT_XMAC2_PCLK, + .max_queue_num = 4, + .tx_queue_id = 0, + .rx_queue_id = 0, + .hotplug_irq_num = FT_XMAC2_HOTPLUG_IRQ_NUM, + .dma_brust_length = 16, + .network_default_config = FXMAC_DEFAULT_OPTIONS, + .queue_irq_num = + { + FT_XMAC2_QUEUE0_IRQ_NUM, + FT_XMAC2_QUEUE1_IRQ_NUM, + FT_XMAC2_QUEUE2_IRQ_NUM, + FT_XMAC2_QUEUE3_IRQ_NUM + } + }, +#endif +#ifdef FT_XMAC3_ID + [FT_XMAC3_ID] = + { + .instance_id = (u32)FT_XMAC3_ID, /* Device ID */ + .base_address = (uintptr)FT_XMAC3_BASEADDRESS, /* Device base address */ + .extral_mode_base = FT_XMAC3_MODE_SEL_BASEADDRESS, + .extral_loopback_base = FT_XMAC3_LOOPBACK_SEL_BASEADDRESS, + .interface = FXMAC_PHY_INTERFACE_MODE_RGMII, + .speed = 1000, + .duplex = 1, + .auto_neg = 1, + .pclk_hz = FT_XMAC3_PCLK, + .max_queue_num = 4, + .tx_queue_id = 0, + .rx_queue_id = 0, + .hotplug_irq_num = FT_XMAC3_HOTPLUG_IRQ_NUM, + .dma_brust_length = 16, + .network_default_config = FXMAC_DEFAULT_OPTIONS, + .queue_irq_num = + { + FT_XMAC3_QUEUE0_IRQ_NUM, + FT_XMAC3_QUEUE1_IRQ_NUM, + FT_XMAC3_QUEUE2_IRQ_NUM, + FT_XMAC3_QUEUE3_IRQ_NUM + } + } +#endif +}; +/** @} */ diff --git a/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_hw.h b/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_hw.h new file mode 100644 index 0000000000..f3814bd897 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_hw.h @@ -0,0 +1,701 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fxmac_hw.h + * Date: 2022-04-06 14:46:52 + * LastEditTime: 2022-04-06 14:46:58 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef BSP_DRIVERS_ETH_FMAC_HW_H +#define BSP_DRIVERS_ETH_FMAC_HW_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "fparameters.h" +#include "fio.h" +#include "ftypes.h" +#include "fkernel.h" + +#define FXMAC_RX_BUF_SIZE 1536U /* Specify the receive buffer size in \ + bytes, 64, 128, ... 10240 */ +#define FXMAC_RX_BUF_SIZE_JUMBO 10240U + +#define FXMAC_RX_BUF_UNIT 64U /* Number of receive buffer bytes as a \ + unit, this is HW setup */ + +#define FXMAC_MAX_RXBD 128U /* Size of RX buffer descriptor queues */ +#define FXMAC_MAX_TXBD 128U /* Size of TX buffer descriptor queues */ + +#define FXMAC_MAX_HASH_BITS 64U /* Maximum value for hash bits. 2**6 */ + +/************************** Constant Definitions *****************************/ + +#define FXMAC_MAX_MAC_ADDR 4U /* Maxmum number of mac address \ + supported */ +#define FXMAC_MAX_TYPE_ID 4U /* Maxmum number of type id supported */ + +#ifdef __aarch64__ +#define FXMAC_BD_ALIGNMENT 64U /* Minimum buffer descriptor alignment \ + on the local bus */ +#else + +#define FXMAC_BD_ALIGNMENT 4U /* Minimum buffer descriptor alignment \ + on the local bus */ +#endif +#define FXMAC_RX_BUF_ALIGNMENT 4U /* Minimum buffer alignment when using \ + options that impose alignment \ + restrictions on the buffer data on \ + the local bus */ + +#define FXMAC_NWCTRL_OFFSET 0x00000000U /* Network Control reg */ +#define FXMAC_NWCFG_OFFSET 0x00000004U /* Network Config reg */ +#define FXMAC_NWSR_OFFSET 0x00000008U /* Network Status reg */ +#define FXMAC_DMACR_OFFSET 0x00000010U /* DMA Control reg */ +#define FXMAC_TXSR_OFFSET 0x00000014U /* TX Status reg */ +#define FXMAC_RXQBASE_OFFSET 0x00000018U /* RX Q Base address reg */ +#define FXMAC_TXQBASE_OFFSET 0x0000001CU /* TX Q Base address reg */ +#define FXMAC_RXSR_OFFSET 0x00000020U /* RX Status reg */ + +#define FXMAC_ISR_OFFSET 0x00000024U /* Interrupt Status reg */ +#define FXMAC_IER_OFFSET 0x00000028U /* Interrupt Enable reg */ +#define FXMAC_IDR_OFFSET 0x0000002CU /* Interrupt Disable reg */ +#define FXMAC_IMR_OFFSET 0x00000030U /* Interrupt Mask reg */ + +#define FXMAC_PHYMNTNC_OFFSET 0x00000034U /* Phy Maintaince reg */ +#define FXMAC_RXPAUSE_OFFSET 0x00000038U /* RX Pause Time reg */ +#define FXMAC_TXPAUSE_OFFSET 0x0000003CU /* TX Pause Time reg */ + +#define FXMAC_JUMBOMAXLEN_OFFSET 0x00000048U /* Jumbo max length reg */ +#define FXMAC_GEM_HSMAC 0x0050 /* Hs mac config register*/ +#define FXMAC_RXWATERMARK_OFFSET 0x0000007CU /* RX watermark reg */ + +#define FXMAC_HASHL_OFFSET 0x00000080U /* Hash Low address reg */ +#define FXMAC_HASHH_OFFSET 0x00000084U /* Hash High address reg */ + +#define FXMAC_GEM_SA1B 0x0088 /* Specific1 Bottom */ +#define FXMAC_GEM_SA1T 0x008C /* Specific1 Top */ +#define FXMAC_GEM_SA2B 0x0090 /* Specific2 Bottom */ +#define FXMAC_GEM_SA2T 0x0094 /* Specific2 Top */ +#define FXMAC_GEM_SA3B 0x0098 /* Specific3 Bottom */ +#define FXMAC_GEM_SA3T 0x009C /* Specific3 Top */ +#define FXMAC_GEM_SA4B 0x00A0 /* Specific4 Bottom */ +#define FXMAC_GEM_SA4T 0x00A4 /* Specific4 Top */ + +#define FXMAC_MATCH1_OFFSET 0x000000A8U /* Type ID1 Match reg */ +#define FXMAC_MATCH2_OFFSET 0x000000ACU /* Type ID2 Match reg */ +#define FXMAC_MATCH3_OFFSET 0x000000B0U /* Type ID3 Match reg */ +#define FXMAC_MATCH4_OFFSET 0x000000B4U /* Type ID4 Match reg */ + +#define FXMAC_STRETCH_OFFSET 0x000000BCU /* IPG Stretch reg */ +#define FXMAC_REVISION_REG_OFFSET 0x000000FCU /* identification number and module revision */ + +#define FXMAC_OCTTXL_OFFSET 0x00000100U /* Octects transmitted Low \ + reg */ +#define FXMAC_OCTTXH_OFFSET 0x00000104U /* Octects transmitted High \ + reg */ + +#define FXMAC_TXCNT_OFFSET 0x00000108U /* Error-free Frmaes \ + transmitted counter */ +#define FXMAC_TXBCCNT_OFFSET 0x0000010CU /* Error-free Broadcast \ + Frames counter*/ +#define FXMAC_TXMCCNT_OFFSET 0x00000110U /* Error-free Multicast \ + Frame counter */ +#define FXMAC_TXPAUSECNT_OFFSET 0x00000114U /* Pause Frames Transmitted \ + Counter */ +#define FXMAC_TX64CNT_OFFSET 0x00000118U /* Error-free 64 byte Frames \ + Transmitted counter */ +#define FXMAC_TX65CNT_OFFSET 0x0000011CU /* Error-free 65-127 byte \ + Frames Transmitted \ + counter */ +#define FXMAC_TX128CNT_OFFSET 0x00000120U /* Error-free 128-255 byte \ + Frames Transmitted \ + counter*/ +#define FXMAC_TX256CNT_OFFSET 0x00000124U /* Error-free 256-511 byte \ + Frames transmitted \ + counter */ +#define FXMAC_TX512CNT_OFFSET 0x00000128U /* Error-free 512-1023 byte \ + Frames transmitted \ + counter */ +#define FXMAC_TX1024CNT_OFFSET 0x0000012CU /* Error-free 1024-1518 byte \ + Frames transmitted \ + counter */ +#define FXMAC_TX1519CNT_OFFSET 0x00000130U /* Error-free larger than \ + 1519 byte Frames \ + transmitted counter */ +#define FXMAC_TXURUNCNT_OFFSET 0x00000134U /* TX under run error \ + counter */ + +#define FXMAC_SNGLCOLLCNT_OFFSET 0x00000138U /* Single Collision Frame \ + Counter */ +#define FXMAC_MULTICOLLCNT_OFFSET 0x0000013CU /* Multiple Collision Frame \ + Counter */ +#define FXMAC_EXCESSCOLLCNT_OFFSET 0x00000140U /* Excessive Collision Frame \ + Counter */ +#define FXMAC_LATECOLLCNT_OFFSET 0x00000144U /* Late Collision Frame \ + Counter */ +#define FXMAC_TXDEFERCNT_OFFSET 0x00000148U /* Deferred Transmission \ + Frame Counter */ +#define FXMAC_TXCSENSECNT_OFFSET 0x0000014CU /* Transmit Carrier Sense \ + Error Counter */ + +#define FXMAC_OCTRXL_OFFSET 0x00000150U /* Octects Received register \ + Low */ +#define FXMAC_OCTRXH_OFFSET 0x00000154U /* Octects Received register \ + High */ + +#define FXMAC_RXCNT_OFFSET 0x00000158U /* Error-free Frames \ + Received Counter */ +#define FXMAC_RXBROADCNT_OFFSET 0x0000015CU /* Error-free Broadcast \ + Frames Received Counter */ +#define FXMAC_RXMULTICNT_OFFSET 0x00000160U /* Error-free Multicast \ + Frames Received Counter */ +#define FXMAC_RXPAUSECNT_OFFSET 0x00000164U /* Pause Frames \ + Received Counter */ +#define FXMAC_RX64CNT_OFFSET 0x00000168U /* Error-free 64 byte Frames \ + Received Counter */ +#define FXMAC_RX65CNT_OFFSET 0x0000016CU /* Error-free 65-127 byte \ + Frames Received Counter */ +#define FXMAC_RX128CNT_OFFSET 0x00000170U /* Error-free 128-255 byte \ + Frames Received Counter */ +#define FXMAC_RX256CNT_OFFSET 0x00000174U /* Error-free 256-512 byte \ + Frames Received Counter */ +#define FXMAC_RX512CNT_OFFSET 0x00000178U /* Error-free 512-1023 byte \ + Frames Received Counter */ +#define FXMAC_RX1024CNT_OFFSET 0x0000017CU /* Error-free 1024-1518 byte \ + Frames Received Counter */ +#define FXMAC_RX1519CNT_OFFSET 0x00000180U /* Error-free 1519-max byte \ + Frames Received Counter */ +#define FXMAC_RXUNDRCNT_OFFSET 0x00000184U /* Undersize Frames Received \ + Counter */ +#define FXMAC_RXOVRCNT_OFFSET 0x00000188U /* Oversize Frames Received \ + Counter */ +#define FXMAC_RXJABCNT_OFFSET 0x0000018CU /* Jabbers Received \ + Counter */ +#define FXMAC_RXFCSCNT_OFFSET 0x00000190U /* Frame Check Sequence \ + Error Counter */ +#define FXMAC_RXLENGTHCNT_OFFSET 0x00000194U /* Length Field Error \ + Counter */ +#define FXMAC_RXSYMBCNT_OFFSET 0x00000198U /* Symbol Error Counter */ +#define FXMAC_RXALIGNCNT_OFFSET 0x0000019CU /* Alignment Error Counter */ +#define FXMAC_RXRESERRCNT_OFFSET 0x000001A0U /* Receive Resource Error \ + Counter */ +#define FXMAC_RXORCNT_OFFSET 0x000001A4U /* Receive Overrun Counter */ +#define FXMAC_RXIPCCNT_OFFSET 0x000001A8U /* IP header Checksum Error \ + Counter */ +#define FXMAC_RXTCPCCNT_OFFSET 0x000001ACU /* TCP Checksum Error \ + Counter */ +#define FXMAC_RXUDPCCNT_OFFSET 0x000001B0U /* UDP Checksum Error \ + Counter */ +#define FXMAC_LAST_OFFSET 0x000001B4U /* Last statistic counter \ + offset, for clearing */ + +#define FXMAC_1588_SEC_OFFSET 0x000001D0U /* 1588 second counter */ +#define FXMAC_1588_NANOSEC_OFFSET 0x000001D4U /* 1588 nanosecond counter */ +#define FXMAC_1588_ADJ_OFFSET 0x000001D8U /* 1588 nanosecond \ + adjustment counter */ +#define FXMAC_1588_INC_OFFSET 0x000001DCU /* 1588 nanosecond \ + increment counter */ +#define FXMAC_PTP_TXSEC_OFFSET 0x000001E0U /* 1588 PTP transmit second \ + counter */ +#define FXMAC_PTP_TXNANOSEC_OFFSET 0x000001E4U /* 1588 PTP transmit \ + nanosecond counter */ +#define FXMAC_PTP_RXSEC_OFFSET 0x000001E8U /* 1588 PTP receive second \ + counter */ +#define FXMAC_PTP_RXNANOSEC_OFFSET 0x000001ECU /* 1588 PTP receive \ + nanosecond counter */ +#define FXMAC_PTPP_TXSEC_OFFSET 0x000001F0U /* 1588 PTP peer transmit \ + second counter */ +#define FXMAC_PTPP_TXNANOSEC_OFFSET 0x000001F4U /* 1588 PTP peer transmit \ + nanosecond counter */ +#define FXMAC_PTPP_RXSEC_OFFSET 0x000001F8U /* 1588 PTP peer receive \ + second counter */ +#define FXMAC_PTPP_RXNANOSEC_OFFSET 0x000001FCU /* 1588 PTP peer receive \ + nanosecond counter */ + +#define FXMAC_PCS_CONTROL_OFFSET 0x00000200U /* All PCS registers */ + +#define FXMAC_PCS_STATUS_OFFSET 0x00000204U /* All PCS status */ + +#define FXMAC_PCS_AN_LP_OFFSET 0x00000214U /* All PCS link partner's base page */ + +#define FXMAC_DESIGNCFG_DEBUG1_OFFSET 0x00000280U /* Design Configuration Register 1 */ + +#define FXMAC_DESIGNCFG_DEBUG2_OFFSET 0x00000284U /* Design Configuration Register 2 */ + +#define FXMAC_INTQ1_STS_OFFSET 0x00000400U /* Interrupt Q1 Status reg */ + +#define FXMAC_TXQ1BASE_OFFSET 0x00000440U /* TX Q1 Base address \ + reg */ +#define FXMAC_RXQ1BASE_OFFSET 0x00000480U /* RX Q1 Base address \ + reg */ + +#define FXMAC_RXBUFQ1_SIZE_OFFSET 0x000004a0U /* Receive Buffer Size */ +#define FXMAC_RXBUFQX_SIZE_OFFSET(x) (FXMAC_RXBUFQ1_SIZE_OFFSET + (x << 2)) +#define FXMAC_RXBUFQX_SIZE_MASK GENMASK(7, 0) + +#define FXMAC_MSBBUF_TXQBASE_OFFSET 0x000004C8U /* MSB Buffer TX Q Base reg */ +#define FXMAC_MSBBUF_RXQBASE_OFFSET 0x000004D4U /* MSB Buffer RX Q Base \ + reg */ +#define FXMAC_INTQ1_IER_OFFSET 0x00000600U /* Interrupt Q1 Enable reg */ +#define FXMAC_INTQX_IER_SIZE_OFFSET(x) (FXMAC_INTQ1_IER_OFFSET + (x << 2)) + +#define FXMAC_INTQ1_IDR_OFFSET 0x00000620U /* Interrupt Q1 Disable reg */ +#define FXMAC_INTQX_IDR_SIZE_OFFSET(x) (FXMAC_INTQ1_IDR_OFFSET + (x << 2)) + +#define FXMAC_INTQ1_IMR_OFFSET 0x00000640U /* Interrupt Q1 Mask \ + reg */ + +#define FXMAC_GEM_USX_CONTROL_OFFSET 0x0A80 /* High speed PCS control register */ +#define FXMAC_TEST_CONTROL_OFFSET 0X0A84 /* USXGMII Test Control Register */ + +#define FXMAC_GEM_SRC_SEL_LN 0x1C04 +#define FXMAC_GEM_DIV_SEL0_LN 0x1C08 +#define FXMAC_GEM_DIV_SEL1_LN 0x1C0C +#define FXMAC_GEM_PMA_XCVR_POWER_STATE 0x1C10 +#define FXMAC_GEM_SPEED_MODE 0x1C14 +#define FXMAC_GEM_MII_SELECT 0x1C18 +#define FXMAC_GEM_SEL_MII_ON_RGMII 0x1C1C +#define FXMAC_GEM_TX_CLK_SEL0 0x1C20 +#define FXMAC_GEM_TX_CLK_SEL1 0x1C24 +#define FXMAC_GEM_TX_CLK_SEL2 0x1C28 +#define FXMAC_GEM_TX_CLK_SEL3 0x1C2C +#define FXMAC_GEM_RX_CLK_SEL0 0x1C30 +#define FXMAC_GEM_RX_CLK_SEL1 0x1C34 +#define FXMAC_GEM_CLK_250M_DIV10_DIV100_SEL 0x1C38 +#define FXMAC_GEM_TX_CLK_SEL5 0x1C3C +#define FXMAC_GEM_TX_CLK_SEL6 0x1C40 +#define FXMAC_GEM_RX_CLK_SEL4 0x1C44 +#define FXMAC_GEM_RX_CLK_SEL5 0x1C48 +#define FXMAC_GEM_TX_CLK_SEL3_0 0x1C70 +#define FXMAC_GEM_TX_CLK_SEL4_0 0x1C74 +#define FXMAC_GEM_RX_CLK_SEL3_0 0x1C78 +#define FXMAC_GEM_RX_CLK_SEL4_0 0x1C7C +#define FXMAC_GEM_RGMII_TX_CLK_SEL0 0x1C80 +#define FXMAC_GEM_RGMII_TX_CLK_SEL1 0x1C84 +#define FXMAC_GEM_MODE_SEL_OFFSET 0xDC00 +#define FXMAC_LOOPBACK_SEL_OFFSET 0xDC04 + +/** + * @name interrupts bit definitions + * Bits definitions are same in FXMAC_ISR_OFFSET, + * FXMAC_IER_OFFSET, FXMAC_IDR_OFFSET, and FXMAC_IMR_OFFSET + * @{ + */ +#define FXMAC_IXR_PTPPSTX_MASK BIT(25) /* PTP Pdelay_resp TXed */ +#define FXMAC_IXR_PTPPDRTX_MASK BIT(24) /* PTP Pdelay_req TXed */ +#define FXMAC_IXR_PTPPSRX_MASK BIT(23) /* PTP Pdelay_resp RXed */ +#define FXMAC_IXR_PTPPDRRX_MASK BIT(22) /* PTP Pdelay_req RXed */ + +#define FXMAC_IXR_PTPSTX_MASK BIT(21) /* PTP Sync TXed */ +#define FXMAC_IXR_PTPDRTX_MASK BIT(20) /* PTP Delay_req TXed */ +#define FXMAC_IXR_PTPSRX_MASK BIT(19) /* PTP Sync RXed */ +#define FXMAC_IXR_PTPDRRX_MASK BIT(18) /* PTP Delay_req RXed */ + +#define FXMAC_IXR_PAUSETX_MASK BIT(14) /* Pause frame transmitted */ +#define FXMAC_IXR_PAUSEZERO_MASK BIT(13) /* Pause time has reached zero */ +#define FXMAC_IXR_PAUSENZERO_MASK BIT(12) /* Pause frame received */ +#define FXMAC_IXR_HRESPNOK_MASK BIT(11) /* hresp not ok */ +#define FXMAC_IXR_RXOVR_MASK BIT(10) /* Receive overrun occurred */ +#define FXMAC_IXR_LINKCHANGE_MASK BIT(9) /* link status change */ +#define FXMAC_IXR_TXCOMPL_MASK BIT(7) /* Frame transmitted ok */ +#define FXMAC_IXR_TXEXH_MASK BIT(6) /* Transmit err occurred or \ + no buffers*/ +#define FXMAC_IXR_RETRY_MASK BIT(5) /* Retry limit exceeded */ +#define FXMAC_IXR_URUN_MASK BIT(4) /* Transmit underrun */ +#define FXMAC_IXR_TXUSED_MASK BIT(3) /* Tx buffer used bit read */ +#define FXMAC_IXR_RXUSED_MASK BIT(2) /* Rx buffer used bit read */ +#define FXMAC_IXR_RXCOMPL_MASK BIT(1) /* Frame received ok */ +#define FXMAC_IXR_MGMNT_MASK BIT(0) /* PHY management complete */ +#define FXMAC_IXR_ALL_MASK GENMASK(14, 0) /* Everything! */ + +#define FXMAC_IXR_TX_ERR_MASK ((u32)FXMAC_IXR_TXEXH_MASK | \ + (u32)FXMAC_IXR_RETRY_MASK | \ + (u32)FXMAC_IXR_URUN_MASK) + +#define FXMAC_IXR_RX_ERR_MASK ((u32)FXMAC_IXR_HRESPNOK_MASK | \ + (u32)FXMAC_IXR_RXUSED_MASK | \ + (u32)FXMAC_IXR_RXOVR_MASK) + +/** @name network control register bit definitions + * @{ + */ +#define FXMAC_NWCTRL_ENABLE_HS_MAC_MASK BIT(31) + +#define FXMAC_NWCTRL_TWO_PT_FIVE_GIG_MASK BIT(29) /* 2.5G operation selected */ + +#define FXMAC_NWCTRL_FLUSH_DPRAM_MASK BIT(18) /* Flush a packet from \ + Rx SRAM */ +#define FXMAC_NWCTRL_ZEROPAUSETX_MASK BIT(11) /* Transmit zero quantum \ + pause frame */ +#define FXMAC_NWCTRL_PAUSETX_MASK BIT(11) /* Transmit pause frame */ +#define FXMAC_NWCTRL_HALTTX_MASK BIT(10) /* Halt transmission \ + after current frame */ +#define FXMAC_NWCTRL_STARTTX_MASK BIT(9) /* Start tx (tx_go) */ + +#define FXMAC_NWCTRL_STATWEN_MASK BIT(7) /* Enable writing to \ + stat counters */ +#define FXMAC_NWCTRL_STATINC_MASK BIT(6) /* Increment statistic \ + registers */ +#define FXMAC_NWCTRL_STATCLR_MASK BIT(5) /* Clear statistic \ + registers */ +#define FXMAC_NWCTRL_MDEN_MASK BIT(4) /* Enable MDIO port */ +#define FXMAC_NWCTRL_TXEN_MASK BIT(3) /* Enable transmit */ +#define FXMAC_NWCTRL_RXEN_MASK BIT(2) /* Enable receive */ +#define FXMAC_NWCTRL_LOOPEN_MASK BIT(1) /* local loopback */ + +/* External address match enable */ + +#define FXMAC_NWCFG_SGMII_MODE_ENABLE_MASK BIT(27) /* SGMII mode enable */ + +#define FXMAC_NWCFG_BUS_WIDTH_32_MASK (0U << 21) +#define FXMAC_NWCFG_BUS_WIDTH_64_MASK (1U << 21) +#define FXMAC_NWCFG_BUS_WIDTH_128_MASK (2U << 21) + +#define FXMAC_NWCFG_CLOCK_DIV224_MASK (7U << 18) +#define FXMAC_NWCFG_CLOCK_DIV128_MASK (6U << 18) +#define FXMAC_NWCFG_CLOCK_DIV96_MASK (5U << 18) +#define FXMAC_NWCFG_CLOCK_DIV64_MASK (4U << 18) +#define FXMAC_NWCFG_CLOCK_DIV48_MASK (3U << 18) +#define FXMAC_NWCFG_CLOCK_DIV32_MASK (2U << 18) +#define FXMAC_NWCFG_CLOCK_DIV16_MASK (1U << 18) +#define FXMAC_NWCFG_CLOCK_DIV8_MASK (0U << 18) + +#define FXMAC_NWCFG_FCS_REMOVE BIT(17) /* FCS remove - setting this bit will cause received frames to be written to memory without their frame check sequence (last 4 bytes). */ +#define FXMAC_NWCFG_PAUSE_ENABLE BIT(13) /* Pause enable - when set, transmission will pause if a non-zero 802.3 classic pause frame is received and PFC has not been negotiated. */ +#define FXMAC_NWCFG_PCSSEL_MASK BIT(11) /* PCS Select */ +#define FXMAC_NWCFG_1000_MASK BIT(10) /* Gigabit mode enable */ +#define FXMAC_NWCFG_1536RXEN_MASK BIT(8) /* Enable 1536 byte \ + frames reception */ +#define FXMAC_NWCFG_UCASTHASHEN_MASK BIT(7) /* Receive unicast hash \ + frames */ +#define FXMAC_NWCFG_MCASTHASHEN_MASK BIT(6) /* Receive multicast hash \ + frames */ +#define FXMAC_NWCFG_BCASTDI_MASK BIT(5) /* Do not receive \ + broadcast frames */ +#define FXMAC_NWCFG_COPYALLEN_MASK BIT(4) /* Copy all frames */ +#define FXMAC_NWCFG_JUMBO_MASK BIT(3) /* Jumbo frames */ +#define FXMAC_NWCFG_NVLANDISC_MASK BIT(2) /* Receive only VLAN \ + frames */ +#define FXMAC_NWCFG_FDEN_MASK BIT(1) /* full duplex */ +#define FXMAC_NWCFG_100_MASK BIT(0) /* 100 Mbps */ +#define FXMAC_NWCFG_RESET_MASK BIT(19) /* reset value */ + +/* Receive buffer descriptor status words bit positions. + * Receive buffer descriptor consists of two 32-bit registers, + * the first - word0 contains a 32-bit word aligned address pointing to the + * address of the buffer. The lower two bits make up the wrap bit indicating + * the last descriptor and the ownership bit to indicate it has been used by + * the xmac. + * The following register - word1, contains status information regarding why + * the frame was received (the filter match condition) as well as other + * useful info. + * @{ + */ +#define FXMAC_RXBUF_BCAST_MASK BIT(31) /* Broadcast frame */ +#define FXMAC_RXBUF_MULTIHASH_MASK BIT(30) /* Multicast hashed frame */ +#define FXMAC_RXBUF_UNIHASH_MASK BIT(29) /* Unicast hashed frame */ +#define FXMAC_RXBUF_EXH_MASK BIT(27) /* buffer exhausted */ +#define FXMAC_RXBUF_AMATCH_MASK GENMASK(26, 25) /* Specific address \ + matched */ +#define FXMAC_RXBUF_IDFOUND_MASK BIT(24) /* Type ID matched */ +#define FXMAC_RXBUF_IDMATCH_MASK GENMASK(23, 22) /* ID matched mask */ +#define FXMAC_RXBUF_VLAN_MASK BIT(21) /* VLAN tagged */ +#define FXMAC_RXBUF_PRI_MASK BIT(20) /* Priority tagged */ +#define FXMAC_RXBUF_VPRI_MASK GENMASK(19, 17) /* Vlan priority */ +#define FXMAC_RXBUF_CFI_MASK BIT(16) /* CFI frame */ +#define FXMAC_RXBUF_EOF_MASK BIT(15) /* End of frame. */ +#define FXMAC_RXBUF_SOF_MASK BIT(14) /* Start of frame. */ +#define FXMAC_RXBUF_FCS_STATUS_MASK BIT(13) /* Status of fcs. */ +#define FXMAC_RXBUF_LEN_MASK GENMASK(12, 0) /* Mask for length field */ +#define FXMAC_RXBUF_LEN_JUMBO_MASK GENMASK(13, 0) /* Mask for jumbo length */ + +#define FXMAC_RXBUF_WRAP_MASK BIT(1) /* Wrap bit, last BD */ +#define FXMAC_RXBUF_NEW_MASK BIT(0) /* Used bit.. */ +#define FXMAC_RXBUF_ADD_MASK GENMASK(31, 2) /* Mask for address */ +/* + * @} + */ + +/* Transmit buffer descriptor status words bit positions. + * Transmit buffer descriptor consists of two 32-bit registers, + * the first - word0 contains a 32-bit address pointing to the location of + * the transmit data. + * The following register - word1, consists of various information to control + * the xmac transmit process. After transmit, this is updated with status + * information, whether the frame was transmitted OK or why it had failed. + * @{ + */ +#define FXMAC_TXBUF_USED_MASK BIT(31) /* Used bit. */ +#define FXMAC_TXBUF_WRAP_MASK BIT(30) /* Wrap bit, last descriptor */ +#define FXMAC_TXBUF_RETRY_MASK BIT(29) /* Retry limit exceeded */ +#define FXMAC_TXBUF_URUN_MASK BIT(28) /* Transmit underrun occurred */ +#define FXMAC_TXBUF_EXH_MASK BIT(27) /* Buffers exhausted */ +#define FXMAC_TXBUF_TCP_MASK BIT(26) /* Late collision. */ +#define FXMAC_TXBUF_NOCRC_MASK BIT(16) /* No CRC */ +#define FXMAC_TXBUF_LAST_MASK BIT(15) /* Last buffer */ +#define FXMAC_TXBUF_LEN_MASK GENMASK(13, 0) /* Mask for length field */ +/* + * @} + */ + +/** @name network configuration register bit definitions + * @{ + */ +#define FXMAC_NWCFG_BADPREAMBEN_MASK BIT(29) /* disable rejection of \ + non-standard preamble */ +#define FXMAC_NWCFG_IPDSTRETCH_MASK BIT(28) /* enable transmit IPG */ +#define FXMAC_NWCFG_SGMIIEN_MASK BIT(27) /* SGMII Enable */ +#define FXMAC_NWCFG_FCSIGNORE_MASK BIT(26) /* disable rejection of \ + FCS error */ +#define FXMAC_NWCFG_HDRXEN_MASK BIT(25) /* RX half duplex */ +#define FXMAC_NWCFG_RXCHKSUMEN_MASK BIT(24) /* enable RX checksum \ + offload */ +#define FXMAC_NWCFG_PAUSECOPYDI_MASK BIT(23) /* Do not copy pause \ + Frames to memory */ +#define FXMAC_NWCFG_DWIDTH_64_MASK BIT(21) /* 64 bit Data bus width */ +#define FXMAC_NWCFG_MDC_SHIFT_MASK 18U /* shift bits for MDC */ +#define FXMAC_NWCFG_MDCCLKDIV_MASK GENMASK(20, 18) /* MDC Mask PCLK divisor */ +#define FXMAC_NWCFG_FCSREM_MASK BIT(17) /* Discard FCS from \ + received frames */ +#define FXMAC_NWCFG_LENERRDSCRD_MASK BIT(16) +/* RX length error discard */ +#define FXMAC_NWCFG_RXOFFS_MASK GENMASK(15) /* RX buffer offset */ +#define FXMAC_NWCFG_PAUSEEN_MASK BIT(13) /* Enable pause RX */ +#define FXMAC_NWCFG_RETRYTESTEN_MASK BIT(12) /* Retry test */ +#define FXMAC_NWCFG_XTADDMACHEN_MASK BIT(9) +#define FXMAC_NWCFG_LOOPBACK_LOCAL_MASK BIT(1) /* Loopback local */ + +/* External address match enable */ + +/** + * @name receive status register bit definitions + * @{ + */ +#define FXMAC_RXSR_HRESPNOK_MASK BIT(3) /* Receive hresp not OK */ +#define FXMAC_RXSR_RXOVR_MASK BIT(2) /* Receive overrun */ +#define FXMAC_RXSR_FRAMERX_MASK BIT(1) /* Frame received OK */ +#define FXMAC_RXSR_BUFFNA_MASK BIT(0) /* RX buffer used bit set */ + +#define FXMAC_RXSR_ERROR_MASK ((u32)FXMAC_RXSR_HRESPNOK_MASK | \ + (u32)FXMAC_RXSR_RXOVR_MASK | \ + (u32)FXMAC_RXSR_BUFFNA_MASK) + +#define FXMAC_SR_ALL_MASK GENMASK(31, 0) /* Mask for full register */ + +/** @name DMA control register bit definitions + * @{ + */ +#define FXMAC_DMACR_ADDR_WIDTH_64 BIT(30) /* 64 bit address bus */ +#define FXMAC_DMACR_TXEXTEND_MASK BIT(29) /* Tx Extended desc mode */ +#define FXMAC_DMACR_RXEXTEND_MASK BIT(28) /* Rx Extended desc mode */ +#define FXMAC_DMACR_ORCE_DISCARD_ON_ERR_MASK BIT(24) /* Auto Discard RX frames during lack of resource. */ +#define FXMAC_DMACR_RXBUF_MASK GENMASK(23, 16) /* Mask bit for RX buffer \ + size */ +#define FXMAC_DMACR_RXBUF_SHIFT 16U /* Shift bit for RX buffer \ + size */ +#define FXMAC_DMACR_TCPCKSUM_MASK BIT(11) /* enable/disable TX \ + checksum offload */ +#define FXMAC_DMACR_TXSIZE_MASK BIT(10) /* TX buffer memory size bit[10] */ +#define FXMAC_DMACR_RXSIZE_MASK GENMASK(9, 8) /* RX buffer memory size bit[9:8] */ +#define FXMAC_DMACR_ENDIAN_MASK BIT(7) /* endian configuration */ +#define FXMAC_DMACR_SWAP_MANAGEMENT_MASK BIT(6) /* When clear, selects little endian mode */ +#define FXMAC_DMACR_BLENGTH_MASK GENMASK(4, 0) /* buffer burst length */ +#define FXMAC_DMACR_SINGLE_AHB_AXI_BURST BIT(0) /* single AHB_AXI bursts */ +#define FXMAC_DMACR_INCR4_AHB_AXI_BURST BIT(2) /* 4 bytes AHB_AXI bursts */ +#define FXMAC_DMACR_INCR8_AHB_AXI_BURST BIT(3) /* 8 bytes AHB_AXI bursts */ +#define FXMAC_DMACR_INCR16_AHB_AXI_BURST BIT(4) /* 16 bytes AHB_AXI bursts */ + +/* This register indicates module identification number and module revision. */ + +#define FXMAC_REVISION_MODULE_MASK GENMASK(15, 0) /* Module revision */ +#define FXMAC_IDENTIFICATION_MASK GENMASK(27, 16) /* Module identification number */ +#define FXMAC_FIX_NUM_MASK GENMASK(31, 28) /* Fix number - incremented for fix releases */ + +/** @name network status register bit definitaions + * @{ + */ +#define FXMAC_NWSR_MDIOIDLE_MASK BIT(2) /* PHY management idle */ +#define FXMAC_NWSR_MDIO_MASK BIT(1) /* Status of mdio_in */ +#define FXMAC_NWSR_PCS_LINK_STATE_MASK BIT(0) + +/** @name PHY Maintenance bit definitions + * @{ + */ +#define FXMAC_PHYMNTNC_OP_MASK (BIT(17) | BIT(30)) /* operation mask bits */ +#define FXMAC_PHYMNTNC_OP_R_MASK BIT(29) /* read operation */ +#define FXMAC_PHYMNTNC_OP_W_MASK BIT(28) /* write operation */ +#define FXMAC_PHYMNTNC_ADDR_MASK GENMASK(27, 23) /* Address bits */ +#define FXMAC_PHYMNTNC_REG_MASK GENMASK(22, 18) /* register bits */ +#define FXMAC_PHYMNTNC_DATA_MASK GENMASK(11, 0) /* data bits */ +#define FXMAC_PHYMNTNC_PHAD_SHFT_MSK 23U /* Shift bits for PHYAD */ +#define FXMAC_PHYMNTNC_PREG_SHFT_MSK 18U /* Shift bits for PHREG */ + +/** @name transmit status register bit definitions + * @{ + */ +#define FXMAC_TXSR_HRESPNOK_MASK BIT(8) /* Transmit hresp not OK */ +#define FXMAC_TXSR_URUN_MASK BIT(6) /* Transmit underrun */ +#define FXMAC_TXSR_TXCOMPL_MASK BIT(5) /* Transmit completed OK */ +#define FXMAC_TXSR_BUFEXH_MASK BIT(4) /* Transmit buffs exhausted \ + mid frame */ +#define FXMAC_TXSR_TXGO_MASK BIT(3) /* Status of go flag */ +#define FXMAC_TXSR_RXOVR_MASK BIT(2) /* Retry limit exceeded */ +#define FXMAC_TXSR_FRAMERX_MASK BIT(1) /* Collision tx frame */ +#define FXMAC_TXSR_USEDREAD_MASK BIT(0) /* TX buffer used bit set */ + +#define FXMAC_TXSR_ERROR_MASK ((u32)FXMAC_TXSR_HRESPNOK_MASK | \ + (u32)FXMAC_TXSR_URUN_MASK | \ + (u32)FXMAC_TXSR_BUFEXH_MASK | \ + (u32)FXMAC_TXSR_RXOVR_MASK | \ + (u32)FXMAC_TXSR_FRAMERX_MASK | \ + (u32)FXMAC_TXSR_USEDREAD_MASK) + +/** + * @name Interrupt Q1 status register bit definitions + * @{ + */ +#define FXMAC_INTQ1SR_TXCOMPL_MASK BIT(7) /* Transmit completed OK */ +#define FXMAC_INTQ1SR_TXERR_MASK BIT(6) /* Transmit AMBA Error */ + +#define FXMAC_INTQ1_IXR_ALL_MASK ((u32)FXMAC_INTQ1SR_TXCOMPL_MASK | \ + (u32)FXMAC_INTQ1SR_TXERR_MASK) + +/** + * @name Interrupt QUEUE status register bit definitions + * @{ + */ +#define FXMAC_INTQUESR_TXCOMPL_MASK BIT(7) /* Transmit completed OK */ +#define FXMAC_INTQUESR_TXERR_MASK BIT(6) /* Transmit AMBA Error */ +#define FXMAC_INTQUESR_RCOMP_MASK BIT(1) +#define FXMAC_INTQUESR_RXUBR_MASK BIT(2) + +#define FXMAC_INTQUE_IXR_ALL_MASK ((u32)FXMAC_INTQUESR_TXCOMPL_MASK | \ + (u32)FXMAC_INTQUESR_TXERR_MASK) + +#define FXMAC_QUEUE_REGISTER_OFFSET(base_addr, queue_id) ((u32)base_addr + (queue_id - 1) * 4) + +/* Design Configuration Register 1 - The GEM has many parameterisation options to configure the IP during compilation stage. */ + +#define FXMAC_DESIGNCFG_DEBUG1_BUS_WIDTH_MASK GENMASK(27, 25) + +/*GEM hs mac config register bitfields*/ +#define FXMAC_GEM_HSMACSPEED_OFFSET 0 +#define FXMAC_GEM_HSMACSPEED_SIZE 3 +#define FXMAC_GEM_HSMACSPEED_MASK 0x7 + +/* Transmit buffer descriptor status words offset + * @{ + */ +#define FXMAC_BD_ADDR_OFFSET 0x00000000U /* word 0/addr of BDs */ +#define FXMAC_BD_STAT_OFFSET 4 /* word 1/status of BDs, 4 bytes */ +#define FXMAC_BD_ADDR_HI_OFFSET BIT(3) /* word 2/addr of BDs */ + +/** @name MAC address register word 1 mask + * @{ + */ +#define FXMAC_GEM_SAB_MASK GENMASK(15, 0) /* Address bits[47:32] \ + bit[31:0] are in BOTTOM */ + +/* USXGMII control register */ +#define FXMAC_GEM_USX_HS_MAC_SPEED_100M (0x0 << 14) /* 100M operation */ +#define FXMAC_GEM_USX_HS_MAC_SPEED_1G (0x1 << 14) /* 1G operation */ +#define FXMAC_GEM_USX_HS_MAC_SPEED_2_5G (0x2 << 14) /* 2.5G operation */ +#define FXMAC_GEM_USX_HS_MAC_SPEED_10G (0x4 << 14) /* 10G operation */ +#define FXMAC_GEM_USX_TX_SCR_BYPASS BIT(8) /* RX Scrambler Bypass. Set high to bypass the receive descrambler. */ +#define FXMAC_GEM_USX_RX_SCR_BYPASS BIT(9) /* TX Scrambler Bypass. Set high to bypass the transmit scrambler. */ +#define FXMAC_GEM_USX_RX_SYNC_RESET BIT(2) /* RX Reset. Set high to reset the receive datapath. When low the receive datapath is enabled. */ +#define FXMAC_GEM_USX_TX_DATAPATH_EN BIT(1) /* TX Datapath Enable. */ +#define FXMAC_GEM_USX_SIGNAL_OK BIT(0) /* Enable the USXGMII/BASE-R receive PCS. */ + +/* All PCS registers */ + +#define FXMAC_PCS_CONTROL_ENABLE_AUTO_NEG BIT(12) /* Enable auto-negotiation - when set active high, autonegotiation operation is enabled. */ + + +/* FXMAC_PCS_STATUS_OFFSET */ +#define FXMAC_PCS_STATUS_LINK_STATUS_OFFSET 2 +#define FXMAC_PCS_STATUS_LINK_STATUS BIT(FXMAC_PCS_STATUS_LINK_STATUS_OFFSET) /* Link status - indicates the status of the physical connection to the link partner. When set to logic 1 the link is up, and when set to logic 0, the link is down. */ + +/* FXMAC_PCS_AN_LP_OFFSET */ + +#define FXMAC_PCS_AN_LP_SPEED_OFFSET 10 +#define FXMAC_PCS_AN_LP_SPEED (0x3U << FXMAC_PCS_AN_LP_SPEED_OFFSET) /* SGMII 11 : Reserved 10 : 1000 Mbps 01 : 100Mbps 00 : 10 Mbps */ +#define FXMAC_PCS_AN_LP_DUPLEX_OFFSET 12 +#define FXMAC_PCS_AN_LP_DUPLEX (0x3U << FXMAC_PCS_AN_LP_DUPLEX_OFFSET) /* SGMII Bit 13: Reserved. read as 0. Bit 12 : 0 : half-duplex. 1: Full Duplex." */ +#define FXMAC_PCS_LINK_PARTNER_NEXT_PAGE_STATUS (1U<<15) /* In sgmii mode, 0 is link down . 1 is link up */ + +/***************** Macros (Inline Functions) Definitions *********************/ + +#define FXMAC_READREG32(addr, reg_offset) FtIn32(addr + (u32)reg_offset) +#define FXMAC_WRITEREG32(addr, reg_offset, reg_value) FtOut32(addr + (u32)reg_offset, (u32)reg_value) + + +#define FXMAC_SetBit32(addr, reg_offset, reg_value) FtSetBit32(addr + (u32)reg_offset, (u32)reg_value) +#define FXMAC_ClearBit32(addr, reg_offset, reg_value) FtClearBit32(addr + (u32)reg_offset, (u32)reg_value) + +/****************************************************************************/ +/** + * + * Enable interrupts specified in Mask. The corresponding interrupt for + * each bit set to 1 in Mask, will be enabled. + * + * @param instance_p is a pointer to the instance to be worked on. + * @param Mask contains a bit mask of interrupts to enable. The mask can + * be formed using a set of bitwise or'd values. + * + * @note + * The state of the transmitter and receiver are not modified by this function. + * C-style signature + * void FXMAC_INT_ENABLE(FXmac *instance_p, u32 Mask) + * + *****************************************************************************/ +#define FXMAC_INT_ENABLE(instance_p, Mask) \ + FXMAC_WRITEREG32((instance_p)->config.base_address, \ + FXMAC_IER_OFFSET, \ + ((Mask)&FXMAC_IXR_ALL_MASK)); + +/****************************************************************************/ +/** + * + * Enable interrupts specified in Mask. The corresponding interrupt for + * each bit set to 1 in Mask, will be enabled. + * + * @param instance_p is a pointer to the instance to be worked on. + * @param Mask contains a bit mask of interrupts to enable. The mask can + * be formed using a set of bitwise or'd values. + * + * @note + * The state of the transmitter and receiver are not modified by this function. + * C-style signature + * void FXMAC_INT_Q1ENABLE(FXmac *instance_p, u32 Mask) + * + *****************************************************************************/ +#define FXMAC_INT_Q1ENABLE(instance_p, Mask) \ + FXMAC_WRITEREG32((instance_p)->config.base_address, \ + FXMAC_INTQ1_IER_OFFSET, \ + ((Mask)&FXMAC_INTQ1_IXR_ALL_MASK)); + + +#ifdef __cplusplus +} +#endif + +#endif // ! diff --git a/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_intr.c b/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_intr.c new file mode 100644 index 0000000000..08c226740c --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_intr.c @@ -0,0 +1,403 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fxmac_intr.c + * Date: 2022-04-06 14:46:52 + * LastEditTime: 2022-04-06 14:46:58 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "fxmac.h" +#include "fxmac_hw.h" +#include "fassert.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + + +/** + * @name: FXmacSetHandler + * @msg: Install an asynchronous handler function for the given handler_type: + * + * @param instance_p is a pointer to the instance to be worked on. + * @param handler_type indicates what interrupt handler type is. + * FXMAC_HANDLER_DMASEND, FXMAC_HANDLER_DMARECV and + * FXMAC_HANDLER_ERROR. + * @param func_pointer is the pointer to the callback function + * @param call_back_ref is the upper layer callback reference passed back when + * when the callback function is invoked. + * + * @return {FError} FT_SUCCESS set is ok + */ +FError FXmacSetHandler(FXmac *instance_p, u32 handler_type, + void *func_pointer, void *call_back_ref) +{ + FError status; + FASSERT(instance_p != NULL); + FASSERT(func_pointer != NULL); + FASSERT(instance_p->is_ready == (u32)FT_COMPONENT_IS_READY); + status = (FError)(FT_SUCCESS); + + switch (handler_type) + { + case FXMAC_HANDLER_DMASEND: + instance_p->send_irq_handler = ((FXmacIrqHandler)(void *)func_pointer); + instance_p->send_args = call_back_ref; + break; + case FXMAC_HANDLER_DMARECV: + instance_p->recv_irq_handler = ((FXmacIrqHandler)(void *)func_pointer); + instance_p->recv_args = call_back_ref; + break; + case FXMAC_HANDLER_ERROR: + instance_p->error_irq_handler = ((FXmacErrorIrqHandler)(void *)func_pointer); + instance_p->error_args = call_back_ref; + break; + case FXMAC_HANDLER_LINKCHANGE: + instance_p->link_change_handler = ((FXmacIrqHandler)(void *)func_pointer); + instance_p->link_change_args = call_back_ref; + break; + case FXMAC_HANDLER_RESTART: + instance_p->restart_handler = ((FXmacIrqHandler)(void *)func_pointer); + instance_p->restart_args = call_back_ref; + break; + default: + status = (FError)(FXMAC_ERR_INVALID_PARAM); + break; + } + return status; +} + + +/** + * @name: FXmacIntrHandler + * @msg: 中断处理函数 + * @param {s32} vector is interrrupt num + * @param {void} *args is a arguments variables + * @return {*} + * @note 目前中断只支持单queue的情况 + */ +void FXmacIntrHandler(s32 vector, void *args) +{ + u32 reg_isr; + u32 reg_qx_isr; + u32 reg_temp; + u32 reg_ctrl; + u32 tx_queue_id; /* 0 ~ FT_XMAC_QUEUE_MAX_NUM ,Index queue number */ + u32 rx_queue_id; /* 0 ~ FT_XMAC_QUEUE_MAX_NUM ,Index queue number */ + FXmac *instance_p = (FXmac *)args; + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == (u32)FT_COMPONENT_IS_READY); + + tx_queue_id = instance_p->tx_bd_queue.queue_id; + rx_queue_id = instance_p->rx_bd_queue.queue_id; + FASSERT((rx_queue_id < FT_XMAC_QUEUE_MAX_NUM) && (tx_queue_id < FT_XMAC_QUEUE_MAX_NUM)) + + /* This ISR will try to handle as many interrupts as it can in a single + * call. However, in most of the places where the user's error handler + * is called, this ISR exits because it is expected that the user will + * reset the device in nearly all instances. + */ + + if ((u32)vector == instance_p->config.queue_irq_num[tx_queue_id]) + { + if (tx_queue_id == 0) + { + reg_isr = FXMAC_READREG32(instance_p->config.base_address, FXMAC_ISR_OFFSET); + + if ((reg_isr & FXMAC_IXR_TXCOMPL_MASK) != 0x00000000U) + { + /* Clear TX status register TX complete indication but preserve + * error bits if there is any */ + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_TXSR_OFFSET, + ((u32)FXMAC_TXSR_TXCOMPL_MASK | + (u32)FXMAC_TXSR_USEDREAD_MASK)); + + if (instance_p->send_irq_handler) + { + /* code */ + instance_p->send_irq_handler(instance_p->send_args); + } + + /* add */ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_ISR_OFFSET, FXMAC_IXR_TXCOMPL_MASK); + } + + /* Transmit error conditions interrupt */ + if (((reg_isr & FXMAC_IXR_TX_ERR_MASK) != 0x00000000U) && + (!(reg_isr & FXMAC_IXR_TXCOMPL_MASK) != 0x00000000U)) + { + /* Clear TX status register */ + reg_temp = FXMAC_READREG32(instance_p->config.base_address, FXMAC_TXSR_OFFSET); + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_TXSR_OFFSET, reg_temp); + if (instance_p->error_irq_handler) + { + instance_p->error_irq_handler(instance_p->error_args, FXMAC_SEND, reg_temp); + } + /* add */ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_ISR_OFFSET, FXMAC_IXR_TX_ERR_MASK); + } + + /* add restart */ + if ((reg_isr & FXMAC_IXR_TXUSED_MASK) != 0x00000000U) + { + /* add */ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_ISR_OFFSET, FXMAC_IXR_TXUSED_MASK); + + if (instance_p->restart_handler) + { + instance_p->restart_handler(instance_p->restart_args); + } + } + + /* link chaged */ + if ((reg_isr & FXMAC_IXR_LINKCHANGE_MASK) != 0x00000000U) + { + if (instance_p->link_change_handler) + { + instance_p->link_change_handler(instance_p->link_change_args); + } + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_ISR_OFFSET, FXMAC_IXR_LINKCHANGE_MASK); + } + } + else /* use queue number more than 0 */ + { + reg_isr = FXMAC_READREG32(instance_p->config.base_address, + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_INTQ1_STS_OFFSET, tx_queue_id)); + + /* Transmit Q1 complete interrupt */ + if (((reg_isr & FXMAC_INTQUESR_TXCOMPL_MASK) != 0x00000000U)) + { + /* Clear TX status register TX complete indication but preserve + * error bits if there is any */ + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_INTQ1_STS_OFFSET, tx_queue_id), + FXMAC_INTQUESR_TXCOMPL_MASK); + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_TXSR_OFFSET, + ((u32)FXMAC_TXSR_TXCOMPL_MASK | + (u32)FXMAC_TXSR_USEDREAD_MASK)); + instance_p->send_irq_handler(instance_p->send_args); + } + + /* Transmit Q1 error conditions interrupt */ + if (((reg_isr & FXMAC_INTQ1SR_TXERR_MASK) != 0x00000000U) && + ((reg_isr & FXMAC_INTQ1SR_TXCOMPL_MASK) != 0x00000000U)) + { + /* Clear Interrupt Q1 status register */ + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_INTQ1_STS_OFFSET, tx_queue_id), reg_isr); + instance_p->error_irq_handler(instance_p->error_args, FXMAC_SEND, + reg_isr); + } + } + } + + if ((u32)vector == instance_p->config.queue_irq_num[rx_queue_id]) + { + if (rx_queue_id == 0) + { + reg_isr = FXMAC_READREG32(instance_p->config.base_address, + FXMAC_ISR_OFFSET); + + /* Receive complete interrupt */ + if ((reg_isr & FXMAC_IXR_RXCOMPL_MASK) != 0x00000000U) + { + /* Clear RX status register RX complete indication but preserve + * error bits if there is any */ + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_RXSR_OFFSET, + ((u32)FXMAC_RXSR_FRAMERX_MASK | + (u32)FXMAC_RXSR_BUFFNA_MASK)); + instance_p->recv_irq_handler(instance_p->recv_args); + + /* add */ + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_ISR_OFFSET, FXMAC_IXR_RXCOMPL_MASK); + + } + + /* Receive error conditions interrupt */ + if ((reg_isr & FXMAC_IXR_RX_ERR_MASK) != 0x00000000U) + { + /* Clear RX status register */ + reg_temp = FXMAC_READREG32(instance_p->config.base_address, + FXMAC_RXSR_OFFSET); + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_RXSR_OFFSET, reg_temp); + + /* Fix for CR # 692702. Write to bit 18 of net_ctrl + * register to flush a packet out of Rx SRAM upon + * an error for receive buffer not available. */ + if ((reg_isr & FXMAC_IXR_RXUSED_MASK) != 0x00000000U) + { + reg_ctrl = FXMAC_READREG32(instance_p->config.base_address, + FXMAC_NWCTRL_OFFSET); + reg_ctrl |= (u32)FXMAC_NWCTRL_FLUSH_DPRAM_MASK; + + /* add */ + reg_ctrl &= (u32)(~FXMAC_NWCTRL_RXEN_MASK); + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_NWCTRL_OFFSET, reg_ctrl); + + /* add */ + reg_ctrl |= (u32)FXMAC_NWCTRL_RXEN_MASK; + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_NWCTRL_OFFSET, reg_ctrl); + } + + /* add */ + if ((reg_isr & FXMAC_IXR_RXOVR_MASK) != 0x00000000U) + { + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_ISR_OFFSET, FXMAC_IXR_RXOVR_MASK); + } + + /* add */ + if ((reg_isr & FXMAC_IXR_HRESPNOK_MASK) != 0x00000000U) + { + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_ISR_OFFSET, FXMAC_IXR_HRESPNOK_MASK); + } + + if (reg_temp != 0) + { + instance_p->error_irq_handler(instance_p->error_args, + FXMAC_RECV, reg_temp); + } + } + } + else /* use queue number more than 0 */ + { + reg_isr = FXMAC_READREG32(instance_p->config.base_address, + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_INTQ1_STS_OFFSET, rx_queue_id)); + + /* Receive complete interrupt */ + if ((reg_isr & FXMAC_INTQUESR_RCOMP_MASK) != 0x00000000U) + { + /* Clear RX status register RX complete indication but preserve + * error bits if there is any */ + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_INTQ1_STS_OFFSET, rx_queue_id), + FXMAC_INTQUESR_RCOMP_MASK); + instance_p->recv_irq_handler(instance_p->recv_args); + } + + /* Receive error conditions interrupt */ + if ((reg_isr & FXMAC_IXR_RX_ERR_MASK) != 0x00000000U) + { + + reg_ctrl = + FXMAC_READREG32(instance_p->config.base_address, + FXMAC_NWCTRL_OFFSET); + reg_ctrl &= ~(u32)FXMAC_NWCTRL_RXEN_MASK; + + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_NWCTRL_OFFSET, reg_ctrl); + + /* Clear RX status register */ + reg_temp = FXMAC_READREG32(instance_p->config.base_address, + FXMAC_RXSR_OFFSET); + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_RXSR_OFFSET, reg_temp); + + /* Fix for CR # 692702. Write to bit 18 of net_ctrl + * register to flush a packet out of Rx SRAM upon + * an error for receive buffer not available. */ + if ((reg_isr & FXMAC_IXR_RXUSED_MASK) != 0x00000000U) + { + reg_ctrl = + FXMAC_READREG32(instance_p->config.base_address, + FXMAC_NWCTRL_OFFSET); + reg_ctrl |= (u32)FXMAC_NWCTRL_FLUSH_DPRAM_MASK; + + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_NWCTRL_OFFSET, reg_ctrl); + } + + /* Clear RX status register RX complete indication but preserve + * error bits if there is any */ + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_QUEUE_REGISTER_OFFSET(FXMAC_INTQ1_STS_OFFSET, rx_queue_id), + FXMAC_INTQUESR_RXUBR_MASK); + instance_p->recv_irq_handler(instance_p->recv_args); + + if (reg_temp != 0) + { + instance_p->error_irq_handler(instance_p->error_args, + FXMAC_RECV, reg_temp); + } + } + } + } + +} + + +/** + * @name: FXmacQueueIrqDisable + * @msg: Disable queue irq + * @param {FXmac} *instance_p a pointer to the instance to be worked on. + * @param {u32} queue_num queue number + * @param {u32} mask is interrupt disable value mask + */ +void FXmacQueueIrqDisable(FXmac *instance_p, u32 queue_num, u32 mask) +{ + FXmacConfig *config_p; + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == (u32)FT_COMPONENT_IS_READY); + FASSERT(instance_p->config.max_queue_num > queue_num); + config_p = &instance_p->config; + + if (queue_num == 0) + { + FXMAC_WRITEREG32(config_p->base_address, FXMAC_IDR_OFFSET, mask & FXMAC_IXR_ALL_MASK); + } + else + { + FXMAC_WRITEREG32(config_p->base_address, FXMAC_INTQX_IDR_SIZE_OFFSET(queue_num), mask & FXMAC_IXR_ALL_MASK); + } +} + +/** + * @name: FXmacQueueIrqEnable + * @msg: Enable queue irq + * @param {FXmac} *instance_p a pointer to the instance to be worked on. + * @param {u32} queue_num is queue number + * @param {u32} mask is interrupt Enable value mask + */ +void FXmacQueueIrqEnable(FXmac *instance_p, u32 queue_num, u32 mask) +{ + FXmacConfig *config_p; + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == (u32)FT_COMPONENT_IS_READY); + FASSERT(instance_p->config.max_queue_num > queue_num); + config_p = &instance_p->config; + + if (queue_num == 0) + { + FXMAC_WRITEREG32(config_p->base_address, FXMAC_IER_OFFSET, mask & FXMAC_IXR_ALL_MASK); + } + else + { + FXMAC_WRITEREG32(config_p->base_address, FXMAC_INTQX_IER_SIZE_OFFSET(queue_num), mask & FXMAC_IXR_ALL_MASK); + } +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_options.c b/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_options.c new file mode 100644 index 0000000000..c4e4b0d570 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_options.c @@ -0,0 +1,745 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fxmac_options.c + * Date: 2022-04-06 14:46:52 + * LastEditTime: 2022-04-06 14:46:58 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "fxmac_hw.h" +#include "fxmac.h" +#include "fassert.h" + + +/** + * @name: FXmacSetMacAddress + * + * @msg: Set the MAC address for this driver/device. The address is a 48-bit value. + * The device must be stopped before calling this function. + * + * @param {FXmac *}: instance_p is a pointer to the instance to be worked on. + * @param address_ptr is a pointer to a 6-byte MAC address. + * @param index plus 1 is a index to which MAC (1-4) address. + * + * @return + * - FT_SUCCESS if the MAC address was set successfully + * - FXMAC_ERR_MAC_IS_PROCESSING if the device has not yet been stopped + * + */ +FError FXmacSetMacAddress(FXmac *instance_p, u8 *address_ptr, u8 index) +{ + u32 mac_addr; + u8 *aptr = (u8 *)(void *)address_ptr; + u8 index_loc = index; + FError status; + FASSERT(instance_p != NULL); + FASSERT(aptr != NULL); + FASSERT(instance_p->is_ready == (u32)FT_COMPONENT_IS_READY); + FASSERT((index_loc < (u8)FXMAC_MAX_MAC_ADDR)); + + /* Be sure device has been stopped */ + if (instance_p->is_started == (u32)FT_COMPONENT_IS_STARTED) + { + status = (FError)(FXMAC_ERR_MAC_IS_PROCESSING); + } + else + { + /* Set the MAC bits [31:0] in BOT */ + mac_addr = *(aptr); + mac_addr |= ((u32)(*(aptr + 1)) << 8U); + mac_addr |= ((u32)(*(aptr + 2)) << 16U); + mac_addr |= ((u32)(*(aptr + 3)) << 24U); + FXMAC_WRITEREG32(instance_p->config.base_address, + ((u32)FXMAC_GEM_SA1B + ((u32)index_loc * (u32)8)), mac_addr); + + /* There are reserved bits in TOP so don't affect them */ + mac_addr = FXMAC_READREG32(instance_p->config.base_address, + ((u32)FXMAC_GEM_SA1T + ((u32)index_loc * (u32)8))); + + mac_addr &= (u32)(~FXMAC_GEM_SAB_MASK); + + /* Set MAC bits [47:32] in TOP */ + mac_addr |= (u32)(*(aptr + 4)); + mac_addr |= (u32)(*(aptr + 5)) << 8U; + + FXMAC_WRITEREG32(instance_p->config.base_address, + ((u32)FXMAC_GEM_SA1T + ((u32)index_loc * (u32)8)), mac_addr); + + status = (FError)(FT_SUCCESS); + } + return status; +} + +/** + * @name: FXmacGetMacAddress + * @msg: Set the MAC address according to index + * @param {FXmac} *mac is a pointer to the instance to be worked on. + * @param {void} *address_ptr is an output parameter, and is a pointer to a buffer into + * which the current MAC address will be copied. + * @param {u8} index is a index to which MAC (0-3) address. + */ +void FXmacGetMacAddress(FXmac *instance_p, u8 *address_ptr, u8 index) +{ + u32 reg_value; + u8 *ptr = (u8 *)address_ptr; + FASSERT(instance_p != NULL); + FASSERT(ptr != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + FASSERT((index < FXMAC_MAX_MAC_ADDR)); + + reg_value = FXMAC_READREG32(instance_p->config.base_address, FXMAC_GEM_SA1B + ((u32)index * (u32)8)); + *ptr = (u8)reg_value; + *(ptr + 1) = (u8)(reg_value >> 8U); + *(ptr + 2) = (u8)(reg_value >> 16U); + *(ptr + 3) = (u8)(reg_value >> 24U); + + reg_value = FXMAC_READREG32(instance_p->config.base_address, FXMAC_GEM_SA1T + ((u32)index * (u32)8)); + *(ptr + 4) = (u8)(reg_value); + *(ptr + 5) = (u8)(reg_value >> 8U); +} + + +/** + * Set the Type ID match for this driver/device. The register is a 32-bit + * value. The device must be stopped before calling this function. + * + * @param instance_p is a pointer to the instance to be worked on. + * @param id_check is type ID to be configured. + * @param index plus 1 is a index to which Type ID (1-4). + * + * @return + * - FT_SUCCESS if the MAC address was set successfully + * - FXMAC_ERR_MAC_IS_PROCESSING if the device has not yet been stopped + * + */ +FError FXmacSetTypeIdCheck(FXmac *instance_p, u32 id_check, u8 index) +{ + u8 index_loc = index; + FError status; + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + FASSERT((index_loc < (u8)FXMAC_MAX_TYPE_ID)); + + /* Be sure device has been stopped */ + if (instance_p->is_started == (u32)FT_COMPONENT_IS_STARTED) + { + status = (FError)(FXMAC_ERR_MAC_IS_PROCESSING); + } + else + { + + /* Set the ID bits in MATCHx register */ + FXMAC_WRITEREG32(instance_p->config.base_address, + ((u32)FXMAC_MATCH1_OFFSET + ((u32)index_loc * (u32)4)), id_check); + + status = (FError)(FT_SUCCESS); + } + return status; +} + + +/** + * Set options for the driver/device. The driver should be stopped with + * FXmacStop() before changing options. + * + * @param instance_p is a pointer to the instance to be worked on. + * @param options are the options to set. Multiple options can be set by OR'ing + * FXMAC_*_OPTIONS constants together. options not specified are not + * affected. + * @param queue_num is the Buffer Queue Index ,Used for jumbo frames only + * + * @return + * - FT_SUCCESS if the options were set successfully + * - FXMAC_ERR_MAC_IS_PROCESSING if the device has not yet been stopped + * + * @note + * See fxmac.h for a description of the available options. + * + */ +FError FXmacSetOptions(FXmac *instance_p, u32 options, u32 queue_num) +{ + u32 reg; /* Generic register contents */ + u32 reg_netcfg; /* Reflects original contents of NET_CONFIG */ + u32 reg_new_netcfg; /* Reflects new contents of NET_CONFIG */ + FError status; + FXmacConfig *config_p; + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == (u32)FT_COMPONENT_IS_READY); + config_p = &instance_p->config; + /* Be sure device has been stopped */ + if (instance_p->is_started == (u32)FT_COMPONENT_IS_STARTED) + { + status = (FError)(FXMAC_ERR_MAC_IS_PROCESSING); + } + else + { + + /* Many of these options will change the NET_CONFIG registers. + * To reduce the amount of IO to the device, group these options here + * and change them all at once. + */ + + /* Grab current register contents */ + reg_netcfg = FXMAC_READREG32(config_p->base_address, + FXMAC_NWCFG_OFFSET); + reg_new_netcfg = reg_netcfg; + + /* + * It is configured to max 1536. + */ + if ((options & FXMAC_FRAME1536_OPTION) != 0x00000000U) + { + reg_new_netcfg |= (FXMAC_NWCFG_1536RXEN_MASK); + } + + /* Turn on VLAN packet only, only VLAN tagged will be accepted */ + if ((options & FXMAC_VLAN_OPTION) != 0x00000000U) + { + reg_new_netcfg |= FXMAC_NWCFG_NVLANDISC_MASK; + } + + /* Turn on FCS stripping on receive packets */ + if ((options & FXMAC_FCS_STRIP_OPTION) != 0x00000000U) + { + reg_new_netcfg |= FXMAC_NWCFG_FCSREM_MASK; + } + + /* Turn on length/type field checking on receive packets */ + if ((options & FXMAC_LENTYPE_ERR_OPTION) != 0x00000000U) + { + reg_new_netcfg |= FXMAC_NWCFG_LENERRDSCRD_MASK; + } + + /* Turn on flow control */ + if ((options & FXMAC_FLOW_CONTROL_OPTION) != 0x00000000U) + { + reg_new_netcfg |= FXMAC_NWCFG_PAUSEEN_MASK; + } + + /* Turn on promiscuous frame filtering (all frames are received) */ + if ((options & FXMAC_PROMISC_OPTION) != 0x00000000U) + { + reg_new_netcfg |= FXMAC_NWCFG_COPYALLEN_MASK; + } + + /* Allow broadcast address reception */ + if ((options & FXMAC_BROADCAST_OPTION) != 0x00000000U) + { + reg_new_netcfg &= (u32)(~FXMAC_NWCFG_BCASTDI_MASK); + } + + /* Allow multicast address filtering */ + if ((options & FXMAC_MULTICAST_OPTION) != 0x00000000U) + { + reg_new_netcfg |= FXMAC_NWCFG_MCASTHASHEN_MASK; + } + + /* enable RX checksum offload */ + if ((options & FXMAC_RX_CHKSUM_ENABLE_OPTION) != 0x00000000U) + { + reg_new_netcfg |= FXMAC_NWCFG_RXCHKSUMEN_MASK; + } + + /* Enable jumbo frames */ + if ((options & FXMAC_JUMBO_ENABLE_OPTION) != 0x00000000U) + { + instance_p->max_mtu_size = FXMAC_MTU_JUMBO; + instance_p->max_frame_size = FXMAC_MTU_JUMBO + + FXMAC_HDR_SIZE + FXMAC_TRL_SIZE; + + reg_new_netcfg |= FXMAC_NWCFG_JUMBO_MASK; + FXMAC_WRITEREG32(config_p->base_address, + FXMAC_JUMBOMAXLEN_OFFSET, FXMAC_MTU_JUMBO); + + if (queue_num == 0) + { + u32 rx_buf_size = 0; + reg = FXMAC_READREG32(config_p->base_address, + FXMAC_DMACR_OFFSET); + reg &= ~FXMAC_DMACR_RXBUF_MASK; + + rx_buf_size = ((u32)instance_p->max_mtu_size / (u32)FXMAC_RX_BUF_UNIT); + rx_buf_size += (((u32)instance_p->max_mtu_size % (u32)FXMAC_RX_BUF_UNIT) != (u32)0) ? 1U : 0U; + + reg |= ((rx_buf_size << (u32)(FXMAC_DMACR_RXBUF_SHIFT)) & + (u32)(FXMAC_DMACR_RXBUF_MASK)); + FXMAC_WRITEREG32(config_p->base_address, + FXMAC_DMACR_OFFSET, reg); + } + else if (queue_num < instance_p->config.max_queue_num) + { + u32 rx_buf_size = 0; + rx_buf_size = ((u32)instance_p->max_mtu_size / (u32)FXMAC_RX_BUF_UNIT); + rx_buf_size += (((u32)instance_p->max_mtu_size % (u32)FXMAC_RX_BUF_UNIT) != (u32)0) ? 1U : 0U; + + FXMAC_WRITEREG32(config_p->base_address, FXMAC_RXBUFQX_SIZE_OFFSET(queue_num), rx_buf_size & FXMAC_RXBUFQX_SIZE_MASK); + } + } + + if (((options & FXMAC_SGMII_ENABLE_OPTION) != 0x00000000U)) + { + reg_new_netcfg |= (FXMAC_NWCFG_SGMIIEN_MASK | + FXMAC_NWCFG_PCSSEL_MASK); + } + + if ((options & FXMAC_LOOPBACK_NO_MII_OPTION) != 0x00000000U) + { + reg = FXMAC_READREG32(config_p->base_address, FXMAC_NWCTRL_OFFSET); + reg |= FXMAC_NWCFG_LOOPBACK_LOCAL_MASK; + FXMAC_WRITEREG32(config_p->base_address, FXMAC_NWCTRL_OFFSET, reg); + } + + if ((options & FXMAC_LOOPBACK_USXGMII_OPTION) != 0x00000000U) + { + FXMAC_WRITEREG32(config_p->base_address, FXMAC_TEST_CONTROL_OFFSET, 2); + } + + /* Officially change the NET_CONFIG registers if it needs to be + * modified. + */ + if (reg_netcfg != reg_new_netcfg) + { + FXMAC_WRITEREG32(config_p->base_address, + FXMAC_NWCFG_OFFSET, reg_new_netcfg); + } + + /* Enable TX checksum offload */ + if ((options & FXMAC_TX_CHKSUM_ENABLE_OPTION) != 0x00000000U) + { + reg = FXMAC_READREG32(config_p->base_address, + FXMAC_DMACR_OFFSET); + reg |= FXMAC_DMACR_TCPCKSUM_MASK; + FXMAC_WRITEREG32(config_p->base_address, + FXMAC_DMACR_OFFSET, reg); + } + + /* Enable transmitter */ + if ((options & FXMAC_TRANSMITTER_ENABLE_OPTION) != 0x00000000U) + { + reg = FXMAC_READREG32(config_p->base_address, + FXMAC_NWCTRL_OFFSET); + reg |= FXMAC_NWCTRL_TXEN_MASK; + FXMAC_WRITEREG32(config_p->base_address, + FXMAC_NWCTRL_OFFSET, reg); + } + + /* Enable receiver */ + if ((options & FXMAC_RECEIVER_ENABLE_OPTION) != 0x00000000U) + { + reg = FXMAC_READREG32(config_p->base_address, + FXMAC_NWCTRL_OFFSET); + reg |= FXMAC_NWCTRL_RXEN_MASK; + FXMAC_WRITEREG32(config_p->base_address, + FXMAC_NWCTRL_OFFSET, reg); + } + + /* The remaining options not handled here are managed elsewhere in the + * driver. No register modifications are needed at this time. Reflecting + * the option in instance_p->options is good enough for now. + */ + + /* Set options word to its new value */ + instance_p->options |= options; + + status = (FError)(FT_SUCCESS); + } + return status; +} + + +/** + * Clear options for the driver/device + * + * @param instance_p is a pointer to the instance to be worked on. + * @param options are the options to clear. Multiple options can be cleared by + * OR'ing FXMAC_*_options constants together. options not specified + * are not affected. + * @param queue_num is the Buffer Queue Index ,Used for jumbo frames only + * @return + * - FT_SUCCESS if the options were set successfully + * - FXMAC_ERR_MAC_IS_PROCESSING if the device has not yet been stopped + * + * @note + * See fxmac.h for a description of the available options. + */ +FError FXmacClearOptions(FXmac *instance_p, u32 options, u32 queue_num) +{ + u32 reg; /* Generic */ + u32 reg_net_cfg; /* Reflects original contents of NET_CONFIG */ + u32 reg_new_net_cfg; /* Reflects new contents of NET_CONFIG */ + FError status; + FXmacConfig *config_p; + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == (u32)FT_COMPONENT_IS_READY); + config_p = &instance_p->config; + /* Be sure device has been stopped */ + if (instance_p->is_started == (u32)FT_COMPONENT_IS_STARTED) + { + status = (FError)(FXMAC_ERR_MAC_IS_PROCESSING); + } + else + { + /* Many of these options will change the NET_CONFIG registers. + * To reduce the amount of IO to the device, group these options here + * and change them all at once. + */ + /* Grab current register contents */ + reg_net_cfg = FXMAC_READREG32(instance_p->config.base_address, + FXMAC_NWCFG_OFFSET); + reg_new_net_cfg = reg_net_cfg; + /* There is only RX configuration!? + * It is configured in two different length, up to 1536 and 10240 bytes + */ + if ((options & FXMAC_FRAME1536_OPTION) != 0x00000000U) + { + reg_new_net_cfg &= (u32)(~FXMAC_NWCFG_1536RXEN_MASK); + } + + /* Turn off VLAN packet only */ + if ((options & FXMAC_VLAN_OPTION) != 0x00000000U) + { + reg_new_net_cfg &= (u32)(~FXMAC_NWCFG_NVLANDISC_MASK); + } + + /* Turn off FCS stripping on receive packets */ + if ((options & FXMAC_FCS_STRIP_OPTION) != 0x00000000U) + { + reg_new_net_cfg &= (u32)(~FXMAC_NWCFG_FCSREM_MASK); + } + + /* Turn off length/type field checking on receive packets */ + if ((options & FXMAC_LENTYPE_ERR_OPTION) != 0x00000000U) + { + reg_new_net_cfg &= (u32)(~FXMAC_NWCFG_LENERRDSCRD_MASK); + } + + /* Turn off flow control */ + if ((options & FXMAC_FLOW_CONTROL_OPTION) != 0x00000000U) + { + reg_new_net_cfg &= (u32)(~FXMAC_NWCFG_PAUSEEN_MASK); + } + + /* Turn off promiscuous frame filtering (all frames are received) */ + if ((options & FXMAC_PROMISC_OPTION) != 0x00000000U) + { + reg_new_net_cfg &= (u32)(~FXMAC_NWCFG_COPYALLEN_MASK); + } + + /* Disallow broadcast address filtering => broadcast reception */ + if ((options & FXMAC_BROADCAST_OPTION) != 0x00000000U) + { + reg_new_net_cfg |= FXMAC_NWCFG_BCASTDI_MASK; + } + + /* Disallow multicast address filtering */ + if ((options & FXMAC_MULTICAST_OPTION) != 0x00000000U) + { + reg_new_net_cfg &= (u32)(~FXMAC_NWCFG_MCASTHASHEN_MASK); + } + + /* Disable RX checksum offload */ + if ((options & FXMAC_RX_CHKSUM_ENABLE_OPTION) != 0x00000000U) + { + reg_new_net_cfg &= (u32)(~FXMAC_NWCFG_RXCHKSUMEN_MASK); + } + + /* Disable jumbo frames */ + if (((options & FXMAC_JUMBO_ENABLE_OPTION) != 0x00000000U)) /* 恢复之前buffer 容量 */ + { + + instance_p->max_mtu_size = FXMAC_MTU; + instance_p->max_frame_size = FXMAC_MTU + FXMAC_HDR_SIZE + FXMAC_TRL_SIZE; + + reg_new_net_cfg &= (u32)(~FXMAC_NWCFG_JUMBO_MASK); + reg = FXMAC_READREG32(instance_p->config.base_address, + FXMAC_DMACR_OFFSET); + reg &= ~FXMAC_DMACR_RXBUF_MASK; + + if (queue_num == 0) + { + u32 rx_buf_size = 0; + + reg = FXMAC_READREG32(instance_p->config.base_address, FXMAC_DMACR_OFFSET); + reg &= ~FXMAC_DMACR_RXBUF_MASK; + + rx_buf_size = ((u32)instance_p->max_mtu_size / (u32)FXMAC_RX_BUF_UNIT); + rx_buf_size += ((u32)instance_p->max_mtu_size % ((u32)FXMAC_RX_BUF_UNIT) != (u32)0) ? 1U : 0U; + + reg |= ((rx_buf_size << (u32)(FXMAC_DMACR_RXBUF_SHIFT)) & (u32)(FXMAC_DMACR_RXBUF_MASK)); + + FXMAC_WRITEREG32(instance_p->config.base_address, FXMAC_DMACR_OFFSET, reg); + } + else if (queue_num < instance_p->config.max_queue_num) + { + u32 rx_buf_size = 0; + rx_buf_size = ((u32)instance_p->max_mtu_size / (u32)FXMAC_RX_BUF_UNIT); + rx_buf_size += (((u32)instance_p->max_mtu_size % (u32)FXMAC_RX_BUF_UNIT) != (u32)0) ? 1U : 0U; + + FXMAC_WRITEREG32(config_p->base_address, FXMAC_RXBUFQX_SIZE_OFFSET(queue_num), rx_buf_size & FXMAC_RXBUFQX_SIZE_MASK); + } + } + + if (((options & FXMAC_SGMII_ENABLE_OPTION) != 0x00000000U)) + { + reg_new_net_cfg &= (u32)(~(FXMAC_NWCFG_SGMIIEN_MASK | + FXMAC_NWCFG_PCSSEL_MASK)); + } + + if ((options & FXMAC_LOOPBACK_NO_MII_OPTION) != 0x00000000U) + { + reg = FXMAC_READREG32(config_p->base_address, FXMAC_NWCTRL_OFFSET); + reg &= (u32)(~FXMAC_NWCFG_LOOPBACK_LOCAL_MASK); + FXMAC_WRITEREG32(config_p->base_address, FXMAC_NWCTRL_OFFSET, reg); + } + + if ((options & FXMAC_LOOPBACK_USXGMII_OPTION) != 0x00000000U) + { + FXMAC_WRITEREG32(config_p->base_address, FXMAC_TEST_CONTROL_OFFSET, (FXMAC_READREG32(config_p->base_address, FXMAC_TEST_CONTROL_OFFSET) & ~2)); + } + + /* Officially change the NET_CONFIG registers if it needs to be + * modified. + */ + if (reg_net_cfg != reg_new_net_cfg) + { + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_NWCFG_OFFSET, reg_new_net_cfg); + } + + /* Disable TX checksum offload */ + if ((options & FXMAC_TX_CHKSUM_ENABLE_OPTION) != 0x00000000U) + { + reg = FXMAC_READREG32(instance_p->config.base_address, + FXMAC_DMACR_OFFSET); + reg &= (u32)(~FXMAC_DMACR_TCPCKSUM_MASK); + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_DMACR_OFFSET, reg); + } + + /* Disable transmitter */ + if ((options & FXMAC_TRANSMITTER_ENABLE_OPTION) != 0x00000000U) + { + reg = FXMAC_READREG32(instance_p->config.base_address, + FXMAC_NWCTRL_OFFSET); + reg &= (u32)(~FXMAC_NWCTRL_TXEN_MASK); + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_NWCTRL_OFFSET, reg); + } + + /* Disable receiver */ + if ((options & FXMAC_RECEIVER_ENABLE_OPTION) != 0x00000000U) + { + reg = FXMAC_READREG32(instance_p->config.base_address, + FXMAC_NWCTRL_OFFSET); + reg &= (u32)(~FXMAC_NWCTRL_RXEN_MASK); + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_NWCTRL_OFFSET, reg); + } + + /* The remaining options not handled here are managed elsewhere in the + * driver. No register modifications are needed at this time. Reflecting + * option in instance_p->options is good enough for now. + */ + + /* Set options word to its new value */ + instance_p->options &= ~options; + + status = (FError)(FT_SUCCESS); + } + return status; +} + + +/** + * Clear the Hash registers for the mac address pointed by address_ptr. + * + * @param instance_p is a pointer to the instance to be worked on. + * + */ +void FXmacClearHash(FXmac *instance_p) +{ + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == (u32)FT_COMPONENT_IS_READY); + + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_HASHL_OFFSET, 0x0U); + + /* write bits [63:32] in TOP */ + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_HASHH_OFFSET, 0x0U); +} + + +/** + * Write data to the specified PHY register. The Ethernet driver does not + * require the device to be stopped before writing to the PHY. Although it is + * probably a good idea to stop the device, it is the responsibility of the + * application to deem this necessary. The MAC provides the driver with the + * ability to talk to a PHY that adheres to the Media Independent Interface + * (MII) as defined in the IEEE 802.3 standard. + * + * Prior to PHY access with this function, the user should have setup the MDIO + * clock with FXmacSetMdioDivisor(). + * + * @param instance_p is a pointer to the FXmac instance to be worked on. + * @param phy_address is the address of the PHY to be written (supports multiple + * PHYs) + * @param register_num is the register number, 0-31, of the specific PHY register + * to write + * @param phy_data is the 16-bit value that will be written to the register + * + * @return + * + * - FT_SUCCESS if the PHY was written to successfully. Since there is no error + * status from the MAC on a write, the user should read the PHY to verify the + * write was successful. + * - FXMAC_ERR_PHY_BUSY if there is another PHY operation in progress + * + * @note + * + * This function is not thread-safe. The user must provide mutually exclusive + * access to this function if there are to be multiple threads that can call it. + * + * There is the possibility that this function will not return if the hardware + * is broken (i.e., it never sets the status bit indicating that the write is + * done). If this is of concern to the user, the user should provide a mechanism + * suitable to their needs for recovery. + * + * For the duration of this function, all host interface reads and writes are + * blocked to the current FXmac instance. + * + ******************************************************************************/ +FError FXmacPhyWrite(FXmac *instance_p, u32 phy_address, + u32 register_num, u16 phy_data) +{ + u32 mgtcr; + volatile u32 ipisr; + u32 ip_write_temp; + FError status; + + FASSERT(instance_p != NULL); + + /* Make sure no other PHY operation is currently in progress */ + if ((!(FXMAC_READREG32(instance_p->config.base_address, + FXMAC_NWSR_OFFSET) & + FXMAC_NWSR_MDIOIDLE_MASK)) == TRUE) + { + status = (FError)(FXMAC_ERR_PHY_BUSY); + } + else + { + /* Construct mgtcr mask for the operation */ + mgtcr = FXMAC_PHYMNTNC_OP_MASK | FXMAC_PHYMNTNC_OP_W_MASK | + (phy_address << FXMAC_PHYMNTNC_PHAD_SHFT_MSK) | + (register_num << FXMAC_PHYMNTNC_PREG_SHFT_MSK) | (u32)phy_data; + + /* Write mgtcr and wait for completion */ + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_PHYMNTNC_OFFSET, mgtcr); + + do + { + ipisr = FXMAC_READREG32(instance_p->config.base_address, + FXMAC_NWSR_OFFSET); + ip_write_temp = ipisr; + } + while ((ip_write_temp & FXMAC_NWSR_MDIOIDLE_MASK) == 0x00000000U); + + status = (FError)(FT_SUCCESS); + } + return status; +} + + +/** + * Read the current value of the PHY register indicated by the phy_address and + * the register_num parameters. The MAC provides the driver with the ability to + * talk to a PHY that adheres to the Media Independent Interface (MII) as + * defined in the IEEE 802.3 standard. + * + * + * @param instance_p is a pointer to the FXmac instance to be worked on. + * @param phy_address is the address of the PHY to be read (supports multiple + * PHYs) + * @param register_num is the register number, 0-31, of the specific PHY register + * to read + * @param phydat_aptr is an output parameter, and points to a 16-bit buffer into + * which the current value of the register will be copied. + * + * @return + * + * - FT_SUCCESS if the PHY was read from successfully + * - FXMAC_ERR_PHY_BUSY if there is another PHY operation in progress + * + * @note + * + * This function is not thread-safe. The user must provide mutually exclusive + * access to this function if there are to be multiple threads that can call it. + * + * There is the possibility that this function will not return if the hardware + * is broken (i.e., it never sets the status bit indicating that the read is + * done). If this is of concern to the user, the user should provide a mechanism + * suitable to their needs for recovery. + * + * For the duration of this function, all host interface reads and writes are + * blocked to the current FXmac instance. + * + ******************************************************************************/ +FError FXmacPhyRead(FXmac *instance_p, u32 phy_address, + u32 register_num, u16 *phydat_aptr) +{ + u32 mgtcr; + volatile u32 ipisr; + u32 IpReadTemp; + FError status; + + FASSERT(instance_p != NULL); + + /* Make sure no other PHY operation is currently in progress */ + if ((!(FXMAC_READREG32(instance_p->config.base_address, + FXMAC_NWSR_OFFSET) & + FXMAC_NWSR_MDIOIDLE_MASK)) == TRUE) + { + status = (FError)(FXMAC_ERR_PHY_BUSY); + } + else + { + /* Construct mgtcr mask for the operation */ + mgtcr = FXMAC_PHYMNTNC_OP_MASK | FXMAC_PHYMNTNC_OP_R_MASK | + (phy_address << FXMAC_PHYMNTNC_PHAD_SHFT_MSK) | + (register_num << FXMAC_PHYMNTNC_PREG_SHFT_MSK); + + /* Write mgtcr and wait for completion */ + FXMAC_WRITEREG32(instance_p->config.base_address, + FXMAC_PHYMNTNC_OFFSET, mgtcr); + + do + { + ipisr = FXMAC_READREG32(instance_p->config.base_address, + FXMAC_NWSR_OFFSET); + IpReadTemp = ipisr; + } + while ((IpReadTemp & FXMAC_NWSR_MDIOIDLE_MASK) == 0x00000000U); + + /* Read data */ + *phydat_aptr = (u16)FXMAC_READREG32(instance_p->config.base_address, + FXMAC_PHYMNTNC_OFFSET); + status = (FError)(FT_SUCCESS); + } + return status; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_phy.c b/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_phy.c new file mode 100644 index 0000000000..4af6752555 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_phy.c @@ -0,0 +1,371 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fxmac_phy.c + * Date: 2022-04-06 14:46:52 + * LastEditTime: 2022-04-06 14:46:58 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "fxmac.h" +#include "eth_ieee_reg.h" +#include "fdebug.h" + +#if defined(CONFIG_FXMAC_PHY_YT) + #include "phy_yt.h" +#endif + + +#define FXMAC_DEBUG_TAG "FXMAC_PHY" +#define FXMAC_ERROR(format, ...) FT_DEBUG_PRINT_E(FXMAC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FXMAC_INFO(format, ...) FT_DEBUG_PRINT_I(FXMAC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FXMAC_DEBUG(format, ...) FT_DEBUG_PRINT_D(FXMAC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FXMAC_WARN(format, ...) FT_DEBUG_PRINT_W(FXMAC_DEBUG_TAG, format, ##__VA_ARGS__) + +static FXmac *instance_b; +static u32 phy_addr_b; + +static FError FXmacDetect(FXmac *instance_p, u32 *phy_addr_p) +{ + u32 phy_addr = 0, i = 0, index; + u16 phy_reg = 0, phy_id1_reg, phy_id2_reg; + FError ret; + instance_b = instance_p; + + for (phy_addr = 0; phy_addr < FT_XMAC_PHY_MAX_NUM; phy_addr++) + { + ret = FXmacPhyRead(instance_p, phy_addr, PHY_STATUS_REG_OFFSET, &phy_reg); + if (ret != FT_SUCCESS) + { + FXMAC_ERROR("%s, PHY operation is busy", __func__); + return ret; + } + FXMAC_INFO("PHY_STATUS_REG_OFFSET is %x \r\n", phy_reg); + if (phy_reg != 0xffff) + { + ret = FXmacPhyRead(instance_p, phy_addr, PHY_IDENTIFIER_1_REG, &phy_id1_reg); + ret |= FXmacPhyRead(instance_p, phy_addr, PHY_IDENTIFIER_2_REG, &phy_id2_reg); + FXMAC_INFO("phy_id1_reg is 0x%x \r\n", phy_id1_reg); + FXMAC_INFO("phy_id2_reg is 0x%x \r\n", phy_id2_reg); + if ((ret == FT_SUCCESS) && (phy_id2_reg != 0) && (phy_id1_reg != 0xffff) && (phy_id1_reg != 0xffff)) + { + *phy_addr_p = phy_addr; + phy_addr_b = phy_addr; + FXMAC_INFO("phy_addr is 0x%x \r\n", phy_addr); + return FT_SUCCESS; + } + } + } + + return FT_SUCCESS; +} + +static FError FXmacGetIeeePhySpeed(FXmac *instance_p, u32 phy_addr) +{ + u16 temp, temp2; + u16 control; + u16 status; + u16 partner_capabilities; + u32 negotitation_timeout_cnt = 0; + FError ret; + volatile s32 wait; + + FXMAC_INFO("Start PHY autonegotiation "); + + ret = FXmacPhyRead(instance_p, phy_addr, PHY_CONTROL_REG_OFFSET, &control); + if (ret != FT_SUCCESS) + { + FXMAC_ERROR("%s line is %d,read PHY_CONTROL_REG_OFFSET is error", __func__, __LINE__); + return ret; + } + control |= PHY_CONTROL_RESET_MASK; + + ret = FXmacPhyWrite(instance_p, phy_addr, PHY_CONTROL_REG_OFFSET, control); + if (ret != FT_SUCCESS) + { + FXMAC_ERROR("%s line is %d,write PHY_CONTROL_REG_OFFSET is error", __func__, __LINE__); + return ret; + } + for (wait = 0; wait < 100000; wait++) + ; + FXMAC_INFO(" PHY reset end "); + ret = FXmacPhyRead(instance_p, phy_addr, PHY_CONTROL_REG_OFFSET, &control); + if (ret != FT_SUCCESS) + { + FXMAC_ERROR("%s line is %d,read PHY_CONTROL_REG_OFFSET is error", __func__, __LINE__); + return ret; + } + + control |= PHY_CONTROL_AUTONEGOTIATE_ENABLE; + control |= PHY_CONTROL_AUTONEGOTIATE_RESTART; + ret = FXmacPhyWrite(instance_p, phy_addr, PHY_CONTROL_REG_OFFSET, control); + if (ret != FT_SUCCESS) + { + FXMAC_ERROR("%s line is %d,write PHY_CONTROL_REG_OFFSET is error", __func__, __LINE__); + return ret; + } + + FXMAC_INFO("Waiting for PHY to complete autonegotiation."); + + ret = FXmacPhyRead(instance_p, phy_addr, PHY_STATUS_REG_OFFSET, &status); + if (ret != FT_SUCCESS) + { + FXMAC_ERROR("%s line is %d,read PHY_CONTROL_REG_OFFSET is error", __func__, __LINE__); + return ret; + } + + + while (!(status & PHY_STATUS_AUTONEGOTIATE_COMPLETE)) + { + for (wait = 0; wait < 1000000; wait++) + ; + ret = FXmacPhyRead(instance_p, phy_addr, PHY_STATUS_REG_OFFSET, &status); + if (ret != FT_SUCCESS) + { + FXMAC_ERROR("%s line is %d,read PHY_STATUS_REG_OFFSET is error", __func__, __LINE__); + return ret; + } + + + if (negotitation_timeout_cnt++ >= 0xfff) + { + FXMAC_ERROR("autonegotiation is error "); + return FXMAC_PHY_AUTO_AUTONEGOTIATION_FAILED; + } + } + FXMAC_INFO("autonegotiation complete "); + + ret = FXmacPhyRead(instance_p, phy_addr, PHY_SPECIFIC_STATUS_REG, &temp); + if (ret != FT_SUCCESS) + { + FXMAC_ERROR("%s line is %d,read PHY_SPECIFIC_STATUS_REG is error", __func__, __LINE__); + return ret; + } + + FXMAC_INFO("temp is %x \r\n", temp); + ret = FXmacPhyRead(instance_p, phy_addr, PHY_STATUS_REG_OFFSET, &temp2); + if (ret != FT_SUCCESS) + { + FXMAC_ERROR("%s line is %d,read PHY_STATUS_REG_OFFSET is error", __func__, __LINE__); + return ret; + } + + FXMAC_INFO("temp2 is %x \r\n", temp2); + + if (temp & (1 << 13)) + { + FXMAC_INFO("duplex is full \r\n"); + instance_p->config.duplex = 1; + } + else + { + FXMAC_INFO("duplex is half \r\n"); + instance_p->config.duplex = 0; + } + + if ((temp & 0xC000) == PHY_SPECIFIC_STATUS_SPEED_1000M) + { + FXMAC_INFO("speed is 1000\r\n"); + instance_p->config.speed = 1000; + } + else if ((temp & 0xC000) == PHY_SPECIFIC_STATUS_SPEED_100M) + { + FXMAC_INFO("speed is 100\r\n"); + instance_p->config.speed = 100; + } + else + { + FXMAC_INFO("speed is 10\r\n"); + instance_p->config.speed = 10; + } + + return FT_SUCCESS; +} + +void FxmaxLinkupCheck(void) +{ + u16 temp; + FXmacPhyRead(instance_b, phy_addr_b, PHY_SPECIFIC_STATUS_REG, &temp); + FXMAC_INFO("0x17 value is %x \r\n", temp); + FXMAC_INFO("linkup status is %x \r\n", temp & (1 << 10)); +} + + +static FError FXmacConfigureIeeePhySpeed(FXmac *instance_p, u32 phy_addr, u32 speed, u32 duplex_mode) +{ + u16 control; + u16 autonereg; + volatile s32 wait; + FError ret; + u16 specific_reg = 0; + + FXMAC_INFO("manual setting ,phy_addr is %d,speed %d, duplex_mode is %d \r\n", phy_addr, speed, duplex_mode); + + ret = FXmacPhyRead(instance_p, phy_addr, PHY_AUTONEGO_ADVERTISE_REG, &autonereg); + if (ret != FT_SUCCESS) + { + FXMAC_ERROR("%s line is %d,read PHY_AUTONEGO_ADVERTISE_REG is error", __func__, __LINE__); + return ret; + } + + autonereg |= PHY_AUTOADVERTISE_ASYMMETRIC_PAUSE_MASK; + autonereg |= PHY_AUTOADVERTISE_PAUSE_MASK; + ret = FXmacPhyWrite(instance_p, phy_addr, PHY_AUTONEGO_ADVERTISE_REG, autonereg); + if (ret != FT_SUCCESS) + { + FXMAC_ERROR("%s line is %d,write PHY_AUTONEGO_ADVERTISE_REG is error", __func__, __LINE__); + return ret; + } + + + ret = FXmacPhyRead(instance_p, phy_addr, PHY_CONTROL_REG_OFFSET, &control); + if (ret != FT_SUCCESS) + { + FXMAC_ERROR("%s line is %d,read PHY_AUTONEGO_ADVERTISE_REG is error", __func__, __LINE__); + return ret; + } + FXMAC_INFO("PHY_CONTROL_REG_OFFSET is %x \r\n", control); + + + control &= ~PHY_CONTROL_LINKSPEED_1000M; + control &= ~PHY_CONTROL_LINKSPEED_100M; + control &= ~PHY_CONTROL_LINKSPEED_10M; + + if (speed == 1000) + { + control |= PHY_CONTROL_LINKSPEED_1000M; + } + else if (speed == 100) + { + control |= PHY_CONTROL_LINKSPEED_100M; + } + else if (speed == 10) + { + control |= PHY_CONTROL_LINKSPEED_10M; + } + + if (duplex_mode == 1) + { + control |= PHY_CONTROL_FULL_DUPLEX_MASK; + } + else + { + control &= ~PHY_CONTROL_FULL_DUPLEX_MASK; + } + + /* disable auto-negotiation */ + control &= ~(PHY_CONTROL_AUTONEGOTIATE_ENABLE); + control &= ~(PHY_CONTROL_AUTONEGOTIATE_RESTART); + + ret = FXmacPhyWrite(instance_p, phy_addr, PHY_CONTROL_REG_OFFSET, control); /* Technology Ability Field */ + if (ret != FT_SUCCESS) + { + FXMAC_ERROR("%s line is %d,write PHY_AUTONEGO_ADVERTISE_REG is error", __func__, __LINE__); + return ret; + } + + for (wait = 0; wait < 100000; wait++) + ; + + FXMAC_INFO("Manual selection completed \r\n"); + + ret = FXmacPhyRead(instance_p, phy_addr, PHY_SPECIFIC_STATUS_REG, &specific_reg); + if (ret != FT_SUCCESS) + { + FXMAC_ERROR("%s line is %d,read PHY_SPECIFIC_STATUS_REG is error", __func__, __LINE__); + return ret; + } + + FXMAC_INFO("specific_reg is %x \r\n", specific_reg); + + if (specific_reg & (1 << 13)) + { + FXMAC_INFO("duplex is full \r\n"); + instance_p->config.duplex = 1; + } + else + { + FXMAC_INFO("duplex is half \r\n"); + instance_p->config.duplex = 0; + } + + if ((specific_reg & 0xC000) == PHY_SPECIFIC_STATUS_SPEED_1000M) + { + FXMAC_INFO("speed is 1000\r\n"); + instance_p->config.speed = 1000; + } + else if ((specific_reg & 0xC000) == PHY_SPECIFIC_STATUS_SPEED_100M) + { + FXMAC_INFO("speed is 100\r\n"); + instance_p->config.speed = 100; + } + else + { + FXMAC_INFO("speed is 10\r\n"); + instance_p->config.speed = 10; + } + + return FT_SUCCESS; +} + +/** + * @name: FXmacPhyInit + * @msg: setup the PHYs for proper speed setting. + * @param {FXmac} *instance_p is a pointer to the instance to be worked on. + * @param {u32} speed is phy operating speed + * @param {u32} phy_addr is the address of the PHY to be read (supports multiple PHYs) + * @param {u32} duplex_mode is The duplex mode can be selected via either the Auto-Negotiation process or manual duplex selection. + * @param {u32} autonegotiation_en is an auto-negotiated flag . 1 is enable auto ,0 is manual + * @return {FError} + */ +FError FXmacPhyInit(FXmac *instance_p, u32 speed, u32 duplex_mode, u32 autonegotiation_en) +{ + FError ret; + u32 index = 0, phy_mask; + u16 phy_identity; + u32 phy_addr; + + if (FXmacDetect(instance_p, &phy_addr) != FT_SUCCESS) + { + FXMAC_ERROR("phy is not found"); + return FXMAC_PHY_IS_NOT_FOUND; + } + FXMAC_INFO("settings phy_addr is %d\n", phy_addr); + instance_p->phy_address = phy_addr; + if (autonegotiation_en) + { + ret = FXmacGetIeeePhySpeed(instance_p, phy_addr); + if (ret != FT_SUCCESS) + { + return ret; + } + } + else + { + FXMAC_INFO("Set the communication speed manually"); + ret = FXmacConfigureIeeePhySpeed(instance_p, phy_addr, speed, duplex_mode); + if (ret != FT_SUCCESS) + { + FXMAC_ERROR("Failed to manually set the PHY"); + return ret; + } + + } + + instance_p->link_status = FXMAC_LINKUP; + return FT_SUCCESS; +} diff --git a/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_phy.h b/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_phy.h new file mode 100644 index 0000000000..49c1e1de38 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_phy.h @@ -0,0 +1,36 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fxmac_phy.h + * Date: 2022-04-06 14:46:52 + * LastEditTime: 2022-04-06 14:46:58 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef DRIVERS_ETH_FXMAC_PHY_H +#define DRIVERS_ETH_FXMAC_PHY_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifdef __cplusplus +} +#endif + +#endif // ! \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_sinit.c b/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_sinit.c new file mode 100644 index 0000000000..13a8592036 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/eth/fxmac/fxmac_sinit.c @@ -0,0 +1,44 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fxmac_sinit.c + * Date: 2022-04-06 14:46:52 + * LastEditTime: 2022-04-06 14:46:58 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "fxmac.h" +#include "fparameters.h" + +extern const FXmacConfig fxmac_cfg_tbl[FT_XMAC_NUM]; + +const FXmacConfig *FXmacLookupConfig(u32 instance_id) +{ + const FXmacConfig *cfg_ptr = NULL; + u32 index; + + for (index = 0; index < (u32)FT_XMAC_NUM; index++) + { + if (fxmac_cfg_tbl[index].instance_id == instance_id) + { + cfg_ptr = &fxmac_cfg_tbl[index]; + break; + } + } + + return (const FXmacConfig *)cfg_ptr; +} diff --git a/bsp/phytium/libraries/standalone/drivers/eth/fxmac/phy/eth_ieee_reg.h b/bsp/phytium/libraries/standalone/drivers/eth/fxmac/phy/eth_ieee_reg.h new file mode 100644 index 0000000000..02ddcee2c0 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/eth/fxmac/phy/eth_ieee_reg.h @@ -0,0 +1,104 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: eth_ieee_reg.h + * Date: 2022-04-06 14:46:52 + * LastEditTime: 2022-04-06 14:46:58 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef DRIVERS_ETH_PHY_IEEE_REG_H +#define DRIVERS_ETH_PHY_IEEE_REG_H + +#ifdef __cplusplus +extern "C" +{ +#endif +/***************************** Include Files *********************************/ + +#include "ferror_code.h" + +/***************** Macros (Inline Functions) Definitions *********************/ + +#define PHY_CONTROL_REG_OFFSET 0 +#define PHY_STATUS_REG_OFFSET 1 +#define PHY_IDENTIFIER_1_REG 2 +#define PHY_IDENTIFIER_2_REG 3 +#define PHY_AUTONEGO_ADVERTISE_REG 4 +#define PHY_PARTNER_ABILITIES_1_REG_OFFSET 5 +#define PHY_PARTNER_ABILITIES_2_REG_OFFSET 8 +#define PHY_PARTNER_ABILITIES_3_REG_OFFSET 10 +#define PHY_1000_ADVERTISE_REG_OFFSET 9 +#define PHY_MMD_ACCESS_CONTROL_REG 13 +#define PHY_MMD_ACCESS_ADDRESS_DATA_REG 14 +#define PHY_SPECIFIC_STATUS_REG 17 + +#define PHY_CONTROL_FULL_DUPLEX_MASK 0x0100 +#define PHY_CONTROL_LINKSPEED_MASK 0x0040 +#define PHY_CONTROL_LINKSPEED_1000M 0x0040 +#define PHY_CONTROL_LINKSPEED_100M 0x2000 +#define PHY_CONTROL_LINKSPEED_10M 0x0000 +#define PHY_CONTROL_RESET_MASK 0x8000 + +#define PHY_CONTROL_AUTONEGOTIATE_ENABLE 0x1000 +#define PHY_CONTROL_AUTONEGOTIATE_RESTART 0x0200 + +#define PHY_STATUS_AUTONEGOTIATE_COMPLETE 0x0020 +#define PHY_STAT_LINK_STATUS 0x0004 + +#define PHY_AUTOADVERTISE_ASYMMETRIC_PAUSE_MASK 0x0800 +#define PHY_AUTOADVERTISE_PAUSE_MASK 0x0400 +#define PHY_AUTOADVERTISE_AUTONEG_ERROR_MASK 0x8000 + +/* Advertisement control register. */ +#define PHY_AUTOADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */ +#define PHY_AUTOADVERTISE_1000XFULL 0x0020 /* Try for 1000BASE-X full-duplex */ +#define PHY_AUTOADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */ +#define PHY_AUTOADVERTISE_1000XHALF 0x0040 /* Try for 1000BASE-X half-duplex */ +#define PHY_AUTOADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */ +#define PHY_AUTOADVERTISE_1000XPAUSE 0x0080 /* Try for 1000BASE-X pause */ +#define PHY_AUTOADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */ +#define PHY_AUTOADVERTISE_1000XPSE_ASYM 0x0100 /* Try for 1000BASE-X asym pause */ +#define PHY_AUTOADVERTISE_100BASE4 0x0200 /* Try for 100mbps 4k packets */ + +#define PHY_AUTOADVERTISE_100_AND_10 (PHY_AUTOADVERTISE_10FULL | PHY_AUTOADVERTISE_100FULL | \ + PHY_AUTOADVERTISE_10HALF | PHY_AUTOADVERTISE_100HALF) +#define PHY_AUTOADVERTISE_100 (PHY_AUTOADVERTISE_100FULL | PHY_AUTOADVERTISE_100HALF) +#define PHY_AUTOADVERTISE_10 (PHY_AUTOADVERTISE_10FULL | PHY_AUTOADVERTISE_10HALF) + +#define PHY_AUTOADVERTISE_1000 0x0300 + +#define PHY_SPECIFIC_STATUS_SPEED_1000M (2L << 14) +#define PHY_SPECIFIC_STATUS_SPEED_100M (1L << 14) +#define PHY_SPECIFIC_STATUS_SPEED_0M (0L << 14) + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +typedef FError(*EthPhyWrite)(void *instance_p, u32 phy_address, u32 phy_reg_offset, u16 phy_data); +typedef FError(*EthPhyRead)(void *instance_p, u32 phy_address, u32 phy_reg_offset, u16 *phy_data_p); + +/************************** Variable Definitions *****************************/ + +/************************** Function Prototypes ******************************/ + +#ifdef __cplusplus +} +#endif + +#endif // ! diff --git a/bsp/phytium/libraries/standalone/drivers/eth/fxmac/phy/yt/phy_yt.c b/bsp/phytium/libraries/standalone/drivers/eth/fxmac/phy/yt/phy_yt.c new file mode 100644 index 0000000000..9aeae51ed3 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/eth/fxmac/phy/yt/phy_yt.c @@ -0,0 +1,224 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: phy_yt.c + * Date: 2022-04-06 14:46:52 + * LastEditTime: 2022-04-06 14:46:58 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "phy_yt.h" +#include "fdebug.h" + +#define PHY_YT_DEBUG_TAG "PHY_YT" +#define PHY_YT_ERROR(format, ...) FT_DEBUG_PRINT_E(PHY_YT_DEBUG_TAG, format, ##__VA_ARGS__) +#define PHY_YT_INFO(format, ...) FT_DEBUG_PRINT_I(PHY_YT_DEBUG_TAG, format, ##__VA_ARGS__) +#define PHY_YT_DEBUG(format, ...) FT_DEBUG_PRINT_D(PHY_YT_DEBUG_TAG, format, ##__VA_ARGS__) +#define PHY_YT_WARN(format, ...) FT_DEBUG_PRINT_W(PHY_YT_DEBUG_TAG, format, ##__VA_ARGS__) + +#define PHY_YT_REG0_LOOPBACK 0x4000 + + +FError PhyYtCheckConnectStatus(void *instance_p, u32 phy_addr, EthPhyWrite write_p, EthPhyRead read_p) +{ + u16 phy_reg0 = 0; + FError status; + /* It's the address offset of the extended register + that will be Write or Read */ + status = write_p(instance_p, phy_addr, 0x1e, 0xa001); + if (status != FT_SUCCESS) + { + PHY_YT_ERROR("write_p 0xa001 to 0x1e failed"); + return FETH_PHY_ERR_READ; + } + + status = read_p(instance_p, phy_addr, 0x1f, &phy_reg0); + if (status != FT_SUCCESS) + { + PHY_YT_ERROR("read_p 0x1f failed"); + return FETH_PHY_ERR_READ; + } + + PHY_YT_INFO("phy_reg0 status is 0x%x", phy_reg0); + + return FT_SUCCESS; +} + +FError PhyYtSetLoopBack(void *instance_p, u32 phy_addr, EthPhyWrite write_p, EthPhyRead read_p) +{ + FError status; + u16 phy_reg0 = 0; + + status = read_p(instance_p, phy_addr, 0, &phy_reg0); + if (status != FT_SUCCESS) + { + PHY_YT_ERROR("Error setup phy loopback"); + return FETH_PHY_ERR_READ; + } + PHY_YT_INFO("0 phy_reg0 is 0x%x \r\n", phy_reg0); + /* + * Enable loopback + */ + phy_reg0 |= PHY_YT_REG0_LOOPBACK; + status = write_p(instance_p, phy_addr, 0, phy_reg0); + + status = read_p(instance_p, phy_addr, 0, &phy_reg0); + if (status != FT_SUCCESS) + { + PHY_YT_ERROR("Error setup phy loopback"); + return FETH_PHY_ERR_READ; + } + + status = read_p(instance_p, phy_addr, 0, &phy_reg0); + if (status != FT_SUCCESS) + { + PHY_YT_ERROR("Error setup phy loopback"); + return FETH_PHY_ERR_READ; + } + PHY_YT_INFO("after 0 phy_reg0 is 0x%x \r\n", phy_reg0); + + return FT_SUCCESS; +} + + +FError PhyChangeModeToSgmii(void *instance_p, u32 phy_addr, EthPhyWrite write_p, EthPhyRead read_p) +{ + FError status; + u16 phy_reg0 = 0; + + /* read default mode */ + status = write_p(instance_p, phy_addr, 0x1e, 0xa001); + if (status != FT_SUCCESS) + { + PHY_YT_ERROR("write_p 0xa001 to 0x1e failed"); + return FETH_PHY_ERR_READ; + } + + + status = read_p(instance_p, phy_addr, 0x1f, &phy_reg0); + if (status != FT_SUCCESS) + { + PHY_YT_ERROR("read_p 0xa001 to 0x1e failed"); + return FETH_PHY_ERR_READ; + } + + PHY_YT_INFO("default 0xa001 status is 0x%x \r\n", phy_reg0); + + /* change mode to sds */ + status = write_p(instance_p, phy_addr, 0x1e, 0xa001); + if (status != FT_SUCCESS) + { + PHY_YT_ERROR("write_p 0xa001 to 0x1e failed"); + return FETH_PHY_ERR_READ; + } + + status = write_p(instance_p, phy_addr, 0x1f, 0x8063); + if (status != FT_SUCCESS) + { + PHY_YT_ERROR("write_p 0xa001 to 0x1e failed"); + return FETH_PHY_ERR_READ; + } + + + /* read changged mode */ + status = write_p(instance_p, phy_addr, 0x1e, 0xa001); + if (status != FT_SUCCESS) + { + PHY_YT_ERROR("write_p 0xa000 to 0x1e failed"); + return FETH_PHY_ERR_READ; + } + + + status = read_p(instance_p, phy_addr, 0x1f, &phy_reg0); + if (status != FT_SUCCESS) + { + PHY_YT_ERROR("read_p 0xa001 to 0x1e failed"); + return FETH_PHY_ERR_READ; + } + + PHY_YT_INFO("changged 0xa001 status is 0x%x \r\n", phy_reg0); + + + + return FT_SUCCESS; +} + +FError PhyChangeModeToSds(void *instance_p, u32 phy_addr, EthPhyWrite write_p, EthPhyRead read_p) +{ + FError status; + u16 phy_reg0 = 0; + + + /* read default mode */ + status = write_p(instance_p, phy_addr, 0x1e, 0xa000); + if (status != FT_SUCCESS) + { + PHY_YT_ERROR("write_p 0xa000 to 0x1e failed"); + return FETH_PHY_ERR_READ; + } + + + status = read_p(instance_p, phy_addr, 0x1f, &phy_reg0); + if (status != FT_SUCCESS) + { + PHY_YT_ERROR("read_p 0xa001 to 0x1e failed"); + return FETH_PHY_ERR_READ; + } + + PHY_YT_INFO("default status is 0x%x \r\n", phy_reg0); + + /* change mode to sds */ + status = write_p(instance_p, phy_addr, 0x1e, 0xa000); + if (status != FT_SUCCESS) + { + PHY_YT_ERROR("write_p 0xa001 to 0x1e failed"); + return FETH_PHY_ERR_READ; + } + + status = write_p(instance_p, phy_addr, 0x1f, 0x2); + if (status != FT_SUCCESS) + { + PHY_YT_ERROR("write_p 0xa001 to 0x1e failed"); + return FETH_PHY_ERR_READ; + } + + + /* read changged mode */ + status = write_p(instance_p, phy_addr, 0x1e, 0xa000); + if (status != FT_SUCCESS) + { + PHY_YT_ERROR("write_p 0xa000 to 0x1e failed"); + return FETH_PHY_ERR_READ; + } + + + status = read_p(instance_p, phy_addr, 0x1f, &phy_reg0); + if (status != FT_SUCCESS) + { + PHY_YT_ERROR("read_p 0xa001 to 0x1e failed"); + return FETH_PHY_ERR_READ; + } + + PHY_YT_INFO("changged status is 0x%x \r\n", phy_reg0); + + + + return FT_SUCCESS; +} + + + diff --git a/bsp/phytium/libraries/standalone/drivers/eth/fxmac/phy/yt/phy_yt.h b/bsp/phytium/libraries/standalone/drivers/eth/fxmac/phy/yt/phy_yt.h new file mode 100644 index 0000000000..4640db8511 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/eth/fxmac/phy/yt/phy_yt.h @@ -0,0 +1,45 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: phy_yt.h + * Date: 2022-04-06 14:46:52 + * LastEditTime: 2022-04-06 14:46:58 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef PHY_YT_H +#define PHY_YT_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ferror_code.h" +#include "eth_ieee_reg.h" + +#define FETH_PHY_ERR_READ FT_MAKE_ERRCODE(ErrModBsp, ErrEthPhy, 0x1u) + +FError PhyYtSetLoopBack(void *instance_p, u32 phy_addr, EthPhyWrite write_p, EthPhyRead read_p); +FError PhyYtCheckConnectStatus(void *instance_p, u32 phy_addr, EthPhyWrite write_p, EthPhyRead read_p); +FError PhyChangeModeToSds(void *instance_p, u32 phy_addr, EthPhyWrite write_p, EthPhyRead read_p); +FError PhyChangeModeToSgmii(void *instance_p, u32 phy_addr, EthPhyWrite write_p, EthPhyRead read_p); +#ifdef __cplusplus +} +#endif + +#endif // ! \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/gic/Kconfig b/bsp/phytium/libraries/standalone/drivers/gic/Kconfig new file mode 100644 index 0000000000..9946994e6d --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/gic/Kconfig @@ -0,0 +1,6 @@ + config ENABLE_GICV3 + bool + prompt "Use Generic Interrupt Controller v3" + default y + + \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic.c b/bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic.c new file mode 100644 index 0000000000..b1fa4fb582 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic.c @@ -0,0 +1,674 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgic.c + * Date: 2022-03-28 09:30:23 + * LastEditTime: 2022-03-28 09:30:24 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "fgic.h" +#include "fgic_distributor.h" +#include "fgic_cpu_interface.h" +#include "fgic_redistributor.h" +#include "fgic_hw.h" +#include "ftypes.h" +#include "fkernel.h" +#include "fassert.h" +#include "fdebug.h" + + +#define FGIC_DEBUG_TAG "FGIC" +#define FGIC_ERROR(format, ...) FT_DEBUG_PRINT_E(FGIC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGIC_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(FGIC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGIC_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(FGIC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGIC_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(FGIC_DEBUG_TAG, format, ##__VA_ARGS__) + + +#define FGIC_SPI_START_ID 32 +#define FGIC_PPI_END_ID 31 +#define FGIC_SGI_END_ID 15 + +#define FGIC_GICD_32_PER_REG 32 +#define FGIC_GICD_16_PER_REG 16 +#define FGIC_GICD_4_PER_REG 4 + +#define FGIC_INT_DEFAULT_PRI_X4 0xa0a0a0a0 /* 考虑到当前一般程序工作于EL1,对于NS 或 S 安全状态 ,0x80 - 0xff 的优先级都有存在的可能性 */ +#define FGIC_CPU_INTERFACE_DEFAULT_FLITER 0xFF + +typedef enum +{ + GICD_CTLR_RWP_WAIT = 0, /* Use GICD_CTLR for test */ + GICR_CTLR_RWP_WAIT, /* Use GICR_CTLR for test */ +} WAIT_RWP_MODE; + +/** + * @name: FGicWaitRwp + * @msg: Wait for register write pending + * @param {uintptr} ctrl_base is a GICD_CTLR address + * @param {WAIT_RWP_MODE} wait_mode + * @return {*} + */ +static FError FGicWaitRwp(uintptr ctrl_base, WAIT_RWP_MODE wait_mode) +{ + u32 rwp_mask; + u32 timeout_cnt = 0; + if (GICD_CTLR_RWP_WAIT == wait_mode) + { + rwp_mask = FGIC_GICD_CTLR_RWP_MASK; + } + else if (GICR_CTLR_RWP_WAIT == wait_mode) + { + rwp_mask = FGIC_GICR_CTLR_RWP_MASK; + } + else + { + FGIC_DEBUG_E(" wait_mode not in WAIT_RWP_MODE "); + return FGIC_CTLR_ERR_TYPE; + } + + while (FGIC_READREG32(ctrl_base, 0) & rwp_mask) + { + if (timeout_cnt ++ >= 0xffffff) + { + FGIC_DEBUG_E(" wait rwp timeout "); + return FGIC_CTLR_ERR_IN_GET; + } + } + + return FGIC_SUCCESS; +} + +/** + * @name: void FGicDistrubutiorInit(FGic *instance_p) + * @msg: Initialize Distrubutior + * @param {FGic} *instance_p is a pointer to the FGic instance. + */ +void FGicDistrubutiorInit(FGic *instance_p) +{ + FASSERT(instance_p); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + u32 max_ints_mun, int_id, int_index; + uintptr dis_base = instance_p->config.dis_base; + max_ints_mun = instance_p->max_spi_num; + /* Disable the distributor */ + FGIC_WRITEREG32(dis_base, FGIC_GICD_CTLR_OFFSET, 0); + FASSERT(FGicWaitRwp(dis_base + FGIC_GICD_CTLR_OFFSET, GICD_CTLR_RWP_WAIT) == FGIC_SUCCESS); + + if (instance_p->security == ONE_NS_SECURITY_STATE) + { + /* Make GICD_CTLR.DS = 1 ,Non-secure accesses are permitted to access and modify registers that control Group 0 interrupts */ + FGIC_SETBIT(dis_base, FGIC_GICD_CTLR_OFFSET, FGIC_GICD_CTLR_DS_MASK); + FASSERT(FGIC_READREG32(dis_base, FGIC_GICD_CTLR_OFFSET) & FGIC_GICD_CTLR_DS_MASK); + } + + /* 关闭所有中断,并将中断分组默认为group 1 */ + for (int_id = FGIC_SPI_START_ID; int_id < max_ints_mun; int_id += FGIC_GICD_32_PER_REG) + { + int_index = int_id / FGIC_GICD_32_PER_REG; + /* Disables forwarding of the corresponding interrupt. */ + FGIC_GICD_ICENABLER_WRITE_N_MASK(dis_base, int_id, FGIC_GICD_ICENABLER_DEFAULT_MASK); + /* Changes the state of the corresponding interrupt from pending to inactive, or from active and pending to active. */ + FGIC_GICD_ICPENDR_WRITE_N_MASK(dis_base, int_id, FGIC_GICD_ICPENDR_DEFAULT_MASK); + + if (instance_p->security == ONE_NS_SECURITY_STATE) + { + FGIC_GICD_IGROUPR_WRITE_N_MASK(dis_base, int_id, FGIC_GICD_ICPENDR_DEFAULT_MASK); + } + else + { + FGIC_GICD_IGROUPR_WRITE_N_MASK(dis_base, int_id, FGIC_GICD_ICPENDR_DEFAULT_MASK); + } + FGIC_GICD_IGRPMODR_WRITE_N_MASK(dis_base, int_id, FGIC_GICD_IGRPMODR_DEFAULT_MASK); + } + + FASSERT(FGicWaitRwp(dis_base + FGIC_GICD_CTLR_OFFSET, GICD_CTLR_RWP_WAIT) == FGIC_SUCCESS); + + for (int_id = FGIC_SPI_START_ID; int_id < max_ints_mun; int_id += FGIC_GICD_4_PER_REG) + { + FGIC_GICD_IPRIORITYR_WRITE_N_MASK(dis_base, int_id, FGIC_INT_DEFAULT_PRI_X4); + } + + for (int_id = FGIC_SPI_START_ID; int_id < max_ints_mun; int_id += FGIC_GICD_16_PER_REG) + { + FGIC_GICD_ICFGR_WRITE_N_MASK(dis_base, int_id, 0); /* level-sensitive */ + } + + if (instance_p->security == ONE_NS_SECURITY_STATE) + { + FGIC_GICD_CTLR_WRITE(dis_base, GICD_CTLR_BIT_ARE_S | GICD_CTLR_ENABLE_GRP1_NS); + } + else + { + FGIC_GICD_CTLR_WRITE(dis_base, GICD_CTLR_BIT_ARE_NS | GICD_CTLR_ENABLE_GRP1_NS); + } + + + +} + +/** + * @name: FError FGicRedistrubutiorInit(FGic *instance_p) + * @msg: Initialize Redistrubutior + * @param {FGic} *instance_p is a pointer to the FGic instance. + * @return {FError} FGIC_SUCCESS is success ,FGIC_ERR_IN_TIMEOUT is timeout + */ +FError FGicRedistrubutiorInit(FGic *instance_p) +{ + FASSERT(instance_p); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + s32 int_id ; + u32 timeout = 0; + uintptr redis_base = instance_p->redis_base; + uintptr sgi_base = instance_p->redis_base + FGIC_GICR_SGI_BASE_OFFSET; + + /* Clear processor sleep and wait till childasleep is cleard */ + + FGIC_GICR_WAKER_CLEAR_BIT(redis_base, FGIC_GICR_WAKER_PROCESSOR_SLEEP_MASK); + while (FGIC_GICR_WAKER_READ(redis_base) & FGIC_GICR_WAKER_CHILDREN_ASLEEP_MASK) + { + timeout++; + if (timeout >= 0xfffffff) + { + return FGIC_ERR_IN_TIMEOUT; + } + } + + FASSERT(FGicWaitRwp(redis_base, GICR_CTLR_RWP_WAIT) == FGIC_SUCCESS); + FGIC_GICR_ICENABLER0_WRITE(sgi_base, FGIC_GICR_ICENABLER0_DEFAULT_MASK); /* Disable all sgi ppi */ + + /* Clear pending */ + FGIC_GICR_ICPENDR0_WRITE(sgi_base, FGIC_GICR_ICPENDR0_DEFAULT_MASK); + + /* Set sgi ppi route to different security group */ + + if (instance_p->security == ONE_NS_SECURITY_STATE) + { + FGIC_GICR_IGROUPR0_WRITE(sgi_base, FGIC_GICR_IGROUPR0_DEFAULT_MASK); + } + else + { + FGIC_GICR_IGROUPR0_WRITE(sgi_base, FGIC_GICR_IGROUPR0_DEFAULT_MASK); + } + FGIC_GICR_IGRPMODR0_WRITE(sgi_base, FGIC_GICR_IGRPMODR0_DEFAULT_MASK); + + /* 默认所有优先级为0xa0 */ + for (int_id = 0; int_id < FGIC_SPI_START_ID; int_id += FGIC_GICD_4_PER_REG) + { + FGIC_GICR_IPRIORITYR_WRITE(sgi_base, int_id, FGIC_INT_DEFAULT_PRI_X4); + } + + FGIC_GICR_ICFGR0_WRITE(sgi_base, 0); /* set level-sensitive */ + FGIC_GICR_ICFGR1_WRITE(sgi_base, 0); + + return FGIC_SUCCESS; +} + +/** + * @name: void FGicCpuInterfaceInit(void) + * @msg: Initialize Cpu interface of current core + */ +void FGicCpuInterfaceInit(void) +{ + u32 reg; + reg = FGicGetICC_SRE_EL1(); + + if (!(reg & GICC_SRE_SRE)) + { + reg |= (GICC_SRE_SRE | GICC_SRE_DFB | GICC_SRE_DIB); + FGicSetICC_SRE_EL1(reg); + reg = FGicGetICC_SRE_EL1(); + FASSERT(reg & GICC_SRE_SRE); + } + + FGicSetICC_PMR(FGIC_CPU_INTERFACE_DEFAULT_FLITER); + + FGicEnableGroup1_EL1(); + + FGicSetICC_CTLR_EL1(GICC_CTLR_CBPR); /* ICC_BPR0_EL1 determines the preemption group for both Group 0 and Group 1 interrupts. */ +} + +/** + * @name: FError FGicCfgInitialize(FGic *instance_p, const FGicConfig *input_config_p , uintptr redis_base) + * @msg: Initialize the GIC driver instance based on the incoming configuration + * @param {FGic} *instance_p is a pointer to the FGic instance. + * @param {FGicConfig} *input_config_p Configuration items that need to be applied in the instance + * @param {uintptr} redis_base is the redistributor adress of current core + * @return {*} + */ +FError FGicCfgInitialize(FGic *instance_p, const FGicConfig *input_config_p, uintptr redis_base) +{ + u32 max_ints_mun; + uintptr dis_base; + + FASSERT(instance_p && input_config_p); + + instance_p->config = *input_config_p; + instance_p->redis_base = redis_base ; + instance_p->is_ready = FT_COMPONENT_IS_READY; + + dis_base = instance_p->config.dis_base; + + max_ints_mun = FGIC_READREG32(dis_base, FGIC_GICD_TYPER_OFFSET); + max_ints_mun &= FGIC_GICD_TYPER_ITLINESNUMBER_MASK ; + max_ints_mun = ((max_ints_mun + 1) << 5) - 1; /* If the value of this field is N, the maximum SPI INTID is 32(N+1) minus 1. */ + instance_p->max_spi_num = max_ints_mun; + + return FGIC_SUCCESS; +} + +/** + * @name: FError FGicIntEnable(FGic *instance_p,s32 int_id) + * @msg: Enables the interrupt function based on the interrupt number + * @param {FGic} *instance_p is a pointer to the FGic instance. + * @param {s32} int_id is interrupt id + * @return {*} + */ +FError FGicIntEnable(FGic *instance_p, s32 int_id) +{ + FASSERT(instance_p); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + + if (int_id > instance_p->max_spi_num) + { + FGIC_DEBUG_E("int_id is over max spi num for FGicIntEnable"); + return FGIC_CTLR_ERR_NUM; + } + + if (int_id <= FGIC_PPI_END_ID) + { + uintptr redis_base = instance_p->redis_base; + FGicEnablePrivateInt(redis_base, int_id); + } + else + { + uintptr dis_base = instance_p->config.dis_base; + FGicEnableSPI(dis_base, int_id); + } + + return FGIC_SUCCESS ; +} + + +/** + * @name: FError FGicIntEnable(FGic *instance_p,s32 int_id) + * @msg: Disable the interrupt function based on the interrupt number + * @param {FGic} *instance_p is a pointer to the FGic instance. + * @param {s32} int_id is interrupt id + */ +FError FGicIntDisable(FGic *instance_p, s32 int_id) +{ + FASSERT(instance_p); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + + + if (int_id > instance_p->max_spi_num) + { + FGIC_DEBUG_E("int_id is over max spi num for FGicIntDisable"); + return FGIC_CTLR_ERR_NUM; + } + + if (int_id <= FGIC_PPI_END_ID) + { + uintptr redis_base = instance_p->redis_base; + FGicDisablePrivateInt(redis_base, int_id); + } + else + { + uintptr dis_base = instance_p->config.dis_base; + FGicDisableSPI(dis_base, int_id); + } + + return FGIC_SUCCESS ; +} + + +/** + * @name: FError FGicSetPriority(FGic *instance_p,s32 int_id,u32 priority) + * @msg: Sets the current interrupt priority value based on the interrupt number + * @param {FGic} *instance_p is a pointer to the FGic instance. + * @param {s32} int_id is interrupt id + * @param {u32} priority is priority value ,valid bit is bit[0:7] + * @return {*} + */ +FError FGicSetPriority(FGic *instance_p, s32 int_id, u32 priority) +{ + FASSERT(instance_p); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + + + if (int_id > instance_p->max_spi_num) + { + FGIC_DEBUG_E("int_id is over max spi num for FGicSetPriority"); + return FGIC_CTLR_ERR_IN_GET; + } + + if (int_id <= FGIC_PPI_END_ID) + { + uintptr redis_base = instance_p->redis_base; + FGicSetPrivatePriority(redis_base, int_id, priority); + } + else + { + uintptr dis_base = instance_p->config.dis_base; + FGicSetSpiPriority(dis_base, int_id, priority); + } + + return FGIC_SUCCESS ; +} + +/** + * @name: u32 FGicGetPriority(FGic *instance_p,s32 int_id) + * @msg: Gets the current interrupt priority value based on the interrupt number + * @param {FGic} *instance_p is a pointer to the FGic instance + * @param {s32} int_id is interrupt id + * @return {u32} priority value ,valid bit is bit[0:7] + */ +u32 FGicGetPriority(FGic *instance_p, s32 int_id) +{ + u32 priority; + FASSERT(instance_p); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + + + if (int_id > instance_p->max_spi_num) + { + FGIC_DEBUG_E("int_id is over max spi num for FGicGetPriority"); + return (u32)FGIC_CTLR_ERR_IN_GET; + } + + if (int_id <= FGIC_PPI_END_ID) + { + uintptr redis_base = instance_p->redis_base; + priority = FGicGetPrivatePriority(redis_base, int_id); + } + else + { + uintptr dis_base = instance_p->config.dis_base; + priority = FGicGetSpiPriority(dis_base, int_id); + } + + return priority ; +} + +/** + * @name: FError FGicSetTriggerLevel(FGic *instance_p,s32 int_id,TRIGGER_LEVEL trigger_way) + * @msg: Sets the interrupt triggering mode based on the current interrupt number + * @param {FGic} *instance_p is a pointer to the FGic instance + * @param {s32} int_id is interrupt id + * @param {TRIGGER_LEVEL} trigger_way is trigger mode + * @return {*} + */ +FError FGicSetTriggerLevel(FGic *instance_p, s32 int_id, TRIGGER_LEVEL trigger_way) +{ + FASSERT(instance_p); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + + if (int_id > instance_p->max_spi_num) + { + FGIC_DEBUG_E("int_id is over max spi num for FGicSetTriggerLevel"); + return FGIC_CTLR_ERR_IN_SET; + } + + if (int_id <= FGIC_SGI_END_ID) + { + uintptr redis_base = instance_p->redis_base; + FGicSetSgiLevel(redis_base, int_id, trigger_way); + } + else if (int_id <= FGIC_PPI_END_ID) + { + uintptr redis_base = instance_p->redis_base; + FGicSetPpiLevel(redis_base, int_id, trigger_way); + } + else + { + uintptr dis_base = instance_p->config.dis_base; + FGicSetSpiLevel(dis_base, int_id, trigger_way); + } + + return FGIC_SUCCESS ; +} + +/** + * @name: u32 FGicGetTriggerLevel(FGic *instance_p,s32 int_id) + * @msg: Gets the interrupt triggering mode based on the current interrupt number + * @param {FGic} *instance_p is a pointer to the FGic instance + * @param {s32} int_id is interrupt id + * @return {u32} triggering mode + */ +u32 FGicGetTriggerLevel(FGic *instance_p, s32 int_id) +{ + u32 trigger_way; + FASSERT(instance_p); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + + + if (int_id > instance_p->max_spi_num) + { + FGIC_DEBUG_E("int_id is over max spi num for FGicGetTriggerLevel"); + return (u32)FGIC_CTLR_ERR_IN_GET; + } + + if (int_id <= FGIC_SGI_END_ID) + { + uintptr redis_base = instance_p->redis_base; + trigger_way = FGicGetSgiLevel(redis_base, int_id); + } + else if (int_id <= FGIC_PPI_END_ID) + { + uintptr redis_base = instance_p->redis_base; + trigger_way = FGicGetPpiLevel(redis_base, int_id); + } + else + { + uintptr dis_base = instance_p->config.dis_base; + trigger_way = FGicGetSpiLevel(dis_base, int_id); + } + + return trigger_way ; +} + +/** + * @name: FGicSetSpiAffinityRouting + * @msg: Set intermediate routing information for a specific SPI interrupt + * @param {FGic} *instance_p is a pointer to the FGic instance. + * @param {u32} int_id is interrupt vector for spi + * @param {SPI_ROUTING_MODE} route_mode is the interrupt routing mode. + * @param {u64} affinity is the affinity level ,format is + * |--------[bit39-32]-------[bit23-16]-------------[bit15-8]--------[bit7-0] + * |--------Affinity level3-----Affinity level2-----Affinity level1---Affinity level0 + * @return {*} + */ +FError FGicSetSpiAffinityRouting(FGic *instance_p, s32 int_id, SPI_ROUTING_MODE route_mode, u64 affinity) +{ + FASSERT(instance_p); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + uintptr dis_base = instance_p->config.dis_base; + + if ((int_id > instance_p->max_spi_num) || (int_id <= FGIC_PPI_END_ID)) + { + FGIC_DEBUG_E("int_id %d is out of range ", int_id); + return FGIC_CTLR_ERR_IN_SET; + } + + FGicSetSpiRoute(dis_base, int_id, route_mode, affinity); + return FGIC_SUCCESS; +} + + +/** + * @name: FGicGetAffinityRouting + * @msg: Get intermediate routing information for a specific SPI interrupt + * @param {FGic} *instance_p is a pointer to the FGic instance. + * @param {u32} int_id is interrupt vector for spi + * @param {SPI_ROUTING_MODE} *route_mode_p is a pointer to get interrupt routing mode. + * @param {u64} *affinity_p is pointer to get affinity level ,format is + * |--------[bit39-32]-------[bit23-16]-------------[bit15-8]--------[bit7-0] + * |--------Affinity level3-----Affinity level2-----Affinity level1---Affinity level0 + * @return {FError} + */ +FError FGicGetAffinityRouting(FGic *instance_p, s32 int_id, SPI_ROUTING_MODE *route_mode_p, u64 *affinity_p) +{ + FASSERT(instance_p); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + FASSERT(route_mode_p != NULL); + FASSERT(affinity_p != NULL); + uintptr dis_base = instance_p->config.dis_base; + u64 reg; + + if ((int_id > instance_p->max_spi_num) || (int_id <= FGIC_PPI_END_ID)) + { + FGIC_DEBUG_E("int_id %d is out of range ", int_id); + return (u32)FGIC_CTLR_ERR_IN_GET; + } + + reg = FGicGetSpiRoute(dis_base, int_id); + *route_mode_p = reg & SPI_ROUTING_TO_ANY; + *affinity_p = reg & FGIC_GICD_IROUTER_AFFINITY_MASK; + return FGIC_SUCCESS; +} + + +/** + * @name: FGicGenerateSgi + * @msg: This interface is used for software generated interrupts + * @param {FGic} *instance_p is a pointer to the FGic instance. + * @param {u32} int_id is interrupt vector for spi + * @param {u32} target_list is 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. + * @param {SGI_ROUTING_MODE} routing_mode is Interrupt Routing Mode. + * @param {u64} affinity is the affinity level ,format is + * |--------[bit55-48]-------[bit39-32]-------------[bit23-16] + * |--------Affinity level3-----Affinity level2-----Affinity level1 + * @return {*} + */ +FError FGicGenerateSgi(FGic *instance_p, s32 int_id, u32 target_list, SGI_ROUTING_MODE routing_mode, u64 affinity) +{ + FASSERT(instance_p); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + + if (int_id > FGIC_SGI_END_ID) + { + FGIC_DEBUG_E("int_id %d is out of range ", int_id); + return FGIC_CTLR_ERR_IN_SET; + } + + FGicSetICC_SGI1R((int_id & FGIC_ICC_SGI1R_INTID_MASK) << 24, target_list, routing_mode, affinity); + return FGIC_SUCCESS; +} + +/** + * @name: FGicDeactionInterrupt + * @msg: Deactive Interruption of the current active state + * @param {FGic} *instance_p is a pointer to the FGic instance. + * @param {s32} int_id is interrupt id + */ +void FGicDeactionInterrupt(FGic *instance_p, s32 int_id) +{ + FASSERT(instance_p); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + FGicSetICC_EOIR1(int_id); +} + +/** + * @name: FGicAcknowledgeInt + * @msg: Acknowledge pending interrupt + * @param {FGic} *instance_p is a pointer to the FGic instance. + * @return {s32} interrupt id + */ +s32 FGicAcknowledgeInt(FGic *instance_p) +{ + FASSERT(instance_p); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + return FGicGetICC_APR1(); +} + + +/** + * @name: FGicSetPriorityFilter + * @msg: By setting the parameter of ICC_PMR, the interrupt range that the interrupt controller can respond to is determined + * @param {FGic} *instance_p is a pointer to the FGic instance. + * @param {u32} priority_mask . If the priority of an interrupt is lower than the value indicated by this field, the interface signals the interrupt to the PE. + * The reference values of priority_mask are as follows + * |priority_mask---------------256-------254--------252------248-------240 + * |Implemented priority bits---[7:0]----[7:1]------[7:2]-----[7:3]-----[7:4] + * |priority the growing steps--any-----even value----4---------8--------16 + */ +void FGicSetPriorityFilter(FGic *instance_p, u32 priority_mask) +{ + FASSERT(instance_p); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + + FGicSetICC_PMR(priority_mask); +} + + +/** + * @name: FGicGetPriorityFilter + * @msg: Gets the current priority filtering value + * @param {FGic} *instance_p is a pointer to the FGic instance. + * @return {u32} Priority Mask for the CPU interface . If the priority of an interrupt is lower than the value + * indicated by this field, the interface signals the interrupt to the PE. + * The reference values of priority_mask are as follows + * |return value----------------256-------254--------252------248-------240 + * |Implemented priority bits---[7:0]----[7:1]------[7:2]-----[7:3]-----[7:4] + * |priority the growing steps--any-----even value----4---------8--------16 + */ +u32 FGicGetPriorityFilter(FGic *instance_p) +{ + FASSERT(instance_p); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + + return FGicGetICC_PMR(); +} + + +/** + * @name: FGicGetPriorityGroup + * @msg: Get Binary point value + * @param {FGic} *instance_p is a pointer to the FGic instance. + * @return {u32} The relationship between Binary point value and grouping is as follows + * |return value----------------0-------1--------2------3-------4------5------6-------7 + * |Group priority field------[---]----[7:1]---[7:2]--[7:3]---[7:4]---[7:5]---[7:6]---[7] + * |Subpriority field---------[---]-----[0]----[1:0]--[2:0]---[3:0]---[4:0]---[5:0]---[6:0] + */ +u32 FGicGetPriorityGroup(FGic *instance_p) +{ + FASSERT(instance_p); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + + return FGicGetICC_BPR1(); +} + +/** + * @name: FGicSetPriorityGroup + * @msg: Sets the value of the current group priority + * @param {FGic} *instance_p is a pointer to the FGic instance. + * @param {u32} binary_point : The value of this field controls how the 8-bit interrupt priority field is split into a group priority field + * The relationship between binary_point value and grouping is as follows + * |binary_point----------------0-------1--------2------3-------4------5------6-------7 + * |Group priority field------[---]----[7:1]---[7:2]--[7:3]---[7:4]---[7:5]---[7:6]---[7] + * |Subpriority field---------[---]-----[0]----[1:0]--[2:0]---[3:0]---[4:0]---[5:0]---[6:0] + */ +void FGicSetPriorityGroup(FGic *instance_p, u32 binary_point) +{ + FASSERT(instance_p); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + + FGicSetICC_BPR1(binary_point); +} + + diff --git a/bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic.h b/bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic.h new file mode 100644 index 0000000000..e08236e916 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic.h @@ -0,0 +1,109 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgic.h + * Date: 2022-03-28 09:30:29 + * LastEditTime: 2022-03-28 09:30:29 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef DRIVERS_GIC_FGIC_H +#define DRIVERS_GIC_FGIC_H + +#include "ftypes.h" +#include "ferror_code.h" +#include "fparameters.h" + + +#define FGIC_RSGI_AFF1_OFFSET 16 +#define FGIC_RSGI_AFF2_OFFSET 32 +#define FGIC_RSGI_AFF3_OFFSET 48 + +#define FGIC_SUCCESS FT_SUCCESS +#define FGIC_CTLR_ERR_TYPE FT_MAKE_ERRCODE(ErrModBsp, ErrGic, 1) /* 错误选择CTLR 寄存器 */ +#define FGIC_CTLR_ERR_NUM FT_MAKE_ERRCODE(ErrModBsp, ErrGic, 2) /* 当前控制器不支持此中断id */ +#define FGIC_CTLR_ERR_IN_SET FT_MAKE_ERRCODE(ErrModBsp, ErrGic, 3) /* 在设置过程中出现的异常 */ +#define FGIC_CTLR_ERR_IN_GET FT_MAKE_ERRCODE(ErrModBsp, ErrGic, 4) /* 在获取过程中出现的异常 */ +#define FGIC_ERR_IN_TIMEOUT FT_MAKE_ERRCODE(ErrModBsp, ErrGic, 5) /* 超时退出 */ + + +typedef enum +{ + TRIGGER_BY_LEVEL_SENSITIVE = 0, /* Corresponding interrupt is level-sensitive. */ + TRIGGER_BY_LEVEL_EDGE, /* Corresponding interrupt is edge-triggered. */ +} TRIGGER_LEVEL; + +typedef enum +{ + TWO_SECURITY_STATE = 0, /* */ + ONE_NS_SECURITY_STATE, /* */ +} SECURITY_STATE; + +/* Interrupt Routing Mode. */ +typedef enum +{ + SGI_ROUTING_TO_SPECIFIC = 0, /* sgi interrupts routed to the PEs specified by affinity level. */ + SGI_ROUTING_TO_ANY = (1ULL << 40) /* sgi interrupts routed to all PEs in the system, excluding "self". */ +} SGI_ROUTING_MODE; + +typedef enum +{ + SPI_ROUTING_TO_SPECIFIC = 0, /* spi interrupts routed to the PE specified by affinity level. */ + SPI_ROUTING_TO_ANY = (1U << 31) /* spi interrupts routed to any PE defined as a participating node. */ +} SPI_ROUTING_MODE; + +typedef struct +{ + u32 instance_id; /* Id of device*/ + uintptr dis_base; /* Distributor base address */ + +} FGicConfig; + +typedef struct +{ + FGicConfig config; /* Configuration data structure */ + u32 is_ready; /* Device is ininitialized and ready*/ + uintptr redis_base; /* Redistributor base address for each core */ + SECURITY_STATE security ; + s32 max_spi_num; /* Max value of spi priority */ +} FGic; + +/* Initialization */ +FGicConfig *FGicLookupConfig(u32 instance_id); +FError FGicCfgInitialize(FGic *instance_p, const FGicConfig *input_config_p, uintptr redis_base); +void FGicDistrubutiorInit(FGic *instance_p); +FError FGicRedistrubutiorInit(FGic *instance_p); +void FGicCpuInterfaceInit(void); + +/* Operation interface */ +FError FGicIntEnable(FGic *instance_p, s32 int_id); +FError FGicIntDisable(FGic *instance_p, s32 int_id); +FError FGicSetPriority(FGic *instance_p, s32 int_id, u32 priority); +u32 FGicGetPriority(FGic *instance_p, s32 int_id); +FError FGicSetTriggerLevel(FGic *instance_p, s32 int_id, TRIGGER_LEVEL trigger_way); +u32 FGicGetTriggerLevel(FGic *instance_p, s32 int_id); +FError FGicSetSpiAffinityRouting(FGic *instance_p, s32 int_id, SPI_ROUTING_MODE route_mode, u64 affinity); +FError FGicGetAffinityRouting(FGic *instance_p, s32 int_id, SPI_ROUTING_MODE *route_mode_p, u64 *affinity_p); +FError FGicGenerateSgi(FGic *instance_p, s32 int_id, u32 target_list, SGI_ROUTING_MODE routing_mode, u64 affinity); +void FGicDeactionInterrupt(FGic *instance_p, s32 int_id); +s32 FGicAcknowledgeInt(FGic *instance_p); +void FGicSetPriorityFilter(FGic *instance_p, u32 priority_mask); +void FGicSetPriorityGroup(FGic *instance_p, u32 binary_point); +u32 FGicGetPriorityFilter(FGic *instance_p); +u32 FGicGetPriorityGroup(FGic *instance_p); + +#endif diff --git a/bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic_cpu_interface.S b/bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic_cpu_interface.S new file mode 100644 index 0000000000..2cc1d927a3 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic_cpu_interface.S @@ -0,0 +1,494 @@ +/* + * @Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * @FilePath: fgic_cpu_interface.S + * @Date: 2022-03-29 18:04:23 + * @LastEditTime: 2022-03-29 18:04:27 + * @Description:  This file is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + + +#ifdef __aarch64__ + +/* + * Mapping of MSR and MRS to physical and virtual CPU interface registers + * + * ARM Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-5 + */ +#define ICC_AP0R0_EL1 S3_0_C12_C8_4 +#define ICC_AP0R1_EL1 S3_0_C12_C8_5 +#define ICC_AP0R2_EL1 S3_0_C12_C8_6 +#define ICC_AP0R3_EL1 S3_0_C12_C8_7 + +#define ICC_AP1R0_EL1 S3_0_C12_C9_0 +#define ICC_AP1R1_EL1 S3_0_C12_C9_1 +#define ICC_AP1R2_EL1 S3_0_C12_C9_2 +#define ICC_AP1R3_EL1 S3_0_C12_C9_3 + +#define ICC_ASGI1R_EL1 S3_0_C12_C11_6 + +#define ICC_BPR0_EL1 S3_0_C12_C8_3 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 + +#define ICC_DIR_EL1 S3_0_C12_C11_1 + +#define ICC_EOIR0_EL1 S3_0_C12_C8_1 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 + +#define ICC_HPPIR0_EL1 S3_0_C12_C8_2 +#define ICC_HPPIR1_EL1 S3_0_C12_C12_2 + +#define ICC_IAR0_EL1 S3_0_C12_C8_0 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 + +#define ICC_IGRPEN0_EL1 S3_0_C12_C12_6 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_IGRPEN1_EL3 S3_6_C12_C12_7 + +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 + +#define ICC_SGI0R_EL1 S3_0_C12_C11_7 +#define ICC_SGI1R_EL1 S3_0_C12_C11_5 + +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 + +/* + * Mapping of MSR and MRS to virtual interface control registers + * + * ARM Generic Interrupt Controller Architecture Specification + * GIC architecture version 3.0 and version 4.0 + * Table 8-6 + */ +#define ICH_AP0R0_EL2 S3_4_C12_C8_0 +#define ICH_AP0R1_EL2 S3_4_C12_C8_1 +#define ICH_AP0R2_EL2 S3_4_C12_C8_2 +#define ICH_AP0R3_EL2 S3_4_C12_C8_3 + +#define ICH_AP1R0_EL2 S3_4_C12_C9_0 +#define ICH_AP1R1_EL2 S3_4_C12_C9_1 +#define ICH_AP1R2_EL2 S3_4_C12_C9_2 +#define ICH_AP1R3_EL2 S3_4_C12_C9_3 + +#define ICH_HCR_EL2 S3_4_C12_C11_0 + +#define ICH_VTR_EL2 S3_4_C12_C11_1 + +#define ICH_MISR_EL2 S3_4_C12_C11_2 + +#define ICH_EISR_EL2 S3_4_C12_C11_3 + +#define ICH_ELRSR_EL2 S3_4_C12_C11_5 + +#define ICH_VMCR_EL2 S3_4_C12_C11_7 + +#define ICH_LR0_EL2 S3_4_C12_C12_0 +#define ICH_LR1_EL2 S3_4_C12_C12_1 +#define ICH_LR2_EL2 S3_4_C12_C12_2 +#define ICH_LR3_EL2 S3_4_C12_C12_3 +#define ICH_LR4_EL2 S3_4_C12_C12_4 +#define ICH_LR5_EL2 S3_4_C12_C12_5 +#define ICH_LR6_EL2 S3_4_C12_C12_6 +#define ICH_LR7_EL2 S3_4_C12_C12_7 +#define ICH_LR8_EL2 S3_4_C12_C13_0 +#define ICH_LR9_EL2 S3_4_C12_C13_1 +#define ICH_LR10_EL2 S3_4_C12_C13_2 +#define ICH_LR11_EL2 S3_4_C12_C13_3 +#define ICH_LR12_EL2 S3_4_C12_C13_4 +#define ICH_LR13_EL2 S3_4_C12_C13_5 +#define ICH_LR14_EL2 S3_4_C12_C13_6 +#define ICH_LR15_EL2 S3_4_C12_C13_7 + +#define FUNC_DEFINE(name) .global name ;\ + .type name, @function ;\ + name: + + +/** + * @name: + * @msg: void FGicSetICC_SRE_EL3(GICC_SRE_BITS bits) --- Interrupt Controller System Register Enable + * @return {*} + */ +FUNC_DEFINE(FGicSetICC_SRE_EL3) + MSR ICC_SRE_EL3, x0 + ISB + RET + +/** + * @name: + * @msg: void FGicSetICC_SRE_EL2(GICC_SRE_BITS bits) + * @return {*} + */ +FUNC_DEFINE(FGicSetICC_SRE_EL2) + MSR ICC_SRE_EL2, x0 + ISB + RET + +/** + * @name: + * @msg: void FGicSetICC_SRE_EL1(GICC_SRE_BITS bits) + * @return {*} + */ +FUNC_DEFINE(FGicSetICC_SRE_EL1) + MSR ICC_SRE_EL1, x0 + ISB + RET + + + +/** + * @name: + * @msg: u32 FGicGetICC_SRE_EL3(void) -- Interrupt Controller System Register Enable + * @return {*} + */ +FUNC_DEFINE(FGicGetICC_SRE_EL3) + MRS x0, ICC_SRE_EL3 + RET + +/** + * @name: + * @msg: u32 FGicGetICC_SRE_EL2(void) + * @return {*} + */ +FUNC_DEFINE(FGicGetICC_SRE_EL2) + MRS x0, ICC_SRE_EL2 + RET + +/** + * @name: + * @msg: u32 FGicGetICC_SRE_EL1(void) + * @return {*} + */ +FUNC_DEFINE(FGicGetICC_SRE_EL1) + MRS x0, ICC_SRE_EL1 + RET + + +/** + * @name: + * @msg: void FGicEnableGroup0(void) + * @return {*} + */ +FUNC_DEFINE(FGicEnableGroup0) + MOV w0, #1 + MSR ICC_IGRPEN0_EL1, x0 + ISB + RET + +/** + * @name: + * @msg: void FGicDisableGroup0(void) + * @return {*} + */ +FUNC_DEFINE(FGicDisableGroup0) + MOV w0, #0 + MSR ICC_IGRPEN0_EL1, x0 + ISB + RET + + +/** + * @name: + * @msg: + * @return {*} + */ +FUNC_DEFINE(FGicEnableGroup1_EL3) + MOV w0, #1 + MSR ICC_IGRPEN1_EL3, x0 + ISB + RET + +/** + * @name: + * @msg: + * @return {*} + */ +FUNC_DEFINE(FGicEnableGroup1_EL1) + MOV w0, #1 + MSR ICC_IGRPEN1_EL1, x0 + ISB + RET + +/** + * @name: + * @msg: void FGicDisableGroup1_EL1(void) + * @return {*} + */ +FUNC_DEFINE(FGicDisableGroup1_EL1) + MOV w0, #0 + MSR ICC_IGRPEN1_EL1, x0 + ISB + RET + +/** + * @name: + * @msg: void FGicDisableGroup1_EL3(void) + * @return {*} + */ +FUNC_DEFINE(FGicDisableGroup1_EL3) + MOV w0, #0 + MSR ICC_IGRPEN1_EL3, x0 + ISB + RET + +/** + * @name: + * @msg: void FGicSetICC_CTLR_EL3(GICC_CTLR_BITS reg_bits) + * @return {*} + */ +FUNC_DEFINE(FGicSetICC_CTLR_EL3) + MSR ICC_CTLR_EL3, x0 + ISB + RET + +/** + * @name: + * @msg: void FGicSetICC_CTLR_EL1(GICC_CTLR_BITS reg_bits) + * @return {*} + */ +FUNC_DEFINE(FGicSetICC_CTLR_EL1) + MSR ICC_CTLR_EL1, x0 + ISB + RET + +/** + * @name: + * @msg: u32 FGicGetICC_CTLR_EL3(void) -- Controls aspects of the behavior of the GIC CPU interface and provides information about the features implemented. + * @return {*} + */ +FUNC_DEFINE(FGicGetICC_CTLR_EL3) + MRS x0, ICC_CTLR_EL3 + RET + +/** + * @name: + * @msg: u32 FGicGetICC_CTLR_EL1(void) -- Controls aspects of the behavior of the GIC CPU interface and provides information about the features implemented. + * @return {*} + */ +FUNC_DEFINE(FGicGetICC_CTLR_EL1) + MRS x0, ICC_CTLR_EL1 + RET + +/** + * @name: + * @msg: u32 FGicWriteICC_APR0(void) -- The PE reads this register to obtain the INTID of the signaled Group 0 interrupt. + * @return {*} + */ +FUNC_DEFINE(FGicGetICC_APR0) + MRS x0, ICC_IAR0_EL1 + RET + +/** + * @name: + * @msg: u32 FGicGetICC_APR1(void) -- The PE reads this register to obtain the INTID of the signaled Group 1 interrupt. + * @return {*} + */ +FUNC_DEFINE(FGicGetICC_APR1) + MRS x0, ICC_IAR1_EL1 + RET + +/** + * @name: + * @msg: void FGicSetICC_EOIR0(u32 intnum) -- /* A PE writes to this register to inform the CPU interface that it has completed the processing of the specified Group 0 interrupt + * @return {*} + */ +FUNC_DEFINE(FGicSetICC_EOIR0) + MSR ICC_EOIR0_EL1, x0 + ISB + RET + +/** + * @name: + * @msg: void FGicSetICC_EOIR1(u32 intnum) -- /* A PE writes to this register to inform the CPU interface that it has completed the processing of the specified Group 1 interrupt + * @return {*} + */ +FUNC_DEFINE(FGicSetICC_EOIR1) + MSR ICC_EOIR1_EL1, x0 + ISB + RET + +/** + * @name: + * @msg: void FGicSetICC_DIR(u32 intnum) -- When interrupt priority drop is separated from interrupt deactivation, a write to this register deactivates the specified interrupt. + * @return {*} + */ +FUNC_DEFINE(FGicSetICC_DIR) + MSR ICC_DIR_EL1, x0 + ISB + RET + +/** + * @name: + * @msg: void FGicSetICC_PMR(u32 priority_mask) -- Provides an interrupt priority filter. + * @return {*} + */ +FUNC_DEFINE(FGicSetICC_PMR) + MSR ICC_PMR_EL1, x0 + ISB + RET + +/** + * @name: + * @msg: u32 FGicGetICC_PMR(void) + * @return {*} + */ +FUNC_DEFINE(FGicGetICC_PMR) + MRS x0,ICC_PMR_EL1 + RET + +/** + * @name: + * @msg: u32 FGicGetICC_BPR1(void) --- Defines the point at which the priority value fields split into two parts, the group priority field and the subpriority field. The group priority field determines Group 1 interrupt preemption. + * @return {*} + */ +FUNC_DEFINE(FGicGetICC_BPR1) + MRS x0,ICC_BPR1_EL1 + RET + +/** + * @name: + * @msg: void FGicSetICC_BPR1(u32 binary_point) + * @return {*} + */ +FUNC_DEFINE(FGicSetICC_BPR1) + MSR ICC_BPR1_EL1, x0 + ISB + RET + +/** + * @name: + * @msg: u32 FGicGetICC_BPR0(void) --- Defines the point at which the priority value fields split into two parts, the group priority field and the subpriority field. The group priority field determines Group 0 interrupt preemption. + * @return {*} + */ +FUNC_DEFINE(FGicGetICC_BPR0) + MRS x0,ICC_BPR0_EL1 + RET + +/** + * @name: + * @msg: void FGicSetICC_BPR0(u32 binary_point) + * @return {*} + */ +FUNC_DEFINE(FGicSetICC_BPR0) + MSR ICC_BPR0_EL1, x0 + ISB + RET + +/** + * @name: + * @msg: u32 FGicGetICC_HPPIR1(void) --- Indicates the highest priority pending Group 1 interrupt on the CPU interface. + * @return {*} + */ +FUNC_DEFINE(FGicGetICC_HPPIR1) + MRS x0,ICC_HPPIR1_EL1 + RET + +/** + * @name: + * @msg: void FGicSetICC_HPPIR1(u32 binary_point) + * @return {*} + */ +FUNC_DEFINE(FGicSetICC_HPPIR1) + MSR ICC_HPPIR1_EL1, x0 + ISB + RET + +/** + * @name: + * @msg: u32 FGicGetICC_HPPIR0(void) --- Indicates the highest priority pending Group 0 interrupt on the CPU interface. + * @return {*} + */ +FUNC_DEFINE(FGicGetICC_HPPIR0) + MRS x0,ICC_HPPIR0_EL1 + RET + +/** + * @name: + * @msg: void FGicSetICC_HPPIR0(u32 binary_point) + * @return {*} + */ +FUNC_DEFINE(FGicSetICC_HPPIR0) + MSR ICC_HPPIR0_EL1, x0 + ISB + RET + +/** + * @name: + * @msg: u32 FGicGetICC_RPR(void) --- Indicates the Running priority of the CPU interface. + * @return {*} + */ +FUNC_DEFINE(FGicGetICC_RPR) + MRS x0,ICC_RPR_EL1 + RET + +/* SGI interface */ +/** + * @name: + * @msg: void FGicSetICC_SGI0R(u32 intnum,u32 target_list,GICC_SGIR_IRM_BITS mode,u64 affinity_list) --- Generates Secure Group 0 SGIs + * @return {*} + */ +FUNC_DEFINE(FGicSetICC_SGI0R) + ORR x0, x0, x1 + ORR x0, x0, x2 + ORR x0, x0, x3 + MSR ICC_SGI0R_EL1, x0 + ISB + RET + +/** + * @name: + * @msg: void FGicSetICC_SGI1R(u32 intnum,u32 target_list,GICC_SGIR_IRM_BITS mode,u64 affinity_list) + * @return {*} + */ +FUNC_DEFINE(FGicSetICC_SGI1R) + ORR x0, x0, x1 + ORR x0, x0, x2 + ORR x0, x0, x3 + MSR ICC_SGI1R_EL1, x0 + ISB + RET + +/** + * @name: + * @msg: void FGicSetICC_ASGI1R(u32 intnum,u32 target_list,GICC_SGIR_IRM_BITS mode,u64 affinity_list) + * @return {*} + */ +FUNC_DEFINE(FGicSetICC_ASGI1R) + ORR x0, x0, x1 + ORR x0, x0, x2 + ORR x0, x0, x3 + MSR ICC_ASGI1R_EL1, x0 + ISB + RET + + +#else + + + + +#endif + + diff --git a/bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic_cpu_interface.h b/bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic_cpu_interface.h new file mode 100644 index 0000000000..9bcdd84c7d --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic_cpu_interface.h @@ -0,0 +1,547 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgic_cpu_interface.h + * Date: 2022-03-28 14:55:27 + * LastEditTime: 2022-03-28 14:55:27 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef DRIVERS_CPU_INTERFACE_H +#define DRIVERS_CPU_INTERFACE_H +#include "ftypes.h" + + +#define FGIC_ICC_SGI1R_INTID_MASK 0xFULL /* The INTID of the SGI. */ + +#define GICC_SGIR_IRM_BITS SGI_ROUTING_MODE + +typedef enum +{ + GICC_SRE_SRE = (1 << 0), + GICC_SRE_DFB = (1 << 1), + GICC_SRE_DIB = (1 << 2), + GICC_SRE_ENABLE = (1 << 3) +} GICC_SRE_BITS; + +typedef enum +{ + GICC_CTLR_CBPR = (1 << 0), + GICC_CTLR_CBPR_EL1S = (1 << 0), + GICC_CTLR_EOIMODE = (1 << 1), + GICC_CTLR_CBPR_EL1NS = (1 << 1), + GICC_CTLR_EOIMODE_EL3 = (1 << 2), + GICC_CTLR_EOIMODE_EL1S = (1 << 3), + GICC_CTLR_EOIMODE_EL1NS = (1 << 4), /* GICC_EOIR and GICC_AEOIR provide priority drop functionality only. GICC_DIR provides interrupt deactivation functionality. */ + GICC_CTLR_RM = (1 << 5), + GICC_CTLR_PMHE = (1 << 6) +} GICC_CTLR_BITS; + + +#ifdef __aarch64__ + +void FGicSetICC_SRE_EL3(GICC_SRE_BITS bits); +void FGicSetICC_SRE_EL2(GICC_SRE_BITS bits); +void FGicSetICC_SRE_EL1(GICC_SRE_BITS bits); +u32 FGicGetICC_SRE_EL3(void); +u32 FGicGetICC_SRE_EL2(void); +u32 FGicGetICC_SRE_EL1(void); +void FGicEnableGroup0(void); +void FGicDisableGroup0(void); +void FGicEnableGroup1_EL1(void); +void FGicEnableGroup1_EL3(void); +void FGicDisableGroup1_EL1(void); +void FGicDisableGroup1_EL3(void); +void FGicSetICC_CTLR_EL3(GICC_CTLR_BITS reg_bits); +void FGicSetICC_CTLR_EL1(GICC_CTLR_BITS reg_bits); +u32 FGicGetICC_CTLR_EL3(void); +u32 FGicGetICC_CTLR_EL1(void); +u32 FGicWriteICC_APR0(void); +s32 FGicGetICC_APR1(void); +void FGicSetICC_EOIR0(u32 intnum); +void FGicSetICC_EOIR1(u32 intnum); +void FGicSetICC_DIR(u32 intnum); +void FGicSetICC_PMR(u32 priority_mask); +u32 FGicGetICC_PMR(void); +u32 FGicGetICC_BPR1(void); +void FGicSetICC_BPR1(u32 binary_point); +u32 FGicGetICC_BPR0(void); +void FGicSetICC_BPR0(u32 binary_point); +u32 FGicGetICC_HPPIR1(void); +void FGicSetICC_HPPIR1(u32 binary_point); +u32 FGicGetICC_HPPIR0(void); +void FGicSetICC_HPPIR0(u32 binary_point); +u32 FGicGetICC_RPR(void); +/* SGI interface */ +void FGicSetICC_SGI0R(u32 intnum, u32 target_list, GICC_SGIR_IRM_BITS mode, u64 affinity_list); +void FGicSetICC_SGI1R(u32 intnum, u32 target_list, GICC_SGIR_IRM_BITS mode, u64 affinity_list); +void FGicSetICC_ASGI1R(u32 intnum, u32 target_list, GICC_SGIR_IRM_BITS mode, u64 affinity_list); + + + +#else /* aarch32 */ + + + +/* For AArch32 state, accesses to GIC registers that are visible in the System register */ +#define FGIC_SYS_READ32(CR, Rt) __asm__ volatile("MRC " CR \ + : "=r"(Rt) \ + : \ + : "memory") +#define FGIC_SYS_WRITE32(CR, Rt) __asm__ volatile("MCR " CR \ + : \ + : "r"(Rt) \ + : "memory") + +#define FGIC_SYS_WRITE64(cp, op1, Rt, CRm) __asm__ volatile("MCRR p" #cp ", " #op1 ", %Q0, %R0, c" #CRm \ + : \ + : "r"(Rt) \ + : "memory") + + +#define ICC_IAR0 "p15, 0, %0, c12, c8, 0" +#define ICC_IAR1 "p15, 0, %0, c12, c12, 0" +/* writes to this register to inform the CPU interface +that it has completed the processing */ +#define ICC_EOIR0 "p15, 0, %0, c12, c8, 1" +#define ICC_EOIR1 "p15, 0, %0, c12, c12, 1" +/* GET the INTID of highest priority pending interrupt */ +#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 FGicSetICC_SRE_EL1 FGicSetICC_SRE +#define FGicSetICC_SRE_EL2 FGicSetICC_SRE +#define FGicSetICC_SRE_EL3 FGicSetICC_SRE + +/** + * @name: + * @msg: void FGicSetICC_SRE(GICC_SRE_BITS bits) --- Interrupt Controller System Register Enable + * @return {*} + */ +static inline void FGicSetICC_SRE(GICC_SRE_BITS bits) +{ + FGIC_SYS_WRITE32(ICC_SRE, (u32)bits); + __asm__ volatile("isb 0xF" :: + : "memory"); +} + + +#define FGicGetICC_SRE_EL3 FGicGetICC_SRE +#define FGicGetICC_SRE_EL2 FGicGetICC_SRE +#define FGicGetICC_SRE_EL1 FGicGetICC_SRE + +/** + * @name: + * @msg: u32 FGicGetICC_SRE(void) -- Interrupt Controller System Register Enable + * @return {*} + */ +static inline u32 FGicGetICC_SRE(void) +{ + u32 value; + + FGIC_SYS_READ32(ICC_SRE, value); + return value; +} + + +/** + * @name: + * @msg: void FGicEnableGroup0(void) + * @return {*} + */ +static inline void FGicEnableGroup0(void) +{ + u32 value = 1; + FGIC_SYS_WRITE32(ICC_IGRPEN0, value); + __asm__ volatile("isb 0xF" :: + : "memory"); +} + +/** + * @name: + * @msg: void FGicDisableGroup0(void) + * @return {*} + */ +static inline void FGicDisableGroup0(void) +{ + u32 value = 0; + FGIC_SYS_WRITE32(ICC_IGRPEN0, value); + __asm__ volatile("isb 0xF" :: + : "memory"); +} + + +#define FGicEnableGroup1_EL3 FGicEnableGroup1 +#define FGicEnableGroup1_EL1 FGicEnableGroup1 + +/** + * @name: + * @msg: + * @return {*} + */ +static inline void FGicEnableGroup1(void) +{ + u32 value = 1; + FGIC_SYS_WRITE32(ICC_IGRPEN1, value); + __asm__ volatile("isb 0xF" :: + : "memory"); +} + + + +#define FGicDisableGroup1_EL3 FGicDisableGroup1 +#define FGicDisableGroup1_EL1 FGicDisableGroup1 + +/** + * @name: + * @msg: void FGicDisableGroup1(void) + * @return {*} + */ +static inline void FGicDisableGroup1(void) +{ + u32 value = 0; + FGIC_SYS_WRITE32(ICC_IGRPEN1, value); + __asm__ volatile("isb 0xF" :: + : "memory"); +} + +#define FGicSetICC_CTLR_EL3 FGicSetICC_CTLR +#define FGicSetICC_CTLR_EL1 FGicSetICC_CTLR + +/** + * @name: + * @msg: void FGicSetICC_CTLR(GICC_CTLR_BITS reg_bits) + * @return {*} + */ +static inline void FGicSetICC_CTLR(GICC_CTLR_BITS bits) +{ + FGIC_SYS_WRITE32(ICC_CTLR, bits); + __asm__ volatile("isb 0xF" :: + : "memory"); +} + +#define FGicGetICC_CTLR_EL3 FGicGetICC_CTLR +#define FGicGetICC_CTLR_EL1 FGicGetICC_CTLR + +/** + * @name: + * @msg: static inline u32 FGicGetICC_CTLR(void) -- Controls aspects of the behavior of the GIC CPU interface and provides information about the features implemented. + * @return {*} + */ +static inline u32 FGicGetICC_CTLR(void) +{ + u32 reg; + FGIC_SYS_READ32(ICC_CTLR, reg); + return reg; +} + +/** + * @name: + * @msg: static inline u32 FGicWriteICC_APR0(void) -- The PE reads this register to obtain the INTID of the signaled Group 0 interrupt. + * @return {*} + */ +static inline u32 FGicWriteICC_APR0(void) +{ + u32 reg; + FGIC_SYS_READ32(ICC_IAR0, reg); + return reg; +} + + +/** + * @name: + * @msg: static inline s32 FGicGetICC_APR1(void) -- The PE reads this register to obtain the INTID of the signaled Group 1 interrupt. + * @return {*} + */ +static inline s32 FGicGetICC_APR1(void) +{ + s32 reg; + FGIC_SYS_READ32(ICC_IAR1, reg); + return reg; +} + + +/** + * @name: + * @msg: static inline void FGicSetICC_EOIR0(u32 intnum) -- /* A PE writes to this register to inform the CPU interface that it has completed the processing of the specified Group 0 interrupt + * @return {*} + * @param {u32} intnum + */ +static inline void FGicSetICC_EOIR0(u32 intnum) +{ + FGIC_SYS_WRITE32(ICC_EOIR0, intnum); + __asm__ volatile("isb 0xF" :: + : "memory"); +} + + +/** + * @name: + * @msg: static inline void FGicSetICC_EOIR1(u32 intnum) -- /* A PE writes to this register to inform the CPU interface that it has completed the processing of the specified Group 1 interrupt + * @return {*} + */ +static inline void FGicSetICC_EOIR1(u32 intnum) +{ + FGIC_SYS_WRITE32(ICC_EOIR1, intnum); + __asm__ volatile("isb 0xF" :: + : "memory"); +} + +/** + * @name: + * @msg: static inline void FGicSetICC_DIR(u32 intnum) -- When interrupt priority drop is separated from interrupt deactivation, a write to this register deactivates the specified interrupt. + * @return {*} + */ +static inline void FGicSetICC_DIR(u32 intnum) +{ + FGIC_SYS_WRITE32(ICC_DIR, intnum); + __asm__ volatile("isb 0xF" :: + : "memory"); +} + +/** + * @name: + * @msg: static inline void FGicSetICC_PMR(u32 priority_mask) -- Provides an interrupt priority filter. + * @return {*} + */ +/** + * @name: + * @msg: + * @param {u32} priority_mask is level for the CPU interface. If the priority of an interrupt is higher than the value + * indicated by this field, the interface signals the interrupt to the PE. + */ +static inline void FGicSetICC_PMR(u32 priority_mask) +{ + FGIC_SYS_WRITE32(ICC_PMR, priority_mask); + __asm__ volatile("isb 0xF" :: + : "memory"); +} + + +/** + * @name: + * @msg: static inline u32 FGicGetICC_PMR(void) + * @return {*} + */ +static inline u32 FGicGetICC_PMR(void) +{ + u32 reg; + FGIC_SYS_READ32(ICC_PMR, reg); + return reg; +} + + +/** + * @name: + * @msg: static inline u32 FGicGetICC_BPR1(void) --- Defines the point at which the priority value fields split into two parts, the group priority field and the subpriority field. The group priority field determines Group 1 interrupt preemption. + * @return {*} + */ +static inline u32 FGicGetICC_BPR1(void) +{ + u32 reg; + FGIC_SYS_READ32(ICC_BPR1, reg); + return reg; +} + +/** + * @name: + * @msg: static inline void FGicSetICC_BPR1(u32 binary_point) + * @return {*} + */ +static inline void FGicSetICC_BPR1(u32 binary_point) +{ + FGIC_SYS_WRITE32(ICC_BPR1, binary_point); + __asm__ volatile("isb 0xF" :: + : "memory"); +} + +/** + * @name: + * @msg: static inline u32 FGicGetICC_BPR0(void) --- Defines the point at which the priority value fields split into two parts, the group priority field and the subpriority field. The group priority field determines Group 0 interrupt preemption. + * @return {*} + */ +static inline u32 FGicGetICC_BPR0(void) +{ + u32 reg; + FGIC_SYS_READ32(ICC_BPR0, reg); + return reg; +} + + +/** + * @name: + * @msg: static inline void FGicSetICC_BPR0(u32 binary_point) + * @return {*} + */ +static inline void FGicSetICC_BPR0(u32 binary_point) +{ + FGIC_SYS_WRITE32(ICC_BPR0, binary_point); + __asm__ volatile("isb 0xF" :: + : "memory"); +} + +/** + * @name: + * @msg: static inline u32 FGicGetICC_HPPIR1(void) --- Indicates the highest priority pending Group 1 interrupt on the CPU interface. + * @return {*} + */ +static inline u32 FGicGetICC_HPPIR1(void) +{ + u32 reg; + FGIC_SYS_READ32(ICC_HPPIR1, reg); + return reg; +} + + +/** + * @name: + * @msg: static inline void FGicSetICC_HPPIR1(u32 binary_point) + * @return {*} + */ +static inline void FGicSetICC_HPPIR1(u32 binary_point) +{ + FGIC_SYS_WRITE32(ICC_HPPIR1, binary_point); + __asm__ volatile("isb 0xF" :: + : "memory"); +} + +/** + * @name: + * @msg: static inline u32 FGicGetICC_HPPIR0(void) --- Indicates the highest priority pending Group 0 interrupt on the CPU interface. + * @return {*} + */ +static inline u32 FGicGetICC_HPPIR0(void) +{ + u32 reg; + FGIC_SYS_READ32(ICC_HPPIR0, reg); + return reg; +} + + +/** + * @name: + * @msg: static inline void FGicSetICC_HPPIR0(u32 binary_point) + * @return {*} + */ +static inline void FGicSetICC_HPPIR0(u32 binary_point) +{ + FGIC_SYS_WRITE32(ICC_HPPIR0, binary_point); + __asm__ volatile("isb 0xF" :: + : "memory"); +} + +/** + * @name: + * @msg: static inline u32 FGicGetICC_RPR(void) --- Indicates the Running priority of the CPU interface. + * @return {*} + */ +static inline u32 FGicGetICC_RPR(void) +{ + u32 reg; + FGIC_SYS_READ32(ICC_RPR, reg); + return reg; +} +/* SGI interface */ + +/** + * @name: + * @msg: static inline void FGicSetICC_SGI0R(u32 intnum,u32 target_list,GICC_SGIR_IRM_BITS mode,u64 affinity_list) --- Generates Secure Group 0 SGIs + * @return {*} + */ +/** + * @name: + * @msg: + * @return {*} + * @param {u32} intnum_bit + * @param {u32} target_list + * @param {GICC_SGIR_IRM_BITS} irm_bit + * @param {u64} affinity_list + */ +static inline void FGicSetICC_SGI0R(u32 intnum_bit, u32 target_list, GICC_SGIR_IRM_BITS irm_bit, u64 affinity_list) +{ + u64 sgi_val; + sgi_val = intnum_bit; /* The INTID of the SGI. */ + sgi_val |= affinity_list ; /* Aff3.Aff2.Aff1 */ + sgi_val |= irm_bit ; /* Interrupt Routing Mode. */ + sgi_val |= target_list; /* Target List. The set of PEs for which SGI interrupts will be generated. */ + + __asm__ volatile("dsb 0xF" :: + : "memory"); + FGIC_SYS_WRITE64(15, 2, sgi_val, 12); + __asm__ volatile("isb 0xF" :: + : "memory"); +} + +/** + * @name: + * @msg: static inline void FGicSetICC_SGI1R(u32 intnum,u32 target_list,GICC_SGIR_IRM_BITS mode,u64 affinity_list) + * @return {*} + */ +static inline void FGicSetICC_SGI1R(u32 intnum_bit, u32 target_list, GICC_SGIR_IRM_BITS irm_bit, u64 affinity_list) +{ + u64 sgi_val; + sgi_val = intnum_bit; /* The INTID of the SGI. */ + sgi_val |= affinity_list ; /* Aff3.Aff2.Aff1 */ + sgi_val |= irm_bit ; /* Interrupt Routing Mode. */ + sgi_val |= target_list; /* Target List. The set of PEs for which SGI interrupts will be generated. */ + + __asm__ volatile("dsb 0xF" :: + : "memory"); + FGIC_SYS_WRITE64(15, 0, sgi_val, 12); + __asm__ volatile("isb 0xF" :: + : "memory"); +} + + +/** + * @name: + * @msg: static inline void FGicSetICC_ASGI1R(u32 intnum,u32 target_list,GICC_SGIR_IRM_BITS mode,u64 affinity_list) + * @return {*} + */ +static inline void FGicSetICC_ASGI1R(u32 intnum_bit, u32 target_list, GICC_SGIR_IRM_BITS irm_bit, u64 affinity_list) +{ + u64 sgi_val; + sgi_val = intnum_bit; /* The INTID of the SGI. */ + sgi_val |= affinity_list ; /* Aff3.Aff2.Aff1 */ + sgi_val |= irm_bit ; /* Interrupt Routing Mode. */ + sgi_val |= target_list; /* Target List. The set of PEs for which SGI interrupts will be generated. */ + + __asm__ volatile("dsb 0xF" :: + : "memory"); + FGIC_SYS_WRITE64(15, 1, sgi_val, 12); + __asm__ volatile("isb 0xF" :: + : "memory"); +} + + +#endif + +#endif + diff --git a/bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic_distributor.h b/bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic_distributor.h new file mode 100644 index 0000000000..1c163dd1fb --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic_distributor.h @@ -0,0 +1,165 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgic_distributor.h + * Date: 2022-03-28 15:18:56 + * LastEditTime: 2022-03-28 15:18:56 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ +#ifndef DRIVERS_GIC_FGIC_DISTRIBUTOR_H +#define DRIVERS_GIC_FGIC_DISTRIBUTOR_H + +#include "fgic.h" +#include "fgic_hw.h" +#include "ftypes.h" + +#define GICD_ICFGR_MODE TRIGGER_LEVEL +#define GICD_IRPITER_MODE SPI_ROUTING_MODE + +typedef enum +{ + GICD_CTLR_ENABLE_GRP0 = (1 << 0), + GICD_CTLR_ENABLE_GRP1_NS = (1 << 1), + GICD_CTLR_ENABLE_GRP1A = (1 << 1), + GICD_CTLR_ENABLE_GRP1S = (1 << 2), + GICD_CTLR_ENABLE_ALL = (1 << 2) | (1 << 1) | (1 << 0), + GICD_CTLR_BIT_ARE_S = (1 << 4), /* Enable Secure state affinity routing , for single Security state ,this bit is */ + GICD_CTLR_BIT_ARE_NS = (1 << 5), /* Enable Non-Secure state affinity routing */ + GICD_CTLR_BIT_DS = (1 << 6), /* Disable Security support */ + GICD_CTLR_BIT_E1NWF = (1 << 7) /* Enable "1-of-N" wakeup model */ +} GICD_CTLR_VALUE; + +typedef enum +{ + GICD_GROUP_G0S = 0, + GICD_GROUP_G1NS = (1 << 0), + GICD_GROUP_G1S = (1 << 2), +} GICD_GROUP_SECURE_MODE; + +static inline void FGicSetGicd(uintptr dist_base, GICD_CTLR_VALUE ctrl_value) +{ + FGIC_GICD_CTLR_WRITE(dist_base, ctrl_value); +} + +/** + * @name: FGicEnableSPI + * @msg: configure the priority for a shared peripheral interrupt + * @param {FGic} *gic_p is a pointer to the FGic instance. + * @param {u32} spi_id spi interrupt identifier ,value range 32-1019 + */ +static inline void FGicEnableSPI(uintptr dist_base, u32 spi_id) +{ + FGIC_GICD_ISENABLER_WRITE_N_MASK(dist_base, spi_id, (1U << (spi_id % 32))); +} + +static inline void FGicDisableSPI(uintptr dist_base, u32 spi_id) +{ + FGIC_GICD_ICENABLER_WRITE_N_MASK(dist_base, spi_id, (1U << (spi_id % 32))); +} + +static inline void FGicSetSpiPriority(uintptr dist_base, u32 spi_id, u32 priority) +{ + u32 mask; + + /* For SPIs , has one byte-wide entry per interrupt */ + mask = FGIC_GICD_IPRIORITYR_READ_N_MASK(dist_base, spi_id); + mask &= ~FGIC_GICD_IPRIORITYR_VALUE_MASK(spi_id); + mask |= ((priority & 0xffU) << (spi_id % 4) * 8U); + FGIC_GICD_IPRIORITYR_WRITE_N_MASK(dist_base, spi_id, mask); +} + +static inline u32 FGicGetSpiPriority(uintptr dist_base, u32 spi_id) +{ + u32 mask; + + /* For SPIs , has one byte-wide entry per interrupt */ + mask = FGIC_GICD_IPRIORITYR_READ_N_MASK(dist_base, spi_id); + + return (mask >> ((spi_id % 4U) * 8U)) & 0xFFU ; +} + +static inline void FGicSetSpiRoute(uintptr dist_base, u32 spi_id, GICD_IRPITER_MODE route_mode, u64 affinity) +{ + u32 bank; + + /* For SPIs ,has one doubleword-wide entry per interrupt */ + bank = spi_id & FGIC_GICD_IROUTER_RANGE_LIMIT; + __asm__ volatile("dsb 0xF" ::: "memory"); + FGIC_GICD_IROUTER_WRITE(dist_base, bank, affinity | route_mode); + __asm__ volatile("isb 0xF" ::: "memory"); +} + +static inline u64 FGicGetSpiRoute(uintptr dist_base, u32 spi_id) +{ + u32 bank; + /* For SPIs ,has one doubleword-wide entry per interrupt */ + bank = spi_id & FGIC_GICD_IROUTER_RANGE_LIMIT; + return FGIC_GICD_IROUTER_READ(dist_base, bank); +} + +static inline void FGicSetSpiLevel(uintptr dist_base, u32 spi_id, GICD_ICFGR_MODE mode) +{ + u32 mask ; + mask = FGIC_GICD_ICFGR_READ_N_MASK(dist_base, spi_id); + mask &= ~FGIC_GICD_ICFGR_VALUE_MASK(spi_id); + mask |= (mode << FGIC_GICD_ICFGR_VALUE_OFFSET(spi_id)); + FGIC_GICD_ICFGR_WRITE_N_MASK(dist_base, spi_id, mask); +} + +static inline u32 FGicGetSpiLevel(uintptr dist_base, u32 spi_id) +{ + u32 mask ; + mask = FGIC_GICD_ICFGR_READ_N_MASK(dist_base, spi_id); + return (mask >> ((spi_id % 16U) >> 1U)) ; +} + +static inline void FGicSetSpiSecurity(uintptr dist_base, u32 spi_id, GICD_GROUP_SECURE_MODE mode) +{ + u32 mask ; + /* Group status */ + mask = FGIC_GICD_IGROUPR_READ_N_MASK(dist_base, spi_id); + mask &= ~FGIC_GICD_IGROUPR_VALUE_MASK(spi_id); + + mask |= ((mode & 0x1) << (spi_id % 32)); + FGIC_GICD_IGROUPR_WRITE_N_MASK(dist_base, spi_id, mask); + + /* Group modifier */ + mask = FGIC_GICD_IGRPMODR_READ_N_MASK(dist_base, spi_id); + mask &= ~FGIC_GICD_IGRPMODR_VALUE_MASK(spi_id); + + mask |= (((mode & 0x2) >> 1) << (spi_id % 32)); + FGIC_GICD_IGRPMODR_WRITE_N_MASK(dist_base, spi_id, mask); +} + +static inline u32 FGicGetSpiSecurity(uintptr dist_base, u32 spi_id) +{ + u32 mask ; + u32 group_status, group_modifier; + /* Group status */ + mask = FGIC_GICD_IGROUPR_READ_N_MASK(dist_base, spi_id); + mask &= FGIC_GICD_IGROUPR_VALUE_MASK(spi_id); + group_status = (mask >> (spi_id % 32)); + + /* Group modifier */ + mask = FGIC_GICD_IGRPMODR_READ_N_MASK(dist_base, spi_id); + mask &= FGIC_GICD_IGRPMODR_VALUE_MASK(spi_id); + group_modifier = (mask >> (spi_id % 32)); + + return ((group_modifier << 1) | group_status); +} + +#endif // ! diff --git a/bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic_g.c b/bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic_g.c new file mode 100644 index 0000000000..3f40151fc7 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic_g.c @@ -0,0 +1,32 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgic_g.c + * Date: 2022-03-30 14:57:43 + * LastEditTime: 2022-03-30 14:57:43 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ +#include "fparameters.h" +#include "fgic.h" + +FGicConfig fgic_config[FGIC_NUM] = +{ + { + .instance_id = 0, /* Id of device */ + .dis_base = GICV3_DISTRIBUTOR_BASEADDRESS, /* Distributor base address */ + } +}; \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic_hw.h b/bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic_hw.h new file mode 100644 index 0000000000..a8947ebbef --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic_hw.h @@ -0,0 +1,315 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgic_hw.h + * Date: 2022-03-24 11:44:48 + * LastEditTime: 2022-03-24 11:44:48 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef DRIVERS_GIC_FGIC_HW_H +#define DRIVERS_GIC_FGIC_HW_H + +#include "ftypes.h" +#include "fio.h" +#include "fkernel.h" + +/* Distributor */ +#define FGIC_GICD_CTLR_OFFSET 0x00000000U /* Distributor Control Register ,RW */ +#define FGIC_GICD_TYPER_OFFSET 0x00000004U /* Interrupt Controller Type Register ,RO */ +#define FGIC_GICD_IIDR_OFFSET 0x00000008U /* Distributor Implementer Identification Register ,RO */ +#define FGIC_GICD_TYPER2_OFFSET 0x0000000CU /* Interrupt controller Type Register 2,RO */ +#define FGIC_GICD_STATUSR_OFFSET 0x00000010U /* Error Reporting Status Register, optional ,RW */ +#define FGIC_GICD_SETSPI_NSR_OFFSET 0x00000040U /* Set SPI Register ,WO */ +#define FGIC_GICD_CLRSPI_NSR_OFFSET 0x00000048U /* Clear SPI Register ,WO */ +#define FGIC_GICD_SETSPI_SR_OFFSET 0x00000050U /* Set SPI, Secure Register ,WO */ +#define FGIC_GICD_CLRSPI_SR_OFFSET 0x00000058U /* Clear SPI, Secure Register ,WO */ +#define FGIC_GICD_IGROUPR_OFFSET 0x00000080U /* Interrupt Group Registers ,RW */ + +#define FGIC_GICD_ISENABLER_OFFSET 0x00000100U /* Interrupt Set-Enable Registers ,RW */ +#define FGIC_GICD_ICENABLER_OFFSET 0x00000180U /* Interrupt Clear-Enable Registers ,RW */ +#define FGIC_GICD_ISPENDR_OFFSET 0x00000200U /* Interrupt Set-Pending Registers ,RW */ +#define FGIC_GICD_ICPENDR_OFFSET 0x00000280U /* Interrupt Clear-Pending Registers ,RW */ +#define FGIC_GICD_ISACTIVER_OFFSET 0x00000300U /* Interrupt Set-Active Registers ,RW */ +#define FGIC_GICD_ICACTIVER_OFFSET 0x00000380U /* Interrupt Clear-Active Registers ,RW */ +#define FGIC_GICD_IPRIORITYR_OFFSET 0x00000400U /* Interrupt Priority Registers ,RW */ +#define FGIC_GICD_ITARGETSR_OFFSET 0x00000800U /* Interrupt Processor Targets Registers ,RO */ + +#define FGIC_GICD_ICFGR_OFFSET 0x00000C00U /* Interrupt Configuration Registers ,RW */ +#define FGIC_GICD_IGRPMODR_OFFSET 0x00000D00U /* Interrupt Group Modifier Registers */ + +#define FGIC_GICD_NSACR_OFFSET 0x00000E00U /* Non-secure Access Control ,RW */ +#define FGIC_GICD_SGIR_OFFSET 0x00000F00U /* Software Generated Interrupt Register ,WO */ +#define FGIC_GICD_CPENDSGIR_OFFSET 0x00000F10U /* SGI Clear-Pending Registers ,RW */ +#define FGIC_GICD_SPENDSGIR_OFFSET 0x00000F20U /* SGI Set-Pending Registers ,RW */ + +#define FGIC_GICD_IGROUPR_E_OFFSET 0x00001000U /* Interrupt Group Registers for extended SPI ,RW */ + +#define FGIC_GICD_ISENABLER_E_OFFSET 0x00001200U /* Interrupt Set-Enable for extended SPI ,RW */ + +#define FGIC_GICD_ICENABLER_E_OFFSET 0x00001400U /* Interrupt Clear-Enable for extended SPI ,RW */ + +#define FGIC_GICD_ISPENDR_E_OFFSET 0x00001600U /* Interrupt Set-Pend for extended SPI range ,RW */ +#define FGIC_GICD_ICPENDR_E_OFFSET 0x00001800U /* Interrupt Clear-Pend for extended SPI ,RW */ + +#define FGIC_GICD_ISACTIVER_E_OFFSET 0x00001A00U /* Interrupt Set-Active for extended SPI ,RW */ + +#define FGIC_GICD_ICACTIVER_E_OFFSET 0x00001C00U /* Interrupt Clear-Active for extended SPI ,RW */ + +#define FGIC_GICD_IPRIORITYR_E_OFFSET 0x00002000U /* Interrupt Priority for extended SPI range ,RW */ +#define FGIC_GICD_ICFGR_E_OFFSET 0x00003000U /* Extended SPI Configuration Register ,RW */ +#define FGIC_GICD_IGRPMODR_E_OFFSET 0x00003400U /* Interrupt Group Modifier for extended SPI ,RW */ +#define FGIC_GICD_NSACR_E_OFFSET 0x00003600U /* Non-secure Access Control Registers for extended SPI range ,RW */ + +#define FGIC_GICD_IROUTER_OFFSET 0x00006000U /* Interrupt Routing Registers ,RW ,The offset of the GICD_IROUTER register is 0x6000 + 8n. */ +#define FGIC_GICD_IROUTER_E_OFFSET 0x00008000U /* Interrupt Routing Registers for extended SPI range ,RW */ + +/* Redistributor - RD_BASE */ +/* Each Redistributor defines two 64KB frames in the physical address map */ +#define FGIC_GICR_CTLR_OFFSET 0x00000000U /* See the register description Redistributor Control Register ,RW */ +#define FGIC_GICR_IIDR_OFFSET 0x00000004U /* Implementer Identification Register ,RO */ +#define FGIC_GICR_TYPER_OFFSET 0x00000008U /* Redistributor Type Register ,RO */ +#define FGIC_GICR_STATUSR_OFFSET 0x00000010U /* Error Reporting Status Register, optional ,RW */ +#define FGIC_GICR_WAKER_OFFSET 0x00000014U /* See the register description Redistributor Wake Register ,RW */ +#define FGIC_GICR_MPAMIDR_OFFSET 0x00000018U /* Report maximum PARTID and PMG Register ,RO */ +#define FGIC_GICR_PARTIDR_OFFSET 0x0000001CU /* Set PARTID and PMG Register ,RW */ + +#define FGIC_GICR_SETLPIR_OFFSET 0x00000040U /* Set LPI Pending Register ,WO */ +#define FGIC_GICR_CLRLPIR_OFFSET 0x00000048U /* Clear LPI Pending Register ,WO */ + +#define FGIC_GICR_PROPBASER_OFFSET 0x00000070U /* Redistributor Properties Base Address Register ,RW */ +#define FGIC_GICR_PENDBASER_OFFSET 0x00000078U /* Redistributor LPI Pending Table Base Address Register ,RW */ + +/* Redistributor - SGI_BASE */ + +#define FGIC_GICR_SGI_BASE_OFFSET 0x10000U /* 64KB frames */ + +#define FGIC_GICR_IGROUPR0_OFFSET 0x00000080U /* Interrupt Group Register 0 ,RW */ +#define FGIC_GICR_IGROUPR_E_OFFSET 0x00000084U /* Interrupt Group Registers for extended PPI range ,RW */ + +#define FGIC_GICR_ISENABLER0_OFFSET 0x00000100U /* Interrupt Set-Enable Register 0 ,RW */ +#define FGIC_GICR_ISENABLER_E_OFFSET 0x00000104U /* Interrupt Set-Enable for extended PPI range ,RW */ + +#define FGIC_GICR_ICENABLER0_OFFSET 0x00000180U /* Interrupt Clear-Enable Register 0 ,RW */ +#define FGIC_GICR_ICENABLER_E_OFFSET 0x00000184U /* Interrupt Clear-Enable for extended PPI range ,RW */ + + +#define FGIC_GICR_ISPENDR0_OFFSET 0x00000200U /* Interrupt Set-Pend Register 0 ,RW */ +#define FGIC_GICR_ISPENDR_E_OFFSET 0x00000204U /* Interrupt Set-Pend for extended PPI range ,RW */ + +#define FGIC_GICR_ICPENDR0_OFFSET 0x00000280U /* Interrupt Clear-Pend Register 0 ,RW */ + +#define FGIC_GICR_ICPENDR_E_OFFSET 0x00000284U /* Interrupt Clear-Pend for extended PPI range ,RW */ + +#define FGIC_GICR_ISACTIVER0_OFFSET 0x00000300U /* Interrupt Set-Active Register 0 ,RW */ +#define FGIC_GICR_ISACTIVER_E_OFFSET 0x00000304U /* Interrupt Set-Active for extended PPI range ,RW */ + +#define FGIC_GICR_ICACTIVER0_OFFSET 0x00000380U /* Interrupt Clear-Active Register 0 ,RW */ +#define FGIC_GICR_ICACTIVER_E_OFFSET 0x00000384U /* Interrput Clear-Active for extended PPI range ,RW */ + +#define FGIC_GICR_IPRIORITYR_OFFSET 0x00000400U /* Interrupt Priority Registers ,RW */ +#define FGIC_GICR_IPRIORITYR_E_OFFSET 0x00000420U /* Interrupt Priority for extended PPI range ,RW */ + +#define FGIC_GICR_ICFGR0_OFFSET 0x00000C00U /* SGI Configuration Register ,RW*/ +#define FGIC_GICR_ICFGR1_OFFSET 0x00000C04U /* PPI Configuration Register ,RW*/ + +#define FGIC_GICR_ICFGR_E_OFFSET 0x00000C08U /* Extended PPI Configuration Register ,RW */ +#define FGIC_GICR_IGRPMODR0_OFFSET 0x00000D00U /* Interrupt Group Modifier Register 0 ,RW */ + +#define FGIC_GICR_IGRPMODR_E_OFFSET 0x00000D04U /* Interrupt Group Modifier for extended PPI range ,RW */ + +#define FGIC_GICR_NSACR_OFFSET 0x00000E00U /* Non-Secure Access Control Register ,RW */ + +#define FGIC_READREG8(addr, reg_offset) FtIn8(addr + (u8)reg_offset) +#define FGIC_WRITEREG8(addr, reg_offset, reg_value) FtOut8(addr + (u8)reg_offset, (u8)reg_value) + +#define FGIC_READREG32(addr, reg_offset) FtIn32(addr + (u32)reg_offset) +#define FGIC_WRITEREG32(addr, reg_offset, reg_value) FtOut32(addr + (u32)reg_offset, (u32)reg_value) + +#define FGIC_READREG64(addr, reg_offset) FtIn64(addr + (u64)reg_offset) +#define FGIC_WRITEREG64(addr, reg_offset, reg_value) FtOut64(addr +(u64)reg_offset, (u64)reg_value) + + +#define FGIC_SETBIT(base_addr, reg_offset, data) \ + FtSetBit32((base_addr) + (u32)(reg_offset), (u32)(data)) + +#define FGIC_CLEARBIT(base_addr, reg_offset, data) \ + FtClearBit32((base_addr) + (u32)(reg_offset), (u32)(data)) + +/* FGIC_GICD_CTLR_OFFSET --- Distributor switch */ +#define FGIC_GICD_CTLR_RWP_MASK BIT(31) +#define FGIC_GICD_CTLR_DS_MASK BIT(6) + +/* Need check system whether support Security states */ + +#define FGIC_GICD_CTLR_WRITE(gicd_base, reg) FGIC_WRITEREG32(gicd_base , FGIC_GICD_CTLR_OFFSET, reg) +#define FGIC_GICD_CTLR_READ(gicd_base) FGIC_READREG32(gicd_base , FGIC_GICD_CTLR_OFFSET) + +/* FGIC_GICD_ISENABLER_OFFSET --- SPI Open */ +#define FGIC_GICD_ISENABLER_VALUE_OFFSET(itnum) ((itnum % 32U)) +#define FGIC_GICD_ISENABLER_VALUE_MASK(itnum) (0x1U << FGIC_GICD_ISENABLER_VALUE_OFFSET(itnum)) +#define FGIC_GICD_ISENABLER_READ_N_MASK(gicd_base,itnum) FGIC_READREG32(gicd_base , FGIC_GICD_ISENABLER_OFFSET + ((itnum >> 5)<<2) ) +#define FGIC_GICD_ISENABLER_WRITE_N_MASK(gicd_base,itnum,reg) FGIC_WRITEREG32(gicd_base , FGIC_GICD_ISENABLER_OFFSET + ((itnum >> 5)<<2), reg) + +/* FGIC_GICD_TYPER_OFFSET --- Provides information about what features the GIC implementation supports. */ +#define FGIC_GICD_TYPER_ITLINESNUMBER_MASK 0x1f + + +/* FGIC_GICD_ICENABLER_OFFSET --- SPI close */ +#define FGIC_GICD_ICENABLER_DEFAULT_MASK BIT_MASK(32) +#define FGIC_GICD_ICENABLER_VALUE_OFFSET(itnum) ((itnum % 32U)) +#define FGIC_GICD_ICENABLER_VALUE_MASK(itnum) (0x1U << FGIC_GICD_ICENABLER_VALUE_OFFSET(itnum)) +#define FGIC_GICD_ICENABLER_READ_N_MASK(gicd_base,itnum) FGIC_READREG32(gicd_base , FGIC_GICD_ICENABLER_OFFSET + ((itnum >> 5)<<2) ) +#define FGIC_GICD_ICENABLER_WRITE_N_MASK(gicd_base,itnum,reg) FGIC_WRITEREG32(gicd_base , FGIC_GICD_ICENABLER_OFFSET + ((itnum >> 5)<<2), reg) + + +/* FGIC_GICD_IPRIORITYR_OFFSET --- SPI priority */ +#define FGIC_GICD_IPRIORITYR_VALUE_MASK(itnum) (0xFFU << ((itnum % 4U) << 3)) +#define FGIC_GICD_IPRIORITYR_READ_N_MASK(gicd_base,itnum) FGIC_READREG32(gicd_base , FGIC_GICD_IPRIORITYR_OFFSET + ((itnum >> 2)<<2) ) +#define FGIC_GICD_IPRIORITYR_WRITE_N_MASK(gicd_base,itnum,reg) FGIC_WRITEREG32(gicd_base , FGIC_GICD_IPRIORITYR_OFFSET + ((itnum >> 2)<<2), reg) + +/* FGIC_GICD_IROUTER_OFFSET --- SPI Routing */ +#define FGIC_GICD_IROUTER_AFFINITY_MASK (((0xFFULL) <<32)|((0xFFULL) <<16)|((0xFFULL)<<8)|(0xFFULL)) /* affinity mask */ +#define FGIC_GICD_IROUTER_RANGE_LIMIT (1023) /* GICD_IROUTER, Interrupt Routing Registers, n = 0 - 1019 */ +#define FGIC_GICD_IROUTER_BYTE_WIDTH 8 +#define FGIC_GICD_IROUTER_WRITE(gicd_base, bank, reg) FGIC_WRITEREG64(gicd_base , FGIC_GICD_IROUTER_OFFSET + (bank * FGIC_GICD_IROUTER_BYTE_WIDTH), reg) +#define FGIC_GICD_IROUTER_READ(gicd_base, bank) FGIC_READREG64(gicd_base , FGIC_GICD_IROUTER_OFFSET + (bank * FGIC_GICD_IROUTER_BYTE_WIDTH)) + +/* FGIC_GICD_ITARGETSR_OFFSET --- legacy operation ( affinity routing is not enabled) */ +#define FGIC_GICD_ITARGETSR_BYTE_WIDTH 4 +#define FGIC_GICD_ITARGETSR_WRITE(gicd_base, bank, reg) FGIC_WRITEREG32(gicd_base , FGIC_GICD_ITARGETSR_OFFSET + (bank * FGIC_GICD_ITARGETSR_BYTE_WIDTH), reg) +#define FGIC_GICD_ITARGETSR_READ(gicd_base, bank) FGIC_READREG32(gicd_base , FGIC_GICD_ITARGETSR_OFFSET + (bank * FGIC_GICD_ITARGETSR_BYTE_WIDTH)) + +/* FGIC_GICD_ICFGR_OFFSET --- edge-triggered or level-sensitive */ +#define FGIC_GICD_ICFGR_VALUE_OFFSET(itnum) ((itnum % 16U) << 1) +#define FGIC_GICD_ICFGR_VALUE_MASK(itnum) (0x3U << FGIC_GICD_ICFGR_VALUE_OFFSET(itnum)) +#define FGIC_GICD_ICFGR_READ_N_MASK(gicd_base,itnum) FGIC_READREG32(gicd_base , FGIC_GICD_ICFGR_OFFSET + ((itnum >> 4)<<2) ) +#define FGIC_GICD_ICFGR_WRITE_N_MASK(gicd_base,itnum,reg) FGIC_WRITEREG32(gicd_base , FGIC_GICD_ICFGR_OFFSET + ((itnum >> 4)<<2), reg) + + +/* FGIC_GICD_ISPENDR_OFFSET --- about spi pending */ +#define FGIC_GICD_ISPENDR_BYTE_WIDTH 4 +#define FGIC_GICD_ISPENDR_WRITE(gicd_base, bank, reg) FGIC_WRITEREG32(gicd_base , FGIC_GICD_ISPENDR_OFFSET + (bank * FGIC_GICD_ISPENDR_BYTE_WIDTH), reg) +#define FGIC_GICD_ISPENDR_READ(gicd_base, bank) FGIC_READREG32(gicd_base , FGIC_GICD_ISPENDR_OFFSET + (bank * FGIC_GICD_ISPENDR_BYTE_WIDTH)) + + +/* FGIC_GICD_ICPENDR_OFFSET --- about spi pending */ +#define FGIC_GICD_ICPENDR_DEFAULT_MASK BIT_MASK(32) +#define FGIC_GICD_ICPENDR_VALUE_OFFSET(itnum) ((itnum % 32U)) +#define FGIC_GICD_ICPENDR_VALUE_MASK(itnum) (0x1U << FGIC_GICD_ICPENDR_VALUE_OFFSET(itnum)) +#define FGIC_GICD_ICPENDR_READ_N_MASK(gicd_base,itnum) FGIC_READREG32(gicd_base , FGIC_GICD_ICPENDR_OFFSET + ((itnum >> 5)<<2) ) +#define FGIC_GICD_ICPENDR_WRITE_N_MASK(gicd_base,itnum,reg) FGIC_WRITEREG32(gicd_base , FGIC_GICD_ICPENDR_OFFSET + ((itnum >> 5)<<2), reg) + + + + +/* FGIC_GICD_IGROUPR_OFFSET --- */ +#define FGIC_GICD_IGROUPR_VALUE_OFFSET(itnum) ((itnum % 32U)) +#define FGIC_GICD_IGROUPR_VALUE_MASK(itnum) (0x1U << FGIC_GICD_IGROUPR_VALUE_OFFSET(itnum)) +#define FGIC_GICD_IGROUPR_READ_N_MASK(gicd_base,itnum) FGIC_READREG32(gicd_base , FGIC_GICD_IGROUPR_OFFSET + ((itnum >> 5)<<2) ) +#define FGIC_GICD_IGROUPR_WRITE_N_MASK(gicd_base,itnum,reg) FGIC_WRITEREG32(gicd_base , FGIC_GICD_IGROUPR_OFFSET + ((itnum >> 5)<<2), reg) + + +/* FGIC_GICD_IGRPMODR_OFFSET --- Controls whether the corresponding interrupt is in Secure Group 0、Non-secure Group 1、 Secure Group 1 */ +#define FGIC_GICD_IGRPMODR_DEFAULT_MASK BIT_MASK(32) +#define FGIC_GICD_IGRPMODR_VALUE_OFFSET(itnum) ((itnum % 32U)) +#define FGIC_GICD_IGRPMODR_VALUE_MASK(itnum) (0x1U << FGIC_GICD_IGRPMODR_VALUE_OFFSET(itnum)) +#define FGIC_GICD_IGRPMODR_READ_N_MASK(gicd_base,itnum) FGIC_READREG32(gicd_base , FGIC_GICD_IGRPMODR_OFFSET + ((itnum >> 5)<<2) ) +#define FGIC_GICD_IGRPMODR_WRITE_N_MASK(gicd_base,itnum,reg) FGIC_WRITEREG32(gicd_base , FGIC_GICD_IGRPMODR_OFFSET + ((itnum >> 5)<<2), reg) + + +/* FGIC_GICR_TYPER_OFFSET --- Provides information about the configuration of this Redistributor. */ +#define FGIC_GICR_TYPER_BYTE_WIDTH 4 +#define FGIC_GICR_TYPER_L_READ(redis_base) FGIC_READREG32(redis_base , FGIC_GICR_TYPER_OFFSET) +#define FGIC_GICR_TYPER_H_READ(redis_base) FGIC_READREG32(redis_base , FGIC_GICR_TYPER_OFFSET + FGIC_GICR_TYPER_BYTE_WIDTH) + +/* FGIC_GICR_WAKER_OFFSET --- Permits software to control the behavior of the WakeRequest power management signal corresponding to the Redistributor */ +#define FGIC_GICR_WAKER_PROCESSOR_SLEEP_MASK BIT(1) +#define FGIC_GICR_WAKER_CHILDREN_ASLEEP_MASK BIT(2) +#define FGIC_GICR_WAKER_CLEAR_BIT(redis_base, bit) FGIC_CLEARBIT(redis_base,FGIC_GICR_WAKER_OFFSET,bit) +#define FGIC_GICR_WAKER_WRITE(redis_base, reg) FGIC_WRITEREG32(redis_base , FGIC_GICR_WAKER_OFFSET, reg) +#define FGIC_GICR_WAKER_READ(redis_base) FGIC_READREG32(redis_base , FGIC_GICR_WAKER_OFFSET) + +/* FGIC_GICR_IPRIORITYR_OFFSET --- Enables forwarding of the corresponding SGI or PPI to the CPU interfaces*/ +#define FGIC_GICR_IPRIORITYR_VALUE_MASK(itnum) (0xFFU << ((itnum % 4U) << 3)) +#define FGIC_GICR_IPRIORITYR_READ(sgi_base,itnum) FGIC_READREG32(sgi_base , FGIC_GICR_IPRIORITYR_OFFSET + ((itnum >> 2)<<2) ) +#define FGIC_GICR_IPRIORITYR_WRITE(sgi_base,itnum,reg) FGIC_WRITEREG32(sgi_base , FGIC_GICR_IPRIORITYR_OFFSET + ((itnum >> 2)<<2), reg) + +/* FGIC_GICR_ISPENDR0_OFFSET --- Adds the pending state to the corresponding SGI or PPI. */ +#define FGIC_GICR_ISPENDR0_WRITE(sgi_base, reg) FGIC_WRITEREG32(sgi_base , FGIC_GICR_ISPENDR0_OFFSET, reg) +#define FGIC_GICR_ISPENDR0_READ(sgi_base) FGIC_READREG32(sgi_base , FGIC_GICR_ISPENDR0_OFFSET) + +/* FGIC_GICR_ICPENDR0_OFFSET --- Removes the pending state from the corresponding SGI or PPI. */ +#define FGIC_GICR_ICPENDR0_DEFAULT_MASK BIT_MASK(32) +#define FGIC_GICR_ICPENDR0_WRITE(sgi_base, reg) FGIC_WRITEREG32(sgi_base , FGIC_GICR_ICPENDR0_OFFSET, reg) +#define FGIC_GICR_ICPENDR0_READ(sgi_base) FGIC_READREG32(sgi_base , FGIC_GICR_ICPENDR0_OFFSET) + +/* FGIC_GICR_ISACTIVER0_OFFSET --- Activates the corresponding SGI or PPI. These registers are used when saving and restoring GIC state. */ + +#define FGIC_GICR_ISACTIVER0_WRITE(sgi_base, reg) FGIC_WRITEREG32(sgi_base , FGIC_GICR_ISACTIVER0_OFFSET, reg) +#define FGIC_GICR_ISACTIVER0_READ(sgi_base) FGIC_READREG32(sgi_base , FGIC_GICR_ISACTIVER0_OFFSET) + +/* FGIC_GICR_ICACTIVER0_OFFSET --- Deactivates the corresponding SGI or PPI. These registers are used when saving and restoring GIC state.*/ +#define FGIC_GICR_ICACTIVER0_WRITE(sgi_base, reg) FGIC_WRITEREG32(sgi_base , FGIC_GICR_ICACTIVER0_OFFSET, reg) +#define FGIC_GICR_ICACTIVER0_READ(sgi_base) FGIC_READREG32(sgi_base , FGIC_GICR_ICACTIVER0_OFFSET) + +/* FGIC_GICR_IGROUPR0_OFFSET --- Controls whether the corresponding SGI or PPI is in Group 0 or Group 1. */ +#define FGIC_GICR_IGROUPR0_DEFAULT_MASK BIT_MASK(32) +#define FGIC_GICR_IGROUPR0_VALUE_OFFSET(itnum) ((itnum % 32U)) +#define FGIC_GICR_IGROUPR0_VALUE_MASK(itnum) (0x1U << FGIC_GICR_IGROUPR0_VALUE_OFFSET(itnum)) +#define FGIC_GICR_IGROUPR0_WRITE(sgi_base, reg) FGIC_WRITEREG32(sgi_base , FGIC_GICR_IGROUPR0_OFFSET, reg) +#define FGIC_GICR_IGROUPR0_READ(sgi_base) FGIC_READREG32(sgi_base , FGIC_GICR_IGROUPR0_OFFSET) + +/* FGIC_GICR_IGRPMODR0_OFFSET --- controls whether the corresponding interrupt is in: • Secure Group 0.• Non-secure Group 1.• When System register access is enabled, Secure Group 1. */ +#define FGIC_GICR_IGRPMODR0_DEFAULT_MASK BIT_MASK(32) +#define FGIC_GICR_IGRPMODR0_VALUE_OFFSET(itnum) ((itnum % 32U)) +#define FGIC_GICR_IGRPMODR0_VALUE_MASK(itnum) (0x1U << FGIC_GICR_IGRPMODR0_VALUE_OFFSET(itnum)) +#define FGIC_GICR_IGRPMODR0_WRITE(sgi_base, reg) FGIC_WRITEREG32(sgi_base , FGIC_GICR_IGRPMODR0_OFFSET, reg) +#define FGIC_GICR_IGRPMODR0_READ(sgi_base) FGIC_READREG32(sgi_base , FGIC_GICR_IGRPMODR0_OFFSET) + +/* FGIC_GICR_ISENABLER0_OFFSET --- Enables forwarding of the corresponding interrupt to the CPU interfaces. */ +#define FGIC_GICR_ISENABLER0_WRITE(sgi_base, reg) FGIC_WRITEREG32(sgi_base , FGIC_GICR_ISENABLER0_OFFSET, reg) +#define FGIC_GICR_ISENABLER0_READ(sgi_base) FGIC_READREG32(sgi_base , FGIC_GICR_ISENABLER0_OFFSET) + +/* FGIC_GICR_ICENABLER0_OFFSET --- Disables forwarding of the corresponding interrupt to the CPU interfaces. */ +#define FGIC_GICR_ICENABLER0_DEFAULT_MASK BIT_MASK(32) +#define FGIC_GICR_ICENABLER0_WRITE(sgi_base, reg) FGIC_WRITEREG32(sgi_base , FGIC_GICR_ICENABLER0_OFFSET, reg) +#define FGIC_GICR_ICENABLER0_READ(sgi_base) FGIC_READREG32(sgi_base , FGIC_GICR_ICENABLER0_OFFSET) + +/* FGIC_GICR_ICFGR0_OFFSET */ +#define FGIC_GICR_ICFGR0_DEFAULT_MASK 0 +#define FGIC_GICR_ICFGR0_VALUE_OFFSET(itnum) ((itnum % 16U) << 1) +#define FGIC_GICR_ICFGR0_VALUE_MASK(itnum) (0x3U << FGIC_GICR_ICFGR0_VALUE_OFFSET(itnum)) +#define FGIC_GICR_ICFGR0_WRITE(sgi_base, reg) FGIC_WRITEREG32(sgi_base , FGIC_GICR_ICFGR0_OFFSET, reg) +#define FGIC_GICR_ICFGR0_READ(sgi_base) FGIC_READREG32(sgi_base , FGIC_GICR_ICFGR0_OFFSET) + +/* FGIC_GICR_ICFGR1_OFFSET */ +#define FGIC_GICR_ICFGR1_VALUE_OFFSET(itnum) ((itnum % 16U) << 1) +#define FGIC_GICR_ICFGR1_VALUE_MASK(itnum) (0x3U << FGIC_GICR_ICFGR1_VALUE_OFFSET(itnum)) +#define FGIC_GICR_ICFGR1_WRITE(sgi_base, reg) FGIC_WRITEREG32(sgi_base , FGIC_GICR_ICFGR1_OFFSET, reg) +#define FGIC_GICR_ICFGR1_READ(sgi_base) FGIC_READREG32(sgi_base , FGIC_GICR_ICFGR1_OFFSET) + +/* FGIC_GICR_CTLR_OFFSET */ + +#define FGIC_GICR_CTLR_RWP_MASK BIT(2) + +/* FGIC_GICR_NSACR_OFFSET */ + +#define FGIC_GICR_NSACR_WRITE(sgi_base, reg) FGIC_WRITEREG32(sgi_base , FGIC_GICR_NSACR_OFFSET, reg) +#define FGIC_GICR_NSACR_READ(sgi_base) FGIC_READREG32(sgi_base , FGIC_GICR_NSACR_OFFSET) + +#endif diff --git a/bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic_redistributor.h b/bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic_redistributor.h new file mode 100644 index 0000000000..23f3bf147f --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic_redistributor.h @@ -0,0 +1,173 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgic_redistributor.h + * Date: 2022-03-28 14:57:01 + * LastEditTime: 2022-03-28 14:57:01 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ +#ifndef DRIVERS_GIC_FGIC_REDISTRIBUTOR_H +#define DRIVERS_GIC_FGIC_REDISTRIBUTOR_H + +#include "fgic.h" +#include "fgic_hw.h" +#include "ftypes.h" + + +typedef enum +{ + GICR_GROUP_G0S = 0, + GICR_GROUP_G1NS = (1 << 0), + GICR_GROUP_G1S = (1 << 2), +} GICR_GROUP_SECURE_MODE; + +typedef enum +{ + GICR_WAKER_PROCESSOR_SLEEP = (1 << 1), + GICR_WAKER_CHILDREN_ASLEEP = (1 << 2) +} GICR_WAKER_MODE; + +static inline u32 FGicGetGicrAffinity(uintptr redis_base) +{ + return FGIC_GICR_TYPER_H_READ(redis_base); +} + + +static inline void FGicWakeGicr(uintptr redis_base) +{ + u32 mask ; + mask = FGIC_GICR_WAKER_READ(redis_base); + mask &= ~GICR_WAKER_PROCESSOR_SLEEP ; + FGIC_GICR_WAKER_WRITE(redis_base, mask); + + do + { + mask = FGIC_GICR_WAKER_READ(redis_base); + + } + while ((mask & GICR_WAKER_CHILDREN_ASLEEP) != 0); /* This PE is not in, and is not entering, a low power state. */ +} + + +static inline void FGicEnablePrivateInt(uintptr redis_base, s32 int_id) +{ + FGIC_GICR_ISENABLER0_WRITE(redis_base + FGIC_GICR_SGI_BASE_OFFSET, (1U << (int_id % 32))); +} + +static inline void FGicDisablePrivateInt(uintptr redis_base, s32 int_id) +{ + FGIC_GICR_ICENABLER0_WRITE(redis_base + FGIC_GICR_SGI_BASE_OFFSET, (1U << (int_id % 32))); +} + +static inline void FGicSetPrivatePriority(uintptr redis_base, s32 spi_id, u32 priority) +{ + u32 mask; + + /* For SPIs , has one byte-wide entry per interrupt */ + mask = FGIC_GICR_IPRIORITYR_READ(redis_base + FGIC_GICR_SGI_BASE_OFFSET, spi_id); + mask &= ~FGIC_GICR_IPRIORITYR_VALUE_MASK(spi_id); + mask |= ((priority & 0xffU) << (spi_id % 4) * 8U); + FGIC_GICR_IPRIORITYR_WRITE(redis_base + FGIC_GICR_SGI_BASE_OFFSET, spi_id, mask); +} + +static inline u32 FGicGetPrivatePriority(uintptr redis_base, s32 spi_id) +{ + u32 mask; + /* For SPIs , has one byte-wide entry per interrupt */ + mask = FGIC_GICR_IPRIORITYR_READ(redis_base + FGIC_GICR_SGI_BASE_OFFSET, spi_id); + + return (mask >> ((spi_id % 4U) * 8U)) & 0xFFU; +} + +static inline void FGicSetSgiLevel(uintptr redis_base, s32 spi_id, GICD_ICFGR_MODE mode) +{ + u32 mask ; + mask = FGIC_GICR_ICFGR0_READ(redis_base + FGIC_GICR_SGI_BASE_OFFSET); + mask &= ~FGIC_GICR_ICFGR0_VALUE_OFFSET(spi_id); + mask |= (mode << FGIC_GICR_ICFGR0_VALUE_OFFSET(spi_id)); + FGIC_GICR_ICFGR0_WRITE(redis_base, mask); +} + +static inline u32 FGicGetSgiLevel(uintptr redis_base, s32 spi_id) +{ + u32 mask ; + mask = FGIC_GICR_ICFGR0_READ(redis_base + FGIC_GICR_SGI_BASE_OFFSET); + return (mask >> ((spi_id % 16U) >> 1U)) ; +} + + +static inline void FGicSetPpiLevel(uintptr redis_base, s32 spi_id, GICD_ICFGR_MODE mode) +{ + u32 mask ; + mask = FGIC_GICR_ICFGR1_READ(redis_base + FGIC_GICR_SGI_BASE_OFFSET); + mask &= ~FGIC_GICR_ICFGR1_VALUE_OFFSET(spi_id); + mask |= (mode << FGIC_GICR_ICFGR1_VALUE_OFFSET(spi_id)); + FGIC_GICR_ICFGR1_WRITE(redis_base, mask); +} + + +static inline u32 FGicGetPpiLevel(uintptr redis_base, s32 spi_id) +{ + u32 mask ; + mask = FGIC_GICR_ICFGR1_READ(redis_base + FGIC_GICR_SGI_BASE_OFFSET); + return (mask >> ((spi_id % 16U) >> 1U)) ; +} + +static inline void FGicSetPrivateSecurity(uintptr redis_base, s32 spi_id, GICD_GROUP_SECURE_MODE mode) +{ + u32 mask ; + /* Group status */ + mask = FGIC_GICR_IGROUPR0_READ(redis_base + FGIC_GICR_SGI_BASE_OFFSET); + mask &= ~FGIC_GICR_IGROUPR0_VALUE_MASK(spi_id); + + mask |= ((mode & 0x1) << (spi_id % 32)); + FGIC_GICR_IGROUPR0_WRITE(redis_base + FGIC_GICR_SGI_BASE_OFFSET, mask); + + /* Group modifier */ + mask = FGIC_GICR_IGRPMODR0_READ(redis_base + FGIC_GICR_SGI_BASE_OFFSET); + mask &= ~FGIC_GICR_IGRPMODR0_VALUE_MASK(spi_id); + + mask |= (((mode & 0x2) >> 1) << (spi_id % 32)); + FGIC_GICR_IGRPMODR0_WRITE(redis_base + FGIC_GICR_SGI_BASE_OFFSET, mask); +} + + +static inline u32 FGicGetPrivateSecurity(uintptr redis_base, s32 spi_id) +{ + u32 mask ; + u32 group_status, group_modifier; + /* Group status */ + mask = FGIC_GICR_IGROUPR0_READ(redis_base + FGIC_GICR_SGI_BASE_OFFSET); + mask &= FGIC_GICR_IGROUPR0_VALUE_MASK(spi_id); + group_status = (mask >> (spi_id % 32)); + + /* Group modifier */ + mask = FGIC_GICR_IGRPMODR0_READ(redis_base + FGIC_GICR_SGI_BASE_OFFSET); + mask &= FGIC_GICR_IGRPMODR0_VALUE_MASK(spi_id); + group_modifier = (mask >> (spi_id % 32)); + + return ((group_modifier << 1) | group_status); +} + + +static inline u32 FGicNonSecureAccessRead(uintptr redis_base) +{ + return FGIC_GICR_NSACR_READ(redis_base + FGIC_GICR_SGI_BASE_OFFSET); +} + +#endif + diff --git a/bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic_sinit.c b/bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic_sinit.c new file mode 100644 index 0000000000..28b9ce2b29 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/gic/fgic/fgic_sinit.c @@ -0,0 +1,49 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgic_sinit.c + * Date: 2022-03-30 15:00:29 + * LastEditTime: 2022-03-30 15:00:29 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ +#include "fgic.h" + +extern FGicConfig fgic_config[FGIC_NUM]; + + +/** + * @name: FGicLookupConfig + * @msg: Gets the default configuration parameters in the current GIC + * @param {u32} instance_id + * @return {*} + */ +FGicConfig *FGicLookupConfig(u32 instance_id) +{ + FGicConfig *ptr = NULL; + u32 index; + + for (index = 0; index < (u32)FGIC_NUM; index++) + { + if (fgic_config[index].instance_id == instance_id) + { + ptr = &fgic_config[index]; + break; + } + } + + return (FGicConfig *)ptr; +} diff --git a/bsp/phytium/libraries/standalone/drivers/i2c/Kconfig b/bsp/phytium/libraries/standalone/drivers/i2c/Kconfig new file mode 100644 index 0000000000..1195673525 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/i2c/Kconfig @@ -0,0 +1,8 @@ +config USE_FI2C + bool + prompt "Use FI2C" + default n + help + Include FI2C driver component + + \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/i2c/fi2c/fi2c.c b/bsp/phytium/libraries/standalone/drivers/i2c/fi2c/fi2c.c new file mode 100644 index 0000000000..43899808a8 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/i2c/fi2c/fi2c.c @@ -0,0 +1,198 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fi2c.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:36:58 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +/***************************** Include Files *********************************/ +#include +#include "fio.h" +#include "ferror_code.h" +#include "ftypes.h" +#include "fdebug.h" +#include "fi2c_hw.h" +#include "fi2c.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FI2C_DEBUG_TAG "I2C" +#define FI2C_ERROR(format, ...) FT_DEBUG_PRINT_E(FI2C_DEBUG_TAG, format, ##__VA_ARGS__) +#define FI2C_INFO(format, ...) FT_DEBUG_PRINT_I(FI2C_DEBUG_TAG, format, ##__VA_ARGS__) +#define FI2C_DEBUG(format, ...) FT_DEBUG_PRINT_D(FI2C_DEBUG_TAG, format, ##__VA_ARGS__) + +/************************** Function Prototypes ******************************/ + +static FError FI2cReset(FI2c *instance_p); + +/************************** Variable Definitions *****************************/ +static const char *FI2C_ERROR_CODE_MSG[FI2C_NUM_OF_ERR_CODE] = +{ + "FI2C_SUCCESS : fi2c success", + "FI2C_ERR_INVAL_PARM : fi2c invalid input parameters", + "FI2C_ERR_NOT_READY : fi2c driver not ready", + "FI2C_ERR_TIMEOUT : fi2c wait timeout", + "FI2C_ERR_NOT_SUPPORT : fi2c non support operation", + "FI2C_ERR_INVAL_STATE : fi2c invalid state" +}; + +/*****************************************************************************/ + +/* 此文件主要为了完成用户对外接口,用户可以使用这些接口直接开始工作 */ + +/* - 包括用户API的定义和实现 + - 同时包含必要的OPTION方法,方便用户进行配置 + - 如果驱动可以直接进行I/O操作,在此源文件下可以将API 进行实现 */ + +/** + * @name: FI2cCfgInitialize + * @msg: 完成I2C驱动实例的初始化,使之可以使用 + * @param {FI2c} *instance_p I2C驱动实例数据 + * @param {FI2cConfig} *cofig_p I2C驱动配置数据 + * @return SUCCESS if initialization was successful + * ERROR + */ +FError FI2cCfgInitialize(FI2c *instance_p, const FI2cConfig *input_config_p) +{ + FASSERT(instance_p && input_config_p); + + FError ret = FI2C_SUCCESS; + + /* + * If the device is started, disallow the initialize and return a Status + * indicating it is started. This allows the user to de-initialize the device + * and reinitialize, but prevents a user from inadvertently + * initializing. + */ + if (FT_COMPONENT_IS_READY == instance_p->is_ready) + { + FI2C_ERROR("device is already initialized!!!"); + return FI2C_ERR_INVAL_STATE; + } + + /* + * Set default values and configuration data, including setting the + * callback handlers to stubs so the system will not crash should the + * application not assign its own callbacks. + */ + FI2cDeInitialize(instance_p); + instance_p->config = *input_config_p; + + /* + * Reset the device. + */ + ret = FI2cReset(instance_p); + if (FI2C_SUCCESS == ret) + { + instance_p->is_ready = FT_COMPONENT_IS_READY; + } + + return ret; +} + +/** + * @name: FI2cDeInitialize + * @msg: 完成I2C驱动实例去使能,清零实例数据 + * @return {*} + * @param {FI2c} *instance_p + */ +void FI2cDeInitialize(FI2c *instance_p) +{ + FASSERT(instance_p); + instance_p->is_ready = 0; + + memset(instance_p, 0, sizeof(*instance_p)); +} + +/** + * @name: FI2cReset + * @msg: 重置I2C控制器 + * @return {*} + * @param {FI2c} *instance_p, I2C驱动实例数据 + */ +static FError FI2cReset(FI2c *instance_p) +{ + FASSERT(instance_p); + + FError ret = FI2C_SUCCESS; + FI2cConfig *config_p = &instance_p->config; + uintptr base_addr = config_p->base_addr; + u32 reg_val = 0; + + ret = FI2cSetEnable(base_addr, FALSE); /* disable i2c ctrl */ + + if (FI2C_MASTER == config_p->work_mode) + { + reg_val |= (config_p->use_7bit_addr) ? FI2C_CON_MASTER_ADR_7BIT : FI2C_CON_MASTER_ADR_10BIT ; + reg_val |= FI2C_CON_SLAVE_DISABLE; + reg_val |= FI2C_CON_MASTER_MODE; + reg_val |= FI2C_CON_RESTART_EN; + } + else + { + reg_val |= (config_p->use_7bit_addr) ? FI2C_CON_SLAVE_ADR_7BIT : FI2C_CON_SLAVE_ADR_10BIT; + reg_val &= (~FI2C_CON_MASTER_MODE); + reg_val |= FI2C_CON_SLAVE_MODE; + } + reg_val |= FI2C_CON_STD_SPEED; + + FI2C_WRITE_REG32(base_addr, FI2C_CON_OFFSET, reg_val); + FI2C_WRITE_REG32(base_addr, FI2C_RX_TL_OFFSET, 0); + FI2C_WRITE_REG32(base_addr, FI2C_TX_TL_OFFSET, 0); + FI2C_SET_INTRRUPT_MASK(base_addr, 0); /* disable all intr */ + + ret = FI2cSetSpeed(base_addr, config_p->speed_rate); + + if (FI2C_SUCCESS == ret) + ret = FI2cSetEnable(base_addr, TRUE); /* enable i2c ctrl */ + + /* if init successed, and i2c is in slave mode, set slave address */ + if ((FI2C_SUCCESS == ret) && (FI2C_SLAVE == config_p->work_mode)) + ret = FI2cSetSar(base_addr, config_p->slave_addr); + + return ret; +} + +/** + * @name: FI2cErrorToMessage + * @msg: 获取I2C模块错误码对应的错误信息 + * @return {const char *}, 错误码信息,NULL表示失败 + * @param {FError} error, I2C输入错误码 + */ +const char *FI2cErrorToMessage(FError error) +{ + const char *msg = NULL; + if (FI2C_SUCCESS != error && (FI2C_ERR_CODE_PREFIX != error & (FT_ERRCODE_SYS_MODULE_MASK | FT_ERRCODE_SUB_MODULE_MASK))) + { + /* if input error do not belong to this module */ + return msg; + } + u32 index = error & FT_ERRCODE_TAIL_VALUE_MASK; + + if (index < FI2C_NUM_OF_ERR_CODE) + { + msg = FI2C_ERROR_CODE_MSG[index]; + } + + return msg; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/i2c/fi2c/fi2c.h b/bsp/phytium/libraries/standalone/drivers/i2c/fi2c/fi2c.h new file mode 100644 index 0000000000..505b310f85 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/i2c/fi2c/fi2c.h @@ -0,0 +1,201 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fi2c.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:37:04 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + + +#ifndef DRIVERS_I2C_FI2C_H +#define DRIVERS_I2C_FI2C_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ + +#include "ftypes.h" +#include "fassert.h" +#include "ferror_code.h" +#include "sdkconfig.h" +/************************** Constant Definitions *****************************/ +#define FI2C_SUCCESS FT_SUCCESS +#define FI2C_ERR_INVAL_PARM FT_MAKE_ERRCODE(ErrModBsp, ErrBspI2c, 1) +#define FI2C_ERR_NOT_READY FT_MAKE_ERRCODE(ErrModBsp, ErrBspI2c, 2) +#define FI2C_ERR_TIMEOUT FT_MAKE_ERRCODE(ErrModBsp, ErrBspI2c, 3) +#define FI2C_ERR_NOT_SUPPORT FT_MAKE_ERRCODE(ErrModBsp, ErrBspI2c, 4) +#define FI2C_ERR_INVAL_STATE FT_MAKE_ERRCODE(ErrModBsp, ErrBspI2c, 5) + +/* add up new error code above and plust FI2C_ERR_CODE_MAX by ONE*/ +#define FI2C_ERR_CODE_MAX FT_MAKE_ERRCODE(ErrModBsp, ErrBspI2c, 6) +#define FI2C_ERR_CODE_PREFIX FI2C_ERR_CODE_MAX & (FT_ERRCODE_SYS_MODULE_MASK | FT_ERRCODE_SUB_MODULE_MASK) +#define FI2C_NUM_OF_ERR_CODE FI2C_ERR_CODE_MAX & FT_ERRCODE_TAIL_VALUE_MASK + +/* + * status codes + */ +#define STATUS_IDLE 0x0 +#define STATUS_WRITE_IN_PROGRESS 0x1 +#define STATUS_READ_IN_PROGRESS 0x2 + +enum +{ + FI2C_MASTER = 0, /* i2c主设备 */ + FI2C_SLAVE /* i2c从设备 */ +}; + +enum +{ + FI2C_SPEED_STANDARD_RATE = 100000, /* 100kb/s */ + FI2C_SPEED_FAST_RATE = 400000 /* 400kb/s */ +}; + +enum +{ + FI2C_EVT_MASTER_TRANS_ABORTED = 0,/*master模式传输出错回调函数事件值*/ + FI2C_EVT_MASTER_READ_DONE, /*master模式接收完成回调函数事件值*/ + FI2C_EVT_MASTER_WRITE_DONE, /*master模式发送完成回调函数事件值*/ + + FI2C_MASTER_INTR_EVT_NUM +}; /* master mode evt */ + +enum/*slave模式回调函数事件值*/ +{ + FI2C_EVT_SLAVE_READ_REQUESTED = 0, /*slave收到主机读取内容的请求*/ + FI2C_EVT_SLAVE_WRITE_REQUESTED, /*slave收到主机发送的写请求*/ + FI2C_EVT_SLAVE_READ_PROCESSED, /*在Slave发送模式下,发送完数据的最后一个字节后,在规定时间内没有收到 Master 端的回应*/ + FI2C_EVT_SLAVE_WRITE_RECEIVED, /*Slave收到主机发送的数据,需要存下*/ + FI2C_EVT_SLAVE_STOP, /*I2C总线接口上是否产生了STOP。与控制器工作在Master模式还是 Slave 模式无关。*/ + + FI2C_SLAVE_INTR_EVT_NUM +}; /* slave mode evt */ + +/**************************** Type Definitions *******************************/ + +typedef struct +{ + u32 instance_id; /* Device instance id */ + uintptr base_addr; /* Device base address */ + u32 irq_num; /* Device intrrupt id */ + u32 irq_prority; /* Device intrrupt priority */ + u32 ref_clk_hz; /* Input reference clock frequency in Hz */ + u32 work_mode; /* Device work mode Slave or Master */ + u32 slave_addr; /*Master mode Slave Address writing/reading to/from ,Slave mode set local address*/ + boolean use_7bit_addr; /* Slave in-chip address offset in 7bit or 10bit */ + u32 speed_rate; /* I2C speed rate */ +} FI2cConfig; /* Device configure setting */ + +typedef void (*FI2cEvtHandler)(void *instance_p, void *param); + +typedef struct +{ + /* data */ + const void *data_buff; + u32 tx_total_num; + volatile u32 tx_cnt; + u32 flag;/* CMD BIT(8), STOP BIT(9) and RESTART BIT(10) Generation */ +} FI2cFrameTX; + +typedef struct +{ + /* data */ + void *data_buff; + u32 rx_total_num; + volatile u32 rx_cnt; +} FI2cFrameRX; + +typedef struct +{ + FI2cConfig config; /* Current active configs */ + u32 is_ready; /* Device is initialized and ready */ + volatile u32 status; + FI2cFrameTX txframe; + FI2cFrameRX rxframe; + /** only apply to master device **/ + /* Master intrrupt handler */ + FI2cEvtHandler master_evt_handlers[FI2C_MASTER_INTR_EVT_NUM]; + + /** only apply to slave device **/ + /* Slave intrrupt handler */ + FI2cEvtHandler slave_evt_handlers[FI2C_SLAVE_INTR_EVT_NUM]; + +} FI2c; /* Device instance */ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/* fi2c_sinit.c */ +/* 获取I2C驱动的默认配置参数 */ +const FI2cConfig *FI2cLookupConfig(u32 instance_id); + +/* fi2c.c */ +/* 完成I2C驱动实例的初始化,使之可以使用*/ +FError FI2cCfgInitialize(FI2c *instance_p, const FI2cConfig *cofig_p); + +/* 完成I2C驱动实例去使能,清零实例数据 */ +void FI2cDeInitialize(FI2c *instance_p); + +/* 获取I2C模块错误码对应的错误信息 */ +const char *FI2cErrorToMessage(FError error); + +/* fi2c_master.c */ +/* I2C主机读,阻塞直到完成读操作或失败 */ +FError FI2cMasterReadPoll(FI2c *instance_p, u32 mem_addr, u8 mem_byte_len, u8 *buf_p, u32 buf_len); + +/*I2C主机读,接收中断读操作或者失败 */ +FError FI2cMasterReadIntr(FI2c *instance_p, u32 mem_addr, u8 mem_byte_len, u8 *buf_p, u32 buf_len); + +/* I2C主机写,中断发送直到完成写操作或失败 */ +FError FI2cMasterWriteIntr(FI2c *instance_p, u32 mem_addr, u8 mem_byte_len, const u8 *buf_p, u32 buf_len); + +/* I2C主机写,阻塞直到完成写操作或失败 */ +FError FI2cMasterWritePoll(FI2c *instance_p, u32 mem_addr, u8 mem_byte_len, const u8 *buf_p, u32 buf_len); + +/* 获取I2C的中断值*/ +u32 FI2cGetIntr(FI2c *instance_p); + +/* 设置I2C主机的中断 */ +FError FI2cMasterSetupIntr(FI2c *instance_p, u32 mask); + +/* 主机模式中断服务函数 */ +void FI2cMasterIntrHandler(s32 vector, void *param); + +/* 注册I2C主机中断事件函数 */ +void FI2cMasterRegisterIntrHandler(FI2c *instance_p, u32 evt, FI2cEvtHandler handler); + +/* 设置I2C从机的中断 */ +FError FI2cSlaveSetupIntr(FI2c *instance_p); + +/* 从机模式中断服务函数 */ +void FI2cSlaveIntrHandler(s32 vector, void *param); + +/* 注册I2C从机中断事件函数 */ +void FI2cSlaveRegisterIntrHandler(FI2c *instance_p, u32 evt, FI2cEvtHandler handler); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsp/phytium/libraries/standalone/drivers/i2c/fi2c/fi2c_g.c b/bsp/phytium/libraries/standalone/drivers/i2c/fi2c/fi2c_g.c new file mode 100644 index 0000000000..92e122f0ad --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/i2c/fi2c/fi2c_g.c @@ -0,0 +1,143 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fi2c_g.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:36:14 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +/* - This file contains a configuration table that specifies the configuration +- 驱动全局变量定义,包括静态配置参数 */ + +/***************************** Include Files *********************************/ + +#include "ftypes.h" +#include "fparameters.h" +#include "fi2c.h" +#include "sdkconfig.h" +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ +/** + * @name: FI2C_CONFIG_TBL + * @msg: I2C驱动的默认配置参数 + */ +#if defined(CONFIG_TARGET_E2000) +const FI2cConfig FI2C_CONFIG_TBL[I2C_INSTANCE_NUM] = +{ + [I2C_INSTANCE_0] = + { + .instance_id = I2C_INSTANCE_0, + .base_addr = I2C_0_BASEADDR, + .irq_num = I2C_0_INTR_IRQ, + .irq_prority = 0, + .ref_clk_hz = I2C_REF_CLK_HZ, + .work_mode = FI2C_MASTER, + .slave_addr = 0, + .use_7bit_addr = TRUE, + .speed_rate = FI2C_SPEED_STANDARD_RATE + }, + [I2C_INSTANCE_1] = + { + .instance_id = I2C_INSTANCE_1, + .base_addr = I2C_1_BASEADDR, + .irq_num = I2C_1_INTR_IRQ, + .irq_prority = 0, + .ref_clk_hz = I2C_REF_CLK_HZ, + .work_mode = FI2C_MASTER, + .slave_addr = 0, + .use_7bit_addr = TRUE, + .speed_rate = FI2C_SPEED_STANDARD_RATE + }, + + [I2C_INSTANCE_2] = + { + .instance_id = I2C_INSTANCE_2, + .base_addr = I2C_2_BASEADDR, + .irq_num = I2C_2_INTR_IRQ, + .irq_prority = 0, + .ref_clk_hz = I2C_REF_CLK_HZ, + .work_mode = FI2C_MASTER, + .slave_addr = 0, + .use_7bit_addr = TRUE, + .speed_rate = FI2C_SPEED_STANDARD_RATE + } +}; +#endif + +#if defined(CONFIG_TARGET_D2000) || defined(CONFIG_TARGET_F2000_4) +const FI2cConfig FI2C_CONFIG_TBL[I2C_INSTANCE_NUM] = +{ + [I2C_INSTANCE_0] = + { + .instance_id = I2C_INSTANCE_0, + .base_addr = I2C_0_BASEADDR, + .irq_num = I2C_0_INTR_IRQ, + .irq_prority = 0, + .ref_clk_hz = I2C_REF_CLK_HZ, + .work_mode = FI2C_MASTER, + .slave_addr = 0, + .use_7bit_addr = TRUE, + .speed_rate = FI2C_SPEED_STANDARD_RATE + }, + [I2C_INSTANCE_1] = + { + .instance_id = I2C_INSTANCE_1, + .base_addr = I2C_1_BASEADDR, + .irq_num = I2C_1_INTR_IRQ, + .irq_prority = 0, + .ref_clk_hz = I2C_REF_CLK_HZ, + .work_mode = FI2C_MASTER, + .slave_addr = 0, + .use_7bit_addr = TRUE, + .speed_rate = FI2C_SPEED_STANDARD_RATE + }, + + [I2C_INSTANCE_2] = + { + .instance_id = I2C_INSTANCE_2, + .base_addr = I2C_2_BASEADDR, + .irq_num = I2C_2_INTR_IRQ, + .irq_prority = 0, + .ref_clk_hz = I2C_REF_CLK_HZ, + .work_mode = FI2C_MASTER, + .slave_addr = 0, + .use_7bit_addr = TRUE, + .speed_rate = FI2C_SPEED_STANDARD_RATE + }, + [I2C_INSTANCE_3] = + { + .instance_id = I2C_INSTANCE_3, + .base_addr = I2C_3_BASEADDR, + .irq_num = I2C_3_INTR_IRQ, + .irq_prority = 0, + .ref_clk_hz = I2C_REF_CLK_HZ, + .work_mode = FI2C_MASTER, + .slave_addr = 0, + .use_7bit_addr = TRUE, + .speed_rate = FI2C_SPEED_STANDARD_RATE + } +}; +#endif diff --git a/bsp/phytium/libraries/standalone/drivers/i2c/fi2c/fi2c_hw.c b/bsp/phytium/libraries/standalone/drivers/i2c/fi2c/fi2c_hw.c new file mode 100644 index 0000000000..6ce4679b14 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/i2c/fi2c/fi2c_hw.c @@ -0,0 +1,433 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fi2c_hw.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:36:22 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +/***************************** Include Files *********************************/ +#include +#include "fsleep.h" +#include "fdebug.h" +#include "ferror_code.h" +#include "fassert.h" +#include "fi2c_hw.h" +#include "fi2c.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +typedef struct +{ + u32 speed_mode; + u32 scl_lcnt; + u32 scl_hcnt; + u32 sda_hold; +} FI2cSpeedCfg; /* speed related configs */ + +typedef struct +{ + u32 speed; + u32 min_scl_hightime_ns; + u32 min_scl_lowtime_ns; + u32 def_risetime_ns; + u32 def_falltime_ns; +} FI2cSpeedModeInfo; /* speed calculation related configs */ + +/************************** Variable Definitions *****************************/ +static const FI2cSpeedModeInfo I2C_SPEED_CFG[FI2C_SPEED_MODE_MAX] = +{ + [FI2C_STANDARD_SPEED] = { + FI2C_SPEED_STANDARD_RATE, + 4000, + 4700, + 1000, + 300, + }, + [FI2C_FAST_SPEED] = { + FI2C_SPEED_FAST_RATE, + 1000, + 1300, + 300, + 300, + } +}; + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FI2C_DEBUG_TAG "I2C_HW" +#define FI2C_ERROR(format, ...) FT_DEBUG_PRINT_E(FI2C_DEBUG_TAG, format, ##__VA_ARGS__) +#define FI2C_INFO(format, ...) FT_DEBUG_PRINT_I(FI2C_DEBUG_TAG, format, ##__VA_ARGS__) +#define FI2C_DEBUG(format, ...) FT_DEBUG_PRINT_D(FI2C_DEBUG_TAG, format, ##__VA_ARGS__) + +#define FI2C_TIMEOUT 500 + +/************************** Function Prototypes ******************************/ +/** + * @name: FI2cSetEnable + * @msg: 设置I2C控制器的使能状态 + * @return {*} + * @param {uintptr} addr, I2c控制器基地址 + * @param {boolean} enable, TRUE: 使能,FALSE: 去使能 + */ +FError FI2cSetEnable(uintptr addr, boolean enable) +{ + u32 status = enable ? FI2C_IC_ENABLE : FI2C_IC_DISABLE; + u32 timeout = FI2C_TIMEOUT; + + do + { + FI2C_WRITE_REG32(addr, FI2C_ENABLE_OFFSET, status); + if (((FI2C_READ_REG32(addr, FI2C_ENABLE_STATUS_OFFSET)) & FI2C_IC_ENABLE_MASK) == status) + { + return FI2C_SUCCESS; + } + + } + while (0 != timeout--); + + FI2C_ERROR("timeout in %sabling I2C ctrl", enable ? "en" : "dis"); + return FI2C_ERR_TIMEOUT; +} + +/** + * @name: FI2cCalcTiming + * @msg: 计算I2C的上升沿下降沿配置 + * @return {*} + * @param {u32} bus_clk_hz, I2C总线时钟速度 Hz,默认48MHz + * @param {u32} spk_cnt, spk数目 + * @param {FI2cSpeedCfg} *speed_cfg_p,速度配置 + */ +static FError FI2cCalcTiming(u32 bus_clk_hz, u32 spk_cnt, FI2cSpeedCfg *speed_cfg_p) +{ + FASSERT(speed_cfg_p); + u32 speed_mode = speed_cfg_p->speed_mode; + const FI2cSpeedModeInfo *info_p = &I2C_SPEED_CFG[speed_mode]; + int fall_cnt, rise_cnt, min_t_low_cnt, min_t_high_cnt; + int hcnt, lcnt, period_cnt, diff, tot; + int sda_hold_time_ns, scl_rise_time_ns, scl_fall_time_ns; + + period_cnt = bus_clk_hz / info_p->speed; + scl_rise_time_ns = info_p->def_risetime_ns; + scl_fall_time_ns = info_p->def_falltime_ns; + + /* convert a period to a number of IC clk cycles */ + rise_cnt = DIV_ROUND_UP(bus_clk_hz / 1000 * scl_rise_time_ns, NANO_TO_KILO); + fall_cnt = DIV_ROUND_UP(bus_clk_hz / 1000 * scl_fall_time_ns, NANO_TO_KILO); + min_t_low_cnt = DIV_ROUND_UP(bus_clk_hz / 1000 * info_p->min_scl_lowtime_ns, NANO_TO_KILO); + min_t_high_cnt = DIV_ROUND_UP(bus_clk_hz / 1000 * info_p->min_scl_hightime_ns, NANO_TO_KILO); + + FI2C_INFO("i2c: mode %d, bus_clk %d, speed %d, period %d rise %d fall %d tlow %d thigh %d spk %d\n", + speed_mode, bus_clk_hz, info_p->speed, period_cnt, rise_cnt, fall_cnt, + min_t_low_cnt, min_t_high_cnt, spk_cnt); + + /* + * Back-solve for hcnt and lcnt according to the following equations: + * SCL_High_time = [(HCNT + IC_*_SPKLEN + 7) * icClk] + SCL_Fall_time + * SCL_Low_time = [(LCNT + 1) * icClk] - SCL_Fall_time + SCL_Rise_time + */ + hcnt = min_t_high_cnt - fall_cnt - 7 - spk_cnt; + lcnt = min_t_low_cnt - rise_cnt + fall_cnt - 1; + + if (hcnt < 0 || lcnt < 0) + { + FI2C_ERROR("i2c: bad counts. hcnt = %d lcnt = %d\n", hcnt, lcnt); + return FI2C_ERR_INVAL_PARM; + } + + /* + * Now add things back up to ensure the period is hit. If it is off, + * split the difference and bias to lcnt for remainder + */ + tot = hcnt + lcnt + 7 + spk_cnt + rise_cnt + 1; + + if (tot < period_cnt) + { + diff = (period_cnt - tot) / 2; + hcnt += diff; + lcnt += diff; + tot = hcnt + lcnt + 7 + spk_cnt + rise_cnt + 1; + lcnt += period_cnt - tot; + } + + speed_cfg_p->scl_lcnt = lcnt; + speed_cfg_p->scl_hcnt = hcnt; + speed_cfg_p->sda_hold = DIV_ROUND_UP(bus_clk_hz / 1000 * 300, NANO_TO_KILO); /* Use internal default unless other value is specified */ + + FI2C_INFO("i2c: hcnt = %d lcnt = %d sda hold = %d\n", + speed_cfg_p->scl_hcnt, + speed_cfg_p->scl_lcnt, + speed_cfg_p->sda_hold); + + return FI2C_SUCCESS; +} + +/** + * @name: FI2cCalcSpeedCfg + * @msg: 计算I2C的速度配置 + * @return {*} + * @param {uintptr} addr, I2C控制器基地址 + * @param {u32} speed, I2C传输速率 + * @param {u32} bus_clk_hz, I2C时钟频率 + * @param {FI2cSpeedCfg} *speed_cfg_p, I2C速度配置 + */ +static FError FI2cCalcSpeedCfg(uintptr addr, u32 speed, u32 bus_clk_hz, FI2cSpeedCfg *speed_cfg_p) +{ + FASSERT(speed_cfg_p); + u32 spk_cnt = 0; + + if (FI2C_SPEED_FAST_RATE <= speed) + speed_cfg_p->speed_mode = FI2C_FAST_SPEED; + else if (FI2C_SPEED_STANDARD_RATE <= speed) + speed_cfg_p->speed_mode = FI2C_STANDARD_SPEED; + else + return FI2C_ERR_INVAL_PARM; + + spk_cnt = FI2C_READ_REG32(addr, FI2C_FS_SPKLEN_OFFSET); + return FI2cCalcTiming(bus_clk_hz, spk_cnt, speed_cfg_p); +} + +/** + * @name: FI2cSetSpeed + * @msg: 设置I2C控制器的速率 + * @return {*} + * @param {uintptr} addr, I2C控制器基地址 + * @param {u32} speed_rate, I2C传输速率 + */ +FError FI2cSetSpeed(uintptr addr, u32 speed_rate) +{ + FError ret = FI2C_SUCCESS; + FI2cSpeedCfg speed_cfg; + u32 enable_status; + u32 reg_val; + + memset(&speed_cfg, 0, sizeof(speed_cfg)); + ret = FI2cCalcSpeedCfg(addr, speed_rate, I2C_REF_CLK_HZ, &speed_cfg); + if (FI2C_SUCCESS != ret) + return ret; + + /* get enable setting for restore later */ + enable_status = FI2cGetEnable(addr); + + /* reset speed mode bits */ + reg_val = ((FI2C_READ_REG32(addr, FI2C_CON_OFFSET)) & (~FI2C_CON_SPEED_MASK)); + switch (speed_cfg.speed_mode) + { + case FI2C_STANDARD_SPEED: + reg_val |= FI2C_CON_STD_SPEED; + FI2C_WRITE_REG32(addr, FI2C_SS_SCL_HCNT_OFFSET, speed_cfg.scl_hcnt); + FI2C_WRITE_REG32(addr, FI2C_SS_SCL_LCNT_OFFSET, speed_cfg.scl_lcnt); + break; + case FI2C_FAST_SPEED: + reg_val |= FI2C_CON_FAST_SPEED; + FI2C_WRITE_REG32(addr, FI2C_FS_SCL_HCNT_OFFSET, speed_cfg.scl_hcnt); + FI2C_WRITE_REG32(addr, FI2C_FS_SCL_LCNT_OFFSET, speed_cfg.scl_lcnt); + break; + default: + ret |= FI2C_ERR_INVAL_PARM; + break; + } + + FI2C_WRITE_REG32(addr, FI2C_CON_OFFSET, reg_val); + + /* Configure SDA Hold Time if required */ + if (0 != speed_cfg.sda_hold) + FI2C_WRITE_REG32(addr, FI2C_SDA_HOLD_OFFSET, speed_cfg.sda_hold); + + /* Restore back i2c now speed set */ + if (FI2C_IC_ENABLE == enable_status) + { + ret |= FI2cSetEnable(addr, TRUE); + } + + return ret; +} + +/** + * @name: FI2cWaitStatus + * @msg: 等待特定的I2C状态位直到状态不存在或者超时 + * @return {*} + * @param {uintptr} addr, I2C控制器基地址 + * @param {u32} stat_bit, I2C状态位 + */ +FError FI2cWaitStatus(uintptr addr, u32 stat_bit) +{ + u32 timeout = 0; + + /* wait until statbit was set or timeout */ + do + { + fsleep_millisec(2); /*wait 2 ms*/ + } + while (!((FI2C_READ_REG32(addr, FI2C_STATUS_OFFSET)) & stat_bit) && (FI2C_TIMEOUT > ++timeout)); + + if (FI2C_TIMEOUT <= timeout) + { + FI2C_ERROR("timeout when wait status: 0x%x", stat_bit); + return FI2C_ERR_TIMEOUT; + } + + return FI2C_SUCCESS; +} + +/** + * @name: FI2cWaitBusBusy + * @msg: 等待I2C总线忙 + * @return {*} + * @param {uintptr} addr, I2C控制器基地址 + */ +FError FI2cWaitBusBusy(uintptr addr) +{ + u32 timeout = FI2C_TIMEOUT; + u32 ret = FI2C_SUCCESS; + + if (((FI2C_READ_REG32(addr, FI2C_STATUS_OFFSET)) & FI2C_STATUS_MST_ACTIVITY) && + (FI2C_SUCCESS != FI2cWaitStatus(addr, FI2C_STATUS_TFE))) + { + ret = FI2C_ERR_TIMEOUT; + FI2C_ERROR("timeout when wait i2c bus busy"); + } + + return ret; +} + +/** + * @name: FI2cSetTar + * @msg: 设置与I2C主机通信的从机地址 + * @return {*} + * @param {uintptr} addr, I2C控制器基地址 + * @param {u32} tar_addr, I2C从机地址 + */ +FError FI2cSetTar(uintptr addr, u32 tar_addr) +{ + u32 enable_status = FI2cGetEnable(addr); + u32 ret = FI2C_SUCCESS; + u32 reg_val = 0; + + if (FI2C_IC_ENABLE == enable_status) + ret = FI2cSetEnable(addr, FALSE); + + if (FI2C_SUCCESS == ret) + FI2C_WRITE_REG32(addr, FI2C_TAR_OFFSET, (tar_addr & FI2C_IC_TAR_MASK)); + + if (FI2C_IC_ENABLE == enable_status) + ret = FI2cSetEnable(addr, TRUE); + + return ret; +} + +/** + * @name: FI2cSetSar + * @msg: 从机模式下,设置I2C地址 + * @return {*} + * @param {uintptr} addr, I2C控制器基地址 + * @param {u32} sar_addr, 作为从机的地址 + */ +FError FI2cSetSar(uintptr addr, u32 sar_addr) +{ + u32 enable_status = FI2cGetEnable(addr); + u32 ret = FI2C_SUCCESS; + u32 reg_val = 0; + + if (FI2C_IC_ENABLE == enable_status) + ret = FI2cSetEnable(addr, FALSE); + + if (FI2C_SUCCESS == ret) + FI2C_WRITE_REG32(addr, FI2C_SAR_OFFSET, (sar_addr & FI2C_IC_SAR_MASK)); + + if (FI2C_IC_ENABLE == enable_status) + ret = FI2cSetEnable(addr, TRUE); + + return ret; +} + +/** + * @name: FI2cFlushRxFifo + * @msg: 等待接收Fifo传输完成 + * @return {*} + * @param {uintptr} addr, I2C控制器基地址 + */ +FError FI2cFlushRxFifo(uintptr addr) +{ + u8 data; + int timeout = 0; + FError ret = FI2C_SUCCESS; + + /* read data to trigger trans until fifo empty */ + while (FI2C_GET_STATUS(addr) & FI2C_STATUS_RFNE) + { + data = FI2C_READ_DATA(addr); + + if (FI2C_TIMEOUT < ++timeout) + { + ret = FI2C_TIMEOUT; + FI2C_ERROR("timeout when flush fifo"); + break; + } + } + + return ret; +} + +/** + * @name: FI2cClearIntrBits + * @msg: 清除中断状态位,返回清除前的中断状态 + * @return {*} + * @param {uintptr} addr, I2C控制器基地址 + * @param {u32} *last_err_p, Abort错误 + */ +u32 FI2cClearIntrBits(uintptr addr, u32 *last_err_p) +{ + FASSERT(last_err_p); + + const u32 stat = FI2C_READ_INTR_STAT(addr); + + /* read to clr intrrupt status bits */ + if (stat & FI2C_INTR_TX_ABRT) + { + *last_err_p = FI2C_READ_REG32(addr, FI2C_TX_ABRT_SOURCE_OFFSET); /* read out abort sources */ + FI2C_READ_REG32(addr, FI2C_CLR_TX_ABRT_OFFSET); + } + + if (stat & FI2C_INTR_RX_UNDER) + FI2C_READ_REG32(addr, FI2C_CLR_RX_UNDER_OFFSET); + + if (stat & FI2C_INTR_RX_OVER) + FI2C_READ_REG32(addr, FI2C_CLR_RX_OVER_OFFSET); + + if (stat & FI2C_INTR_TX_OVER) + FI2C_READ_REG32(addr, FI2C_CLR_TX_OVER_OFFSET); + + if (stat & FI2C_INTR_RX_DONE) + FI2C_READ_REG32(addr, FI2C_CLR_RX_DONE_OFFSET); + + if (stat & FI2C_INTR_ACTIVITY) + FI2C_READ_REG32(addr, FI2C_CLR_ACTIVITY_OFFSET); + + if (stat & FI2C_INTR_STOP_DET) + FI2C_READ_REG32(addr, FI2C_CLR_STOP_DET_OFFSET); + + if (stat & FI2C_INTR_START_DET) + FI2C_READ_REG32(addr, FI2C_CLR_START_DET_OFFSET); + + if (stat & FI2C_INTR_GEN_CALL) + FI2C_READ_REG32(addr, FI2C_CLR_GEN_CALL_OFFSET); + + return stat; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/i2c/fi2c/fi2c_hw.h b/bsp/phytium/libraries/standalone/drivers/i2c/fi2c/fi2c_hw.h new file mode 100644 index 0000000000..1a39e67968 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/i2c/fi2c/fi2c_hw.h @@ -0,0 +1,271 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fi2c_hw.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:36:32 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#ifndef DRIVERS_I2C_FI2C_HW_H +#define DRIVERS_I2C_FI2C_HW_H + +/* - 传入模块基地址,不能复杂结构体 +- hardware interface of device || low-level driver function prototypes + +- 包括驱动寄存器参数和low-level操作定义 +1. 定义寄存器偏移 +2. 对上提供该模块寄存器操作的接口 +3. 一些简单外设提供直接操作接口 +4. 可以定义一些状态的接口,用于响应驱动状态的变化 */ + +#ifdef __cplusplus +extern "C" +{ +#endif +/***************************** Include Files *********************************/ + +#include "fparameters.h" +#include "fio.h" +#include "ferror_code.h" +#include "fkernel.h" + +/************************** Constant Definitions *****************************/ + +/** @name Register Map + * + * Register offsets from the base address of an I2C device. + * @{ + */ +#define FI2C_CON_OFFSET 0x00 +#define FI2C_TAR_OFFSET 0x04 +#define FI2C_SAR_OFFSET 0x08 +#define FI2C_HS_MADDR_OFFSET 0x0C +#define FI2C_DATA_CMD_OFFSET 0x10 +#define FI2C_SS_SCL_HCNT_OFFSET 0x14 +#define FI2C_SS_SCL_LCNT_OFFSET 0x18 +#define FI2C_FS_SCL_HCNT_OFFSET 0x1C +#define FI2C_FS_SCL_LCNT_OFFSET 0x20 +#define FI2C_HS_SCL_HCNT_OFFSET 0x24 +#define FI2C_HS_SCL_LCNT_OFFSET 0x28 +#define FI2C_INTR_STAT_OFFSET 0x2C +#define FI2C_INTR_MASK_OFFSET 0x30 +#define FI2C_RAW_INTR_STAT_OFFSET 0x34 +#define FI2C_RX_TL_OFFSET 0x38 +#define FI2C_TX_TL_OFFSET 0x3C +#define FI2C_CLR_INTR_OFFSET 0x40 +#define FI2C_CLR_RX_UNDER_OFFSET 0x44 +#define FI2C_CLR_RX_OVER_OFFSET 0x48 +#define FI2C_CLR_TX_OVER_OFFSET 0x4C +#define FI2C_CLR_RD_REQ_OFFSET 0x50 +#define FI2C_CLR_TX_ABRT_OFFSET 0x54 +#define FI2C_CLR_RX_DONE_OFFSET 0x58 +#define FI2C_CLR_ACTIVITY_OFFSET 0x5c +#define FI2C_CLR_STOP_DET_OFFSET 0x60 +#define FI2C_CLR_START_DET_OFFSET 0x64 +#define FI2C_CLR_GEN_CALL_OFFSET 0x68 +#define FI2C_ENABLE_OFFSET 0x6C +#define FI2C_STATUS_OFFSET 0x70 +#define FI2C_TXFLR_OFFSET 0x74 +#define FI2C_RXFLR_OFFSET 0x78 +#define FI2C_SDA_HOLD_OFFSET 0x7c +#define FI2C_TX_ABRT_SOURCE_OFFSET 0x80 +#define FI2C_SLV_DATA_NACK_ONLY_OFFSET 0x84 +#define FI2C_DMA_CR_OFFSET 0x88 +#define FI2C_DMA_TDLR_OFFSET 0x8c +#define FI2C_DMA_RDLR_OFFSET 0x90 +#define FI2C_SDA_SETUP_OFFSET 0x94 +#define FI2C_ACK_GENERAL_CALL_OFFSET 0x98 +#define FI2C_ENABLE_STATUS_OFFSET 0x9C +#define FI2C_FS_SPKLEN_OFFSET 0xa0 +#define FI2C_HS_SPKLEN_OFFSET 0xa4 +#define FI2C_COMP_PARAM_1_OFFSET 0xf4 +#define FI2C_COMP_VERSION_OFFSET 0xf8 +#define FI2C_COMP_TYPE_OFFSET 0xfc + +/** @name FI2C_CON_OFFSET Register + */ +#define FI2C_CON_MASTER_MODE (0x1 << 0) +#define FI2C_CON_SLAVE_MODE (0x0 << 0) + +#define FI2C_CON_SPEED_MASK GENMASK(2, 1) +#define FI2C_CON_STD_SPEED (0x1 << 1) +#define FI2C_CON_FAST_SPEED (0x2 << 1) +#define FI2C_CON_HIGH_SPEED (0x3 << 1) + +/* for slave mode */ +#define FI2C_CON_SLAVE_ADR_7BIT (0x0 << 3) +#define FI2C_CON_SLAVE_ADR_10BIT (0x1 << 3) + +/* for master mode */ +#define FI2C_CON_MASTER_ADR_7BIT (0x0 << 4) +#define FI2C_CON_MASTER_ADR_10BIT (0x1 << 4) + +#define FI2C_CON_RESTART_EN (0x1 << 5) +#define FI2C_CON_SLAVE_DISABLE (0x1 << 6) + +/** @name FI2C_TAR_OFFSET Register + */ +#define FI2C_IC_TAR_MASK GENMASK(9, 0) +#define FI2C_GC_OR_START (0x1 << 10) +#define FI2C_SPECIAL (0x1 << 11) +#define FI2C_TAR_ADR_7BIT (0x0 << 12) +#define FI2C_TAR_ADR_10BIT (0x1 << 12) + +/** @name FI2C_SAR_OFFSET Register + */ +#define FI2C_IC_SAR_MASK GENMASK(9, 0) //Slave addr when in slave mode + +/** @name FI2C_HS_MADDR_OFFSET Register + */ +#define FI2C_IC_HS_MAR GENMASK(2, 0) //I2C High Speed模式主机编码 + +/** @name FI2C_DATA_CMD_OFFSET Register + */ +#define FI2C_DATA_MASK GENMASK(7, 0) +#define FI2C_DATA_CMD_READ (0x1 << 8) +#define FI2C_DATA_CMD_WRITE (0x0 << 8) +#define FI2C_DATA_CMD_STOP (0x1 << 9) +#define FI2C_DATA_CMD_RESTART (0x1 << 10) + +/** @name FI2C_INTR_STAT_OFFSET Register + * @name FI2C_INTR_MASK_OFFSET Register + * @name FI2C_RAW_INTR_STAT_OFFSET Register + */ +#define FI2C_INTR_RX_UNDER (0x1 << 0) /* 接收缓冲区为空 */ +#define FI2C_INTR_RX_OVER (0x1 << 1) /* 接收缓冲区大小达到 IC_RX_BUFFER_DEPTH ,且还继续从外部接收数据 */ +#define FI2C_INTR_RX_FULL (0x1 << 2) /* 接收缓冲区大于等于 IC_RX_TL 中设定的门限值(RX_TL) */ +#define FI2C_INTR_TX_OVER (0x1 << 3) /* 发送缓冲区大小达到 IC_TX_BUFFER_DEPTH,并且还在尝试写数据 */ +#define FI2C_INTR_TX_EMPTY (0x1 << 4) /* 发送缓冲区小于等于 IC_TX_TL 寄存器中设定的门限值 */ +#define FI2C_INTR_RD_REQ (0x1 << 5) /* 读请求标志 */ +#define FI2C_INTR_TX_ABRT (0x1 << 6) /* I2C 不能完成FIFO内容的传输 */ +#define FI2C_INTR_RX_DONE (0x1 << 7) /* Slave工作模式下,规定时间内没有Master的回应,通信结束 */ +#define FI2C_INTR_ACTIVITY (0x1 << 8) /* I2C 控制器的活动状态 */ +#define FI2C_INTR_STOP_DET (0x1 << 9) /* I2C 总线接口上是否产生了 STOP */ +#define FI2C_INTR_START_DET (0x1 << 10) /* I2C 总线接口上是否产生了 START 或 RESTART */ +#define FI2C_INTR_GEN_CALL (0x1 << 11) /* I2C 总线接口上接收并识别到General Call格式 */ + +#define FI2C_INTR_ALL_MASK 0x8FF + +#define FI2C_INTR_MASTER_WR_MASK (FI2C_INTR_TX_EMPTY | FI2C_INTR_TX_ABRT) +#define FI2C_INTR_MASTER_RD_MASK (FI2C_INTR_MASTER_WR_MASK | FI2C_INTR_RX_FULL) + +#define FI2C_INTR_SLAVE_DEF_MASK (FI2C_INTR_RX_FULL | FI2C_INTR_STOP_DET | \ + FI2C_INTR_RD_REQ | FI2C_INTR_RX_DONE | \ + FI2C_INTR_RX_UNDER | FI2C_INTR_TX_ABRT |\ + FI2C_INTR_START_DET) + +/** @name FI2C_RX_TL_OFFSET Register + */ +#define FI2C_RX_TL_MASK GENMASK(7, 0) + +/** @name FI2C_TX_TL_OFFSET Register + */ +#define FI2C_TX_TL_MASK GENMASK(7, 0) + +/** @name FI2C_IC_ENABLE_OFFSET Register + */ +#define FI2C_ENABLE_CONTROLLER (0x1 << 0) +#define FI2C_DISABLE_CONTROLLER (0x0 << 0) + +/** @name FI2C_STATUS_OFFSET Register + */ +#define FI2C_STATUS_ACTIVITY (0x1 << 0) +#define FI2C_STATUS_TFNF (0x1 << 1) +#define FI2C_STATUS_TFE (0x1 << 2) +#define FI2C_STATUS_RFNE (0x1 << 3) +#define FI2C_STATUS_RFF (0x1 << 4) +#define FI2C_STATUS_MST_ACTIVITY (0x1 << 5) +#define FI2C_STATUS_SLV_ACTIVITY (0x1 << 6) + +/** @name FI2C_ENABLE_OFFSET Register + */ +#define FI2C_IC_ENABLE (0x1 << 0) +#define FI2C_IC_DISABLE (0x0 << 0) +#define FI2C_IC_ENABLE_MASK (0x1 << 0) + +#define FI2C_SLV_DISABLED_WHILE_BUSY (0x1 << 1) +#define FI2C_SLV_RX_DATA_LOST (0x1 << 2) + +/* High and low times in different speed modes (in ns) */ +#define FI2C_MIN_SS_SCL_HIGHTIME 4000 +#define FI2C_MIN_SS_SCL_LOWTIME 4700 +#define FI2C_MIN_FS_SCL_HIGHTIME 600 +#define FI2C_MIN_FS_SCL_LOWTIME 1300 +#define FI2C_MIN_FP_SCL_HIGHTIME 500 +#define FI2C_MIN_FP_SCL_LOWTIME 500 +#define FI2C_MIN_HS_SCL_HIGHTIME 60 +#define FI2C_MIN_HS_SCL_LOWTIME 160 +#define FI2C_DEFAULT_SDA_HOLD_TIME 300 + +#define FI2C_READ_REG32(addr, reg_offset) FtIn32((addr) + (u32)(reg_offset)) +#define FI2C_WRITE_REG32(addr, reg_offset, reg_value) FtOut32((addr) + (u32)(reg_offset), (u32)(reg_value)) + +#define FI2C_IIC_FIFO_MAX_LV 8 + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ +enum +{ + FI2C_STANDARD_SPEED = 0, + FI2C_FAST_SPEED, + + FI2C_SPEED_MODE_MAX +}; +/***************** Macros (Inline Functions) Definitions *********************/ +#define FI2C_GET_INTRRUPT_MASK(addr) FI2C_READ_REG32((addr), FI2C_INTR_MASK_OFFSET) + +#define FI2C_SET_INTRRUPT_MASK(addr, mask) FI2C_WRITE_REG32((addr), FI2C_INTR_MASK_OFFSET, (mask)) + +#define FI2C_CLEAR_INTR_STATUS(addr) FI2C_READ_REG32((addr), FI2C_CLR_INTR_OFFSET) + +#define FI2C_GET_STATUS(addr) FI2C_READ_REG32((addr), FI2C_STATUS_OFFSET) + +#define FI2C_READ_DATA(addr) (u8)(FI2C_DATA_MASK & FI2C_READ_REG32(addr, FI2C_DATA_CMD_OFFSET)) + +#define FI2C_WRITE_DATA(addr, dat) FI2C_WRITE_REG32((addr), FI2C_DATA_CMD_OFFSET, (dat)) + +#define FI2C_SET_RX_TL(addr, level) FI2C_WRITE_REG32((addr), FI2C_RX_TL_OFFSET, (level)) + +#define FI2C_SET_TX_TL(addr, level) FI2C_WRITE_REG32((addr), FI2C_TX_TL_OFFSET, (level)) + +#define FI2C_READ_INTR_STAT(addr) FI2C_READ_REG32((addr), FI2C_INTR_STAT_OFFSET) + +#define FI2C_READ_RAW_INTR_STAT(addr) FI2C_READ_REG32((addr), FI2C_RAW_INTR_STAT_OFFSET) + +inline static u32 FI2cGetEnable(uintptr addr) +{ + return FI2C_READ_REG32(addr, FI2C_ENABLE_STATUS_OFFSET); +} + +/************************** Function Prototypes ******************************/ +FError FI2cSetEnable(uintptr addr, boolean enable); +FError FI2cSetSpeed(uintptr addr, u32 speed_rate); +FError FI2cWaitStatus(uintptr addr, u32 stat_bit); +FError FI2cWaitBusBusy(uintptr addr); +FError FI2cSetTar(uintptr addr, u32 tar_addr); +FError FI2cSetSar(uintptr addr, u32 sar_addr); +FError FI2cFlushRxFifo(uintptr addr); +u32 FI2cClearIntrBits(uintptr addr, u32 *last_err_p); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/i2c/fi2c/fi2c_intr.c b/bsp/phytium/libraries/standalone/drivers/i2c/fi2c/fi2c_intr.c new file mode 100644 index 0000000000..85ebbd2af1 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/i2c/fi2c/fi2c_intr.c @@ -0,0 +1,448 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fi2c_intr.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:36:38 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +/***************************** Include Files *********************************/ +#include +#include "fio.h" +#include "ferror_code.h" +#include "ftypes.h" +#include "fdebug.h" +#include "fi2c_hw.h" +#include "fi2c.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FI2C_DEBUG_TAG "I2C" +#define FI2C_ERROR(format, ...) FT_DEBUG_PRINT_E(FI2C_DEBUG_TAG, format, ##__VA_ARGS__) +#define FI2C_INFO(format, ...) FT_DEBUG_PRINT_I(FI2C_DEBUG_TAG, format, ##__VA_ARGS__) +#define FI2C_DEBUG(format, ...) FT_DEBUG_PRINT_D(FI2C_DEBUG_TAG, format, ##__VA_ARGS__) + +/************************** Variable Definitions *****************************/ + +/*****************************************************************************/ + +/************************** Function Prototypes ******************************/ +static inline void FI2cMasterCallEvtHandler(FI2c *instance_p, u32 evt, void *para) +{ + FASSERT(instance_p); + FASSERT(evt < FI2C_MASTER_INTR_EVT_NUM); + + if (instance_p->master_evt_handlers[evt]) + { + instance_p->master_evt_handlers[evt](instance_p, para); + } +} + +static inline void FI2cSlaveCallEvtHandler(FI2c *instance_p, u32 evt, void *para) +{ + FASSERT(instance_p); + FASSERT(evt < FI2C_SLAVE_INTR_EVT_NUM); + FASSERT(para); + + if (instance_p->slave_evt_handlers[evt]) + { + instance_p->slave_evt_handlers[evt](instance_p, para); + } +} + +/** + * @name: FI2cStubHandler + * @msg: 主机模式下的I2C中断默认回调函数 + * @return {*} + * @param {void} *instance_p I2C驱动实例数据 + * @param {void} *param, 中断输入参数 + */ +static void FI2cStubHandler(void *instance_p, void *param) +{ + FASSERT(instance_p); + FI2c *instance = (FI2c *)(instance_p); + uintptr base_addr = instance->config.base_addr; + + FI2C_INFO("id: %d ,intr cause: 0x%x", + instance->config.instance_id, + FI2C_READ_INTR_STAT(base_addr)); +} + +/** + * @name: FI2cMasterIntrTxEmptyHandler + * @msg: 主机模式下的I2C TX_FIFO空中断处理函数 + * @return {*} + * @param {void} *instance_p I2C驱动实例数据 + */ +static void FI2cMasterIntrTxEmptyHandler(FI2c *instance_p) +{ + FASSERT(instance_p); + uintptr base_addr = instance_p->config.base_addr; + const u8 *buf_p = instance_p->txframe.data_buff; + + u32 intr_mask; + u32 buf_len; + u32 reg_val; + u32 rx_limit, tx_limit; + + buf_len = instance_p->txframe.tx_total_num - instance_p->txframe.tx_cnt; + + rx_limit = FI2C_IIC_FIFO_MAX_LV - FI2C_READ_REG32(base_addr, FI2C_RXFLR_OFFSET); + tx_limit = FI2C_IIC_FIFO_MAX_LV - FI2C_READ_REG32(base_addr, FI2C_TXFLR_OFFSET); + while (buf_len > 0 & rx_limit > 0 & tx_limit > 0) + { + if (1 == buf_len) + { + if (instance_p->status == STATUS_WRITE_IN_PROGRESS) + { + reg_val = (FI2C_DATA_MASK & *((u8 *)(instance_p->txframe.data_buff))) | + FI2C_DATA_CMD_WRITE | + FI2C_DATA_CMD_STOP; + instance_p->txframe.data_buff++; + FI2C_INFO("Write Stop Singal"); + } + else if (instance_p->status == STATUS_READ_IN_PROGRESS) + { + reg_val = FI2C_DATA_CMD_READ | FI2C_DATA_CMD_STOP; + } + } + else + { + if (instance_p->status == STATUS_WRITE_IN_PROGRESS) + { + reg_val = (FI2C_DATA_MASK & *((u8 *)(instance_p->txframe.data_buff))) | + FI2C_DATA_CMD_WRITE; + instance_p->txframe.data_buff++; + } + else if (instance_p->status == STATUS_READ_IN_PROGRESS) + { + reg_val = FI2C_DATA_CMD_READ; + } + } + FI2C_WRITE_REG32(base_addr, FI2C_DATA_CMD_OFFSET, reg_val); + rx_limit--; + tx_limit--; + buf_len--; + } + instance_p->txframe.tx_cnt = instance_p->txframe.tx_total_num - buf_len; + /*tx over.*/ + if (instance_p->txframe.tx_cnt == instance_p->txframe.tx_total_num) + { + instance_p->txframe.tx_cnt = 0; + if (instance_p->status == STATUS_WRITE_IN_PROGRESS) + { + instance_p->status = STATUS_IDLE; + } + intr_mask = FI2C_GET_INTRRUPT_MASK(instance_p->config.base_addr); + intr_mask &= ~(FI2C_INTR_TX_EMPTY); + FI2C_SET_INTRRUPT_MASK(instance_p->config.base_addr, intr_mask); + } +} + +/** + * @name: FI2cMasterIntrRxFullHandler + * @msg: 主机模式下的I2C RX_FIFO满处理函数,可通过FI2C_SET_RX_TL(address,level);设置触发level + * @return {*} + * @param {void} *instance_p I2C驱动实例数据 + */ +static void FI2cMasterIntrRxFullHandler(FI2c *instance_p) +{ + FASSERT(instance_p); + u32 intr_mask; + uintptr base_addr = instance_p->config.base_addr; + u8 emptyfifo = FI2C_READ_REG32(base_addr, FI2C_RXFLR_OFFSET); + u32 i = 0u; + u32 reg_val; + for (i = 0; i < emptyfifo; i++) + { + *((u8 *)(instance_p->rxframe.data_buff++)) = FI2C_READ_DATA(base_addr); + } + instance_p->rxframe.rx_cnt += emptyfifo; + if (instance_p->rxframe.rx_cnt >= instance_p->rxframe.rx_total_num) + { + instance_p->rxframe.rx_cnt = 0; + instance_p->status = STATUS_IDLE; + intr_mask = FI2C_GET_INTRRUPT_MASK(base_addr); + intr_mask &= ~(FI2C_INTR_RX_FULL); + FI2C_SET_INTRRUPT_MASK(base_addr, intr_mask); + FI2cFlushRxFifo(base_addr); + } +} + +/** + * @name: FI2cMasterIntrHandler + * @msg: 主机模式下的I2C中断响应函数 + * @return {*} + * @param {s32} vector + * @param {void} *param, 中断输入参数 + */ +void FI2cMasterIntrHandler(s32 vector, void *param) +{ + FASSERT(param); + FI2c *instance_p = (FI2c *)param; + uintptr base_addr = instance_p->config.base_addr; + u32 last_err = 0; + u32 stat = FI2cClearIntrBits(base_addr, &last_err); + u32 raw_stat = FI2C_READ_RAW_INTR_STAT(base_addr); + u32 enabled = FI2C_READ_REG32(base_addr, FI2C_ENABLE_OFFSET); + u32 val = 0; + FASSERT(FI2C_MASTER == instance_p->config.work_mode); + if (!(enabled & FI2C_IC_ENABLE) || !(raw_stat & ~FI2C_INTR_ACTIVITY)) + { + return; + } + + if (stat & FI2C_INTR_TX_ABRT) /* trans abort error */ + { + FI2C_ERROR("last error: 0x%x", last_err); + FI2C_ERROR("abort source: 0x%x", FI2C_READ_REG32(base_addr, FI2C_TX_ABRT_SOURCE_OFFSET)); + instance_p->status = STATUS_IDLE; + FI2C_SET_INTRRUPT_MASK(base_addr, 0); /* disable all intr */ + FI2C_READ_REG32(base_addr, FI2C_CLR_TX_ABRT_OFFSET); + FI2C_WRITE_REG32(base_addr, FI2C_ENABLE_OFFSET, 1); + FI2cMasterCallEvtHandler(instance_p, FI2C_EVT_MASTER_TRANS_ABORTED, &val); + return; + } + + if (stat & FI2C_INTR_RX_FULL) /* rx complete */ + { + FI2cMasterIntrRxFullHandler(instance_p); + FI2cMasterCallEvtHandler(instance_p, FI2C_EVT_MASTER_READ_DONE, &val); + return; + } + + if (stat & FI2C_INTR_TX_EMPTY) /* tx complete */ + { + FI2cMasterIntrTxEmptyHandler(instance_p); + FI2cMasterCallEvtHandler(instance_p, FI2C_EVT_MASTER_WRITE_DONE, &val); + return; + } + return; +} +/** + * @name: FI2cMasterRegisterIntrHandler + * @msg: 注册I2C主机中断事件函数 + * @return {*} + * @param {FI2c} *instance_p I2C驱动实例数据 + * @param {u32} evt 中断事件,参考 FI2C_MASTER_INTR_EVT_NUM + * @param {FI2cEvtHandler} handler 中断事件回调函数 + */ +void FI2cMasterRegisterIntrHandler(FI2c *instance_p, u32 evt, FI2cEvtHandler handler) +{ + FASSERT(instance_p && evt < FI2C_MASTER_INTR_EVT_NUM); + instance_p->master_evt_handlers[evt] = handler; +} + +/** + * @name: FI2cMasterGetIntr + * @msg: 获取I2C主机的中断 + * @return {u32 } 返回中断寄存器值 + * @param {FI2c} *instance_p I2C驱动实例数据 + */ +u32 FI2cGetIntr(FI2c *instance_p) +{ + FASSERT(instance_p); + FI2cConfig *config_p = &instance_p->config; + uintptr base_addr = config_p->base_addr; + + if (FT_COMPONENT_IS_READY != instance_p->is_ready) + { + FI2C_ERROR("i2c driver not ready"); + return FI2C_ERR_NOT_READY; + } + + return FI2C_GET_INTRRUPT_MASK(base_addr); +} +/** + * @name: FI2cMasterSetupIntr + * @msg: 设置I2C主机的中断 + * @return {FError *} 返回错误码 + * @param {FI2c} *instance_p I2C驱动实例数据 + * @param {u32} mask 需要操作的中断寄存器位 + */ +FError FI2cMasterSetupIntr(FI2c *instance_p, u32 mask) +{ + FASSERT(instance_p); + FI2cConfig *config_p = &instance_p->config; + uintptr base_addr = config_p->base_addr; + u32 evt; + + if (FT_COMPONENT_IS_READY != instance_p->is_ready) + { + FI2C_ERROR("i2c driver not ready"); + return FI2C_ERR_NOT_READY; + } + + if (FI2C_MASTER != instance_p->config.work_mode) + { + FI2C_ERROR("i2c work mode shall be master"); + return FI2C_ERR_INVAL_STATE; + } + + /* disable all i2c irq */ + FI2C_CLEAR_INTR_STATUS(base_addr); + + for (evt = FI2C_EVT_MASTER_TRANS_ABORTED; evt < FI2C_MASTER_INTR_EVT_NUM; evt++) + { + if (instance_p->master_evt_handlers[evt] == NULL) + { + FI2cMasterRegisterIntrHandler(instance_p, evt, FI2cStubHandler); + FI2C_INFO("evt :%d.is default.\r\n", evt); + } + } + FI2C_SET_INTRRUPT_MASK(base_addr, mask); + + return FI2C_SUCCESS; +} + +/** + * @name: FI2cSlaveIntrHandler + * @msg: I2C从机的中断响应函数 + * @return {*} + * @param {s32} vector + * @param {void} *param, 输入参数 + */ +void FI2cSlaveIntrHandler(s32 vector, void *param) +{ + FASSERT(param); + FI2c *instance_p = (FI2c *)param; + uintptr base_addr = instance_p->config.base_addr; + u32 last_err = 0; + + u32 stat = FI2C_READ_INTR_STAT(base_addr); + u32 raw_stat = FI2C_READ_RAW_INTR_STAT(base_addr); + u32 enabled = FI2C_READ_REG32(base_addr, FI2C_ENABLE_OFFSET); + boolean slave_active = (FI2C_GET_STATUS(base_addr) & FI2C_STATUS_SLV_ACTIVITY) ? TRUE : FALSE; + u8 val = 0; + u32 reg_val; + + FASSERT(FI2C_SLAVE == instance_p->config.work_mode); + + if (!(enabled & FI2C_IC_ENABLE) || !(raw_stat & ~FI2C_INTR_ACTIVITY)) + { + return; + } + stat = FI2cClearIntrBits(base_addr, &last_err); + + if (stat & FI2C_INTR_RX_FULL) + { + if (instance_p->status != STATUS_WRITE_IN_PROGRESS) + { + /* code */ + instance_p->status = STATUS_WRITE_IN_PROGRESS; + FI2cSlaveCallEvtHandler(instance_p, FI2C_EVT_SLAVE_WRITE_REQUESTED, &val); + } + val = FI2C_READ_DATA(base_addr); + FI2cSlaveCallEvtHandler(instance_p, FI2C_EVT_SLAVE_WRITE_RECEIVED, &val); + } + if (stat & FI2C_INTR_RD_REQ) + { + if (slave_active) + { + FI2C_READ_REG32(base_addr, FI2C_CLR_RD_REQ_OFFSET); + instance_p->status = STATUS_WRITE_IN_PROGRESS; + FI2cSlaveCallEvtHandler(instance_p, FI2C_EVT_SLAVE_READ_REQUESTED, &val); + reg_val = val; + FI2C_WRITE_DATA(base_addr, reg_val); + } + } + + if (stat & FI2C_INTR_RX_DONE) + { + FI2cSlaveCallEvtHandler(instance_p, FI2C_EVT_SLAVE_READ_PROCESSED, &val); + FI2C_READ_REG32(base_addr, FI2C_CLR_RX_DONE_OFFSET); + return; + } + + if (stat & FI2C_INTR_STOP_DET) + { + instance_p->status = STATUS_IDLE; + FI2cSlaveCallEvtHandler(instance_p, FI2C_EVT_SLAVE_STOP, &val); + } + + if (stat & FI2C_INTR_TX_ABRT) /* trans abort error */ + { + FI2C_ERROR("last error: 0x%x", last_err); + FI2C_ERROR("abort source: 0x%x", FI2C_READ_REG32(base_addr, FI2C_TX_ABRT_SOURCE_OFFSET)); + } + + return; +} + +/** + * @name: FI2cSlaveRegisterIntrHandler + * @msg: 注册I2C从机中断事件函数 + * @return {*} + * @param {FI2c} *instance_p I2C驱动实例数据 + * @param {u32} evt 中断事件,参考 FI2C_SLAVE_INTR_EVT_NUM + * @param {FI2cEvtHandler} handler 中断事件回调函数 + */ +void FI2cSlaveRegisterIntrHandler(FI2c *instance_p, u32 evt, FI2cEvtHandler handler) +{ + FASSERT(instance_p && evt < FI2C_SLAVE_INTR_EVT_NUM); + instance_p->slave_evt_handlers[evt] = handler; +} + +/** + * @name: FI2cSlaveSetupIntr + * @msg: 设置I2C从机的中断 + * @return {FError *} 返回错误码 + * @param {FI2c} *instance_p + */ +FError FI2cSlaveSetupIntr(FI2c *instance_p) +{ + FASSERT(instance_p); + FI2cConfig *config_p = &instance_p->config; + uintptr base_addr = config_p->base_addr; + u32 evt; + u32 mask; + + if (FT_COMPONENT_IS_READY != instance_p->is_ready) + { + FI2C_ERROR("i2c driver not ready"); + return FI2C_ERR_NOT_READY; + } + + if (FI2C_SLAVE != instance_p->config.work_mode) + { + FI2C_ERROR("i2c work mode shall be slave"); + return FI2C_ERR_INVAL_STATE; + } + + /* disable all i2c irq */ + FI2C_CLEAR_INTR_STATUS(base_addr); + + for (evt = FI2C_EVT_SLAVE_READ_REQUESTED; evt < FI2C_SLAVE_INTR_EVT_NUM; evt++) + { + if (instance_p->slave_evt_handlers[evt] == NULL) + { + FI2cSlaveRegisterIntrHandler(instance_p, evt, FI2cStubHandler); + FI2C_INFO("evt :%d.is default.\r\n", evt); + } + } + FI2C_SET_RX_TL(instance_p->config.base_addr, 0);/* 0 表示接收缓冲区大于等于 1 时触发中断 */ + FI2C_SET_TX_TL(instance_p->config.base_addr, 0);/* 0 表示发送缓冲区大于等于 1 时触发中断 */ + mask = FI2C_GET_INTRRUPT_MASK(base_addr); + mask |= (FI2C_INTR_SLAVE_DEF_MASK); + FI2C_SET_INTRRUPT_MASK(base_addr, mask); + + return FI2C_SUCCESS; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/i2c/fi2c/fi2c_master.c b/bsp/phytium/libraries/standalone/drivers/i2c/fi2c/fi2c_master.c new file mode 100644 index 0000000000..49afc9cbc3 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/i2c/fi2c/fi2c_master.c @@ -0,0 +1,411 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fi2c_master.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:36:46 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +/* + - 一些驱动模块,直接操作硬件的I/O接口,无法实现有意义的操作,此时需要针对中间件或者用户使用习惯设计此模块 (i2c,nand,eth) + - 部分场景适用, 分角色的 I/O 操作 + - 此模块的函数原型,在fooxx.h 中声明一次,方便用户或者中间件层调用 + +*/ + +/***************************** Include Files *********************************/ + +#include "fio.h" +#include "fsleep.h" +#include "fdebug.h" +#include "fi2c_hw.h" +#include "fi2c.h" +#include "finterrupt.h" +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FI2C_DEBUG_TAG "I2C_MASTER" +#define FI2C_ERROR(format, ...) FT_DEBUG_PRINT_E(FI2C_DEBUG_TAG, format, ##__VA_ARGS__) +#define FI2C_INFO(format, ...) FT_DEBUG_PRINT_I(FI2C_DEBUG_TAG, format, ##__VA_ARGS__) +#define FI2C_DEBUG(format, ...) FT_DEBUG_PRINT_D(FI2C_DEBUG_TAG, format, ##__VA_ARGS__) + +#define FI2C_TIMEOUT 500 + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + +/*****************************************************************************/ +/** + * @name: FI2cMasterStartTrans + * @msg: I2C主机开始传输 + * @return {*} + * @param {FI2c} *instance_p, I2C驱动实例数据 + * @param {u32} mem_addr, 从机的片内偏移 + * @param {u8} mem_byte_len, Size of internal memory address 1->8bit ~ 4->32bit + * @param {u8} flag ,for cmd reg STOP,RESTART. + */ +static FError FI2cMasterStartTrans(FI2c *instance_p, u32 mem_addr, u8 mem_byte_len, u16 flag) +{ + FASSERT(instance_p); + uintptr base_addr = instance_p->config.base_addr; + FError ret = FI2C_SUCCESS; + u32 addr_len = mem_byte_len; + + ret = FI2cWaitBusBusy(base_addr); + if (FI2C_SUCCESS != ret) + return ret; + ret = FI2cSetTar(base_addr, instance_p->config.slave_addr); /* 设备地址 */ + if (FI2C_SUCCESS != ret) + return ret; + while (addr_len) + { + ret = FI2cWaitStatus(base_addr, FI2C_STATUS_TFNF); + if (FI2C_SUCCESS != ret) + break; + if (FI2C_GET_STATUS(base_addr) & FI2C_STATUS_TFNF) + { + addr_len--; + if (addr_len != 0) + { + FI2C_WRITE_REG32(base_addr, FI2C_DATA_CMD_OFFSET, + (mem_addr >> (addr_len * BITS_PER_BYTE)) & FI2C_DATA_MASK); /* word address */ + } + else + { + FI2C_WRITE_REG32(base_addr, FI2C_DATA_CMD_OFFSET, + ((mem_addr >> (addr_len * BITS_PER_BYTE)) & FI2C_DATA_MASK) + flag); /* word address */ + } + } + } + return ret; +} + +/** + * @name: FI2cMasterStopTrans + * @msg: I2C主机结束传输 + * @return {*} + * @param {FI2c} *instance_p, I2C驱动实例数据 + */ +static FError FI2cMasterStopTrans(FI2c *instance_p) +{ + FASSERT(instance_p); + FError ret = FI2C_SUCCESS; + uintptr base_addr = instance_p->config.base_addr; + u32 reg_val; + u32 timeout = 0; + + FI2C_INFO("GET MASTER STOP, stat: 0x%x, 0x%x", FI2C_READ_INTR_STAT(base_addr), + FI2C_READ_RAW_INTR_STAT(base_addr)); + + while (TRUE) + { + if (FI2C_READ_RAW_INTR_STAT(base_addr) & FI2C_INTR_STOP_DET) + { + reg_val = FI2C_READ_REG32(base_addr, FI2C_CLR_STOP_DET_OFFSET); /* read to clr intr status */ + break; + } + else if (FI2C_TIMEOUT < ++timeout) + { + break; /* wait timeout, but no error code ret */ + } + } + + ret = FI2cWaitBusBusy(base_addr); + if (FI2C_SUCCESS == ret) + ret = FI2cFlushRxFifo(base_addr); + + return ret; +} + +/** + * @name: FI2cMasterReadPoll + * @msg: I2C主机读,阻塞直到完成读操作或失败 + * @return {FError *} 返回错误码 + * @param {FI2c} *instance_p I2C驱动实例数据 + * @param {u32} mem_addr 从机的内部偏移地址 + * @param {u8} mem_byte_len, Size of internal memory address 1->8bit ~ 4->32bit + * @param {u8} *buf_p 读目的缓冲区 + * @param {int} buf_len 读目的缓冲区长度 + */ +FError FI2cMasterReadPoll(FI2c *instance_p, u32 mem_addr, u8 mem_byte_len, u8 *buf_p, u32 buf_len) +{ + FError ret = FI2C_SUCCESS; + FASSERT(instance_p); + u32 mask; + u32 reg_val; + u32 tx_len = buf_len; + u32 rx_len = buf_len; + u32 rx_limit, tx_limit; + u32 trans_timeout = 0; + uintptr base_addr = instance_p->config.base_addr; + + if (FT_COMPONENT_IS_READY != instance_p->is_ready) + { + FI2C_ERROR("i2c driver not ready"); + return FI2C_ERR_NOT_READY; + } + + if (FI2C_MASTER != instance_p->config.work_mode) + { + FI2C_ERROR("i2c work mode shall be master"); + return FI2C_ERR_INVAL_STATE; + } + + ret = FI2cMasterStartTrans(instance_p, mem_addr, mem_byte_len, FI2C_DATA_CMD_WRITE); + if (FI2C_SUCCESS != ret) + return ret; + /*for trigger rx intr*/ + while (tx_len > 0 || rx_len > 0) + { + /* code */ + rx_limit = FI2C_IIC_FIFO_MAX_LV - FI2C_READ_REG32(base_addr, FI2C_RXFLR_OFFSET); + tx_limit = FI2C_IIC_FIFO_MAX_LV - FI2C_READ_REG32(base_addr, FI2C_TXFLR_OFFSET); + + while (tx_len > 0 & rx_limit > 0 & tx_limit > 0) + { + /* code */ + if (tx_len == 1) + { + + reg_val = FI2C_DATA_CMD_READ | FI2C_DATA_CMD_STOP; + FI2C_WRITE_REG32(instance_p->config.base_addr, FI2C_DATA_CMD_OFFSET, reg_val); + } + else + { + reg_val = FI2C_DATA_CMD_READ; + FI2C_WRITE_REG32(instance_p->config.base_addr, FI2C_DATA_CMD_OFFSET, reg_val); + } + tx_len--; + rx_limit--; + tx_limit--; + } + + u8 rx_tem = FI2C_READ_REG32(base_addr, FI2C_RXFLR_OFFSET); + while (rx_tem > 0 & rx_len > 0) + { + /* code */ + if (FI2C_GET_STATUS(base_addr) & FI2C_STATUS_RFNE) + { + /* trans one byte */ + *buf_p++ = FI2C_READ_DATA(base_addr); + rx_len--; + rx_tem--; + trans_timeout = 0; + } + else if (FI2C_TIMEOUT < (++trans_timeout)) + { + ret = FI2C_ERR_TIMEOUT; + FI2C_ERROR("timeout in i2c master read"); + break; + } + } + } + if (FI2C_SUCCESS == ret) + { + ret = FI2cMasterStopTrans(instance_p); + } + return ret; +} + +/** + * @name: FI2cMasterWritePoll + * @msg: I2C主机写,阻塞直到完成写操作或失败 + * @return {FError *} 返回错误码 + * @param {FI2c} *instance_p I2C驱动实例数据 + * @param {u32} mem_addr 从机的内部偏移地址 + * @param {u8} mem_byte_len, Size of internal memory address 1->8bit ~ 4->32bit + * @param {u8} *buf_p 写源缓冲区 + * @param {size_t} buf_len 写源缓冲区长度 + */ +FError FI2cMasterWritePoll(FI2c *instance_p, u32 mem_addr, u8 mem_byte_len, const u8 *buf_p, u32 buf_len) +{ + FASSERT(instance_p && buf_p); + FError ret = FI2C_SUCCESS; + u32 buf_idx = buf_len; + u32 tx_limit; + uintptr base_addr = instance_p->config.base_addr; + u32 reg_val; + u32 trans_timeout = 0; + + if (FT_COMPONENT_IS_READY != instance_p->is_ready) + { + FI2C_ERROR("i2c driver not ready"); + return FI2C_ERR_NOT_READY; + } + if (FI2C_MASTER != instance_p->config.work_mode) + { + FI2C_ERROR("i2c work mode shall be master"); + return FI2C_ERR_INVAL_STATE; + } + + ret = FI2cMasterStartTrans(instance_p, mem_addr, mem_byte_len, FI2C_DATA_CMD_WRITE); + if (FI2C_SUCCESS != ret) + return ret; + while (buf_idx) + { + tx_limit = FI2C_IIC_FIFO_MAX_LV - FI2C_READ_REG32(base_addr, FI2C_TXFLR_OFFSET); + while (tx_limit > 0 & buf_idx > 0) + { + if (FI2C_GET_STATUS(base_addr) & FI2C_STATUS_TFNF) + { + if (1 == buf_idx) + { + reg_val = (FI2C_DATA_MASK & *buf_p) | + FI2C_DATA_CMD_WRITE | + FI2C_DATA_CMD_STOP; + FI2C_INFO("Write Stop Singal"); + } + else + { + reg_val = (FI2C_DATA_MASK & *buf_p) | + FI2C_DATA_CMD_WRITE; + } + buf_idx--; + tx_limit--; + FI2C_WRITE_REG32(base_addr, FI2C_DATA_CMD_OFFSET, reg_val); + buf_p++; + trans_timeout = 0; + } + else if (FI2C_TIMEOUT < ++trans_timeout) + { + ret = FI2C_ERR_TIMEOUT; + FI2C_ERROR("timeout in i2c master write"); + break; + } + } + } + if (FI2C_SUCCESS == ret) + { + ret = FI2cMasterStopTrans(instance_p); + } + return ret; +} + +/** + * @name: FI2cMasterReadIntr + * @msg: I2C主机读,中断完成读操作或失败 + * @return {FError *} 返回错误码 + * @param {FI2c} *instance_p I2C驱动实例数据 + * @param {u32} mem_addr 从机的内部偏移地址 + * @param {u8} mem_byte_len, Size of internal memory address 1->8bit ~ 4->32bit + * @param {u8} *buf_p 读目的缓冲区 + * @param {int} buf_len 读目的缓冲区长度 + */ +FError FI2cMasterReadIntr(FI2c *instance_p, u32 mem_addr, u8 mem_byte_len, u8 *buf_p, u32 buf_len) +{ + FError ret = FI2C_SUCCESS; + FASSERT(instance_p); + u32 mask; + u32 reg_val; + u32 trans_timeout; + + if (FT_COMPONENT_IS_READY != instance_p->is_ready) + { + FI2C_ERROR("i2c driver not ready"); + return FI2C_ERR_NOT_READY; + } + if (FI2C_MASTER != instance_p->config.work_mode) + { + FI2C_ERROR("i2c work mode shall be master"); + return FI2C_ERR_INVAL_STATE; + } + + while (instance_p->status != STATUS_IDLE) + { + /* code */ + fsleep_millisec(1); + if (FI2C_TIMEOUT < (++trans_timeout)) + { + ret = FI2C_ERR_TIMEOUT; + FI2C_ERROR("timeout in i2c master read intr."); + break; + } + } + instance_p->rxframe.data_buff = buf_p; + instance_p->rxframe.rx_total_num = buf_len; + instance_p->txframe.tx_total_num = buf_len; + instance_p->rxframe.rx_cnt = 0; + FI2C_SET_RX_TL(instance_p->config.base_addr, 0);/* 0 表示接收缓冲区大于等于 1 时触发中断 */ + ret = FI2cMasterStartTrans(instance_p, mem_addr, mem_byte_len, FI2C_DATA_CMD_WRITE); + instance_p->status = STATUS_READ_IN_PROGRESS; + if (FI2C_SUCCESS != ret) + return ret; + mask = FI2C_GET_INTRRUPT_MASK(instance_p->config.base_addr); + mask |= FI2C_INTR_MASTER_RD_MASK; + ret = FI2cMasterSetupIntr(instance_p, mask); + if (FI2C_SUCCESS != ret) + return ret; + return ret; +} + +/** + * @name: FI2cMasterWriteIntr + * @msg: I2C主机写,中断写操作或失败 + * @return {FError *} 返回错误码 + * @param {FI2c} *instance_p I2C驱动实例数据 + * @param {u32} mem_addr 从机的内部偏移地址 + * @param {u8} mem_byte_len, Size of internal memory address 1->8bit ~ 4->32bit + * @param {u8} *buf_p 写源缓冲区 + * @param {size_t} buf_len 写源缓冲区长度 + */ +FError FI2cMasterWriteIntr(FI2c *instance_p, u32 mem_addr, u8 mem_byte_len, const u8 *buf_p, u32 buf_len) +{ + FError ret = FI2C_SUCCESS; + FASSERT(instance_p); + u32 mask; + u32 trans_timeout = 0; + if (FT_COMPONENT_IS_READY != instance_p->is_ready) + { + FI2C_ERROR("i2c driver not ready"); + return FI2C_ERR_NOT_READY; + } + + if (FI2C_MASTER != instance_p->config.work_mode) + { + FI2C_ERROR("i2c work mode shall be master"); + return FI2C_ERR_INVAL_STATE; + } + while (instance_p->status != STATUS_IDLE) + { + /* code */ + fsleep_millisec(1); + if (FI2C_TIMEOUT < (++trans_timeout)) + { + ret = FI2C_ERR_TIMEOUT; + FI2C_ERROR("timeout in i2c master write intr."); + break; + } + } + instance_p->txframe.data_buff = buf_p; + instance_p->txframe.tx_total_num = buf_len; + instance_p->txframe.tx_cnt = 0; + ret = FI2cMasterStartTrans(instance_p, mem_addr, mem_byte_len, FI2C_DATA_CMD_WRITE); + if (FI2C_SUCCESS != ret) + return ret; + instance_p->status = STATUS_WRITE_IN_PROGRESS; + mask = FI2C_GET_INTRRUPT_MASK(instance_p->config.base_addr); + mask |= FI2C_INTR_MASTER_WR_MASK; + ret = FI2cMasterSetupIntr(instance_p, mask); + if (FI2C_SUCCESS != ret) + return ret; + return ret; +} diff --git a/bsp/phytium/libraries/standalone/drivers/i2c/fi2c/fi2c_sinit.c b/bsp/phytium/libraries/standalone/drivers/i2c/fi2c/fi2c_sinit.c new file mode 100644 index 0000000000..8ea6a12dec --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/i2c/fi2c/fi2c_sinit.c @@ -0,0 +1,66 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fi2c_sinit.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:36:52 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + + +/* - This file contains the implementation of driver's static initialization functionality. +- 驱动静态初始化 */ + +/***************************** Include Files *********************************/ + +#include "ftypes.h" +#include "fparameters.h" +#include "fi2c.h" +#include "sdkconfig.h" +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Variable Definitions *****************************/ + +extern const FI2cConfig FI2C_CONFIG_TBL[I2C_INSTANCE_NUM]; +/************************** Function Prototypes ******************************/ +/** + * @name: FI2cLookupConfig + * @msg: 获取I2C驱动的默认配置参数 + * @return {const FI2cConfig*} 驱动默认参数 + * @param {u32} instance_id, 当前控制的I2C控制器实例号 + */ +const FI2cConfig *FI2cLookupConfig(u32 instance_id) +{ + const FI2cConfig *ptr = NULL; + u32 index; + + for (index = 0; index < (u32)I2C_INSTANCE_NUM; index++) + { + if (FI2C_CONFIG_TBL[index].instance_id == instance_id) + { + ptr = &FI2C_CONFIG_TBL[index]; + break; + } + } + + return (const FI2cConfig *)ptr; +} diff --git a/bsp/phytium/libraries/standalone/drivers/ipc/Kconfig b/bsp/phytium/libraries/standalone/drivers/ipc/Kconfig new file mode 100644 index 0000000000..2012b165d2 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/ipc/Kconfig @@ -0,0 +1,9 @@ +config ENABLE_FSEMAPHORE + bool + prompt "Use FSemaphore" + default n + depends on TARGET_E2000S || TARGET_E2000D || TARGET_E2000Q + help + Select FSemaphore driver component + + \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/ipc/fsemaphore/fsemaphore.c b/bsp/phytium/libraries/standalone/drivers/ipc/fsemaphore/fsemaphore.c new file mode 100644 index 0000000000..6409551a3a --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/ipc/fsemaphore/fsemaphore.c @@ -0,0 +1,337 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsemaphore.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:25:29 + * Description:  This files is for semaphore user api implmentation + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2022/5/23 init commit + */ + + +/***************************** Include Files *********************************/ +#include + +#include "fio.h" +#include "ferror_code.h" +#include "ftypes.h" +#include "fdebug.h" +#include "fassert.h" + +#include "fsemaphore_hw.h" +#include "fsemaphore.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FSEMA_DEBUG_TAG "SEMA" +#define FSEMA_ERROR(format, ...) FT_DEBUG_PRINT_E(FSEMA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSEMA_WARN(format, ...) FT_DEBUG_PRINT_W(FSEMA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSEMA_INFO(format, ...) FT_DEBUG_PRINT_I(FSEMA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSEMA_DEBUG(format, ...) FT_DEBUG_PRINT_D(FSEMA_DEBUG_TAG, format, ##__VA_ARGS__) + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + +/*****************************************************************************/ +/** + * @name: FSemaCfgInitialize + * @msg: 初始化Semaphore控制器 + * @return {FError} FSEMA_SUCCESS 表示初始化成功 + * @param {FSema} *instance, Semaphore控制器实例 + * @param {FSemaConfig} *input_config, Semaphore控制器配置 + */ +FError FSemaCfgInitialize(FSema *const instance, const FSemaConfig *input_config) +{ + FASSERT(instance && input_config); + uintptr base_addr = input_config->base_addr; + FASSERT_MSG((0 != base_addr), "invalid base addr"); + FError ret = FSEMA_SUCCESS; + + if (FT_COMPONENT_IS_READY == instance->is_ready) + { + FSEMA_WARN("device is already initialized!!!"); + } + + if (&instance->config != input_config) + instance->config = *input_config; + + FSemaHwResetAll(base_addr); /* 重置所有的锁 */ + + if (FSEMA_SUCCESS == ret) + instance->is_ready = FT_COMPONENT_IS_READY; + + return ret; +} + +/** + * @name: FSemaDeInitialize + * @msg: 去初始化Semaphore控制器 + * @return {void} 无 + * @param {FSema} *instance, Semaphore控制器实例 + */ +void FSemaDeInitialize(FSema *const instance) +{ + FASSERT(instance); + u32 loop; + uintptr base_addr = instance->config.base_addr; + + for (loop = 0; loop < FSEMA_NUM_OF_LOCKER; loop++) + { + if (NULL != instance->locker[loop]) + { + FSEMA_WARN("locker %d @%p is not yet deleted !!!", loop, instance->locker[loop]); + memset(instance->locker[loop], 0, sizeof(*instance->locker[loop])); + } + + } + + if (0 != base_addr) /* 如果base addr为0,实例可能还没有初始化 */ + { + FSemaHwResetAll(base_addr); + } + + memset(instance, 0, sizeof(*instance)); + + return; +} + +/** + * @name: FSemaCreateLocker + * @msg: 分配和创建Semaphore锁 + * @return {FError} FSEMA_SUCCESS 表示分配成功 + * @param {FSema} *instance, Semaphore控制器实例 + * @param {FSemaLocker} *locker, Semaphore锁的实例 + */ +FError FSemaCreateLocker(FSema *const instance, FSemaLocker *const locker) +{ + FASSERT(instance && locker); + u32 locker_idx; + + if (FT_COMPONENT_IS_READY != instance->is_ready) + { + FSEMA_ERROR("device@%p not yet inited !!!", instance->config.base_addr); + return FSEMA_ERR_NOT_INIT; + } + + for (locker_idx = 0; locker_idx < FSEMA_NUM_OF_LOCKER; locker_idx++) + { + /* 分配一把未创建的锁 */ + if (NULL == instance->locker[locker_idx]) + { + FSEMA_INFO("allocate locker %d", locker_idx); + break; + } + } + + if (locker_idx >= FSEMA_NUM_OF_LOCKER) + { + FSEMA_ERROR("no locker available !!!"); + return FSEMA_ERR_NO_AVAILABLE_LOCKER; /* 所有的锁都已经分配创建 */ + } + + instance->locker[locker_idx] = locker; + locker->index = locker_idx; /* 分配锁,将锁的实例挂在控制器实例上 */ + + locker->owner = FSEMA_OWNER_NONE; /* 当前锁还没有owner */ + locker->name[0] = '\0'; + locker->sema = instance; + + return FSEMA_SUCCESS; +} + +/** + * @name: FSemaTryLock + * @msg: 尝试获取Semaphore锁 + * @return {FError} FSEMA_SUCCESS 表示成功获取锁,FSEMA_ERR_LOCK_TIMEOUT 表示锁已经被占用 + * @param {FSemaLocker} *locker, Semaphore锁的实例 + * @param {u32} owner, 当前尝试获取锁的是谁 + * @param {u32} try_times, 尝试获取的次数 + * @param {FSemaRelaxHandler} relax_handler, 每次尝试获取锁失败后的relax函数 + */ +FError FSemaTryLock(FSemaLocker *const locker, u32 owner, u32 try_times, FSemaRelaxHandler relax_handler) +{ + FASSERT(locker && locker->sema); + FSema *const instance = locker->sema; + uintptr base_addr = instance->config.base_addr; + boolean lock_success = FALSE; + FError ret = FSEMA_SUCCESS; + + if (FT_COMPONENT_IS_READY != instance->is_ready) + { + FSEMA_ERROR("device@%p not yet inited !!!", instance->config.base_addr); + return FSEMA_ERR_NOT_INIT; + } + + while (try_times > 0) + { + /* 尝试获取锁 */ + lock_success = FSemaTryLockOnce(base_addr, locker->index); + if (TRUE == lock_success) + break; + + if (relax_handler) + relax_handler(instance); + + try_times--; + } + + if (FALSE == lock_success) + { + ret = FSEMA_ERR_LOCK_TIMEOUT; + FSEMA_ERROR("locker-%d has been taken by owner 0x%x", locker->index, locker->owner); + } + else + { + locker->owner = owner; /* 记录当前locker的owner */ + FSEMA_INFO("locker-%d taken success by owner 0x%x", locker->index, owner); + } + + return ret; +} + +/** + * @name: FSemaUnlock + * @msg: 尝试释放Semaphore锁 + * @return {FError} FSEMA_SUCCESS释放锁成功 + * @param {FSemaLocker} *locker, Semaphore锁实例 + * @param {u32} owner, 当前尝试释放锁的身份 + */ +FError FSemaUnlock(FSemaLocker *const locker, u32 owner) +{ + FASSERT(locker && locker->sema); + FSema *const instance = locker->sema; + uintptr base_addr = instance->config.base_addr; + FError ret = FSEMA_SUCCESS; + u32 reg_val; + + if (FT_COMPONENT_IS_READY != instance->is_ready) + { + FSEMA_ERROR("device@%p not yet inited !!!", instance->config.base_addr); + return FSEMA_ERR_NOT_INIT; + } + + if (locker->owner != owner) + { + FSEMA_ERROR("locker is owned by 0x%x, 0x%x has no premission to unlock it !!!", + locker->owner, owner); + return FSEMA_ERR_NO_PERMISSION; + } + + if (FALSE == FSemaHwGetStatus(base_addr, locker->index)) + { + FSEMA_INFO("locker-%d is not in locked status 0x%x!!!", + locker->index, FSemaReadReg(base_addr, FSEMA_STATE_REG_OFFSET)); + return ret; + } + + reg_val = FSEMA_RLOCK_X_UNLOCK; + FSemaWriteReg(base_addr, FSEMA_RLOCK_X_REG_OFFSET(locker->index), reg_val); /* 写0解锁信号量 */ + locker->owner = FSEMA_OWNER_NONE; /* 解锁成功,当前锁持有者为None */ + + return ret; +} + +/** + * @name: FSemaUnlockAll + * @msg: 强制解除所有Semaphore锁 + * @return {FError} FSEMA_SUCCESS表示强制解锁成功 + * @param {FSema} *instance, Semaphore控制器实例 + */ +FError FSemaUnlockAll(FSema *const instance) +{ + FASSERT(instance); + uintptr base_addr = instance->config.base_addr; + u32 loop; + + if (FT_COMPONENT_IS_READY != instance->is_ready) + { + FSEMA_ERROR("device@%p not yet inited !!!", instance->config.base_addr); + return FSEMA_ERR_NOT_INIT; + } + + FSemaHwResetAll(base_addr); + + for (loop = 0; loop < FSEMA_NUM_OF_LOCKER; loop++) + { + if (NULL != instance->locker[loop]) + { + instance->locker[loop]->owner = FSEMA_OWNER_NONE; /* 解锁成功,当前锁持有者为None */ + } + } + + return FSEMA_SUCCESS; +} + +/** + * @name: FSemaDeleteLocker + * @msg: 强制解除Semaphore锁并删除锁实例 + * @return {FError} FSEMA_SUCCESS 表示删除锁成功 + * @param {FSemaLocker} *locker, Semaphore锁实例 + */ +FError FSemaDeleteLocker(FSemaLocker *const locker) +{ + FASSERT(locker && locker->sema); + FSema *const instance = locker->sema; + uintptr base_addr = instance->config.base_addr; + u32 locker_idx = locker->index; + + if (FT_COMPONENT_IS_READY != instance->is_ready) + { + FSEMA_ERROR("device@%p not yet inited !!!", instance->config.base_addr); + return FSEMA_ERR_NOT_INIT; + } + + if (TRUE == FSemaHwGetStatus(base_addr, locker_idx)) + { + FSEMA_WARN("caution, locker-%d has been taken by 0x%x !!!", + locker_idx, locker->owner); + } + + FASSERT_MSG((instance->locker[locker_idx] == locker), "invalid locker index %d", locker_idx); + + FSemaWriteReg(base_addr, FSEMA_RLOCK_X_REG_OFFSET(locker->index), FSEMA_RLOCK_X_UNLOCK); /* 写0解锁信号量 */ + + instance->locker[locker_idx] = NULL; + memset(locker, 0, sizeof(*locker)); + + return FSEMA_SUCCESS; +} + +/** + * @name: FSemaIsLocked + * @msg: 检查指定Semaphore锁是否处于锁定状态 + * @return {boolean} TRUE: 处于锁定状态 + * @param {FSemaLocker} *locker, Semaphore锁实例 + */ +boolean FSemaIsLocked(FSemaLocker *locker) +{ + FASSERT(locker && locker->sema); + FSema *const instance = locker->sema; + uintptr base_addr = instance->config.base_addr; + + if (FT_COMPONENT_IS_READY != instance->is_ready) + { + FSEMA_ERROR("device@%p not yet inited !!!", instance->config.base_addr); + return FSEMA_ERR_NOT_INIT; + } + + return FSemaHwGetStatus(base_addr, locker->index); +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/ipc/fsemaphore/fsemaphore.h b/bsp/phytium/libraries/standalone/drivers/ipc/fsemaphore/fsemaphore.h new file mode 100644 index 0000000000..361234d1e5 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/ipc/fsemaphore/fsemaphore.h @@ -0,0 +1,109 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsemaphore.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:25:35 + * Description:  This files is for semaphore user api definition + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2022/5/23 init commit + */ + + +#ifndef DRIVERS_IPC_FSEMAPHORE_H +#define DRIVERS_IPC_FSEMAPHORE_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ +#include "ftypes.h" +#include "ferror_code.h" + +/************************** Constant Definitions *****************************/ +#define FSEMA_NUM_OF_LOCKER 32U +#define FSEMA_OWNER_NONE 0U + +#define FSEMA_SUCCESS FT_SUCCESS +#define FSEMA_ERR_NOT_INIT FT_MAKE_ERRCODE(ErrModBsp, ErrSema, 0U) +#define FSEMA_ERR_NO_AVAILABLE_LOCKER FT_MAKE_ERRCODE(ErrModBsp, ErrSema, 1U) +#define FSEMA_ERR_LOCK_TIMEOUT FT_MAKE_ERRCODE(ErrModBsp, ErrSema, 2U) +#define FSEMA_ERR_NO_PERMISSION FT_MAKE_ERRCODE(ErrModBsp, ErrSema, 3U) +/**************************** Type Definitions *******************************/ +typedef struct +{ + u32 id; /* Semaphore控制器id */ + uintptr base_addr; /* Semaphore控制器基地址 */ +} FSemaConfig; /* Semaphore控制器配置 */ + +typedef struct _FSema FSema; + +typedef struct +{ + u32 index; /* Semaphore锁id */ +#define FSEMA_LOCKER_NAME_LEN 32U + char name[FSEMA_LOCKER_NAME_LEN]; /* Semaphore锁的名字 */ + u32 owner; /* Semaphore锁的拥有者, 当前持有锁的人, 如果没有上锁就标记FSEMA_OWNER_NONE */ + FSema *sema; /* Semaphore控制器实例 */ +} FSemaLocker; /* Semaphore锁实例 */ + +typedef struct _FSema +{ + FSemaConfig config; /* Semaphore控制器配置 */ + u32 is_ready; /* Semaphore控制器初始化是否完成 */ + FSemaLocker *locker[FSEMA_NUM_OF_LOCKER]; /* Semaphore锁实例,locker[i] == NULL 表示锁尚未分配 */ +} FSema; /* Semaphore控制器实例 */ + +typedef void (*FSemaRelaxHandler)(FSema *const instance); /* 等待下一次上锁的relax函数 */ +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ +/* 获取Semaphore的默认配置 */ +const FSemaConfig *FSemaLoopkupConfig(u32 instance_id); + +/* 初始化Semaphore控制器 */ +FError FSemaCfgInitialize(FSema *const instance, const FSemaConfig *config); + +/* 去初始化Semaphore控制器 */ +void FSemaDeInitialize(FSema *const instance); + +/* 分配和创建Semaphore锁 */ +FError FSemaCreateLocker(FSema *const instance, FSemaLocker *const locker); + +/* 强制解除Semaphore锁并删除锁实例 */ +FError FSemaDeleteLocker(FSemaLocker *const locker); + +/* 尝试获取指定Semaphore锁 */ +FError FSemaTryLock(FSemaLocker *const locker, u32 owner, u32 try_times, FSemaRelaxHandler relax_handler); + +/* 尝试释放指定Semaphore锁 */ +FError FSemaUnlock(FSemaLocker *const locker, u32 owner); + +/* 强制解除所有Semaphore锁 */ +FError FSemaUnlockAll(FSema *const instance); + +/* 检查指定Semaphore锁是否处于锁定状态 */ +boolean FSemaIsLocked(FSemaLocker *locker); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsp/phytium/libraries/standalone/drivers/ipc/fsemaphore/fsemaphore_g.c b/bsp/phytium/libraries/standalone/drivers/ipc/fsemaphore/fsemaphore_g.c new file mode 100644 index 0000000000..1ee775f7d1 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/ipc/fsemaphore/fsemaphore_g.c @@ -0,0 +1,50 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsemaphore_g.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:25:09 + * Description:  This files is for semaphore static variables + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2022/5/23 init commit + */ + +/***************************** Include Files *********************************/ + +#include "ftypes.h" +#include "fparameters.h" +#include "fsemaphore.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + +const FSemaConfig fsema_cfg_tbl[FSEMA_INSTANCE_NUM] = +{ + [FSEMA0_ID] = + { + .id = FSEMA0_ID, + .base_addr = FSEMA0_BASE_ADDR + } +}; + +/*****************************************************************************/ diff --git a/bsp/phytium/libraries/standalone/drivers/ipc/fsemaphore/fsemaphore_hw.h b/bsp/phytium/libraries/standalone/drivers/ipc/fsemaphore/fsemaphore_hw.h new file mode 100644 index 0000000000..d70e93ebf5 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/ipc/fsemaphore/fsemaphore_hw.h @@ -0,0 +1,153 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsemaphore_hw.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:24:52 + * Description:  This files is for semaphore register definition + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2022/5/23 init commit + */ + + +#ifndef DRIVERS_IPC_FSEMAPHORE_HW_H +#define DRIVERS_IPC_FSEMAPHORE_HW_H + +#ifdef __cplusplus +extern "C" +{ +#endif +/***************************** Include Files *********************************/ + +#include "fparameters.h" +#include "ftypes.h" +#include "fio.h" +#include "fassert.h" +#include "fkernel.h" + +/************************** Constant Definitions *****************************/ +/** @name Register Map + * + * Register offsets from the base address of an Semaphore. + * @{ + */ +#define FSEMA_RST_REG_OFFSET 0x0U /* 写 1 复位所有信号量 */ +#define FSEMA_IND_RST_REG_OFFSET 0x4U /* 写信号量独热码为 1,复位对应信号量 */ +#define FSEMA_STATE_REG_OFFSET 0x8U /* 信号量 0 ~ 31的状态 */ +#define FSEMA_RLOCK_X_REG_OFFSET(x) (0x10U + (x) * 4) /* 信号量 X 读锁定寄存器 */ + +/** @name FSEMA_RST_REG_OFFSET Register + */ +#define FSEMA_RST_ALL BIT(0) /* 写 1 复位所有信号量 */ + +/** @name FSEMA_IND_RST_REG_OFFSET Register + */ +#define FSEMA_IND_RSET(locker_idx) BIT(locker_idx) /* 写信号量独热码为 1,复位对应信号量 */ + +/** @name FSEMA_STATE_REG_OFFSET Register + */ +#define FSEMA_LOCKER_STATE(locker_idx) BIT(locker_idx) /* 状态:0 表示解锁,1 表示锁定 */ + +/** @name FSEMA_RLOCK_X_REG_OFFSET Register + */ +#define FSEMA_RLOCK_X_UNLOCK 0 /* 写 0:信号量被解锁 */ +/* 读返回 0:信号量当前未被锁定,读后被锁定 + 读返回 1:信号量当前已经被锁定,锁定失败 */ +#define FSEMA_RLOCK_X_IS_LOCKED 1 +#define FSEMA_RLOCK_X_TAKE_LOCKER 0 + +#define FSEMA_MIN_LOCKER_IDX 0 +#define FSEMA_MAX_LOCKER_IDX 31 + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +static u32 FSemaReadReg(uintptr base_addr, u32 reg_off) +{ + return FtIn32(base_addr + reg_off); +} + +static inline void FSemaWriteReg(uintptr base_addr, u32 reg_off, u32 reg_val) +{ + FtOut32(base_addr + reg_off, reg_val); + return; +} + +/** + * @name: FSemaHwResetAll + * @msg: 强制解除所有锁定 + * @return {void} 无 + * @param {uintptr} base_addr, Semaphore 控制器基地址 + */ +static inline void FSemaHwResetAll(uintptr base_addr) +{ + FSemaWriteReg(base_addr, FSEMA_RST_REG_OFFSET, FSEMA_RST_ALL); +} + +/** + * @name: FSemaHwResetLocker + * @msg: 强制解除指定锁定 + * @return {void} 无 + * @param {uintptr} base_addr, Semaphore 控制器基地址 + * @param {u32} locker_idx, Semaphore锁id + */ +static inline void FSemaHwResetLocker(uintptr base_addr, u32 locker_idx) +{ + u32 reg_val = FSemaReadReg(base_addr, FSEMA_IND_RST_REG_OFFSET); + reg_val |= FSEMA_IND_RSET(locker_idx); + FSemaWriteReg(base_addr, FSEMA_IND_RST_REG_OFFSET, reg_val); + return; +} + +/** + * @name: FSemaHwGetStatus + * @msg: 获取锁定状态 + * @return {boolean} TRUE: 被锁定, FALSE: 未被锁定 + * @param {uintptr} base_addr, Semaphore 控制器基地址 + * @param {u32} locker_idx, Semaphore锁id + */ +static inline boolean FSemaHwGetStatus(uintptr base_addr, u32 locker_idx) +{ + u32 reg_val = FSemaReadReg(base_addr, FSEMA_STATE_REG_OFFSET); + return (reg_val & FSEMA_LOCKER_STATE(locker_idx)) ? TRUE : FALSE; +} + +/** + * @name: FSemaTryLockOnce + * @msg: 尝试锁定 + * @return {boolean} TRUE: 锁定成功 + * @param {uintptr} base_addr, Semaphore 控制器基地址 + * @param {u32} locker_idx, Semaphore锁id + */ +static inline boolean FSemaTryLockOnce(uintptr base_addr, u32 locker_idx) +{ + boolean lock_success = FALSE; + u32 reg_val = FSemaReadReg(base_addr, FSEMA_RLOCK_X_REG_OFFSET(locker_idx)); /* 读寄存器,尝试上锁 */ + + /* 读返回 1:信号量之前已经被锁定,本次锁定失败 + 读返回 0:信号量之前未被锁定,本次锁定成功 */ + return (FSEMA_RLOCK_X_IS_LOCKED & reg_val) ? FALSE : TRUE; +} + +/************************** Function Prototypes ******************************/ + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/ipc/fsemaphore/fsemaphore_sinit.c b/bsp/phytium/libraries/standalone/drivers/ipc/fsemaphore/fsemaphore_sinit.c new file mode 100644 index 0000000000..4e52d6fd1a --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/ipc/fsemaphore/fsemaphore_sinit.c @@ -0,0 +1,63 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsemaphore_sinit.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:25:15 + * Description:  This files is for semaphore static init + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2022/5/23 init commit + */ + +/***************************** Include Files *********************************/ + +#include "ftypes.h" +#include "fparameters.h" +#include "fsemaphore.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Variable Definitions *****************************/ + +extern const FSemaConfig fsema_cfg_tbl[FSEMA_INSTANCE_NUM]; + +/************************** Function Prototypes ******************************/ +/** + * @name: FSemaLoopkupConfig + * @msg: 获取Semaphore的默认配置 + * @return {const FSemaConfig *} Semaphore的默认配置 + * @param {u32} instance_id, Semaphore的实例id + */ +const FSemaConfig *FSemaLoopkupConfig(u32 instance_id) +{ + const FSemaConfig *ptr = NULL; + u32 index; + + for (index = 0; index < (u32)FSEMA_INSTANCE_NUM; index++) + { + if (fsema_cfg_tbl[index].id == instance_id) + { + ptr = &fsema_cfg_tbl[index]; + break; + } + } + + return (const FSemaConfig *)ptr; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/mio/Kconfig b/bsp/phytium/libraries/standalone/drivers/mio/Kconfig new file mode 100644 index 0000000000..018035d6e8 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/mio/Kconfig @@ -0,0 +1,9 @@ +menu "Hardware Mio Configuration" + config ENABLE_MIO + bool + prompt "Use Mio" + depends on TARGET_E2000S || TARGET_E2000D || TARGET_E2000Q + + default n +endmenu + diff --git a/bsp/phytium/libraries/standalone/drivers/mio/fmio/fmio.c b/bsp/phytium/libraries/standalone/drivers/mio/fmio/fmio.c new file mode 100644 index 0000000000..ab35e436aa --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/mio/fmio/fmio.c @@ -0,0 +1,137 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fmio.c + * Date: 2022-07-06 15:01:30 + * LastEditTime: 2022-07-06 15:01:30 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ +#include +#include "fmio_hw.h" +#include "fmio.h" + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FMIO_DEBUG_TAG "MIO" +#define FMIO_ERROR(format, ...) FT_DEBUG_PRINT_E(FMIO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FMIO_INFO(format, ...) FT_DEBUG_PRINT_I(FMIO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FMIO_DEBUG(format, ...) FT_DEBUG_PRINT_D(FMIO_DEBUG_TAG, format, ##__VA_ARGS__) + +/** + * @name: FMioFuncInit + * @msg: 初始化 + * @return {*} + * @param {FMioCtrl} *instance_p + * @param {u32} mio_type + */ +FError FMioFuncInit(FMioCtrl *instance_p, u32 mio_type) +{ + FASSERT(instance_p); + + FError ret = FMIO_SUCCESS; + + /* + * If the device is started, disallow the initialize and return a Status + * indicating it is started. This allows the user to de-initialize the device + * and reinitialize, but prevents a user from inadvertently + * initializing. + */ + if (FT_COMPONENT_IS_READY == instance_p->is_ready) + { + FMIO_ERROR("device is already initialized!!!"); + return FMIO_ERR_INVAL_STATE; + } + + ret = FMioSelectFunc(instance_p->config.mio_base_addr, mio_type); + if (FMIO_SUCCESS == ret) + { + instance_p->is_ready = FT_COMPONENT_IS_READY; + } + return ret; +} + +/** + * @name: FMioFuncDeinit + * @msg: 去初始化 + * @return {*} + * @param {FMioCtrl} *instance_p + */ +FError FMioFuncDeinit(FMioCtrl *instance_p) +{ + FASSERT(instance_p); + FError ret = FMIO_SUCCESS; + + instance_p->is_ready = 0; + + /* 重新配置成默认IIC模式 */ + ret = FMioSelectFunc(instance_p->config.mio_base_addr, FMIO_FUNC_SET_I2C); + + memset(instance_p, 0, sizeof(*instance_p)); + + return ret; +} + +/** + * @name: FMioFuncGetAddress + * @msg: 获取功能设置的基地址 + * @return {uintptr} + * @param {FMioCtrl} *instance_p + */ +uintptr FMioFuncGetAddress(FMioCtrl *instance_p, u32 mio_type) +{ + FASSERT(instance_p); + FError ret = FMIO_SUCCESS; + + if (instance_p->is_ready != FT_COMPONENT_IS_READY) + { + FMIO_ERROR("Mio instance_id: %d ,not init.", instance_p->config.instance_id); + return FMIO_ERR_NOT_READY; + } + + if (FMioGetFunc(instance_p->config.mio_base_addr) != mio_type) + { + FMIO_ERROR("Mio instance_id: %d ,mio_type error,please init type first.", instance_p->config.instance_id); + return FMIO_ERR_INVAL_STATE; + } + + return instance_p->config.func_base_addr; +} + +/** + * @name: FMioFuncGetIrqNum + * @msg: 获取MIO的中断号 + * @return {u32}中断号 + * @param {FMioCtrl} *instance_p + */ +u32 FMioFuncGetIrqNum(FMioCtrl *instance_p, u32 mio_type) +{ + FASSERT(instance_p); + FError ret = FMIO_SUCCESS; + + if (instance_p->is_ready != FT_COMPONENT_IS_READY) + { + FMIO_ERROR("Mio instance_id: %d ,not init.", instance_p->config.instance_id); + return FMIO_ERR_NOT_READY; + } + + if (FMioGetFunc(instance_p->config.mio_base_addr) != mio_type) + { + FMIO_ERROR("Mio instance_id: %d ,mio_type error,please init type first.", instance_p->config.instance_id); + return FMIO_ERR_INVAL_STATE; + } + + return instance_p->config.irq_num; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/mio/fmio/fmio.h b/bsp/phytium/libraries/standalone/drivers/mio/fmio/fmio.h new file mode 100644 index 0000000000..5343130add --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/mio/fmio/fmio.h @@ -0,0 +1,82 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fmio.h + * Date: 2022-06-21 15:40:06 + * LastEditTime: 2022-06-21 15:40:06 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef DRIVERS_MIO_FMIO_H +#define DRIVERS_MIO_FMIO_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ + +#include "ftypes.h" +#include "ferror_code.h" +#include "fassert.h" + +/************************** Constant Definitions *****************************/ +#define FMIO_SUCCESS FT_SUCCESS +#define FMIO_ERR_INVAL_PARM FT_MAKE_ERRCODE(ErrModBsp, ErrBspMio, 1) +#define FMIO_ERR_NOT_READY FT_MAKE_ERRCODE(ErrModBsp, ErrBspMio, 2) +#define FMIO_ERR_TIMEOUT FT_MAKE_ERRCODE(ErrModBsp, ErrBspMio, 3) +#define FMIO_ERR_NOT_SUPPORT FT_MAKE_ERRCODE(ErrModBsp, ErrBspMio, 4) +#define FMIO_ERR_INVAL_STATE FT_MAKE_ERRCODE(ErrModBsp, ErrBspMio, 5) + +/**************************** Type Definitions *******************************/ + +typedef struct +{ + u32 instance_id; /*mio id*/ + uintptr func_base_addr; /*I2C or UART function address*/ + u32 irq_num; /* Device intrrupt id */ + uintptr mio_base_addr; /*MIO control address*/ +} FMioConfig; /*mio configs*/ + +typedef struct +{ + FMioConfig config; /* mio config */ + u32 is_ready; /* mio initialize the complete flag */ +} FMioCtrl; + +/************************** Function Prototypes ******************************/ +/*获取MIO的配置信息*/ +const FMioConfig *FMioLookupConfig(u32 instance_id); + +/*初始化MIO的功能*/ +FError FMioFuncInit(FMioCtrl *instance_p, u32 mio_type); + +/*去初始化*/ +FError FMioFuncDeinit(FMioCtrl *instance_p); + +/*获取功能配置的基地址*/ +uintptr FMioFuncGetAddress(FMioCtrl *instance_p, u32 mio_type); + +/*获取功能的中断号*/ +u32 FMioFuncGetIrqNum(FMioCtrl *instance_p, u32 mio_type); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/mio/fmio/fmio_g.c b/bsp/phytium/libraries/standalone/drivers/mio/fmio/fmio_g.c new file mode 100644 index 0000000000..e018aa6452 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/mio/fmio/fmio_g.c @@ -0,0 +1,130 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fmio_g.c + * Date: 2022-06-20 21:05:07 + * LastEditTime: 2022-06-20 21:05:07 + * Description:  This file is for mio + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 0.1.0 liu 2022.06.20 init + */ + +/***************************** Include Files *********************************/ +#include "fparameters.h" +#include "fmio_hw.h" +#include "fmio.h" + +/************************** Constant Definitions *****************************/ + +const FMioConfig FMioConfigTbl[MIO_INSTANCE_NUM] = +{ + { + .instance_id = MIO_INSTANCE_0, + .func_base_addr = FMIO_BASE_ADDR(MIO_INSTANCE_0), + .irq_num = FMIO_IRQ_NUM(MIO_INSTANCE_0), + .mio_base_addr = FMIO_BASE_SET_ADDR(MIO_INSTANCE_0) + }, + { + .instance_id = MIO_INSTANCE_1, + .func_base_addr = FMIO_BASE_ADDR(MIO_INSTANCE_1), + .irq_num = FMIO_IRQ_NUM(MIO_INSTANCE_1), + .mio_base_addr = FMIO_BASE_SET_ADDR(MIO_INSTANCE_1) + }, + { + .instance_id = MIO_INSTANCE_2, + .func_base_addr = FMIO_BASE_ADDR(MIO_INSTANCE_2), + .irq_num = FMIO_IRQ_NUM(MIO_INSTANCE_2), + .mio_base_addr = FMIO_BASE_SET_ADDR(MIO_INSTANCE_2) + }, + { + .instance_id = MIO_INSTANCE_3, + .func_base_addr = FMIO_BASE_ADDR(MIO_INSTANCE_3), + .irq_num = FMIO_IRQ_NUM(MIO_INSTANCE_3), + .mio_base_addr = FMIO_BASE_SET_ADDR(MIO_INSTANCE_3) + }, + { + .instance_id = MIO_INSTANCE_4, + .func_base_addr = FMIO_BASE_ADDR(MIO_INSTANCE_4), + .irq_num = FMIO_IRQ_NUM(MIO_INSTANCE_4), + .mio_base_addr = FMIO_BASE_SET_ADDR(MIO_INSTANCE_4) + }, + { + .instance_id = MIO_INSTANCE_5, + .func_base_addr = FMIO_BASE_ADDR(MIO_INSTANCE_5), + .irq_num = FMIO_IRQ_NUM(MIO_INSTANCE_5), + .mio_base_addr = FMIO_BASE_SET_ADDR(MIO_INSTANCE_5) + }, + { + .instance_id = MIO_INSTANCE_6, + .func_base_addr = FMIO_BASE_ADDR(MIO_INSTANCE_6), + .irq_num = FMIO_IRQ_NUM(MIO_INSTANCE_6), + .mio_base_addr = FMIO_BASE_SET_ADDR(MIO_INSTANCE_6) + }, + { + .instance_id = MIO_INSTANCE_7, + .func_base_addr = FMIO_BASE_ADDR(MIO_INSTANCE_7), + .irq_num = FMIO_IRQ_NUM(MIO_INSTANCE_7), + .mio_base_addr = FMIO_BASE_SET_ADDR(MIO_INSTANCE_7) + }, + { + .instance_id = MIO_INSTANCE_8, + .func_base_addr = FMIO_BASE_ADDR(MIO_INSTANCE_8), + .irq_num = FMIO_IRQ_NUM(MIO_INSTANCE_8), + .mio_base_addr = FMIO_BASE_SET_ADDR(MIO_INSTANCE_8) + }, + { + .instance_id = MIO_INSTANCE_9, + .func_base_addr = FMIO_BASE_ADDR(MIO_INSTANCE_9), + .irq_num = FMIO_IRQ_NUM(MIO_INSTANCE_9), + .mio_base_addr = FMIO_BASE_SET_ADDR(MIO_INSTANCE_9) + }, + { + .instance_id = MIO_INSTANCE_10, + .func_base_addr = FMIO_BASE_ADDR(MIO_INSTANCE_10), + .irq_num = FMIO_IRQ_NUM(MIO_INSTANCE_10), + .mio_base_addr = FMIO_BASE_SET_ADDR(MIO_INSTANCE_10) + }, + { + .instance_id = MIO_INSTANCE_11, + .func_base_addr = FMIO_BASE_ADDR(MIO_INSTANCE_11), + .irq_num = FMIO_IRQ_NUM(MIO_INSTANCE_11), + .mio_base_addr = FMIO_BASE_SET_ADDR(MIO_INSTANCE_11) + }, + { + .instance_id = MIO_INSTANCE_12, + .func_base_addr = FMIO_BASE_ADDR(MIO_INSTANCE_12), + .irq_num = FMIO_IRQ_NUM(MIO_INSTANCE_12), + .mio_base_addr = FMIO_BASE_SET_ADDR(MIO_INSTANCE_12) + }, + { + .instance_id = MIO_INSTANCE_13, + .func_base_addr = FMIO_BASE_ADDR(MIO_INSTANCE_13), + .irq_num = FMIO_IRQ_NUM(MIO_INSTANCE_13), + .mio_base_addr = FMIO_BASE_SET_ADDR(MIO_INSTANCE_13) + }, + { + .instance_id = MIO_INSTANCE_14, + .func_base_addr = FMIO_BASE_ADDR(MIO_INSTANCE_14), + .irq_num = FMIO_IRQ_NUM(MIO_INSTANCE_14), + .mio_base_addr = FMIO_BASE_SET_ADDR(MIO_INSTANCE_14) + }, + { + .instance_id = MIO_INSTANCE_15, + .func_base_addr = FMIO_BASE_ADDR(MIO_INSTANCE_15), + .irq_num = FMIO_IRQ_NUM(MIO_INSTANCE_15), + .mio_base_addr = FMIO_BASE_SET_ADDR(MIO_INSTANCE_15) + } +}; diff --git a/bsp/phytium/libraries/standalone/drivers/mio/fmio/fmio_hw.c b/bsp/phytium/libraries/standalone/drivers/mio/fmio/fmio_hw.c new file mode 100644 index 0000000000..336d93c52e --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/mio/fmio/fmio_hw.c @@ -0,0 +1,89 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fmio_hw.c + * Date: 2022-06-20 21:05:23 + * LastEditTime: 2022-06-20 21:05:23 + * Description:  This file is for mio + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 0.1.0 liushengming 2022.06.20 init + */ + +/***************************** Include Files *********************************/ + +#include "fmio_hw.h" +#include "ftypes.h" +#include "fassert.h" +#include "fmio.h" +/***************** Macros (Inline Functions) Definitions *********************/ + +#define FMIO_DEBUG_TAG "MIO-HW" +#define FMIO_ERROR(format, ...) FT_DEBUG_PRINT_E(FMIO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FMIO_INFO(format, ...) FT_DEBUG_PRINT_I(FMIO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FMIO_DEBUG(format, ...) FT_DEBUG_PRINT_D(FMIO_DEBUG_TAG, format, ##__VA_ARGS__) +/************************** Function Prototypes ******************************/ + +/** + * @name: FMioSelectFunc + * @msg: 设置Mio功能 + * @return {*} + * @param {uintptr} addr + * @param {u32} mio_type + */ +FError FMioSelectFunc(uintptr addr, u32 mio_type) +{ + FASSERT(mio_type < FMIO_NUM_OF_MIO_FUNC); + FASSERT(addr); + + u32 reg_val; + + reg_val = FMioReadStatus(addr); + + if (mio_type == reg_val) + { + return FMIO_SUCCESS; + } + + FMioWriteFunc(addr, mio_type); + + return FMIO_SUCCESS; +} + +/** + * @name: FMioGetMioFunc + * @msg: 获取Mio状态 + * @return {*} + * @param {uintptr} addr + */ +u32 FMioGetFunc(uintptr addr) +{ + FASSERT(addr); + + return FMioReadStatus(addr); +} + +/** + * @name: FMioGetVersion + * @msg: 获取版本信息,默认32'h1 + * @return {*} + * @param {uintptr} addr + */ +u32 FMioGetVersion(uintptr addr) +{ + FASSERT(addr); + + return FMioReadVersion(addr); +} diff --git a/bsp/phytium/libraries/standalone/drivers/mio/fmio/fmio_hw.h b/bsp/phytium/libraries/standalone/drivers/mio/fmio/fmio_hw.h new file mode 100644 index 0000000000..acc1699fae --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/mio/fmio/fmio_hw.h @@ -0,0 +1,134 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fmio_hw.h + * Date: 2022-06-20 21:05:34 + * LastEditTime: 2022-06-20 21:05:34 + * Description:  This file is for mio + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 0.1.0 liushengming 2022.06.20 init + */ +#ifndef DRIVERS_MIO_FMIO_HW_H +#define DRIVERS_MIO_FMIO_HW_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "fparameters.h" +#include "fio.h" +#include "fkernel.h" +#include "fdebug.h" +#include "ferror_code.h" + + +/************************** Constant Definitions *****************************/ + +/** @name Register Map + * + * Register offsets from the base address of MIO device. + * + */ +#define FMIO_BASE_SET_ADDR(x) FMIO_BASE_ADDR(x)+0x1000 + +#define FMIO_FUNC_OFFSET 0x00 +#define FMIO_SEL_STATE_OFFSET 0x04 +#define FMIO_VER_OFFSET 0x100 + +/* creg_mio_func_sel */ +#define FMIO_FUNC_SEL_MASK GENMASK(1,0) +#define FMIO_FUNC_SET(n) (FMIO_FUNC_SEL_MASK & (n)) +enum +{ + FMIO_FUNC_SET_I2C = 0b00, + FMIO_FUNC_SET_UART = 0b01, + + FMIO_NUM_OF_MIO_FUNC +}; + +#define FMIO_FUNC_STATE_MASK GENMASK(1, 0) +#define FMIO_VERSION_MASK GENMASK(31, 0) + +/***************** Macros (Inline Functions) Definitions *********************/ +/** + * @name: FMIO_READ_REG32 + * @msg: 读取MIO寄存器 + * @param {u32} addr MIO的基地址 + * @param {u32} reg_offset MIO的寄存器的偏移 + * @return {u32} 寄存器参数 + */ +#define FMIO_READ_REG32(addr, reg_offset) FtIn32(addr + (u32)reg_offset) + +/** + * @name: FMIO_WRITE_REG32 + * @msg: 写入MIO寄存器 + * @param {u32} addr MIO的基地址 + * @param {u32} reg_offset MIO的寄存器的偏移 + * @param {u32} reg_value 写入寄存器参数 + * @return {void} + */ +#define FMIO_WRITE_REG32(addr, reg_offset, reg_value) FtOut32(addr + (u32)reg_offset, (u32)reg_value) + +/** + * @name: FMioWriteFunc + * @msg: 设置MIO的功能 + * @return {*} + * @param {uintptr} addr + * @param {u32} val + */ +static inline void FMioWriteFunc(uintptr addr, u32 val) +{ + FMIO_WRITE_REG32(addr, FMIO_FUNC_OFFSET, val); +} + +/** + * @name: FMioReadStatus + * @msg: 获取MIO的设置 + * @return {u32} register value + * @param {uintptr} addr + */ +static inline u32 FMioReadStatus(uintptr addr) +{ + return FMIO_READ_REG32(addr, FMIO_SEL_STATE_OFFSET) & FMIO_FUNC_STATE_MASK; +} + +/** + * @name: FMioReadVersion + * @msg: 获取MIO的版本信息 + * @return {u32} register value + * @param {uintptr} addr + */ +static inline u32 FMioReadVersion(uintptr addr) +{ + return FMIO_READ_REG32(addr, FMIO_VER_OFFSET) & FMIO_VERSION_MASK; +} + +/************************** Function Prototypes ******************************/ +/*设置Mio功能*/ +FError FMioSelectFunc(uintptr addr, u32 mio_type); + +/*获取Mio状态*/ +u32 FMioGetFunc(uintptr addr); + +/*获取版本信息,默认32'h1*/ +u32 FMioGetVersion(uintptr addr); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/mio/fmio/fmio_sinit.c b/bsp/phytium/libraries/standalone/drivers/mio/fmio/fmio_sinit.c new file mode 100644 index 0000000000..a971924e37 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/mio/fmio/fmio_sinit.c @@ -0,0 +1,60 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fmio_sinit.c + * Date: 2022-06-20 20:33:25 + * LastEditTime: 2022-06-20 20:33:25 + * Description:  This file is for mio + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 0.1.0 liushengming 2022.06.20 init + */ +#include "ftypes.h" +#include "fparameters.h" +#include "fassert.h" +#include "fmio.h" +#include "fmio_hw.h" + + +extern FMioConfig FMioConfigTbl[MIO_INSTANCE_NUM]; + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FMIO_DEBUG_TAG "MIO" +#define FMIO_ERROR(format, ...) FT_DEBUG_PRINT_E(FMIO_DEBUG_TAG, format, ##__VA_ARGS__) + +/************************** Function Prototypes ******************************/ + +/** + * @name: FMioLookupConfig + * @msg: get mio configs by id + * @param {u32} instance_id, id of mio ctrl + * @return {FMioConfig *}, mio config address + */ +const FMioConfig *FMioLookupConfig(u32 instance_id) +{ + FASSERT(instance_id < MIO_INSTANCE_NUM); + const FMioConfig *pconfig = NULL; + u32 index; + + for (index = 0; index < (u32)MIO_INSTANCE_NUM; index++) + { + if (FMioConfigTbl[index].instance_id == instance_id) + { + pconfig = &FMioConfigTbl[index]; + break; + } + } + return (const FMioConfig *)pconfig; +} diff --git a/bsp/phytium/libraries/standalone/drivers/mmc/Kconfig b/bsp/phytium/libraries/standalone/drivers/mmc/Kconfig new file mode 100644 index 0000000000..4bf40f4a68 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/mmc/Kconfig @@ -0,0 +1,19 @@ +config ENABLE_FSDMMC + bool + prompt "Use FSdmmc" + default n + depends on USE_SDMMC + depends on TARGET_F2000_4 || TARGET_D2000 + help + Select FSdmmc driver component + +config ENABLE_FSDIO + bool + prompt "Use FSdio" + default n + depends on USE_SDMMC + depends on TARGET_E2000S || TARGET_E2000D || TARGET_E2000Q + help + Select FSdio driver component + + \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio.c b/bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio.c new file mode 100644 index 0000000000..771e4ff9ca --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio.c @@ -0,0 +1,482 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsdio.c + * Date: 2022-05-26 16:27:54 + * LastEditTime: 2022-05-26 16:27:54 + * Description:  This files is for SDIO user function implementation + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2021/12/2 init + * 1.1 zhugengyu 2022/6/6 modify according to tech manual. + */ + +/***************************** Include Files *********************************/ + +#include "fio.h" +#include "fdebug.h" +#include "fassert.h" +#include "ftypes.h" +#include "fsleep.h" + +#include "fcache.h" + +#include "fsdio_hw.h" +#include "fsdio.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FSDIO_DEBUG_TAG "FSDIO" +#define FSDIO_ERROR(format, ...) FT_DEBUG_PRINT_E(FSDIO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSDIO_WARN(format, ...) FT_DEBUG_PRINT_W(FSDIO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSDIO_INFO(format, ...) FT_DEBUG_PRINT_I(FSDIO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSDIO_DEBUG(format, ...) FT_DEBUG_PRINT_D(FSDIO_DEBUG_TAG, format, ##__VA_ARGS__) + +/************************** Function Prototypes ******************************/ +static FError FSdioReset(FSdio *const instance_p); +static FError FSdioUpdateExternalClk(uintptr base_addr, u32 uhs_reg_val); + +/*****************************************************************************/ +/** + * @name: FSdioCfgInitialize + * @msg: initialization SDIO controller instance + * @return {FError} FSDIO_SUCCESS if initialization success, otherwise failed + * @param {FSdio} *instance_p, SDIO controller instance + * @param {FSdioConfig} *input_config_p, SDIO controller configure + * @note get into card-detect mode after initialization, bus width = 1, card freq = 400kHz + */ +FError FSdioCfgInitialize(FSdio *const instance_p, const FSdioConfig *input_config_p) +{ + FASSERT(instance_p && input_config_p); + FError ret = FSDIO_SUCCESS; + + if (FT_COMPONENT_IS_READY == instance_p->is_ready) + { + FSDIO_WARN("device is already initialized!!!"); + } + + if (&instance_p->config != input_config_p) + instance_p->config = *input_config_p; + + ret = FSdioReset(instance_p); /* reset the device */ + + if (FSDIO_SUCCESS == ret) + { + instance_p->is_ready = FT_COMPONENT_IS_READY; + FSDIO_INFO("device initialize success !!!"); + } + + return ret; +} + +/** + * @name: FSdioDeInitialize + * @msg: deinitialization SDIO controller instance + * @return {NONE} + * @param {FSdio} *instance_p, SDIO controller instance + */ +void FSdioDeInitialize(FSdio *const instance_p) +{ + FASSERT(instance_p); + uintptr base_addr = instance_p->config.base_addr; + + FSdioSetInterruptMask(instance_p, FSDIO_GENERAL_INTR, FSDIO_INT_ALL_BITS, FALSE); /* 关闭控制器中断位 */ + FSdioSetInterruptMask(instance_p, FSDIO_IDMA_INTR, FSDIO_DMAC_INT_ENA_ALL, FALSE); /* 关闭DMA中断位 */ + + FSdioClearRawStatus(base_addr); /* 清除中断状态 */ + FSdioClearDMAStatus(base_addr); + + FSdioSetPower(base_addr, FALSE); /* 关闭电源 */ + FSdioSetClock(base_addr, FALSE); /* 关闭卡时钟 */ + FSDIO_CLR_BIT(base_addr, FSDIO_UHS_REG_EXT_OFFSET, FSDIO_UHS_EXT_CLK_ENA); /* 关闭外部时钟 */ + FSDIO_CLR_BIT(base_addr, FSDIO_UHS_REG_OFFSET, FSDIO_UHS_REG_VOLT_180); /* 恢复为3.3v默认电压 */ + + instance_p->is_ready = 0; +} + +/** + * @name: FSdioSetClkFreq + * @msg: Set the Card clock freqency + * @return {None} + * @param {FSdio} *instance_p, SDIO controller instance + * @param {u32} input_clk_hz, Card clock freqency in Hz + */ +FError FSdioSetClkFreq(FSdio *const instance_p, u32 input_clk_hz) +{ + FASSERT(instance_p); + uintptr base_addr = instance_p->config.base_addr; + u32 reg_val; + u32 div = 0xff, drv = 0, sample = 0; + u32 first_uhs_div, tmp_ext_reg, div_reg; + FError ret = FSDIO_SUCCESS; + + FSDIO_INFO("set clk as %ld", input_clk_hz); + + /* must set 2nd stage clcok first then set 1st stage clock */ + /* experimental uhs setting --> 2nd stage clock, below setting parameters get from + experiment, for better sample timing */ + if (input_clk_hz >= FSDIO_SD_25_MHZ) /* e.g. 25MHz or 50MHz */ + { + tmp_ext_reg = FSDIO_UHS_REG(0U, 0U, 0x2U) | FSDIO_UHS_EXT_CLK_ENA; + FASSERT(tmp_ext_reg == 0x202); + } + else if (input_clk_hz == FSDIO_SD_400KHZ) /* 400kHz */ + { + tmp_ext_reg = FSDIO_UHS_REG(0U, 0U, 0x5U) | FSDIO_UHS_EXT_CLK_ENA; + FASSERT(tmp_ext_reg == 0x502); + } + else /* e.g. 20MHz */ + { + tmp_ext_reg = FSDIO_UHS_REG(0U, 0U, 0x3U) | FSDIO_UHS_EXT_CLK_ENA; + FASSERT(tmp_ext_reg == 0x302); + } + + /* update uhs setting */ + ret = FSdioUpdateExternalClk(base_addr, tmp_ext_reg); + if (FSDIO_SUCCESS != ret) + return ret; + + FSdioSetClock(base_addr, FALSE); /* disable clock */ + + /* send private cmd to update clock */ + ret = FSdioSendPrivateCmd(base_addr, FSDIO_CMD_UPD_CLK, 0U); + if (FSDIO_SUCCESS != ret) + return ret; + + /* experimental clk divide setting -- 1st stage clock */ + first_uhs_div = 1 + FSDIO_UHS_CLK_DIV_GET(tmp_ext_reg); + div = FSDIO_CLK_RATE_HZ / (2 * first_uhs_div * input_clk_hz); + if (div > 2) + { + sample = div / 2 + 1; + drv = sample - 1; + } + else if (div == 2) + { + drv = 0; + sample = 1; + } + + div_reg = FSDIO_CLK_DIV(sample, drv, div); + FSDIO_WRITE_REG(base_addr, FSDIO_CLKDIV_OFFSET, div_reg); + + FSDIO_INFO("UHS_REG_EXT: %x, CLKDIV: %x", + FSDIO_READ_REG(base_addr, FSDIO_UHS_REG_EXT_OFFSET), + FSDIO_READ_REG(base_addr, FSDIO_CLKDIV_OFFSET)); + + FSDIO_INFO("UHS_REG_EXT ext: 0x%x, CLKDIV: 0x%x", + FSDIO_READ_REG(base_addr, FSDIO_UHS_REG_EXT_OFFSET), + FSDIO_READ_REG(base_addr, FSDIO_CLKDIV_OFFSET)); + + FSdioSetClock(base_addr, TRUE); /* enable clock */ + + /* update clock for 1 stage clock */ + ret = FSdioSendPrivateCmd(base_addr, FSDIO_CMD_UPD_CLK, 0U); + if (FSDIO_SUCCESS != ret) + return ret; + + return ret; +} + +/** + * @name: FSdioWaitClkReady + * @msg: Wait clock ready after modify clock setting + * @return {FError} FSDIO_SUCCESS if wait success, FSDIO_ERR_TIMEOUT if wait timeout + * @param {uintptr} base_addr, base address of SDIO controller + * @param {int} retries, retry times in waiting + */ +static FError FSdioWaitClkReady(uintptr base_addr, int retries) +{ + FASSERT(retries > 1); + u32 reg_val = 0; + + do + { + reg_val = FSDIO_READ_REG(base_addr, FSDIO_GPIO_OFFSET); + } + while (!(reg_val & FSDIO_CLK_READY) && (retries-- > 0)); + + if (!(reg_val & FSDIO_CLK_READY) && (retries <= 0)) + { + FSDIO_ERROR("wait clk ready timeout !!! status: 0x%x", + reg_val); + return FSDIO_ERR_TIMEOUT; + } + + return FSDIO_SUCCESS; +} + +/** + * @name: FSdioUpdateExternalClk + * @msg: update uhs clock value and wait clock ready + * @return {FError} + * @param {uintptr} base_addr + * @param {u32} uhs_reg_val + */ +static FError FSdioUpdateExternalClk(uintptr base_addr, u32 uhs_reg_val) +{ + u32 reg_val; + int retries = FSDIO_TIMEOUT; + FSDIO_WRITE_REG(base_addr, FSDIO_UHS_REG_EXT_OFFSET, 0U); + FSDIO_WRITE_REG(base_addr, FSDIO_UHS_REG_EXT_OFFSET, uhs_reg_val); + + do + { + reg_val = FSDIO_READ_REG(base_addr, FSDIO_GPIO_OFFSET); + if (--retries <= 0) + break; + } + while (!(reg_val & FSDIO_CLK_READY)); + + return (retries <= 0) ? FSDIO_ERR_TIMEOUT : FSDIO_SUCCESS; +} + +/** + * @name: FSdioResetCtrl + * @msg: Reset fifo/DMA in cntrl register + * @return {FError} FSDIO_SUCCESS if reset success + * @param {uintptr} base_addr, base address of SDIO controller + * @param {u32} reset_bits, bits to be reset + */ +FError FSdioResetCtrl(uintptr base_addr, u32 reset_bits) +{ + u32 reg_val; + int retries = FSDIO_TIMEOUT; + FSDIO_SET_BIT(base_addr, FSDIO_CNTRL_OFFSET, reset_bits); + + do + { + reg_val = FSDIO_READ_REG(base_addr, FSDIO_CNTRL_OFFSET); + if (--retries <= 0) + break; + } + while (reset_bits & reg_val); + + if (retries <= 0) + return FSDIO_ERR_TIMEOUT; + + return FSDIO_SUCCESS; +} + +/** + * @name: FSdioResetBusyCard + * @msg: reset controller from card busy state + * @return {FError} FSDIO_SUCCESS if reset success + * @param {uintptr} base_addr, base address of controller + */ +FError FSdioResetBusyCard(uintptr base_addr) +{ + u32 reg_val; + int retries = FSDIO_TIMEOUT; + FSDIO_SET_BIT(base_addr, FSDIO_CNTRL_OFFSET, FSDIO_CNTRL_CONTROLLER_RESET); + + do + { + FSDIO_SET_BIT(base_addr, FSDIO_CNTRL_OFFSET, FSDIO_CNTRL_CONTROLLER_RESET); + reg_val = FSDIO_READ_REG(base_addr, FSDIO_STATUS_OFFSET); + if (--retries <= 0) + break; + } + while (reg_val & FSDIO_STATUS_DATA_BUSY); + + return (retries <= 0) ? FSDIO_ERR_BUSY : FSDIO_SUCCESS; +} + +/** + * @name: FSdioRestartClk + * @msg: restart controller clock from error status + * @return {FError} FSDIO_SUCCESS if reset success + * @param {uintptr} base_addr, base address of controller + */ +FError FSdioRestartClk(uintptr base_addr) +{ + u32 clk_div, uhs; + int retries = FSDIO_TIMEOUT; + u32 reg_val; + FError ret = FSDIO_SUCCESS; + + /* wait command finish if previous command is in error state */ + do + { + reg_val = FSDIO_READ_REG(base_addr, FSDIO_CMD_OFFSET); + if (--retries <= 0) + break; + } + while (reg_val & FSDIO_CMD_START); + + if (retries <= 0) + return FSDIO_ERR_TIMEOUT; + + /* update clock */ + FSdioSetClock(base_addr, FALSE); + + clk_div = FSDIO_READ_REG(base_addr, FSDIO_CLKDIV_OFFSET); + uhs = FSDIO_READ_REG(base_addr, FSDIO_UHS_REG_EXT_OFFSET); + + ret = FSdioUpdateExternalClk(base_addr, uhs); + if (FSDIO_SUCCESS != ret) + return ret; + + FSDIO_WRITE_REG(base_addr, FSDIO_CLKDIV_OFFSET, clk_div); + + FSdioSetClock(base_addr, TRUE); + + ret = FSdioSendPrivateCmd(base_addr, FSDIO_CMD_UPD_CLK, 0U); + + return ret; +} + +/** + * @name: FSdioReset + * @msg: Reset SDIO controller instance + * @return {FError} FSDIO_SUCCESS if reset success + * @param {FSdio} *instance_p, SDIO controller instance + */ +static FError FSdioReset(FSdio *const instance_p) +{ + FASSERT(instance_p); + uintptr base_addr = instance_p->config.base_addr; + u32 reg_val; + FError ret = FSDIO_SUCCESS; + + /* set creg_nand_mmcsd DMA path */ + FSDIO_INFO("Prev LSD CFG: 0x%x", FtIn32(FLSD_CONFIG_BASE + FLSD_NAND_MMCSD_HADDR)); + FtOut32(FLSD_CONFIG_BASE + FLSD_NAND_MMCSD_HADDR, 0x0U); + FSDIO_INFO("Curr LSD CFG: 0x%x", FtIn32(FLSD_CONFIG_BASE + FLSD_NAND_MMCSD_HADDR)); + + /* set fifo */ + reg_val = FSDIO_FIFOTH(FSDIO_FIFOTH_DMA_TRANS_8, FSDIO_RX_WMARK, FSDIO_TX_WMARK); + FSDIO_WRITE_REG(base_addr, FSDIO_FIFOTH_OFFSET, reg_val); + + /* set card threshold */ + reg_val = FSDIO_CARD_THRCTL_THRESHOLD(FSDIO_FIFO_DEPTH_8) | FSDIO_CARD_THRCTL_CARDRD; + FSDIO_WRITE_REG(base_addr, FSDIO_CARD_THRCTL_OFFSET, reg_val); + + /* disable clock and update ext clk */ + FSdioSetClock(base_addr, FALSE); + + /* set 1st clock */ + reg_val = FSDIO_UHS_REG(0U, 0U, 0x5U) | FSDIO_UHS_EXT_CLK_ENA; + FASSERT_MSG(0x502 == reg_val, "invalid uhs config"); + ret = FSdioUpdateExternalClk(base_addr, reg_val); + if (FSDIO_SUCCESS != ret) + { + FSDIO_ERROR("update extern clock failed !!!"); + return ret; + } + + /* power on */ + FSdioSetPower(base_addr, TRUE); + FSdioSetClock(base_addr, TRUE); + FSdioSetExtClock(base_addr, TRUE); + + /* set voltage as 3.3v */ + if (FSDIO_SD_1_8V_VOLTAGE == instance_p->config.voltage) + FSdioSetVoltage1_8V(base_addr, TRUE); + else + FSdioSetVoltage1_8V(base_addr, FALSE); + + /* reset controller and card */ + ret = FSdioResetCtrl(base_addr, FSDIO_CNTRL_FIFO_RESET | FSDIO_CNTRL_DMA_RESET); + if (FSDIO_SUCCESS != ret) + { + FSDIO_ERROR("reset controller failed !!!"); + return ret; + } + + /* send private command to update clock */ + ret = FSdioSendPrivateCmd(base_addr, FSDIO_CMD_UPD_CLK, 0U); + if (FSDIO_SUCCESS != ret) + { + FSDIO_ERROR("update clock failed !!!"); + return ret; + } + + /* reset card for no-removeable media, e.g. eMMC */ + if (TRUE == instance_p->config.non_removable) + FSDIO_SET_BIT(base_addr, FSDIO_CARD_RESET_OFFSET, FSDIO_CARD_RESET_ENABLE); + else + FSDIO_CLR_BIT(base_addr, FSDIO_CARD_RESET_OFFSET, FSDIO_CARD_RESET_ENABLE); + + /* clear interrupt status */ + FSDIO_WRITE_REG(base_addr, FSDIO_INT_MASK_OFFSET, 0U); + reg_val = FSDIO_READ_REG(base_addr, FSDIO_RAW_INTS_OFFSET); + FSDIO_WRITE_REG(base_addr, FSDIO_RAW_INTS_OFFSET, reg_val); + + FSDIO_WRITE_REG(base_addr, FSDIO_DMAC_INT_EN_OFFSET, 0U); + reg_val = FSDIO_READ_REG(base_addr, FSDIO_DMAC_STATUS_OFFSET); + FSDIO_WRITE_REG(base_addr, FSDIO_DMAC_STATUS_OFFSET, reg_val); + + /* enable card detect interrupt */ + if (FALSE == instance_p->config.non_removable) + FSDIO_SET_BIT(base_addr, FSDIO_INT_MASK_OFFSET, FSDIO_INT_CD_BIT); + + /* enable controller and internal DMA */ + FSDIO_SET_BIT(base_addr, FSDIO_CNTRL_OFFSET, FSDIO_CNTRL_INT_ENABLE | FSDIO_CNTRL_USE_INTERNAL_DMAC); + + /* set data and resp timeout */ + FSDIO_WRITE_REG(base_addr, FSDIO_TMOUT_OFFSET, + FSDIO_TIMEOUT_DATA(FSDIO_MAX_DATA_TIMEOUT, FSDIO_MAX_RESP_TIMEOUT)); + + /* reset descriptors and dma */ + FSdioSetDescriptor(base_addr, (uintptr)NULL); /* set decriptor list as NULL */ + FSdioResetIDMA(base_addr); + + FSDIO_INFO("init hardware done !!!"); + return ret; +} + +/** + * @name: FSdioRestart + * @msg: reset controller from error state + * @return {FError} FSDIO_SUCCESS if restart success + * @param {FSdio} *instance_p, instance of controller + */ +FError FSdioRestart(FSdio *const instance_p) +{ + FASSERT(instance_p); + uintptr base_addr = instance_p->config.base_addr; + u32 reg_val; + FError ret = FSDIO_SUCCESS; + + if (FT_COMPONENT_IS_READY != instance_p->is_ready) + { + FSDIO_ERROR("device is not yet initialized!!!"); + return FSDIO_ERR_NOT_INIT; + } + + /* reset controller */ + ret = FSdioResetCtrl(base_addr, FSDIO_CNTRL_FIFO_RESET); + if (FSDIO_SUCCESS != ret) + return ret; + + /* reset controller if in busy state */ + ret = FSdioResetBusyCard(base_addr); + if (FSDIO_SUCCESS != ret) + return ret; + + /* reset clock */ + ret = FSdioRestartClk(base_addr); + if (FSDIO_SUCCESS != ret) + return ret; + + /* reset internal DMA */ + FSdioResetIDMA(base_addr); + + return ret; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio.h b/bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio.h new file mode 100644 index 0000000000..6d1f0ba4b9 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio.h @@ -0,0 +1,227 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsdio.h + * Date: 2022-05-26 16:20:52 + * LastEditTime: 2022-05-26 16:20:53 + * Description:  This files is for sdio user interface definition + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2021/12/2 init + * 1.1 zhugengyu 2022/6/6 modify according to tech manual. + * 1.2 zhugengyu 2022/7/15 adopt to e2000 + */ + +#ifndef DRIVERS_FSDIO_H +#define DRIVERS_FSDIO_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ + +#include "ftypes.h" +#include "ferror_code.h" +#include "fkernel.h" + +/************************** Constant Definitions *****************************/ + +/* SDIO driver error code */ +#define FSDIO_SUCCESS FT_SUCCESS +#define FSDIO_ERR_TIMEOUT FT_MAKE_ERRCODE(ErrModBsp, ErrBspMmc, 1) +#define FSDIO_ERR_NOT_INIT FT_MAKE_ERRCODE(ErrModBsp, ErrBspMmc, 2) +#define FSDIO_ERR_SHORT_BUF FT_MAKE_ERRCODE(ErrModBsp, ErrBspMmc, 3) +#define FSDIO_ERR_NOT_SUPPORT FT_MAKE_ERRCODE(ErrModBsp, ErrBspMmc, 4) +#define FSDIO_ERR_INVALID_STATE FT_MAKE_ERRCODE(ErrModBsp, ErrBspMmc, 5) +#define FSDIO_ERR_TRANS_TIMEOUT FT_MAKE_ERRCODE(ErrModBsp, ErrBspMmc, 6) +#define FSDIO_ERR_CMD_TIMEOUT FT_MAKE_ERRCODE(ErrModBsp, ErrBspMmc, 7) +#define FSDIO_ERR_NO_CARD FT_MAKE_ERRCODE(ErrModBsp, ErrBspMmc, 8) +#define FSDIO_ERR_BUSY FT_MAKE_ERRCODE(ErrModBsp, ErrBspMmc, 9) + +typedef enum +{ + FSDIO_IDMA_TRANS_MODE, /* DMA trans mode */ + FSDIO_PIO_TRANS_MODE, /* NO-DMA trans by read/write Fifo */ +} FSdioTransMode; /* SDIO trans mode */ + +typedef enum +{ + FSDIO_GENERAL_INTR, /* interrupt status belongs to controller */ + FSDIO_IDMA_INTR, /* interrupt status belongs to DMA */ +} FSdioIntrType; /* SDIO interrupt status type */ + +typedef enum +{ + FSDIO_SD_3_3V_VOLTAGE = 0, /* 3.3v */ + FSDIO_SD_1_8V_VOLTAGE /* 1.8v */ +} FSdioVoltageType; /* SDIO card voltage type */ + +typedef enum +{ + FSDIO_EVT_CARD_DETECTED = 0, /* card detected event */ + FSDIO_EVT_CMD_DONE, /* cmd transfer finish event */ + FSDIO_EVT_DATA_DONE, /* cmd with data transfer finish event */ + FSDIO_EVT_ERR_OCCURE, /* error occurred in transfer */ + + FSDIO_NUM_OF_EVT +} FSdioEvtType; /* SDIO event type */ + +#define FSDIO_DEFAULT_BLOCK_SZ 512U +#define FSDIO_SD_400KHZ 400000U +#define FSDIO_SD_25_MHZ 25000000U +#define FSDIO_SD_50_MHZ 50000000U +/**************************** Type Definitions *******************************/ +typedef struct _FSdio FSdio; + +typedef void (*FSdioRelaxHandler)(void); +typedef void (*FSdioEvtHandler)(FSdio *const instance_p, void *args); + +typedef struct +{ + u32 attribute; /* ds0 */ +#define FSDIO_IDMAC_DES0_DIC BIT(1)/* 内部描述表不触发TI/RI中断 */ +#define FSDIO_IDMAC_DES0_LD BIT(2)/* 数据的最后一个描述符 */ +#define FSDIO_IDMAC_DES0_FD BIT(3)/* 数据的第一个描述符 */ +#define FSDIO_IDMAC_DES0_CH BIT(4)/* 链接下一个描述符地址 */ +#define FSDIO_IDMAC_DES0_ER BIT(5)/* 链表已经到达最后一个链表 */ +#define FSDIO_IDMAC_DES0_CES BIT(30)/* RINTSTS寄存器错误汇总 */ +#define FSDIO_IDMAC_DES0_OWN BIT(31)/* 描述符关联DMA,完成传输后该位置置0 */ + u32 non1; /* ds1 --> unused */ + u32 len; /* ds2 bit[25:13] buffer2 size,bit[12:0] buffer1 size*/ +#define FSDIO_IDMAC_DES2_BUF1_MASK GENMASK(12, 0) +#define FSDIO_IDMAC_DES2_BUF1_SIZE(x) (FSDIO_IDMAC_DES2_BUF1_MASK & (x)) +#define FSDIO_IDMAC_DES2_BUF2_MASK GENMASK(25, 13) +#define FSDIO_IDMAC_DES2_BUF2_SIZE(x) (FSDIO_IDMAC_DES2_BUF2_MASK & (x << 13)) + u32 non2; /* ds3 --> unused */ + u32 addr_lo; /* ds4 Lower 32-bits of Buffer Address Pointer 1 --> buffer 1 */ + u32 addr_hi; /* ds5 Upper 32-bits of Buffer Address Pointer 1 */ + u32 desc_lo; /* ds6 Lower 32-bits of Next Descriptor Address --> buffer 2 */ + u32 desc_hi; /* ds7 Upper 32-bits of Next Descriptor Address */ +} __attribute__((packed)) __attribute((aligned(4))) FSdioIDmaDesc; /* SDIO DMA descriptr */ + +typedef struct +{ + volatile FSdioIDmaDesc *first_desc; /* first descriptor in the list */ + u32 desc_num; /* num of descriptors in the list */ +} FSdioIDmaDescList; /* SDIO DMA descriptors list */ + +typedef struct +{ + u8 *buf; /* trans buffer */ + u32 blksz; /* card block size */ + u32 blkcnt; /* num of block in trans */ + u32 datalen; /* bytes in trans */ +} FSdioData; /* SDIO trans data */ + +typedef struct +{ + u32 cmdidx; /* command index */ + u32 cmdarg; /* command argument */ + u32 response[4]; /* command response buffer */ + u32 flag; /* command flags */ +#define FSDIO_CMD_FLAG_NEED_INIT BIT(1) /* need initialization */ +#define FSDIO_CMD_FLAG_EXP_RESP BIT(2) /* need reply */ +#define FSDIO_CMD_FLAG_EXP_LONG_RESP BIT(3) /* need 136 bits long reply */ +#define FSDIO_CMD_FLAG_NEED_RESP_CRC BIT(4) /* need CRC */ +#define FSDIO_CMD_FLAG_EXP_DATA BIT(5) /* need trans data */ +#define FSDIO_CMD_FLAG_WRITE_DATA BIT(6) /* need trans data to write card */ +#define FSDIO_CMD_FLAG_READ_DATA BIT(7) /* need trans data to read card */ +#define FSDIO_CMD_FLAG_NEED_AUTO_STOP BIT(8) /* need auto stop after command */ +#define FSDIO_CMD_FLAG_ADTC BIT(9) /* need ADTC */ +#define FSDIO_CMD_FLAG_SWITCH_VOLTAGE BIT(10) /* need switch voltage */ + FSdioData *data_p; /* SDIO trans data */ + volatile boolean success; /* TRUE: comand and data transfer success */ +} FSdioCmdData; /* SDIO trans command and data */ + +typedef struct +{ + u32 instance_id; /* Device instance id */ + uintptr base_addr; /* Device base address */ + u32 irq_num; /* Interrupt num */ + FSdioTransMode trans_mode; /* Trans mode, PIO/DMA */ + FSdioVoltageType voltage; /* Card voltage type */ + boolean non_removable; /* No removeable media, e.g eMMC */ +} FSdioConfig; /* SDIO intance configuration */ + +typedef struct _FSdio +{ + FSdioConfig config; /* Current active configs */ + u32 is_ready; /* Device is initialized and ready */ + FSdioIDmaDescList desc_list; /* DMA descriptor list, valid in DMA trans mode */ + FSdioEvtHandler evt_handlers[FSDIO_NUM_OF_EVT]; /* call-backs for interrupt event */ + void *evt_args[FSDIO_NUM_OF_EVT]; /* arguments for event call-backs */ +} FSdio; /* SDIO intance */ +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/* Get the device instance default configure */ +const FSdioConfig *FSdioLookupConfig(u32 instance_id); + +/* initialization SDIO controller instance */ +FError FSdioCfgInitialize(FSdio *const instance_p, const FSdioConfig *cofig_p); + +/* deinitialization SDIO controller instance */ +void FSdioDeInitialize(FSdio *const instance_p); + +/* Setup DMA descriptor for SDIO controller instance */ +FError FSdioSetIDMAList(FSdio *const instance_p, volatile FSdioIDmaDesc *desc, u32 desc_num); + +/* Set the Card clock freqency */ +FError FSdioSetClkFreq(FSdio *const instance_p, u32 input_clk_hz); + +/* Start command and data transfer in DMA mode */ +FError FSdioDMATransfer(FSdio *const instance_p, FSdioCmdData *const cmd_data_p); + +/* Wait DMA transfer finished by poll */ +FError FSdioPollWaitDMAEnd(FSdio *const instance_p, FSdioCmdData *const cmd_data_p, FSdioRelaxHandler relax); + +/* Start command and data transfer in PIO mode */ +FError FSdioPIOTransfer(FSdio *const instance_p, FSdioCmdData *const cmd_data_p); + +/* Wait PIO transfer finished by poll */ +FError FSdioPollWaitPIOEnd(FSdio *const instance_p, FSdioCmdData *const cmd_data_p, FSdioRelaxHandler relax); + +/* Get cmd response and received data after wait poll status or interrupt signal */ +FError FSdioGetCmdResponse(FSdio *const instance_p, FSdioCmdData *const cmd_data_p); + +/* Get SDIO controller interrupt mask */ +u32 FSdioGetInterruptMask(FSdio *const instance_p, FSdioIntrType intr_type); + +/* Enable/Disable SDIO controller interrupt */ +void FSdioSetInterruptMask(FSdio *const instance_p, FSdioIntrType type, u32 set_mask, boolean enable); + +/* Interrupt handler for SDIO instance */ +void FSdioInterruptHandler(s32 vector, void *param); + +/* Reset controller from error state */ +FError FSdioRestart(FSdio *const instance_p); + +/* Register event call-back function as handler for interrupt events */ +void FSdioRegisterEvtHandler(FSdio *const instance_p, FSdioEvtType evt, FSdioEvtHandler handler, void *handler_arg); + +/* Dump all register value of SDIO instance */ +void FSdioDumpRegister(uintptr base_addr); + +#ifdef __cplusplus +} +#endif + + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio_cmd.c b/bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio_cmd.c new file mode 100644 index 0000000000..0bcd30d960 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio_cmd.c @@ -0,0 +1,210 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsdio_cmd.c + * Date: 2022-06-01 14:23:59 + * LastEditTime: 2022-06-01 14:24:00 + * Description:  This files is for SDIO command related function + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.1 zhugengyu 2022/6/6 modify according to tech manual. + */ +/***************************** Include Files *********************************/ + +#include "fio.h" +#include "fdebug.h" +#include "fassert.h" +#include "ftypes.h" + +#include "fcache.h" + +#include "fsdio_hw.h" +#include "fsdio.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FSDIO_DEBUG_TAG "FSDIO-CMD" +#define FSDIO_ERROR(format, ...) FT_DEBUG_PRINT_E(FSDIO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSDIO_WARN(format, ...) FT_DEBUG_PRINT_W(FSDIO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSDIO_INFO(format, ...) FT_DEBUG_PRINT_I(FSDIO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSDIO_DEBUG(format, ...) FT_DEBUG_PRINT_D(FSDIO_DEBUG_TAG, format, ##__VA_ARGS__) + +/************************** Function Prototypes ******************************/ +extern FError FSdioPIOReadData(FSdio *const instance_p, FSdioData *data_p); + +/*****************************************************************************/ +FError FSdioSendPrivateCmd(uintptr base_addr, u32 cmd, u32 arg) +{ + u32 reg_val; + int retries = FSDIO_TIMEOUT; + + do + { + reg_val = FSDIO_READ_REG(base_addr, FSDIO_STATUS_OFFSET); + if (--retries <= 0) + break; + } + while (FSDIO_STATUS_DATA_BUSY & reg_val); + + if (retries <= 0) + return FSDIO_ERR_BUSY; + + FSDIO_WRITE_REG(base_addr, FSDIO_CMD_ARG_OFFSET, arg); + FSDIO_WRITE_REG(base_addr, FSDIO_CMD_OFFSET, FSDIO_CMD_START | cmd); + + retries = FSDIO_TIMEOUT; + do + { + reg_val = FSDIO_READ_REG(base_addr, FSDIO_CMD_OFFSET); + if (--retries <= 0) + break; + } + while (FSDIO_CMD_START & reg_val); + + return (retries <= 0) ? FSDIO_ERR_TIMEOUT : FSDIO_SUCCESS; +} + +/** + * @name: FSdioTransferCmd + * @msg: pack and transfer command + * @return {FError} FSDIO_SUCCESS if transfer success + * @param {FSdio} *instance_p, SDIO controller instance + * @param {FSdioCmdData} *cmd_data_p, contents of transfer command and data + */ +FError FSdioTransferCmd(FSdio *const instance_p, FSdioCmdData *const cmd_data_p) +{ + FASSERT(cmd_data_p); + FError ret = FSDIO_SUCCESS; + uintptr base_addr = instance_p->config.base_addr; + u32 cmd_flag = cmd_data_p->flag; + u32 raw_cmd = FSDIO_CMD_USE_HOLD_REG; /* USE_HOLD_REG必须为1 */ + + /* 命令需要进行卡初始化,如CMD-0 */ + if (FSDIO_CMD_FLAG_NEED_INIT & cmd_flag) + { + raw_cmd |= FSDIO_CMD_INIT; + } + + /* 命令涉及电压切换 */ + if (FSDIO_CMD_FLAG_SWITCH_VOLTAGE & cmd_flag) + { + raw_cmd |= FSDIO_CMD_VOLT_SWITCH; + } + + /* 命令传输过程伴随数据传输 */ + if (FSDIO_CMD_FLAG_EXP_DATA & cmd_flag) + { + raw_cmd |= FSDIO_CMD_DAT_EXP; + + if (FSDIO_CMD_FLAG_WRITE_DATA & cmd_flag) /* 写卡 */ + { + raw_cmd |= FSDIO_CMD_DAT_WRITE; + } + } + + /* 命令需要进行CRC校验 */ + if (FSDIO_CMD_FLAG_NEED_RESP_CRC & cmd_flag) + { + raw_cmd |= FSDIO_CMD_RESP_CRC; + } + + /* 命令需要响应回复 */ + if (FSDIO_CMD_FLAG_EXP_RESP & cmd_flag) + { + raw_cmd |= FSDIO_CMD_RESP_EXP; + + if (FSDIO_CMD_FLAG_EXP_LONG_RESP & cmd_flag) /* 命令需要136字节长回复 */ + { + raw_cmd |= FSDIO_CMD_RESP_LONG; + } + } + + raw_cmd |= FSDIO_CMD_INDX_SET(cmd_data_p->cmdidx); + + FSDIO_DEBUG("============[CMD-%d]@0x%x begin ============", cmd_data_p->cmdidx, base_addr); + FSDIO_DEBUG(" cmd: 0x%x", raw_cmd); + FSDIO_DEBUG(" arg: 0x%x", cmd_data_p->cmdarg); + + /* enable related interrupt */ + FSdioSetInterruptMask(instance_p, FSDIO_GENERAL_INTR, + FSDIO_INTS_CMD_MASK, TRUE); + + FSdioSendPrivateCmd(base_addr, raw_cmd, cmd_data_p->cmdarg); + FSDIO_INFO("cmd send done ..."); + return ret; +} + +/** + * @name: FSdioGetCmdResponse + * @msg: Get cmd response and received data after wait poll status or interrupt signal + * @return {FError} FSDIO_SUCCESS if get success + * @param {FSdio} *instance_p, SDIO controller instance + * @param {FSdioCmdData} *cmd_data_p, contents of transfer command and data + */ +FError FSdioGetCmdResponse(FSdio *const instance_p, FSdioCmdData *const cmd_data_p) +{ + FASSERT(instance_p); + FASSERT(cmd_data_p); + FError ret = FSDIO_SUCCESS; + u32 reg_val; + const boolean read = cmd_data_p->flag & FSDIO_CMD_FLAG_READ_DATA; + uintptr base_addr = instance_p->config.base_addr; + + if (FT_COMPONENT_IS_READY != instance_p->is_ready) + { + FSDIO_ERROR("device is not yet initialized!!!"); + return FSDIO_ERR_NOT_INIT; + } + + if ((NULL != cmd_data_p->data_p) && (read)) + { + if (FSDIO_PIO_TRANS_MODE == instance_p->config.trans_mode) + { + ret = FSdioPIOReadData(instance_p, cmd_data_p->data_p); + } + } + + /* check response of cmd */ + if (FSDIO_CMD_FLAG_EXP_RESP & cmd_data_p->flag) + { + if (FSDIO_CMD_FLAG_EXP_LONG_RESP & cmd_data_p->flag) + { + cmd_data_p->response[0] = FSDIO_READ_REG(base_addr, FSDIO_RESP0_OFFSET); + cmd_data_p->response[1] = FSDIO_READ_REG(base_addr, FSDIO_RESP1_OFFSET); + cmd_data_p->response[2] = FSDIO_READ_REG(base_addr, FSDIO_RESP2_OFFSET); + cmd_data_p->response[3] = FSDIO_READ_REG(base_addr, FSDIO_RESP3_OFFSET); + FSDIO_DEBUG(" resp: 0x%x-0x%x-0x%x-0x%x", + cmd_data_p->response[0], cmd_data_p->response[1], + cmd_data_p->response[2], cmd_data_p->response[3]); + } + else + { + cmd_data_p->response[0] = FSDIO_READ_REG(base_addr, FSDIO_RESP0_OFFSET); + FSDIO_DEBUG(" resp: 0x%x", cmd_data_p->response[0]); + } + } + + cmd_data_p->success = TRUE; /* cmd / data transfer finished successful */ + FSDIO_DEBUG("============[CMD-%d]@0x%x end ============", cmd_data_p->cmdidx, base_addr); + + /* disable related interrupt */ + FSdioSetInterruptMask(instance_p, FSDIO_GENERAL_INTR, FSDIO_INTS_CMD_MASK | FSDIO_INTS_DATA_MASK, FALSE); + FSdioSetInterruptMask(instance_p, FSDIO_IDMA_INTR, FSDIO_DMAC_INTS_MASK, FALSE); + + return ret; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio_dma.c b/bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio_dma.c new file mode 100644 index 0000000000..9fc1507d39 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio_dma.c @@ -0,0 +1,365 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsdio_dma.c + * Date: 2022-06-01 14:21:41 + * LastEditTime: 2022-06-01 14:21:42 + * Description:  This files is for DMA related function implementation + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.1 zhugengyu 2022/6/6 modify according to tech manual. + */ +/***************************** Include Files *********************************/ +#include + +#include "fio.h" +#include "fdebug.h" +#include "fassert.h" +#include "ftypes.h" + +#include "fcache.h" + +#include "fsdio_hw.h" +#include "fsdio.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FSDIO_DEBUG_TAG "FSDIO-DMA" +#define FSDIO_ERROR(format, ...) FT_DEBUG_PRINT_E(FSDIO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSDIO_WARN(format, ...) FT_DEBUG_PRINT_W(FSDIO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSDIO_INFO(format, ...) FT_DEBUG_PRINT_I(FSDIO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSDIO_DEBUG(format, ...) FT_DEBUG_PRINT_D(FSDIO_DEBUG_TAG, format, ##__VA_ARGS__) + +/************************** Function Prototypes ******************************/ +extern FError FSdioTransferCmd(FSdio *const instance_p, FSdioCmdData *const cmd_data_p); + +/*****************************************************************************/ +/** + * @name: FSdioDumpDMADescriptor + * @msg: dump DMA descriptor list + * @return {*} + * @param {FSdio} *instance_p, instance of controller + * @param {u32} desc_in_use, max index of descriptor in use + */ +static void FSdioDumpDMADescriptor(FSdio *const instance_p, u32 desc_in_use) +{ + u32 loop; + volatile FSdioIDmaDesc *cur_desc = NULL; + + if (instance_p->desc_list.first_desc) + { + for (loop = 0; loop < desc_in_use; loop++) + { + cur_desc = &(instance_p->desc_list.first_desc[loop]); + FSDIO_DEBUG("descriptor@%p", cur_desc); + FSDIO_DEBUG("\tattribute: 0x%x", cur_desc->attribute); + FSDIO_DEBUG("\tnon1: 0x%x", cur_desc->non1); + FSDIO_DEBUG("\tlen: 0x%x", cur_desc->len); + FSDIO_DEBUG("\tnon2: 0x%x", cur_desc->non2); + FSDIO_DEBUG("\taddr_lo: 0x%x", cur_desc->addr_lo); + FSDIO_DEBUG("\taddr_hi: 0x%x", cur_desc->addr_hi); + FSDIO_DEBUG("\tdesc_lo: 0x%x", cur_desc->desc_lo); + FSDIO_DEBUG("\tdesc_hi: 0x%x", cur_desc->desc_hi); + } + } +} + +/** + * @name: FSdioSetupDMADescriptor + * @msg: setup DMA descriptor list before do transcation + * @return {FError} FSDIO_SUCCESS if setup success + * @param {FSdio} *instance_p, instance of controller + * @param {FSdioData} *data_p, data in transcation + */ +static FError FSdioSetupDMADescriptor(FSdio *const instance_p, FSdioData *data_p) +{ + FASSERT(data_p); + FASSERT(instance_p->desc_list.first_desc); + u32 loop; + u32 buf_num = data_p->datalen / data_p->blksz + + ((data_p->datalen % data_p->blksz) ? 1U : 0U); + volatile FSdioIDmaDesc *cur_desc = NULL; + uintptr buff_addr = 0U; + uintptr desc_addr = 0U; + boolean is_first = TRUE; + boolean is_last = FALSE; + + if (buf_num > instance_p->desc_list.desc_num) + { + FSDIO_ERROR("descriptor is short for transfer %d < %d", + instance_p->desc_list.desc_num, buf_num); + return FSDIO_ERR_SHORT_BUF; + } + + memset((void *)instance_p->desc_list.first_desc, 0, + sizeof(FSdioIDmaDesc) * instance_p->desc_list.desc_num); + + FSDIO_INFO("%d of descriptor in use", buf_num); + for (loop = 0U; loop < buf_num; loop++) + { + cur_desc = &(instance_p->desc_list.first_desc[loop]); + is_first = (0U == loop) ? TRUE : FALSE; + is_last = ((buf_num - 1U) == loop) ? TRUE : FALSE; + + /* set properity of descriptor entry */ + cur_desc->attribute = FSDIO_IDMAC_DES0_CH | FSDIO_IDMAC_DES0_OWN; /* descriptor list in chain, and set OWN bit */ + cur_desc->attribute |= (is_first) ? FSDIO_IDMAC_DES0_FD : 0; /* is it the first entry ? */ + cur_desc->attribute |= (is_last) ? (FSDIO_IDMAC_DES0_LD | FSDIO_IDMAC_DES0_ER) : 0; /* is it the last entry ? */ + + /* set data length in transfer */ + cur_desc->non1 = 0U; + cur_desc->len = FSDIO_IDMAC_DES2_BUF1_SIZE(data_p->blksz) | FSDIO_IDMAC_DES2_BUF2_SIZE(0U); + + /* set data buffer for transfer */ + buff_addr = (uintptr)data_p->buf + (uintptr)(loop * data_p->blksz); +#ifdef __aarch64__ + cur_desc->addr_hi = UPPER_32_BITS(buff_addr); + cur_desc->addr_lo = LOWER_32_BITS(buff_addr); +#else + cur_desc->addr_hi = 0U; + cur_desc->addr_lo = (u32)(buff_addr); +#endif + + /* set address of next descriptor entry, NULL for last entry */ + desc_addr = is_last ? 0U : (uintptr)&instance_p->desc_list.first_desc[loop + 1]; +#ifdef __aarch64__ + cur_desc->desc_hi = UPPER_32_BITS(desc_addr); + cur_desc->desc_lo = LOWER_32_BITS(desc_addr); +#else + cur_desc->desc_hi = 0U; + cur_desc->desc_lo = (u32)(desc_addr); +#endif + } + + /* flush cache of descripor list and transfer buffer */ + FCacheDCacheFlushRange((uintptr)instance_p->desc_list.first_desc, sizeof(FSdioIDmaDesc) * instance_p->desc_list.desc_num); + FCacheDCacheFlushRange((uintptr)data_p->buf, data_p->datalen); + + FSdioDumpDMADescriptor(instance_p, buf_num); + return FSDIO_SUCCESS; +} + +/** + * @name: FSdioDMATransferData + * @msg: + * @return {*} + * @param {FSdio} *instance_p + * @param {FSdioData} *data_p + */ +static FError FSdioDMATransferData(FSdio *const instance_p, FSdioData *data_p) +{ + FASSERT(data_p); + FError ret = FSDIO_SUCCESS; + uintptr base_addr = instance_p->config.base_addr; + + /* enable related interrupt */ + FSdioSetInterruptMask(instance_p, FSDIO_GENERAL_INTR, FSDIO_INTS_DATA_MASK, TRUE); + FSdioSetInterruptMask(instance_p, FSDIO_IDMA_INTR, FSDIO_DMAC_INTS_MASK, TRUE); + + /* fill transfer buffer to DMA descriptor */ + ret = FSdioSetupDMADescriptor(instance_p, data_p); + if (FSDIO_SUCCESS != ret) + return ret; + + FSDIO_INFO("descriptor@%p, trans bytes: %d, block size: %d", + instance_p->desc_list.first_desc, + data_p->datalen, + data_p->blksz); + + /* set transfer info to register */ + FSdioSetDescriptor(base_addr, (uintptr)(instance_p->desc_list.first_desc)); + FSdioSetTransBytes(base_addr, data_p->datalen); + FSdioSetBlockSize(base_addr, data_p->blksz); + + return ret; +} + +/** + * @name: FSdioDMATransfer + * @msg: Start command and data transfer in DMA mode + * @return {FError} FSDIO_SUCCESS if transfer success, otherwise failed + * @param {FSdio} *instance_p, SDIO controller instance + * @param {FSdioCmdData} *cmd_data_p, contents of transfer command and data + */ +FError FSdioDMATransfer(FSdio *const instance_p, FSdioCmdData *const cmd_data_p) +{ + FASSERT(instance_p); + FASSERT(cmd_data_p); + FError ret = FSDIO_SUCCESS; + uintptr base_addr = instance_p->config.base_addr; + + cmd_data_p->success = FALSE; /* reset cmd transfer status */ + + if (FT_COMPONENT_IS_READY != instance_p->is_ready) + { + FSDIO_ERROR("device is not yet initialized!!!"); + return FSDIO_ERR_NOT_INIT; + } + + if (FSDIO_IDMA_TRANS_MODE != instance_p->config.trans_mode) + { + FSDIO_ERROR("device is not configure in DMA transfer mode"); + return FSDIO_ERR_INVALID_STATE; + } + + /* for removable media, check if card exists */ + if ((FALSE == instance_p->config.non_removable) && + (FALSE == FSdioCheckIfCardExists(base_addr))) + { + FSDIO_ERROR("card not detected !!!"); + return FSDIO_ERR_NO_CARD; + } + + /* reset fifo and DMA before transfer */ + FSDIO_SET_BIT(base_addr, FSDIO_CNTRL_OFFSET, FSDIO_CNTRL_FIFO_RESET | FSDIO_CNTRL_DMA_RESET); + ret = FSdioResetCtrl(base_addr, FSDIO_CNTRL_FIFO_RESET | FSDIO_CNTRL_DMA_RESET); + if (FSDIO_SUCCESS != ret) + return ret; + + /* enable use of DMA */ + FSDIO_SET_BIT(base_addr, FSDIO_CNTRL_OFFSET, FSDIO_CNTRL_USE_INTERNAL_DMAC); + FSDIO_SET_BIT(base_addr, FSDIO_BUS_MODE_OFFSET, FSDIO_BUS_MODE_DE); + + if (NULL != cmd_data_p->data_p) /* transfer data */ + { + ret = FSdioDMATransferData(instance_p, + cmd_data_p->data_p); + } + + if (FSDIO_SUCCESS == ret) /* transfer command */ + { + ret = FSdioTransferCmd(instance_p, cmd_data_p); + } + + return ret; +} + +/** + * @name: FSdioPollWaitDMAEnd + * @msg: Wait DMA transfer finished by poll + * @return {FError} FSDIO_SUCCESS if wait success, otherwise wait failed + * @param {FSdio} *instance_p, SDIO controller instance + * @param {FSdioCmdData} *cmd_data_p, contents of transfer command and data + * @param {FSdioRelaxHandler} relax, handler of relax when wait busy + */ +FError FSdioPollWaitDMAEnd(FSdio *const instance_p, FSdioCmdData *const cmd_data_p, FSdioRelaxHandler relax) +{ + FASSERT(instance_p); + FASSERT(cmd_data_p); + FError ret = FSDIO_SUCCESS; + u32 reg_val; + int delay; + const boolean read = cmd_data_p->flag & FSDIO_CMD_FLAG_READ_DATA; + uintptr base_addr = instance_p->config.base_addr; + + if (FT_COMPONENT_IS_READY != instance_p->is_ready) + { + FSDIO_ERROR("device is not yet initialized!!!"); + return FSDIO_ERR_NOT_INIT; + } + + if (FSDIO_IDMA_TRANS_MODE != instance_p->config.trans_mode) + { + FSDIO_ERROR("device is not configure in DMA transfer mode"); + return FSDIO_ERR_INVALID_STATE; + } + + /* wait command done or timeout */ + delay = FSDIO_TIMEOUT; + do + { + reg_val = FSdioGetRawStatus(base_addr); + if (relax) + relax(); + } + while (!(FSDIO_INT_CMD_BIT & reg_val) && (--delay > 0)); + + if (!(FSDIO_INT_CMD_BIT & reg_val) && (delay <= 0)) + { + FSDIO_ERROR("wait cmd done timeout, raw ints: 0x%x", reg_val); + return FSDIO_ERR_CMD_TIMEOUT; + } + + if (NULL != cmd_data_p->data_p) /* wait data transfer done or timeout */ + { + delay = FSDIO_TIMEOUT; + do + { + reg_val = FSDIO_READ_REG(base_addr, FSDIO_RAW_INTS_OFFSET); + if (relax) + relax(); + } + while (!(FSDIO_INT_DTO_BIT & reg_val) && (--delay > 0)); + + /* clear status to ack data done */ + FSdioClearRawStatus(base_addr); + + if (!(FSDIO_INT_DTO_BIT & reg_val) && (delay <= 0)) + { + FSDIO_ERROR("wait DMA transfer timeout, raw ints: 0x%x", reg_val); + return FSDIO_ERR_TRANS_TIMEOUT; + } + + /* invalidate cache of transfer buffer */ + if (read) + { + FCacheDCacheInvalidateRange((uintptr)cmd_data_p->data_p, cmd_data_p->data_p->datalen); + } + } + + /* clear status to ack cmd done */ + FSdioClearRawStatus(base_addr); + + if (FSDIO_SUCCESS == ret) + { + ret = FSdioGetCmdResponse(instance_p, cmd_data_p); + } + + return ret; +} + +/** + * @name: FSdioSetIDMAList + * @msg: Setup DMA descriptor for SDIO controller instance + * @return {FError} FSDIO_SUCCESS if setup done, otherwise failed + * @param {FSdio} *instance_p, SDIO controller instance + * @param {volatile FSdioIDmaDesc} *desc, first item in DMA descriptor lists + * @param {u32} desc_num, number of items in DMA descriptor lists + */ +FError FSdioSetIDMAList(FSdio *const instance_p, volatile FSdioIDmaDesc *desc, u32 desc_num) +{ + FASSERT(instance_p); + FError ret = FSDIO_SUCCESS; + uintptr base_addr = instance_p->config.base_addr; + + if (FT_COMPONENT_IS_READY != instance_p->is_ready) + { + FSDIO_ERROR("device is not yet initialized!!!"); + return FSDIO_ERR_NOT_INIT; + } + + if (FSDIO_IDMA_TRANS_MODE != instance_p->config.trans_mode) + { + FSDIO_ERROR("device is not configure in DMA transfer mode"); + return FSDIO_ERR_INVALID_STATE; + } + + instance_p->desc_list.first_desc = desc; + instance_p->desc_list.desc_num = desc_num; + return ret; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio_g.c b/bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio_g.c new file mode 100644 index 0000000000..78b00cc0e1 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio_g.c @@ -0,0 +1,67 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsdio_g.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:37:44 + * Description:  This files is for static init + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2021/12/2 init + * 1.1 zhugengyu 2022/6/6 modify according to tech manual. + */ + +/***************************** Include Files *********************************/ + +#include "ftypes.h" +#include "fparameters.h" +#include "fsdio.h" +#include "fsdio_hw.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + +const FSdioConfig FSDIO_CONFIG_TBL[FSDIO_HOST_INSTANCE_NUM] = +{ + [FSDIO_HOST_INSTANCE_0] = + { + .instance_id = FSDIO_HOST_INSTANCE_0, + .base_addr = FSDIO_HOST_0_BASE_ADDR, + .irq_num = FSDIO_HOST_0_IRQ_NUM, + .trans_mode = FSDIO_IDMA_TRANS_MODE, + .voltage = FSDIO_SD_3_3V_VOLTAGE, + .non_removable = FALSE + }, + + [FSDIO_HOST_INSTANCE_1] = + { + .instance_id = FSDIO_HOST_INSTANCE_1, + .base_addr = FSDIO_HOST_1_BASE_ADDR, + .irq_num = FSDIO_HOST_1_IRQ_NUM, + .trans_mode = FSDIO_IDMA_TRANS_MODE, + .voltage = FSDIO_SD_3_3V_VOLTAGE, + .non_removable = FALSE + } +}; + + +/*****************************************************************************/ diff --git a/bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio_hw.h b/bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio_hw.h new file mode 100644 index 0000000000..a0a1a0b1b6 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio_hw.h @@ -0,0 +1,625 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsdio_hw.h + * Date: 2022-05-26 15:32:34 + * LastEditTime: 2022-05-26 15:32:35 + * Description:  This files is for SDIO register function definition + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2021/12/2 init + * 1.1 zhugengyu 2022/5/26 modify according to tech manual. + */ + +#ifndef DRIVERS_FSDIO_HW_H +#define DRIVERS_FSDIO_HW_H + +#ifdef __cplusplus +extern "C" +{ +#endif +/***************************** Include Files *********************************/ +#include "fparameters.h" +#include "fio.h" +#include "ftypes.h" +#include "fassert.h" +#include "fkernel.h" + +/************************** Constant Definitions *****************************/ + +/** @name Register Map + * + * Register offsets from the base address of an SD device. + * @{ + */ +#define FSDIO_CNTRL_OFFSET 0x00U /* the controller config reg */ +#define FSDIO_PWREN_OFFSET 0x04U /* the power enable reg */ +#define FSDIO_CLKDIV_OFFSET 0x08U /* the clock divider reg */ +#define FSDIO_CLKENA_OFFSET 0x10U /* the clock enable reg */ +#define FSDIO_TMOUT_OFFSET 0x14U /* the timeout reg */ +#define FSDIO_CTYPE_OFFSET 0x18U /* the card type reg */ +#define FSDIO_BLK_SIZ_OFFSET 0x1CU /* the block size reg */ +#define FSDIO_BYT_CNT_OFFSET 0x20U /* the byte count reg */ +#define FSDIO_INT_MASK_OFFSET 0x24U /* the interrupt mask reg */ +#define FSDIO_CMD_ARG_OFFSET 0x28U /* the command argument reg */ +#define FSDIO_CMD_OFFSET 0x2CU /* the command reg */ +#define FSDIO_RESP0_OFFSET 0x30U /* the response reg0 */ +#define FSDIO_RESP1_OFFSET 0x34U /* the response reg1 */ +#define FSDIO_RESP2_OFFSET 0x38U /* the response reg2 */ +#define FSDIO_RESP3_OFFSET 0x3CU /* the response reg3 */ +#define FSDIO_MASKED_INTS_OFFSET 0x40U /* the masked interrupt status reg */ +#define FSDIO_RAW_INTS_OFFSET 0x44U /* the raw interrupt status reg */ +#define FSDIO_STATUS_OFFSET 0x48U /* the status reg */ +#define FSDIO_FIFOTH_OFFSET 0x4CU /* the FIFO threshold watermark reg */ +#define FSDIO_CARD_DETECT_OFFSET 0x50U /* the card detect reg */ +#define FSDIO_CARD_WRTPRT_OFFSET 0x54U /* the card write protect reg */ +#define FSDIO_GPIO_OFFSET 0x58U /* the ciu ready */ +#define FSDIO_TRAN_CARD_CNT_OFFSET 0x5CU /* the transferred CIU card byte count reg */ +#define FSDIO_TRAN_FIFO_CNT_OFFSET 0x60U /* the transferred host to FIFO byte count reg */ +#define FSDIO_DEBNCE_OFFSET 0x64U /* the debounce count reg */ +#define FSDIO_UID_OFFSET 0x68U /* the user ID reg */ +#define FSDIO_VID_OFFSET 0x6CU /* the controller version ID reg */ +#define FSDIO_HWCONF_OFFSET 0x70U /* the hardware configuration reg */ +#define FSDIO_UHS_REG_OFFSET 0x74U /* the UHS-I reg */ +#define FSDIO_CARD_RESET_OFFSET 0x78U /* the card reset reg */ +#define FSDIO_BUS_MODE_OFFSET 0x80U /* the bus mode reg */ +#define FSDIO_DESC_LIST_ADDRL_OFFSET 0x88U /* the descriptor list low base address reg */ +#define FSDIO_DESC_LIST_ADDRH_OFFSET 0x8CU /* the descriptor list high base address reg */ +#define FSDIO_DMAC_STATUS_OFFSET 0x90U /* the internal DMAC status reg */ +#define FSDIO_DMAC_INT_EN_OFFSET 0x94U /* the internal DMAC interrupt enable reg */ +#define FSDIO_CUR_DESC_ADDRL_OFFSET 0x98U /* the current host descriptor low address reg */ +#define FSDIO_CUR_DESC_ADDRH_OFFSET 0x9CU /* the current host descriptor high address reg */ +#define FSDIO_CUR_BUF_ADDRL_OFFSET 0xA0U /* the current buffer low address reg */ +#define FSDIO_CUR_BUF_ADDRH_OFFSET 0xA4U /* the current buffer high address reg */ +#define FSDIO_CARD_THRCTL_OFFSET 0x100U /* the card threshold control reg */ +#define FSDIO_UHS_REG_EXT_OFFSET 0x108U /* the UHS register extension */ +#define FSDIO_EMMC_DDR_REG_OFFSET 0x10CU /* the EMMC DDR reg */ +#define FSDIO_ENABLE_SHIFT_OFFSET 0x110U /* the enable phase shift reg */ +#define FSDIO_DATA_OFFSET 0x200U /* the data FIFO access */ + +/** @name FSDIO_CNTRL_OFFSET x0 Register + */ +#define FSDIO_CNTRL_CONTROLLER_RESET BIT(0) /* RW 复位控制器,除 DMA,FIFO */ +#define FSDIO_CNTRL_FIFO_RESET BIT(1) /* RW 复位 FIFO, 1 有效 */ +#define FSDIO_CNTRL_DMA_RESET BIT(2) /* RW 复位内部 DMA, 1 有效 */ +#define FSDIO_CNTRL_INT_ENABLE BIT(4) /* RW 全局中断使能配置, 1 使能 */ +#define FSDIO_CNTRL_DMA_ENABLE BIT(5) /* RW 外部 DMA 模式使能 */ +#define FSDIO_CNTRL_READ_WAIT BIT(6) /* RW SDIO 读等待 1 有效 */ +#define FSDIO_CNTRL_SEND_IRQ_RESPONSE BIT(7) /* RW MMC 中断自动响应配置 1 有效 */ +#define FSDIO_CNTRL_ABORT_READ_DATA BIT(8) /* RW 读暂停异常清除 */ +#define FSDIO_CNTRL_SEND_CCSD BIT(9) /* RW 发送CCD (NOT USED) */ +#define FSDIO_CNTRL_SEND_AUTO_STOP_CCSD BIT(10) /* RW 发送CCD,自动STOP (NOT USED) */ +#define FSDIO_CNTRL_ENDIAN BIT(11) /* RW 0:小端,1:大端 */ +#define FSDIO_CNTRL_CARD_VOLTAGE_A_MASK GENMASK(19, 16) /* RW A电压选择 */ +#define FSDIO_CNTRL_CARD_VOLTAGE_B_MASK GENMASK(23, 20) /* RW B电压选择 */ +#define FSDIO_CNTRL_ENABLE_OD_PULLUP BIT(24) /* RW 外部开漏输出 */ +#define FSDIO_CNTRL_USE_INTERNAL_DMAC BIT(25) /* RW 使用内部DMA */ + +/** @name FSDIO_PWREN_OFFSET 0x4 Register + */ +#define FSDIO_PWREN_ENABLE BIT(0) /* RW 卡供电开关, 0:关;1:开*/ + +/** @name FSDIO_CLKDIV_OFFSET 0x8 Register + */ +/* CLK_SAMPLE 和 CLK_SAMPLE 必须小于 CLK_DIVIDER */ +#define FSDIO_CLK_SAMPLE_SET(x) SET_REG32_BITS((x), 23, 16) +#define FSDIO_CLK_DRV_SET(x) SET_REG32_BITS((x), 15, 8) +#define FSDIO_CLK_DIVIDER_SET(x) SET_REG32_BITS((x), 7, 0) /* 分频系数 = 2 * bit[7:0] */ +#define FSDIO_CLK_DIV(samp, drv, div) FSDIO_CLK_SAMPLE_SET(samp) | \ + FSDIO_CLK_DRV_SET(drv) | \ + FSDIO_CLK_DIVIDER_SET(div) + +/** @name FSDIO_CLKENA_OFFSET Register + */ +#define FSDIO_CLKENA_CCLK_ENABLE BIT(0) /* RW 0:Clock disabled;1:Clock enabled */ +#define FSDIO_CLKENA_CCLK_LOW_POWER BIT(16) /* RW 0x0:非低功耗;0x1:低功耗 */ + +/** @name FSDIO_TMOUT_OFFSET Register + */ +#define FSDIO_MAX_DATA_TIMEOUT 0xffffff /* RW 读卡超时(以卡时钟为单位) */ +#define FSDIO_MAX_RESP_TIMEOUT 0xff /* RW 响应超时(以卡时钟为单位) */ +#define FSDIO_TIMEOUT_DATA(data_timeout, resp_timeout) \ + ((GENMASK(31, 8) & ((data_timeout) << 8)) | \ + (GENMASK(7, 0) & ((resp_timeout)))) + +/** @name FSDIO_CTYPE_OFFSET Register + */ +#define FSDIO_CARD0_WIDTH1_8BIT BIT(16) /* 1: 8-bit mode */ +#define FSDIO_CARD0_WIDTH2_4BIT BIT(0) /* 1: 4-bit mode */ +#define FSDIO_CARD0_WIDTH2_1BIT 0x0U /* 0: 1-bit mode */ + +/** @name FSDIO_INT_MASK_OFFSET Register + * @name FSDIO_MASKED_INTS_OFFSET Register + * @name FSDIO_RAW_INTS_OFFSET Register + */ +#define FSDIO_INT_CD_BIT BIT(0) /* RW Card detect (CD) */ +#define FSDIO_INT_RE_BIT BIT(1) /* RW Response error (RE) */ +#define FSDIO_INT_CMD_BIT BIT(2) /* RW Command done (CD) */ +#define FSDIO_INT_DTO_BIT BIT(3) /* RW Data transfer over (DTO) */ +#define FSDIO_INT_TXDR_BIT BIT(4) /* RW Transmit FIFO data request (TXDR) */ +#define FSDIO_INT_RXDR_BIT BIT(5) /* RW Receive FIFO data request (RXDR) */ +#define FSDIO_INT_RCRC_BIT BIT(6) /* RW Response CRC error (RCRC) */ +#define FSDIO_INT_DCRC_BIT BIT(7) /* RW Data CRC error (DCRC) */ +#define FSDIO_INT_RTO_BIT BIT(8) /* RW Response timeout (RTO) */ +#define FSDIO_INT_DRTO_BIT BIT(9) /* RW Data read timeout (DRTO) */ +#define FSDIO_INT_HTO_BIT BIT(10) /* RW Data starvation-by-host timeout (HTO) */ +#define FSDIO_INT_FRUN_BIT BIT(11) /* RW FIFO underrun/overrun error (FRUN) */ +#define FSDIO_INT_HLE_BIT BIT(12) /* RW Hardware locked write error (HLE) */ +#define FSDIO_INT_SBE_BCI_BIT BIT(13) /* RW Start-bit error (SBE) */ +#define FSDIO_INT_ACD_BIT BIT(14) /* RW Auto command done (ACD) */ +#define FSDIO_INT_EBE_BIT BIT(15) /* RW End-bit error (read)/Write no CRC (EBE) */ +#define FSDIO_INT_SDIO_BIT BIT(16) /* RW SDIO interrupt for card */ + +#define FSDIO_INT_ALL_BITS GENMASK(16, 0) +#define FSDIO_INTS_CMD_MASK (FSDIO_INT_RE_BIT | FSDIO_INT_CMD_BIT | FSDIO_INT_RCRC_BIT | \ + FSDIO_INT_RTO_BIT | FSDIO_INT_HTO_BIT | FSDIO_INT_HLE_BIT) + +#define FSDIO_INTS_DATA_MASK (FSDIO_INT_DTO_BIT | FSDIO_INT_DCRC_BIT | FSDIO_INT_DRTO_BIT | \ + FSDIO_INT_SBE_BCI_BIT) + +/** @name FSDIO_CMD_OFFSET Register + */ +#define FSDIO_CMD_START BIT(31) /* 启动命令 */ +#define FSDIO_CMD_USE_HOLD_REG BIT(29) /* 0: 旁路HOLD寄存器,1: 使能HOLD寄存器 */ +#define FSDIO_CMD_VOLT_SWITCH BIT(28) /* 0: 无电压转换,1: 有电压转换 */ +#define FSDIO_CMD_BOOT_MODE BIT(27) /* 0: Mandatory boot, 1: Alternate boot */ +#define FSDIO_CMD_DISABLE_BOOT BIT(26) /* 中止boot进程 */ +#define FSDIO_CMD_EXPECT_BOOT_ACK BIT(25) /* 1: Expect book ack */ +#define FSDIO_CMD_ENABLE_BOOT BIT(24) /* 1: 使能 boot for mandatory */ +#define FSDIO_CMD_UPD_CLK BIT(21) /* 1:不发送指令,仅更新时钟寄存器的值到卡时钟域内 */ +#define FSDIO_CMD_CARD_NUM_SET(num) SET_REG32_BITS((num), 20, 16) +#define FSDIO_CMD_INIT BIT(15) /* 0:在发送指令前不发送初始化序列(80 个周期) 1: 发送 */ +#define FSDIO_CMD_STOP_ABORT BIT(14) /* 1:停止或中止命令,用于停止当前的数据传输 */ +#define FSDIO_CMD_WAIT_PRVDATA_COMPLETE BIT(13) /* 1:等待前面的数据传输完成后再发送指令 0: 立即发送命令 */ +#define FSDIO_CMD_SEND_AUTO_STOP BIT(12) /* 1:在数据传送结束时发送停止命令 */ +#define FSDIO_CMD_TRANSF_MODE_SET(mode) SET_REG32_BITS((mode), 12, 11) /* 1: 流数据传输指令 */ +#define FSDIO_CMD_DAT_WRITE BIT(10) /* 0:读卡 1:写卡 */ +#define FSDIO_CMD_DAT_EXP BIT(9) /* 0:不等待数据传输, 1:等待数据传输 */ +#define FSDIO_CMD_RESP_CRC BIT(8) /* 1:检查响应 CRC */ +#define FSDIO_CMD_RESP_LONG BIT(7) /* 0:等待卡的短响应 1:等待卡的长响应 */ +#define FSDIO_CMD_RESP_EXP BIT(6) /* 1:等待卡的响应,0:命令不需要卡响应 */ +#define FSDIO_CMD_INDX_SET(ind) SET_REG32_BITS((ind), 5, 0) /* 命令索引号 */ + +/** @name FSDIO_STATUS_OFFSET Register + */ +#define FSDIO_STATUS_FIFO_RX BIT(0) /* RO, 达到 FIFO_RX 标记 */ +#define FSDIO_STATUS_FIFO_TX BIT(1) /* RO, 达到 FIFO_TX 标记 */ +#define FSDIO_STATUS_FIFO_EMPTY BIT(2) /* RO, FIFO empty */ +#define FSDIO_STATUS_FIFO_FULL BIT(3) /* RO, FIFO full */ +#define FSDIO_STATUS_CMD_FSM_GET(reg_val) GET_REG32_BITS((reg_val), 7, 4) +#define FSDIO_STATUS_DATA3_STATUS BIT(8) /* RO DATA[3] 卡在位检测,1:在位 */ +#define FSDIO_STATUS_DATA_BUSY BIT(9) /* RO 1: 卡 busy */ +#define FSDIO_STATUS_DATA_STATE_MC_BUSY BIT(10) /* RO DATA TX|RX FSM busy */ +#define FSDIO_STATUS_RESP_INDEX_GET(reg_val) GET_REG32_BITS((reg_val), 16, 11) +#define FSDIO_STATUS_FIFO_CNT_GET(reg_val) GET_REG32_BITS((reg_val), 29, 17) /* RO: FIFO 填充计数器 */ +#define FSDIO_STATUS_DMA_ACK BIT(30) /* RO DMA 确认 */ +#define FSDIO_STATUS_DMA_REQ BIT(31) /* RO DMA 请求 */ + +/** @name FSDIO_FIFOTH_OFFSET Register + */ +enum +{ + FSDIO_FIFOTH_DMA_TRANS_1 = 0b000, + FSDIO_FIFOTH_DMA_TRANS_4 = 0b001, + FSDIO_FIFOTH_DMA_TRANS_8 = 0b010, + FSDIO_FIFOTH_DMA_TRANS_16 = 0b011, + FSDIO_FIFOTH_DMA_TRANS_32 = 0b100, + FSDIO_FIFOTH_DMA_TRANS_64 = 0b101, + FSDIO_FIFOTH_DMA_TRANS_128 = 0b110, + FSDIO_FIFOTH_DMA_TRANS_256 = 0b111 +}; + +#define FSDIO_FIFOTH_DMA_TRANS_MASK GENMASK(30, 28) /* 多次传输的突发大小 */ +#define FSDIO_FIFOTH_RX_WMARK_MASK GENMASK(27, 16) /* 当接收数据给卡时FIFO的阈值 */ +#define FSDIO_FIFOTH_TX_WMARK_MASK GENMASK(11, 0) /* 当发送数据给卡时FIFO的阈值 */ + +#define FSDIO_RX_WMARK 0x7U +#define FSDIO_TX_WMARK 0x100U + +/* + trans_size: Burst size of multiple transaction; + rx_wmark: FIFO threshold watermark level when receiving data to card. + tx_wmark: FIFO threshold watermark level when transmitting data to card +*/ +#define FSDIO_FIFOTH(trans_size, rx_wmark, tx_wmark) \ + (((FSDIO_FIFOTH_DMA_TRANS_MASK) & ((trans_size) << 28)) | \ + ((FSDIO_FIFOTH_RX_WMARK_MASK) & ((rx_wmark) << 16)) | \ + ((FSDIO_FIFOTH_TX_WMARK_MASK) & (tx_wmark))) + +#define FSDIO_DMA_TRANS_SIZE_SET(x) SET_REG32_BITS((x), 30, 28) +#define FSDIO_RX_MARK_SIZE_SET(x) SET_REG32_BITS((x), 27, 16) +#define FSDIO_TX_MARK_SIZE_SET(x) SET_REG32_BITS((x), 11, 0) + +/** @name FSDIO_CARD_DETECT_OFFSET Register + */ +#define FSDIO_CARD_DETECTED BIT(0) /* 1:卡不在位;0:卡在位 */ + +/** @name FSDIO_CARD_WRTPRT_OFFSET Register + */ +#define FSDIO_CARD_WRITE_PROTECTED BIT(0) /* 1:写保护;0:无写保护 */ + +/** @name FSDIO_GPIO_OFFSET Register + */ +#define FSDIO_CLK_READY BIT(0) /* CIU 时钟 ready */ + +/** @name FSDIO_UHS_REG_OFFSET Register + */ +#define FSDIO_UHS_REG_VOLT_180 BIT(0) /* RW 外部调压器接口电压 0: 3.3v, 1: 1.8v */ +#define FSDIO_UHS_REG_VOLT_330 0U +#define FSDIO_UHS_REG_DDR BIT(16) /* RW DDR 模式 */ + +/** @name FSDIO_CARD_RESET_OFFSET Register + */ +#define FSDIO_CARD_RESET_ENABLE BIT(0) /* RW 1:运行;0:复位 */ + +/** @name FSDIO_BUS_MODE_OFFSET Register + */ +#define FSDIO_BUS_MODE_SWR BIT(0) /* RW 软复位,复位idma内部寄存器 */ +#define FSDIO_BUS_MODE_FB BIT(1) /* RW 固定burst */ +#define FSDIO_BUS_MODE_DE BIT(7) /* RW idma使能 */ +#define FSDIO_BUS_MODE_PBL_GET(reg_val) GET_REG32_BITS((reg_val), 10, 8) /* burst LEN */ + +/** @name FSDIO_DMAC_STATUS_OFFSET Register + */ +#define FSDIO_DMAC_STATUS_TI BIT(0) /* RW 发送中断。表示链表的数据发送完成 */ +#define FSDIO_DMAC_STATUS_RI BIT(1) /* RW 接收中断。表示链表的数据接收完成 */ +#define FSDIO_DMAC_STATUS_FBE BIT(2) /* RW 致命总线错误中断 */ +#define FSDIO_DMAC_STATUS_DU BIT(4) /* RW 链表不可用中断 */ +#define FSDIO_DMAC_STATUS_CES BIT(5) /* RW 卡错误汇总 */ +#define FSDIO_DMAC_STATUS_NIS BIT(8) /* RW 正常中断汇总 */ +#define FSDIO_DMAC_STATUS_AIS BIT(9) /* RW 异常中断汇总 */ +#define FSDIO_DMAC_STATUS_EB_GET(reg_val) GET_REG32_BITS((reg_val), 12, 10) +#define FSDIO_DMAC_STATUS_ALL_BITS GENMASK(9, 0) + +#define FSDIO_DMAC_STATUS_EB_TX 0b001 +#define FSDIO_DMAC_STATUS_EB_RX 0b010 + +/** @name FSDIO_DMAC_INT_EN_OFFSET Register + */ +#define FSDIO_DMAC_INT_ENA_TI BIT(0) /* RW 发送完成中断使能 */ +#define FSDIO_DMAC_INT_ENA_RI BIT(1) /* RW 接收完成中断使能 */ +#define FSDIO_DMAC_INT_ENA_FBE BIT(2) /* RW 总线错误中断使能 */ +#define FSDIO_DMAC_INT_ENA_DU BIT(4) /* RW 描述符不可读中断使能 */ +#define FSDIO_DMAC_INT_ENA_CES BIT(5) /* RW 卡错误中断使能 */ +#define FSDIO_DMAC_INT_ENA_NIS BIT(8) /* RW 正常中断汇总使能 */ +#define FSDIO_DMAC_INT_ENA_AIS BIT(9) /* RW 异常中断汇总使能 */ +#define FSDIO_DMAC_INT_ENA_ALL GENMASK(9, 0) + +#define FSDIO_DMAC_INTS_MASK (FSDIO_DMAC_INT_ENA_FBE | FSDIO_DMAC_INT_ENA_DU | \ + FSDIO_DMAC_INT_ENA_NIS | FSDIO_DMAC_INT_ENA_AIS) + +/** @name FSDIO_CARD_THRCTL_OFFSET Register + */ +#define FSDIO_CARD_THRCTL_CARDRD BIT(0) /* RW 读卡threshold使能 */ +#define FSDIO_CARD_THRCTL_BUSY_CLR BIT(1) /* RW busy清中断 */ +#define FSDIO_CARD_THRCTL_CARDWR BIT(2) /* RO 写卡threshold使能 */ +enum +{ + FSDIO_FIFO_DEPTH_8 = 23, + FSDIO_FIFO_DEPTH_16 = 24, + FSDIO_FIFO_DEPTH_32 = 25, + FSDIO_FIFO_DEPTH_64 = 26, + FSDIO_FIFO_DEPTH_128 = 27 +}; + +#define FSDIO_CARD_THRCTL_THRESHOLD(n) BIT(n) /* 读卡 Threshold */ + +/** @name FSDIO_UHS_REG_EXT_OFFSET Register + */ +#define FSDIO_UHS_EXT_MMC_VOLT BIT(0) /* RW 1.2V供电选择 */ +#define FSDIO_UHS_EXT_CLK_ENA BIT(1) /* RW 外部时钟,CIU时钟使能 */ +#define FSDIO_UHS_CLK_DIV_MASK GENMASK(14, 8) /* RW 分频系数,ciu_f = clk_div_ctrl + 1, min=1*/ +#define FSDIO_UHS_CLK_DIV(x) (FSDIO_UHS_CLK_DIV_MASK & ((x) << 8)) +#define FSDIO_UHS_CLK_SAMP_MASK GENMASK(22, 16) /* RW 采样相位参数,相对于ciu时钟相位点 */ +#define FSDIO_UHS_CLK_SAMP(x) (FSDIO_UHS_CLK_SAMP_MASK & ((x) << 16)) +#define FSDIO_UHS_CLK_DRV_MASK GENMASK(30, 24) /* RW 输出相位参数,相对于ciu时钟相位点 */ +#define FSDIO_UHS_CLK_DRV(x) (FSDIO_UHS_CLK_DRV_MASK & ((x) << 24)) +#define FSDIO_UHS_EXT_CLK_MUX BIT(31) + +/* FSDIO_UHS_REG_EXT_OFFSET 和 FSDIO_CLKDIV_OFFSET 两个寄存器配合完成卡时钟和驱动采样相位调整 + UHS_REG_EXT 配置一级分频,CLK_DIV 决定CARD工作时钟频率, DRV 和 SAMP 分别控制驱动相位和采样相位粗调 + 分配系数 = bit [14 : 8] + 1 + CLKDIV 配置二级分频, DIVIDER , DRV 和 SAMP 分别控制驱动相位和采样相位精调 + 分配系数 = bit [7: 0] * 2 +*/ +#define FSDIO_UHS_REG(drv_phase, samp_phase, clk_div) \ + (FSDIO_UHS_CLK_DRV(drv_phase) | \ + FSDIO_UHS_CLK_SAMP(samp_phase) | \ + FSDIO_UHS_CLK_DIV(clk_div)) + +#define FSDIO_UHS_CLK_DIV_SET(x) SET_REG32_BITS((x), 14, 8) +#define FSDIO_UHS_CLK_DIV_GET(reg_val) GET_REG32_BITS((reg_val), 14, 8) +#define FSDIO_UHS_CLK_SAMP_SET(x) SET_REG32_BITS((x), 22, 16) +#define FSDIO_UHS_CLK_DRV_SET(x) SET_REG32_BITS((x), 30, 24) + +/** @name FSDIO_REG_EMMC_DDR_REG_OFFSET Register + */ +#define FSDIO_EMMC_DDR_CYCLE BIT(0) /* RW 1: start bit小于一个周期,0:start bit 为一个周期 */ + +#define FSDIO_TIMEOUT (50000) /* timeout for retries */ +#define FSDIO_DELAY_US (5) +#define FSDIO_400_KHZ (400000UL) +#define FSDIO_25_MHZ (25000000UL) +#define FSDIO_MAX_FIFO_CNT (0x800U) + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FSDIO_READ_REG(addr, reg_off) FtIn32((addr) + (u32)(reg_off)) +#define FSDIO_WRITE_REG(addr, reg_off, reg_val) FtOut32((addr) + (u32)(reg_off), (u32)(reg_val)) +#define FSDIO_CLR_BIT(addr, reg_off, bits) FtClearBit32((addr) + (u32)(reg_off), bits) +#define FSDIO_SET_BIT(addr, reg_off, bits) FtSetBit32((addr) + (u32)(reg_off), bits) + +/************************** Function Prototypes ******************************/ +FError FSdioSendPrivateCmd(uintptr base_addr, u32 cmd, u32 arg); +FError FSdioResetCtrl(uintptr base_addr, u32 reset_bits); + +/*****************************************************************************/ +/** + * @name: FSdioSetClock + * @msg: Enable/Disable controller clock + * @return {NONE} + * @param {uintptr} base_addr, base address of SDIO controller + * @param {boolean} enable, TRUE: enable clock + */ +static inline void FSdioSetClock(uintptr base_addr, boolean enable) +{ + u32 reg_val = FSDIO_READ_REG(base_addr, FSDIO_CLKENA_OFFSET); + if (enable) + reg_val |= FSDIO_CLKENA_CCLK_ENABLE; + else + reg_val &= ~FSDIO_CLKENA_CCLK_ENABLE; + FSDIO_WRITE_REG(base_addr, FSDIO_CLKENA_OFFSET, reg_val); +} + +static inline void FSdioSetPower(uintptr base_addr, boolean enable) +{ + u32 reg_val = FSDIO_READ_REG(base_addr, FSDIO_PWREN_OFFSET); + if (enable) + reg_val |= FSDIO_PWREN_ENABLE; + else + reg_val &= ~FSDIO_PWREN_ENABLE; + FSDIO_WRITE_REG(base_addr, FSDIO_PWREN_OFFSET, reg_val); +} + +static inline void FSdioSetExtClock(uintptr base_addr, boolean enable) +{ + u32 reg_val = FSDIO_READ_REG(base_addr, FSDIO_UHS_REG_EXT_OFFSET); + if (enable) + reg_val |= FSDIO_UHS_EXT_CLK_ENA; + else + reg_val &= ~FSDIO_UHS_EXT_CLK_ENA; + FSDIO_WRITE_REG(base_addr, FSDIO_UHS_REG_EXT_OFFSET, reg_val); +} + +static inline void FSdioSetVoltage1_8V(uintptr base_addr, boolean v1_8) +{ + u32 reg_val = FSDIO_READ_REG(base_addr, FSDIO_UHS_REG_OFFSET); + if (v1_8) + reg_val |= FSDIO_UHS_REG_VOLT_180; + else + reg_val &= ~FSDIO_UHS_REG_VOLT_180; /* 3.3v */ + FSDIO_WRITE_REG(base_addr, FSDIO_UHS_REG_OFFSET, reg_val); +} + +/** + * @name: FSdioGetRawStatus + * @msg: Get raw interrupt status of controller + * @return {u32} raw interrupt status + * @param {uintptr} base_addr, base address of SDIO controller + */ +static inline u32 FSdioGetRawStatus(uintptr base_addr) +{ + return FSDIO_READ_REG(base_addr, FSDIO_RAW_INTS_OFFSET); +} + +/** + * @name: FSdioClearRawStatus + * @msg: Clear raw interrupt status of controller + * @return {NONE} + * @param {uintptr} base_addr, base address of SDIO controller + */ +static inline void FSdioClearRawStatus(uintptr base_addr) +{ + u32 reg_val = FSdioGetRawStatus(base_addr); + FSDIO_WRITE_REG(base_addr, FSDIO_RAW_INTS_OFFSET, reg_val); +} + +/** + * @name: FSdioGetStatus + * @msg: Get status of controller + * @return {u32} controller status + * @param {uintptr} base_addr, base address of SDIO controller + */ +static inline u32 FSdioGetStatus(uintptr base_addr) +{ + return FSDIO_READ_REG(base_addr, FSDIO_STATUS_OFFSET); +} + +/** + * @name: FSdioGetDMAStatus + * @msg: Get interrupt status of DMA + * @return {u32} DMA interrupt status + * @param {uintptr} base_addr, base address of SDIO controller + */ +static inline u32 FSdioGetDMAStatus(uintptr base_addr) +{ + return FSDIO_READ_REG(base_addr, FSDIO_DMAC_STATUS_OFFSET); +} + +/** + * @name: FSdioClearDMAStatus + * @msg: Clear interrupt status of DMA + * @return {NONE} + * @param {uintptr} base_addr, base address of SDIO controller + */ +static inline void FSdioClearDMAStatus(uintptr base_addr) +{ + u32 reg_val = FSdioGetDMAStatus(base_addr); + FSDIO_WRITE_REG(base_addr, FSDIO_DMAC_STATUS_OFFSET, reg_val); +} + +/** + * @name: FSdioSetDescriptor + * @msg: Set base address of DMA descriptors + * @return {NONE} + * @param {uintptr} base_addr, base address of SDIO controller + * @param {uintptr} descriptor, base address of DMA descriptors + */ +static inline void FSdioSetDescriptor(uintptr base_addr, uintptr descriptor) +{ +#ifdef __aarch64___ + FSDIO_WRITE_REG(base_addr, FSDIO_DESC_LIST_ADDRH_OFFSET, UPPER_32_BITS(descriptor)); + FSDIO_WRITE_REG(base_addr, FSDIO_DESC_LIST_ADDRL_OFFSET, LOWER_32_BITS(descriptor)); +#else + FSDIO_WRITE_REG(base_addr, FSDIO_DESC_LIST_ADDRH_OFFSET, 0x0U); + FSDIO_WRITE_REG(base_addr, FSDIO_DESC_LIST_ADDRL_OFFSET, (u32)(descriptor)); +#endif +} + +/** + * @name: FSdioSetTransBytes + * @msg: Set number of bytes to transfer + * @return {NONE} + * @param {uintptr} base_addr, base address of SDIO controller + * @param {u32} bytes, number of bytes to transfer + */ +static inline void FSdioSetTransBytes(uintptr base_addr, u32 bytes) +{ + FSDIO_WRITE_REG(base_addr, FSDIO_BYT_CNT_OFFSET, bytes); +} + +/** + * @name: FSdioSetBlockSize + * @msg: Set size of blocks in card + * @return {NONE} + * @param {uintptr} base_addr, base address of SDIO controller + * @param {u32} block_size, size of blocks in card + */ +static inline void FSdioSetBlockSize(uintptr base_addr, u32 block_size) +{ + FSDIO_WRITE_REG(base_addr, FSDIO_BLK_SIZ_OFFSET, block_size); +} + +/** + * @name: FSdioSetBusWidth + * @msg: Set bus width of card + * @return {NONE} + * @param {uintptr} base_addr, base address of SDIO controller + * @param {u32} width, bus width of card 1-bit, 4-bit, 8-bit + */ +static inline void FSdioSetBusWidth(uintptr base_addr, u32 width) +{ + u32 reg_val; + + switch (width) + { + case 1: + reg_val = FSDIO_CARD0_WIDTH2_1BIT; + break; + case 4: + reg_val = FSDIO_CARD0_WIDTH2_4BIT; + break; + case 8: + reg_val = FSDIO_CARD0_WIDTH1_8BIT; + break; + default: + FASSERT_MSG(0, "invalid bus width %d", width); + break; + } + + FSDIO_WRITE_REG(base_addr, FSDIO_CTYPE_OFFSET, reg_val); +} + +/** + * @name: FSdioGetBusWidth + * @msg: Get bus width for card + * @return {u32} current bus width setting + * @param {uintptr} base_addr, base address of SDIO controller + */ +static inline u32 FSdioGetBusWidth(uintptr base_addr) +{ + u32 bus_width = 1; + u32 reg_val = FSDIO_READ_REG(base_addr, FSDIO_CTYPE_OFFSET); + + if (FSDIO_CARD0_WIDTH2_4BIT & reg_val) + { + bus_width = 4; + } + else if (FSDIO_CARD0_WIDTH1_8BIT & reg_val) + { + bus_width = 8; + } + + return bus_width; +} + +/** + * @name: FSdioResetIDMA + * @msg: Reset for internal DMA + * @return {NONE} + * @param {uintptr} base_addr, base address of SDIO controller + */ +static inline void FSdioResetIDMA(uintptr base_addr) +{ + u32 reg_val = FSDIO_READ_REG(base_addr, FSDIO_BUS_MODE_OFFSET); + reg_val |= FSDIO_BUS_MODE_SWR; /* 写1软复位idma,复位完成后硬件自动清0 */ + FSDIO_WRITE_REG(base_addr, FSDIO_BUS_MODE_OFFSET, reg_val); +} + +/** + * @name: FSdioSetDDRMode + * @msg: Enable/Disable DDR mode + * @return {*} + * @param {uintptr} base_addr, base address of SDIO controller + * @param {boolean} enable, TRUE: enable DDR mode + */ +static inline void FSdioSetDDRMode(uintptr base_addr, boolean enable) +{ + u32 uhs_val = FSDIO_READ_REG(base_addr, FSDIO_UHS_REG_OFFSET); + u32 emmc_val = FSDIO_READ_REG(base_addr, FSDIO_EMMC_DDR_REG_OFFSET); + if (enable) + { + uhs_val |= FSDIO_UHS_REG_DDR; + emmc_val |= FSDIO_EMMC_DDR_CYCLE; + } + else + { + uhs_val &= ~FSDIO_UHS_REG_DDR; + emmc_val &= ~FSDIO_EMMC_DDR_CYCLE; + } + FSDIO_WRITE_REG(base_addr, FSDIO_UHS_REG_OFFSET, uhs_val); + FSDIO_WRITE_REG(base_addr, FSDIO_EMMC_DDR_REG_OFFSET, emmc_val); + + return; +} + +/** + * @name: FSdioCheckIfCardExists + * @msg: Check if card inserted + * @return {boolean} TRUE: inserted, FALSE: not-found + * @param {uintptr} base_addr, base address of SDIO controller + */ +static inline boolean FSdioCheckIfCardExists(uintptr base_addr) +{ + return (FSDIO_READ_REG(base_addr, FSDIO_CARD_DETECT_OFFSET) & FSDIO_CARD_DETECTED) ? + FALSE : TRUE; +} + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio_intr.c b/bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio_intr.c new file mode 100644 index 0000000000..333795b2db --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio_intr.c @@ -0,0 +1,225 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsdio_intr.c + * Date: 2022-06-01 15:08:58 + * LastEditTime: 2022-06-01 15:08:58 + * Description:  This files is for SDIO interrupt related function implementation + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.1 zhugengyu 2022/6/6 modify according to tech manual. + */ +/***************************** Include Files *********************************/ + +#include "fio.h" +#include "fdebug.h" +#include "fassert.h" +#include "ftypes.h" + +#include "fsdio_hw.h" +#include "fsdio.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FSDIO_DEBUG_TAG "FSDIO-INTR" +#define FSDIO_ERROR(format, ...) FT_DEBUG_PRINT_E(FSDIO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSDIO_WARN(format, ...) FT_DEBUG_PRINT_W(FSDIO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSDIO_INFO(format, ...) FT_DEBUG_PRINT_I(FSDIO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSDIO_DEBUG(format, ...) FT_DEBUG_PRINT_D(FSDIO_DEBUG_TAG, format, ##__VA_ARGS__) + +#define FSDIO_CALL_EVT_HANDLER(instance_p, evt) \ + if (instance_p->evt_handlers[evt]) \ + { \ + instance_p->evt_handlers[evt](instance_p, instance_p->evt_args[evt]); \ + } + +static const u32 cmd_err_ints_mask = FSDIO_INT_RTO_BIT | FSDIO_INT_RCRC_BIT | FSDIO_INT_RE_BIT | + FSDIO_INT_DCRC_BIT | FSDIO_INT_DRTO_BIT | + FSDIO_INT_SBE_BCI_BIT | FSDIO_INT_HLE_BIT; + +static const u32 dmac_err_ints_mask = FSDIO_DMAC_INT_ENA_FBE | FSDIO_DMAC_INT_ENA_DU | + FSDIO_DMAC_INT_ENA_AIS; +/************************** Function Prototypes ******************************/ + +/*****************************************************************************/ + +/** + * @name: FSdioGetInterruptMask + * @msg: Get SDIO controller interrupt mask + * @return {u32} interrupt mask bits + * @param {FSdio} *instance_p, SDIO controller instance + * @param {FSdioIntrType} type, Type of interrupt, controller/DMA interrupt + */ +u32 FSdioGetInterruptMask(FSdio *const instance_p, FSdioIntrType type) +{ + FASSERT(instance_p); + uintptr base_addr = instance_p->config.base_addr; + u32 mask = 0U; + + if (0 == instance_p->config.base_addr) + { + FSDIO_ERROR("device is not yet initialized!!!"); + return mask; + } + + if (FSDIO_GENERAL_INTR == type) + { + mask = FSDIO_READ_REG(base_addr, FSDIO_INT_MASK_OFFSET); + } + else + { + mask = FSDIO_READ_REG(base_addr, FSDIO_DMAC_INT_EN_OFFSET); + } + + return mask; +} + +/** + * @name: FSdioSetInterruptMask + * @msg: Enable/Disable SDIO controller interrupt + * @return {NONE} + * @param {FSdio} *instance_p, SDIO controller instance + * @param {FSdioIntrType} type, Type of interrupt, controller/DMA interrupt + * @param {u32} set_mask, interrupt mask bits + * @param {boolean} enable, TRUE: enable interrupt mask bits + */ +void FSdioSetInterruptMask(FSdio *const instance_p, FSdioIntrType type, u32 set_mask, boolean enable) +{ + FASSERT(instance_p); + uintptr base_addr = instance_p->config.base_addr; + u32 mask = 0U; + + if (0 == instance_p->config.base_addr) + { + FSDIO_ERROR("device is not yet initialized!!!"); + return; + } + + mask = FSdioGetInterruptMask(instance_p, type); + + if (TRUE == enable) + { + mask |= set_mask; + } + else + { + mask &= ~set_mask; + } + + if (FSDIO_GENERAL_INTR == type) + { + FSDIO_WRITE_REG(base_addr, FSDIO_INT_MASK_OFFSET, mask); + } + else + { + FSDIO_WRITE_REG(base_addr, FSDIO_DMAC_INT_EN_OFFSET, mask); + } + + return; +} + +/** + * @name: FSdioInterruptHandler + * @msg: Interrupt handler for SDIO instance + * @return {NONE} + * @param {s32} vector, Interrupt id + * @param {void} *param, Interrupt params, is SDIO instance + */ +void FSdioInterruptHandler(s32 vector, void *param) +{ + FASSERT(param); + FSdio *const instance_p = (FSdio * const)param; + uintptr base_addr = instance_p->config.base_addr; + u32 events, event_mask, dmac_events, dmac_evt_mask; + + events = FSDIO_READ_REG(base_addr, FSDIO_RAW_INTS_OFFSET); + dmac_events = FSDIO_READ_REG(base_addr, FSDIO_DMAC_STATUS_OFFSET); + event_mask = FSDIO_READ_REG(base_addr, FSDIO_INT_MASK_OFFSET); + dmac_evt_mask = FSDIO_READ_REG(base_addr, FSDIO_DMAC_INT_EN_OFFSET); + + if (!(events & FSDIO_INT_ALL_BITS) && + !(dmac_events & FSDIO_DMAC_STATUS_ALL_BITS)) + { + FSDIO_DEBUG("irq exit with no action"); + return; /* no interrupt status */ + } + + FSDIO_WRITE_REG(base_addr, 0xfd0U, 0U); + + FSDIO_DEBUG("events:0x%x,mask:0x%x,dmac_events:%x,dmac_mask:0x%x", events, event_mask, dmac_events, dmac_evt_mask); + + FSDIO_WRITE_REG(base_addr, FSDIO_RAW_INTS_OFFSET, events); + FSDIO_WRITE_REG(base_addr, FSDIO_DMAC_STATUS_OFFSET, dmac_events); + + if (((events & event_mask) == 0) && + ((dmac_events & dmac_evt_mask == 0))) + { + return; /* no need to handle interrupt */ + } + + /* handle card detect event */ + if (((events & event_mask) & FSDIO_INT_CD_BIT) && (FALSE == instance_p->config.non_removable)) + { + FSDIO_DEBUG("sd status changed here ! status:[%d]", FSDIO_READ_REG(base_addr, FSDIO_CARD_DETECT_OFFSET)); + FSDIO_CALL_EVT_HANDLER(instance_p, FSDIO_EVT_CARD_DETECTED); + } + + if ((events & FSDIO_INT_DTO_BIT) && (events & FSDIO_INT_CMD_BIT)) /* handle cmd && data done */ + { + FSDIO_DEBUG("cmd and data over"); + FSDIO_CALL_EVT_HANDLER(instance_p, FSDIO_EVT_CMD_DONE); + FSDIO_CALL_EVT_HANDLER(instance_p, FSDIO_EVT_DATA_DONE); + } + else if (events & FSDIO_INT_CMD_BIT) /* handle cmd done */ + { + FSDIO_DEBUG("cmd over"); + FSDIO_CALL_EVT_HANDLER(instance_p, FSDIO_EVT_CMD_DONE); + } + else if (events & FSDIO_INT_DTO_BIT) /* handle data done */ + { + FSDIO_DEBUG("data over"); + FSDIO_CALL_EVT_HANDLER(instance_p, FSDIO_EVT_DATA_DONE); + } + + /* handle error state */ + if ((dmac_events & dmac_err_ints_mask) || (events & cmd_err_ints_mask)) + { + FSDIO_ERROR("ERR:events:0x%x,mask:0x%x,dmac_evts:0x%x,dmac_mask:0x%x", + events, event_mask, dmac_events, dmac_evt_mask); + FSDIO_CALL_EVT_HANDLER(instance_p, FSDIO_EVT_ERR_OCCURE); + } + + return; +} + +/** + * @name: FSdioRegisterEvtHandler + * @msg: Register event call-back function as handler for interrupt events + * @return {NONE} + * @param {FSdio} *instance_p, SDIO controller instance + * @param {FSdioEvtType} evt, interrupt event + * @param {FSdioEvtHandler} handler, event call-back function + * @param {void} *handler_arg, argument of event call-back function + */ +void FSdioRegisterEvtHandler(FSdio *const instance_p, FSdioEvtType evt, FSdioEvtHandler handler, void *handler_arg) +{ + FASSERT(instance_p); + + instance_p->evt_handlers[evt] = handler; + instance_p->evt_args[evt] = handler_arg; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio_pio.c b/bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio_pio.c new file mode 100644 index 0000000000..dfe4f1b5a9 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio_pio.c @@ -0,0 +1,266 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsdio_pio.c + * Date: 2022-06-01 14:21:47 + * LastEditTime: 2022-06-01 14:21:47 + * Description:  This files is for PIO transfer related function implementation + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.1 zhugengyu 2022/6/6 modify according to tech manual. + */ +/***************************** Include Files *********************************/ + +#include "fio.h" +#include "fdebug.h" +#include "fassert.h" +#include "ftypes.h" + +#include "fcache.h" + +#include "fsdio_hw.h" +#include "fsdio.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FSDIO_DEBUG_TAG "FSDIO-PIO" +#define FSDIO_ERROR(format, ...) FT_DEBUG_PRINT_E(FSDIO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSDIO_WARN(format, ...) FT_DEBUG_PRINT_W(FSDIO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSDIO_INFO(format, ...) FT_DEBUG_PRINT_I(FSDIO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSDIO_DEBUG(format, ...) FT_DEBUG_PRINT_D(FSDIO_DEBUG_TAG, format, ##__VA_ARGS__) + +/************************** Function Prototypes ******************************/ +extern FError FSdioTransferCmd(FSdio *const instance_p, FSdioCmdData *const cmd_data_p); + +/*****************************************************************************/ +/** + * @name: FSdioPIOWriteData + * @msg: Write data to fifo + * @return {FError} FSDIO_SUCCESS if write success + * @param {FSdio} *instance_p, SDIO controller instance + * @param {FSdioData} *data_p, contents of transfer data + */ +static FError FSdioPIOWriteData(FSdio *const instance_p, FSdioData *data_p) +{ + FASSERT(data_p); + FError ret = FSDIO_SUCCESS; + u32 loop; + uintptr base_addr = instance_p->config.base_addr; + const u32 wr_times = data_p->datalen / sizeof(u32); /* u8 --> u32 */ + u32 *wr_buf = (u32 *)data_p->buf; + + /* while in PIO mode, max data transferred is 0x800 */ + if (data_p->datalen > FSDIO_MAX_FIFO_CNT) + { + FSDIO_ERROR("Fifo do not support write more than 0x%x", FSDIO_MAX_FIFO_CNT); + return FSDIO_ERR_NOT_SUPPORT; + } + + /* write fifo data */ + FSDIO_WRITE_REG(base_addr, FSDIO_CMD_OFFSET, FSDIO_CMD_DAT_WRITE); + for (loop = 0; loop < wr_times; loop++) + { + FSDIO_WRITE_REG(base_addr, FSDIO_DATA_OFFSET, wr_buf[loop]); + } + + return ret; +} + +/** + * @name: FSdioPIOReadData + * @msg: Read data from fifo + * @return {FError} FSDIO_SUCCESS if read success + * @param {FSdio} *instance_p, SDIO controller instance + * @param {FSdioData} *data_p, contents of transfer data + */ +FError FSdioPIOReadData(FSdio *const instance_p, FSdioData *data_p) +{ + FASSERT(data_p); + FError ret = FSDIO_SUCCESS; + u32 loop; + uintptr base_addr = instance_p->config.base_addr; + const u32 rd_times = data_p->datalen / sizeof(u32); /* u8 --> u32 */ + u32 *rd_buf = (u32 *)data_p->buf; + + /* while in PIO mode, max data transferred is 0x800 */ + if (data_p->datalen > FSDIO_MAX_FIFO_CNT) + { + FSDIO_ERROR("Fifo do not support write more than 0x%x", FSDIO_MAX_FIFO_CNT); + return FSDIO_ERR_NOT_SUPPORT; + } + + /* read data from fifo */ + for (loop = 0; loop < rd_times; loop++) + { + rd_buf[loop] = FSDIO_READ_REG(base_addr, FSDIO_DATA_OFFSET); + } + + return ret; +} + +/** + * @name: FSdioPIOTransfer + * @msg: Start command and data transfer in PIO mode + * @return {FError} FSDIO_SUCCESS if transfer success, otherwise failed + * @param {FSdio} *instance_p, SDIO controller instance + * @param {FSdioCmdData} *cmd_data_p, contents of transfer command and data + */ +FError FSdioPIOTransfer(FSdio *const instance_p, FSdioCmdData *const cmd_data_p) +{ + FASSERT(instance_p); + FASSERT(cmd_data_p); + FError ret = FSDIO_SUCCESS; + const boolean read = cmd_data_p->flag & FSDIO_CMD_FLAG_READ_DATA; + uintptr base_addr = instance_p->config.base_addr; + + cmd_data_p->success = FALSE; /* reset cmd transfer status */ + + if (FT_COMPONENT_IS_READY != instance_p->is_ready) + { + FSDIO_ERROR("device is not yet initialized!!!"); + return FSDIO_ERR_NOT_INIT; + } + + if (FSDIO_PIO_TRANS_MODE != instance_p->config.trans_mode) + { + FSDIO_ERROR("device is not configure in PIO transfer mode"); + return FSDIO_ERR_INVALID_STATE; + } + + /* for removable media, check if card exists */ + if ((FALSE == instance_p->config.non_removable) && + (FALSE == FSdioCheckIfCardExists(base_addr))) + { + FSDIO_ERROR("card not detected !!!"); + return FSDIO_ERR_NO_CARD; + } + + /* reset fifo and not use DMA */ + FSDIO_CLR_BIT(base_addr, FSDIO_CNTRL_OFFSET, FSDIO_CNTRL_USE_INTERNAL_DMAC); + ret = FSdioResetCtrl(base_addr, FSDIO_CNTRL_FIFO_RESET); + if (FSDIO_SUCCESS != ret) + return ret; + FSDIO_CLR_BIT(base_addr, FSDIO_BUS_MODE_OFFSET, FSDIO_BUS_MODE_DE); + + if (NULL != cmd_data_p->data_p) + { + /* set transfer data length and block size */ + FSdioSetTransBytes(base_addr, cmd_data_p->data_p->datalen); + FSdioSetBlockSize(base_addr, cmd_data_p->data_p->blksz); + + if (FALSE == read) /* if need to write, write to fifo before send command */ + { + /* invalide buffer for data to write */ + FCacheDCacheInvalidateRange((uintptr)cmd_data_p->data_p->buf, + cmd_data_p->data_p->datalen); + + ret = FSdioPIOWriteData(instance_p, cmd_data_p->data_p); + } + } + + if (FSDIO_SUCCESS == ret) /* send command */ + { + ret = FSdioTransferCmd(instance_p, cmd_data_p); + } + + return ret; +} + +/** + * @name: FSdioPollWaitPIOEnd + * @msg: Wait PIO transfer finished by poll + * @return {FError} FSDIO_SUCCESS if wait success, otherwise wait failed + * @param {FSdio} *instance_p, SDIO controller instance + * @param {FSdioCmdData} *cmd_data_p, contents of transfer command and data + * @param {FSdioRelaxHandler} relax, handler of relax when wait busy + */ +FError FSdioPollWaitPIOEnd(FSdio *const instance_p, FSdioCmdData *const cmd_data_p, FSdioRelaxHandler relax) +{ + FASSERT(instance_p); + FASSERT(cmd_data_p); + FError ret = FSDIO_SUCCESS; + u32 loop; + u32 reg_val; + int delay; + const boolean read = cmd_data_p->flag & FSDIO_CMD_FLAG_READ_DATA; + uintptr base_addr = instance_p->config.base_addr; + + if (FT_COMPONENT_IS_READY != instance_p->is_ready) + { + FSDIO_ERROR("device is not yet initialized!!!"); + return FSDIO_ERR_NOT_INIT; + } + + if (FSDIO_PIO_TRANS_MODE != instance_p->config.trans_mode) + { + FSDIO_ERROR("device is not configure in PIO transfer mode"); + return FSDIO_ERR_INVALID_STATE; + } + + FSDIO_INFO("wait for PIO cmd to finish ..."); + delay = FSDIO_TIMEOUT; + do + { + reg_val = FSdioGetRawStatus(base_addr); + if (relax) + relax(); + } + while (!(FSDIO_INT_CMD_BIT & reg_val) && (--delay > 0)); + + if (!(FSDIO_INT_CMD_BIT & reg_val) && (delay <= 0)) + { + FSDIO_ERROR("wait cmd done timeout, raw ints: 0x%x", reg_val); + return FSDIO_ERR_CMD_TIMEOUT; + } + + /* if need to read data, read fifo after send command */ + if ((NULL != cmd_data_p->data_p) && (read)) + { + FSDIO_INFO("wait for PIO data to read ..."); + delay = FSDIO_TIMEOUT; + do + { + reg_val = FSdioGetRawStatus(base_addr); + if (relax) + relax(); + } + while (!(FSDIO_INT_DTO_BIT & reg_val) && (--delay > 0)); + + /* clear status to ack */ + FSdioClearRawStatus(base_addr); + FSDIO_INFO("card cnt: 0x%x, fifo cnt: 0x%x", + FSDIO_READ_REG(base_addr, FSDIO_TRAN_CARD_CNT_OFFSET), + FSDIO_READ_REG(base_addr, FSDIO_TRAN_FIFO_CNT_OFFSET)); + + if (!(FSDIO_INT_DTO_BIT & reg_val) && (delay <= 0)) + { + FSDIO_ERROR("wait PIO transfer timeout, raw ints: 0x%x", reg_val); + return FSDIO_ERR_TRANS_TIMEOUT; + } + } + + /* clear status to ack cmd done */ + FSdioClearRawStatus(base_addr); + + if (FSDIO_SUCCESS == ret) + { + ret = FSdioGetCmdResponse(instance_p, cmd_data_p); + } + + return ret; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio_selftest.c b/bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio_selftest.c new file mode 100644 index 0000000000..34e8b8520a --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio_selftest.c @@ -0,0 +1,103 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsdio_selftest.c + * Date: 2022-06-02 11:49:44 + * LastEditTime: 2022-06-02 11:49:45 + * Description:  This files is for SDIO self-test function + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.1 zhugengyu 2022/6/6 modify according to tech manual. + */ +/***************************** Include Files *********************************/ + +#include "fio.h" +#include "fdebug.h" +#include "fassert.h" +#include "ftypes.h" + +#include "fsdio_hw.h" +#include "fsdio.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FSDIO_DEBUG_TAG "FSDIO-TEST" +#define FSDIO_ERROR(format, ...) FT_DEBUG_PRINT_E(FSDIO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSDIO_WARN(format, ...) FT_DEBUG_PRINT_W(FSDIO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSDIO_INFO(format, ...) FT_DEBUG_PRINT_I(FSDIO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSDIO_DEBUG(format, ...) FT_DEBUG_PRINT_D(FSDIO_DEBUG_TAG, format, ##__VA_ARGS__) + +#define FSDIO_DUMPER(base_addr, reg_off, reg_name) \ + FSDIO_DEBUG("\t\t[%s]@0x%x\t=\t0x%x", reg_name, (reg_off), FSDIO_READ_REG((base_addr), (reg_off))) +/************************** Function Prototypes ******************************/ + +/*****************************************************************************/ + +/** + * @name: FSdioDumpRegister + * @msg: Dump all register value of SDIO instance + * @return {NONE} + * @param {uintptr} base_addr, base address of SDIO controller + */ +void FSdioDumpRegister(uintptr base_addr) +{ + FSDIO_DEBUG("Dump register info @0x%x", base_addr); + FSDIO_DUMPER(base_addr, FSDIO_CNTRL_OFFSET, "cntrl"); + FSDIO_DUMPER(base_addr, FSDIO_PWREN_OFFSET, "pwren"); + FSDIO_DUMPER(base_addr, FSDIO_CLKDIV_OFFSET, "clkdiv"); + FSDIO_DUMPER(base_addr, FSDIO_CLKENA_OFFSET, "clkena"); + FSDIO_DUMPER(base_addr, FSDIO_TMOUT_OFFSET, "tmout"); + FSDIO_DUMPER(base_addr, FSDIO_CTYPE_OFFSET, "ctype"); + FSDIO_DUMPER(base_addr, FSDIO_BLK_SIZ_OFFSET, "blksz"); + FSDIO_DUMPER(base_addr, FSDIO_BYT_CNT_OFFSET, "blkcnt"); + FSDIO_DUMPER(base_addr, FSDIO_INT_MASK_OFFSET, "intmask"); + FSDIO_DUMPER(base_addr, FSDIO_CMD_ARG_OFFSET, "cmdarg"); + FSDIO_DUMPER(base_addr, FSDIO_CMD_OFFSET, "cmd"); + FSDIO_DUMPER(base_addr, FSDIO_RESP0_OFFSET, "resp0"); + FSDIO_DUMPER(base_addr, FSDIO_RESP1_OFFSET, "reps1"); + FSDIO_DUMPER(base_addr, FSDIO_RESP2_OFFSET, "resp2"); + FSDIO_DUMPER(base_addr, FSDIO_RESP3_OFFSET, "resp3"); + FSDIO_DUMPER(base_addr, FSDIO_MASKED_INTS_OFFSET, "maskints"); + FSDIO_DUMPER(base_addr, FSDIO_RAW_INTS_OFFSET, "rawints"); + FSDIO_DUMPER(base_addr, FSDIO_STATUS_OFFSET, "status"); + FSDIO_DUMPER(base_addr, FSDIO_FIFOTH_OFFSET, "fifoth"); + FSDIO_DUMPER(base_addr, FSDIO_CARD_DETECT_OFFSET, "carddet"); + FSDIO_DUMPER(base_addr, FSDIO_CARD_WRTPRT_OFFSET, "wrtprt"); + FSDIO_DUMPER(base_addr, FSDIO_GPIO_OFFSET, "gpio"); + FSDIO_DUMPER(base_addr, FSDIO_TRAN_CARD_CNT_OFFSET, "trans_cardcnt"); + FSDIO_DUMPER(base_addr, FSDIO_TRAN_FIFO_CNT_OFFSET, "trans_fifocnt"); + FSDIO_DUMPER(base_addr, FSDIO_DEBNCE_OFFSET, "debnce"); + FSDIO_DUMPER(base_addr, FSDIO_UID_OFFSET, "uid"); + FSDIO_DUMPER(base_addr, FSDIO_VID_OFFSET, "vid"); + FSDIO_DUMPER(base_addr, FSDIO_HWCONF_OFFSET, "hwconf"); + FSDIO_DUMPER(base_addr, FSDIO_UHS_REG_OFFSET, "uhsreg"); + FSDIO_DUMPER(base_addr, FSDIO_CARD_RESET_OFFSET, "cardreset"); + FSDIO_DUMPER(base_addr, FSDIO_BUS_MODE_OFFSET, "busmode"); + FSDIO_DUMPER(base_addr, FSDIO_DESC_LIST_ADDRL_OFFSET, "descaddrl"); + FSDIO_DUMPER(base_addr, FSDIO_DESC_LIST_ADDRH_OFFSET, "descaddrh"); + FSDIO_DUMPER(base_addr, FSDIO_DMAC_STATUS_OFFSET, "dmacstatus"); + FSDIO_DUMPER(base_addr, FSDIO_DMAC_INT_EN_OFFSET, "dmacinten"); + FSDIO_DUMPER(base_addr, FSDIO_CUR_DESC_ADDRL_OFFSET, "curdescaddrl"); + FSDIO_DUMPER(base_addr, FSDIO_CUR_DESC_ADDRH_OFFSET, "curdescaddrh"); + FSDIO_DUMPER(base_addr, FSDIO_CUR_BUF_ADDRL_OFFSET, "curbufaddrl"); + FSDIO_DUMPER(base_addr, FSDIO_CUR_BUF_ADDRH_OFFSET, "curbufaddrh"); + FSDIO_DUMPER(base_addr, FSDIO_CARD_THRCTL_OFFSET, "card_thrctl"); + FSDIO_DUMPER(base_addr, FSDIO_UHS_REG_EXT_OFFSET, "uhsregext"); + FSDIO_DUMPER(base_addr, FSDIO_EMMC_DDR_REG_OFFSET, "emmcddr"); + FSDIO_DUMPER(base_addr, FSDIO_ENABLE_SHIFT_OFFSET, "enableshift"); +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio_sinit.c b/bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio_sinit.c new file mode 100644 index 0000000000..3111024e72 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/mmc/fsdio/fsdio_sinit.c @@ -0,0 +1,64 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsdio_sinit.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:48:41 + * Description:  This files is for static init + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2021/12/2 init + * 1.1 zhugengyu 2022/6/6 modify according to tech manual. + */ + +/***************************** Include Files *********************************/ + +#include "ftypes.h" +#include "fparameters.h" +#include "fsdio.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Variable Definitions *****************************/ + +extern const FSdioConfig FSDIO_CONFIG_TBL[FSDIO_HOST_INSTANCE_NUM]; + +/************************** Function Prototypes ******************************/ +/** + * @name: FSdioLookupConfig + * @msg: Get the device instance default configure + * @return {const FSdioConfig *} default configure + * @param {u32} instance_id + */ +const FSdioConfig *FSdioLookupConfig(u32 instance_id) +{ + const FSdioConfig *ptr = NULL; + u32 index; + + for (index = 0; index < (u32)FSDIO_HOST_INSTANCE_NUM; index++) + { + if (FSDIO_CONFIG_TBL[index].instance_id == instance_id) + { + ptr = &FSDIO_CONFIG_TBL[index]; + break; + } + } + + return (const FSdioConfig *)ptr; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc.c b/bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc.c new file mode 100644 index 0000000000..cb15f01464 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc.c @@ -0,0 +1,389 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsdmmc.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:55:23 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2021/12/2 init + */ + +/***************************** Include Files *********************************/ +#include + +#include "fassert.h" +#include "fio.h" +#include "ferror_code.h" +#include "ftypes.h" +#include "fdebug.h" + +#include "fcache.h" +#include "fsleep.h" + +#include "fsdmmc_hw.h" +#include "fsdmmc.h" +#include "fsdmmc_dma.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FSDMMC_DEBUG_TAG "FSDMMC" +#define FSDMMC_ERROR(format, ...) FT_DEBUG_PRINT_E(FSDMMC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSDMMC_WARN(format, ...) FT_DEBUG_PRINT_W(FSDMMC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSDMMC_INFO(format, ...) FT_DEBUG_PRINT_I(FSDMMC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSDMMC_DEBUG(format, ...) FT_DEBUG_PRINT_D(FSDMMC_DEBUG_TAG, format, ##__VA_ARGS__) + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + +/*****************************************************************************/ + +/* 此文件主要为了完成用户对外接口,用户可以使用这些接口直接开始工作 */ + +/* - 包括用户API的定义和实现 + - 同时包含必要的OPTION方法,方便用户进行配置 + - 如果驱动可以直接进行I/O操作,在此源文件下可以将API 进行实现 */ + +/** + * @name: FSdmmcCfgInitialize + * @msg: 初始化FSDMMC控制器, 使之可以使用 + * @return {FError} 驱动初始化的错误码信息,FSDMMC_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + * @param {FSdmmc} *instance_p FSDMMC驱动控制数据 + * @param {FSdmmcConfig} *input_config_p FSDMMC用户输入配置 + * @note 输入配置通过FSdmmcLookupConfig获取,用户按照需要修改后传入此函数 + */ +FError FSdmmcCfgInitialize(FSdmmc *instance_p, const FSdmmcConfig *input_config_p) +{ + FASSERT(instance_p && input_config_p); + uintptr base_addr; + FError ret = FSDMMC_SUCCESS; + + /* + * If the device is started, disallow the initialize and return a Status + * indicating it is started. This allows the user to de-initialize the device + * and reinitialize, but prevents a user from inadvertently + * initializing. + */ + if (FT_COMPONENT_IS_READY == instance_p->is_ready) + { + FSDMMC_WARN("device is already initialized!!!"); + } + + /* + * Set default values and configuration data, including setting the + * callback handlers to stubs so the system will not crash should the + * application not assign its own callbacks. + */ + FSdmmcDeInitialize(instance_p); + + instance_p->config = *input_config_p; + base_addr = instance_p->config.base_addr; + + /* + * Check if card exists + */ + if (!FSdmmcCheckIfCardExists(base_addr)) + { + FSDMMC_ERROR("storage device not found !!! 0x%x", base_addr); + return FSDMMC_ERR_CARD_NO_FOUND; + } + + /* + * Reset the device. + */ + ret = FSdmmcReset(base_addr); + if (FSDMMC_SUCCESS == ret) + instance_p->is_ready = FT_COMPONENT_IS_READY; + + return ret; +} + +/** + * @name: FSdmmcDeInitialize + * @msg: 去使能FSDMMC控制器, 清零实例数据 + * @return {*} + * @param {FSdmmc} *instance_p FSDMMC驱动控制数据 + */ +void FSdmmcDeInitialize(FSdmmc *instance_p) +{ + FASSERT(instance_p); + + instance_p->is_ready = 0; + memset(instance_p, 0, sizeof(*instance_p)); + + return; +} + +/** + * @name: FSdmmcMakeRawCmd + * @msg: 组装生成待发送的命令 + * @return {*} + * @param {FSdmmcCmd} *cmd_p 命令控制数据 + */ +u32 FSdmmcMakeRawCmd(FSdmmcCmd *cmd_p) +{ + FASSERT(cmd_p); + u32 raw_cmd = 0; + + /* + * rawcmd : + * trty << 14 | opcode << 8 | cmdw << 6 | cice << 4 | crce << 3 | resp + */ + raw_cmd |= FSDMMC_CMD_SETTING_CMDI(cmd_p->cmdidx); + + if (cmd_p->flag & FSDMMC_CMD_FLAG_ADTC) + raw_cmd |= FSDMMC_CMD_SETTING_TRTY(0b10); /* adtc指令 */ + + if (0 == (cmd_p->flag & FSDMMC_CMD_FLAG_EXP_RESP)) + raw_cmd |= FSDMMC_CMD_NO_RESP; + else if (cmd_p->flag & FSDMMC_CMD_FLAG_EXP_LONG_RESP) + raw_cmd |= FSDMMC_CMD_RESP_136_BIT; + else + raw_cmd |= FSDMMC_CMD_RESP_48_BIT; + + return raw_cmd; +} + +/** + * @name: FSdmmcWaitCmdEnd + * @msg: 阻塞等待命令发送完成,获取命令返回的响应 + * @return {FError} FSDMMC_SUCCESS表示命令发送成功,其它表示命令发送失败 + * @param {uintptr} base_addr FSDMMC控制器基地址 + * @param {FSdmmcCmd} *cmd_p 命令控制数据 + */ +static FError FSdmmcWaitCmdEnd(uintptr base_addr, FSdmmcCmd *cmd_p) +{ + FASSERT(cmd_p); + FError ret = FSDMMC_SUCCESS; + + ret = FSdmmcWaitStatus(base_addr, FSDMMC_TIMEOUT); + if (FSDMMC_SUCCESS != ret) + return ret; + + if (cmd_p->flag & FSDMMC_CMD_FLAG_EXP_RESP) + { + if (cmd_p->flag & FSDMMC_CMD_FLAG_EXP_LONG_RESP) + { + cmd_p->response[0] = FSDMMC_READ_REG(base_addr, FSDMMC_CMD_RESP_1_REG_OFFSET); + cmd_p->response[1] = FSDMMC_READ_REG(base_addr, FSDMMC_CMD_RESP_2_REG_OFFSET); + cmd_p->response[2] = FSDMMC_READ_REG(base_addr, FSDMMC_CMD_RESP_3_REG_OFFSET); + cmd_p->response[3] = FSDMMC_READ_REG(base_addr, FSDMMC_CMD_RESP_4_REG_OFFSET); + } + else + { + cmd_p->response[0] = FSDMMC_READ_REG(base_addr, FSDMMC_CMD_RESP_1_REG_OFFSET); + cmd_p->response[1] = 0; + cmd_p->response[2] = 0; + cmd_p->response[3] = 0; + } + } + + FSDMMC_INFO("get cmd resp: 0x%x:0x%x:0x%x:0x%x", + cmd_p->response[0], + cmd_p->response[1], + cmd_p->response[2], + cmd_p->response[3]); + + return FSDMMC_SUCCESS; +} + +/** + * @name: FSdmmcSendCmd + * @msg: 发送命令 + * @return {FError} FSDMMC_SUCCESS表示命令发送成功,其它表示命令发送失败 + * @param {uintptr} base_addr FSDMMC控制器基地址 + * @param {FSdmmcCmd} *cmd_p 命令控制数据 + */ +void FSdmmcSendCmd(uintptr base_addr, FSdmmcCmd *cmd_p) +{ + FASSERT(cmd_p); + u32 raw_cmd = FSdmmcMakeRawCmd(cmd_p); + + FSdmmcSendPrivateCmd(base_addr, raw_cmd, cmd_p->cmdarg); +} + +/** + * @name: FSdmmcTransferCmdPoll + * @msg: 通过FSDMMC轮询方式发送命令,阻塞等待命令返回 + * @return {*} + * @param {uintptr} base_addr FSDMMC控制器基地址 + * @param {FSdmmcCmd} *cmd_p 命令控制数据 + */ +static FError FSdmmcTransferCmdPoll(uintptr base_addr, FSdmmcCmd *cmd_p) +{ + FASSERT(cmd_p); + FSdmmcSendCmd(base_addr, cmd_p); + return FSdmmcWaitCmdEnd(base_addr, cmd_p); +} + +/** + * @name: FSdmmcSendAdtcCmd + * @msg: 发送ADTC命令 + * @return {*} + * @param {uintptr} base_addr FSDMMC控制器基地址 + * @param {FSdmmcCmd} *cmd_p 命令控制数据 + */ +static void FSdmmcSendAdtcCmd(uintptr base_addr, FSdmmcCmd *cmd_p) +{ + FASSERT(cmd_p); + u32 raw_cmd = FSdmmcMakeRawCmd(cmd_p); + + FSdmmcClearNormalInterruptStatus(base_addr); + raw_cmd |= FSDMMC_CMD_SETTING_TRTY(0b10); /* adtc指令 */ + FSDMMC_WRITE_REG(base_addr, FSDMMC_CMD_SETTING_REG_OFFSET, raw_cmd); + return; +} + +/** + * @name: FSdmmcSendData + * @msg: 发送数据 + * @return {*} + * @param {uintptr} base_addr FSDMMC控制器基地址 + * @param {boolean} read TREU: 读数据 FALSE: 写数据 + * @param {FSdmmcCmd} *cmd_p 命令控制数据 + */ +FError FSdmmcSendData(uintptr base_addr, boolean read, FSdmmcCmd *cmd_p) +{ + FASSERT(cmd_p); + FSdmmcData *dat_p = cmd_p->data_p; + u32 card_addr; + u32 blk_cnt; + FError ret = FSDMMC_SUCCESS; + + if ((dat_p->datalen >= FSDMMC_DMA_ADDR_ALIGN) && (dat_p->datalen % FSDMMC_DMA_ADDR_ALIGN != 0)) + { + FSDMMC_ERROR("invalid size: total = %d ", dat_p->datalen); + return FSDMMC_ERR_INVALID_BUF; + } + + if (((uintptr)(dat_p->buf) % FSDMMC_DMA_ADDR_ALIGN) != 0) + { + FSDMMC_ERROR("buffer %p can not be used for DMA", dat_p->buf); + return FSDMMC_ERR_INVALID_BUF; + } + + card_addr = cmd_p->cmdarg; + blk_cnt = dat_p->datalen / dat_p->blksz; + if (dat_p->datalen % dat_p->blksz) + blk_cnt++; + + FSDMMC_INFO("data len: %d, card addr: 0x%x, blk cnt: %d, is %s", + dat_p->datalen, card_addr, blk_cnt, read ? "read" : "write"); + + if (read) + { + if ((cmd_p->flag & FSDMMC_CMD_FLAG_ADTC) && (dat_p->blksz > dat_p->datalen)) + { + FSdmmcSendAdtcCmd(base_addr, cmd_p); + } + /* read data */ + FSdmmcSetReadDMA(base_addr, (uintptr)card_addr, blk_cnt, dat_p->buf); + } + else + { + /* invalidate write buf */ + FCacheDCacheInvalidateRange((uintptr)dat_p->buf, dat_p->datalen); + + /* write data */ + FSdmmcSetWriteDMA(base_addr, (uintptr)card_addr, blk_cnt, dat_p->buf); + } + + return ret; +} + +/** + * @name: FSdmmcTransferDataPoll + * @msg: 通过FSDMMC轮询方式发送数据,阻塞等待数据返回 + * @return {*} + * @param {uintptr} base_addr FSDMMC控制器基地址 + * @param {FSdmmcCmd} *cmd_p 待发送数据 + */ +static FError FSdmmcTransferDataPoll(uintptr base_addr, FSdmmcCmd *cmd_p) +{ + FASSERT(cmd_p); + FError ret = FSDMMC_SUCCESS; + FSdmmcData *dat_p = cmd_p->data_p; + const boolean read = (FSDMMC_CMD_FLAG_READ_DATA == (cmd_p->flag & FSDMMC_CMD_FLAG_READ_DATA)); + + ret = FSdmmcSendData(base_addr, read, cmd_p); + if (FSDMMC_SUCCESS != ret) + return ret; + + ret = FSdmmcWaitCmdEnd(base_addr, cmd_p); + if (FSDMMC_SUCCESS != ret) + return ret; + + ret = FSdmmcWaitDMAStatus(base_addr, read, FSDMMC_TIMEOUT); + if (FSDMMC_SUCCESS != ret) + return ret; + + FCacheDCacheInvalidateRange((uintptr)dat_p->buf, dat_p->datalen); + return ret; +} + +/** + * @name: FSdmmcPollTransfer + * @msg: 通过FSDMMC轮询方式发送/接收数据和命令 + * @return {FError} 驱动初始化的错误码信息,FSDMMC_SUCCESS 表示发送/接收成功,其它返回值表示发送/接收失败 + * @param {FSdmmc} *instance_p FSDMMC驱动控制数据 + * @param {FSdmmcCmd} *cmd_data_p FSDMMC数据和命令 + * @note FSDMMC控制器初始化后才能调用此函数 + */ +FError FSdmmcPollTransfer(FSdmmc *instance_p, FSdmmcCmd *cmd_data_p) +{ + FASSERT(instance_p && cmd_data_p); + uintptr base_addr = instance_p->config.base_addr; + FError ret = FSDMMC_SUCCESS; + + if (FALSE == FSdmmcCheckIfCardExists(base_addr)) + { + FSDMMC_ERROR("card not found !!! fsdio ctrl base 0x%x", base_addr); + return FSDMMC_ERR_CARD_NO_FOUND; + } + + if (cmd_data_p->flag & FSDMMC_CMD_FLAG_EXP_DATA) + { + /* transfer data */ + FSDMMC_INFO("====DATA [%d] START: buf: %p=====", cmd_data_p->cmdidx, cmd_data_p->data_p->buf); + ret = FSdmmcTransferDataPoll(base_addr, cmd_data_p); + if (FSDMMC_SUCCESS != ret) + { + FSDMMC_ERROR("trans data failed 0x%x", ret); + return ret; + } + + FSDMMC_INFO("====DATA [%d] END 0x%x=====", cmd_data_p->cmdidx, ret); + } + else + { + /* transfer command */ + FSDMMC_INFO("=====CMD [%d] START=====", cmd_data_p->cmdidx); + ret = FSdmmcTransferCmdPoll(base_addr, cmd_data_p); + if (FSDMMC_SUCCESS != ret) + { + FSDMMC_ERROR("send cmd failed 0x%x", ret); + return ret; + } + + FSDMMC_INFO("=====CMD [%d] END: 0x%x=====", cmd_data_p->cmdidx, ret); + } + + return ret; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc.h b/bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc.h new file mode 100644 index 0000000000..25573e68af --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc.h @@ -0,0 +1,173 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsdmmc.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:55:57 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2021/12/2 init + */ + +#ifndef DRIVERS_MMC_FSDMMC_H +#define DRIVERS_MMC_FSDMMC_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ + +#include "ftypes.h" +#include "ferror_code.h" +#include "fkernel.h" + +/************************** Constant Definitions *****************************/ +#define FSDMMC_SUCCESS FT_SUCCESS +#define FSDMMC_ERR_NOT_READY FT_MAKE_ERRCODE(ErrModBsp, ErrBspMmc, 1) +#define FSDMMC_ERR_TIMEOUT FT_MAKE_ERRCODE(ErrModBsp, ErrBspMmc, 2) +#define FSDMMC_ERR_CMD_FAILED FT_MAKE_ERRCODE(ErrModBsp, ErrBspMmc, 3) +#define FSDMMC_ERR_DATA_FAILED FT_MAKE_ERRCODE(ErrModBsp, ErrBspMmc, 4) +#define FSDMMC_ERR_CARD_NO_FOUND FT_MAKE_ERRCODE(ErrModBsp, ErrBspMmc, 5) +#define FSDMMC_ERR_INVALID_BUF FT_MAKE_ERRCODE(ErrModBsp, ErrBspMmc, 6) + +/**************************** Type Definitions *******************************/ +enum +{ + FSDMMC_DMA_BD_INTR = 0, + FSDMMC_CMD_INTR, + FSDMMC_ERROR_INTR, + + FSDMMC_INTR_NUM +}; /* 中断类型 */ + +enum +{ + FSDMMC_EVT_CARD_REMOVED = 0, + FSDMMC_EVT_CMD_DONE, + FSDMMC_EVT_CMD_ERROR, + FSDMMC_EVT_CMD_RESP_ERROR, + FSDMMC_EVT_DATA_ERROR, + FSDMMC_EVT_DATA_READ_DONE, + FSDMMC_EVT_DATA_WRITE_DONE, + + FSDMMC_EVT_NUM +}; /* 事件类型 */ + +/** + * This typedef contains data information for the device. + */ +typedef struct +{ + u8 *buf; + u32 blksz; + u32 blkcnt; + u32 datalen; +} FSdmmcData; + +/** + * This typedef contains command information for the device. + */ +typedef struct +{ + u32 cmdidx; + u32 cmdarg; + u32 resptype; + u32 response[4]; + u32 flag; +#define FSDMMC_CMD_FLAG_NEED_STOP BIT(0) +#define FSDMMC_CMD_FLAG_NEED_INIT BIT(1) +#define FSDMMC_CMD_FLAG_EXP_RESP BIT(2) +#define FSDMMC_CMD_FLAG_EXP_LONG_RESP BIT(3) +#define FSDMMC_CMD_FLAG_NEED_RESP_CRC BIT(4) +#define FSDMMC_CMD_FLAG_EXP_DATA BIT(5) +#define FSDMMC_CMD_FLAG_WRITE_DATA BIT(6) +#define FSDMMC_CMD_FLAG_READ_DATA BIT(7) +#define FSDMMC_CMD_FLAG_NEED_AUTO_STOP BIT(8) +#define FSDMMC_CMD_FLAG_ADTC BIT(9) + FSdmmcData *data_p; +} FSdmmcCmd; + +/** + * This typedef contains configuration information for the device. + */ +typedef struct +{ + u32 instance_id; /* Device instance id */ + uintptr base_addr; /* Device base address */ + u32 irq_num[FSDMMC_INTR_NUM]; +} FSdmmcConfig; + +typedef void (*FSdmmcEventHandler)(void *instance_p); + +/** + * This typedef contains driver instance data. The user is required to allocate a + * variable of this type for every device in the system. A pointer + * to a variable of this type is then passed to the driver API functions. + */ +typedef struct +{ + FSdmmcConfig config; /* Current active configs */ + u32 is_ready; /* Device is initialized and ready */ + FSdmmcEventHandler evt_handler[FSDMMC_EVT_NUM]; +} FSdmmc; /* Device instance */ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/* 获取FSDMMC控制器默认配置 */ +const FSdmmcConfig *FSdmmcLookupConfig(u32 instance_id); + +/* 初始化FSDMMC控制器, 使之可以使用 */ +FError FSdmmcCfgInitialize(FSdmmc *instance_p, const FSdmmcConfig *cofig_p); + +/* 去使能FSDMMC控制器, 清零实例数据 */ +void FSdmmcDeInitialize(FSdmmc *instance_p); + +/* 通过FSDMMC轮询方式发送/接收数据和命令 */ +FError FSdmmcPollTransfer(FSdmmc *instance_p, FSdmmcCmd *cmd_data_p); + +/* 通过FSDMMC中断方式发送/接收数据和命令 */ +FError FSdmmcInterruptTransfer(FSdmmc *instance_p, FSdmmcCmd *cmd_data_p); + +/* 获取FSDMMC的中断掩码 */ +u32 FSdmmcGetInterruptMask(uintptr base_addr, u32 intr_type); + +/* 设置FSDMMC的中断掩码 */ +void FSdmmcSetInterruptMask(uintptr base_addr, u32 intr_type, u32 mask, boolean enable); + +/* 命令中断响应函数 */ +void FSdmmcCmdInterrupHandler(s32 vector, void *param); + +/* 错误中断响应函数 */ +void FSdmmcErrInterrupHandler(s32 vector, void *param); + +/* DMA中断响应函数 */ +void FSdmmcDmaInterrupHandler(s32 vector, void *param); + +/* 注册中断事件响应函数 */ +void FSdmmcRegisterInterruptHandler(FSdmmc *instance_p, u32 event, FSdmmcEventHandler handler); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc_dma.c b/bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc_dma.c new file mode 100644 index 0000000000..d2c4da00c4 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc_dma.c @@ -0,0 +1,142 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsdmmc_dma.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:49:31 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2021/12/2 init + */ + +/***************************** Include Files *********************************/ +#include "fassert.h" +#include "fio.h" +#include "fdebug.h" + +#include "fsdmmc_hw.h" +#include "fsdmmc.h" +#include "fsdmmc_dma.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FSDMMC_DEBUG_TAG "FSDMMC-DMA" +#define FSDMMC_ERROR(format, ...) FT_DEBUG_PRINT_E(FSDMMC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSDMMC_WARN(format, ...) FT_DEBUG_PRINT_W(FSDMMC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSDMMC_INFO(format, ...) FT_DEBUG_PRINT_I(FSDMMC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSDMMC_DEBUG(format, ...) FT_DEBUG_PRINT_D(FSDMMC_DEBUG_TAG, format, ##__VA_ARGS__) + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + +/*****************************************************************************/ +/** + * @name: FSdmmcSetReadDMA + * @msg: 设置读数据DMA配置 + * @return {*} + * @param {uintptr} base_addr FSDMMC 控制器基地址 + * @param {uintptr} card_addr 读卡地址 + * @param {u32} blk_cnt 读卡的block数 + * @param {void} *buf_p 读卡的目的地址 + */ +void FSdmmcSetReadDMA(uintptr base_addr, uintptr card_addr, u32 blk_cnt, void *buf_p) +{ + FASSERT(buf_p); + + u32 dsth = UPPER_32_BITS((uintptr)buf_p); /* DMA传输目的地址--> sd read buf */ + u32 dstl = LOWER_32_BITS((uintptr)buf_p); + u32 srch = UPPER_32_BITS((uintptr)card_addr); /* DMA传输源地址 --> sd card */ + u32 srcl = LOWER_32_BITS((uintptr)card_addr); + + FSDMMC_INFO("sd card: 0x%x:0x%x ==> mem space: 0x%x:0x%x", + srch, srcl, dsth, dstl); + + FSDMMC_INFO("read %d blks from 0x%x", blk_cnt, card_addr); + + /* DMA 复位 */ + FSDMMC_SET_BIT(base_addr, FSDMMC_SOFTWARE_RESET_REG_OFFSET, FSDMMC_SOFTWARE_RESET_BDRST); + FSDMMC_CLR_BIT(base_addr, FSDMMC_SOFTWARE_RESET_REG_OFFSET, FSDMMC_SOFTWARE_RESET_BDRST); + + /* 设置传输块数目 */ + FSDMMC_WRITE_REG(base_addr, FSDMMC_BLK_CNT_REG_OFFSET, blk_cnt); + + /* 清除状态寄存器 */ + FSdmmcClearErrorInterruptStatus(base_addr); + FSdmmcClearBDInterruptStatus(base_addr); + FSdmmcClearNormalInterruptStatus(base_addr); + + FSDMMC_INFO("base addr: 0x%x buf_p: %p", base_addr, buf_p); + + /* DMA 读卡地址配置:4 个 cycle + 系统低 4B-系统高 4B-SD 低 4B- SD 高 4B */ + FSDMMC_WRITE_REG(base_addr, FSDMMC_DAT_IN_M_RX_BD_REG_OFFSET, dstl); + FSDMMC_WRITE_REG(base_addr, FSDMMC_DAT_IN_M_RX_BD_REG_OFFSET, dsth); + FSDMMC_WRITE_REG(base_addr, FSDMMC_DAT_IN_M_RX_BD_REG_OFFSET, srcl); + FSDMMC_WRITE_REG(base_addr, FSDMMC_DAT_IN_M_RX_BD_REG_OFFSET, srch); + + + FSDMMC_INFO("DMA READ START!"); + return; +} + +/** + * @name: FSdmmcSetWriteDMA + * @msg: 设置写数据DMA配置 + * @return {*} + * @param {uintptr} base_addr FSDMMC 控制器基地址 + * @param {uintptr} card_addr 写卡地址 + * @param {u32} blk_cnt 写卡的block数 + * @param {void} *buf_p 写卡的源地址 + */ +void FSdmmcSetWriteDMA(uintptr base_addr, uintptr card_addr, u32 blk_cnt, const void *buf_p) +{ + FASSERT(buf_p); + u32 srch = UPPER_32_BITS((uintptr)buf_p); /* DMA传输源地址--> sd read buf */ + u32 srcl = LOWER_32_BITS((uintptr)buf_p); + u32 dsth = UPPER_32_BITS((uintptr)card_addr); /* DMA传输目的地址 --> sd card */ + u32 dstl = LOWER_32_BITS((uintptr)card_addr); + + FSDMMC_INFO("mem space: 0x%x:0x%x ==> sd card: 0x%x:0x%x", + srch, srcl, dsth, dstl); + + FSDMMC_INFO("write %d blks from 0x%x", blk_cnt, card_addr); + + /* DMA 复位 */ + FSDMMC_SET_BIT(base_addr, FSDMMC_SOFTWARE_RESET_REG_OFFSET, FSDMMC_SOFTWARE_RESET_BDRST); + FSDMMC_CLR_BIT(base_addr, FSDMMC_SOFTWARE_RESET_REG_OFFSET, FSDMMC_SOFTWARE_RESET_BDRST); + + /* 设置传输块数目 */ + FSDMMC_WRITE_REG(base_addr, FSDMMC_BLK_CNT_REG_OFFSET, blk_cnt); + + /* 清除状态寄存器 */ + FSdmmcClearErrorInterruptStatus(base_addr); + FSdmmcClearBDInterruptStatus(base_addr); + FSdmmcClearNormalInterruptStatus(base_addr); + + /* DMA 写卡地址配置:4 个 cycle + 系统低 4B-系统高 4B-SD 低 4B- SD 高 4B */ + FSDMMC_WRITE_REG(base_addr, FSDMMC_DAT_IN_M_TX_BD_REG_OFFSET, srcl); + FSDMMC_WRITE_REG(base_addr, FSDMMC_DAT_IN_M_TX_BD_REG_OFFSET, srch); + FSDMMC_WRITE_REG(base_addr, FSDMMC_DAT_IN_M_TX_BD_REG_OFFSET, dstl); + FSDMMC_WRITE_REG(base_addr, FSDMMC_DAT_IN_M_TX_BD_REG_OFFSET, dsth); + + FSDMMC_INFO("DMA WRITE START!"); + return; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc_dma.h b/bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc_dma.h new file mode 100644 index 0000000000..06cff5d8f4 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc_dma.h @@ -0,0 +1,54 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsdmmc_dma.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:51:25 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2021/12/2 init + */ + +#ifndef DRIVERS_MMC_FSDMMC_DMA_H +#define DRIVERS_MMC_FSDMMC_DMA_H + +#ifdef __cplusplus +extern "C" +{ +#endif +/***************************** Include Files *********************************/ +#include "ftypes.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ +/* 设置读数据DMA配置 */ +void FSdmmcSetReadDMA(uintptr base_addr, uintptr card_addr, u32 blk_cnt, void *buf_p); + +/* 设置写数据DMA配置 */ +void FSdmmcSetWriteDMA(uintptr base_addr, uintptr card_addr, u32 blk_cnt, const void *buf_p); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc_g.c b/bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc_g.c new file mode 100644 index 0000000000..bef9e3f28d --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc_g.c @@ -0,0 +1,62 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsdmmc_g.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:53:31 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2021/12/2 init + */ + +/* - This file contains a configuration table that specifies the configuration +- 驱动全局变量定义,包括静态配置参数 */ + +/***************************** Include Files *********************************/ + +#include "ftypes.h" +#include "fparameters.h" + +#include "fsdmmc.h" +#include "fsdmmc_hw.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + +const FSdmmcConfig FSDMMC_CONFIG_TBL[FSDMMC_HOST_INSTANCE_NUM] = +{ + [FSDMMC_HOST_INSTANCE_0] = + { + .instance_id = FSDMMC_HOST_INSTANCE_0, /* Id of device*/ + .base_addr = FSDMMC_HOST_0_BASEADDR, + .irq_num = + { + [FSDMMC_CMD_INTR] = FSDMMC_HOST_0_CMD_INTR_IRQ, + [FSDMMC_DMA_BD_INTR] = FSDMMC_HOST_0_DMA_INTR_IRQ, + [FSDMMC_ERROR_INTR] = FSDMMC_HOST_0_ERR_INTR_IRQ + } + } +}; + + +/*****************************************************************************/ diff --git a/bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc_hw.c b/bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc_hw.c new file mode 100644 index 0000000000..155f388561 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc_hw.c @@ -0,0 +1,258 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsdmmc_hw.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:54:02 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2021/12/2 init + */ + +/***************************** Include Files *********************************/ +#include "fassert.h" +#include "fdebug.h" + +#include "fsdmmc_hw.h" +#include "fsdmmc.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FSDMMC_DEBUG_TAG "FSDMMC-HW" +#define FSDMMC_ERROR(format, ...) FT_DEBUG_PRINT_E(FSDMMC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSDMMC_WARN(format, ...) FT_DEBUG_PRINT_W(FSDMMC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSDMMC_INFO(format, ...) FT_DEBUG_PRINT_I(FSDMMC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSDMMC_DEBUG(format, ...) FT_DEBUG_PRINT_D(FSDMMC_DEBUG_TAG, format, ##__VA_ARGS__) + +/************************** Function Prototypes ******************************/ +/** + * @name: FSdmmcSoftwareReset + * @msg: 完成软复位 + * @return {*} + * @param {uintptr} base_addr FSDMMC控制器基地址 + * @param {int} retries 重试次数 + */ +FError FSdmmcSoftwareReset(uintptr base_addr, int retries) +{ + FASSERT(retries > 1); + u32 reg_val; + + FSDMMC_SET_BIT(base_addr, FSDMMC_SOFTWARE_RESET_REG_OFFSET, FSDMMC_SOFTWARE_RESET_SRST); + FSDMMC_CLR_BIT(base_addr, FSDMMC_SOFTWARE_RESET_REG_OFFSET, FSDMMC_SOFTWARE_RESET_SRST); + + do + { + reg_val = FSDMMC_READ_REG(base_addr, FSDMMC_STATUS_REG_OFFSET); + } + while (!(reg_val & FSDMMC_STATUS_IDIE) && + (retries-- > 0)); + + if (!(reg_val & FSDMMC_STATUS_IDIE) && (retries <= 0)) + { + FSDMMC_ERROR("software reset timeout!!! status: 0x%x", reg_val); + return FSDMMC_ERR_TIMEOUT; + } + + return FSDMMC_SUCCESS; +} + +/** + * @name: FSdmmcSetCardClk + * @msg: 设置FSDMMC的时钟 + * @return {*} + * @param {uintptr} base_addr FSDMMC控制器基地址 + * @param {u32} clk_freq_hz 时钟频率,HZ + */ +FError FSdmmcSetCardClk(uintptr base_addr, u32 clk_freq_hz) +{ + FSDMMC_WRITE_REG(base_addr, FSDMMC_CLOCK_DIV_REG_OFFSET, FSDMMC_CLK_DIVIDER(clk_freq_hz)); + FSDMMC_WRITE_REG(base_addr, FSDMMC_SD_DRV_REG_OFFSET, FSDMMC_DEFAULT_DRV); + FSDMMC_WRITE_REG(base_addr, FSDMMC_SD_SAMP_REG_OFFSET, FSDMMC_DEFAULT_SAMP); + + return FSdmmcSoftwareReset(base_addr, FSDMMC_TIMEOUT); +} + +static const char *FSdmmcGetRespTypeStr(u32 hw_cmd) +{ + const char *str; + + switch (FSDMMC_CMD_RESP_MASK & hw_cmd) + { + case FSDMMC_CMD_NO_RESP: + str = "NONE"; + break; + case FSDMMC_CMD_RESP_136_BIT: + str = "LONG"; + break; + case FSDMMC_CMD_RESP_48_BIT: + str = "SHORT"; + break; + case FSDMMC_CMD_RESP_48_BIT_BUSY_CHECK: + str = "SHORT CHECK BUSY"; + break; + default: + FASSERT(0); + } + + return str; +} + +/** + * @name: FSdmmcSendPrivateCmd + * @msg: 发送命令 + * @return {*} + * @param {uintptr} base_addr FSDMMC控制器基地址 + * @param {u32} cmd 待发送的命令 + * @param {u32} arg 待发送命令的参数 + */ +void FSdmmcSendPrivateCmd(uintptr base_addr, u32 cmd, u32 arg) +{ + /* 清空状态寄存器 */ + FSdmmcClearNormalInterruptStatus(base_addr); + FSdmmcClearErrorInterruptStatus(base_addr); + FSdmmcClearBDInterruptStatus(base_addr); + + /* 设置命令 */ + FSDMMC_WRITE_REG(base_addr, FSDMMC_CMD_SETTING_REG_OFFSET, cmd); + + /* 设置参数,同时触发发送命令 */ + FSDMMC_WRITE_REG(base_addr, FSDMMC_ARGUMENT_REG_OFFSET, FSDMMC_ARGUMENT_MASK & arg); + + FSDMMC_INFO("CMD: 0x%08x ", FSDMMC_READ_REG(base_addr, FSDMMC_CMD_SETTING_REG_OFFSET)); + FSDMMC_INFO("ARG: 0x%08x", FSDMMC_READ_REG(base_addr, FSDMMC_ARGUMENT_REG_OFFSET)); + FSDMMC_INFO("RESP: %s", FSdmmcGetRespTypeStr(cmd)); +} + +/** + * @name: FSdmmcReset + * @msg: 重置FSDMMC控制器 + * @return {*} + * @param {uintptr} base_addr FSDMMC控制器基地址 + */ +FError FSdmmcReset(uintptr base_addr) +{ + u32 reg_val; + FError ret = FSDMMC_SUCCESS; + + ret = FSdmmcSoftwareReset(base_addr, FSDMMC_TIMEOUT); + if (FSDMMC_SUCCESS != ret) + return ret; + + /* set card detection */ + FSDMMC_WRITE_REG(base_addr, FSDMMC_SD_SEN_REG_OFFSET, 0x0); + reg_val = FSDMMC_SEN_CREFR_VAL | FSDMMC_SEN_DEBNCE_VAL; + FSDMMC_WRITE_REG(base_addr, FSDMMC_SD_SEN_REG_OFFSET, reg_val); + + /* configure cmd data timeout */ + FSDMMC_WRITE_REG(base_addr, FSDMMC_TIMEOUT_CMD_REG_OFFSET, FSDMMC_CMD_TIMEOUT); + FSDMMC_WRITE_REG(base_addr, FSDMMC_TIMEOUT_DATA_REG_OFFSET, FSDMMC_DATA_TIMEOUT); + + /* handle DMA cache */ + FSDMMC_WRITE_REG(base_addr, FSDMMC_HDS_AXI_REG_CONF1_REG_OFFSET, FSDMMC_AXI_CONF1); + FSDMMC_WRITE_REG(base_addr, FSDMMC_HDS_AXI_REG_CONF2_REG_OFFSET, FSDMMC_AXI_CONF2); + + /* set ending */ + reg_val = FSDMMC_PERMDW_STD_END | FSDMMC_PERMDR_STD_END; + FSDMMC_WRITE_REG(base_addr, FSDMMC_CONTROLL_SETTING_REG_OFFSET, reg_val); + + /* disable interrupt */ + FSDMMC_WRITE_REG(base_addr, FSDMMC_NORMAL_INT_EN_REG_OFFSET, 0x0); + FSDMMC_WRITE_REG(base_addr, FSDMMC_ERROR_INT_EN_REG_OFFSET, 0x0); + FSDMMC_WRITE_REG(base_addr, FSDMMC_BD_ISR_EN_REG_OFFSET, 0x0); + + /* clear interrupr status */ + FSdmmcClearNormalInterruptStatus(base_addr); + FSdmmcClearErrorInterruptStatus(base_addr); + FSdmmcClearBDInterruptStatus(base_addr); + + return ret; +} + +/** + * @name: FSdmmcWaitStatus + * @msg: 等待命令完成或者命令错误状态 + * @return {*} + * @param {uintptr} base_addr FSDMMC控制器基地址 + * @param {int} retries 重试次数 + */ +FError FSdmmcWaitStatus(uintptr base_addr, int retries) +{ + FASSERT(retries > 1); + const u32 status_mask = FSDMMC_NORMAL_INT_STATUS_CC | FSDMMC_NORMAL_INT_STATUS_EI; /* 等待命令完成或者发生错误 */ + u32 status; + + do + { + status = status_mask & FSDMMC_READ_REG(base_addr, FSDMMC_NORMAL_INT_STATUS_REG_OFFSET); + } + while ((!status) && (retries-- > 0)); + + if (FSDMMC_NORMAL_INT_STATUS_EI & status) + { + FSDMMC_ERROR("error status: 0x%x, remain retries: %d", status, retries); + FSdmmcReset(base_addr); + return FSDMMC_ERR_CMD_FAILED; + } + else if (0 >= retries) + { + FSDMMC_ERROR("wait timeout!!! status 0x%x", status); + return FSDMMC_ERR_TIMEOUT; + } + + return FSDMMC_SUCCESS; +} + +/** + * @name: FSdmmcWaitDMAStatus + * @msg: 等待数据传输完成或者传输错误状态 + * @return {*} + * @param {uintptr} base_addr FSDMMC控制器基地址 + * @param {boolean} read TRUE: 当前是读数据 FALSE: 当前是写数据 + * @param {int} retries 重试次数 + */ +FError FSdmmcWaitDMAStatus(uintptr base_addr, boolean read, int retries) +{ + const u32 status_mask = read ? + (FSDMMC_BD_ISR_REG_RESPE | FSDMMC_BD_ISR_REG_DAIS) : /* 等待DMA传输完成或者发生错误 */ + (FSDMMC_BD_ISR_REG_TRS | FSDMMC_BD_ISR_REG_DAIS); /* 等待DMA传输完成或者发生错误 */ + u32 status; + + /* 等待DMA传输完成或者发生错误 */ + do + { + status = status_mask & FSDMMC_READ_REG(base_addr, FSDMMC_BD_ISR_REG_OFFSET); + } + while ((!status) && (retries-- > 0)); + + if (status & FSDMMC_BD_ISR_REG_DAIS) + { + FSDMMC_ERROR("BD Data error when %s blk!", read ? "read" : "write"); + FSdmmcReset(base_addr); + return FSDMMC_ERR_DATA_FAILED; + } + else if (0 >= retries) + { + FSDMMC_ERROR("BD Data timeout !!!"); + return FSDMMC_ERR_TIMEOUT; + } + + return FSDMMC_SUCCESS; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc_hw.h b/bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc_hw.h new file mode 100644 index 0000000000..623f2af31d --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc_hw.h @@ -0,0 +1,346 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsdmmc_hw.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:54:24 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2021/12/2 init + */ + +#ifndef DRIVERS_MMC_FSDMMC_HW_H +#define DRIVERS_MMC_FSDMMC_HW_H + +#ifdef __cplusplus +extern "C" +{ +#endif +/***************************** Include Files *********************************/ + +#include "fparameters.h" +#include "fio.h" +#include "fkernel.h" + +/************************** Constant Definitions *****************************/ + +/** @name Register Map + * + * Register offsets from the base address of an SD device. + * @{ + */ +#define FSDMMC_CONTROLL_SETTING_REG_OFFSET 0x00 /* Controller configuration register */ +#define FSDMMC_ARGUMENT_REG_OFFSET 0x04 /* Parameter register */ +#define FSDMMC_CMD_SETTING_REG_OFFSET 0x08 /* Command register */ +#define FSDMMC_CLOCK_DIV_REG_OFFSET 0x0C /* Clock division register */ +#define FSDMMC_SOFTWARE_RESET_REG_OFFSET 0x10 /* Reset control register */ +#define FSDMMC_POWER_CONTROLL_REG_OFFSET 0x14 /* Power control register */ +#define FSDMMC_TIMEOUT_CMD_REG_OFFSET 0x18 /* Cmd Timeout setting register */ +#define FSDMMC_TIMEOUT_DATA_REG_OFFSET 0x1C /* Data timeout setting register */ +#define FSDMMC_NORMAL_INT_EN_REG_OFFSET 0x20 /* Interrupt Enable Register */ +#define FSDMMC_ERROR_INT_EN_REG_OFFSET 0x24 /* Error Interrupt Enable Register */ +#define FSDMMC_BD_ISR_EN_REG_OFFSET 0x28 /* Data Transfer Interrupt Enable Register */ +#define FSDMMC_CAPABILIES_REG_OFFSET 0x2c /* capabilies register */ +#define FSDMMC_SD_DRV_REG_OFFSET 0x30 /* SD card driver phase register */ +#define FSDMMC_SD_SAMP_REG_OFFSET 0x34 /* SD card sampling phase register */ +#define FSDMMC_SD_SEN_REG_OFFSET 0x38 /* Card detection controller */ +#define FSDMMC_HDS_AXI_REG_CONF1_REG_OFFSET 0x3c /* AXI boundary configuration register 1 */ +#define FSDMMC_DAT_IN_M_RX_BD_REG_OFFSET 0x40 /* SD BD RX address register */ +#define FSDMMC_DAT_IN_M_TX_BD_REG_OFFSET 0x60 /* SD BD TX address register */ +#define FSDMMC_BLK_CNT_REG_OFFSET 0x80 /* Block reads and writes configuration registers */ +#define FSDMMC_HDS_AXI_REG_CONF2_REG_OFFSET 0xa8 /* AXI boundary configuration register 2 */ +#define FSDMMC_NORMAL_INT_STATUS_REG_OFFSET 0xc0 /* Interrupt status register */ +#define FSDMMC_ERROR_INT_STATUS_REG_OFFSET 0xc4 /* ERROR interrupt register */ +#define FSDMMC_BD_ISR_REG_OFFSET 0xc8 /* Data Transfer Interrupt Status Register */ +#define FSDMMC_BD_STATUS_REG_OFFSET 0xcc /* BD descriptor register */ +#define FSDMMC_STATUS_REG_OFFSET 0xd0 /* Status register */ +#define FSDMMC_BLOCK_REG_OFFSET 0xd4 /* Block length register */ +#define FSDMMC_CMD_RESP_1_REG_OFFSET 0xe0 /* Command response register 1 */ +#define FSDMMC_CMD_RESP_2_REG_OFFSET 0xe4 /* Command response register 2 */ +#define FSDMMC_CMD_RESP_3_REG_OFFSET 0xe8 /* Command response register 3 */ +#define FSDMMC_CMD_RESP_4_REG_OFFSET 0xec /* Command response register 4 */ + +/** @name FSDMMC_CONTROLL_SETTING_REG_OFFSET Register + */ +#define FSDMMC_PERMDW_MASK GENMASK(11, 10) +#define FSDMMC_PERMDW(x) (FSDMMC_PERMDW_MASK & ((x) << 10)) /* 写操作对应的大小端选择 */ +#define FSDMMC_PERMDW_LIT_END FSDMMC_PERMDW(0x0) +#define FSDMMC_PERMDW_BIG_END FSDMMC_PERMDW(0x1) +#define FSDMMC_PERMDW_STD_END FSDMMC_PERMDW(0x3) + +#define FSDMMC_PERMDR_MASK GENMASK(9, 8) +#define FSDMMC_PERMDR(x) (FSDMMC_PERMDR_MASK & ((x) << 8)) /* 读操作对应的大小端选择 */ +#define FSDMMC_PERMDR_LIT_END FSDMMC_PERMDR(0x0) +#define FSDMMC_PERMDR_BIG_END FSDMMC_PERMDR(0x1) +#define FSDMMC_PERMDR_STD_END FSDMMC_PERMDR(0x3) + +/** @name FSDMMC_ARGUMENT_REG_OFFSET Register + */ +#define FSDMMC_ARGUMENT_MASK GENMASK(31, 0) + +/** @name FSDMMC_CMD_SETTING_REG_OFFSET Register + */ +#define FSDMMC_CMD_RESP_MASK GENMASK(1, 0) +#define FSDMMC_CMD_SETTING_RTS(x) (FSDMMC_CMD_RESP_MASK & ((x) << 0)) /* 0: No response 01: Response byte length 136 10: Response byte length 48 11: Response byte length 48 */ +#define FSDMMC_CMD_NO_RESP FSDMMC_CMD_SETTING_RTS(0x0) /* 00:不响应 */ +#define FSDMMC_CMD_RESP_136_BIT FSDMMC_CMD_SETTING_RTS(0x1) /* 01:响应字节长度 136 */ +#define FSDMMC_CMD_RESP_48_BIT FSDMMC_CMD_SETTING_RTS(0x2) /* 10: 响应字节长度 48 */ +#define FSDMMC_CMD_RESP_48_BIT_BUSY_CHECK FSDMMC_CMD_SETTING_RTS(0x3) /* 11: 响应字节长度 48, check busy after resp */ + +#define FSDMMC_CMD_SETTING_CRCE BIT(3) /* 0: CRC check is not performed on CMD response 1: CRC check is performed on CMD response */ +#define FSDMMC_CMD_SETTING_CICE BIT(4) /* 0:CMD 响应时,不执行索引检查 1:CMD 响应时,执行索引检查 */ +#define FSDMMC_CMD_SETTING_CMDTP(x) (GENMASK(7, 6) & ((x) << 6)) /* 命令类型 */ +#define FSDMMC_CMD_SETTING_CMDI(x) (GENMASK(13, 8) & ((x) << 8)) /* 命令索引 */ +#define FSDMMC_CMD_SETTING_TRTY(x) (GENMASK(15, 14) & ((x) << 14)) /* 10: adtc 指令 ,其它: 读写操作 */ + + +/** @name FSDMMC_CLOCK_DIV_REG_OFFSET Register + */ +#define FSDMMC_CLK_DIVIDER(clk) (GENMASK(31, 0) & ((FSDMMC_CLK_FREQ_HZ / (2 * (clk))) - 1)) + +/** @name FSDMMC_SOFTWARE_RESET_REG_OFFSET Register + */ +#define FSDMMC_SOFTWARE_RESET_SRST BIT(0) /* 控制器软复位 */ +#define FSDMMC_SOFTWARE_RESET_BDRST BIT(2) /* DMA BD 清 0 */ +#define FSDMMC_SOFTWARE_RESET_CFCLF BIT(3) /* 卡插入拔出状态触发标志清 0 */ + +/** @name FSDMMC_TIMEOUT_CMD_REG_OFFSET Register + */ +#define FSDMMC_TIMEOUT_CMD_MASK GENMASK(31, 0) + +/** @name FSDMMC_TIMEOUT_DATA_REG_OFFSET Register + */ +#define FSDMMC_TIMEOUT_DATA_MASK GENMASK(31, 0) + +/** @name FSDMMC_NORMAL_INT_EN_REG_OFFSET Register + */ +#define FSDMMC_NORMAL_INT_EN_ECC BIT(0) /* 命令完成中断使能 */ +#define FSDMMC_NORMAL_INT_EN_ECCRCE BIT(1) /* 卡拔出中断使能 */ +#define FSDMMC_NORMAL_INT_EN_ECIE BIT(15) /* 错误中断使能 */ +#define FSDMCC_NORMAL_INT_ALL_BITS (FSDMMC_NORMAL_INT_EN_ECC | FSDMMC_NORMAL_INT_EN_ECCRCE |\ + FSDMMC_NORMAL_INT_EN_ECIE) + +/** @name FSDMMC_ERROR_INT_EN_REG_OFFSET Register + */ +#define FSDMMC_ERROR_INT_EN_CNR BIT(4) /* 命令响应错误中断 */ +#define FSDMMC_ERROR_INT_EN_CIR BIT(3) /* 命令索引错误中断使能 */ +#define FSDMMC_ERROR_INT_EN_CCRCE BIT(1) /* 命令 CRC 错误中断使能 */ +#define FSDMMC_ERROR_INT_EN_CTE BIT(0) /* 命令超时中断使能 */ +#define FSDMMC_ERROR_INT_ALL_BITS (FSDMMC_ERROR_INT_EN_CNR | FSDMMC_ERROR_INT_EN_CIR |\ + FSDMMC_ERROR_INT_EN_CCRCE | FSDMMC_ERROR_INT_EN_CTE) + +/** @name FSDMMC_BD_ISR_EN_REG_OFFSET Register + */ +#define FSDMMC_BD_ISR_EN_ETRS BIT(0) /* DMA 传输完成中断使能 */ +#define FSDMMC_BD_ISR_EN_EDTE BIT(3) /* 数据超时中断使能 */ +#define FSDMMC_BD_ISR_EN_ECMDE BIT(4) /* 命令响应错误中断使能 */ +#define FSDMMC_BD_ISR_EN_ETRE BIT(5) /* 传输错误中断使能 */ +#define FSDMMC_BD_ISR_EN_ENRCRCE BIT(6) /* CRC 校验错误中断使能 */ +#define FSDMMC_BD_ISR_EN_EDATFRAXE BIT(7) /* AXI 总线错误中断使能 */ +#define FSDMMC_BD_ISR_EN_RESPE BIT(8) /* 读 SD 卡操作,AXI BR 通道完成中断 */ +#define FSDMMC_BD_ISR_EN_EDAISE BIT(15) /* DMA 错误中断使能 */ +#define FSDMMC_BD_ISR_ALL_BITS (FSDMMC_BD_ISR_EN_ETRS | FSDMMC_BD_ISR_EN_EDTE | \ + FSDMMC_BD_ISR_EN_ECMDE | FSDMMC_BD_ISR_EN_ETRE | \ + FSDMMC_BD_ISR_EN_ENRCRCE | FSDMMC_BD_ISR_EN_EDATFRAXE | \ + FSDMMC_BD_ISR_EN_RESPE | FSDMMC_BD_ISR_EN_EDAISE) + + +/** @name FSDMMC_SD_DRV_REG_OFFSET Register + */ +#define FSDMMC_SD_DRV_MASK GENMASK(31, 0) /* 卡驱动相位配置参数 */ + +/** @name FSDMMC_SD_SAMP_REG_OFFSET Register + */ +#define FSDMMC_SD_SAMP_MASK GENMASK(31, 0) /* 卡采样相位配置参数 */ + +/** @name FSDMMC_SD_SEN_REG_OFFSET Register + */ +#define FSDMMC_SD_SEN_REG_CREFR BIT(1) /* 卡拔出时自动释放 AXI 总线选择 */ +#define FSDMMC_SD_SEN_REG_CRES BIT(2) /* CARD 在位状态标志选择 0: 卡在位-0,不在位-1 1: 卡在位-1,不在位-0 */ +#define FSDMMC_SD_SEN_REG_DEBNCE(x) ((x << 8) & GENMASK(31, 8)) /* 去抖时钟分频参数 */ + +/** @name FSDMMC_HDS_AXI_REG_CONF1_REG_OFFSET Register + */ +#define FSDMMC_HDS_AXI_CONF1_AWREGION_HDS_M GENMASK(22, 19) +#define FSDMMC_HDS_AXI_CONF1_AWSNOOP_HDS_M GENMASK(18, 16) +#define FSDMMC_HDS_AXI_CONF1_ARBAR_HDS_M GENMASK(15, 14) +#define FSDMMC_HDS_AXI_CONF1_ARDOMAIN_HDS_M GENMASK(13, 12) +#define FSDMMC_HDS_AXI_CONF1_ARREGION_HDS_M GENMASK(11, 8) +#define FSDMMC_HDS_AXI_CONF1_ARSNOOP_HDS_M GENMASK(7, 4) +#define FSDMMC_HDS_AXI_CONF1_AWBAR_HDS_M GENMASK(3, 2) +#define FSDMMC_HDS_AXI_CONF1_AWDOMAIN_HDS_M GENMASK(1, 0) + +/** @name FSDMMC_DAT_IN_M_RX_BD_REG_OFFSET Register + */ +#define FSDMMC_DAT_IN_M_RX_BD_MASK GENMASK(31, 0) + +/** @name FSDMMC_DAT_IN_M_TX_BD_REG_OFFSET Register + */ +#define FSDMMC_DAT_IN_M_TX_BD_MASK GENMASK(31, 0) + +/** @name FSDMMC_BLK_CNT_REG_OFFSET Register + */ +#define FSDMMC_BLK_CNT_MASK GENMASK(31, 0) + +/** @name FSDMMC_HDS_AXI_REG_CONF2_REG_OFFSET Register + */ +#define FSDMMC_HDS_AXI_CONF2_SD_ARPROT GENMASK(29, 27) +#define FSDMMC_HDS_AXI_CONF2_SD_AWPROT GENMASK(26, 24) +#define FSDMMC_HDS_AXI_CONF2_SD_ARCACHE_M GENMASK(23, 20) +#define FSDMMC_HDS_AXI_CONF2_SD_AWCACHE_M GENMASK(19, 16) +#define FSDMMC_HDS_AXI_CONF2_HDA_ARPRO GENMASK(13, 11) +#define FSDMMC_HDS_AXI_CONF2_HDA_AWPROT GENMASK(10, 8) +#define FSDMMC_HDS_AXI_CONF2_HDA_ARCACHE_M GENMASK(7, 4) +#define FSDMMC_HDS_AXI_CONF2_HDA_AWCACHE_M GENMASK(3, 0) + +/** @name FSDMMC_NORMAL_INT_STATUS_REG_OFFSET Register + */ +#define FSDMMC_NORMAL_INT_STATUS_EI BIT(15) /* 命令错误中断 */ +#define FSDMMC_NORMAL_INT_STATUS_CR BIT(1) /* 卡移除中断 */ +#define FSDMMC_NORMAL_INT_STATUS_CC BIT(0) /* 命令完成中断 */ +#define FSDMMC_NORMAL_INT_STATUS_ALL_MASK (FSDMMC_NORMAL_INT_STATUS_EI | FSDMMC_NORMAL_INT_STATUS_CR | FSDMMC_NORMAL_INT_STATUS_CC) + +/** @name FSDMMC_ERROR_INT_STATUS_REG_OFFSET Register + */ +#define FSDMMC_ERROR_INT_STATUS_CNR BIT(4) /* 命令响应错误中断 */ +#define FSDMMC_ERROR_INT_STATUS_CIR BIT(3) /* 命令索引错误中断 */ +#define FSDMMC_ERROR_INT_STATUS_CCRCE BIT(1) /* 命令 CRC 错误中断 */ +#define FSDMMC_ERROR_INT_STATUS_CTE BIT(0) /* 命令超时错误中断 */ +#define FSDMMC_ERROR_INT_STATUS_ALL_MASK (FSDMMC_ERROR_INT_STATUS_CNR | FSDMMC_ERROR_INT_STATUS_CIR | FSDMMC_ERROR_INT_STATUS_CCRCE | FSDMMC_ERROR_INT_STATUS_CTE) + +/** @name FSDMMC_BD_ISR_REG_OFFSET Register + */ +#define FSDMMC_BD_ISR_REG_DAIS BIT(15) /* DMA 错误中断*/ +#define FSDMMC_BD_ISR_REG_RESPE BIT(8) /* 读 SD 卡操作,AXI BR 通道完成中断*/ +#define FSDMMC_BD_ISR_REG_DATFRAX BIT(7) /* AXI 总线强制释放中断*/ +#define FSDMMC_BD_ISR_REG_NRCRC BIT(6) /* 无 CRC 响应中断*/ +#define FSDMMC_BD_ISR_REG_TRE BIT(5) /* CRC 响应错误中断*/ +#define FSDMMC_BD_ISR_REG_CMDE BIT(4) /* 命令响应错误中断*/ +#define FSDMMC_BD_ISR_REG_DTE BIT(3) /* 超时中断*/ +#define FSDMMC_BD_ISR_REG_TRS BIT(0) /* DMA 传输完成中断*/ + +/** @name FSDMMC_BD_STATUS_REG_OFFSET Register + */ +#define FSDMMC_BD_STATUS_MASK GENMASK(31, 0) /* BD 描述符 */ + +/** @name FSDMMC_STATUS_REG_OFFSET Register + */ +#define FSDMMC_STATUS_REG_DATMAST(x) (GENMASK(30, 27) & ((x) << 27)) /* data_master 状态机 */ +#define FSDMMC_STATUS_REG_CDIF BIT(26) /* 卡在位标志 */ +#define FSDMMC_STATUS_REG_CDRF BIT(25) /* 卡不在位标志 */ +#define FSDMMC_STATUS_REG_CLSL BIT(24) /* 命令闲信号 */ +#define FSDMMC_STATUS_REG_DLSL(x) (GENMASK(23, 20) & ((x) << 20)) /* 线信号 */ +#define FSDMMC_STATUS_REG_CDSL BIT(19) /* 卡检测管脚信号 */ +#define FSDMMC_STATUS_REG_CST(x) (GENMASK(15, 12) & ((x) << 12)) /* cmd_host state 状态机 */ +#define FSDMMC_STATUS_IDIE (0x1 << 12) +#define FSDMMC_STATUS_REG_CSM(x) (GENMASK(11, 7) & ((x) << 7)) +#define FSDMMC_STATUS_REG_DAT_AVA BIT(6) /* DAT_AVA 当前命令状态流程运转完 */ +#define FSDMMC_STATUS_REG_CRC_VALID BIT(5) +#define FSDMMC_STATUS_REG_CICMD BIT(0) /* RO 0x0 CMD 总线状态 */ +#define FSDMMC_STATUS_CMD_BUSY (0x0 << 0) +#define FSDMMC_STATUS_CMD_READY (0x1 << 0) + +/** @name FSDMMC_BLOCK_REG_OFFSET Register + */ +#define FSDMMC_BLOCK_MASK GENMASK(31, 0) + +/** @name FSDMMC_CMD_RESP_1_REG_OFFSET Register + */ +#define FSDMMC_CMD_RESP_1_MASK GENMASK(31, 0) + +/** @name FSDMMC_CMD_RESP_2_REG_OFFSET Register + */ +#define FSDMMC_CMD_RESP_2_MASK GENMASK(31, 0) + +/** @name FSDMMC_CMD_RESP_3_REG_OFFSET Register + */ +#define FSDMMC_CMD_RESP_3_MASK GENMASK(31, 0) + +/** @name FSDMMC_CMD_RESP_4_REG_OFFSET Register + */ +#define FSDMMC_CMD_RESP_4_MASK GENMASK(31, 0) + +#define FSDMMC_DEFAULT_DRV 1 +#define FSDMMC_DEFAULT_SAMP 5 +#define FSDMMC_SEN_CREFR_VAL (0x1 << 1) +#define FSDMMC_SEN_DEBNCE_VAL (0xB << 8) +#define FSDMMC_CMD_TIMEOUT 10000000 /* 1s */ +#define FSDMMC_DATA_TIMEOUT 40000000 /* 4s */ +#define FSDMMC_AXI_CONF1 0x1001 +#define FSDMMC_AXI_CONF2 0x12221222 +#define FSDMMC_TIMEOUT 5000000 /* timeout for retries */ +#define FSDMMC_BLOCK_SIZE 512 + +#define FSDMMC_DMA_ADDR_ALIGN 32 +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +#define FSDMMC_READ_REG(addr, reg_offset) FtIn32((addr) + (u32)(reg_offset)) +#define FSDMMC_WRITE_REG(addr, reg_offset, reg_value) FtOut32((addr) + (u32)(reg_offset), (u32)(reg_value)) +#define FSDMMC_SET_BIT(addr, reg_offset, bit) FtSetBit32((addr) + (u32)(reg_offset), (u32)(bit)) +#define FSDMMC_CLR_BIT(addr, reg_offset, bit) FtClearBit32((addr) + (u32)(reg_offset), (u32)(bit)) + +/* 检查卡检测管脚信号, 低电平表示卡在位 */ +static inline boolean FSdmmcCheckIfCardExists(uintptr base_addr) +{ + return (FSDMMC_STATUS_REG_CDSL != (FSDMMC_STATUS_REG_CDSL & FSDMMC_READ_REG(base_addr, FSDMMC_STATUS_REG_OFFSET))); +} + +/* 清除命令中断状态位 */ +static inline void FSdmmcClearNormalInterruptStatus(uintptr base_addr) +{ + FSDMMC_WRITE_REG(base_addr, FSDMMC_NORMAL_INT_STATUS_REG_OFFSET, (0x1)); +} + +/* 清除错误中断状态位 */ +static inline void FSdmmcClearErrorInterruptStatus(uintptr base_addr) +{ + FSDMMC_WRITE_REG(base_addr, FSDMMC_ERROR_INT_STATUS_REG_OFFSET, (0x1)); +} + +/* 清除DMA中断状态位 */ +static inline void FSdmmcClearBDInterruptStatus(uintptr base_addr) +{ + FSDMMC_WRITE_REG(base_addr, FSDMMC_BD_ISR_REG_OFFSET, (0x1)); +} + +/************************** Function Prototypes ******************************/ +/* 重置FSDMMC控制器 */ +FError FSdmmcReset(uintptr base_addr); + +/* 完成软复位 */ +FError FSdmmcSoftwareReset(uintptr base_addr, int retries); + +/* 设置FSDMMC的时钟 */ +FError FSdmmcSetCardClk(uintptr base_addr, u32 clk_freq_hz); + +/* 发送命令 */ +void FSdmmcSendPrivateCmd(uintptr base_addr, u32 cmd, u32 arg); + +/* 等待命令完成或者命令错误状态 */ +FError FSdmmcWaitStatus(uintptr base_addr, int retries); + +/* 等待数据传输完成或者传输错误状态 */ +FError FSdmmcWaitDMAStatus(uintptr base_addr, boolean read, int retries); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc_intr.c b/bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc_intr.c new file mode 100644 index 0000000000..5fa29e8737 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc_intr.c @@ -0,0 +1,318 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsdmmc_intr.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:54:53 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2021/12/2 init + */ + +/***************************** Include Files *********************************/ +#include "fassert.h" +#include "fio.h" +#include "fdebug.h" + +#include "fsdmmc_hw.h" +#include "fsdmmc.h" + +/************************** Constant Definitions *****************************/ +#define FSDMMC_DEBUG_TAG "FSDMMC-INTR" +#define FSDMMC_ERROR(format, ...) FT_DEBUG_PRINT_E(FSDMMC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSDMMC_WARN(format, ...) FT_DEBUG_PRINT_W(FSDMMC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSDMMC_INFO(format, ...) FT_DEBUG_PRINT_I(FSDMMC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSDMMC_DEBUG(format, ...) FT_DEBUG_PRINT_D(FSDMMC_DEBUG_TAG, format, ##__VA_ARGS__) + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +static void FSdmmcCallEvtHandler(FSdmmcEventHandler handler, FSdmmc *instance_p) +{ + if (NULL != handler) + { + handler((void *)instance_p); + } +} + +/************************** Function Prototypes ******************************/ +extern u32 FSdmmcMakeRawCmd(FSdmmcCmd *cmd_p); +extern void FSdmmcSendCmd(uintptr base_addr, FSdmmcCmd *cmd_p); +extern FError FSdmmcSendData(uintptr base_addr, boolean read, FSdmmcCmd *cmd_p); + +/************************** Variable Definitions *****************************/ + +/*****************************************************************************/ +/** + * @name: FSdmmcGetInterruptMask + * @msg: 获取FSDMMC的中断掩码 + * @return {u32} 中断掩码 + * @param {uintptr} base_addr FSDMMC控制器基地址 + * @param {u32} intr_type FSDMMC中断类型, 参考FSDMMC_INTR_NUM + * @note FSDMMC控制器初始化后才能调用此函数 + */ +u32 FSdmmcGetInterruptMask(uintptr base_addr, u32 intr_type) +{ + u32 mask; + + switch (intr_type) + { + case FSDMMC_CMD_INTR: + mask = FSDMMC_READ_REG(base_addr, FSDMMC_NORMAL_INT_EN_REG_OFFSET); + break; + case FSDMMC_ERROR_INTR: + mask = FSDMMC_READ_REG(base_addr, FSDMMC_ERROR_INT_EN_REG_OFFSET); + break; + case FSDMMC_DMA_BD_INTR: + mask = FSDMMC_READ_REG(base_addr, FSDMMC_BD_ISR_EN_REG_OFFSET); + break; + default: + FASSERT(0); + break; + } + + return mask; +} + +/** + * @name: FSdmmcSetInterruptMask + * @msg: 设置FSDMMC的中断掩码 + * @param {uintptr} base_addr FSDMMC控制器基地址 + * @param {u32} intr_type FSDMMC中断类型, 参考FSDMMC_INTR_NUM + * @param {u32} mask 中断掩码 + * @param {boolean} enable TRUE:打开中断, FALSE:关闭中断 + * @note FSDMMC控制器初始化后才能调用此函数 + */ +void FSdmmcSetInterruptMask(uintptr base_addr, u32 intr_type, u32 mask, boolean enable) +{ + u32 old_mask = FSdmmcGetInterruptMask(base_addr, intr_type); + u32 new_mask = 0; + + if (TRUE == enable) + new_mask = old_mask | mask; + else + new_mask = old_mask & (~mask); + + switch (intr_type) + { + case FSDMMC_CMD_INTR: + FSDMMC_WRITE_REG(base_addr, FSDMMC_NORMAL_INT_EN_REG_OFFSET, new_mask); + break; + case FSDMMC_ERROR_INTR: + FSDMMC_WRITE_REG(base_addr, FSDMMC_ERROR_INT_EN_REG_OFFSET, new_mask); + break; + case FSDMMC_DMA_BD_INTR: + FSDMMC_WRITE_REG(base_addr, FSDMMC_BD_ISR_EN_REG_OFFSET, new_mask); + break; + default: + FASSERT(0); + break; + } + + return; +} + +/** + * @name: FSdmmcCmdInterrupHandler + * @msg: 命令中断响应函数 + * @return {*} 无 + * @param {s32} vector 中断向量号 + * @param {void} *param 中断响应输入参数 + * @note 此函数用于设置FSDMMC中断时注册,用户可以自定义一个中断响应函数替换此函数 + */ +void FSdmmcCmdInterrupHandler(s32 vector, void *param) +{ + FASSERT(param); + u32 status; + FSdmmc *instance_p = (FSdmmc *)param; + uintptr base_addr = instance_p->config.base_addr; + + /* clear interrupts */ + status = FSDMMC_READ_REG(base_addr, FSDMMC_NORMAL_INT_STATUS_REG_OFFSET); + + if (status & FSDMMC_NORMAL_INT_STATUS_CR) /* 卡移除中断 */ + { + FSdmmcCallEvtHandler(instance_p->evt_handler[FSDMMC_EVT_CARD_REMOVED], instance_p); + } + + if (status & FSDMMC_NORMAL_INT_STATUS_CC) /* 命令完成中断 */ + { + FSdmmcCallEvtHandler(instance_p->evt_handler[FSDMMC_EVT_CMD_DONE], instance_p); + } + + if (status & FSDMMC_NORMAL_INT_STATUS_EI) /* 命令错误中断 */ + { + FSdmmcCallEvtHandler(instance_p->evt_handler[FSDMMC_EVT_CMD_ERROR], instance_p); + } + + FSdmmcClearNormalInterruptStatus(base_addr); +} + +/** + * @name: FSdmmcDmaInterrupHandler + * @msg: DMA中断响应函数 + * @return {*} + * @param {s32} vector 中断向量号 + * @param {void} *param 中断响应输入参数 + * @note 此函数用于设置FSDMMC中断时注册,用户可以自定义一个中断响应函数替换此函数 + */ +void FSdmmcDmaInterrupHandler(s32 vector, void *param) +{ + FASSERT(param); + u32 status; + FSdmmc *instance_p = (FSdmmc *)param; + uintptr base_addr = instance_p->config.base_addr; + + /* clear interrupts */ + status = FSDMMC_READ_REG(base_addr, FSDMMC_BD_ISR_REG_OFFSET); + + if (status & FSDMMC_BD_ISR_REG_DAIS) /* DMA 错误中断 */ + { + FSdmmcCallEvtHandler(instance_p->evt_handler[FSDMMC_EVT_DATA_ERROR], instance_p); + } + + if (status & FSDMMC_BD_ISR_REG_RESPE) /* 读 SD 卡操作,AXI BR 通道完成中断 */ + { + FSdmmcCallEvtHandler(instance_p->evt_handler[FSDMMC_EVT_DATA_READ_DONE], instance_p); + } + + if (status & FSDMMC_BD_ISR_REG_DATFRAX) /* AXI 总线强制释放中断*/ + { + FSDMMC_ERROR("FSDMMC_BD_ISR_REG_DATFRAX"); + } + + if (status & FSDMMC_BD_ISR_REG_NRCRC) /* 无 CRC 响应中断*/ + { + FSDMMC_ERROR("FSDMMC_BD_ISR_REG_NRCRC"); + } + + if (status & FSDMMC_BD_ISR_REG_TRE) /* CRC 响应错误中断*/ + { + FSDMMC_ERROR("FSDMMC_BD_ISR_REG_TRE"); + } + + if (status & FSDMMC_BD_ISR_REG_CMDE) /* 命令响应错误中断*/ + { + FSDMMC_ERROR("FSDMMC_BD_ISR_REG_CMDE"); + } + + if (status & FSDMMC_BD_ISR_REG_DTE) /* 超时中断*/ + { + FSDMMC_ERROR("FSDMMC_BD_ISR_REG_DTE"); + } + + if (status & FSDMMC_BD_ISR_REG_TRS) /* DMA 传输完成中断*/ + { + FSdmmcCallEvtHandler(instance_p->evt_handler[FSDMMC_EVT_DATA_WRITE_DONE], instance_p); + } + + FSdmmcClearBDInterruptStatus(base_addr); +} + +/** + * @name: FSdmmcErrInterrupHandler + * @msg: 错误中断响应函数 + * @return {*} + * @param {s32} vector 中断向量号 + * @param {void} *param 中断响应输入参数 + * @note 此函数用于设置FSDMMC中断时注册,用户可以自定义一个中断响应函数替换此函数 + */ +void FSdmmcErrInterrupHandler(s32 vector, void *param) +{ + FASSERT(param); + u32 status; + FSdmmc *instance_p = (FSdmmc *)param; + uintptr base_addr = instance_p->config.base_addr; + + status = FSDMMC_READ_REG(base_addr, FSDMMC_ERROR_INT_STATUS_REG_OFFSET); + + if (status & FSDMMC_ERROR_INT_STATUS_CNR) /* 命令响应错误中断 */ + { + FSdmmcCallEvtHandler(instance_p->evt_handler[FSDMMC_EVT_CMD_RESP_ERROR], instance_p); + } + + if (status & FSDMMC_ERROR_INT_STATUS_CIR) /* 命令索引错误中断 */ + { + FSDMMC_ERROR("FSDMMC_ERROR_INT_STATUS_CIR"); + } + + if (status & FSDMMC_ERROR_INT_STATUS_CCRCE) /* 命令 CRC 错误中断 */ + { + FSDMMC_ERROR("FSDMMC_ERROR_INT_STATUS_CCRCE"); + } + + if (status & FSDMMC_ERROR_INT_STATUS_CTE) /* 命令超时错误中断 */ + { + FSDMMC_ERROR("FSDMMC_ERROR_INT_STATUS_CTE"); + } + + /* clear command error status */ + FSdmmcClearErrorInterruptStatus(base_addr); +} + +/** + * @name: FSdmmcRegisterInterruptHandler + * @msg: 注册中断事件响应函数 + * @return {*} + * @param {FSdmmc} *instance_p FSDMMC驱动控制数据 + * @param {u32} event FSDMMC中断事件类型,参考FSDMMC_EVT_NUM + * @param {FSdmmcEventHandler} handler, FSDMMC中断事件响应函数 + * @note 此函数用于设置FSDMMC中断时注册,被注册的函数被FSdmmcCmdInterrupHandler、FSdmmcErrInterrupHandler + * 和FSdmmcDmaInterrupHandler调用 + */ +void FSdmmcRegisterInterruptHandler(FSdmmc *instance_p, u32 event, FSdmmcEventHandler handler) +{ + FASSERT(instance_p); + instance_p->evt_handler[event] = handler; +} + +/** + * @name: FSdmmcInterruptTransfer + * @msg: 通过FSDMMC中断方式发送/接收数据和命令 + * @return {FError} 驱动初始化的错误码信息,FSDMMC_SUCCESS 表示发送/接收成功,其它返回值表示发送/接收失败 + * @param {FSdmmc} *instance_p FSDMMC驱动控制数据 + * @param {FSdmmcCmd} *cmd_data_p FSDMMC数据和命令 + * @note FSDMMC控制器初始化后才能调用此函数,使用前需要确保FSDMMC中断设置完成 + */ +FError FSdmmcInterruptTransfer(FSdmmc *instance_p, FSdmmcCmd *cmd_data_p) +{ + FASSERT(instance_p && cmd_data_p); + uintptr base_addr = instance_p->config.base_addr; + FError ret = FSDMMC_SUCCESS; + + if (FALSE == FSdmmcCheckIfCardExists(base_addr)) + { + FSDMMC_ERROR("card not found !!! fsdio ctrl base 0x%x", base_addr); + return FSDMMC_ERR_CARD_NO_FOUND; + } + + if (cmd_data_p->flag & FSDMMC_CMD_FLAG_EXP_DATA) + { + /* transfer data */ + FSDMMC_INFO("====DATA [%d] START: buf: %p=====", cmd_data_p->cmdidx, cmd_data_p->data_p); + ret = FSdmmcSendData(base_addr, + (FSDMMC_CMD_FLAG_READ_DATA == (cmd_data_p->flag & FSDMMC_CMD_FLAG_READ_DATA)), + cmd_data_p); + } + else + { + /* transfer command */ + FSDMMC_INFO("=====CMD [%d] START=====", cmd_data_p->cmdidx); + FSdmmcSendCmd(base_addr, cmd_data_p); + } + + return ret; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc_sinit.c b/bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc_sinit.c new file mode 100644 index 0000000000..96d040abe1 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/mmc/fsdmmc/fsdmmc_sinit.c @@ -0,0 +1,69 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsdmmc_sinit.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:55:09 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2021/12/2 init + */ + + +/* - This file contains the implementation of driver's static initialization functionality. +- 驱动静态初始化 */ + +/***************************** Include Files *********************************/ + +#include "ftypes.h" +#include "fparameters.h" + +#include "fsdmmc.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Variable Definitions *****************************/ + +extern const FSdmmcConfig FSDMMC_CONFIG_TBL[FSDMMC_HOST_INSTANCE_NUM]; + +/************************** Function Prototypes ******************************/ +/** + * @name: FSdmmcLookupConfig + * @msg: 获取FSDMMC控制器默认配置 + * @return {const FSdmmcConfig *} FSDMMC默认配置,返回NULL如果找不到默认配置 + * @param {u32} instance_id 驱动控制器ID + * @note instance_id从0开始,取决于FSDMMC控制器的个数 + */ +const FSdmmcConfig *FSdmmcLookupConfig(u32 instance_id) +{ + const FSdmmcConfig *ptr = NULL; + u32 index; + + for (index = 0; index < (u32)FSDMMC_HOST_INSTANCE_NUM; index++) + { + if (FSDMMC_CONFIG_TBL[index].instance_id == instance_id) + { + ptr = &FSDMMC_CONFIG_TBL[index]; + break; + } + } + + return (const FSdmmcConfig *)ptr; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/nand/Kconfig b/bsp/phytium/libraries/standalone/drivers/nand/Kconfig new file mode 100644 index 0000000000..4638b049f9 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/nand/Kconfig @@ -0,0 +1,34 @@ + +menu "NAND Configuration" + menu "FNAND ip config" + choice + prompt "choice fnand driver" + config ENABLE_FNAND + bool + prompt "Use FNAND" + + if ENABLE_FNAND + config FNAND_COMMON_DEBUG_EN + bool + prompt "Use FNAND common mode debug" + + config FNAND_DMA_DEBUG_EN + bool + prompt "Use FNAND DMA mode debug" + + config FNAND_TOGGLE_DEBUG_EN + bool + prompt "Use FNAND toggle mode debug" + + config FNAND_ONFI_DEBUG_EN + bool + prompt "Use FNAND onfi mode debug" + + endif + + endchoice + endmenu + +endmenu + + diff --git a/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand.c b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand.c new file mode 100644 index 0000000000..5396ff2600 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand.c @@ -0,0 +1,567 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fnand.c + * Date: 2022-05-10 14:53:42 + * LastEditTime: 2022-05-10 08:56:27 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ +#include "fnand.h" +#include "fnand_hw.h" +#include +#include +#include "fnand_id.h" +#include "fnand_common_cmd.h" +#include "fdebug.h" +#define FNAND_DEBUG_TAG "FNAND" +#define FNAND_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(FNAND_DEBUG_TAG, format, ##__VA_ARGS__) +#define FNAND_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(FNAND_DEBUG_TAG, format, ##__VA_ARGS__) +#define FNAND_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(FNAND_DEBUG_TAG, format, ##__VA_ARGS__) + +extern void FNandHwInit(uintptr_t base_address, FNandInterMode inter_mode); +extern void FNandHwReset(uintptr_t base_address); +extern void FNandEnable(uintptr_t base_address); +extern FError FNandToggleInit(FNand *instance_p, u32 chip_addr); +extern FError FNandOnfiInit(FNand *instance_p, u32 chip_addr); +extern FError FNandTimingInterfaceUpdate(FNand *instance_p, u32 chip_addr); +extern void FNandIsrEnable(FNand *instance_p, u32 int_mask); + + +/** + * @name: FNandScan + * @msg: Nand scanning + * @note: + * @param {FNand} *instance_p is the pointer to the FNand instance. + * @return {FT_SUCCESS} Scan nand is ok + */ +FError FNandScan(FNand *instance_p) +{ + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + return FNandDetect(instance_p); + +} + +u32 FNandCheckBusy(FNand *instance_p) +{ + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + FNandConfig *config_p; + config_p = &instance_p->config; + + return FNAND_READREG(config_p->base_address, FNAND_STATE_OFFSET) & FNAND_STATE_BUSY_OFFSET; +} + + +FError FNandSendCmd(FNand *instance_p, struct FNandDmaDescriptor *descriptor_p, FNandOperationType isr_type) +{ + FNandConfig *config_p; + u32 timeout_cnt = 0; + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + FASSERT(isr_type < FNAND_TYPE_NUM); + + config_p = &instance_p->config; + + FNandHwReset(config_p->base_address); + + if (0 != FNandCheckBusy(instance_p)) + { + FNAND_DEBUG_E("Nand is busy"); + return FNAND_IS_BUSY; + } + + /* write dma addr to register */ + FNAND_WRITEREG(config_p->base_address, FNAND_MADDR0_OFFSET, ((uintptr)descriptor_p) & FNAND_MADDR0_DT_LOW_ADDR_MASK); + +#ifdef __aarch64__ + /* 将高位地址填入寄存器 */ + FNAND_CLEARBIT(config_p->base_address, FNAND_MADDR1_OFFSET, FNAND_MADDR1_DT_HIGH_8BITADDR_MASK); + FNAND_SETBIT(config_p->base_address, FNAND_MADDR1_OFFSET, ((uintptr)descriptor_p >> 32) & FNAND_MADDR1_DT_HIGH_8BITADDR_MASK); +#else + FNAND_CLEARBIT(config_p->base_address, FNAND_MADDR1_OFFSET, FNAND_MADDR1_DT_HIGH_8BITADDR_MASK); +#endif + /* 中断模式操作 */ + if (instance_p->work_mode == FNAND_WORK_MODE_ISR) + { + if (isr_type == FNAND_CMD_TYPE) + { + FNandIsrEnable(instance_p, FNAND_INTRMASK_CMD_FINISH_MASK); + } + else if (isr_type == FNAND_WRITE_PAGE_TYPE) + { + FNandIsrEnable(instance_p, FNAND_INTRMASK_PGFINISH_MASK); + } + else if (isr_type == FNAND_READ_PAGE_TYPE) + { + FNandIsrEnable(instance_p, FNAND_INTRMASK_DMA_PGFINISH_MASK); + } + else if (isr_type == FNAND_WAIT_ECC_TYPE) + { + FNandIsrEnable(instance_p, FNAND_INTRMASK_ECC_FINISH_MASK); + } + } + + FNAND_SETBIT(config_p->base_address, FNAND_MADDR1_OFFSET, FNAND_MADDR1_DMA_EN_MASK); + + if (instance_p->work_mode == FNAND_WORK_MODE_ISR && (instance_p->wait_irq_fun_p != NULL)) + { + if (instance_p->wait_irq_fun_p) + { + if (instance_p->wait_irq_fun_p(instance_p->wait_args) != FT_SUCCESS) + { + FNAND_DEBUG_E("wait_irq_fun_p is failed"); + return FNAND_ERR_IRQ_OP_FAILED; + } + } + else + { + FNAND_DEBUG_E("The lack of wait_irq_fun_p"); + FNAND_WRITEREG(config_p->base_address, FNAND_INTRMASK_OFFSET, FNAND_INTRMASK_ALL_INT_MASK); + return FNAND_ERR_IRQ_LACK_OF_CALLBACK; + } + + return FT_SUCCESS ; + } + else + { + if (isr_type == FNAND_CMD_TYPE) + { + while (0 == (FNAND_READREG(config_p->base_address, FNAND_STATE_OFFSET) & FNAND_STATE_CMD_PGFINISH_OFFSET)) + { + if (timeout_cnt++ >= 0xffffff) + { + FNAND_DEBUG_E("FNAND_CMD_TYPE is send timeout"); + return FNAND_OP_TIMEOUT; + } + } + } + else if (isr_type == FNAND_WRITE_PAGE_TYPE) + { + while (0 == (FNAND_READREG(config_p->base_address, FNAND_STATE_OFFSET) & FNAND_STATE_PG_PGFINISH_OFFSET)) + { + if (timeout_cnt++ >= 0xffffff) + { + FNAND_DEBUG_E("FNAND_CMD_TYPE is send timeout"); + return FNAND_OP_TIMEOUT; + } + } + } + else if (isr_type == FNAND_READ_PAGE_TYPE) + { + while (0 == (FNAND_READREG(config_p->base_address, FNAND_STATE_OFFSET) & FNAND_STATE_DMA_PGFINISH_OFFSET)) + { + if (timeout_cnt++ >= 0xffffff) + { + FNAND_DEBUG_E("FNAND_CMD_TYPE is send timeout"); + return FNAND_OP_TIMEOUT; + } + } + } + else if (isr_type == FNAND_WAIT_ECC_TYPE) + { + while (0 == (FNAND_READREG(config_p->base_address, FNAND_STATE_OFFSET) & FNAND_STATE_ECC_FINISH_OFFSET)) + { + if (timeout_cnt++ >= 0xffffff) + { + FNAND_DEBUG_E("FNAND_CMD_TYPE is send timeout"); + return FNAND_OP_TIMEOUT; + } + } + } + } + + return FT_SUCCESS; +} + + +/** + * @name: FNandOperationWaitIrqRegister + * @msg: When nand is sent in interrupt mode, the action that waits while the operation completes + * @note: + * @param {FNand} *instance_p is the pointer to the FNand instance. + * @param {FNandOperationWaitIrqCallback} wait_irq_fun_p , When the user adds this function, return FT_SUCCESS reports success, otherwise failure + * @param {void} *wait_args + * @return {*} + */ +void FNandOperationWaitIrqRegister(FNand *instance_p, FNandOperationWaitIrqCallback wait_irq_fun_p, void *wait_args) +{ + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + + instance_p->wait_irq_fun_p = wait_irq_fun_p; + instance_p->wait_args = wait_args; +} + + + +/** + * @name: FNandCfgInitialize + * @msg: Initialize the NAND controller + * @param {FNand} *instance_p is the pointer to the FNand instance. + * @param {FNandConfig} * points to the FNand device configuration structure. + * @return {FError} FT_SUCCESS if successful + * @note: + */ +FError FNandCfgInitialize(FNand *instance_p, + FNandConfig *config_p) +{ + u32 i; + FError ret; + /* Assert arguments */ + FASSERT(instance_p != NULL); + FASSERT(config_p != NULL); + + /* Clear instance memory and make copy of configuration */ + memset(instance_p, 0, sizeof(FNand)); + instance_p->config = *config_p; + instance_p->is_ready = FT_COMPONENT_IS_READY; + + /* lsd config */ + FNAND_CLEARBIT(FLSD_CONFIG_BASE, 0xc0, 1); + + instance_p->work_mode = FNAND_WORK_MODE_ISR ; /* 默认采用中断模式 */ + for (i = 0; i < FNAND_CONNECT_MAX_NUM; i++) + { + instance_p->inter_mode[i] = FNAND_ASYN_SDR; /* 初始化阶段以异步模式启动 */ + instance_p->timing_mode[i] = FNAND_TIMING_MODE0 ; + /* 初始化时序配置 */ + ret = FNandTimingInterfaceUpdate(instance_p, i); + if (ret != FT_SUCCESS) + { + FNAND_DEBUG_E("%s, FNandTimingInterfaceUpdate is error", __func__); + return ret; + } + } + + FNandHwInit(instance_p->config.base_address, instance_p->inter_mode[0]); + FNandHwReset(instance_p->config.base_address); + + /* init ecc strength */ + FNAND_CLEARBIT(instance_p->config.base_address, FNAND_CTRL0_OFFSET, FNAND_CTRL0_ECC_CORRECT_MAKE(7UL)); /* clear all ecc_correct */ + if (instance_p->config.ecc_strength == 0x8) + { + FNAND_SETBIT(instance_p->config.base_address, FNAND_CTRL0_OFFSET, FNAND_CTRL0_ECC_CORRECT_MAKE(7UL)); + } + else if (instance_p->config.ecc_strength == 0x4) + { + FNAND_SETBIT(instance_p->config.base_address, FNAND_CTRL0_OFFSET, FNAND_CTRL0_ECC_CORRECT_MAKE(3UL)); + } + else if (instance_p->config.ecc_strength == 0x2) + { + FNAND_SETBIT(instance_p->config.base_address, FNAND_CTRL0_OFFSET, FNAND_CTRL0_ECC_CORRECT_MAKE(1UL)); + } + else + { + FNAND_SETBIT(instance_p->config.base_address, FNAND_CTRL0_OFFSET, FNAND_CTRL0_ECC_CORRECT_MAKE(0UL)); + } + + FNandEnable(instance_p->config.base_address); + + /* init bbm */ + FNandInitBbtDesc(instance_p); + return (FT_SUCCESS); +} + + +/** + * @name: FNandWritePage + * @msg: Write operations one page at a time, including writing page data and spare data + * @note: + * @param {FNand} *instance_p is the pointer to the FNand instance. + * @param {u32} page_addr is the address to which the page needs to be written + * @param {u8} *buffer is page writes a pointer to the buffer + * @param {u32} page_copy_offset is the offset of the page writing , Buffer write data to 0 + page_copy_offset + * @param {u32} length is page data write length + * @param {u8} *oob_buffer is the data buffer pointer needs to be written to the spare space + * @param {u32} oob_copy_offset is the offset of the spare space writing , Buffer write data to page length + oob_copy_offset + * @param {u32} oob_length is the length to be written to the spare space + * @param {u32} chip_addr chip address + * @return {FError} FT_SUCCESS ,write page is successful + */ +FError FNandWritePage(FNand *instance_p, u32 page_addr, u8 *buffer, u32 page_copy_offset, u32 length, u8 *oob_buffer, u32 oob_copy_offset, u32 oob_length, u32 chip_addr) +{ + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + FASSERT(chip_addr < FNAND_CONNECT_MAX_NUM); + FASSERT(instance_p->write_hw_ecc_p); + + FNandOpData op_data = + { + .page_addr = page_addr, + .page_buf = NULL, /* page 数据缓存空间 */ + .page_offset = 0, /* 从offset开始拷贝页数据 */ + .page_length = 0, /* 从offset开始拷贝页数据的长度 */ + .obb_required = 0, /* obb 是否读取的标志位,1 需要操作oob 区域 */ + .oob_buf = NULL, /* obb 数据缓存空间 */ + .oob_offset = 0, /* 从offset开始拷贝页数据 */ + .oob_length = 0, /* 从offset开始拷贝页数据的长度 */ + .chip_addr = chip_addr, /* 芯片地址 */ + }; + + if (buffer && (length > 0)) + { + op_data.page_buf = buffer; + op_data.page_length = length; + op_data.page_offset = page_copy_offset; + } + + if (oob_buffer && (oob_length > 0)) + { + op_data.obb_required = 1; + op_data.oob_buf = oob_buffer; + op_data.oob_length = oob_length; + op_data.oob_offset = oob_copy_offset; + } + + return instance_p->write_hw_ecc_p(instance_p, &op_data); +} + +/** + * @name: FNandWritePage + * @msg: Write operations one page at a time, including writing page data and spare data ,without hw ecc + * @note: + * @param {FNand} *instance_p is the pointer to the FNand instance. + * @param {u32} page_addr is the address to which the page needs to be written + * @param {u8} *buffer is page writes a pointer to the buffer + * @param {u32} page_copy_offset is the offset of the page writing , Buffer write data to 0 + page_copy_offset + * @param {u32} length is page data write length + * @param {u8} *oob_buffer is the data buffer pointer needs to be written to the spare space + * @param {u32} oob_copy_offset is the offset of the spare space writing , Buffer write data to page length + oob_copy_offset + * @param {u32} oob_length is the length to be written to the spare space + * @param {u32} chip_addr chip address + * @return {FError} FT_SUCCESS ,write page is successful + */ +FError FNandWritePageRaw(FNand *instance_p, u32 page_addr, u8 *buffer, u32 page_copy_offset, u32 length, u8 *oob_buffer, u32 oob_copy_offset, u32 oob_length, u32 chip_addr) +{ + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + FASSERT(chip_addr < FNAND_CONNECT_MAX_NUM); + FASSERT(instance_p->write_hw_ecc_p); + + FNandOpData op_data = + { + .page_addr = page_addr, + .page_buf = NULL, /* page 数据缓存空间 */ + .page_offset = 0, /* 从offset开始拷贝页数据 */ + .page_length = 0, /* 从offset开始拷贝页数据的长度 */ + .obb_required = 0, /* obb 是否读取的标志位,1 需要操作oob 区域 */ + .oob_buf = NULL, /* obb 数据缓存空间 */ + .oob_offset = 0, /* 从offset开始拷贝页数据 */ + .oob_length = 0, /* 从offset开始拷贝页数据的长度 */ + .chip_addr = chip_addr, /* 芯片地址 */ + }; + + if (buffer && (length > 0)) + { + op_data.page_buf = buffer; + op_data.page_length = length; + op_data.page_offset = page_copy_offset; + } + + if (oob_buffer && (oob_length > 0)) + { + op_data.obb_required = 1; + op_data.oob_buf = oob_buffer; + op_data.oob_length = oob_length; + op_data.oob_offset = oob_copy_offset; + } + + return instance_p->write_p(instance_p, &op_data); +} + +/** + * @name: FNandReadPage + * @msg: Read operations one page at a time, including reading page data and spare space data + * @note: + * @param {FNand} *instance_p is the pointer to the FNand instance. + * @param {u32} page_addr is the address to which the page needs to be readed + * @param {u8} *buffer is the buffer used by the user to read page data + * @param {u32} page_copy_offset is the offset of the page reading , Buffer read data from 0 + page_copy_offset of per page + * @param {u32} length is page data read length + * @param {u8} *oob_buffer is buffer that read data from the spare space + * @param {u32} oob_copy_offset is the offset of the spare space reading , Buffer reads data from page length + oob_copy_offset + * @param {u32} oob_length is the length to be written to the spare space + * @param {u32} chip_addr chip address + * @return {FError} FT_SUCCESS is read successful + */ +FError FNandReadPage(FNand *instance_p, u32 page_addr, u8 *buffer, u32 page_copy_offset, u32 length, u8 *oob_buffer, u32 oob_copy_offset, u32 oob_length, u32 chip_addr) +{ + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + FASSERT(chip_addr < FNAND_CONNECT_MAX_NUM); + FASSERT(instance_p->read_hw_ecc_p); + + FNandOpData op_data = + { + .page_addr = page_addr, + .page_buf = NULL, /* page 数据缓存空间 */ + .page_offset = 0, /* 从offset开始拷贝页数据 */ + .page_length = 0, /* 从offset开始拷贝页数据的长度 */ + .obb_required = 0, /* obb 是否读取的标志位,1 需要操作oob 区域 */ + .oob_buf = NULL, /* obb 数据缓存空间 */ + .oob_offset = 0, /* 从offset开始拷贝页数据 */ + .oob_length = 0, /* 从offset开始拷贝页数据的长度 */ + .chip_addr = chip_addr, /* 芯片地址 */ + }; + + /* clear buffer */ + if (buffer && (length > 0)) + { + op_data.page_buf = buffer; + op_data.page_length = length; + op_data.page_offset = page_copy_offset; + } + + if (oob_buffer && (oob_length > 0)) + { + op_data.obb_required = 1; + op_data.oob_buf = oob_buffer; + op_data.oob_length = oob_length; + op_data.oob_offset = oob_copy_offset; + } + + return instance_p->read_hw_ecc_p(instance_p, &op_data); +} + +FError FNandReadPageRaw(FNand *instance_p, u32 page_addr, u8 *buffer, u32 page_copy_offset, u32 length, u8 *oob_buffer, u32 oob_copy_offset, u32 oob_length, u32 chip_addr) +{ + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + FASSERT(chip_addr < FNAND_CONNECT_MAX_NUM); + FASSERT(instance_p->read_hw_ecc_p); + + FNandOpData op_data = + { + .page_addr = page_addr, + .page_buf = NULL, /* page 数据缓存空间 */ + .page_offset = 0, /* 从offset开始拷贝页数据 */ + .page_length = 0, /* 从offset开始拷贝页数据的长度 */ + .obb_required = 0, /* obb 是否读取的标志位,1 需要操作oob 区域 */ + .oob_buf = NULL, /* obb 数据缓存空间 */ + .oob_offset = 0, /* 从offset开始拷贝页数据 */ + .oob_length = 0, /* 从offset开始拷贝页数据的长度 */ + .chip_addr = chip_addr, /* 芯片地址 */ + }; + + /* clear buffer */ + if (buffer && (length > 0)) + { + op_data.page_buf = buffer; + op_data.page_length = length; + op_data.page_offset = page_copy_offset; + } + + if (oob_buffer && (oob_length > 0)) + { + op_data.obb_required = 1; + op_data.oob_buf = oob_buffer; + op_data.oob_length = oob_length; + op_data.oob_offset = oob_copy_offset; + } + + return instance_p->read_p(instance_p, &op_data); +} + +/** + * @name: FNandEraseBlock + * @msg: erase block data + * @note: 擦除之后增加read status 命令进行检查。(70h) + * @param {FNand} *instance_p is the pointer to the FNand instance. + * @param {u32} block is block number + * @param {u32} chip_addr is chip address + * @return {FError} FT_SUCCESS is erase is successful + */ +FError FNandEraseBlock(FNand *instance_p, u32 block, u32 chip_addr) +{ + u32 page_address; + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + FASSERT(chip_addr < FNAND_CONNECT_MAX_NUM); + page_address = block * instance_p->nand_geometry[chip_addr].pages_per_block; + return instance_p->erase_p(instance_p, page_address, chip_addr); +} + + + +/** + * @name: FNandReadPageOOb + * @msg: Read spare space fo per page + * @note: + * @param {FNand} *instance_p is the instance pointer + * @param {u32} page_addr is the Row Address of the spare space needs to be read + * @param {u8} *oob_buffer is the buffer used by the user to read spare space data + * @param {u32} oob_copy_offset is the offset of the spare space reading , Buffer reads data from page length + page_copy_offset + * @param {u32} oob_length is the length of data retrieved from spare space + * @param {u32} chip_addr is chip address + * @return {FError} FT_SUCCESS is read successful + */ +FError FNandReadPageOOb(FNand *instance_p, u32 page_addr, u8 *oob_buffer, u32 oob_copy_offset, u32 oob_length, u32 chip_addr) +{ + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + FASSERT(chip_addr < FNAND_CONNECT_MAX_NUM); + + FNandOpData op_data = + { + .page_addr = page_addr, + .page_buf = NULL, /* page 数据缓存空间 */ + .page_offset = 0, /* 从offset开始拷贝页数据 */ + .page_length = 0, /* 从offset开始拷贝页数据的长度 */ + .obb_required = 1, /* obb 是否读取的标志位,1 需要操作oob 区域 */ + .oob_buf = oob_buffer, /* obb 数据缓存空间 */ + .oob_offset = oob_copy_offset, /* 从offset开始拷贝页数据 */ + .oob_length = oob_length, /* 从offset开始拷贝页数据的长度 */ + .chip_addr = chip_addr, /* 芯片地址 */ + }; + + return instance_p->read_oob_p(instance_p, &op_data); +} + +/** + * @name: FNandWritePageOOb + * @msg: write data to the spare space + * @note: + * @param {FNand} *instance_p is the instance pointer + * @param {u32} page_addr is the Row Address of the spare space needs to be write + * @param {u8} *oob_buffer is buffer that writes data to the spare space + * @param {u32} page_copy_offset is the offset of the spare space writing , Buffer write data to page length + page_copy_offset + * @param {u32} oob_length is the length to be written to the spare space + * @param {u32} chip_addr is chip address + * @return {FError} FT_SUCCESS is write successful + */ +FError FNandWritePageOOb(FNand *instance_p, u32 page_addr, u8 *oob_buffer, u32 page_copy_offset, u32 oob_length, u32 chip_addr) +{ + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + FASSERT(chip_addr < FNAND_CONNECT_MAX_NUM); + + FNandOpData op_data = + { + .page_addr = page_addr, + .page_buf = NULL, /* page 数据缓存空间 */ + .page_offset = 0, /* 从offset开始拷贝页数据 */ + .page_length = 0, /* 从offset开始拷贝页数据的长度 */ + .obb_required = 1, /* obb 是否读取的标志位,1 需要操作oob 区域 */ + .oob_buf = oob_buffer, /* obb 数据缓存空间 */ + .oob_offset = page_copy_offset, /* 从offset开始拷贝页数据 */ + .oob_length = oob_length, /* 从offset开始拷贝页数据的长度 */ + .chip_addr = chip_addr, /* 芯片地址 */ + }; + + return instance_p->write_oob_p(instance_p, &op_data); +} diff --git a/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand.h b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand.h new file mode 100644 index 0000000000..2ded2317a5 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand.h @@ -0,0 +1,302 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fnand.h + * Date: 2022-05-07 15:40:42 + * LastEditTime: 2022-05-07 15:40:42 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ +#ifndef DRIVERS_NAND_FNAND_H +#define DRIVERS_NAND_FNAND_H + +#ifdef __cplusplus +extern "C" +{ +#endif + + +#include "ftypes.h" +#include "fassert.h" +#include "fnand_dma.h" +#include "fnand_timing.h" +#include "fparameters.h" +#include "fkernel.h" + +#define FNAND_ERR_OPERATION FT_CODE_ERR(ErrModBsp, ErrNand, 0x1u) +#define FNAND_ERR_INVAILD_PARAMETER FT_CODE_ERR(ErrModBsp, ErrNand, 0x2u) +#define FNAND_IS_BUSY FT_CODE_ERR(ErrModBsp, ErrNand, 0x3u) +#define FNAND_OP_TIMEOUT FT_CODE_ERR(ErrModBsp, ErrNand, 0x4u) +#define FNAND_VALUE_ERROR FT_CODE_ERR(ErrModBsp, ErrNand, 0x7u) +#define FNAND_VALUE_FAILURE FT_CODE_ERR(ErrModBsp, ErrNand, 0x8u) +#define FNAND_NOT_FET_TOGGLE_MODE FT_CODE_ERR(ErrModBsp, ErrNand, 0xCu) +#define FNAND_ERR_READ_ECC FT_CODE_ERR(ErrModBsp, ErrNand, 0xBu) +#define FNAND_ERR_IRQ_LACK_OF_CALLBACK FT_CODE_ERR(ErrModBsp, ErrNand, 0xCu) +#define FNAND_ERR_IRQ_OP_FAILED FT_CODE_ERR(ErrModBsp, ErrNand, 0xdu) +#define FNAND_ERR_NOT_MATCH FT_CODE_ERR(ErrModBsp, ErrNand, 0xEu) + + + +#define FNAND_MAX_BLOCKS 32768 /* Max number of Blocks */ +#define FNAND_MAX_PAGE_SIZE 16384 /* Max page size of NAND \ + flash */ +#define FNAND_MAX_SPARE_SIZE 1024 /* Max spare bytes of a NAND \ + flash page */ + + +/* dma */ +#define FNAND_DMA_MAX_LENGTH (32*1024) + +/* options */ +/* These constants are used as option to FNandSetOption() */ +#define FNAND_OPS_INTER_MODE_SELECT 1U /* */ + + +/* These constants are used as parameters to FNandSetIsrHandler() */ +#define FNAND_WORK_MODE_POLL 0U +#define FNAND_WORK_MODE_ISR 1U + + +/* NAND Flash Interface */ + +#define FNAND_ONFI_MODE 0U +#define FNAND_TOGGLE_MODE 1U + + +typedef enum +{ + FNAND_ASYNC_TIM_INT_MODE0 = 0, + FNAND_ASYNC_TIM_INT_MODE1, + FNAND_ASYNC_TIM_INT_MODE2, + FNAND_ASYNC_TIM_INT_MODE3, + FNAND_ASYNC_TIM_INT_MODE4, +} FNandAsyncTimint; + +typedef enum +{ + FNAND_CMD_TYPE = 0, /* 采用cmd 类型的操作类型 */ + FNAND_WRITE_PAGE_TYPE, /* PAGE program 操作 */ + FNAND_READ_PAGE_TYPE, /* PAGE read 操作 */ + FNAND_WAIT_ECC_TYPE, /* Waiting ECC FINISH 操作 */ + FNAND_TYPE_NUM +} FNandOperationType; + +/* Irq Callback events */ +typedef enum +{ + FNAND_IRQ_BUSY_EVENT = 0,/* nandflash控制器忙状态中断状态位 */ + FNAND_IRQ_DMA_BUSY_EVENT, /* dma控制器忙状态中断状态位 */ + FNAND_IRQ_DMA_PGFINISH_EVENT, /* dma页操作完成中断状态位 */ + FNAND_IRQ_DMA_FINISH_EVENT, /* dma操作完成中断完成中断状态位 */ + FNAND_IRQ_FIFO_EMP_EVENT, /* fifo为空中断状态位 */ + FNAND_IRQ_FIFO_FULL_EVENT, /* fifo为满中断状态位 */ + FNAND_IRQ_FIFO_TIMEOUT_EVENT, /* fifo超时中断状态位 */ + FNAND_IRQ_CMD_FINISH_EVENT, /* nand接口命令完成中断状态位 */ + FNAND_IRQ_PGFINISH_EVENT, /* nand接口页操作完成中断状态位 */ + FNAND_IRQ_RE_EVENT, /* re_n门控打开中断状态位 */ + FNAND_IRQ_DQS_EVENT, /* dqs门控打开中断状态位 */ + FNAND_IRQ_RB_EVENT, /* rb_n信号busy中断状态位 */ + FNAND_IRQ_ECC_FINISH_EVENT, /* ecc完成中断状态蔽位 */ + FNAND_IRQ_ECC_ERR_EVENT /* ecc正确中断状态蔽位 */ +} FNAND_CALL_BACK_EVENT; + +typedef struct +{ + u32 bytes_per_page; /* Bytes per page */ + u32 spare_bytes_per_page; /* Size of spare area in bytes */ + u32 pages_per_block; /* Pages per block */ + u32 blocks_per_lun; /* Bocks per LUN */ + u8 num_lun; /* Total number of LUN */ + u8 flash_width; /* Data width of flash device */ + u64 num_pages; /* Total number of pages in device */ + u64 num_blocks; /* Total number of blocks in device */ + u64 block_size; /* Size of a block in bytes */ + u64 device_size; /* Total device size in bytes */ + u8 rowaddr_cycles; /* Row address cycles */ + u8 coladdr_cycles; /* Column address cycles */ + u32 hw_ecc_steps; /* number of ECC steps per page */ + u32 hw_ecc_length; /* 产生硬件ecc校验参数的个数 */ + u32 ecc_offset; /* spare_bytes_per_page - hw_ecc_length = obb存放硬件ecc校验参数页位置的偏移 */ + u32 ecc_step_size; /* 进行读写操作时,单次ecc 的步骤的跨度 */ +} FNandNandGeometry; + +typedef enum +{ + FNAND_ASYN_SDR = 0, /* ONFI & Toggle async */ + FNAND_ONFI_DDR = 1, /* ONFI sync */ + FNAND_TOG_ASYN_DDR = 2 /* Toggle async */ +} FNandInterMode; + +typedef enum +{ + FNAND_TIMING_MODE0 = 0, + FNAND_TIMING_MODE1 = 1, + FNAND_TIMING_MODE2 = 2, + FNAND_TIMING_MODE3 = 3, + FNAND_TIMING_MODE4 = 4, + FNAND_TIMING_MODE5 = 5, +} FNandTimingMode; + +typedef struct +{ + u32 instance_id; /* Id of device*/ + u32 irq_num; /* Irq number */ + volatile uintptr_t base_address; + u32 ecc_strength; /* 每次ecc 步骤纠正的位数 */ + u32 ecc_step_size; /* 进行读写操作时,单次ecc 的步骤的跨度 */ +} FNandConfig; + +/** + * Bad block pattern + */ +typedef struct +{ + u32 options; /**< Options to search the bad block pattern */ + u32 offset; /**< Offset to search for specified pattern */ + u32 length; /**< Number of bytes to check the pattern */ + u8 pattern[2]; /**< Pattern format to search for */ +} FNandBadBlockPattern; + + +typedef struct +{ + u32 page_addr; + u8 *page_buf; /* page 数据缓存空间 */ + u32 page_offset; /* 从offset开始拷贝页数据 */ + u32 page_length; /* 从offset开始拷贝页数据的长度 */ + s32 obb_required; /* obb 是否读取的标志位,1 需要操作oob 区域 */ + u8 *oob_buf; /* obb 数据缓存空间 */ + u32 oob_offset; /* 从offset开始拷贝页数据 */ + u32 oob_length; /* 从offset开始拷贝页数据的长度 */ + u32 chip_addr; /* 芯片地址 */ +} FNandOpData; + +/** + * Bad block table descriptor + */ +typedef struct +{ + u32 page_offset; /* Page offset where BBT resides */ + u32 sig_offset; /* Signature offset in Spare area */ + u32 ver_offset; /* Offset of BBT version */ + u32 sig_length; /* Length of the signature */ + u32 max_blocks; /* Max blocks to search for BBT */ + char signature[4]; /* BBT signature */ + u8 version; /* BBT version */ + u32 valid; /* BBT descriptor is valid or not */ +} FNandBbtDesc; + +typedef struct +{ + u8 bbt[FNAND_MAX_BLOCKS >> 2]; + FNandBbtDesc bbt_desc; /* Bad block table descriptor */ + FNandBbtDesc bbt_mirror_desc; /* Mirror BBT descriptor */ + FNandBadBlockPattern bb_pattern; /* Bad block pattern to search */ +} FNandBadBlockManager; + +/* DMA */ + +/* DMA buffer */ +struct FNandDmaBuffer +{ + u8 data_buffer[FNAND_DMA_MAX_LENGTH]; +} __attribute__((packed)) __attribute__((aligned(128))); + +/* operation api */ +typedef struct _FNand FNand; + +typedef FError(*FNandTransferP)(FNand *instance_p, FNandOpData *op_data_p); +typedef FError(*FNandEraseP)(FNand *instance_p, u32 page_address, u32 chip_addr); + +typedef void (*FnandIrqEventHandler)(void *args, FNAND_CALL_BACK_EVENT event) ; +typedef FError(*FNandOperationWaitIrqCallback)(void *args); + + +typedef struct +{ + u8 data[8]; + u32 len; + +} FNandId; + +typedef struct _FNand +{ + u32 is_ready; /* Device is ininitialized and ready*/ + FNandConfig config; + u32 work_mode; /* NAND controler work mode */ + + /* nand flash info */ + FNandInterMode inter_mode[FNAND_CONNECT_MAX_NUM]; /* NAND controler timing work mode */ + FNandTimingMode timing_mode[FNAND_CONNECT_MAX_NUM]; + u32 nand_flash_interface[FNAND_CONNECT_MAX_NUM] ; /* Nand Flash Interface , followed by FNAND_ONFI_MODE \ FNAND_TOGGLE_MODE*/ + + struct FNandDmaBuffer dma_data_buffer; /* DMA data buffer */ + struct FNandDmaBuffer descriptor_buffer; /* DMA descriptor */ + struct FNandSdrTimings sdr_timing; /* SDR NAND chip timings */ + + /* bbm */ + FNandBadBlockManager bbt_manager[FNAND_CONNECT_MAX_NUM]; /* bad block manager handler */ + /* nand detect */ + FNandNandGeometry nand_geometry[FNAND_CONNECT_MAX_NUM]; /* nand flash infomation */ + /* dma 页操作 */ + FnandIrqEventHandler irq_event_fun_p; /* Interrupt event response function */ + void *irq_args; + + FNandOperationWaitIrqCallback wait_irq_fun_p; /* The NAND controller operates the wait function */ + void *wait_args; + + /* operations */ + FNandTransferP write_p ; /* Write page function */ + FNandTransferP read_p ; /* Read page function */ + FNandTransferP write_oob_p ; /* Write page spare space function */ + FNandTransferP read_oob_p ; /* Read page spare space function */ + FNandTransferP write_hw_ecc_p ; /* Write page with hardware function */ + FNandTransferP read_hw_ecc_p ; /* Read page with hardware function */ + FNandEraseP erase_p; /* Erase block function */ +} FNand; + +FNandConfig *FNandLookupConfig(u32 instance_id); +FError FNandCfgInitialize(FNand *instance_p, + FNandConfig *config_p); +FError FNandScan(FNand *instance_p); +FError FNandSetOption(FNand *instance_p, u32 options, u32 value); + +/* API */ +FError FNandWritePage(FNand *instance_p, u32 page_addr, u8 *buffer, u32 page_copy_offset, u32 length, u8 *oob_buffer, u32 oob_copy_offset, u32 oob_length, u32 chip_addr); +FError FNandWritePageRaw(FNand *instance_p, u32 page_addr, u8 *buffer, u32 page_copy_offset, u32 length, u8 *oob_buffer, u32 oob_copy_offset, u32 oob_length, u32 chip_addr); +FError FNandReadPage(FNand *instance_p, u32 page_addr, u8 *buffer, u32 page_copy_offset, u32 length, u8 *oob_buffer, u32 oob_copy_offset, u32 oob_length, u32 chip_addr); +FError FNandEraseBlock(FNand *instance_p, u32 block, u32 chip_addr); +FError FNandReadPageOOb(FNand *instance_p, u32 page_addr, u8 *oob_buffer, u32 oob_copy_offset, u32 oob_length, u32 chip_addr); +FError FNandWritePageOOb(FNand *instance_p, u32 page_addr, u8 *oob_buffer, u32 page_copy_offset, u32 oob_length, u32 chip_addr); +FError FNandReadPageRaw(FNand *instance_p, u32 page_addr, u8 *buffer, u32 page_copy_offset, u32 length, u8 *oob_buffer, u32 oob_copy_offset, u32 oob_length, u32 chip_addr); +/* irq */ +void FNandSetIsrHandler(FNand *instance_p, FnandIrqEventHandler event_p, void *irq_args); +void FNandIrqHandler(s32 vector, void *param); +void FNandOperationWaitIrqRegister(FNand *instance_p, FNandOperationWaitIrqCallback wait_irq_fun_p, void *wait_args); +void FNandIrqDisable(FNand *instance_p, u32 int_mask); +void FNandIsrEnable(FNand *instance_p, u32 int_mask); +/* bbm */ +void FNandInitBbtDesc(FNand *instance_p); +FError FNandScanBbt(FNand *instance_p, u32 target_addr); +FError FNandIsBlockBad(FNand *instance_p, u32 block, u32 target_addr); +FError FNandMarkBlockBad(FNand *instance_p, u32 block, u32 chip_addr); +#ifdef __cplusplus +} +#endif + + + +#endif // ! diff --git a/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_bbm.c b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_bbm.c new file mode 100644 index 0000000000..50bfada9de --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_bbm.c @@ -0,0 +1,882 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fnand_bbm.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:56:12 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#include "fnand_bbm.h" +#include "fnand.h" +#include "fparameters.h" + +#include +#include "fdebug.h" +#define FNAND_BBM_DEBUG_TAG "FNAND_BBM" +#define FNAND_BBM_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(FNAND_BBM_DEBUG_TAG, format, ##__VA_ARGS__) +#define FNAND_BBM_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(FNAND_BBM_DEBUG_TAG, format, ##__VA_ARGS__) +#define FNAND_BBM_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(FNAND_BBM_DEBUG_TAG, format, ##__VA_ARGS__) + +static FError FNandWriteBbt(FNand *instance_p, + FNandBbtDesc *desc_p, + FNandBbtDesc *mirror_desc_p, + u32 chip_addr); + +/** + * @name: FNandInitBbtDesc + * @msg: This function initializes the Bad Block Table(BBT) descriptors with a + * predefined pattern for searching Bad Block Table(BBT) in flash. + * @param {FNand} *instance_p is the pointer to the FNand instance. + * @return {*} + * @note: + */ +void FNandInitBbtDesc(FNand *instance_p) +{ + u32 i; + int index; + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + + for (i = 0; i < FNAND_CONNECT_MAX_NUM; i++) + { + /* + * Initialize primary Bad Block Table(BBT) + */ + instance_p->bbt_manager[i].bbt_desc.page_offset = FNAND_BBT_DESC_PAGE_OFFSET; + instance_p->bbt_manager[i].bbt_desc.sig_offset = FNAND_BBT_DESC_SIG_OFFSET; + instance_p->bbt_manager[i].bbt_desc.ver_offset = FNAND_BBT_DESC_VER_OFFSET; + instance_p->bbt_manager[i].bbt_desc.max_blocks = FNAND_BBT_DESC_MAX_BLOCKS; + instance_p->bbt_manager[i].bbt_desc.sig_length = FNAND_BBT_DESC_SIG_LEN; + strcpy(&instance_p->bbt_manager[i].bbt_desc.signature[0], "Bbt0"); + instance_p->bbt_manager[i].bbt_desc.version = 0; + instance_p->bbt_manager[i].bbt_desc.valid = 0; + /* + * Initialize mirror Bad Block Table(BBT) + */ + instance_p->bbt_manager[i].bbt_mirror_desc.page_offset = FNAND_BBT_DESC_PAGE_OFFSET; + instance_p->bbt_manager[i].bbt_mirror_desc.sig_offset = FNAND_BBT_DESC_SIG_OFFSET; + instance_p->bbt_manager[i].bbt_mirror_desc.ver_offset = FNAND_BBT_DESC_VER_OFFSET; + instance_p->bbt_manager[i].bbt_mirror_desc.sig_length = FNAND_BBT_DESC_SIG_LEN; + instance_p->bbt_manager[i].bbt_mirror_desc.max_blocks = FNAND_BBT_DESC_MAX_BLOCKS; + strcpy(&instance_p->bbt_manager[i].bbt_mirror_desc.signature[0], "1tbB"); + instance_p->bbt_manager[i].bbt_mirror_desc.version = 0; + instance_p->bbt_manager[i].bbt_mirror_desc.valid = 0; + + /* + * Initialize Bad block search pattern structure + */ + if (instance_p->nand_geometry[i].bytes_per_page > 512) + { + /* For flash page size > 512 bytes */ + instance_p->bbt_manager[i].bb_pattern.options = FNAND_BBT_SCAN_2ND_PAGE; + instance_p->bbt_manager[i].bb_pattern.offset = + FNAND_BB_PATTERN_OFFSET_LARGE_PAGE; + instance_p->bbt_manager[i].bb_pattern.length = + FNAND_BB_PATTERN_LENGTH_LARGE_PAGE; + } + else + { + instance_p->bbt_manager[i].bb_pattern.options = FNAND_BBT_SCAN_2ND_PAGE; + instance_p->bbt_manager[i].bb_pattern.offset = + FNAND_BB_PATTERN_OFFSET_SMALL_PAGE; + instance_p->bbt_manager[i].bb_pattern.length = + FNAND_BB_PATTERN_LENGTH_SMALL_PAGE; + } + for (index = 0; index < FNAND_BB_PATTERN_LENGTH_LARGE_PAGE; index++) + { + instance_p->bbt_manager[i].bb_pattern.pattern[index] = FNAND_BB_PATTERN; + } + } +} + +/** + * @name: FNandConvertBbt + * @msg: Convert bitmask read in flash to information stored in RAM + * @return {*} + * @note: + * @param {FNand} *instance_p is the pointer to the FNand instance. + * @param {u8} *buf is buffer to store the bitmask + * @param {u32} chip_addr is chip address + */ +static void FNandConvertBbt(FNand *instance_p, u8 *buf, u32 chip_addr) +{ + u32 block_offset; + u32 block_shift; + u32 data; + u8 block_type; + u32 block_index; + u32 bbtlen = instance_p->nand_geometry[chip_addr].num_blocks >> + FNAND_BBT_BLOCK_SHIFT; + + for (block_offset = 0; block_offset < bbtlen; block_offset++) + { + data = buf[block_offset]; + + /* + * Clear the RAM based Bad Block Table(BBT) contents + */ + instance_p->bbt_manager[chip_addr].bbt[block_offset] = 0x0; + + /* + * Loop through the every 4 blocks in the bitmap + */ + for (block_index = 0; block_index < FNAND_BBT_ENTRY_NUM_BLOCKS; + block_index++) + { + block_shift = FNAND_BBTBLOCKSHIFT(block_index); + block_type = (data >> block_shift) & + FNAND_BLOCK_TYPE_MASK; + // FNAND_BBM_DEBUG_E("block_offset %d,block_shift %d",block_offset,block_shift); + // FNAND_BBM_DEBUG_E("block_type is %d \r\n",block_type); + switch (block_type) + { + case FNAND_FLASH_BLOCK_FACTORY_BAD: + /* Factory bad block */ + instance_p->bbt_manager[chip_addr].bbt[block_offset] |= + FNAND_BLOCK_FACTORY_BAD << block_shift; + break; + case FNAND_FLASH_BLOCK_RESERVED: + /* Reserved block */ + instance_p->bbt_manager[chip_addr].bbt[block_offset] |= + FNAND_BLOCK_RESERVED << block_shift; + break; + case FNAND_FLASH_BLOCK_BAD: + /* Bad block due to wear */ + instance_p->bbt_manager[chip_addr].bbt[block_offset] |= + FNAND_BLOCK_BAD << block_shift; + break; + default: + /* Good block */ + /* The BBT entry already defaults to + * zero */ + break; + } + } + } +} + +static FError FNandUpdateBbt(FNand *instance_p, u32 chip_addr) +{ + FError ret; + u8 version; + + /* + * Update the version number + */ + version = instance_p->bbt_manager[chip_addr].bbt_desc.version; + instance_p->bbt_manager[chip_addr].bbt_desc.version = (version + 1) % 256; + version = instance_p->bbt_manager[chip_addr].bbt_mirror_desc.version; + instance_p->bbt_manager[chip_addr].bbt_mirror_desc.version = (version + 1) % 256; + + /* + * Update the primary Bad Block Table(BBT) in flash + */ + ret = FNandWriteBbt(instance_p, + &instance_p->bbt_manager[chip_addr].bbt_desc, + &instance_p->bbt_manager[chip_addr].bbt_mirror_desc, chip_addr); + if (ret != FT_SUCCESS) + { + return ret; + } + + /* + * Update the mirrored Bad Block Table(BBT) in flash + */ + ret = FNandWriteBbt(instance_p, + &instance_p->bbt_manager[chip_addr].bbt_mirror_desc, + &instance_p->bbt_manager[chip_addr].bbt_desc, chip_addr); + if (ret != FT_SUCCESS) + { + return ret; + } + + return FT_SUCCESS; +} + +static FError FNandMarkBbt(FNand *instance_p, + FNandBbtDesc *desc_p, + u32 chip_addr) +{ + + u32 block_index; + u32 block_offset; + u8 block_shift; + u8 old_val; + u8 new_val; + FError ret; + u32 updatebbt = 0; + u32 index; + + /* + * Mark the last four blocks as Reserved + */ + block_index = instance_p->nand_geometry[chip_addr].num_blocks - desc_p->max_blocks; + + for (index = 0; index < desc_p->max_blocks; index++, block_index++) + { + block_offset = block_index >> FNAND_BBT_BLOCK_SHIFT; + block_shift = FNAND_BBTBLOCKSHIFT(block_index); + old_val = instance_p->bbt_manager[chip_addr].bbt[block_offset]; + new_val = old_val | (FNAND_BLOCK_RESERVED << block_shift); + instance_p->bbt_manager[chip_addr].bbt[block_offset] = new_val; + + if (old_val != new_val) + { + updatebbt = 1; + } + } + + /* + * Update the BBT to flash + */ + if (updatebbt) + { + ret = FNandUpdateBbt(instance_p, chip_addr); + if (ret != FT_SUCCESS) + { + return ret; + } + } + return FT_SUCCESS; +} + +static FError FNandWriteBbt(FNand *instance_p, + FNandBbtDesc *desc_p, + FNandBbtDesc *mirror_desc_p, + u32 chip_addr) +{ + u64 offset; + u32 block = instance_p->nand_geometry[chip_addr].num_blocks - 1; + u8 buf[FNAND_MAX_BLOCKS >> FNAND_BBT_BLOCK_SHIFT]; + u8 sparebuf[FNAND_MAX_SPARE_SIZE]; + u8 mask[4] = {0x00, 0x01, 0x02, 0x03}; + u8 Data; + u32 block_offset; + u32 block_shift; + FError ret; + u32 block_index; + u32 index; + u8 block_type; + u32 bbtlen = instance_p->nand_geometry[chip_addr].num_blocks >> + FNAND_BBT_BLOCK_SHIFT; + FNAND_BBM_DEBUG_I("FNandWriteBbt bbtlen is %d", bbtlen); + + /* + * Find a valid block to write the Bad Block Table(BBT) + */ + if (!desc_p->valid) + { + for (index = 0; index < desc_p->max_blocks; index++) + { + block = (block - index); + block_offset = block >> FNAND_BBT_BLOCK_SHIFT; + block_shift = FNAND_BBTBLOCKSHIFT(block); + block_type = (instance_p->bbt_manager[chip_addr].bbt[block_offset] >> + block_shift) & + FNAND_BLOCK_TYPE_MASK; + switch (block_type) + { + case FNAND_BLOCK_BAD: + case FNAND_BLOCK_FACTORY_BAD: + continue; + default: + /* Good Block */ + break; + } + desc_p->page_offset = block * + instance_p->nand_geometry[chip_addr].pages_per_block; + if (desc_p->page_offset != mirror_desc_p->page_offset) + { + /* Free block found */ + desc_p->valid = 1; + break; + } + } + + /* + * Block not found for writing Bad Block Table(BBT) + */ + if (index >= desc_p->max_blocks) + { + return FNAND_VALUE_FAILURE; + } + } + else + { + block = desc_p->page_offset / instance_p->nand_geometry[chip_addr].pages_per_block; + } + + /* + * Convert the memory based BBT to flash based table + */ + memset(buf, 0xff, bbtlen); + + /* + * Loop through the number of blocks + */ + for (block_offset = 0; block_offset < bbtlen; block_offset++) + { + Data = instance_p->bbt_manager[chip_addr].bbt[block_offset]; + /* + * Calculate the bit mask for 4 blocks at a time in loop + */ + for (block_index = 0; block_index < FNAND_BBT_ENTRY_NUM_BLOCKS; + block_index++) + { + block_shift = FNAND_BBTBLOCKSHIFT(block_index); + buf[block_offset] &= ~(mask[Data & + FNAND_BLOCK_TYPE_MASK] + << block_shift); + Data >>= FNAND_BBT_BLOCK_SHIFT; + } + // FNAND_BBM_DEBUG_I("buf[%d] 0x%x",block_offset,buf[block_offset]); + } + + /* + * Write the Bad Block Table(BBT) to flash + */ + // printf("erase_p is %p \r\n",instance_p->erase_p); + ret = FNandEraseBlock(instance_p, block, chip_addr); + //instance_p->erase_p(instance_p, block, chip_addr); + if (ret != FT_SUCCESS) + { + return ret; + } + + /* + * Write the signature and version in the spare data area + */ + memset(sparebuf, 0xff, instance_p->nand_geometry[chip_addr].spare_bytes_per_page); + memcpy(sparebuf + desc_p->sig_offset, &desc_p->signature[0], + desc_p->sig_length); + memcpy(sparebuf + desc_p->ver_offset, &desc_p->version, 1); + + /* + * Write the BBT to page offset + */ + FNandWritePage(instance_p, desc_p->page_offset, &buf[0], 0, bbtlen, sparebuf, 0, sizeof(sparebuf), chip_addr); + if (ret != FT_SUCCESS) + { + return ret; + } + return FT_SUCCESS; +} + +static void FNandCreateBbt(FNand *instance_p, u32 chip_addr) +{ + u32 block_index; + u32 page_index; + u32 length; + u32 block_offset; + u32 block_shift; + u32 num_pages; + u32 page; + u8 buf[FNAND_MAX_SPARE_SIZE]; + u32 bbt_len = instance_p->nand_geometry[chip_addr].num_blocks >> + FNAND_BBT_BLOCK_SHIFT; + FError ret; + /* + * Number of pages to search for bad block pattern + */ + if (instance_p->bbt_manager[chip_addr].bb_pattern.options & FNAND_BBT_SCAN_2ND_PAGE) + { + num_pages = 2; + } + else + { + num_pages = 1; + } + + /* + * Zero the RAM based Bad Block Table(BBT) entries + */ + memset(&instance_p->bbt_manager[chip_addr].bbt[0], 0, bbt_len); + + /* + * Scan all the blocks for factory marked bad blocks + */ + for (block_index = 0; block_index < + instance_p->nand_geometry[chip_addr].num_blocks; + block_index++) + { + /* + * Block offset in Bad Block Table(BBT) entry + */ + block_offset = block_index >> FNAND_BBT_BLOCK_SHIFT; + /* + * Block shift value in the byte + */ + block_shift = FNAND_BBTBLOCKSHIFT(block_index); + page = block_index * instance_p->nand_geometry[chip_addr].pages_per_block; + + /* + * Search for the bad block pattern + */ + for (page_index = 0; page_index < num_pages; page_index++) + { + ret = FNandReadPageOOb(instance_p, page + page_index, buf, 0, sizeof(buf), chip_addr); + if (ret != FT_SUCCESS) + { + FNAND_BBM_DEBUG_E("%s FNandReadPageOOb is error", __func__); + /* Marking as bad block */ + instance_p->bbt_manager[chip_addr].bbt[block_offset] |= + (FNAND_BLOCK_FACTORY_BAD << block_shift); + break; + } + + /* + * Read the spare bytes to check for bad block + * pattern + */ + for (length = 0; length < + instance_p->bbt_manager[chip_addr].bb_pattern.length; + length++) + { + if (buf[instance_p->bbt_manager[chip_addr].bb_pattern.offset + length] != + instance_p->bbt_manager[chip_addr].bb_pattern.pattern[length]) + { + /* Bad block found */ + instance_p->bbt_manager[chip_addr].bbt[block_offset] |= + (FNAND_BLOCK_FACTORY_BAD << block_shift); + FNAND_BBM_DEBUG_E("Bad block found block is %d", page + page_index); + break; + } + } + } + } +} + +FError FNandSearchBbt(FNand *instance_p, FNandBbtDesc *desc, u32 chip_addr) +{ + u32 start_block; + u32 sig_offset; + u32 ver_offset; + u32 max_blocks; + u32 pageoff; + u32 sig_length; + u8 buf[FNAND_MAX_SPARE_SIZE]; + u32 block; + u32 offset; + FError ret; + + start_block = instance_p->nand_geometry[chip_addr].num_blocks - 1; /* start block is last block start */ + sig_offset = desc->sig_offset; + ver_offset = desc->ver_offset; + max_blocks = desc->max_blocks; + sig_length = desc->sig_length; + FNAND_BBM_DEBUG_I("FNandSearchBbt is start 0x%x", start_block); + FNAND_BBM_DEBUG_I("pages_per_block is start %d", instance_p->nand_geometry[chip_addr].pages_per_block); + for (block = 0; block < max_blocks; block++) + { + pageoff = (start_block - block) * + instance_p->nand_geometry[chip_addr].pages_per_block; + FNAND_BBM_DEBUG_I("block 0x%x", block); + FNAND_BBM_DEBUG_I("%s, pageoff is 0x%x", __func__, pageoff); + ret = FNandReadPageOOb(instance_p, pageoff, buf, 0, sizeof(buf), chip_addr); + if (ret != FT_SUCCESS) + { + continue; + } + + /* + * Check the Bad Block Table(BBT) signature + */ + for (offset = 0; offset < sig_length; offset++) + { + if (buf[offset + sig_offset] != desc->signature[offset]) + { + break; /* Check the next blocks */ + } + } + if (offset >= sig_length) + { + /* + * Bad Block Table(BBT) found + */ + desc->page_offset = pageoff; + desc->version = buf[ver_offset]; + desc->valid = 1; + return FT_SUCCESS; + } + } + /* + * Bad Block Table(BBT) not found + */ + return FNAND_VALUE_ERROR; +} + +/** + * @name: + * @msg: + * @return {*} + * @note: + * @param {FNand} *instance_p + * @param {u32} chip_addr + */ +FError FNandReadBbt(FNand *instance_p, u32 chip_addr) +{ + u64 offset; + u8 buf[FNAND_MAX_BLOCKS >> FNAND_BBT_BLOCK_SHIFT]; + FError status1; + FError status2; + FError ret; + u32 bbtlen; + + + FNandBbtDesc *desc_p = &instance_p->bbt_manager[chip_addr].bbt_desc; + FNandBbtDesc *mirror_desc_p = &instance_p->bbt_manager[chip_addr].bbt_mirror_desc; + + bbtlen = instance_p->nand_geometry[chip_addr].num_blocks >> FNAND_BBT_BLOCK_SHIFT; /* 根据nand 介质信息获取的总块数,除以4 的含义为每字节存储4个块信息 */ + FNAND_BBM_DEBUG_I("FNandReadBbt ,bbtlen is %d", bbtlen); + + status1 = FNandSearchBbt(instance_p, desc_p, chip_addr); + status2 = FNandSearchBbt(instance_p, mirror_desc_p, chip_addr); + + if ((status1 != FT_SUCCESS) && (status2 != FT_SUCCESS)) + { + FNAND_BBM_DEBUG_E("FNandReadBbt error status1 %x, status2 %x", status1, status2); + return FNAND_VALUE_FAILURE; + } + + /* + * Bad Block Table found + */ + + if (desc_p->valid && mirror_desc_p->valid) + { + if (desc_p->version > mirror_desc_p->version) + { + /* + * Valid BBT & Mirror BBT found + */ + FNAND_BBM_DEBUG_I("desc_p->version > mirror_desc_p->version read page is %d", desc_p->page_offset); + ret = FNandReadPage(instance_p, desc_p->page_offset, buf, 0, bbtlen, NULL, 0, 0, chip_addr); + if (ret != FT_SUCCESS) + { + FNAND_BBM_DEBUG_I("desc_p->version > mirror_desc_p->version read page is error 0x%x", ret); + return ret; + } + /* + * Convert flash BBT to memory based BBT + */ + FNandConvertBbt(instance_p, &buf[0], chip_addr); + mirror_desc_p->version = desc_p->version; + + /* + * Write the BBT to Mirror BBT location in flash + */ + ret = FNandWriteBbt(instance_p, mirror_desc_p, + desc_p, chip_addr); + if (ret != FT_SUCCESS) + { + return ret; + } + } + else if (desc_p->version < mirror_desc_p->version) + { + FNAND_BBM_DEBUG_I("desc_p->version < mirror_desc_p->version read page is %d", mirror_desc_p->page_offset); + ret = FNandReadPage(instance_p, mirror_desc_p->page_offset, buf, 0, bbtlen, NULL, 0, 0, chip_addr); + if (ret != FT_SUCCESS) + { + FNAND_BBM_DEBUG_I("desc_p->version < mirror_desc_p->version read page is error 0x%x", ret); + return ret; + } + /* + * Convert flash BBT to memory based BBT + */ + FNandConvertBbt(instance_p, &buf[0], chip_addr); + desc_p->version = mirror_desc_p->version; + + /* + * Write the BBT to Mirror BBT location in flash + */ + ret = FNandWriteBbt(instance_p, desc_p, + mirror_desc_p, chip_addr); + if (ret != FT_SUCCESS) + { + return ret; + } + } + else + { + /* Both are up-to-date */ + FNAND_BBM_DEBUG_I("Both are up-to-date read page is %d", desc_p->page_offset); + + ret = FNandReadPage(instance_p, desc_p->page_offset, buf, 0, bbtlen, NULL, 0, 0, chip_addr); + if (ret != FT_SUCCESS) + { + FNAND_BBM_DEBUG_I("Both are up-to-date read page is error 0x%x", ret); + return ret; + } + + /* + * Convert flash BBT to memory based BBT + */ + FNandConvertBbt(instance_p, &buf[0], chip_addr); + } + } + else if (desc_p->valid) + { + /* + * Valid Primary BBT found + */ + + FNAND_BBM_DEBUG_I("Valid Primary BBT found is %d", desc_p->page_offset); + + ret = FNandReadPage(instance_p, desc_p->page_offset, buf, 0, bbtlen, NULL, 0, 0, chip_addr); + if (ret != FT_SUCCESS) + { + FNAND_BBM_DEBUG_I("Valid Primary BBT found is error 0x%x", ret); + return ret; + } + + + /* + * Convert flash BBT to memory based BBT + */ + FNandConvertBbt(instance_p, &buf[0], chip_addr); + desc_p->version = mirror_desc_p->version; + + /* + * Write the BBT to Mirror BBT location in flash + */ + ret = FNandWriteBbt(instance_p, mirror_desc_p, + desc_p, chip_addr); + if (ret != FT_SUCCESS) + { + return ret; + } + } + else + { + /* + * Valid Mirror BBT found + */ + + FNAND_BBM_DEBUG_I("Valid Mirror BBT found is %d", mirror_desc_p->page_offset); + + ret = FNandReadPage(instance_p, mirror_desc_p->page_offset, buf, 0, bbtlen, NULL, 0, 0, chip_addr); + if (ret != FT_SUCCESS) + { + FNAND_BBM_DEBUG_I("Valid Mirror BBT found is error 0x%x", ret); + return ret; + } + + /* + * Convert flash BBT to memory based BBT + */ + FNandConvertBbt(instance_p, &buf[0], chip_addr); + desc_p->version = mirror_desc_p->version; + + /* + * Write the BBT to Mirror BBT location in flash + */ + ret = FNandWriteBbt(instance_p, desc_p, + mirror_desc_p, chip_addr); + if (ret != FT_SUCCESS) + { + return ret; + } + } + return FT_SUCCESS; +} + + + +static void FNandBbtDumpDebug(FNand *instance_p) +{ + int i; + FNAND_BBM_DEBUG_W("/********************* master bbt descriptor **********************/"); + + FNAND_BBM_DEBUG_I("page_offset 0x%x", instance_p->bbt_manager[0].bbt_desc.page_offset); /* Page offset where BBT resides */ + FNAND_BBM_DEBUG_I("sig_offset 0x%x", instance_p->bbt_manager[0].bbt_desc.sig_offset); /* Signature offset in Spare area */ + FNAND_BBM_DEBUG_I("ver_offset 0x%x", instance_p->bbt_manager[0].bbt_desc.ver_offset); /* Offset of BBT version */ + FNAND_BBM_DEBUG_I("sig_length 0x%x", instance_p->bbt_manager[0].bbt_desc.sig_length); /* Length of the signature */ + FNAND_BBM_DEBUG_I("max_blocks 0x%x", instance_p->bbt_manager[0].bbt_desc.max_blocks); /* Max blocks to search for BBT */ + for (i = 0; i < 4; i++) + { + FNAND_BBM_DEBUG_I("signature[%d] %c", i, instance_p->bbt_manager[0].bbt_desc.signature[i]); + } + FNAND_BBM_DEBUG_I("version 0x%x", instance_p->bbt_manager[0].bbt_desc.version); /* BBT version */ + FNAND_BBM_DEBUG_I("valid 0x%x", instance_p->bbt_manager[0].bbt_desc.valid); /* BBT descriptor is valid or not */ + + FNAND_BBM_DEBUG_W("/********************* mirror bbt descriptor **********************/"); + FNAND_BBM_DEBUG_I("page_offset 0x%x", instance_p->bbt_manager[0].bbt_mirror_desc.page_offset); /* Page offset where BBT resides */ + FNAND_BBM_DEBUG_I("sig_offset 0x%x", instance_p->bbt_manager[0].bbt_mirror_desc.sig_offset); /* Signature offset in Spare area */ + FNAND_BBM_DEBUG_I("ver_offset 0x%x", instance_p->bbt_manager[0].bbt_mirror_desc.ver_offset); /* Offset of BBT version */ + FNAND_BBM_DEBUG_I("sig_length 0x%x", instance_p->bbt_manager[0].bbt_mirror_desc.sig_length); /* Length of the signature */ + FNAND_BBM_DEBUG_I("max_blocks 0x%x", instance_p->bbt_manager[0].bbt_mirror_desc.max_blocks); /* Max blocks to search for BBT */ + for (i = 0; i < 4; i++) + { + FNAND_BBM_DEBUG_I("signature[%d] %c", i, instance_p->bbt_manager[0].bbt_mirror_desc.signature[i]); + } + FNAND_BBM_DEBUG_I("version 0x%x", instance_p->bbt_manager[0].bbt_mirror_desc.version); /* BBT version */ + FNAND_BBM_DEBUG_I("valid 0x%x", instance_p->bbt_manager[0].bbt_mirror_desc.valid); /* BBT descriptor is valid or not */ + + + FNAND_BBM_DEBUG_W("/********************* bbt info **********************/"); + FtDumpHexWord((const u32 *)instance_p->bbt_manager[0].bbt, instance_p->nand_geometry[0].num_blocks >> + FNAND_BBT_BLOCK_SHIFT); +} + +/** + * @name: FNandScanBbt + * @msg: This function reads the Bad Block Table(BBT) if present in flash. + * @note: + * @param {FNand} *instance_p is the pointer to the FNand instance. + * @param {u32} chip_addr is chip address + * @return {FError} + */ +FError FNandScanBbt(FNand *instance_p, u32 chip_addr) +{ + FError ret; + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + + if (FNandReadBbt(instance_p, chip_addr) != FT_SUCCESS) + { + /* + * Create memory based Bad Block Table(BBT) + */ + FNandCreateBbt(instance_p, chip_addr); + + /* + * Write the Bad Block Table(BBT) to the flash + */ + ret = FNandWriteBbt(instance_p, + &instance_p->bbt_manager[chip_addr].bbt_desc, + &instance_p->bbt_manager[chip_addr].bbt_mirror_desc, chip_addr); + if (ret != FT_SUCCESS) + { + return ret; + } + + /* + * Write the Mirror Bad Block Table(BBT) to the flash + */ + ret = FNandWriteBbt(instance_p, + &instance_p->bbt_manager[chip_addr].bbt_mirror_desc, + &instance_p->bbt_manager[chip_addr].bbt_desc, chip_addr); + if (ret != FT_SUCCESS) + { + return ret; + } + + /* + * Mark the blocks containing Bad Block Table(BBT) as Reserved + */ + FNandMarkBbt(instance_p, &instance_p->bbt_manager[chip_addr].bbt_desc, chip_addr); + FNandMarkBbt(instance_p, &instance_p->bbt_manager[chip_addr].bbt_mirror_desc, chip_addr); + + FNAND_BBM_DEBUG_I("New bbt is ready"); + + FNandBbtDumpDebug(instance_p) ; + + } + else + { + FNAND_BBM_DEBUG_I("old bbt is valid"); + FNandBbtDumpDebug(instance_p) ; + } + + return FT_SUCCESS; +} + + +/** + * @name: FNandIsBlockBad + * @msg: This function checks whether a block is bad or not. + * @note: + * @param {FNand} *instance_p is the pointer to the FNand instance. + * @param {u32} block is the fnand flash block number + * @param {u32} chip_addr is chip address + * @return {FError} FT_SUCCESS if block is bad + */ +FError FNandIsBlockBad(FNand *instance_p, u32 block, u32 chip_addr) +{ + u8 data; + u8 block_shift; + u8 BlockType; + u32 BlockOffset; + + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + FASSERT_MSG(block < instance_p->nand_geometry[chip_addr].num_blocks, "block is %d,num_blocks is %d", block, instance_p->nand_geometry[chip_addr].num_blocks); + + BlockOffset = block >> FNAND_BBT_BLOCK_SHIFT; + block_shift = FNAND_BBTBLOCKSHIFT(block); + data = instance_p->bbt_manager[chip_addr].bbt[BlockOffset]; /* Block information in BBT */ + BlockType = (data >> block_shift) & FNAND_BLOCK_TYPE_MASK; + + if (BlockType != FNAND_BLOCK_GOOD) + return FT_SUCCESS; + else + return FNAND_VALUE_FAILURE; +} + + + +/** + * @name: FNandMarkBlockBad + * @msg: This function marks a block as bad in the RAM based Bad Block Table(BBT). + * @note: + * @param {FNand} *instance_p is the pointer to the FNand instance. + * @param {u32} block is the fnand flash block number + * @param {u32} chip_addr is chip address + * @return {*} FT_SUCCESS if successful + */ +FError FNandMarkBlockBad(FNand *instance_p, u32 block, u32 chip_addr) +{ + u8 data; + u8 block_shift; + u32 block_offset; + u8 oldval; + u8 newval; + u32 Status; + + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + FASSERT(block < instance_p->nand_geometry[chip_addr].num_blocks); + + block_offset = block >> FNAND_BBT_BLOCK_SHIFT; + block_shift = FNAND_BBTBLOCKSHIFT(block); + data = instance_p->bbt_manager[chip_addr].bbt[block_offset]; /* Block information in BBT */ + + /* + * Mark the block as bad in the RAM based Bad Block Table + */ + oldval = data; + data &= ~(FNAND_BLOCK_TYPE_MASK << block_shift); + data |= (FNAND_BLOCK_BAD << block_shift); + newval = data; + instance_p->bbt_manager[chip_addr].bbt[block_offset] = data; + + /* + * Update the Bad Block Table(BBT) in flash + */ + if (oldval != newval) + { + Status = FNandUpdateBbt(instance_p, chip_addr); + if (Status != FT_SUCCESS) + { + return Status; + } + } + return FT_SUCCESS; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_bbm.h b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_bbm.h new file mode 100644 index 0000000000..4159553147 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_bbm.h @@ -0,0 +1,106 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fnand_bbm.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:56:17 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#ifndef DRIVERS_NAND_FNAND_BBM_H +#define DRIVERS_NAND_FNAND_BBM_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ftypes.h" +#include "fnand.h" + +/************************** Constant Definitions *****************************/ +/* + * Block definitions for RAM based Bad Block Table (BBT) + */ +#define FNAND_BLOCK_GOOD 0x0 /* Block is good */ +#define FNAND_BLOCK_BAD 0x1 /* Block is bad */ +#define FNAND_BLOCK_RESERVED 0x2 /* Reserved block */ +#define FNAND_BLOCK_FACTORY_BAD 0x3 /* Factory marked bad block */ + +/* + * Block definitions for FLASH based Bad Block Table (BBT) + */ +#define FNAND_FLASH_BLOCK_GOOD 0x3 /* Block is good */ +#define FNAND_FLASH_BLOCK_BAD 0x2 /* Block is bad */ +#define FNAND_FLASH_BLOCK_RESERVED 0x1 /* Reserved block */ +#define FNAND_FLASH_BLOCK_FACTORY_BAD 0x0 /* Factory marked bad block */ + +#define FNAND_BBT_SCAN_2ND_PAGE 0x00000001 /* Scan the \ + second page \ + for bad block \ + information \ + */ +#define FNAND_BBT_DESC_PAGE_OFFSET 0 /* Page offset of Bad \ + Block Table Desc */ +#define FNAND_BBT_DESC_SIG_OFFSET 8 /* Bad Block Table \ + signature offset */ +#define FNAND_BBT_DESC_VER_OFFSET 12 /* Bad block Table \ + version offset */ +#define FNAND_BBT_DESC_SIG_LEN 4 /* Bad block Table \ + signature length */ +#define FNAND_BBT_DESC_MAX_BLOCKS 4 /* Bad block Table \ + max blocks */ + +#define FNAND_BBT_BLOCK_SHIFT 2 /* Block shift value \ + for a block in BBT */ +#define FNAND_BBT_ENTRY_NUM_BLOCKS 4 /* Num of blocks in \ + one BBT entry */ +#define FNAND_BB_PATTERN_OFFSET_SMALL_PAGE 5 /* Bad block pattern \ + offset in a page */ +#define FNAND_BB_PATTERN_LENGTH_SMALL_PAGE 1 /* Bad block pattern \ + length */ +#define FNAND_BB_PATTERN_OFFSET_LARGE_PAGE 0 /* Bad block pattern \ + offset in a large \ + page */ +#define FNAND_BB_PATTERN_LENGTH_LARGE_PAGE 2 /* Bad block pattern \ + length */ +#define FNAND_BB_PATTERN 0xFF /* Bad block pattern \ + to search in a page \ + */ +#define FNAND_BLOCK_TYPE_MASK 0x03 /* Block type mask */ +#define FNAND_BLOCK_SHIFT_MASK 0x06 /* Block shift mask \ + for a Bad Block Table \ + entry byte */ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/****************************************************************************/ + +#define FNAND_BBTBLOCKSHIFT(block) \ + ((block * 2) & FNAND_BLOCK_SHIFT_MASK) + + + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_common_cmd.c b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_common_cmd.c new file mode 100644 index 0000000000..00627cc2f9 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_common_cmd.c @@ -0,0 +1,731 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fnand_common_cmd.c + * Date: 2022-06-24 03:51:06 + * LastEditTime: 2022-06-24 03:51:07 + * Description: This file is for + * + * Modify History: + * Ver Who Date Changes + * ----- ------ -------- -------------------------------------- + */ + +#include "fnand_common_cmd.h" +#include "fnand_hw.h" +#include "stdio.h" +#include "string.h" +#include "fnand_dma.h" +#include "fnand_onfi.h" +#include "fnand_timing.h" +#include "fnand_ecc.h" +#include "fcache.h" +#include "fsleep.h" +#include "fdebug.h" +#include "sdkconfig.h" + + +#define FNAND_COMMON_DEBUG_TAG "FNAND_COMMON" + + +#ifdef CONFIG_FNAND_COMMON_DEBUG_EN + #define FNAND_COMMON_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(FNAND_COMMON_DEBUG_TAG, format, ##__VA_ARGS__) + #define FNAND_COMMON_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(FNAND_COMMON_DEBUG_TAG, format, ##__VA_ARGS__) + #define FNAND_COMMON_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(FNAND_COMMON_DEBUG_TAG, format, ##__VA_ARGS__) + #define FNAND_COMMON_DEBUG_D(format, ...) FT_DEBUG_PRINT_D(FNAND_COMMON_DEBUG_TAG, format, ##__VA_ARGS__) +#else + #define FNAND_COMMON_DEBUG_I(format, ...) + #define FNAND_COMMON_DEBUG_W(format, ...) + #define FNAND_COMMON_DEBUG_E(format, ...) + #define FNAND_COMMON_DEBUG_D(format, ...) +#endif + +#define FNAND_ADDR_CYCLE_NUM0 0 +#define FNAND_ADDR_CYCLE_NUM1 1 +#define FNAND_ADDR_CYCLE_NUM2 2 +#define FNAND_ADDR_CYCLE_NUM3 3 +#define FNAND_ADDR_CYCLE_NUM4 4 +#define FNAND_ADDR_CYCLE_NUM5 5 + +#define FNAND_COMMON_CRC_BASE 0x4F4E + +#define FNAND_CTRL_ECC_EN 1 +#define FNAND_CTRL_ECC_DIS 0 + +#define FNAND_CTRL_AUTO_AUTO_RS_EN 1 +#define FNAND_CTRL_AUTO_AUTO_RS_DIS 0 + +/* + * Special handling must be done for the WAITRDY timeout parameter as it usually + * is either tPROG (after a prog), tR (before a read), tRST (during a reset) or + * tBERS (during an erase) which all of them are u64 values that cannot be + * divided by usual kernel macros and must be handled with the special + * DIV_ROUND_UP_ULL() macro. + * + * Cast to type of dividend is needed here to guarantee that the result won't + * be an unsigned long long when the dividend is an unsigned long (or smaller), + * which is what the compiler does when it sees ternary operator with 2 + * different return types (picks the largest type to make sure there's no + * loss). + */ +#define __DIVIDE(dividend, divisor) ({ \ + (__typeof__(dividend))(sizeof(dividend) <= sizeof(unsigned long) ? \ + DIV_ROUND_UP(dividend, divisor) : \ + DIV_ROUND_UP_ULL(dividend, divisor)); \ + }) +#define PSEC_TO_NSEC(x) __DIVIDE(x, 1000) +#define PSEC_TO_MSEC(x) __DIVIDE(x, 1000000000) + +extern FError FNandSendCmd(FNand *instance_p, struct FNandDmaDescriptor *descriptor_p, FNandOperationType isr_type); +extern FError FNandTimingInterfaceUpdate(FNand *instance_p, u32 chip_addr); + +extern FError FNandDmaPack(FNandCmdFormat *cmd_format, + struct FNandDmaDescriptor *descriptor_p, + FNandDmaPackData *pack_data_p + ); + + + +enum CommandsEnumNew +{ + CMD_READ_OPTION_NEW = 0, + CMD_RANDOM_DATA_OUT, + CMD_PAGE_PROGRAM, + CMD_PAGE_PROGRAM_WITH_OTHER, + CMD_COPY_BACK_PROGRAM, + CMD_BLOCK_ERASE, + CMD_RESET, + CMD_READ_ID, + CMD_READ_DEVICE_TABLE, + CMD_READ_PAGE, + CMD_READ_STATUS, + CMD_INDEX_LENGTH_NEW, +}; + + +static FNandCmdFormat cmd_format[CMD_INDEX_LENGTH_NEW] = +{ + {NAND_CMD_READ1, NAND_CMD_READ2, FNAND_ADDR_CYCLE_NUM5, FNAND_CMDCTRL_TYPE_READ, FNAND_CTRL_ECC_DIS, FNAND_CTRL_AUTO_AUTO_RS_EN}, + {NAND_CMD_CHANGE_READ_COLUMN1, NAND_CMD_CHANGE_READ_COLUMN2, FNAND_ADDR_CYCLE_NUM2, FNAND_CMDCTRL_TYPE_READ_COL, FNAND_CTRL_ECC_EN, FNAND_CTRL_AUTO_AUTO_RS_DIS}, + {NAND_CMD_PAGE_PROG1, NAND_CMD_PAGE_PROG2, FNAND_ADDR_CYCLE_NUM5, FNAND_CMDCTRL_TYPE_PAGE_PRO, FNAND_CTRL_ECC_DIS, FNAND_CTRL_AUTO_AUTO_RS_EN}, + {NAND_CMD_PAGE_PROG1, NAND_END_CMD_NONE, FNAND_ADDR_CYCLE_NUM5, FNAND_CMDCTRL_CH_ROW_ADDR, FNAND_CTRL_ECC_DIS, FNAND_CTRL_AUTO_AUTO_RS_DIS}, + {NAND_CMD_CHANGE_WRITE_COLUMN, NAND_CMD_PAGE_PROG2, FNAND_ADDR_CYCLE_NUM2, FNAND_CMDCTRL_TYPE_PAGE_PRO, FNAND_CTRL_ECC_EN, FNAND_CTRL_AUTO_AUTO_RS_EN}, + {NAND_CMD_BLOCK_ERASE1, NAND_CMD_BLOCK_ERASE2, FNAND_ADDR_CYCLE_NUM3, FNAND_CMDCTRL_TYPE_ERASE, FNAND_CTRL_ECC_DIS, FNAND_CTRL_AUTO_AUTO_RS_EN}, + {NAND_CMD_RESET, NAND_END_CMD_NONE, FNAND_ADDR_CYCLE_NUM5, FNAND_CMDCTRL_TYPE_RESET, FNAND_CTRL_ECC_DIS, FNAND_CTRL_AUTO_AUTO_RS_EN}, + {NAND_CMD_READ_ID, NAND_END_CMD_NONE, FNAND_ADDR_CYCLE_NUM1, FNAND_CMDCTRL_TYPE_READ_ID, FNAND_CTRL_ECC_DIS, FNAND_CTRL_AUTO_AUTO_RS_DIS}, + {NAND_CMD_READ_PARAM_PAGE, NAND_END_CMD_NONE, FNAND_ADDR_CYCLE_NUM1, FNAND_CMDCTRL_READ_PARAM, FNAND_CTRL_ECC_DIS, FNAND_CTRL_AUTO_AUTO_RS_EN}, + {NAND_CMD_READ1, NAND_CMD_READ2, FNAND_ADDR_CYCLE_NUM5, FNAND_CMDCTRL_TYPE_READ_ID, FNAND_CTRL_ECC_DIS, FNAND_CTRL_AUTO_AUTO_RS_EN}, + {NAND_CMD_READ_STATUS, NAND_END_CMD_NONE, FNAND_ADDR_CYCLE_NUM5, FNAND_CMDCTRL_TYPE_READ_COL, FNAND_CTRL_ECC_DIS, FNAND_CTRL_AUTO_AUTO_RS_DIS}, +}; + + +FError FNandFlashReadId(FNand *instance_p, u8 address, u8 *id_buffer, u32 buffer_length, u32 chip_addr) +{ + FError ret; + u32 memcpy_length; + FNandDmaPackData pack_data = + { + .addr_p = &address, + .addr_length = 1, + .phy_address = (uintptr)instance_p->dma_data_buffer.data_buffer, + .phy_bytes_length = (buffer_length > FNAND_DMA_MAX_LENGTH) ? FNAND_DMA_MAX_LENGTH : buffer_length, + .chip_addr = chip_addr, + .contiune_dma = 0, + }; + + memset((struct FNandDmaDescriptor *)&instance_p->descriptor_buffer.data_buffer[0], 0, sizeof(struct FNandDmaDescriptor)); + FNandDmaPack(&cmd_format[CMD_READ_ID], (struct FNandDmaDescriptor *)&instance_p->descriptor_buffer.data_buffer[0], &pack_data); + ret = FNandSendCmd(instance_p, (struct FNandDmaDescriptor *)&instance_p->descriptor_buffer.data_buffer[0], FNAND_CMD_TYPE); + if (ret != FT_SUCCESS) + { + return FNAND_ERR_OPERATION; + } + + if (buffer_length && id_buffer) + { + memcpy_length = (buffer_length > pack_data.phy_bytes_length) ? pack_data.phy_bytes_length : buffer_length; + FCacheDCacheFlushRange((intptr)instance_p->dma_data_buffer.data_buffer, memcpy_length); + memcpy(id_buffer, instance_p->dma_data_buffer.data_buffer, memcpy_length); + } + + return FT_SUCCESS; +} + + +static FError FNandFlashReadStatus(FNand *instance_p, u32 chip_addr) +{ + FError ret; + FASSERT(instance_p != NULL); + FNandDmaPackData pack_data = + { + .addr_p = NULL, + .addr_length = 0, + .phy_address = (uintptr)instance_p->dma_data_buffer.data_buffer, + .phy_bytes_length = 4, + .chip_addr = chip_addr, + .contiune_dma = 0, + }; + + memset((struct FNandDmaDescriptor *)&instance_p->descriptor_buffer.data_buffer[0], 0, sizeof(struct FNandDmaDescriptor)); + FNandDmaPack(&cmd_format[CMD_READ_STATUS], (struct FNandDmaDescriptor *)&instance_p->descriptor_buffer.data_buffer[0], &pack_data); /* FNAND_CMDCTRL_TYPE_RESET */ + ret = FNandSendCmd(instance_p, (struct FNandDmaDescriptor *)&instance_p->descriptor_buffer.data_buffer[0], FNAND_READ_PAGE_TYPE); + if (ret != FT_SUCCESS) + { + return ret; + } + + FNAND_COMMON_DEBUG_I("read status is 0x%x", instance_p->dma_data_buffer.data_buffer[0]); + + return (instance_p->dma_data_buffer.data_buffer[0] == 0xe0) ? FT_SUCCESS : FNAND_IS_BUSY; +} + +FError FNandFlashReset(FNand *instance_p, u32 chip_addr) +{ + FASSERT(instance_p != NULL); + FNandDmaPackData pack_data = + { + .addr_p = NULL, + .addr_length = 0, + .phy_address = (uintptr)NULL, + .phy_bytes_length = 0, + .chip_addr = chip_addr, + .contiune_dma = 0, + }; + + memset((struct FNandDmaDescriptor *)&instance_p->descriptor_buffer.data_buffer[0], 0, sizeof(struct FNandDmaDescriptor)); + FNandDmaPack(&cmd_format[CMD_RESET], (struct FNandDmaDescriptor *)&instance_p->descriptor_buffer.data_buffer[0], &pack_data); /* FNAND_CMDCTRL_TYPE_RESET */ + return FNandSendCmd(instance_p, (struct FNandDmaDescriptor *)&instance_p->descriptor_buffer.data_buffer[0], FNAND_CMD_TYPE); +} + + +FError FNandFlashEraseBlock(FNand *instance_p, u32 page_addr, u32 chip_addr) +{ + u8 addr_buf[3] = {0}; + FError ret; + u32 nand_state; + + while (FNandFlashReadStatus(instance_p, chip_addr) == FNAND_IS_BUSY) + ; /* wait i/o idle */ + /* read operation */ + + + addr_buf[0] = page_addr; + addr_buf[1] = page_addr >> 8; + addr_buf[2] = page_addr >> 16; + + FNandDmaPackData pack_data = + { + .addr_p = addr_buf, + .addr_length = 3, + .phy_address = (uintptr)NULL, + .phy_bytes_length = 0, + .chip_addr = chip_addr, + .contiune_dma = 0, + }; + + FNandDmaPack(&cmd_format[CMD_BLOCK_ERASE], (struct FNandDmaDescriptor *)&instance_p->descriptor_buffer.data_buffer[0], &pack_data); + ret = FNandSendCmd(instance_p, (struct FNandDmaDescriptor *)&instance_p->descriptor_buffer.data_buffer[0], FNAND_CMD_TYPE); + + if (ret != FT_SUCCESS) + { + return FNAND_ERR_OPERATION; + } + + nand_state = FNAND_READREG(instance_p->config.base_address, FNAND_STATE_OFFSET); + while (nand_state & FNAND_STATE_BUSY_OFFSET) /* wait busy state is over */ + { + nand_state = FNAND_READREG(instance_p->config.base_address, FNAND_STATE_OFFSET); + } + + return FT_SUCCESS; +} + +static FError FNandPageRead(FNand *instance_p, u32 page_addr, u8 *buf, u32 page_copy_offset, u32 length, u32 chip_addr) +{ + u8 addr_buf[5] = {0}; + u32 memcpy_length; + FError ret; + addr_buf[4] = (page_addr >> 16); + addr_buf[3] = (page_addr >> 8); + addr_buf[2] = (page_addr); + + FNandDmaPackData pack_data = + { + .addr_p = addr_buf, + .addr_length = 5, + .phy_address = (uintptr)instance_p->dma_data_buffer.data_buffer, + .phy_bytes_length = instance_p->nand_geometry[chip_addr].bytes_per_page, + .chip_addr = chip_addr, + .contiune_dma = 0, + }; + + FNandDmaPack(&cmd_format[CMD_READ_OPTION_NEW], (struct FNandDmaDescriptor *)&instance_p->descriptor_buffer.data_buffer[0], &pack_data); + ret = FNandSendCmd(instance_p, (struct FNandDmaDescriptor *)&instance_p->descriptor_buffer.data_buffer[0], FNAND_READ_PAGE_TYPE); + + if (ret != FT_SUCCESS) + { + return FNAND_ERR_OPERATION; + } + + if (length && buf) + { + memcpy_length = (length > (pack_data.phy_bytes_length - page_copy_offset)) ? (pack_data.phy_bytes_length - page_copy_offset) : length; + FCacheDCacheFlushRange((intptr)(instance_p->dma_data_buffer.data_buffer + page_copy_offset), memcpy_length); + memcpy(buf, instance_p->dma_data_buffer.data_buffer + page_copy_offset, memcpy_length); + } + + return FT_SUCCESS; +} + +static FError FNandPageWrite(FNand *instance_p, u32 page_addr, u8 *buf, u32 page_copy_offset, u32 length, u32 chip_addr) +{ + u8 addr_buf[5] = {0}; + FError ret; + u32 bytes_per_page = 0; + addr_buf[4] = (page_addr >> 16); + addr_buf[3] = (page_addr >> 8); + addr_buf[2] = (page_addr); + bytes_per_page = instance_p->nand_geometry[chip_addr].bytes_per_page; + FNandDmaPackData pack_data = + { + .addr_p = addr_buf, + .addr_length = 5, + .phy_address = (uintptr)instance_p->dma_data_buffer.data_buffer, + .phy_bytes_length = bytes_per_page, + .chip_addr = chip_addr, + .contiune_dma = 0, + }; + + memcpy(instance_p->dma_data_buffer.data_buffer + page_copy_offset, buf, ((bytes_per_page - page_copy_offset) > length) ? length : (bytes_per_page - page_copy_offset)); + + FNandDmaPack(&cmd_format[CMD_PAGE_PROGRAM], (struct FNandDmaDescriptor *)&instance_p->descriptor_buffer.data_buffer[0], &pack_data); + ret = FNandSendCmd(instance_p, (struct FNandDmaDescriptor *)&instance_p->descriptor_buffer.data_buffer[0], FNAND_WRITE_PAGE_TYPE); + + if (ret != FT_SUCCESS) + { + return FNAND_ERR_OPERATION; + } + while (FNandFlashReadStatus(instance_p, chip_addr) == FNAND_IS_BUSY); + return FT_SUCCESS; +} + +static FError FNandPageWriteHwEcc(FNand *instance_p, u32 page_addr, u8 *buf, u32 page_copy_offset, u32 length, u32 chip_addr) +{ + FError ret; + u32 nand_state = 0; + u8 addr_buf[5] = {0}; + u32 ecc_offset = 0; + u32 bytes_per_page = 0; + u32 spare_bytes_per_page = 0; + /* read operation */ + addr_buf[2] = page_addr; + addr_buf[3] = page_addr >> 8; + addr_buf[4] = page_addr >> 16; + + ecc_offset = instance_p->nand_geometry[chip_addr].ecc_offset; + bytes_per_page = instance_p->nand_geometry[chip_addr].bytes_per_page; + spare_bytes_per_page = instance_p->nand_geometry[chip_addr].spare_bytes_per_page; + + FNandDmaPackData pack_data = + { + .addr_p = addr_buf, + .addr_length = 5, + .phy_address = (uintptr)instance_p->dma_data_buffer.data_buffer, + .phy_bytes_length = bytes_per_page, + .chip_addr = chip_addr, + .contiune_dma = 1, + }; + + memset(instance_p->dma_data_buffer.data_buffer, 0xff, FNAND_DMA_MAX_LENGTH); + memcpy(instance_p->dma_data_buffer.data_buffer + page_copy_offset, buf, ((bytes_per_page - page_copy_offset) < length) ? (bytes_per_page - page_copy_offset) : length); + + FNandDmaPack(&cmd_format[CMD_PAGE_PROGRAM_WITH_OTHER], (struct FNandDmaDescriptor *)&instance_p->descriptor_buffer.data_buffer[0], &pack_data) ; + /* Random Data Input */ + + /* 写入存储硬件ecc 偏移位置参数 */ + memset(addr_buf, 0, sizeof(addr_buf)); + addr_buf[0] = bytes_per_page + ecc_offset; + addr_buf[1] = (bytes_per_page + ecc_offset) >> 8; + memset((instance_p->dma_data_buffer.data_buffer + bytes_per_page), 0xff, spare_bytes_per_page); + FNandDmaPackData pack_data2 = + { + .addr_p = addr_buf, + .addr_length = 2, + .phy_address = (uintptr)instance_p->dma_data_buffer.data_buffer + bytes_per_page + ecc_offset, + .phy_bytes_length = instance_p->nand_geometry[chip_addr].hw_ecc_length, + .chip_addr = chip_addr, + .contiune_dma = 0, + }; + + FNandDmaPack(&cmd_format[CMD_COPY_BACK_PROGRAM], (struct FNandDmaDescriptor *)&instance_p->descriptor_buffer.data_buffer[FNAND_DESCRIPTORS_SIZE], &pack_data2) ; + ret = FNandSendCmd(instance_p, (struct FNandDmaDescriptor *)&instance_p->descriptor_buffer.data_buffer[0], FNAND_WRITE_PAGE_TYPE); + if (ret != FT_SUCCESS) + { + return FNAND_ERR_OPERATION; + } + + nand_state = FNAND_READREG(instance_p->config.base_address, FNAND_STATE_OFFSET); + while (nand_state & FNAND_STATE_BUSY_OFFSET) /* wait busy state is over */ + { + nand_state = FNAND_READREG(instance_p->config.base_address, FNAND_STATE_OFFSET); + } + // printf("write after nand_state 0x%x \r\n",nand_state); + while (FNandFlashReadStatus(instance_p, chip_addr) == FNAND_IS_BUSY) + ; /* wait i/o idle */ + + return FT_SUCCESS; +} + +static FError FNandPageReadOOb(FNand *instance_p, u32 page_addr, u8 *buf, u32 page_copy_offset, u32 length, u32 chip_addr) +{ + FError ret; + u8 addr_buf[5] = {0}; + u32 bytes_per_page = 0; + u32 spare_bytes_per_page = 0; + u32 memcpy_length = 0; + bytes_per_page = instance_p->nand_geometry[chip_addr].bytes_per_page ; + spare_bytes_per_page = instance_p->nand_geometry[chip_addr].spare_bytes_per_page ; + + FNandDmaPackData pack_data = + { + .addr_p = addr_buf, + .addr_length = 5, + .phy_address = (uintptr)instance_p->dma_data_buffer.data_buffer, + .phy_bytes_length = spare_bytes_per_page, /* 读取所有的oob 数据 */ + .chip_addr = chip_addr, + .contiune_dma = 0, + }; + + addr_buf[4] = (page_addr >> 16); + addr_buf[3] = (page_addr >> 8); + addr_buf[2] = (page_addr); + addr_buf[1] = ((bytes_per_page >> 8) & 0xff); /* 从oob 位置读取 */ + addr_buf[0] = (bytes_per_page & 0xff); + + FNandDmaPack(&cmd_format[CMD_READ_OPTION_NEW], (struct FNandDmaDescriptor *)&instance_p->descriptor_buffer.data_buffer[0], &pack_data); + ret = FNandSendCmd(instance_p, (struct FNandDmaDescriptor *)&instance_p->descriptor_buffer.data_buffer[0], FNAND_READ_PAGE_TYPE); + + if (ret != FT_SUCCESS) + { + return FNAND_ERR_OPERATION; + } + + if (length && buf) + { + memcpy_length = (length > (spare_bytes_per_page - page_copy_offset)) ? (spare_bytes_per_page - page_copy_offset) : length; + FCacheDCacheFlushRange((intptr)(instance_p->dma_data_buffer.data_buffer + page_copy_offset), memcpy_length); + memcpy(buf, instance_p->dma_data_buffer.data_buffer + page_copy_offset, memcpy_length); + } + + return FT_SUCCESS; +} + +static FError FNandPageWriteOOb(FNand *instance_p, u32 page_addr, u8 *buf, u32 spare_page_offset, u32 length, u32 chip_addr) +{ + FError ret; + u8 addr_buf[5] = {0}; + u32 bytes_per_page = 0; + u32 spare_bytes_per_page = 0; + + bytes_per_page = instance_p->nand_geometry[chip_addr].bytes_per_page; + spare_bytes_per_page = instance_p->nand_geometry[chip_addr].spare_bytes_per_page ; + memset(instance_p->dma_data_buffer.data_buffer, 0xff, spare_bytes_per_page); + + FNandDmaPackData pack_data = + { + .addr_p = addr_buf, + .addr_length = 5, + .phy_address = (uintptr)instance_p->dma_data_buffer.data_buffer, + .phy_bytes_length = (spare_bytes_per_page > length) ? length : spare_bytes_per_page, + .chip_addr = chip_addr, + .contiune_dma = 0, + }; + + addr_buf[4] = (page_addr >> 16); + addr_buf[3] = (page_addr >> 8); + addr_buf[2] = (page_addr); + addr_buf[1] = ((bytes_per_page >> 8) & 0xff); + addr_buf[0] = (bytes_per_page & 0xff); + + memcpy(instance_p->dma_data_buffer.data_buffer + spare_page_offset, buf, ((spare_bytes_per_page - spare_page_offset) > length) ? length : (spare_bytes_per_page - spare_page_offset)); + + FNandDmaPack(&cmd_format[CMD_PAGE_PROGRAM], (struct FNandDmaDescriptor *)&instance_p->descriptor_buffer.data_buffer[0], &pack_data); + + ret = FNandSendCmd(instance_p, (struct FNandDmaDescriptor *)&instance_p->descriptor_buffer.data_buffer[0], FNAND_WRITE_PAGE_TYPE); + + if (ret != FT_SUCCESS) + { + return FNAND_ERR_OPERATION; + } + while (FNandFlashReadStatus(instance_p, chip_addr) == FNAND_IS_BUSY); + return FT_SUCCESS ; +} + +static FError FNandPageReadHwEcc(FNand *instance_p, u32 page_addr, u8 *buf, u32 page_copy_offset, u32 length, u32 chip_addr) +{ + FError ret; + u32 nand_state = 0; + u8 addr_buf[5] = {0}; + u32 memcpy_length = 0; + volatile u32 wait_cnt; + /* read operation */ + addr_buf[2] = page_addr; + addr_buf[3] = page_addr >> 8; + addr_buf[4] = page_addr >> 16; + + FNandDmaPackData pack_data = + { + .addr_p = addr_buf, + .addr_length = 5, + .phy_address = (uintptr)instance_p->dma_data_buffer.data_buffer, + .phy_bytes_length = instance_p->nand_geometry[chip_addr].bytes_per_page, + .chip_addr = chip_addr, + .contiune_dma = 1, + }; + + FNandDmaPack(&cmd_format[CMD_READ_OPTION_NEW], (struct FNandDmaDescriptor *)&instance_p->descriptor_buffer.data_buffer[0], &pack_data) ; + + /* Random Data Output */ + memset(addr_buf, 0, sizeof(addr_buf)); + /* 读取存储硬件ecc 偏移位置参数 */ + addr_buf[0] = instance_p->nand_geometry[chip_addr].bytes_per_page + instance_p->nand_geometry[chip_addr].ecc_offset; + addr_buf[1] = (instance_p->nand_geometry[chip_addr].bytes_per_page + instance_p->nand_geometry[chip_addr].ecc_offset) >> 8; + + FNandDmaPackData pack_data2 = + { + .addr_p = addr_buf, + .addr_length = 2, + .phy_address = (uintptr)instance_p->dma_data_buffer.data_buffer + instance_p->nand_geometry[chip_addr].bytes_per_page, + .phy_bytes_length = instance_p->nand_geometry[chip_addr].hw_ecc_length, + .chip_addr = chip_addr, + .contiune_dma = 0, + }; + + FNandDmaPack(&cmd_format[CMD_RANDOM_DATA_OUT], (struct FNandDmaDescriptor *)&instance_p->descriptor_buffer.data_buffer[16], &pack_data2) ; + ret = FNandSendCmd(instance_p, (struct FNandDmaDescriptor *)&instance_p->descriptor_buffer.data_buffer[0], FNAND_WAIT_ECC_TYPE); + if (ret != FT_SUCCESS) + { + return FNAND_ERR_OPERATION; + } + + fsleep_microsec(100); + /* 增加判断bit(16) 是否ecc 正忙 */ + nand_state = FNAND_READREG(instance_p->config.base_address, FNAND_STATE_OFFSET); + + while ((nand_state & FNAND_STATE_ECC_BUSY_OFFSET) && ((nand_state & FNAND_STATE_ECC_FINISH_OFFSET) == 0)) + { + nand_state = FNAND_READREG(instance_p->config.base_address, FNAND_STATE_OFFSET); + } + + if (nand_state & FNAND_STATE_ECC_ERROVER_OFFSET) + { + FNAND_COMMON_DEBUG_E("FNAND_STATE_ECC_ERROVER %x ,page is %x \n", 0, page_addr) ; + return FNAND_ERR_READ_ECC ; + } + else if (nand_state & FNAND_STATE_ECC_ERR_OFFSET) + { + s32 correct_num; + FNAND_COMMON_DEBUG_W("FNAND ecc correct is in \r\n"); + correct_num = FNandCorrectEcc(instance_p->config.base_address, instance_p->nand_geometry[chip_addr].ecc_step_size, + instance_p->nand_geometry[chip_addr].hw_ecc_steps, instance_p->dma_data_buffer.data_buffer, + instance_p->nand_geometry[chip_addr].bytes_per_page); + + if (correct_num < 0) + { + FNAND_COMMON_DEBUG_W("CRC ECC IS ERROR \n") ; + return FNAND_ERR_READ_ECC; + } + + FNAND_COMMON_DEBUG_W("FNAND_STATE_ECC_ERR %x ,page is %x \n", correct_num, page_addr) ; + } + + if (length && buf) + { + memcpy_length = (length > (pack_data.phy_bytes_length - page_copy_offset)) ? (pack_data.phy_bytes_length - page_copy_offset) : length; + FCacheDCacheFlushRange((intptr)(instance_p->dma_data_buffer.data_buffer + page_copy_offset), memcpy_length); + memcpy(buf, instance_p->dma_data_buffer.data_buffer + page_copy_offset, memcpy_length); + } + + return FT_SUCCESS; +} + +FError FNandFlashReadPageRaw(FNand *instance_p, FNandOpData *op_data_p) +{ + FASSERT(instance_p != NULL); + FASSERT(op_data_p != NULL); + FError ret; + ret = FNandPageRead(instance_p, op_data_p->page_addr, op_data_p->page_buf, op_data_p->page_offset, op_data_p->page_length, op_data_p->chip_addr); + + if (ret != FT_SUCCESS) + { + FNAND_COMMON_DEBUG_E("%s,FNandPageRead is error %x", __func__, ret); + return ret; + } + + if (op_data_p->obb_required) + { + ret = FNandPageReadOOb(instance_p, op_data_p->page_addr, op_data_p->oob_buf, op_data_p->oob_offset, op_data_p->oob_length, op_data_p->chip_addr); + + if (ret != FT_SUCCESS) + { + FNAND_COMMON_DEBUG_E("%s,FNandPageReadOOb is error %x", __func__, ret); + return ret; + } + } + + return FT_SUCCESS; +} + +FError FNandFlashReadPageHwEcc(FNand *instance_p, FNandOpData *op_data_p) +{ + FASSERT(instance_p != NULL); + FASSERT(op_data_p != NULL); + FError ret; + FNandConfig *config_p; + config_p = &instance_p->config; + FNandFlashReadStatus(instance_p, op_data_p->chip_addr); + + FNandEnableHwEcc(config_p->base_address); + ret = FNandPageReadHwEcc(instance_p, op_data_p->page_addr, op_data_p->page_buf, op_data_p->page_offset, op_data_p->page_length, op_data_p->chip_addr); + FNandDisableHwEcc(config_p->base_address); + + if (ret != FT_SUCCESS) + { + FNAND_COMMON_DEBUG_E("%s,FNandPageReadHwEcc is error %x", __func__, ret); + return ret; + } + + + if (op_data_p->obb_required) + { + ret = FNandPageReadOOb(instance_p, op_data_p->page_addr, op_data_p->oob_buf, op_data_p->oob_offset, op_data_p->oob_length, op_data_p->chip_addr); + if (ret != FT_SUCCESS) + { + FNAND_COMMON_DEBUG_E("%s,FNandPageReadOOb is error %x", __func__, ret); + return ret; + } + } + + return FT_SUCCESS; +} + +FError FNandFlashWritePageRaw(FNand *instance_p, FNandOpData *op_data_p) +{ + FASSERT(instance_p != NULL); + FASSERT(op_data_p != NULL); + FError ret; + if (op_data_p->obb_required) + { + ret = FNandPageWriteOOb(instance_p, op_data_p->page_addr, op_data_p->oob_buf, op_data_p->oob_offset, op_data_p->oob_length, op_data_p->chip_addr); + + if (ret != FT_SUCCESS) + { + FNAND_COMMON_DEBUG_E("%s,FNandPageWriteOOb is error %x", __func__, ret); + return ret; + } + } + + ret = FNandPageWrite(instance_p, op_data_p->page_addr, op_data_p->page_buf, op_data_p->page_offset, op_data_p->page_length, op_data_p->chip_addr); + + if (ret != FT_SUCCESS) + { + FNAND_COMMON_DEBUG_E("%s,FNandPageWrite is error %x", __func__, ret); + return ret; + } + + return FT_SUCCESS; +} + + +FError FNandFlashWritePageRawHwEcc(FNand *instance_p, FNandOpData *op_data_p) +{ + FError ret; + FNandConfig *config_p; + FASSERT(instance_p != NULL); + FASSERT(op_data_p != NULL); + + FNandFlashReadStatus(instance_p, op_data_p->chip_addr); + + config_p = &instance_p->config; + if (op_data_p->obb_required) + { + ret = FNandPageWriteOOb(instance_p, op_data_p->page_addr, op_data_p->oob_buf, op_data_p->oob_offset, op_data_p->oob_length, op_data_p->chip_addr); + + if (ret != FT_SUCCESS) + { + FNAND_COMMON_DEBUG_E("%s,FNandPageWriteOOb is error %x", __func__, ret); + return ret; + } + } + FNandEnableHwEcc(config_p->base_address); + ret = FNandPageWriteHwEcc(instance_p, op_data_p->page_addr, op_data_p->page_buf, op_data_p->page_offset, op_data_p->page_length, op_data_p->chip_addr); + FNandDisableHwEcc(config_p->base_address); + + if (ret != FT_SUCCESS) + { + FNAND_COMMON_DEBUG_E("%s,FNandPageWrite is error %x", __func__, ret); + return ret; + } + + return FT_SUCCESS; +} + + +FError FNandFlashOObRead(FNand *instance_p, FNandOpData *op_data_p) +{ + FASSERT(instance_p != NULL); + FASSERT(op_data_p != NULL); + FError ret; + FNandConfig *config_p; + config_p = &instance_p->config; + + ret = FNandPageReadOOb(instance_p, op_data_p->page_addr, op_data_p->oob_buf, op_data_p->oob_offset, op_data_p->oob_length, op_data_p->chip_addr); + if (ret != FT_SUCCESS) + { + FNAND_COMMON_DEBUG_E("%s,FNandPageReadOOb is error %x", __func__, ret); + return ret; + } + + return FT_SUCCESS; +} + + + +FError FNandFlashOObWrite(FNand *instance_p, FNandOpData *op_data_p) +{ + + FASSERT(instance_p != NULL); + FASSERT(op_data_p != NULL); + FError ret; + + ret = FNandPageWriteOOb(instance_p, op_data_p->page_addr, op_data_p->oob_buf, op_data_p->oob_offset, op_data_p->oob_length, op_data_p->chip_addr); + if (ret != FT_SUCCESS) + { + FNAND_COMMON_DEBUG_E("%s,FNandPageWriteOOb is error %x", __func__, ret); + return ret; + } +} + + +void FNandFlashFuncRegister(FNand *instance_p) +{ + FASSERT(instance_p != NULL); + instance_p->write_p = FNandFlashWritePageRaw ; + instance_p->read_p = FNandFlashReadPageRaw ; + instance_p->erase_p = FNandFlashEraseBlock ; + instance_p->write_hw_ecc_p = FNandFlashWritePageRawHwEcc ; + instance_p->read_hw_ecc_p = FNandFlashReadPageHwEcc ; + instance_p->write_oob_p = FNandFlashOObWrite; + instance_p->read_oob_p = FNandFlashOObRead; +} + diff --git a/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_common_cmd.h b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_common_cmd.h new file mode 100644 index 0000000000..2c2733d5bd --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_common_cmd.h @@ -0,0 +1,74 @@ +/* + * @Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fnand_common_cmd.h + * @Date: 2022-07-05 19:01:01 + * @LastEditTime: 2022-07-05 19:01:02 + * @Description: This file is for + * + * @Modify History: + * Ver Who Date Changes + * ----- ------ -------- -------------------------------------- + */ + +#ifndef DRIVERS_NAND_COMMON_CMD_H +#define DRIVERS_NAND_COMMON_CMD_H + +#include "ftypes.h" +#include "fnand.h" + +/* + * Mandatory commands + */ + +#define NAND_CMD_READ1 0x00 +#define NAND_CMD_READ2 0x30 /* READ PAGE */ + +#define NAND_CMD_CHANGE_READ_COLUMN1 0x05 /* NAND Random data Read \ + Column command (1st \ + cycle) */ +#define NAND_CMD_CHANGE_READ_COLUMN2 0xE0 /* NAND Random data Read \ + Column command (2nd \ + cycle) */ +#define NAND_CMD_BLOCK_ERASE1 0x60 /* NAND Block Erase \ + (1st cycle) */ +#define NAND_CMD_BLOCK_ERASE2 0xD0 /* NAND Block Erase \ + (2nd cycle) */ + +#define NAND_CMD_PAGE_PROG1 0x80 /* NAND Page Program \ + command (1st cycle) \ + */ +#define NAND_CMD_PAGE_PROG2 0x10 /* NAND Page Program \ + command (2nd cycle) \ + */ + +#define NAND_CMD_CHANGE_WRITE_COLUMN 0x85 /* NAND Change Write \ + Column command */ +#define NAND_CMD_READ_ID 0x90 /* NAND Read ID \ + command */ +#define NAND_CMD_READ_PARAM_PAGE 0xEC /* NAND Read \ + Parameter Page \ + command */ +#define NAND_CMD_RESET 0xFF /* NAND Reset \ + command */ + +#define NAND_END_CMD_NONE 0xfff /* No End command */ + +#define NAND_CMD_READ_STATUS 0x70 /* Read status */ + +FError FNandFlashReset(FNand *instance_p, u32 chip_addr) ; +FError FNandFlashReadId(FNand *instance_p, u8 address, u8 *id_buffer, u32 buffer_length, u32 chip_addr); +void FNandFlashFuncRegister(FNand *instance_p); + +#endif + diff --git a/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_dma.c b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_dma.c new file mode 100644 index 0000000000..0c76bce917 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_dma.c @@ -0,0 +1,153 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fnand_dma.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:56:22 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#include "fnand_dma.h" +#include "fnand.h" +#include "ferror_code.h" +#include "fdebug.h" +#include +#include +#include "fcache.h" +#include "fnand_toggle.h" +#include "sdkconfig.h" + +#ifdef CONFIG_FNAND_DMA_DEBUG_EN + #define FNAND_DMA_DEBUG_TAG "FNAND_DMA" + #define FNAND_DMA_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(FNAND_DMA_DEBUG_TAG, format, ##__VA_ARGS__) + #define FNAND_DMA_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(FNAND_DMA_DEBUG_TAG, format, ##__VA_ARGS__) + #define FNAND_DMA_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(FNAND_DMA_DEBUG_TAG, format, ##__VA_ARGS__) +#else + #define FNAND_DMA_DEBUG_I(format, ...) + #define FNAND_DMA_DEBUG_W(format, ...) + #define FNAND_DMA_DEBUG_E(format, ...) +#endif + + +void FNandDmaDump(struct FNandDmaDescriptor *descriptor_p) +{ + FNAND_DMA_DEBUG_I("Phytium NFC cmd dump:\n"); + FNAND_DMA_DEBUG_I("cmd0:%x, cmd1:%x, ctrl:%x, page_cnt:%d\n", + descriptor_p->cmd0, descriptor_p->cmd1, descriptor_p->cmd_ctrl.ctrl, descriptor_p->page_cnt); + FNAND_DMA_DEBUG_I("mem_addr_first:%02x %02x %02x %02x %02x\n", + descriptor_p->mem_addr_first[0], descriptor_p->mem_addr_first[1], descriptor_p->mem_addr_first[2], descriptor_p->mem_addr_first[3], descriptor_p->mem_addr_first[4]); + FNAND_DMA_DEBUG_I("addr:%02x %02x %02x %02x %02x\n", + descriptor_p->addr[0], descriptor_p->addr[1], descriptor_p->addr[2], descriptor_p->addr[3], descriptor_p->addr[4]); + + FNAND_DMA_DEBUG_I(" csel : 0x%x ", descriptor_p->cmd_ctrl.nfc_ctrl.csel); + FNAND_DMA_DEBUG_I(" dbc : %d ", descriptor_p->cmd_ctrl.nfc_ctrl.dbc); + FNAND_DMA_DEBUG_I(" addr_cyc : %d ", descriptor_p->cmd_ctrl.nfc_ctrl.addr_cyc); + FNAND_DMA_DEBUG_I(" nc : %d ", descriptor_p->cmd_ctrl.nfc_ctrl.nc); + FNAND_DMA_DEBUG_I(" cmd_type : %d ", descriptor_p->cmd_ctrl.nfc_ctrl.cmd_type); + FNAND_DMA_DEBUG_I(" dc : %d ", descriptor_p->cmd_ctrl.nfc_ctrl.dc); + FNAND_DMA_DEBUG_I(" auto_rs : %d ", descriptor_p->cmd_ctrl.nfc_ctrl.auto_rs); + FNAND_DMA_DEBUG_I(" ecc_en : %d ", descriptor_p->cmd_ctrl.nfc_ctrl.ecc_en); + +} + +static void FNandAddrCorrect(struct FNandDmaDescriptor *descriptor_p, + u8 *addr_p, + u32 addr_length) +{ + int i, j; + if (addr_length == 0 || addr_p == NULL) + { + FNAND_DMA_DEBUG_I("addr_p is null ,Calibration is not required "); + return; + } + + if (addr_length >= FNAND_NFC_ADDR_MAX_LEN) + { + memcpy(descriptor_p->addr, addr_p, FNAND_NFC_ADDR_MAX_LEN); + descriptor_p->cmd_ctrl.nfc_ctrl.addr_cyc = FNAND_NFC_ADDR_MAX_LEN; + return; + } + + descriptor_p->cmd_ctrl.nfc_ctrl.addr_cyc = 0; + for (i = addr_length - 1, j = FNAND_NFC_ADDR_MAX_LEN - 1; i >= 0; i--, j--) + { + descriptor_p->addr[j] = addr_p[i]; /* data shift to high array */ + descriptor_p->addr[i] = 0; + descriptor_p->cmd_ctrl.nfc_ctrl.addr_cyc++; + } +} + +FError FNandDmaPack(FNandCmdFormat *cmd_format, + struct FNandDmaDescriptor *descriptor_p, + FNandDmaPackData *pack_data_p + ) +{ + u32 i; + FASSERT(cmd_format != NULL); + FASSERT(descriptor_p != NULL); + // printf(" descriptor_p is %p \r\n",descriptor_p); + descriptor_p->cmd_ctrl.ctrl = 0; + + /* cmd */ + if (cmd_format->end_cmd == TOGGLE_END_CMD_NONE) /* Only one cmd ,need to correct */ + { + descriptor_p->cmd1 = cmd_format->start_cmd; + descriptor_p->cmd0 = 0; + } + else + { + descriptor_p->cmd0 = cmd_format->start_cmd; + descriptor_p->cmd1 = cmd_format->end_cmd; + descriptor_p->cmd_ctrl.nfc_ctrl.dbc = 1; + } + + /* addr */ + FNandAddrCorrect(descriptor_p, pack_data_p->addr_p, pack_data_p->addr_length); + descriptor_p->cmd_ctrl.nfc_ctrl.cmd_type = cmd_format->cmd_type; /* cmd type */ + FNAND_DMA_DEBUG_I("cmd_type is %x \r\n", descriptor_p->cmd_ctrl.nfc_ctrl.cmd_type); + if (pack_data_p->contiune_dma) + { + descriptor_p->cmd_ctrl.nfc_ctrl.nc = 1; + } + + descriptor_p->cmd_ctrl.nfc_ctrl.csel = (0xf ^ (1 << pack_data_p->chip_addr)); + + if (pack_data_p->phy_address && (pack_data_p->phy_bytes_length > 0)) + { + descriptor_p->cmd_ctrl.nfc_ctrl.dc = 1; + for (i = 0; i < FNAND_NFC_ADDR_MAX_LEN; i++) + { + descriptor_p->mem_addr_first[i] = pack_data_p->phy_address >> (8 * i) & 0xff; + } + descriptor_p->page_cnt = pack_data_p->phy_bytes_length; + } + + if (cmd_format->auto_rs) + descriptor_p->cmd_ctrl.nfc_ctrl.auto_rs = 1; + + if (cmd_format->ecc_en) + descriptor_p->cmd_ctrl.nfc_ctrl.ecc_en = 1; + + /* invalid descriptor and buffer */ + FCacheDCacheInvalidateRange((intptr)descriptor_p, sizeof(struct FNandDmaDescriptor)); + FCacheDCacheInvalidateRange((intptr)pack_data_p->addr_p, pack_data_p->addr_length); + + FNandDmaDump(descriptor_p); + + return FT_SUCCESS; +} diff --git a/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_dma.h b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_dma.h new file mode 100644 index 0000000000..4e792eaa4d --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_dma.h @@ -0,0 +1,111 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fnand_dma.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:56:27 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef DRIVERS_NAND_FNAND_DMA_H +#define DRIVERS_NAND_FNAND_DMA_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ftypes.h" + +#define FNAND_CMDCTRL_TYPE_RESET 0x00 /* reset */ +#define FNAND_CMDCTRL_TYPE_SET_FTR 0x01 /* Set features */ +#define FNAND_CMDCTRL_TYPE_GET_FTR 0x02 /* Get features */ +#define FNAND_CMDCTRL_TYPE_READ_ID 0x03 /* Read ID */ +#define FNAND_CMDCTRL_TYPE_READ_COL 0x03 /* Read Column */ +#define FNAND_CMDCTRL_TYPE_PAGE_PRO 0x04 /* Page program */ +#define FNAND_CMDCTRL_TYPE_ERASE 0x05 /* Block Erase */ +#define FNAND_CMDCTRL_TYPE_READ 0x06 /* Read */ +#define FNAND_CMDCTRL_TYPE_TOGGLE 0x07 /* Toggle Two_plane */ + +#define FNAND_CMDCTRL_READ_PARAM 0x02 +#define FNAND_CMDCTRL_READ_STATUS 0x03 + +#define FNAND_CMDCTRL_CH_READ_COL 0x03 +#define FNAND_CMDCTRL_CH_ROW_ADDR 0x01 +#define FNAND_CMDCTRL_CH_WR_COL 0x01 + +#define FNAND_NFC_ADDR_MAX_LEN 0x5 + +#define FNAND_DESCRIPTORS_SIZE 16 + +struct CmdCtrl +{ + u16 csel : 4; /* 每一位表示选择NAND FLASH 设备 */ + u16 dbc : 1; /* 表示是否有2级命令,1表示有,只有此位为1时,描述符表的CMD1才有效 */ + u16 addr_cyc : 3; /* 表示指令有几个周期,‘b000’:表示没有周期 ‘b001’:表示1一个地址周期,一次类推 */ + u16 nc : 1; /* 表示是否有连续的下一个指令,一般多页操作需要连续发送多个指令 */ + u16 cmd_type : 4; /* 表示命令类型 */ + u16 dc : 1; /* 表示命令发送是否包含有数据周期,有数据此位为1 */ + u16 auto_rs : 1; /* 表示命令发送完成后是否检测闪存状态 */ + u16 ecc_en : 1; /* ECC 数据发送和读取使能位,位1 表示该命令仅发送或者读取ECC数据,当读命令该位使能位1后,控制器会对上一次数据进行ECC 校验,并返回结果 */ +}; + +struct FNandDmaDescriptor +{ + u8 cmd0; /* NAND FLASH 第一个命令编码 */ + u8 cmd1; /* NAND FLASH 第二个命令编码 */ + union + { + u16 ctrl; + struct CmdCtrl nfc_ctrl; + } cmd_ctrl; /* 16位命令字 */ + u8 addr[FNAND_NFC_ADDR_MAX_LEN]; + u16 page_cnt; + u8 mem_addr_first[FNAND_NFC_ADDR_MAX_LEN]; + +} __attribute__((packed)) __attribute__((aligned(128))); + + + +typedef struct +{ + u8 *addr_p; /* Address */ + u32 addr_length; + uintptr phy_address; + u32 phy_bytes_length; + u32 chip_addr; + u8 contiune_dma; /* */ +} FNandDmaPackData; + + +/* DMA format */ +typedef struct +{ + s32 start_cmd; /* Start command */ + s32 end_cmd; /* End command */ + u8 addr_cycles; /* Number of address cycles */ + u8 cmd_type; /* Presentation command type ,followed by FNAND_CMDCTRL_XXXX */ + u8 ecc_en; /* Hardware ecc open */ + u8 auto_rs; /* 表示命令发送完成后是否检测闪存状态 */ +} FNandCmdFormat; + + +#ifdef __cplusplus +} +#endif + +#endif // ! diff --git a/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_ecc.c b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_ecc.c new file mode 100644 index 0000000000..f76f28544c --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_ecc.c @@ -0,0 +1,266 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fnand_ecc.c + * Date: 2022-05-12 14:17:42 + * LastEditTime: 2022-05-12 15:56:27 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#include "fnand_ecc.h" +#include "fnand.h" +#include "fnand_hw.h" +#include "fdebug.h" +#define FNAND_ECC_DEBUG_TAG "FNAND_ECC" +#define FNAND_ECC_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(FNAND_ECC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FNAND_ECC_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(FNAND_ECC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FNAND_ECC_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(FNAND_ECC_DEBUG_TAG, format, ##__VA_ARGS__) + + +/** + * @name: FNandGetEccTotalLength + * @msg: 根据page size 与 ecc_strength(纠错个数)确定硬件ecc 产生 + * @param {u32} bytes_per_page + * @param {u32} ecc_strength + * @return {*} + * @note: + */ +u32 FNandGetEccTotalLength(u32 bytes_per_page, u32 ecc_strength) +{ + int ecc_total = 0; + switch (bytes_per_page) + { + case 0x200: + if (ecc_strength == 8) + ecc_total = 0x0D; + else if (ecc_strength == 4) + ecc_total = 7; + else if (ecc_strength == 2) + ecc_total = 4; + else + ecc_total = 0; + break; + case 0x800: + if (ecc_strength == 8) + ecc_total = 0x34; + else if (ecc_strength == 4) + ecc_total = 0x1a; + else if (ecc_strength == 2) + ecc_total = 0xd; + else + ecc_total = 0; + break; + case 0x1000: + if (ecc_strength == 8) + ecc_total = 0x68; + else if (ecc_strength == 4) + ecc_total = 0x34; + else if (ecc_strength == 2) + ecc_total = 0x1a; + else + ecc_total = 0; + break; + case 0x2000: + if (ecc_strength == 8) + ecc_total = 0xD0; + else if (ecc_strength == 4) + ecc_total = 0x68; + else if (ecc_strength == 2) + ecc_total = 0x34; + else + ecc_total = 0; + break; + case 0x4000: + if (ecc_strength == 8) + ecc_total = 0x1A0; + if (ecc_strength == 4) + ecc_total = 0xD0; + else if (ecc_strength == 2) + ecc_total = 0x68; + else + ecc_total = 0; + break; + default: + ecc_total = 0; + break; + } + + FNAND_ECC_DEBUG_I("[%s %d]writesize: 0x%x, ecc strength: %d, ecc_total: 0x%x\n", __func__, __LINE__, bytes_per_page, ecc_strength, ecc_total); + return ecc_total; +} + + +/** + * @name: FNandCorrectEcc + * @msg: + * @note: + * @return {*} + * @param {uintptr_t} base_address + * @param {u32} ecc_step_size 单次ecc 使用的步长大小 + * @param {u32} hw_ecc_steps 一页操作需要进行的ecc 次数 + * @param {u8*} buf page 页 对应的指针 + * @param {u32} length + */ +// s32 FNandCorrectEcc(uintptr_t base_address,u32 ecc_step_size,u32 hw_ecc_steps,u8* buf ,u32 length) +// { +// u32 i, j; +// u32 value, tmp; +// int stat = 0; +// if(!buf) +// { +// FNAND_ECC_DEBUG_E("page buffer is null"); +// return -1; +// } + +// /* i */ +// for (i = 0; i < hw_ecc_steps; i++) +// { +// for (j = 0; j < 2; j++) { +// value = FNAND_READREG(base_address, 0xB8 + 4 * (2 * i + j)); +// FNAND_ECC_DEBUG_W("index:%x i is %d ,j is %d ", +// 0xB8 + 4 * (2 * i + j),i,j); +// if (value) +// { +// FNAND_ECC_DEBUG_W("offset:%x value:0x%08x\n", +// 0xB8 + 4 * (2 * i + j), value); +// //phytium_nfc_data_dump2(nfc, nfc->dma_buf + (ecc_step_size * i + tmp/8)/512, 512); +// } + +// tmp = value & 0xFFFF; +// if (tmp && (tmp <= 4096)) +// { +// tmp -= 1; +// FNAND_ECC_DEBUG_W( "ECC_CORRECT %x %02x\n", +// ecc_step_size * i + tmp / 8, +// buf[ecc_step_size * i + tmp / 8]); + +// buf[ecc_step_size*i + tmp/8] ^= (0x01 << tmp%8); +// stat++; + +// FNAND_ECC_DEBUG_W( "ECC_CORRECT xor %x %02x\n", +// 0x01 << tmp % 8, +// buf[ecc_step_size * i + tmp / 8]); +// } +// else +// { +// FNAND_ECC_DEBUG_E("ECC_CORRECT offset > 4096!\n"); +// } + +// tmp = (value >> 16) & 0xFFFF; +// if (tmp && (tmp <= 4096) ) +// { +// tmp -= 1; +// FNAND_ECC_DEBUG_W( "ECC_CORRECT %x %02x\n", +// ecc_step_size * i + tmp / 8, +// buf[ecc_step_size * i + tmp / 8]); + +// buf[ecc_step_size*i + tmp/8] ^= (0x01 << tmp%8); +// stat++; + +// FNAND_ECC_DEBUG_W( "ECC_CORRECT xor %x %02x\n", +// ecc_step_size * i + tmp / 8, +// buf[ecc_step_size * i + tmp / 8]); +// } +// else +// { +// FNAND_ECC_DEBUG_E("ECC_CORRECT offset > 4096!\n"); +// } +// } +// } + +// return stat; + +// } + +// 校验offset 0xb8 + i * 0x10 +// 校验强度为 2 j = 1 +// 校验强度为 4 j = 2 +// 校验强度为 8 j = 4 + + + + +s32 FNandCorrectEcc(uintptr_t base_address, u32 ecc_step_size, u32 hw_ecc_steps, u8 *buf, u32 length) +{ + u32 i, j; + u32 value, tmp; + int stat = 0; + if (!buf) + { + FNAND_ECC_DEBUG_E("page buffer is null"); + return -1; + } + + /* i */ + for (i = 0; i < hw_ecc_steps; i++) + { + for (j = 0; j < 4; j++) + { + // value = FNAND_READREG(base_address, 0xB8 + 4 * (2 * i + j)); + value = FNAND_READREG(base_address, 0xB8 + 0x10 * i + 4 * j); + // FNAND_ECC_DEBUG_W("index:%x i is %d ,j is %d ", + // 0xB8 + 0x10 * i + 4*j,i,j); + if (value) + { + // FNAND_ECC_DEBUG_W("offset:%x value:0x%08x\n", + // 0xB8 + 0x10 * i + 4*j, value); + //phytium_nfc_data_dump2(nfc, nfc->dma_buf + (ecc_step_size * i + tmp/8)/512, 512); + } + + tmp = value & 0xFFFF; + if (tmp && (tmp <= 4096)) + { + tmp -= 1; + FNAND_ECC_DEBUG_W("ECC_CORRECT %x %02x\n", + ecc_step_size * i + tmp / 8, + buf[ecc_step_size * i + tmp / 8]); + + buf[ecc_step_size * i + tmp / 8] ^= (0x01 << tmp % 8); + stat++; + + FNAND_ECC_DEBUG_W("ECC_CORRECT xor %x %02x\n", + 0x01 << tmp % 8, + buf[ecc_step_size * i + tmp / 8]); + } + + + tmp = (value >> 16) & 0xFFFF; + if (tmp && (tmp <= 4096)) + { + tmp -= 1; + FNAND_ECC_DEBUG_W("ECC_CORRECT %x %02x\n", + ecc_step_size * i + tmp / 8, + buf[ecc_step_size * i + tmp / 8]); + + buf[ecc_step_size * i + tmp / 8] ^= (0x01 << tmp % 8); + stat++; + + FNAND_ECC_DEBUG_W("ECC_CORRECT xor %x %02x\n", + ecc_step_size * i + tmp / 8, + buf[ecc_step_size * i + tmp / 8]); + } + + } + } + + return stat; + +} + + + diff --git a/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_ecc.h b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_ecc.h new file mode 100644 index 0000000000..3c0bbe5bd9 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_ecc.h @@ -0,0 +1,45 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fnand_ecc.h + * Date: 2022-05-12 11:17:42 + * LastEditTime: 2022-05-12 13:56:27 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef DRIVERS_NAND_FNAND_ECC_H +#define DRIVERS_NAND_FNAND_ECC_H + +#include "ftypes.h" +#include "fnand_hw.h" +#include "stdio.h" + +static inline void FNandEnableHwEcc(uintptr_t base_address) +{ + FNAND_SETBIT(base_address, FNAND_CTRL0_OFFSET, FNAND_CTRL0_ECC_EN_MASK); + // printf("base_address is %p ,value is 0x%x \r\n",base_address,FNAND_READREG(base_address,FNAND_CTRL0_OFFSET)); +} + + +static inline void FNandDisableHwEcc(uintptr_t base_address) +{ + FNAND_CLEARBIT(base_address, FNAND_CTRL0_OFFSET, FNAND_CTRL0_ECC_EN_MASK); +} + +u32 FNandGetEccTotalLength(u32 bytes_per_page, u32 ecc_strength); +s32 FNandCorrectEcc(uintptr_t base_address, u32 ecc_step_size, u32 hw_ecc_steps, u8 *buf, u32 length); +#endif diff --git a/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_g.c b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_g.c new file mode 100644 index 0000000000..4686dbe832 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_g.c @@ -0,0 +1,40 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fnand_g.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:56:31 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "fnand.h" +#include "fnand_hw.h" +#include "fparameters.h" + + +FNandConfig FNandConfigTable[FNAND_NUM] = +{ + { + .instance_id = FNAND_INSTANCE0, /* Id of device*/ + .irq_num = FNAND_IRQ_NUM, /* Irq number */ + .base_address = FNAND_BASEADDRESS, + .ecc_strength = 8, /* 每次ecc 步骤纠正的位数 */ + .ecc_step_size = 512 /* 进行读写操作时,单次ecc 的步骤的跨度 */ + }, +}; + + diff --git a/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_hw.c b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_hw.c new file mode 100644 index 0000000000..e72a98a260 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_hw.c @@ -0,0 +1,58 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fnand_hw.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:56:36 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "fnand_hw.h" +#include "fnand.h" + + + +void FNandEnable(uintptr_t base_address) +{ + FNAND_SETBIT(base_address, FNAND_CTRL0_OFFSET, FNAND_CTRL0_EN_MASK); /* 使能控制器 */ +} + + +void FNandHwReset(uintptr_t base_address) +{ + FNAND_WRITEREG(base_address, FNAND_INTRMASK_OFFSET, FNAND_INTRMASK_ALL_INT_MASK); /* 屏蔽所有中断位 */ + FNAND_WRITEREG(base_address, FNAND_STATE_OFFSET, FNAND_STATE_ALL_BIT); /* 清空所有状态位 */ + FNAND_WRITEREG(base_address, FNAND_ERROR_CLEAR_OFFSET, FNAND_ERROR_ALL_CLEAR); /* 清除所有错误位 */ + FNAND_WRITEREG(base_address, FNAND_FIFO_FREE_OFFSET, FNAND_FIFO_FREE_MASK); /* 清空fifo */ + // FNAND_CLEARBIT(base_address,FNAND_CTRL0_OFFSET,FNAND_CTRL0_SPARE_SIZE_EN_MASK|FNAND_CTRL0_ECC_EN_MASK ); /* 关闭ECC 位、并且关闭spare size 有效位 */ +} + + +void FNandHwInit(uintptr_t base_address, FNandInterMode inter_mode) +{ + // FNAND_WRITEREG(base_address, FNAND_CTRL1_OFFSET, FNAND_CTRL1_SAMPL_PHASE_MAKE(1UL)); /* 读取数据时的采样速度 */ + FNAND_SETBIT(base_address, FNAND_CTRL1_OFFSET, FNAND_CTRL1_SAMPL_PHASE_MAKE(5UL)); + FNAND_CLEARBIT(base_address, FNAND_CTRL1_OFFSET, FNAND_CTRL1_ECC_DATA_FIRST_EN_MASK); + + // FNAND_SETBIT(base_address, FNAND_CTRL1_OFFSET, FNAND_CTRL1_ECC_DATA_FIRST_EN_MASK); /* 开启先读取ECC 数据,然后读入对应数据 */ + FNAND_WRITEREG(base_address, FNAND_INTERVAL_OFFSET, FNAND_INTERVAL_TIME_MAKE(1UL)); /* 命令 、地址、数据之间的时间间隔 */ + FNAND_WRITEREG(base_address, FNAND_FIFO_LEVEL0_FULL_OFFSET, FNAND_FIFO_LEVEL0_FULL_THRESHOLD_MASK & 4); /* 满阈值配置 1/2 full, default 0 */ + FNAND_WRITEREG(base_address, FNAND_FIFO_LEVEL1_EMPTY_OFFSET, FNAND_FIFO_LEVEL1_EMPTY_THRESHOLD_MASK & 4); /* 空阈值配置 1/2 empty, default 0 */ + FNAND_WRITEREG(base_address, FNAND_FIFO_FREE_OFFSET, FNAND_FIFO_FREE_MASK); /* 清空fifo 操作 */ + FNAND_WRITEREG(base_address, FNAND_ERROR_CLEAR_OFFSET, FNAND_ERROR_CLEAR_DSP_ERR_CLR_MASK); /* 清除所有错误 */ + FNAND_WRITEREG(base_address, FNAND_CTRL0_OFFSET, FNAND_CTRL0_ECC_CORRECT_MAKE(1UL) | FNAND_CTRL0_INTER_MODE((unsigned long)(inter_mode))); /* 纠错为2位 ,使用配置的模式 */ +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_hw.h b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_hw.h new file mode 100644 index 0000000000..cf71a2d04c --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_hw.h @@ -0,0 +1,320 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fnand_hw.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:56:40 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#ifndef DRIVERS_NAND_FNAND_HW_H +#define DRIVERS_NAND_FNAND_HW_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "fkernel.h" +#include "ftypes.h" +#include "fio.h" +#include "fkernel.h" + +#define FNAND_CTRL0_OFFSET 0x00000000U +#define FNAND_CTRL1_OFFSET 0x00000004U +#define FNAND_MADDR0_OFFSET 0x00000008U +#define FNAND_MADDR1_OFFSET 0x0000000CU +/* ASY */ +#define FNAND_ASY_TIMING0_OFFSET 0x00000010U +#define FNAND_ASY_TIMING1_OFFSET 0x00000014U +#define FNAND_ASY_TIMING2_OFFSET 0x00000018U +#define FNAND_ASY_TIMING3_OFFSET 0x0000001CU +#define FNAND_ASY_TIMING4_OFFSET 0x00000020U +#define FNAND_ASY_TIMING5_OFFSET 0x00000024U +/* SYN */ +#define FNAND_SYN_TIMING6_OFFSET 0x00000028U +#define FNAND_SYN_TIMING7_OFFSET 0x0000002CU +#define FNAND_SYN_TIMING8_OFFSET 0x00000030U +#define FNAND_SYN_TIMING9_OFFSET 0x00000034U +#define FNAND_SYN_TIMING10_OFFSET 0x00000038U +#define FNAND_SYN_TIMING11_OFFSET 0x0000003CU +#define FNAND_SYN_TIMING12_OFFSET 0x00000040U +/* TOG */ +#define FNAND_TOG_TIMING13_OFFSET 0x00000044U +#define FNAND_TOG_TIMING14_OFFSET 0x00000048U +#define FNAND_TOG_TIMING15_OFFSET 0x0000004CU +#define FNAND_TOG_TIMING16_OFFSET 0x00000050U +#define FNAND_TOG_TIMING17_OFFSET 0x00000054U +#define FNAND_TOG_TIMING18_OFFSET 0x00000058U + +#define FNAND_FIFORSTA_OFFSET 0x0000005CU +#define FNAND_INTERVAL_OFFSET 0x00000060U +#define FNAND_CMDINTERVAL_OFFSET 0x00000064U +#define FNAND_FIFO_TIMEOUT_OFFSET 0x00000068U +#define FNAND_FIFO_LEVEL0_FULL_OFFSET 0x0000006CU +#define FNAND_FIFO_LEVEL1_EMPTY_OFFSET 0x00000070U +#define FNAND_WP_OFFSET 0x00000074U +#define FNAND_FIFO_FREE_OFFSET 0x00000078U +#define FNAND_STATE_OFFSET 0x0000007CU +#define FNAND_INTRMASK_OFFSET 0x00000080U +#define FNAND_INTR_OFFSET 0x00000084U +#define FNAND_ERROR_CLEAR_OFFSET 0x0000008CU +#define FNAND_ERROR_LOCATION_OFFSET 0x000000B8U + +/* FNAND_CTRL0_OFFSET */ +#define FNAND_CTRL0_EN_MASK BIT(0) +#define FNAND_CTRL0_WIDTH_MASK BIT(1) /* DQ width, only for ONFI async mode. 0: 8bits, 1: 16bits*/ +#define FNAND_CTRL0_INTER_MODE(x) (min((x), (0x3UL)) << 2) /* Nand Flash interface mode. 00: ONFI Async; 01: ONFI Sync; 10: Toggle Async*/ +#define FNAND_CTRL0_ECC_EN_MASK BIT(4) /* Nand Flash hardware ECC enable */ +#define FNAND_CTRL0_ECC_CORRECT_MAKE(x) (min((x), (0x7UL)) << 5) /* Nand Flash ECC strength. 3'h2: 2bits; 3'h4: 4bits */ +#define FNAND_CTRL0_SPARE_SIZE_EN_MASK BIT(8) /* Data with spare */ +#define FNAND_CTRL0_SPARE_SIZE_MASK GENMASK(31, 9) /* Spare size */ + +/* FNAND_CTRL1_OFFSET */ +// #define FNAND_CTRL1_SAMPL_PHASE_MASK GENMASK(15,0) /* when onfi synchronization or toggle mode, the cycle of receive data sampling phase */ +#define FNAND_CTRL1_SAMPL_PHASE_MAKE(x) (min((x), GENMASK(15, 0))) /* when onfi synchronization or toggle mode, the cycle of receive data sampling phase */ +#define FNAND_CTRL1_ECC_DATA_FIRST_EN_MASK BIT(16) /* ECC data is read first and then the corresponding data is read */ +#define FNAND_CTRL1_RB_SHARE_EN_MASK BIT(17) /* The R/B signal sharing function of four devices is enabled. Write 1 is enabled */ +#define FNAND_CTRL1_ECC_BYPASS_MASK BIT(18) /* When the received ECC encoded address data is 13'hff, the ECC verification function is bypass. 1 indicates that the function is enabled */ + +/* FNAND_MADDR0_OFFSET */ +#define FNAND_MADDR0_DT_LOW_ADDR_MASK GENMASK(31, 0) /* The lower 32 bits of the descriptor table header address in memory storage */ + +/* FNAND_MADDR1_OFFSET */ +#define FNAND_MADDR1_DT_HIGH_8BITADDR_MASK GENMASK(7, 0) /* The high 8 bits of the first address of the descriptor table stored in memory */ +#define FNAND_MADDR1_DMA_EN_MASK BIT(8) /* DMA transfer enable bit. This bit is 1 for the controller to start DMA transfers */ +#define FNAND_MADDR1_DMA_READ_LENGTH_MASK GENMASK(23, 16) /* Sets the length to which dma reads data */ +#define FNAND_MADDR1_DMA_WRITE_LENGTH_MASK GENMASK(31, 24) /* Sets the length to which dma writes data */ + +/* FNAND_ASY_TIMING0_OFFSET */ +#define FNAND_ASY_TIMING0_TCLS_TWP_MASK GENMASK(31, 16) /* tCL-tWP */ +#define FNAND_ASY_TIMING0_TCLS_TCS_MASK GENMASK(15, 0) /* tCS-tCLS */ + +/* FNAND_ASY_TIMING1_OFFSET */ +#define FNAND_ASY_TIMING1_TWH_MASK GENMASK(31, 16) /* tWH */ +#define FNAND_ASY_TIMING1_TWP_MASK GENMASK(15, 0) /* tWP */ + +/* FNAND_ASY_TIMING2_OFFSET */ +#define FNAND_ASY_TIMING2_TCLH_TWH_MASK GENMASK(31, 16) /* tCLH-tWH */ +#define FNAND_ASY_TIMING2_TCH_TCLH_MASK GENMASK(15, 0) /* tCH-tCLH */ + +/* FNAND_ASY_TIMING3_OFFSET */ +#define FNAND_ASY_TIMING3_TCH_TWH_MASK GENMASK(31, 16) /* tCH-tWH */ +#define FNAND_ASY_TIMING3_TDQ_EN_MASK GENMASK(15, 0) + +/* FNAND_ASY_TIMING4_OFFSET */ +#define FNAND_ASY_TIMING4_TREH_MASK GENMASK(31, 16) /* TREH */ +#define FNAND_ASY_TIMING4_TWHR_MASK GENMASK(15, 0) /* TWHR */ + +/* FNAND_ASY_TIMING5_OFFSET */ +#define FNAND_ASY_TIMING5_TADL_MASK GENMASK(31, 16) +#define FNAND_ASY_TIMING5_TRC_MASK GENMASK(15, 0) + +/* FNAND_SYN_TIMING6_OFFSET */ +#define FNAND_SYN_TIMING6_TCALS_TCH_MASK GENMASK(31, 16) +#define FNAND_SYN_TIMING6_TRC_MASK GENMASK(15, 0) + +/* FNAND_SYN_TIMING7_OFFSET */ +#define FNAND_SYN_TIMING7_TDQ_EN_MASK GENMASK(31, 16) +#define FNAND_SYN_TIMING7_TCK_MASK GENMASK(15, 0) + +/* FNAND_SYN_TIMING8_OFFSET */ +#define FNAND_SYN_TIMING8_TCK_MASK GENMASK(31, 16) +#define FNAND_SYN_TIMING8_TCAD_TCK_MASK GENMASK(15, 0) + +/* FNAND_SYN_TIMING9_OFFSET */ +#define FNAND_SYN_TIMING9_TCCS_MASK GENMASK(31, 16) +#define FNAND_SYN_TIMING9_TWHR_MASK GENMASK(15, 0) + +/* FNAND_SYN_TIMING10_OFFSET */ +#define FNAND_SYN_TIMING10_TCK_MASK GENMASK(31, 16) +#define FNAND_SYN_TIMING10_MTCK_MASK GENMASK(15, 0) + +/* FNAND_SYN_TIMING11_OFFSET */ +#define FNAND_SYN_TIMING11_TCK_TCALS_MASK GENMASK(15, 0) + +/* FNAND_SYN_TIMING12_OFFSET */ +#define FNAND_SYN_TIMING12_TCKWR_MASK GENMASK(31, 16) +#define FNAND_SYN_TIMING12_TWRCK_MASK GENMASK(15, 0) + +/* FNAND_TOG_TIMING13_OFFSET */ +#define FNAND_TOG_TIMING13_TWRPST_MASK GENMASK(31, 16) +#define FNAND_TOG_TIMING13_TWPRE_MASK GENMASK(15, 0) + +/* FNAND_TOG_TIMING14_OFFSET */ +#define FNAND_TOG_TIMING14_TCLS_TWP_MASK GENMASK(31, 16) +#define FNAND_TOG_TIMING14_TCS_TCLS_MASK GENMASK(15, 0) + +/* FNAND_TOG_TIMING15_OFFSET */ +#define FNAND_TOG_TIMING15_TWHR_MASK GENMASK(31, 16) +#define FNAND_TOG_TIMING15_TADL_MASK GENMASK(15, 0) + +/* FNAND_TOG_TIMING16_OFFSET */ +#define FNAND_TOG_TIMING16_TCLH_TWH_MASK GENMASK(31, 16) +#define FNAND_TOG_TIMING16_TCH_TCLH_MASK GENMASK(15, 0) + +/* FNAND_TOG_TIMING17_OFFSET */ +#define FNAND_TOG_TIMING17_TRPST_MASK GENMASK(31, 16) +#define FNAND_TOG_TIMING17_TRPRE_MASK GENMASK(15, 0) + +/* FNAND_TOG_TIMING18_OFFSET */ +#define FNAND_TOG_TIMING18_TRPSTH_MASK GENMASK(31, 16) +#define FNAND_TOG_TIMING18_DSC_MASK GENMASK(15, 0) + +/* FNAND_FIFORSTA_OFFSET */ +#define FNAND_FIFORSTA_FIFO_FULL_MASK BIT(11) +#define FNAND_FIFORSTA_FIFO_EMPTY_MASK BIT(10) +#define FNAND_FIFORSTA_FIFO_COUNT_MASK GENMASK(9, 0) + +/* FNAND_INTERVAL_OFFSET */ +// #define FNAND_INTERVAL_TIME_MASK GENMASK(15,0) /* The interval between commands, addresses, and data. The timeout increases by 2ns for every 1 increase in the write value */ +#define FNAND_INTERVAL_TIME_MAKE(x) (min((x), (0xFFUL))) + +/* FNAND_CMDINTERVAL_OFFSET */ +#define FNAND_CMDINTERVAL_MASK GENMASK(31, 0) /* The interval between requests. The timeout increases by 2ns for every 1 increase in the write value */ + +/* FNAND_FIFO_TIMEOUT_OFFSET */ +#define FNAND_FIFO_TIMEOUT_MASK GENMASK(31, 0) /* FIFO timeout counter, the timeout time increases by 2ns for each increment of the value written */ + +/* FNAND_FIFO_LEVEL0_FULL_OFFSET */ +#define FNAND_FIFO_LEVEL0_FULL_THRESHOLD_MASK GENMASK(3, 0) + +/* FNAND_FIFO_LEVEL1_EMPTY_OFFSET */ +#define FNAND_FIFO_LEVEL1_EMPTY_THRESHOLD_MASK GENMASK(3, 0) + +/* FNAND_WP_OFFSET */ +#define FNAND_WP_EN_MASK BIT(0) + +/* FNAND_FIFO_FREE_OFFSET */ +#define FNAND_FIFO_FREE_MASK BIT(0) + +/* FNAND_STATE_OFFSET */ +#define FNAND_STATE_BUSY_OFFSET BIT(0) /* nandflash控制器忙 */ +#define FNAND_STATE_DMA_BUSY_OFFSET BIT(1) /* dma控制器忙 */ +#define FNAND_STATE_DMA_PGFINISH_OFFSET BIT(2) /* dma数据操作完成 */ +#define FNAND_STATE_DMA_FINISH_OFFSET BIT(3) /* dma完成 */ +#define FNAND_STATE_FIFO_EMP_OFFSET BIT(4) +#define FNAND_STATE_FIFO_FULL_OFFSET BIT(5) +#define FNAND_STATE_FIFO_TIMEOUT_OFFSET BIT(6) +#define FNAND_STATE_CS_OFFSET GENMASK(10, 7) +#define FNAND_STATE_CMD_PGFINISH_OFFSET BIT(11) /* nand接口命令操作完成 */ +#define FNAND_STATE_PG_PGFINISH_OFFSET BIT(12) /* nand接口数据操作完成 */ +#define FNAND_STATE_RE_OFFSET BIT(13) /* re_n门控状态 */ +#define FNAND_STATE_DQS_OFFSET BIT(14) /* dqs门控状态 */ +#define FNAND_STATE_RB_OFFSET BIT(15) /* RB_N接口的状态 */ +#define FNAND_STATE_ECC_BUSY_OFFSET BIT(16) +#define FNAND_STATE_ECC_FINISH_OFFSET BIT(17) +#define FNAND_STATE_ECC_RIGHT_OFFSET BIT(18) +#define FNAND_STATE_ECC_ERR_OFFSET BIT(19) /* ECC 校验有错 */ +#define FNAND_STATE_ECC_ERROVER_OFFSET BIT(20) /* 错误超过可校验范围 */ +#define FNAND_STATE_AXI_DSP_ERR_OFFSET BIT(21) /* 描述符错误 */ +#define FNAND_STATE_AXI_RD_ERR_OFFSET BIT(22) +#define FNAND_STATE_AXI_WR_ERR_OFFSET BIT(23) +#define FNAND_STATE_RB_N_OFFSET GENMASK(27, 24) +#define FNAND_STATE_PROT_ERR_OFFSET BIT(28) +#define FNAND_STATE_ECCBYPASS_STA_OFFSET BIT(29) +#define FNAND_STATE_ALL_BIT GENMASK(29, 0) + +/* FNAND_INTRMASK_OFFSET */ +#define FNAND_INTRMASK_ALL_INT_MASK GENMASK(17, 0) +#define FNAND_INTRMASK_BUSY_MASK BIT(0) /* nandflash控制器忙状态中断屏蔽位 */ +#define FNAND_INTRMASK_DMA_BUSY_MASK BIT(1) /* dma控制器忙状态中断屏蔽位 */ +#define FNAND_INTRMASK_DMA_PGFINISH_MASK BIT(2) /* dma页操作完成中断屏蔽位 */ +#define FNAND_INTRMASK_DMA_FINISH_MASK BIT(3) /* dma操作完成中断完成中断屏蔽位 */ +#define FNAND_INTRMASK_FIFO_EMP_MASK BIT(4) /* fifo为空中断屏蔽位 */ +#define FNAND_INTRMASK_FIFO_FULL_MASK BIT(5) /* fifo为满中断屏蔽位 */ +#define FNAND_INTRMASK_FIFO_TIMEOUT_MASK BIT(6) /* fifo超时中断屏蔽位 */ +#define FNAND_INTRMASK_CMD_FINISH_MASK BIT(7) /* nand接口命令完成中断屏蔽位 */ +#define FNAND_INTRMASK_PGFINISH_MASK BIT(8) /* nand接口页操作完成中断屏蔽位 */ +#define FNAND_INTRMASK_RE_MASK BIT(9) /* re_n门控打开中断屏蔽位 */ +#define FNAND_INTRMASK_DQS_MASK BIT(10) /* dqs门控打开中断屏蔽位 */ +#define FNAND_INTRMASK_RB_MASK BIT(11) /* rb_n信号busy中断屏蔽位 */ +#define FNAND_INTRMASK_ECC_FINISH_MASK BIT(12) /* ecc完成中断屏蔽位 */ +#define FNAND_INTRMASK_ECC_ERR_MASK BIT(13) /* ecc 中断屏蔽位 */ + +/* FNAND_INTR_OFFSET */ +#define FNAND_INTR_ALL_INT_MASK GENMASK(17, 0) +#define FNAND_INTR_BUSY_MASK BIT(0) /* nandflash控制器忙状态中断状态位 */ +#define FNAND_INTR_DMA_BUSY_MASK BIT(1) /* dma控制器忙状态中断状态位 */ +#define FNAND_INTR_DMA_PGFINISH_MASK BIT(2) /* dma页操作完成中断状态位 */ +#define FNAND_INTR_DMA_FINISH_MASK BIT(3) /* dma操作完成中断完成中断状态位 */ +#define FNAND_INTR_FIFO_EMP_MASK BIT(4) /* fifo为空中断状态位 */ +#define FNAND_INTR_FIFO_FULL_MASK BIT(5) /* fifo为满中断状态位 */ +#define FNAND_INTR_FIFO_TIMEOUT_MASK BIT(6) /* fifo超时中断状态位 */ +#define FNAND_INTR_CMD_FINISH_MASK BIT(7) /* nand接口命令完成中断状态位 */ +#define FNAND_INTR_PGFINISH_MASK BIT(8) /* nand接口页操作完成中断状态位 */ +#define FNAND_INTR_RE_MASK BIT(9) /* re_n门控打开中断状态位 */ +#define FNAND_INTR_DQS_MASK BIT(10) /* dqs门控打开中断状态位 */ +#define FNAND_INTR_RB_MASK BIT(11) /* rb_n信号busy中断状态位 */ +#define FNAND_INTR_ECC_FINISH_MASK BIT(12) /* ecc完成中断状态蔽位 */ +#define FNAND_INTR_ECC_ERR_MASK BIT(13) /* ecc正确中断状态蔽位 */ + +/* FNAND_ERROR_CLEAR_OFFSET */ +#define FNAND_ERROR_CLEAR_DSP_ERR_CLR_MASK BIT(0) +#define FNAND_ERROR_CLEAR_AXI_RD_ERR_CLR_MASK BIT(1) +#define FNAND_ERROR_CLEAR_AXI_WR_ERR_CLR_MASK BIT(2) +#define FNAND_ERROR_CLEAR_ECC_ERR_CLR_MASK BIT(3) +#define FNAND_ERROR_ALL_CLEAR GENMASK(3, 0) + +#define FNAND_SELETED_MAX_NUMBER 4 + + + +/** +* +* This macro reads the given register. +* +* @param base_addr is the base address of the device. +* @param reg_offset is the register offset to be read. +* +* @return The 32-bit value of the register +* +* @note None. +* +*****************************************************************************/ +#define FNAND_READREG(base_addr, reg_offset) \ + FtIn32((base_addr) + (u32)(reg_offset)) + +/****************************************************************************/ +/** +* +* This macro writes the given register. +* +* @param base_addr is the base address of the device. +* @param reg_offset is the register offset to be written. +* @param data is the 32-bit value to write to the register. +* +* @return None. +* +* @note None. +* +*****************************************************************************/ +#define FNAND_WRITEREG(base_addr, reg_offset, data) \ + FtOut32((base_addr) + (u32)(reg_offset), (u32)(data)) + +#define FNAND_SETBIT(base_addr, reg_offset, data) \ + FtSetBit32((base_addr) + (u32)(reg_offset), (u32)(data)) + +#define FNAND_CLEARBIT(base_addr, reg_offset, data) \ + FtClearBit32((base_addr) + (u32)(reg_offset), (u32)(data)) + +#ifdef __cplusplus +} +#endif + +#endif // ! diff --git a/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_id.c b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_id.c new file mode 100644 index 0000000000..4b6c90713f --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_id.c @@ -0,0 +1,264 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fnand_id.c + * Date: 2022-07-06 08:34:27 + * LastEditTime: 2022-07-06 08:34:27 + * Description: This file is for + * + * Modify History: + * Ver Who Date Changes + * ----- ------ -------- -------------------------------------- + */ +#include "fdebug.h" +#include "fnand.h" +#include "fnand_id.h" +#include "fnand_common_cmd.h" +#include "fdebug.h" +#include "fkernel.h" +#include "sdkconfig.h" + +#define CONFIG_FNAND_ID_DEBUG_EN +#define FNAND_ID_DEBUG_TAG "FNAND_ID" +#ifdef CONFIG_FNAND_ID_DEBUG_EN + + #define FNAND_ID_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(FNAND_ID_DEBUG_TAG, format, ##__VA_ARGS__) + #define FNAND_ID_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(FNAND_ID_DEBUG_TAG, format, ##__VA_ARGS__) + #define FNAND_ID_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(FNAND_ID_DEBUG_TAG, format, ##__VA_ARGS__) + #define FNAND_ID_DEBUG_D(format, ...) FT_DEBUG_PRINT_D(FNAND_ID_DEBUG_TAG, format, ##__VA_ARGS__) +#else + #define FNAND_ID_DEBUG_I(format, ...) + #define FNAND_ID_DEBUG_W(format, ...) + #define FNAND_ID_DEBUG_E(format, ...) + #define FNAND_ID_DEBUG_D(format, ...) +#endif + + +/* + * NAND Flash Manufacturer ID Codes + */ +#define NAND_MFR_TOSHIBA 0x98 + + +/* Cell info constants */ +#define NAND_CI_CHIPNR_MSK 0x03 +#define NAND_CI_CELLTYPE_MSK 0x0C +#define NAND_CI_CELLTYPE_SHIFT 2 + + + +#define NAND_MAX_ID_LEN 8 + + + +extern FError FNandToggleInit(FNand *instance_p, u32 chip_addr); +extern FError FNandOnfiInit(FNand *instance_p, u32 chip_addr); +extern FError FNandTimingInterfaceUpdate(FNand *instance_p, u32 chip_addr); + +extern const struct FNandManuFacturerOps toshiba_ops; + +static const FNandManuFacturer fnand_manufacturers[] = +{ + {NAND_MFR_TOSHIBA, "Toshiba", &toshiba_ops}, +}; + +static int FnandIdHasPeriod(u8 *id_data_p, int arrlen, int period) +{ + int i, j; + for (i = 0; i < period; i++) + for (j = i + period; j < arrlen; j += period) + if (id_data_p[i] != id_data_p[j]) + return 0; + return 1; +} + +static int FNandIdLen(u8 *id_data_p, int data_length) +{ + int last_nonzero, period; + + for (last_nonzero = data_length - 1; last_nonzero >= 0; last_nonzero--) + if (id_data_p[last_nonzero]) + break; + + /* All zeros */ + if (last_nonzero < 0) + return 0; + + /* Calculate wraparound period */ + for (period = 1; period < data_length; period++) + if (FnandIdHasPeriod(id_data_p, data_length, period)) + break; + + /* There's a repeated pattern */ + if (period < data_length) + return period; + + /* There are trailing zeros */ + if (last_nonzero < data_length - 1) + return last_nonzero + 1; + + /* No pattern detected */ + return data_length; + +} + + +const FNandManuFacturer *FNandGetManuFacturer(u8 id) +{ + u32 i; + + for (i = 0; i < ARRAY_SIZE(fnand_manufacturers); i++) + { + if (fnand_manufacturers[i].id == id) + { + return &fnand_manufacturers[i]; + } + } + return NULL; +} + + +static FError FNandIdDetect(FNand *instance_p, u32 chip_addr) +{ + FError ret; + u32 i; + FNandId nand_id; + u8 *id_data = (u8 *)&nand_id.data; + u8 maf_id, dev_id; + const FNandManuFacturer *manufacturer_p ; + + + ret = FNandFlashReset(instance_p, chip_addr); + if (ret != FT_SUCCESS) + { + FNAND_ID_DEBUG_E("FNandFlashReset is error"); + return ret; + } + + /* step2 read device ID */ + ret = FNandFlashReadId(instance_p, 0, id_data, 2, chip_addr); + if (ret != FT_SUCCESS) + { + FNAND_ID_DEBUG_E("FNandFlashReadId is error"); + return ret; + } + + /* Read manufacturer and device IDs */ + maf_id = id_data[0]; + dev_id = id_data[1]; + + /* step 3 get entire device ID*/ + ret = FNandFlashReadId(instance_p, 0, id_data, sizeof(nand_id.data), chip_addr); + if (ret != FT_SUCCESS) + { + FNAND_ID_DEBUG_E("FNandFlashReadId is error"); + return ret; + } + /* step 5 compare ID string and device id */ + if (id_data[0] != maf_id || id_data[1] != dev_id) + { + FNAND_ID_DEBUG_E("second ID read did not match %02x,%02x against %02x,%02x\n", + maf_id, dev_id, id_data[0], id_data[1]); + return FNAND_ERR_NOT_MATCH; + } + + nand_id.len = FNandIdLen(id_data, ARRAY_SIZE(nand_id.data)); + + /* step 6 通过maf_id获取对应的参数 */ + manufacturer_p = FNandGetManuFacturer(maf_id); + + if (manufacturer_p == NULL) + { + FNAND_ID_DEBUG_E("Manufacturer not in list"); + return FNAND_ERR_NOT_MATCH; + } + + FNAND_ID_DEBUG_I("find manufacturer"); + if (manufacturer_p->ops->detect) + { + FNAND_ID_DEBUG_I("manufacturer_p->ops->detect"); + return manufacturer_p->ops->detect(instance_p, &nand_id, chip_addr); + } + else + { + FNAND_ID_DEBUG_E("manufacturer detect is empty"); + return FNAND_ERR_NOT_MATCH; + } + + return FT_SUCCESS; +} + +FError FNandDetect(FNand *instance_p) +{ + FError ret; + u32 i = 0; + + for (i = 0; i < FNAND_CONNECT_MAX_NUM; i++) + { + ret = FNandIdDetect(instance_p, i); + if (ret != FT_SUCCESS) + { + FNAND_ID_DEBUG_W("normal flash is not found"); + } + else + { + FNandFlashFuncRegister(instance_p) ; + FNAND_ID_DEBUG_I("Normal flash is found"); + continue; + } + + + ret = FNandToggleInit(instance_p, i); /* toggle 1.0 */ + if (ret != FT_SUCCESS) + { + FNAND_ID_DEBUG_W("toggle flash is not found"); + } + else + { + FNAND_ID_DEBUG_I("Scan %d nand is toggle mode", i); + instance_p->nand_flash_interface[i] = FNAND_TOGGLE_MODE; + ret = FNandTimingInterfaceUpdate(instance_p, i); + if (ret != FT_SUCCESS) + { + FNAND_ID_DEBUG_E("FNandTimingInterfaceUpdate is error"); + return ret; + } + /* open ecc length config */ + FNandFlashFuncRegister(instance_p) ; + FNAND_ID_DEBUG_I("Toggle flash is found"); + continue; + } + + ret = FNandOnfiInit(instance_p, i); + if (ret != FT_SUCCESS) + { + FNAND_ID_DEBUG_W("Onfi flash is not found"); + } + else + { + instance_p->nand_flash_interface[i] = FNAND_ONFI_MODE; + ret = FNandTimingInterfaceUpdate(instance_p, i); + if (ret != FT_SUCCESS) + { + FNAND_ID_DEBUG_E("FNandTimingInterfaceUpdate is error"); + return ret; + } + /* open ecc length config ,需要确保 ecc 校验的空间必须小于oob 的空间*/ + FNandFlashFuncRegister(instance_p) ; + FNAND_ID_DEBUG_I("Onfi flash is found"); + continue; + } + + } + return FT_SUCCESS; +} diff --git a/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_id.h b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_id.h new file mode 100644 index 0000000000..ccc80290c2 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_id.h @@ -0,0 +1,53 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fnand_id.h + * Date: 2022-07-06 14:19:15 + * LastEditTime: 2022-07-06 14:19:15 + * Description: This file is for + * + * Modify History: + * Ver Who Date Changes + * ----- ------ -------- -------------------------------------- + */ + + +#ifndef DRIVERS_NAND_DRIVER_FNAND +#define DRIVERS_NAND_DRIVER_FNAND + + +#include "ftypes.h" +#include "fnand.h" + + + + +struct FNandManuFacturerOps +{ + FError(*detect)(FNand *instance_p, FNandId *id_p, u32 chip_addr); /* detect chip */ + int (*init)(FNand *instance_p, u32 chip_addr); + void (*cleanup)(FNand *instance_p, u32 chip_addr); +}; + + +typedef struct +{ + int id; + char *name; + const struct FNandManuFacturerOps *ops; +} FNandManuFacturer; + +FError FNandDetect(FNand *instance_p); + + +#endif diff --git a/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_intr.c b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_intr.c new file mode 100644 index 0000000000..b4bdb48ea8 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_intr.c @@ -0,0 +1,203 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fnand_intr.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:56:46 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#include "fnand.h" +#include "fnand_hw.h" +#include "fassert.h" +#include "ferror_code.h" + +#include "fdebug.h" +#define FNAND_INTR_DEBUG_TAG "FNAND_INTR" +#define FNAND_INTR_ERROR(format, ...) FT_DEBUG_PRINT_E(FNAND_INTR_DEBUG_TAG, format, ##__VA_ARGS__) +#define FNAND_INTR_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(FNAND_INTR_DEBUG_TAG, format, ##__VA_ARGS__) +#define FNAND_INTR_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(FNAND_INTR_DEBUG_TAG, format, ##__VA_ARGS__) +#define FNAND_INTR_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(FNAND_INTR_DEBUG_TAG, format, ##__VA_ARGS__) + + +/** + * @name: FNandIsrEnable + * @msg: Enable the corresponding interrupt based on the interrupt mask + * @return {*} + * @note: + * @param {FNand} *instance_p is the pointer to the FNand instance + * @param {u32} int_mask is interrupt mask + */ +void FNandIsrEnable(FNand *instance_p, u32 int_mask) +{ + u32 reg_value; + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + FNandConfig *config_p; + config_p = &instance_p->config; + + FNAND_CLEARBIT(config_p->base_address, FNAND_INTRMASK_OFFSET, int_mask); +} + +/** + * @name: FNandIrqDisable + * @msg: Disable the corresponding interrupt based on the interrupt mask + * @note: + * @param {FNand} *instance_p is the pointer to the FNand instance + * @param {u32} int_mask is interrupt mask + * @return {*} + */ +void FNandIrqDisable(FNand *instance_p, u32 int_mask) +{ + u32 reg_value; + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + FNandConfig *config_p; + config_p = &instance_p->config; + + FNAND_SETBIT(config_p->base_address, FNAND_INTRMASK_OFFSET, int_mask); +} + + +/** + * @name: FNandSetIsrHandler + * @msg: Initializes isr event callback function + * @note: + * @param {FNand} *instance_p is the pointer to the FNand instance. + * @param {FnandIrqEventHandler} event_p is callback function used to respond to the interrupt event + * @param {void} *irq_args is the arguments of event callback + * @return {*} + */ +void FNandSetIsrHandler(FNand *instance_p, FnandIrqEventHandler event_p, void *irq_args) +{ + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + instance_p->irq_event_fun_p = event_p; + instance_p->irq_args = irq_args; +} + + +/** + * @name: FNandIrqHandler + * @msg: Nand driver isr handler + * @note: + * @param {s32} vector is interrupt number + * @param {void} *param is argument + * @return {*} + */ +void FNandIrqHandler(s32 vector, void *param) +{ + FNand *instance_p = (FNand *)param; + FNandConfig *config_p; + u32 status; + u32 en_irq; + (void)vector; + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + config_p = &instance_p->config; + + status = FNAND_READREG(config_p->base_address, FNAND_INTR_OFFSET); + en_irq = (~FNAND_READREG(config_p->base_address, FNAND_INTRMASK_OFFSET)) & FNAND_INTRMASK_ALL_INT_MASK; + + if ((status & en_irq) == 0) + { + FNAND_INTR_DEBUG_E("No irq exit"); + return; + } + + FNandIrqDisable(instance_p, status & FNAND_INTRMASK_ALL_INT_MASK); + FNAND_WRITEREG(config_p->base_address, 0xfd0, 0); + FNAND_WRITEREG(config_p->base_address, FNAND_INTR_OFFSET, status); + + if (instance_p->irq_event_fun_p) + { + if (status & FNAND_INTR_BUSY_MASK) + { + + instance_p->irq_event_fun_p(instance_p->irq_args, FNAND_IRQ_BUSY_EVENT); + } + + if (status & FNAND_INTR_DMA_BUSY_MASK) + { + + instance_p->irq_event_fun_p(instance_p->irq_args, FNAND_IRQ_DMA_BUSY_EVENT); + } + + if (status & FNAND_INTR_DMA_PGFINISH_MASK) + { + instance_p->irq_event_fun_p(instance_p->irq_args, FNAND_IRQ_DMA_PGFINISH_EVENT); + } + + if (status & FNAND_INTR_DMA_FINISH_MASK) + { + instance_p->irq_event_fun_p(instance_p->irq_args, FNAND_IRQ_DMA_FINISH_EVENT); + } + + if (status & FNAND_INTR_FIFO_EMP_MASK) + { + instance_p->irq_event_fun_p(instance_p->irq_args, FNAND_IRQ_FIFO_EMP_EVENT); + } + + if (status & FNAND_INTR_FIFO_FULL_MASK) + { + instance_p->irq_event_fun_p(instance_p->irq_args, FNAND_IRQ_FIFO_FULL_EVENT); + } + + if (status & FNAND_INTR_FIFO_TIMEOUT_MASK) + { + instance_p->irq_event_fun_p(instance_p->irq_args, FNAND_IRQ_FIFO_TIMEOUT_EVENT); + } + + if (status & FNAND_INTR_CMD_FINISH_MASK) + { + instance_p->irq_event_fun_p(instance_p->irq_args, FNAND_IRQ_CMD_FINISH_EVENT); + } + + if (status & FNAND_INTR_PGFINISH_MASK) + { + instance_p->irq_event_fun_p(instance_p->irq_args, FNAND_IRQ_PGFINISH_EVENT); + } + + if (status & FNAND_INTR_RE_MASK) + { + instance_p->irq_event_fun_p(instance_p->irq_args, FNAND_IRQ_RE_EVENT); + } + + if (status & FNAND_INTR_DQS_MASK) + { + instance_p->irq_event_fun_p(instance_p->irq_args, FNAND_IRQ_DQS_EVENT); + } + + if (status & FNAND_INTR_RB_MASK) + { + instance_p->irq_event_fun_p(instance_p->irq_args, FNAND_IRQ_RB_EVENT); + } + + if (status & FNAND_INTR_ECC_FINISH_MASK) + { + instance_p->irq_event_fun_p(instance_p->irq_args, FNAND_IRQ_ECC_FINISH_EVENT); + } + + if (status & FNAND_INTR_ECC_ERR_MASK) + { + FNAND_WRITEREG(config_p->base_address, FNAND_ERROR_CLEAR_OFFSET, FNAND_ERROR_CLEAR_ECC_ERR_CLR_MASK); + FNAND_WRITEREG(config_p->base_address, FNAND_FIFO_FREE_OFFSET, FNAND_FIFO_FREE_MASK); + instance_p->irq_event_fun_p(instance_p->irq_args, FNAND_IRQ_ECC_ERR_EVENT); + } + } +} diff --git a/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_option.c b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_option.c new file mode 100644 index 0000000000..285e17e946 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_option.c @@ -0,0 +1,59 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fnand_option.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:56:51 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#include "fnand.h" +#include "fnand_hw.h" + +/** + * @name: FNandSetOption + * @msg: + * @note: + * @param {FNand} *instance_p is the pointer to the FNand instance. + * @param {u32} options is configuration options ,use FNAND_OPS_INTER_MODE_SELECT to select nand flash interface + * @param {u32} value is set value + * @return {FError} FT_SUCCESS set option is ok ,FNAND_ERR_INVAILD_PARAMETER options is invalid + */ +FError FNandSetOption(FNand *instance_p, u32 options, u32 value) +{ + u32 reg_value; + FNandConfig *config_p; + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + config_p = &instance_p->config; + + switch (options) + { + case FNAND_OPS_INTER_MODE_SELECT: + FASSERT(FNAND_TOG_ASYN_DDR >= value) ; + FNAND_CLEARBIT(config_p->base_address, FNAND_CTRL0_OFFSET, FNAND_CTRL0_INTER_MODE(3UL)) ; + FNAND_SETBIT(config_p->base_address, FNAND_CTRL0_OFFSET, FNAND_CTRL0_INTER_MODE((unsigned long)value)) ; + break; + default: + return FNAND_ERR_INVAILD_PARAMETER; + } + + return FT_SUCCESS; +} + + diff --git a/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_sinit.c b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_sinit.c new file mode 100644 index 0000000000..f6e714ece0 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_sinit.c @@ -0,0 +1,51 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fnand_sinit.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:56:56 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#include "fnand.h" +#include "fparameters.h" + +extern FNandConfig FNandConfigTable[FNAND_NUM] ; + +/** + * @name: FNandLookupConfig + * @msg: This function looks up the device configuration based on the unique device ID. + * @param {u32} instance_id contains the ID of the device + * @return {FNandConfig *} - A pointer to the configuration found . - NULL if the specified device ID is not found + */ +FNandConfig *FNandLookupConfig(u32 instance_id) +{ + FNandConfig *ptr = NULL; + u32 index; + + for (index = 0; index < (u32)FNAND_NUM; index++) + { + if (FNandConfigTable[index].instance_id == instance_id) + { + ptr = &FNandConfigTable[index]; + break; + } + } + + return (FNandConfig *)ptr; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_timing.c b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_timing.c new file mode 100644 index 0000000000..d8c364485a --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_timing.c @@ -0,0 +1,467 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fnand_timing.c + * Date: 2022-05-09 14:53:42 + * LastEditTime: 2022-05-09 08:56:27 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ +#include "ferror_code.h" +#include "fnand.h" +#include "fnand_hw.h" + +#include "fdebug.h" +#define FNAND_TIMING_DEBUG_TAG "FNAND_TIMING" +#define FNAND_TIMING_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(FNAND_TIMING_DEBUG_TAG, format, ##__VA_ARGS__) +#define FNAND_TIMING_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(FNAND_TIMING_DEBUG_TAG, format, ##__VA_ARGS__) +#define FNAND_TIMING_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(FNAND_TIMING_DEBUG_TAG, format, ##__VA_ARGS__) + +#define FNAND_TIMING_ASY_NUM 12 +#define FNAND_TIMING_SYN_NUM 14 +#define FNAND_TIMING_TOG_NUM 12 + +const u16 fnand_timing_asy_mode0[FNAND_TIMING_ASY_NUM] = {0x03, 0x03, 0x28, 0x28, 0x03, 0x03, 0x06, 0x06, 0x28, 0x70, 0x30, 0x50}; +const u16 fnand_timing_asy_mode1[FNAND_TIMING_ASY_NUM] = {0x03, 0x03, 0x14, 0x14, 0x03, 0x03, 0x06, 0x06, 0x14, 0x70, 0x20, 0x28}; +const u16 fnand_timing_asy_mode2[FNAND_TIMING_ASY_NUM] = {0x03, 0x03, 0x0D, 0x0D, 0x03, 0x03, 0x06, 0x06, 0x0D, 0x70, 0x20, 0x1A}; +const u16 fnand_timing_asy_mode3[FNAND_TIMING_ASY_NUM] = {0x03, 0x03, 0x0A, 0x0A, 0x03, 0x03, 0x06, 0x06, 0x0A, 0x70, 0x20, 0x14}; +const u16 fnand_timing_asy_mode4[FNAND_TIMING_ASY_NUM] = {0x03, 0x03, 0x08, 0x08, 0x03, 0x03, 0x06, 0x06, 0x08, 0x70, 0x15, 0x10}; +const u16 fnand_timing_asy_mode5[FNAND_TIMING_ASY_NUM] = {0x03, 0x03, 0x07, 0x07, 0x03, 0x03, 0x06, 0x06, 0x07, 0x20, 0x15, 0x0E}; + +const u16 fnand_timing_syn_mode0[FNAND_TIMING_SYN_NUM] = {0x20, 0x41, 0x05, 0x20, 0x10, 0x19, 0x62, 0x40, 0x38, 0x20, 0x00, 0x09, 0x50, 0x20}; +const u16 fnand_timing_syn_mode1[FNAND_TIMING_SYN_NUM] = {0x18, 0x32, 0x06, 0x18, 0x0C, 0x10, 0x76, 0x40, 0x2A, 0x18, 0x00, 0x12, 0x24, 0x18}; +const u16 fnand_timing_syn_mode2[FNAND_TIMING_SYN_NUM] = {0x10, 0x0A, 0x04, 0x10, 0x08, 0x0A, 0x6E, 0x50, 0x1D, 0x10, 0x00, 0x0C, 0x18, 0x10}; +const u16 fnand_timing_syn_mode3[FNAND_TIMING_SYN_NUM] = {0x0C, 0x1A, 0x02, 0x0C, 0x06, 0x08, 0x78, 0x7C, 0x15, 0x0C, 0x00, 0x08, 0x12, 0x0C}; +const u16 fnand_timing_syn_mode4[FNAND_TIMING_SYN_NUM] = {0x08, 0x17, 0x05, 0x08, 0x04, 0x01, 0x73, 0x40, 0x0C, 0x08, 0x00, 0x06, 0x0C, 0x10}; + +const u16 fnand_timing_tog_ddr_mode0[FNAND_TIMING_TOG_NUM] = {0x14, 0x0a, 0x08, 0x08, 0xc8, 0xc8, 0x08, 0x08, 0x14, 0x0a, 0x14, 0x08}; /* 600M clk */ + + +#define ONFI_DYN_TIMING_MAX ((u16)~0U) + +static const struct FNandSdrTimings onfi_sdr_timings[] = +{ + { + .tCCS_min = 500000, + .tR_max = 200000000, + .tADL_min = 400000, + .tALH_min = 20000, + .tALS_min = 50000, + .tAR_min = 25000, + .tCEA_max = 100000, + .tCEH_min = 20000, + .tCH_min = 20000, + .tCHZ_max = 100000, + .tCLH_min = 20000, + .tCLR_min = 20000, + .tCLS_min = 50000, + .tCOH_min = 0, + .tCS_min = 70000, + .tDH_min = 20000, + .tDS_min = 40000, + .tFEAT_max = 1000000, + .tIR_min = 10000, + .tITC_max = 1000000, + .tRC_min = 100000, + .tREA_max = 40000, + .tREH_min = 30000, + .tRHOH_min = 0, + .tRHW_min = 200000, + .tRHZ_max = 200000, + .tRLOH_min = 0, + .tRP_min = 50000, + .tRR_min = 40000, + .tRST_max = 250000000000ULL, + .tWB_max = 200000, + .tWC_min = 100000, + .tWH_min = 30000, + .tWHR_min = 120000, + .tWP_min = 50000, + .tWW_min = 100000, + }, + + /* Mode 1 */ + { + .tCCS_min = 500000, + .tR_max = 200000000, + .tADL_min = 400000, + .tALH_min = 10000, + .tALS_min = 25000, + .tAR_min = 10000, + .tCEA_max = 45000, + .tCEH_min = 20000, + .tCH_min = 10000, + .tCHZ_max = 50000, + .tCLH_min = 10000, + .tCLR_min = 10000, + .tCLS_min = 25000, + .tCOH_min = 15000, + .tCS_min = 35000, + .tDH_min = 10000, + .tDS_min = 20000, + .tFEAT_max = 1000000, + .tIR_min = 0, + .tITC_max = 1000000, + .tRC_min = 50000, + .tREA_max = 30000, + .tREH_min = 15000, + .tRHOH_min = 15000, + .tRHW_min = 100000, + .tRHZ_max = 100000, + .tRLOH_min = 0, + .tRP_min = 25000, + .tRR_min = 20000, + .tRST_max = 500000000, + .tWB_max = 100000, + .tWC_min = 45000, + .tWH_min = 15000, + .tWHR_min = 80000, + .tWP_min = 25000, + .tWW_min = 100000, + }, + + /* Mode 2 */ + { + .tCCS_min = 500000, + .tR_max = 200000000, + .tADL_min = 400000, + .tALH_min = 10000, + .tALS_min = 15000, + .tAR_min = 10000, + .tCEA_max = 30000, + .tCEH_min = 20000, + .tCH_min = 10000, + .tCHZ_max = 50000, + .tCLH_min = 10000, + .tCLR_min = 10000, + .tCLS_min = 15000, + .tCOH_min = 15000, + .tCS_min = 25000, + .tDH_min = 5000, + .tDS_min = 15000, + .tFEAT_max = 1000000, + .tIR_min = 0, + .tITC_max = 1000000, + .tRC_min = 35000, + .tREA_max = 25000, + .tREH_min = 15000, + .tRHOH_min = 15000, + .tRHW_min = 100000, + .tRHZ_max = 100000, + .tRLOH_min = 0, + .tRR_min = 20000, + .tRST_max = 500000000, + .tWB_max = 100000, + .tRP_min = 17000, + .tWC_min = 35000, + .tWH_min = 15000, + .tWHR_min = 80000, + .tWP_min = 17000, + .tWW_min = 100000, + }, + + /* Mode 3 */ + { + .tCCS_min = 500000, + .tR_max = 200000000, + .tADL_min = 400000, + .tALH_min = 5000, + .tALS_min = 10000, + .tAR_min = 10000, + .tCEA_max = 25000, + .tCEH_min = 20000, + .tCH_min = 5000, + .tCHZ_max = 50000, + .tCLH_min = 5000, + .tCLR_min = 10000, + .tCLS_min = 10000, + .tCOH_min = 15000, + .tCS_min = 25000, + .tDH_min = 5000, + .tDS_min = 10000, + .tFEAT_max = 1000000, + .tIR_min = 0, + .tITC_max = 1000000, + .tRC_min = 30000, + .tREA_max = 20000, + .tREH_min = 10000, + .tRHOH_min = 15000, + .tRHW_min = 100000, + .tRHZ_max = 100000, + .tRLOH_min = 0, + .tRP_min = 15000, + .tRR_min = 20000, + .tRST_max = 500000000, + .tWB_max = 100000, + .tWC_min = 30000, + .tWH_min = 10000, + .tWHR_min = 80000, + .tWP_min = 15000, + .tWW_min = 100000, + }, + /* Mode 4 */ + { + .tCCS_min = 500000, + .tR_max = 200000000, + .tADL_min = 400000, + .tALH_min = 5000, + .tALS_min = 10000, + .tAR_min = 10000, + .tCEA_max = 25000, + .tCEH_min = 20000, + .tCH_min = 5000, + .tCHZ_max = 30000, + .tCLH_min = 5000, + .tCLR_min = 10000, + .tCLS_min = 10000, + .tCOH_min = 15000, + .tCS_min = 20000, + .tDH_min = 5000, + .tDS_min = 10000, + .tFEAT_max = 1000000, + .tIR_min = 0, + .tITC_max = 1000000, + .tRC_min = 25000, + .tREA_max = 20000, + .tREH_min = 10000, + .tRHOH_min = 15000, + .tRHW_min = 100000, + .tRHZ_max = 100000, + .tRLOH_min = 5000, + .tRP_min = 12000, + .tRR_min = 20000, + .tRST_max = 500000000, + .tWB_max = 100000, + .tWC_min = 25000, + .tWH_min = 10000, + .tWHR_min = 80000, + .tWP_min = 12000, + .tWW_min = 100000, + }, + /* Mode 5 */ + { + .tCCS_min = 500000, + .tR_max = 200000000, + .tADL_min = 400000, + .tALH_min = 5000, + .tALS_min = 10000, + .tAR_min = 10000, + .tCEA_max = 25000, + .tCEH_min = 20000, + .tCH_min = 5000, + .tCHZ_max = 30000, + .tCLH_min = 5000, + .tCLR_min = 10000, + .tCLS_min = 10000, + .tCOH_min = 15000, + .tCS_min = 15000, + .tDH_min = 5000, + .tDS_min = 7000, + .tFEAT_max = 1000000, + .tIR_min = 0, + .tITC_max = 1000000, + .tRC_min = 20000, + .tREA_max = 16000, + .tREH_min = 7000, + .tRHOH_min = 15000, + .tRHW_min = 100000, + .tRHZ_max = 100000, + .tRLOH_min = 5000, + .tRP_min = 10000, + .tRR_min = 20000, + .tRST_max = 500000000, + .tWB_max = 100000, + .tWC_min = 20000, + .tWH_min = 7000, + .tWHR_min = 80000, + .tWP_min = 10000, + .tWW_min = 100000, + }, +}; + +static FError FNandMemcpyToReg16(FNand *instance_p, u32 reg, u32 reg_step, const u16 *buf, u32 len) +{ + u32 i; + u32 value = 0; + + if (!instance_p || !buf) + { + FNAND_TIMING_DEBUG_E("instance_p is %p ,buf is %p", instance_p, buf); + return FNAND_ERR_INVAILD_PARAMETER; + } + + for (i = 0; i < len; i++) + { + value = (value << 16) + buf[i]; + if (i % 2) + { + FNAND_WRITEREG(instance_p->config.base_address, reg, value); + value = 0; + reg += reg_step; + } + } + + return FT_SUCCESS; +} + +/** + * @name: + * @msg: 根据inter_mode 与 timing_mode + * @note: + * @return {*} + * @param {FNand} *instance_p + */ +FError FNandTimingInterfaceUpdate(FNand *instance_p, u32 chip_addr) +{ + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + FASSERT(chip_addr < FNAND_CONNECT_MAX_NUM); + const u16 *target_timming_data = NULL; + FNandConfig *config_p = &instance_p->config ; + u32 value = 0 ; + FError ret; + + FNandSetOption(instance_p, FNAND_OPS_INTER_MODE_SELECT, instance_p->inter_mode[chip_addr]); + + FNAND_CLEARBIT(config_p->base_address, FNAND_CTRL1_OFFSET, FNAND_CTRL1_SAMPL_PHASE_MAKE(0xffffUL)); /* clear sampl_phase */ + switch (instance_p->inter_mode[chip_addr]) + { + case FNAND_ASYN_SDR: + if (FNAND_TIMING_MODE4 == (instance_p->timing_mode[chip_addr] & 0xf)) + { + target_timming_data = fnand_timing_asy_mode4; + value = FNAND_CTRL1_SAMPL_PHASE_MAKE(4UL) ; + } + else if (FNAND_TIMING_MODE3 == (instance_p->timing_mode[chip_addr] & 0xf)) + { + target_timming_data = fnand_timing_asy_mode3; + value = FNAND_CTRL1_SAMPL_PHASE_MAKE(5UL) ; + } + else if (FNAND_TIMING_MODE2 == (instance_p->timing_mode[chip_addr] & 0xf)) + { + target_timming_data = fnand_timing_asy_mode2; + value = FNAND_CTRL1_SAMPL_PHASE_MAKE(6UL) ; + } + else if (FNAND_TIMING_MODE1 == (instance_p->timing_mode[chip_addr] & 0xf)) + { + target_timming_data = fnand_timing_asy_mode1; + value = FNAND_CTRL1_SAMPL_PHASE_MAKE(5UL) ; + } + else + { + target_timming_data = fnand_timing_asy_mode0; + value = FNAND_CTRL1_SAMPL_PHASE_MAKE(1UL) ; + } + ret = FNandMemcpyToReg16(instance_p, FNAND_ASY_TIMING0_OFFSET, 4, target_timming_data, FNAND_TIMING_ASY_NUM); + if (ret != FT_SUCCESS) + { + return ret; + } + FNAND_SETBIT(config_p->base_address, FNAND_CTRL1_OFFSET, value); + FNAND_WRITEREG(config_p->base_address, FNAND_INTERVAL_OFFSET, 1); + break; + case FNAND_ONFI_DDR: + if (FNAND_TIMING_MODE4 == (instance_p->timing_mode[chip_addr] & 0xf)) + { + FNAND_WRITEREG(config_p->base_address, FNAND_INTERVAL_OFFSET, 0x30); + target_timming_data = fnand_timing_syn_mode4; + value = FNAND_CTRL1_SAMPL_PHASE_MAKE(0xdUL) ; + } + else if (FNAND_TIMING_MODE3 == (instance_p->timing_mode[chip_addr] & 0xf)) + { + FNAND_WRITEREG(config_p->base_address, FNAND_INTERVAL_OFFSET, 0x18); + target_timming_data = fnand_timing_syn_mode3; + value = FNAND_CTRL1_SAMPL_PHASE_MAKE(5UL) ; + } + else if (FNAND_TIMING_MODE2 == (instance_p->timing_mode[chip_addr] & 0xf)) + { + FNAND_WRITEREG(config_p->base_address, FNAND_INTERVAL_OFFSET, 0x20); + target_timming_data = fnand_timing_syn_mode2; + value = FNAND_CTRL1_SAMPL_PHASE_MAKE(0x8UL) ; + } + else if (FNAND_TIMING_MODE1 == (instance_p->timing_mode[chip_addr] & 0xf)) + { + FNAND_WRITEREG(config_p->base_address, FNAND_INTERVAL_OFFSET, 0x40); + target_timming_data = fnand_timing_syn_mode1; + value = FNAND_CTRL1_SAMPL_PHASE_MAKE(0x12UL) ; + } + else + { + FNAND_WRITEREG(config_p->base_address, FNAND_INTERVAL_OFFSET, 0x40); + target_timming_data = fnand_timing_syn_mode0; + value = FNAND_CTRL1_SAMPL_PHASE_MAKE(0x12UL) ; + } + ret = FNandMemcpyToReg16(instance_p, FNAND_SYN_TIMING6_OFFSET, 4, target_timming_data, FNAND_TIMING_SYN_NUM); + if (ret != FT_SUCCESS) + { + return ret; + } + FNAND_SETBIT(config_p->base_address, FNAND_CTRL1_OFFSET, value); + break; + case FNAND_TOG_ASYN_DDR: + value = FNAND_CTRL1_SAMPL_PHASE_MAKE(8UL); + target_timming_data = fnand_timing_tog_ddr_mode0; + ret = FNandMemcpyToReg16(instance_p, FNAND_TOG_TIMING13_OFFSET, 4, target_timming_data, FNAND_TIMING_SYN_NUM); + if (ret != FT_SUCCESS) + { + return ret; + } + FNAND_WRITEREG(config_p->base_address, FNAND_INTERVAL_OFFSET, 0xc8); + FNAND_SETBIT(config_p->base_address, FNAND_CTRL1_OFFSET, value); + break; + default: + FNAND_TIMING_DEBUG_E("Error inter_mode is %x", instance_p->inter_mode[chip_addr]); + return FNAND_ERR_INVAILD_PARAMETER; + } + + return FT_SUCCESS; +} + + +const struct FNandSdrTimings *FNandAsyncTimingModeToSdrTimings(FNandAsyncTimint mode) +{ + if (mode >= FNAND_ASYNC_TIM_INT_MODE4) + { + FNAND_TIMING_DEBUG_E("FNandAsyncTimingModeToSdrTimings set is over mode range"); + return NULL; + } + + return &onfi_sdr_timings[mode]; +} + + + +/** + * @name: + * @msg: + * @return {*} + * @param {FNand} *instance_p + * @param {FNandAsyncTimint} mode + * @Note 当前只支持onfi 模式 + */ +FError FNandFillTimingModeTiming(FNand *instance_p, FNandAsyncTimint mode) +{ + struct FNandSdrTimings *sdr_timing_p = NULL; + const struct FNandSdrTimings *source_timing_p = NULL; + FASSERT(instance_p != NULL); + sdr_timing_p = &instance_p->sdr_timing; + source_timing_p = FNandAsyncTimingModeToSdrTimings(mode); + FASSERT(source_timing_p != NULL); + *sdr_timing_p = *source_timing_p; + + + return FT_SUCCESS; +} diff --git a/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_timing.h b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_timing.h new file mode 100644 index 0000000000..8289e1cc32 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/nand/fnand/fnand_timing.h @@ -0,0 +1,124 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fnand_timing.h + * Date: 2022-04-28 18:53:58 + * LastEditTime: 2022-04-28 18:53:58 + * Description:  This file is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ +#ifndef DRIVERS_NAND_FNAND +#define DRIVERS_NAND_FNAND + +#include "ftypes.h" + +/** + * struct FNandSdrTimings - SDR NAND chip timings + * + * This struct defines the timing requirements of a SDR NAND chip. + * These information can be found in every NAND datasheets and the timings + * meaning are described in the ONFI specifications: + * www.onfi.org/~/media/ONFI/specs/onfi_3_1_spec.pdf (chapter 4.15 Timing + * Parameters) + * + * All these timings are expressed in picoseconds. + * + * @tBERS_max: Block erase time + * @tCCS_min: Change column setup time + * @tPROG_max: Page program time + * @tR_max: Page read time + * @tALH_min: ALE hold time + * @tADL_min: ALE to data loading time + * @tALS_min: ALE setup time + * @tAR_min: ALE to RE# delay + * @tCEA_max: CE# access time + * @tCEH_min: CE# high hold time + * @tCH_min: CE# hold time + * @tCHZ_max: CE# high to output hi-Z + * @tCLH_min: CLE hold time + * @tCLR_min: CLE to RE# delay + * @tCLS_min: CLE setup time + * @tCOH_min: CE# high to output hold + * @tCS_min: CE# setup time + * @tDH_min: Data hold time + * @tDS_min: Data setup time + * @tFEAT_max: Busy time for Set Features and Get Features + * @tIR_min: Output hi-Z to RE# low + * @tITC_max: Interface and Timing Mode Change time + * @tRC_min: RE# cycle time + * @tREA_max: RE# access time + * @tREH_min: RE# high hold time + * @tRHOH_min: RE# high to output hold + * @tRHW_min: RE# high to WE# low + * @tRHZ_max: RE# high to output hi-Z + * @tRLOH_min: RE# low to output hold + * @tRP_min: RE# pulse width + * @tRR_min: Ready to RE# low (data only) + * @tRST_max: Device reset time, measured from the falling edge of R/B# to the + * rising edge of R/B#. + * @tWB_max: WE# high to SR[6] low + * @tWC_min: WE# cycle time + * @tWH_min: WE# high hold time + * @tWHR_min: WE# high to RE# low + * @tWP_min: WE# pulse width + * @tWW_min: WP# transition to WE# low + */ +struct FNandSdrTimings +{ + u64 tBERS_max; + u32 tCCS_min; + u64 tPROG_max; + u64 tR_max; + u32 tALH_min; + u32 tADL_min; + u32 tALS_min; + u32 tAR_min; + u32 tCEA_max; + u32 tCEH_min; + u32 tCH_min; + u32 tCHZ_max; + u32 tCLH_min; + u32 tCLR_min; + u32 tCLS_min; + u32 tCOH_min; + u32 tCS_min; + u32 tDH_min; + u32 tDS_min; + u32 tFEAT_max; + u32 tIR_min; + u32 tITC_max; + u32 tRC_min; + u32 tREA_max; + u32 tREH_min; + u32 tRHOH_min; + u32 tRHW_min; + u32 tRHZ_max; + u32 tRLOH_min; + u32 tRP_min; + u32 tRR_min; + u64 tRST_max; + u32 tWB_max; + u32 tWC_min; + u32 tWH_min; + u32 tWHR_min; + u32 tWP_min; + u32 tWW_min; +}; + + + +#endif + diff --git a/bsp/phytium/libraries/standalone/drivers/nand/fnand/manufacturer/fnand_onfi.c b/bsp/phytium/libraries/standalone/drivers/nand/fnand/manufacturer/fnand_onfi.c new file mode 100644 index 0000000000..4ba74b30e7 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/nand/fnand/manufacturer/fnand_onfi.c @@ -0,0 +1,284 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fnand_onfi.c + * Date: 2022-07-05 19:10:40 + * LastEditTime: 2022-07-05 19:10:41 + * Description: This file is for + * + * Modify History: + * Ver Who Date Changes + * ----- ------ -------- -------------------------------------- + */ + + +#include "fnand.h" +#include "fnand_hw.h" +#include "stdio.h" +#include "string.h" +#include "fnand_dma.h" +#include "fnand_common_cmd.h" +#include "fnand_onfi.h" +#include "fnand_timing.h" +#include "fnand_ecc.h" +#include "fcache.h" +// #include "fsleep.h" +#include "fdebug.h" +#include "sdkconfig.h" + + +#define FNAND_ONFI_DEBUG_TAG "FNAND_ONFI" + +#ifdef CONFIG_FNAND_ONFI_DEBUG_EN + + #define FNAND_ONFI_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(FNAND_ONFI_DEBUG_TAG, format, ##__VA_ARGS__) + #define FNAND_ONFI_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(FNAND_ONFI_DEBUG_TAG, format, ##__VA_ARGS__) + #define FNAND_ONFI_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(FNAND_ONFI_DEBUG_TAG, format, ##__VA_ARGS__) + #define FNAND_ONFI_DEBUG_D(format, ...) FT_DEBUG_PRINT_D(FNAND_ONFI_DEBUG_TAG, format, ##__VA_ARGS__) +#else + #define FNAND_ONFI_DEBUG_I(format, ...) + #define FNAND_ONFI_DEBUG_W(format, ...) + #define FNAND_ONFI_DEBUG_E(format, ...) + #define FNAND_ONFI_DEBUG_D(format, ...) +#endif + +#define FNAND_ADDR_CYCLE_NUM0 0 +#define FNAND_ADDR_CYCLE_NUM1 1 +#define FNAND_ADDR_CYCLE_NUM2 2 +#define FNAND_ADDR_CYCLE_NUM3 3 +#define FNAND_ADDR_CYCLE_NUM4 4 +#define FNAND_ADDR_CYCLE_NUM5 5 + +#define FNAND_ONFI_CRC_BASE 0x4F4E + +#define FNAND_CTRL_ECC_EN 1 +#define FNAND_CTRL_ECC_DIS 0 + +#define FNAND_CTRL_AUTO_AUTO_RS_EN 1 +#define FNAND_CTRL_AUTO_AUTO_RS_DIS 0 + + + +/* + * Special handling must be done for the WAITRDY timeout parameter as it usually + * is either tPROG (after a prog), tR (before a read), tRST (during a reset) or + * tBERS (during an erase) which all of them are u64 values that cannot be + * divided by usual kernel macros and must be handled with the special + * DIV_ROUND_UP_ULL() macro. + * + * Cast to type of dividend is needed here to guarantee that the result won't + * be an unsigned long long when the dividend is an unsigned long (or smaller), + * which is what the compiler does when it sees ternary operator with 2 + * different return types (picks the largest type to make sure there's no + * loss). + */ +#define __DIVIDE(dividend, divisor) ({ \ + (__typeof__(dividend))(sizeof(dividend) <= sizeof(unsigned long) ? \ + DIV_ROUND_UP(dividend, divisor) : \ + DIV_ROUND_UP_ULL(dividend, divisor)); \ + }) +#define PSEC_TO_NSEC(x) __DIVIDE(x, 1000) +#define PSEC_TO_MSEC(x) __DIVIDE(x, 1000000000) + + +extern FError FNandSendCmd(FNand *instance_p, struct FNandDmaDescriptor *descriptor_p, FNandOperationType isr_type); +extern FError FNandTimingInterfaceUpdate(FNand *instance_p, u32 chip_addr); + +FError FNandDmaPack(FNandCmdFormat *cmd_format, + struct FNandDmaDescriptor *descriptor_p, + FNandDmaPackData *pack_data_p + ); + + + +enum CommandsEnum +{ + CMD_READ_ID = 0, + CMD_READ_DEVICE_TABLE, + CMD_INDEX_LENGTH_NEW, +}; + +static u16 FNandOnfiCrc16(u16 crc, u8 const *p, size_t len) +{ + int i; + while (len--) + { + crc ^= *p++ << 8; + for (i = 0; i < 8; i++) + crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0); + } + + return crc; +} + + + +/* Sanitize ONFI strings so we can safely print them */ +static void FNandSanitizeString(u8 *s, fsize_t len) +{ + fsize_t i; + + /* Null terminate */ + s[len - 1] = 0; + + /* Remove non printable chars */ + for (i = 0; i < len - 1; i++) + { + if (s[i] < ' ' || s[i] > 127) + s[i] = '?'; + } + +} + + + +static FNandCmdFormat cmd_format[CMD_INDEX_LENGTH_NEW] = +{ + {ONFI_CMD_READ_ID, ONFI_END_CMD_NONE, FNAND_ADDR_CYCLE_NUM1, FNAND_CMDCTRL_TYPE_READ_ID, FNAND_CTRL_ECC_DIS, FNAND_CTRL_AUTO_AUTO_RS_DIS}, + {ONFI_CMD_READ_PARAM_PAGE, ONFI_END_CMD_NONE, FNAND_ADDR_CYCLE_NUM1, FNAND_CMDCTRL_READ_PARAM, FNAND_CTRL_ECC_DIS, FNAND_CTRL_AUTO_AUTO_RS_EN}, +}; + + +static FError FNandOnfiDetectJedec(FNand *instance_p, struct OnfiNandGeometry *onfi_geometry_p, FNandNandGeometry *geometry_p) +{ + /* 多参数页冗余检查 */ + if (FNandOnfiCrc16(FNAND_ONFI_CRC_BASE, (u8 *)onfi_geometry_p, 510) != onfi_geometry_p->crc) + { + FNAND_ONFI_DEBUG_W("Onfi error mode"); + } + + FNAND_ONFI_DEBUG_I("revision is %x", onfi_geometry_p->revision); + + FNandSanitizeString(onfi_geometry_p->manufacturer, sizeof(onfi_geometry_p->manufacturer)); + FNandSanitizeString(onfi_geometry_p->model, sizeof(onfi_geometry_p->model)); + FNAND_ONFI_DEBUG_I("manufacturer %s", onfi_geometry_p->manufacturer); + FNAND_ONFI_DEBUG_I("model %s", onfi_geometry_p->model); + + geometry_p->bytes_per_page = onfi_geometry_p->byte_per_page; + geometry_p->spare_bytes_per_page = onfi_geometry_p->spare_bytes_per_page; + geometry_p->pages_per_block = onfi_geometry_p->pages_per_block; + geometry_p->blocks_per_lun = onfi_geometry_p->blocks_per_lun ; + geometry_p->num_lun = onfi_geometry_p->lun_count; + geometry_p->num_pages = (geometry_p->num_lun * + geometry_p->blocks_per_lun * + geometry_p->pages_per_block); + geometry_p->num_blocks = (geometry_p->num_lun * geometry_p->blocks_per_lun); + geometry_p->block_size = (geometry_p->pages_per_block * geometry_p->bytes_per_page); + geometry_p->device_size = (geometry_p->num_blocks * geometry_p->block_size * geometry_p->bytes_per_page); + geometry_p->rowaddr_cycles = onfi_geometry_p->addr_cycles & 0xf; + geometry_p->coladdr_cycles = (onfi_geometry_p->addr_cycles >> 4) & 0xf ; + geometry_p->hw_ecc_length = FNandGetEccTotalLength(geometry_p->bytes_per_page, instance_p->config.ecc_strength); /* 需要增加检查oob 长度 */ + geometry_p->ecc_offset = geometry_p->spare_bytes_per_page - geometry_p->hw_ecc_length; + geometry_p->hw_ecc_steps = geometry_p->bytes_per_page / instance_p->config.ecc_step_size ; + geometry_p->ecc_step_size = instance_p->config.ecc_step_size; + FNAND_ONFI_DEBUG_D("bytes_per_page %d ", geometry_p->bytes_per_page); /* Bytes per page */ + FNAND_ONFI_DEBUG_D("spare_bytes_per_page %d ", geometry_p->spare_bytes_per_page) ; /* Size of spare area in bytes */ + FNAND_ONFI_DEBUG_D("pages_per_block %d ", geometry_p->pages_per_block) ; /* Pages per block */ + FNAND_ONFI_DEBUG_D("blocks_per_lun %d ", geometry_p->blocks_per_lun) ; /* Bocks per LUN */ + FNAND_ONFI_DEBUG_D("num_lun %d ", geometry_p->num_lun) ; /* Total number of LUN */ + FNAND_ONFI_DEBUG_D("num_pages %d ", geometry_p->num_pages) ; /* Total number of pages in device */ + FNAND_ONFI_DEBUG_D("num_blocks %d ", geometry_p->num_blocks) ; /* Total number of blocks in device */ + FNAND_ONFI_DEBUG_D("block_size %d ", geometry_p->block_size) ; /* Size of a block in bytes */ + FNAND_ONFI_DEBUG_D("device_size %d ", geometry_p->device_size) ; /* Total device size in bytes */ + FNAND_ONFI_DEBUG_D("rowaddr_cycles %d ", geometry_p->rowaddr_cycles) ; /* Row address cycles */ + FNAND_ONFI_DEBUG_D("coladdr_cycles %d ", geometry_p->coladdr_cycles) ; /* Column address cycles */ + FNAND_ONFI_DEBUG_D("hw_ecc_length %d ", geometry_p->hw_ecc_length) ; /* 产生硬件ecc校验参数的个数 */ + FNAND_ONFI_DEBUG_D("ecc_offset %d ", geometry_p->ecc_offset) ; /* obb存放硬件ecc校验参数页位置的偏移 */ + FNAND_ONFI_DEBUG_D("hw_ecc_steps %d ", geometry_p->hw_ecc_steps) ; /* number of ECC steps per page */ + FNAND_ONFI_DEBUG_D("ecc_step_size %d ", geometry_p->ecc_step_size) ; /* 进行读写操作时,单次ecc 的步骤的跨度 */ + + return FT_SUCCESS; +} + +static FError FNandOnfiReadParamPage(FNand *instance_p, u8 *id_buffer, u32 buffer_length, u32 chip_addr) +{ + FError ret; + u8 address = 0x00; + u32 memcpy_length; + FNandDmaPackData pack_data = + { + .addr_p = &address, + .addr_length = 1, + .phy_address = (uintptr)instance_p->dma_data_buffer.data_buffer, + .phy_bytes_length = (3 * sizeof(struct OnfiNandGeometry) > FNAND_DMA_MAX_LENGTH) ? FNAND_DMA_MAX_LENGTH : (3 * sizeof(struct OnfiNandGeometry)), + .chip_addr = chip_addr, + .contiune_dma = 0, + }; + + FNandDmaPack(&cmd_format[CMD_READ_DEVICE_TABLE], (struct FNandDmaDescriptor *)&instance_p->descriptor_buffer.data_buffer[0], &pack_data); + ret = FNandSendCmd(instance_p, (struct FNandDmaDescriptor *)&instance_p->descriptor_buffer.data_buffer[0], FNAND_READ_PAGE_TYPE); + + if (ret != FT_SUCCESS) + { + return FNAND_ERR_OPERATION; + } + + if (buffer_length && id_buffer) + { + memcpy_length = (buffer_length > pack_data.phy_bytes_length) ? pack_data.phy_bytes_length : buffer_length; + FCacheDCacheFlushRange((intptr)instance_p->dma_data_buffer.data_buffer, memcpy_length); + memcpy(id_buffer, instance_p->dma_data_buffer.data_buffer, memcpy_length); + } + return FT_SUCCESS; +} + +/** + * @name: FNandOnfiInit + * @msg: Onfi mode interface initialization + * @note: + * @param {FNand} *instance_p is the pointer to the FNand instance. + * @param {u32} chip_addr is chip address + * @return {FError} FT_SUCCESS 初始化成功 ,FNAND_NOT_FET_TOGGLE_MODE 初始化toggle 模式错误。 + */ +FError FNandOnfiInit(FNand *instance_p, u32 chip_addr) +{ + FError ret; + char id[5]; + FASSERT(instance_p != NULL); + struct OnfiNandGeometry *onfi_geometry_p; + /* step 1 .reset nand chip */ + + ret = FNandFlashReset(instance_p, chip_addr); + if (ret != FT_SUCCESS) + { + FNAND_ONFI_DEBUG_E("FNandFlashReset is error"); + return ret; + } + /* step 2. readid operation 20h */ + ret = FNandFlashReadId(instance_p, 0x20, id, sizeof(id), chip_addr); + if (ret != FT_SUCCESS || strncmp(id, "ONFI", sizeof(id) - 1)) + { + FNAND_ONFI_DEBUG_E("20H read id is %s ", id); + return FNAND_NOT_FET_TOGGLE_MODE; + } + + instance_p->inter_mode[chip_addr] = FNAND_ASYN_SDR; + + + FNandTimingInterfaceUpdate(instance_p, chip_addr); + /* step 3. read device id table */ + + ret = FNandOnfiReadParamPage(instance_p, NULL, 0, chip_addr); + if (ret != FT_SUCCESS) + { + FNAND_ONFI_DEBUG_E("read device id table is error"); + return FNAND_NOT_FET_TOGGLE_MODE; + } + + /* step 4. device id table parse */ + onfi_geometry_p = (struct OnfiNandGeometry *)instance_p->dma_data_buffer.data_buffer; + + return FNandOnfiDetectJedec(instance_p, onfi_geometry_p, &instance_p->nand_geometry[chip_addr]); +} + + diff --git a/bsp/phytium/libraries/standalone/drivers/nand/fnand/manufacturer/fnand_onfi.h b/bsp/phytium/libraries/standalone/drivers/nand/fnand/manufacturer/fnand_onfi.h new file mode 100644 index 0000000000..aab8892209 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/nand/fnand/manufacturer/fnand_onfi.h @@ -0,0 +1,107 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fnand_onfi.h + * Date: 2022-07-05 19:10:47 + * LastEditTime: 2022-07-05 19:10:47 + * Description: This file is for + * + * Modify History: + * Ver Who Date Changes + * ----- ------ -------- -------------------------------------- + */ +#ifndef DRIVERS_NAND_FNAND_ONFI_H +#define DRIVERS_NAND_FNAND_ONFI_H + +#include "ftypes.h" +#include "fnand.h" + + +#define ONFI_CMD_READ_ID 0x90 /* ONFI Read ID \ + command */ +#define ONFI_CMD_READ_PARAM_PAGE 0xEC /* ONFI Read \ + Parameter Page \ + command */ + + +#define ONFI_END_CMD_NONE 0xfff /* No End command */ + +struct OnfiNandGeometry +{ + /* rev info and features block */ + /* 'O' 'N' 'F' 'I' */ + u8 sig[4]; + u16 revision; + u16 features; + u16 opt_cmd; + u8 reserved0[2]; + u16 ext_param_page_length; /* since ONFI 2.1 */ + u8 num_of_param_pages; /* since ONFI 2.1 */ + u8 reserved1[17]; + + /* manufacturer information block */ + char manufacturer[12]; + char model[20]; + u8 jedec_id; + u16 date_code; + u8 reserved2[13]; + + /* memory organization block */ + u32 byte_per_page; + u16 spare_bytes_per_page; + u32 data_bytes_per_ppage; + u16 spare_bytes_per_ppage; + u32 pages_per_block; + u32 blocks_per_lun; + u8 lun_count; + u8 addr_cycles; + u8 bits_per_cell; + u16 bb_per_lun; + u16 block_endurance; + u8 guaranteed_good_blocks; + u16 guaranteed_block_endurance; + u8 programs_per_page; + u8 ppage_attr; + u8 ecc_bits; + u8 interleaved_bits; + u8 interleaved_ops; + u8 reserved3[13]; + + /* electrical parameter block */ + u8 io_pin_capacitance_max; + u16 async_timing_mode; + u16 program_cache_timing_mode; + u16 t_prog; + u16 t_bers; + u16 t_r; + u16 t_ccs; + u16 src_sync_timing_mode; + u8 src_ssync_features; + u16 clk_pin_capacitance_typ; + u16 io_pin_capacitance_typ; + u16 input_pin_capacitance_typ; + u8 input_pin_capacitance_max; + u8 driver_strength_support; + u16 t_int_r; + u16 t_adl; + u8 reserved4[8]; + + /* vendor */ + u16 vendor_revision; + u8 vendor[88]; + + u16 crc; +} __attribute__((__packed__)); + + +#endif // !1 \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/nand/fnand/manufacturer/fnand_toggle.c b/bsp/phytium/libraries/standalone/drivers/nand/fnand/manufacturer/fnand_toggle.c new file mode 100644 index 0000000000..af1913f036 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/nand/fnand/manufacturer/fnand_toggle.c @@ -0,0 +1,294 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fnand_toggle.c + * Date: 2022-07-05 20:00:31 + * LastEditTime: 2022-07-05 20:00:31 + * Description: This file is for + * + * Modify History: + * Ver Who Date Changes + * ----- ------ -------- -------------------------------------- + */ + +#include "fnand.h" +#include "fnand_hw.h" +#include "stdio.h" +#include "string.h" +#include "fnand_dma.h" +#include "fnand_toggle.h" +#include "fnand_timing.h" +#include "fnand_ecc.h" +#include "fnand_common_cmd.h" +#include "fcache.h" +// #include "fsleep.h" +#include "fdebug.h" +#include "sdkconfig.h" + +#define CONFIG_FNAND_TOGGLE_DEBUG_EN +#define FNAND_TOGGLE_DEBUG_TAG "FNAND_TOGGLE" +#ifdef CONFIG_FNAND_TOGGLE_DEBUG_EN + + #define FNAND_TOGGLE_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(FNAND_TOGGLE_DEBUG_TAG, format, ##__VA_ARGS__) + #define FNAND_TOGGLE_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(FNAND_TOGGLE_DEBUG_TAG, format, ##__VA_ARGS__) + #define FNAND_TOGGLE_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(FNAND_TOGGLE_DEBUG_TAG, format, ##__VA_ARGS__) + #define FNAND_TOGGLE_DEBUG_D(format, ...) FT_DEBUG_PRINT_D(FNAND_TOGGLE_DEBUG_TAG, format, ##__VA_ARGS__) +#else + #define FNAND_TOGGLE_DEBUG_I(format, ...) + #define FNAND_TOGGLE_DEBUG_W(format, ...) + #define FNAND_TOGGLE_DEBUG_E(format, ...) + #define FNAND_TOGGLE_DEBUG_D(format, ...) +#endif + +#define FNAND_ADDR_CYCLE_NUM0 0 +#define FNAND_ADDR_CYCLE_NUM1 1 +#define FNAND_ADDR_CYCLE_NUM2 2 +#define FNAND_ADDR_CYCLE_NUM3 3 +#define FNAND_ADDR_CYCLE_NUM4 4 +#define FNAND_ADDR_CYCLE_NUM5 5 + +#define FNAND_TOGGLE_CRC_BASE 0x4F4E + +#define FNAND_CTRL_ECC_EN 1 +#define FNAND_CTRL_ECC_DIS 0 + +#define FNAND_CTRL_AUTO_AUTO_RS_EN 1 +#define FNAND_CTRL_AUTO_AUTO_RS_DIS 0 + +/* + * Special handling must be done for the WAITRDY timeout parameter as it usually + * is either tPROG (after a prog), tR (before a read), tRST (during a reset) or + * tBERS (during an erase) which all of them are u64 values that cannot be + * divided by usual kernel macros and must be handled with the special + * DIV_ROUND_UP_ULL() macro. + * + * Cast to type of dividend is needed here to guarantee that the result won't + * be an unsigned long long when the dividend is an unsigned long (or smaller), + * which is what the compiler does when it sees ternary operator with 2 + * different return types (picks the largest type to make sure there's no + * loss). + */ +#define __DIVIDE(dividend, divisor) ({ \ + (__typeof__(dividend))(sizeof(dividend) <= sizeof(unsigned long) ? \ + DIV_ROUND_UP(dividend, divisor) : \ + DIV_ROUND_UP_ULL(dividend, divisor)); \ + }) +#define PSEC_TO_NSEC(x) __DIVIDE(x, 1000) +#define PSEC_TO_MSEC(x) __DIVIDE(x, 1000000000) + +extern FError FNandSendCmd(FNand *instance_p, struct FNandDmaDescriptor *descriptor_p, FNandOperationType isr_type); +extern FError FNandTimingInterfaceUpdate(FNand *instance_p, u32 chip_addr); + +FError FNandDmaPack(FNandCmdFormat *cmd_format, + struct FNandDmaDescriptor *descriptor_p, + FNandDmaPackData *pack_data_p + ); + + + + + +enum CommandsEnumNew +{ + CMD_READ_ID = 0, + CMD_READ_DEVICE_TABLE, + CMD_INDEX_LENGTH_NEW, +}; + + +static FNandCmdFormat cmd_format[CMD_INDEX_LENGTH_NEW] = +{ + {TOGGLE_CMD_READ_ID, TOGGLE_END_CMD_NONE, FNAND_ADDR_CYCLE_NUM1, FNAND_CMDCTRL_TYPE_READ_ID, FNAND_CTRL_ECC_DIS, FNAND_CTRL_AUTO_AUTO_RS_DIS}, + {TOGGLE_CMD_READ_PARAM_PAGE, TOGGLE_END_CMD_NONE, FNAND_ADDR_CYCLE_NUM1, FNAND_CMDCTRL_READ_PARAM, FNAND_CTRL_ECC_DIS, FNAND_CTRL_AUTO_AUTO_RS_EN}, +}; + +static u16 FNandToggleCrc16(u16 crc, u8 const *p, size_t len) +{ + int i; + while (len--) + { + crc ^= *p++ << 8; + for (i = 0; i < 8; i++) + crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0); + } + + return crc; +} + + + +/* Sanitize ONFI strings so we can safely print them */ +static void FNandSanitizeString(u8 *s, fsize_t len) +{ + fsize_t i; + + /* Null terminate */ + s[len - 1] = 0; + + /* Remove non printable chars */ + for (i = 0; i < len - 1; i++) + { + if (s[i] < ' ' || s[i] > 127) + s[i] = '?'; + } + +} + + +static FError FNandToggleReadParamPage(FNand *instance_p, u8 *id_buffer, u32 buffer_length, u32 chip_addr) +{ + FError ret; + u8 address = 0x40; + u32 memcpy_length; + FNandDmaPackData pack_data = + { + .addr_p = &address, + .addr_length = 1, + .phy_address = (uintptr)instance_p->dma_data_buffer.data_buffer, + .phy_bytes_length = (3 * sizeof(struct ToggleNandGeometry) > FNAND_DMA_MAX_LENGTH) ? FNAND_DMA_MAX_LENGTH : (3 * sizeof(struct ToggleNandGeometry)), + .chip_addr = chip_addr, + .contiune_dma = 0, + }; + + FNandDmaPack(&cmd_format[CMD_READ_DEVICE_TABLE], (struct FNandDmaDescriptor *)&instance_p->descriptor_buffer.data_buffer[0], &pack_data); + ret = FNandSendCmd(instance_p, (struct FNandDmaDescriptor *)&instance_p->descriptor_buffer.data_buffer[0], FNAND_READ_PAGE_TYPE); + + + if (ret != FT_SUCCESS) + { + return FNAND_ERR_OPERATION; + } + + if (buffer_length && id_buffer) + { + memcpy_length = (buffer_length > pack_data.phy_bytes_length) ? pack_data.phy_bytes_length : buffer_length; + FCacheDCacheFlushRange((intptr)instance_p->dma_data_buffer.data_buffer, memcpy_length); + memcpy(id_buffer, instance_p->dma_data_buffer.data_buffer, memcpy_length); + } + + return FT_SUCCESS; +} + +static FError FNandToggleDetectJedec(FNand *instance_p, struct ToggleNandGeometry *toggle_geometry_p, FNandNandGeometry *geometry_p) +{ + /* 检查crc */ + if (FNandToggleCrc16(FNAND_TOGGLE_CRC_BASE, (u8 *)toggle_geometry_p, 510) != toggle_geometry_p->crc) + { + FNAND_TOGGLE_DEBUG_E("Toggle error mode"); + } + + FNAND_TOGGLE_DEBUG_I("revision is %x", toggle_geometry_p->revision); + + FNandSanitizeString(toggle_geometry_p->manufacturer, sizeof(toggle_geometry_p->manufacturer)); + FNandSanitizeString(toggle_geometry_p->model, sizeof(toggle_geometry_p->model)); + FNAND_TOGGLE_DEBUG_I("manufacturer %s", toggle_geometry_p->manufacturer); + FNAND_TOGGLE_DEBUG_I("model %s", toggle_geometry_p->model); + + geometry_p->bytes_per_page = toggle_geometry_p->byte_per_page; + geometry_p->spare_bytes_per_page = toggle_geometry_p->spare_bytes_per_page; + geometry_p->pages_per_block = toggle_geometry_p->pages_per_block; + geometry_p->blocks_per_lun = toggle_geometry_p->blocks_per_lun ; + geometry_p->num_lun = toggle_geometry_p->lun_count; + geometry_p->num_pages = (geometry_p->num_lun * + geometry_p->blocks_per_lun * + geometry_p->pages_per_block); + geometry_p->num_blocks = (geometry_p->num_lun * geometry_p->blocks_per_lun); + geometry_p->block_size = (geometry_p->pages_per_block * geometry_p->bytes_per_page); + geometry_p->device_size = (geometry_p->num_blocks * geometry_p->block_size * geometry_p->bytes_per_page); + geometry_p->rowaddr_cycles = toggle_geometry_p->addr_cycles & 0xf; + geometry_p->coladdr_cycles = (toggle_geometry_p->addr_cycles >> 4) & 0xf ; + geometry_p->hw_ecc_length = FNandGetEccTotalLength(geometry_p->bytes_per_page, instance_p->config.ecc_strength); + geometry_p->ecc_offset = geometry_p->spare_bytes_per_page - geometry_p->hw_ecc_length; + geometry_p->hw_ecc_steps = geometry_p->bytes_per_page / instance_p->config.ecc_step_size ; + geometry_p->ecc_step_size = instance_p->config.ecc_step_size; + FNAND_TOGGLE_DEBUG_D("bytes_per_page %d ", geometry_p->bytes_per_page); /* Bytes per page */ + FNAND_TOGGLE_DEBUG_D("spare_bytes_per_page %d ", geometry_p->spare_bytes_per_page) ; /* Size of spare area in bytes */ + FNAND_TOGGLE_DEBUG_D("pages_per_block %d ", geometry_p->pages_per_block) ; /* Pages per block */ + FNAND_TOGGLE_DEBUG_D("blocks_per_lun %d ", geometry_p->blocks_per_lun) ; /* Bocks per LUN */ + FNAND_TOGGLE_DEBUG_D("num_lun %d ", geometry_p->num_lun) ; /* Total number of LUN */ + FNAND_TOGGLE_DEBUG_D("num_pages %d ", geometry_p->num_pages) ; /* Total number of pages in device */ + FNAND_TOGGLE_DEBUG_D("num_blocks %d ", geometry_p->num_blocks) ; /* Total number of blocks in device */ + FNAND_TOGGLE_DEBUG_D("block_size %d ", geometry_p->block_size) ; /* Size of a block in bytes */ + FNAND_TOGGLE_DEBUG_D("device_size %d ", geometry_p->device_size) ; /* Total device size in bytes */ + FNAND_TOGGLE_DEBUG_D("rowaddr_cycles %d ", geometry_p->rowaddr_cycles) ; /* Row address cycles */ + FNAND_TOGGLE_DEBUG_D("coladdr_cycles %d ", geometry_p->coladdr_cycles) ; /* Column address cycles */ + FNAND_TOGGLE_DEBUG_D("hw_ecc_length %d ", geometry_p->hw_ecc_length) ; /* 产生硬件ecc校验参数的个数 */ + FNAND_TOGGLE_DEBUG_D("ecc_offset %d ", geometry_p->ecc_offset) ; /* obb存放硬件ecc校验参数页位置的偏移 */ + FNAND_TOGGLE_DEBUG_D("hw_ecc_steps %d ", geometry_p->hw_ecc_steps) ; /* number of ECC steps per page */ + FNAND_TOGGLE_DEBUG_D("ecc_step_size %d ", geometry_p->ecc_step_size) ; /* 进行读写操作时,单次ecc 的步骤的跨度 */ + + + + return FT_SUCCESS; +} + + +/** + * @name: FNandToggleInit + * @msg: Toggle mode interface initialization + * @note: + * @param {FNand} *instance_p is the pointer to the FNand instance. + * @param {u32} chip_addr is chip address + * @return {FError} FT_SUCCESS 初始化成功 ,FNAND_NOT_FET_TOGGLE_MODE 初始化toggle 模式错误。 + */ +FError FNandToggleInit(FNand *instance_p, u32 chip_addr) +{ + FError ret; + char id[6]; + FASSERT(instance_p != NULL); + struct ToggleNandGeometry *toggle_geometry_p; + /* step 1 .reset nand chip */ + ret = FNandFlashReset(instance_p, chip_addr) ; + if (ret != FT_SUCCESS) + { + FNAND_TOGGLE_DEBUG_E("FNandFlashReset is error"); + return ret; + } + + /* step 2. readid operation 40h */ + ret = FNandFlashReadId(instance_p, 0x40, id, sizeof(id), chip_addr); + if (ret != FT_SUCCESS || strncmp(id, "JEDEC", sizeof(id) - 1)) + { + FNAND_TOGGLE_DEBUG_E("40H read id is %s ", id); + return FNAND_NOT_FET_TOGGLE_MODE; + } + + if (id[5] == 1) + { + instance_p->inter_mode[chip_addr] = FNAND_ASYN_SDR; + } + else if (id[5] == 2) + { + instance_p->inter_mode[chip_addr] = FNAND_TOG_ASYN_DDR; + } + else if (id[5] == 4) + { + instance_p->inter_mode[chip_addr] = FNAND_ASYN_SDR; + } + + FNandTimingInterfaceUpdate(instance_p, chip_addr); + + /* step 3. read device id table */ + + ret = FNandToggleReadParamPage(instance_p, NULL, 0, chip_addr); + if (ret != FT_SUCCESS) + { + FNAND_TOGGLE_DEBUG_E("read device id table is error"); + return FNAND_NOT_FET_TOGGLE_MODE; + } + + /* step 4. device id table parse */ + toggle_geometry_p = (struct ToggleNandGeometry *)instance_p->dma_data_buffer.data_buffer; + + return FNandToggleDetectJedec(instance_p, toggle_geometry_p, &instance_p->nand_geometry[chip_addr]); +} diff --git a/bsp/phytium/libraries/standalone/drivers/nand/fnand/manufacturer/fnand_toggle.h b/bsp/phytium/libraries/standalone/drivers/nand/fnand/manufacturer/fnand_toggle.h new file mode 100644 index 0000000000..229a6234e6 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/nand/fnand/manufacturer/fnand_toggle.h @@ -0,0 +1,157 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fnand_toggle.h + * Date: 2022-07-05 20:00:45 + * LastEditTime: 2022-07-05 20:00:45 + * Description: This file is for + * + * Modify History: + * Ver Who Date Changes + * ----- ------ -------- -------------------------------------- + */ + +#ifndef DRIVERS_NAND_FNAND_TOGGLE_H +#define DRIVERS_NAND_FNAND_TOGGLE_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ftypes.h" +#include "fnand.h" + + +/* + * Mandatory commands + */ + +#define TOGGLE_CMD_READ1 0x00 +#define TOGGLE_CMD_READ2 0x30 + +#define TOGGLE_CMD_CHANGE_READ_COLUMN1 0x05 /* TOGGLE Change Read \ + Column command (1st \ + cycle) */ +#define TOGGLE_CMD_CHANGE_READ_COLUMN2 0xE0 /* TOGGLE Change Read \ + Column command (2nd \ + cycle) */ +#define TOGGLE_CMD_BLOCK_ERASE1 0x60 /* TOGGLE Block Erase \ + (1st cycle) */ +#define TOGGLE_CMD_BLOCK_ERASE2 0xD0 /* TOGGLE Block Erase \ + (2nd cycle) */ +#define TOGGLE_CMD_READ_STATUS 0x70 /* TOGGLE Read status \ + command */ +#define TOGGLE_CMD_PAGE_PROG1 0x80 /* TOGGLE Page Program \ + command (1st cycle) \ + */ +#define TOGGLE_CMD_PAGE_PROG2 0x10 /* TOGGLE Page Program \ + command (2nd cycle) \ + */ +#define TOGGLE_CMD_CHANGE_WRITE_COLUMN 0x85 /* TOGGLE Change Write \ + Column command */ +#define TOGGLE_CMD_READ_ID 0x90 /* TOGGLE Read ID \ + command */ +#define TOGGLE_CMD_READ_PARAM_PAGE 0xEC /* TOGGLE Read \ + Parameter Page \ + command */ +#define TOGGLE_CMD_RESET 0xFF /* TOGGLE Reset \ + command */ + +#define TOGGLE_END_CMD_NONE 0xfff /* No End command */ + + +struct jedec_ecc_info +{ + u8 ecc_bits; + u8 codeword_size; + u16 bb_per_lun; + u16 block_endurance; + u8 reserved[2]; +} __attribute__((packed)); + +struct ToggleNandGeometry +{ + u8 sig[4]; /* Parameter page signature */ + u16 revision; /* Revision number */ + u16 features; /* Features supported */ + u8 opt_cmd[3]; /* Optional commands supported */ + u16 sec_cmd; + u8 num_of_param_pages; + u8 reserved0[18]; + + /* manufacturer information block */ + char manufacturer[12]; /* Device manufacturer */ + char model[20]; /* Device model */ + u8 jedec_id[6]; /* JEDEC manufacturer ID */ + u8 reserved1[10]; + + /* memory organization block */ + u32 byte_per_page; /* Number of data bytes per page */ + u16 spare_bytes_per_page; /* Number of spare bytes per page */ + u8 reserved2[6]; /* */ + u32 pages_per_block; /* Number of pages per block */ + u32 blocks_per_lun; /* Number of blocks per logical unit */ + u8 lun_count; /* Number of logical unit */ + u8 addr_cycles; + u8 bits_per_cell; + u8 programs_per_page; + u8 multi_plane_addr; + u8 multi_plane_op_attr; + u8 reserved3[38]; + + /* electrical parameter block */ + u16 async_sdr_speed_grade; + u16 toggle_ddr_speed_grade; + u16 sync_ddr_speed_grade; + u8 async_sdr_features; + u8 toggle_ddr_features; + u8 sync_ddr_features; + u16 t_prog; + u16 t_bers; + u16 t_r; + u16 t_r_multi_plane; + u16 t_ccs; + u16 io_pin_capacitance_typ; + u16 input_pin_capacitance_typ; + u16 clk_pin_capacitance_typ; + u8 driver_strength_support; + u16 t_adl; + u8 reserved4[36]; + + /* ECC and endurance block */ + u8 guaranteed_good_blocks; + u16 guaranteed_block_endurance; + struct jedec_ecc_info ecc_info[4]; + u8 reserved5[29]; + + /* reserved */ + u8 reserved6[148]; + + /* vendor */ + u16 vendor_rev_num; + u8 reserved7[88]; + + /* CRC for Parameter Page */ + u16 crc; + +} __attribute__((__packed__)); + + +#ifdef __cplusplus +} +#endif + +#endif // ! + + diff --git a/bsp/phytium/libraries/standalone/drivers/nand/fnand/manufacturer/fnand_toshiba.c b/bsp/phytium/libraries/standalone/drivers/nand/fnand/manufacturer/fnand_toshiba.c new file mode 100644 index 0000000000..c6d366c3e9 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/nand/fnand/manufacturer/fnand_toshiba.c @@ -0,0 +1,110 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fnand_toshiba.c + * Date: 2022-07-06 08:32:43 + * LastEditTime: 2022-07-06 08:32:44 + * Description: This file is for + * + * Modify History: + * Ver Who Date Changes + * ----- ------ -------- -------------------------------------- + */ + + +#include "fnand.h" +#include "fnand_id.h" +#include "fnand_ecc.h" +#include "fdebug.h" +#include "sdkconfig.h" + + +#define FNAND_T_NAND_DEBUG_TAG "FNAND_T_NAND" +#define CONFIG_FNAND_T_NAND_DEBUG_EN +// #define CONFIG_FNAND_T_NAND_DEBUG_EN +#ifdef CONFIG_FNAND_T_NAND_DEBUG_EN + #define FNAND_T_NAND_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(FNAND_T_NAND_DEBUG_TAG, format, ##__VA_ARGS__) + #define FNAND_T_NAND_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(FNAND_T_NAND_DEBUG_TAG, format, ##__VA_ARGS__) + #define FNAND_T_NAND_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(FNAND_T_NAND_DEBUG_TAG, format, ##__VA_ARGS__) + #define FNAND_T_NAND_DEBUG_D(format, ...) FT_DEBUG_PRINT_D(FNAND_T_NAND_DEBUG_TAG, format, ##__VA_ARGS__) +#else + #define FNAND_T_NAND_DEBUG_I(format, ...) + #define FNAND_T_NAND_DEBUG_W(format, ...) + #define FNAND_T_NAND_DEBUG_E(format, ...) + #define FNAND_T_NAND_DEBUG_D(format, ...) +#endif + +static FError TC58NVM9S3ETAI0_CHECK(FNand *instance_p, FNandId *id_p, u32 chip_addr) +{ + FNandNandGeometry *geometry_p = &instance_p->nand_geometry[chip_addr]; + + if (((id_p->data[3] & 0x3) == 1)) /* */ + { + FNAND_T_NAND_DEBUG_I("TC58NVM9S3ETAI0 is checked") ; + geometry_p->bytes_per_page = 2048; + geometry_p->spare_bytes_per_page = 64; + geometry_p->pages_per_block = 64; + geometry_p->blocks_per_lun = 512; + geometry_p->num_lun = 1; + geometry_p->num_pages = (geometry_p->num_lun * + geometry_p->blocks_per_lun * + geometry_p->pages_per_block); + geometry_p->num_blocks = (geometry_p->num_lun * geometry_p->blocks_per_lun); + geometry_p->block_size = (geometry_p->pages_per_block * geometry_p->bytes_per_page); + geometry_p->device_size = (geometry_p->num_blocks * geometry_p->block_size * geometry_p->bytes_per_page); + geometry_p->rowaddr_cycles = 3; + geometry_p->coladdr_cycles = 2 ; + geometry_p->hw_ecc_length = FNandGetEccTotalLength(geometry_p->bytes_per_page, instance_p->config.ecc_strength); + geometry_p->ecc_offset = geometry_p->spare_bytes_per_page - geometry_p->hw_ecc_length; + geometry_p->hw_ecc_steps = geometry_p->bytes_per_page / instance_p->config.ecc_step_size ; + geometry_p->ecc_step_size = instance_p->config.ecc_step_size; + FNAND_T_NAND_DEBUG_D("bytes_per_page %d ", geometry_p->bytes_per_page); /* Bytes per page */ + FNAND_T_NAND_DEBUG_D("spare_bytes_per_page %d ", geometry_p->spare_bytes_per_page) ; /* Size of spare area in bytes */ + FNAND_T_NAND_DEBUG_D("pages_per_block %d ", geometry_p->pages_per_block) ; /* Pages per block */ + FNAND_T_NAND_DEBUG_D("blocks_per_lun %d ", geometry_p->blocks_per_lun) ; /* Bocks per LUN */ + FNAND_T_NAND_DEBUG_D("num_lun %d ", geometry_p->num_lun) ; /* Total number of LUN */ + FNAND_T_NAND_DEBUG_D("num_pages %d ", geometry_p->num_pages) ; /* Total number of pages in device */ + FNAND_T_NAND_DEBUG_D("num_blocks %d ", geometry_p->num_blocks) ; /* Total number of blocks in device */ + FNAND_T_NAND_DEBUG_D("block_size %d ", geometry_p->block_size) ; /* Size of a block in bytes */ + FNAND_T_NAND_DEBUG_D("device_size %d ", geometry_p->device_size) ; /* Total device size in bytes */ + FNAND_T_NAND_DEBUG_D("rowaddr_cycles %d ", geometry_p->rowaddr_cycles) ; /* Row address cycles */ + FNAND_T_NAND_DEBUG_D("coladdr_cycles %d ", geometry_p->coladdr_cycles) ; /* Column address cycles */ + FNAND_T_NAND_DEBUG_D("hw_ecc_length %d ", geometry_p->hw_ecc_length) ; /* 产生硬件ecc校验参数的个数 */ + FNAND_T_NAND_DEBUG_D("ecc_offset %d ", geometry_p->ecc_offset) ; /* obb存放硬件ecc校验参数页位置的偏移 */ + FNAND_T_NAND_DEBUG_D("hw_ecc_steps %d ", geometry_p->hw_ecc_steps) ; /* number of ECC steps per page */ + FNAND_T_NAND_DEBUG_D("ecc_step_size %d ", geometry_p->ecc_step_size) ; /* 进行读写操作时,单次ecc 的步骤的跨度 */ + } + else + { + FNAND_T_NAND_DEBUG_E("TC58NVM9S3ETAI0_CHECK error"); + return FNAND_ERR_NOT_MATCH; + } + + return FT_SUCCESS; +} + +FError toshiba_nand_decode_id(FNand *instance_p, FNandId *id_p, u32 chip_addr) +{ + + switch (id_p->data[1]) + { + case 0xf0: + return TC58NVM9S3ETAI0_CHECK(instance_p, id_p, chip_addr) ; + break; + default: + FNAND_T_NAND_DEBUG_E("Driver not supported 0x%x device", id_p->data[1]) ; + return FNAND_ERR_NOT_MATCH; + } +} + +const struct FNandManuFacturerOps toshiba_ops = {.detect = toshiba_nand_decode_id}; diff --git a/bsp/phytium/libraries/standalone/drivers/pcie/Kconfig b/bsp/phytium/libraries/standalone/drivers/pcie/Kconfig new file mode 100644 index 0000000000..c4476fa491 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/pcie/Kconfig @@ -0,0 +1,9 @@ + +menu "Pcie Configuration" + config ENABLE_F_PCIE + bool + prompt "Use F_PCIE" + default n + +endmenu + diff --git a/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie.c b/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie.c new file mode 100644 index 0000000000..470a37fc14 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie.c @@ -0,0 +1,863 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fpcie.c + * Date: 2022-02-10 14:55:11 + * LastEditTime: 2022-02-18 08:59:28 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +/***************************** Include Files *********************************/ +#include "fpcie.h" +#include "fpcie_hw.h" +#include "fpcie_common.h" +#include "fparameters.h" +#include "fkernel.h" +#include +#include +#include "fdebug.h" + + +#define CONFIG_SYS_PCI_CACHE_LINE_SIZE 8 +#define upper_32_bits(n) ((u32)(((n) >> 16) >> 16)) + + +/***************** Macros (Inline Functions) Definitions *********************/ + +#define FPCIE_DEBUG_TAG "FPCIE" +#define FPCIE_ERROR(format, ...) FT_DEBUG_PRINT_E(FPCIE_DEBUG_TAG, format, ##__VA_ARGS__) +#define FPCIE_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(FPCIE_DEBUG_TAG, format, ##__VA_ARGS__) +#define FPCIE_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(FPCIE_DEBUG_TAG, format, ##__VA_ARGS__) +#define FPCIE_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(FPCIE_DEBUG_TAG, format, ##__VA_ARGS__) + + + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +extern int FPcieEpCleanBar(FPcie *instance_p, u32 peu_num, u32 bar_num) ; + +static void FPcieShowRegion(const char *name, struct FPcieRegion *region) +{ + FPCIE_DEBUG_I("PCI Autoconfig: Bus %s region: [%llx-%llx],\n" + "\t\tPhysical Memory [%llx-%llx]", name, + (unsigned long long)region->bus_start, + (unsigned long long)(region->bus_start + region->size - 1), + (unsigned long long)region->phys_start, + (unsigned long long)(region->phys_start + region->size - 1)); + FPCIE_DEBUG_I("bus_lower is %llx ", (unsigned long long)region->bus_lower) ; +} + +/** + * @name: FPcieRegionConfigInit + * @msg: 初始化PEU 用于分配的地址空间 + * @param {FPcie} *instance_p is a pointer to the FPcie instance. + * @param {FPcieRegion} *regs 地址空间对应的指针 + * @param {u32} regs_num 传入regs 结构体的数量 + */ +//用于资源初始化到instance_p中 +static void FPcieRegionConfigInit(FPcie *instance_p, struct FPcieRegion *regs, u32 regs_num) +{ + u32 i ; + + for (i = 0; i < regs_num; i++) + { + switch (regs[i].flags) + { + case FPCIE_REGION_IO: + memset(&instance_p->mem_io, 0, sizeof(struct FPcieRegion)) ; + memcpy(&instance_p->mem_io, regs, sizeof(struct FPcieRegion)) ; + instance_p->mem_io.exist_flg = FPCIE_REGION_EXIST_FLG ; + instance_p->mem_io.bus_lower = instance_p->mem_io.phys_start; + FPcieShowRegion("I/O", &instance_p->mem_io); + break; + case FPCIE_REGION_MEM: + memset(&instance_p->mem, 0, sizeof(struct FPcieRegion)) ; + memcpy(&instance_p->mem, regs, sizeof(struct FPcieRegion)) ; + instance_p->mem.exist_flg = FPCIE_REGION_EXIST_FLG ; + instance_p->mem.bus_lower = instance_p->mem.phys_start; + FPcieShowRegion("Memory", &instance_p->mem); + break; + case (PCI_REGION_PREFETCH|FPCIE_REGION_MEM): + memset(&instance_p->mem_prefetch, 0, sizeof(struct FPcieRegion)) ; + memcpy(&instance_p->mem_prefetch, regs, sizeof(struct FPcieRegion)) ; + instance_p->mem_prefetch.exist_flg = FPCIE_REGION_EXIST_FLG ; + instance_p->mem_prefetch.bus_lower = instance_p->mem_prefetch.phys_start; + FPcieShowRegion("Prefetchable Mem", &instance_p->mem_prefetch); + break; + default: + break; + } + } +} + +/** + * @name: FPcieCfgInitialize + * @msg: This function initializes the config space and PCIe bridge. + * @param {FPcie} *instance_p is a pointer to the FPcie instance. + * @param {FPcieConfig} *config_p pointer to FPcieConfig instrance Pointer. + * @return FError + */ +FError FPcieCfgInitialize(FPcie *instance_p, FPcieConfig *config_p) //用于从全局配置数据中获取数据,初始化instance_p +{ + fsize_t i; + struct FPcieRegion mem_region = {0} ; + struct FPcieRegion prefetch_region = {0} ; + struct FPcieRegion io_region = {0} ; + + /* Assert arguments */ + FASSERT(instance_p != NULL); + FASSERT(config_p != NULL); + + /* Clear instance memory and make copy of configuration */ + memset(instance_p, 0, sizeof(FPcie)); + memcpy(&instance_p->config, config_p, sizeof(FPcieConfig)); + + /* 为枚举过程中,涉及的配置空间提供地址划分 */ + /* mem32 地址 */ //使用获取到的硬件信息,来初始化mem32 + mem_region.phys_start = instance_p->config.npmem_base_addr ; + mem_region.bus_start = instance_p->config.npmem_base_addr ; + mem_region.size = instance_p->config.npmem_size ; + mem_region.flags = FPCIE_REGION_MEM ; + + /* mem64 地址 */ //使用获取到的硬件信息,来初始化mem64 + prefetch_region.phys_start = instance_p->config.pmem_base_addr ; + prefetch_region.bus_start = instance_p->config.pmem_base_addr ; + prefetch_region.size = instance_p->config.pmem_size ; + prefetch_region.flags = (PCI_REGION_PREFETCH | FPCIE_REGION_MEM); + + /* memio 地址 */ //使用获取到的硬件信息,来初始化io + io_region.phys_start = instance_p->config.io_base_addr ; + io_region.bus_start = instance_p->config.io_base_addr ; + io_region.size = instance_p->config.io_size ; + io_region.flags = FPCIE_REGION_IO; + + /* scaned bdf array clean */ + instance_p->scaned_bdf_count = 0; + + FPcieRegionConfigInit(instance_p, &mem_region, 1) ; +#if defined(__aarch64__) + FPcieRegionConfigInit(instance_p, &prefetch_region, 1) ; +#endif + FPcieRegionConfigInit(instance_p, &io_region, 1) ; + + instance_p->is_ready = FT_COMPONENT_IS_READY; + + /* 关闭当前所有misc 中断 */ + // FPcieMiscIrqDisable(instance_p, FPCIE_PEU0_C0); + // FPcieMiscIrqDisable(instance_p, FPCIE_PEU0_C1); + // FPcieMiscIrqDisable(instance_p, FPCIE_PEU0_C2); + // FPcieMiscIrqDisable(instance_p, FPCIE_PEU1_C0); + // FPcieMiscIrqDisable(instance_p, FPCIE_PEU1_C1); + // FPcieMiscIrqDisable(instance_p, FPCIE_PEU1_C2); + + /* 清空ep模式下所有配置地址 */ + // for (i = 0; i <= FPCIE_PEU1_C2; i++) + // { + // /* code */ + // FPcieEpCleanBar(instance_p, i, FPCIE_BAR_0); + // FPcieEpCleanBar(instance_p, i, FPCIE_BAR_1); + // FPcieEpCleanBar(instance_p, i, FPCIE_BAR_2); + // FPcieEpCleanBar(instance_p, i, FPCIE_BAR_3); + // FPcieEpCleanBar(instance_p, i, FPCIE_BAR_4); + // FPcieEpCleanBar(instance_p, i, FPCIE_BAR_5); + // } + + return (FT_SUCCESS); +} + +u32 FPcieFindCapability(FPcie *instance_p, u32 bdf, u32 cid_type, u32 cid, u32 *cid_offset) +{ + + u32 reg_value; + u32 next_cap_offset; + //u32 ret; + + if (cid_type == PCIE_CAP) + { + + /* Serach in PCIe configuration space */ + FPcieEcamReadConfig32bit(instance_p->config.ecam, bdf, FPCIE_CAPABILITY_LIST, ®_value); + if (reg_value == 0xffffffff) + return -1; + + next_cap_offset = (reg_value & 0xff); + while (next_cap_offset) + { + FPcieEcamReadConfig32bit(instance_p->config.ecam, bdf, next_cap_offset, ®_value); + if ((reg_value & 0xff) == cid) + { + *cid_offset = next_cap_offset; + return 0; + } + next_cap_offset = ((reg_value >> 8) & 0xff); + } + } + else if (cid_type == PCIE_ECAP) + { + + /* Serach in PCIe extended configuration space */ + next_cap_offset = FPCIE_ECAP_START; + while (next_cap_offset) + { + FPcieEcamReadConfig32bit(instance_p->config.ecam, bdf, next_cap_offset, ®_value); + if ((reg_value & 0xffff) == cid) + { + *cid_offset = next_cap_offset; + return 0; + } + next_cap_offset = ((reg_value >> 20) & 0xfff); + } + } + + /* The capability was not found */ + return -1; +} + +const char *FPcieClassStr(u8 class) +{ + switch (class) + { + case FPCI_CLASS_NOT_DEFINED: + return "Build before PCI Rev2.0"; + break; + case FPCI_BASE_CLASS_STORAGE: + return "Mass storage controller"; + break; + case FPCI_BASE_CLASS_NETWORK: + return "Network controller"; + break; + case FPCI_BASE_CLASS_DISPLAY: + return "Display controller"; + break; + case FPCI_BASE_CLASS_MULTIMEDIA: + return "Multimedia device"; + break; + case FPCI_BASE_CLASS_MEMORY: + return "Memory controller"; + break; + case FPCI_BASE_CLASS_BRIDGE: + return "Bridge device"; + break; + case FPCI_BASE_CLASS_COMMUNICATION: + return "Simple comm. controller"; + break; + case FPCI_BASE_CLASS_SYSTEM: + return "Base system peripheral"; + break; + case FPCI_BASE_CLASS_INPUT: + return "Input device"; + break; + case FPCI_BASE_CLASS_DOCKING: + return "Docking station"; + break; + case FPCI_BASE_CLASS_PROCESSOR: + return "Processor"; + break; + case FPCI_BASE_CLASS_SERIAL: + return "Serial bus controller"; + break; + case FPCI_BASE_CLASS_INTELLIGENT: + return "Intelligent controller"; + break; + case FPCI_BASE_CLASS_SATELLITE: + return "Satellite controller"; + break; + case FPCI_BASE_CLASS_CRYPT: + return "Cryptographic device"; + break; + case FPCI_BASE_CLASS_SIGNAL_PROCESSING: + return "DSP"; + break; + case FPCI_CLASS_OTHERS: + return "Does not fit any class"; + break; + default: + return "???"; + break; + }; +} + + +void FPcieAutoRegionAlign(struct FPcieRegion *res, pci_size_t size) +{ + res->bus_lower = ((res->bus_lower - 1) | (size - 1)) + 1; +} + +int FPcieAutoRegionAllocate(struct FPcieRegion *res, pci_size_t size, + pci_addr_t *bar, bool supports_64bit) +{ + pci_addr_t addr; + + if (!res) + { + printf("No resource\n"); + goto error; + } + + addr = ((res->bus_lower - 1) | (size - 1)) + 1; + + if (addr - res->bus_start + size > res->size) + { + printf("No room in resource"); + goto error; + } + + if (upper_32_bits(addr) && !supports_64bit) + { + printf("Cannot assign 64-bit address to 32-bit-only resource\n"); + goto error; + } + + res->bus_lower = addr + size; + + //printf("address=0x%llx bus_lower=0x%llx\n", (unsigned long long)addr, + // (unsigned long long)res->bus_lower); + + *bar = addr; + return 0; + +error: + *bar = (pci_addr_t) -1; + return -1; +} + + + +void FPcieAutoSetupDevice(FPcie *instance_p, u32 bdf, int bars_num, + struct FPcieRegion *mem, + struct FPcieRegion *prefetch, struct FPcieRegion *io, + bool enum_only) +{ + u32 bar_response; + pci_size_t bar_size; + u16 cmdstat = 0; + int bar, bar_nr = 0; + u8 header_type; + int rom_addr; + pci_addr_t bar_value; + struct FPcieRegion *bar_res = NULL; + int found_mem64 = 0; + u16 class; + + FPcieEcamReadConfig16bit(instance_p->config.ecam, bdf, FPCIE_COMMAND_REG, &cmdstat); + cmdstat = (cmdstat & ~(FPCIE_COMMAND_IO | FPCIE_COMMAND_MEMORY)) | + FPCIE_COMMAND_MASTER; + + for (bar = FPCIE_BASE_ADDRESS_0; + bar < FPCIE_BASE_ADDRESS_0 + (bars_num * 4); bar += 4) + { + /* Tickle the BAR and get the response */ + if (!enum_only) + FPcieEcamWriteConfig32bit(instance_p->config.ecam, bdf, bar, 0xffffffff); + + FPcieEcamReadConfig32bit(instance_p->config.ecam, bdf, bar, &bar_response); + + /* If BAR is not implemented go to the next BAR */ + if (!bar_response) + continue; + + found_mem64 = 0; + + /* Check the BAR type and set our address mask */ + if (bar_response & FPCIE_BASE_ADDRESS_SPACE) + { + bar_size = ((~(bar_response & FPCIE_BASE_ADDRESS_IO_MASK)) + & 0xffff) + 1; + if (!enum_only) + bar_res = io; + + //printf("PCI Autoconfig: BAR %d, I/O, size=0x%llx, ", + // bar_nr, (unsigned long long)bar_size); + } + else + { + if ((bar_response & FPCIE_BASE_ADDRESS_MEM_TYPE_MASK) == + FPCIE_BASE_ADDRESS_MEM_TYPE_64) + { + u32 bar_response_upper; + u64 bar64; + + if (!enum_only) + { + FPcieEcamWriteConfig32bit(instance_p->config.ecam, bdf, bar + 4, 0xffffffff); + } + FPcieEcamReadConfig32bit(instance_p->config.ecam, bdf, bar + 4, &bar_response_upper); + + bar64 = ((u64)bar_response_upper << 32) | + bar_response; + + bar_size = ~(bar64 & FPCIE_BASE_ADDRESS_MEM_MASK) + + 1; + if (!enum_only) + found_mem64 = 1; + } + else + { + bar_size = (u32)(~(bar_response & + FPCIE_BASE_ADDRESS_MEM_MASK) + 1); + } + if (!enum_only) + { + if ((prefetch->exist_flg & FPCIE_REGION_EXIST_FLG) & (bar_response & + FPCIE_BASE_ADDRESS_MEM_PREFETCH)) + { + bar_res = prefetch; + } + else + { + bar_res = mem; + } + } + + //printf("PCI Autoconfig: BAR %d, %s, size=0x%llx, ", + // bar_nr, bar_res == prefetch ? "Prf" : "Mem", + // (unsigned long long)bar_size); + } + + if (!enum_only && FPcieAutoRegionAllocate(bar_res, bar_size, + &bar_value, + found_mem64) == 0) + { + /* Write it out and update our limit */ + FPcieEcamWriteConfig32bit(instance_p->config.ecam, bdf, bar, (u32)bar_value); + + if (found_mem64) + { + bar += 4; + +#ifdef CONFIG_SYS_PCI_64BIT + FPcieEcamWriteConfig32bit(instance_p->config.ecam, bdf, bar, (u32)(bar_value >> 32)); +#else + /* + * If we are a 64-bit decoder then increment to + * the upper 32 bits of the bar and force it to + * locate in the lower 4GB of memory. + */ + FPcieEcamWriteConfig32bit(instance_p->config.ecam, bdf, bar, 0x00000000); +#endif + } + } + + cmdstat |= (bar_response & FPCIE_BASE_ADDRESS_SPACE) ? + FPCIE_COMMAND_IO : FPCIE_COMMAND_MEMORY; + + //printf("\n"); + + bar_nr++; + } + + if (!enum_only) + { + /* Configure the expansion ROM address */ + FPcieEcamReadConfig8bit(instance_p->config.ecam, bdf, FPCIE_HEADER_TYPE_REG, &header_type); + header_type &= 0x7f; + if (header_type != FPCIE_HEADER_TYPE_CARDBUS) + { + rom_addr = (header_type == FPCIE_HEADER_TYPE_NORMAL) ? + FPCIE_ROM_ADDRESS : FPCIE_ROM_ADDRESS1; + FPcieEcamWriteConfig32bit(instance_p->config.ecam, bdf, rom_addr, 0xfffffffe); + FPcieEcamReadConfig32bit(instance_p->config.ecam, bdf, rom_addr, &bar_response); + if (bar_response) + { + bar_size = -(bar_response & ~1); + //printf("PCI Autoconfig: ROM, size=%#x, ", + // (unsigned int)bar_size); + if (FPcieAutoRegionAllocate(mem, bar_size, + &bar_value, + false) == 0) + { + FPcieEcamWriteConfig32bit(instance_p->config.ecam, bdf, rom_addr, bar_value); + + } + cmdstat |= FPCIE_COMMAND_MEMORY; + //printf("\n"); + } + } + } + + /* PCI_COMMAND_IO must be set for VGA device */ + FPcieEcamReadConfig16bit(instance_p->config.ecam, bdf, FPCI_CLASS_DEVICE_REG, &class); + if (class == FPCI_CLASS_DISPLAY_VGA) + cmdstat |= FPCIE_COMMAND_IO; + + FPcieEcamWriteConfig16bit(instance_p->config.ecam, bdf, FPCIE_COMMAND_REG, cmdstat); + FPcieEcamWriteConfig8bit(instance_p->config.ecam, bdf, FPCIE_CACHE_LINE_SIZE_REG, + CONFIG_SYS_PCI_CACHE_LINE_SIZE); + FPcieEcamWriteConfig8bit(instance_p->config.ecam, bdf, FPCIE_LATENCY_TIMER_REG, 0x80); +} + +void FPcieAutoPrescanSetupBridge(FPcie *instance_p, u32 bdf, int sub_bus) +{ + struct FPcieRegion *pci_mem; + struct FPcieRegion *pci_prefetch; + struct FPcieRegion *pci_io; + u16 cmdstat, prefechable_64; + + pci_mem = &(instance_p->mem); + pci_prefetch = &(instance_p->mem_prefetch); + pci_io = &(instance_p->mem_io); + + FPcieEcamReadConfig16bit(instance_p->config.ecam, bdf, FPCIE_COMMAND_REG, &cmdstat) ; + FPcieEcamReadConfig16bit(instance_p->config.ecam, bdf, FPCIE_PREF_MEMORY_BASE_REG, &prefechable_64) ; + prefechable_64 &= FPCIE_PREF_RANGE_TYPE_MASK; + + /* Configure bus number registers *///暂时只有一个pcie配置空间的做法,如果多个pci配置空间,则需当前bus减去该配置空间对应设备的起始bus号 + FPcieEcamWriteConfig8bit(instance_p->config.ecam, bdf, FPCIE_PRIMARY_BUS_REG, FPCIE_BUS(bdf)); + FPcieEcamWriteConfig8bit(instance_p->config.ecam, bdf, FPCIE_SECONDARY_BUS_REG, sub_bus); + FPcieEcamWriteConfig8bit(instance_p->config.ecam, bdf, FPCIE_SUBORDINATE_BUS_REG, 0xff); + + if (pci_mem->exist_flg & FPCIE_REGION_EXIST_FLG) + { + /* Round memory allocator to 1MB boundary */ + FPcieAutoRegionAlign(pci_mem, 0x100000); + + /* + * Set up memory and I/O filter limits, assume 32-bit + * I/O space + */ + FPcieEcamWriteConfig16bit(instance_p->config.ecam, bdf, FPCIE_MEMORY_BASE_REG, + (pci_mem->bus_lower & 0xfff00000) >> 16); + + cmdstat |= FPCIE_COMMAND_MEMORY; + } + + if (pci_prefetch->exist_flg & FPCIE_REGION_EXIST_FLG) + { + /* Round memory allocator to 1MB boundary */ + FPcieAutoRegionAlign(pci_prefetch, 0x100000); + + /* + * Set up memory and I/O filter limits, assume 32-bit + * I/O space + */ + FPcieEcamWriteConfig16bit(instance_p->config.ecam, bdf, FPCIE_PREF_MEMORY_BASE_REG, + (pci_prefetch->bus_lower & 0xfff00000) >> 16); + + if (prefechable_64 == FPCIE_PREF_RANGE_TYPE_64) +#ifdef CONFIG_SYS_PCI_64BIT + FPcieEcamWriteConfig32bit(instance_p->config.ecam, bdf, FPCIE_PREF_BASE_UPPER32_REG, + pci_prefetch->bus_lower >> 32); +#else + FPcieEcamWriteConfig32bit(instance_p->config.ecam, bdf, FPCIE_PREF_BASE_UPPER32_REG, + 0x0); +#endif + + cmdstat |= FPCIE_COMMAND_MEMORY; + } + else + { + /* We don't support prefetchable memory for now, so disable */ + FPcieEcamWriteConfig16bit(instance_p->config.ecam, bdf, FPCIE_PREF_MEMORY_BASE_REG, 0x1000); + FPcieEcamWriteConfig16bit(instance_p->config.ecam, bdf, FPCIE_PREF_MEMORY_LIMIT_REG, 0x0); + if (prefechable_64 == FPCIE_PREF_RANGE_TYPE_64) + { + FPcieEcamWriteConfig16bit(instance_p->config.ecam, bdf, FPCIE_PREF_BASE_UPPER32_REG, 0x0); + FPcieEcamWriteConfig16bit(instance_p->config.ecam, bdf, FPCIE_PREF_LIMIT_UPPER32_REG, 0x0); + } + } + + if (pci_io->exist_flg & FPCIE_REGION_EXIST_FLG) + { + /* Round I/O allocator to 4KB boundary */ + FPcieAutoRegionAlign(pci_io, 0x1000); + + FPcieEcamWriteConfig8bit(instance_p->config.ecam, bdf, FPCIE_IO_BASE_REG, + (pci_io->bus_lower & 0x0000f000) >> 8); + FPcieEcamWriteConfig16bit(instance_p->config.ecam, bdf, FPCIE_IO_BASE_UPPER16_REG, + (pci_io->bus_lower & 0xffff0000) >> 16); + + cmdstat |= FPCIE_COMMAND_IO; + } + + /* Enable memory and I/O accesses, enable bus master */ + FPcieEcamWriteConfig16bit(instance_p->config.ecam, bdf, FPCIE_COMMAND_REG, cmdstat | FPCIE_COMMAND_MASTER); +} + + +void FPcieAutoPostscanSetupBridge(FPcie *instance_p, u32 bdf, int sub_bus) +{ + struct FPcieRegion *pci_mem; + struct FPcieRegion *pci_prefetch; + struct FPcieRegion *pci_io; + + pci_mem = &(instance_p->mem); + pci_prefetch = &(instance_p->mem_prefetch); + pci_io = &(instance_p->mem_io); + + /* Configure bus number registers */ + FPcieEcamWriteConfig8bit(instance_p->config.ecam, bdf, FPCIE_SUBORDINATE_BUS_REG, sub_bus);//配置一下subordinate-bus,可能在固件下不一定必须用 + + if (pci_mem->exist_flg & FPCIE_REGION_EXIST_FLG) + { + /* Round memory allocator to 1MB boundary */ + FPcieAutoRegionAlign(pci_mem, 0x100000); + + FPcieEcamWriteConfig16bit(instance_p->config.ecam, bdf, FPCIE_MEMORY_LIMIT_REG, (pci_mem->bus_lower - 1) >> 16); + } + + if (pci_prefetch->exist_flg & FPCIE_REGION_EXIST_FLG) + { + u16 prefechable_64; + + FPcieEcamReadConfig16bit(instance_p->config.ecam, bdf, FPCIE_PREF_MEMORY_LIMIT_REG, &prefechable_64); + prefechable_64 &= FPCIE_PREF_RANGE_TYPE_MASK; + + /* Round memory allocator to 1MB boundary */ + FPcieAutoRegionAlign(pci_prefetch, 0x100000); + + FPcieEcamWriteConfig16bit(instance_p->config.ecam, bdf, FPCIE_PREF_MEMORY_LIMIT_REG, (pci_prefetch->bus_lower - 1) >> 16); + if (prefechable_64 == FPCIE_PREF_RANGE_TYPE_64) +#ifdef CONFIG_SYS_PCI_64BIT + + FPcieEcamWriteConfig32bit(instance_p->config.ecam, bdf, FPCIE_PREF_LIMIT_UPPER32_REG, + (pci_prefetch->bus_lower - 1) >> 32); +#else + FPcieEcamWriteConfig32bit(instance_p->config.ecam, bdf, FPCIE_PREF_LIMIT_UPPER32_REG, 0x0); +#endif + } + + if (pci_io->exist_flg & FPCIE_REGION_EXIST_FLG) + { + /* Round I/O allocator to 4KB boundary */ + FPcieAutoRegionAlign(pci_io, 0x1000); + + FPcieEcamWriteConfig8bit(instance_p->config.ecam, bdf, FPCIE_IO_LIMIT_REG, + ((pci_io->bus_lower - 1) & 0x0000f000) >> 8); + FPcieEcamWriteConfig16bit(instance_p->config.ecam, bdf, FPCIE_IO_LIMIT_UPPER16_REG, + ((pci_io->bus_lower - 1) & 0xffff0000) >> 16); + } +} + + +int FPcieHoseProbeBus(FPcie *instance_p, u32 bdf) +{ + int sub_bus; + int ret; + + instance_p->bus_max = instance_p->bus_max + 1; + + sub_bus = instance_p->bus_max; + + FPcieAutoPrescanSetupBridge(instance_p, bdf, sub_bus); + + FPcieScanBus(instance_p, sub_bus, bdf); + + sub_bus = instance_p->bus_max; + FPcieAutoPostscanSetupBridge(instance_p, bdf, sub_bus); + + return sub_bus; +} + +/* + * HJF: Changed this to return int. I think this is required + * to get the correct result when scanning bridges + */ +int FPcieAutoConfigDevice(FPcie *instance_p, u32 bdf) +{ + u16 class = 0; + + struct FPcieRegion *pci_mem; + struct FPcieRegion *pci_prefetch; + struct FPcieRegion *pci_io; + + bool enum_only = false; + + int n; + +#ifdef CONFIG_PCI_ENUM_ONLY + enum_only = true; +#endif + + pci_mem = &(instance_p->mem); + pci_prefetch = &(instance_p->mem_prefetch); + pci_io = &(instance_p->mem_io); + + FPcieEcamReadConfig16bit(instance_p->config.ecam, bdf, FPCIE_CLASS_DEVICE_REG, &class) ;//读取classcode编号 + + switch (class) + { + case FPCI_CLASS_BRIDGE_PCI: + FPcieAutoSetupDevice(instance_p, bdf, 2, pci_mem, pci_prefetch, pci_io, + enum_only); + + n = FPcieHoseProbeBus(instance_p, bdf); + if (n < 0) + return n; + break; + + case FPCI_CLASS_BRIDGE_CARDBUS: + /* + * just do a minimal setup of the bridge, + * let the OS take care of the rest + */ + FPcieAutoSetupDevice(instance_p, bdf, 0, pci_mem, pci_prefetch, pci_io, + enum_only); + + printf("PCI Autoconfig: Found P2CardBus bridge, device %d\n", FPCIE_DEV(bdf)); + + break; + + case FPCI_CLASS_PROCESSOR_POWERPC: /* an agent or end-point */ + printf("PCI AutoConfig: Found PowerPC device\n"); + /* fall through */ + + default: + FPcieAutoSetupDevice(instance_p, bdf, 6, pci_mem, pci_prefetch, pci_io, + enum_only); + break; + } + + return FT_SUCCESS; +} + + +FError FPcieBindBusDevices(FPcie *instance_p, u32 bus_num, u32 parent_bdf, struct FPcieBus *bus) +{ + int dev_count = 0; + u16 vendor, device; + u8 header_type; + s32 bdf, end; + bool found_multi; + FError ret; + u8 class_show; + u32 dev_exp_cap, bus_exp_cap, dev_ext_ari_cap; + u32 data; + char buf_bdf_print[20]; + found_multi = false; + end = FPCIE_BDF(bus_num, FT_PCIE_CFG_MAX_NUM_OF_DEV - 1, + FT_PCIE_CFG_MAX_NUM_OF_FUN - 1); + for (bdf = FPCIE_BDF(bus_num, 0, 0); bdf <= end; //使用bus的seq成员来进行扫描,其实相当于secondory_bus号 + bdf += FPCIE_BDF(0, 0, 1)) + { + u32 class; + + /* phytium old pci ip version, need skip in some bus */ + if (instance_p->config.need_skip) + { + if (FPcieSkipDevice(instance_p->config.ecam, parent_bdf) == FPCIE_NEED_SKIP) + { + continue; + } + } + + if (!FPCIE_FUNC(bdf)) + found_multi = false; + if (FPCIE_FUNC(bdf) && !found_multi) + continue; + + /* Check only the first access, we don't expect problems */ + FPcieEcamReadConfig16bit(instance_p->config.ecam, bdf, FPCIE_VENDOR_REG, &vendor) ; + + if (vendor == 0xffff || vendor == 0x0000) + continue; + + FPcieEcamReadConfig8bit(instance_p->config.ecam, bdf, FPCIE_HEADER_TYPE_REG, &header_type) ; + + if (!FPCIE_FUNC(bdf)) + found_multi = header_type & 0x80; + + FPcieEcamReadConfig16bit(instance_p->config.ecam, bdf, FPCIE_DEVICE_ID_REG, &device) ; //读取deviceid + FPcieEcamReadConfig32bit(instance_p->config.ecam, bdf, FPCI_CLASS_REVISION, &class) ; //读取classcode + class >>= 8; + + FPcieEcamReadConfig8bit(instance_p->config.ecam, bdf, FPCIE_CLASS_CODE_REG, &class_show) ; + + if (parent_bdf == 0xffffffff) + { + strcpy(buf_bdf_print, "root-controller"); + } + else + { + sprintf(buf_bdf_print, "pci_%x:%x:%x", + FPCIE_BUS(parent_bdf), FPCIE_DEV(parent_bdf), FPCIE_FUNC(parent_bdf)); + } + printf(" %02x:%02x.%02x - %04lx:%04lx %s", + FPCIE_BUS(bdf), FPCIE_DEV(bdf), FPCIE_FUNC(bdf), vendor, device, + buf_bdf_print); + printf(" 0x%.2x (%s)\n", (int)class_show, FPcieClassStr(class_show)); + + /* ARI function handle */ + /* step 1: detect if PCI Express Device */ + ret = FPcieFindCapability(instance_p, bdf, PCIE_CAP, FPCI_CAP_ID_EXP, &dev_exp_cap); + if (ret == 0 && dev_exp_cap > 0) + { + /* step2: check if the device is an ARI device */ + ret = FPcieFindCapability(instance_p, bdf, PCIE_ECAP, FPCI_EXT_CAP_ID_ARI, &dev_ext_ari_cap); + if (ret == 0 && dev_ext_ari_cap > 0) + { + /* step3: check if its parent supports ARI forwarding */ + ret = FPcieFindCapability(instance_p, parent_bdf, PCIE_CAP, FPCI_CAP_ID_EXP, &bus_exp_cap); + /* config bus ARI forwarding */ + if (ret == 0 && bus_exp_cap > 0) + { + FPcieEcamReadConfig32bit(instance_p->config.ecam, parent_bdf, + bus_exp_cap + FPCIE_CAPABILITY_DEVICE_CAPABILITIES_2_OFFSET, &data); + if ((data & FPCIE_CAPABILITY_DEVICE_CAPABILITIES_2_ARI_FORWARDING) != 0) + { + /* step4: ARI forwarding support in bridge, so enable it */ + FPcieEcamReadConfig32bit(instance_p->config.ecam, parent_bdf, + bus_exp_cap + FPCIE_CAPABILITY_DEVICE_CONTROL_2_OFFSET, &data); + if (data & FPCIE_CAPABILITY_DEVICE_CONTROL_2_ARI_FORWARDING == 0) + { + data |= FPCIE_CAPABILITY_DEVICE_CONTROL_2_ARI_FORWARDING; + FPcieEcamWriteConfig32bit(instance_p->config.ecam, parent_bdf, + bus_exp_cap + FPCIE_CAPABILITY_DEVICE_CONTROL_2_OFFSET, data); + } + } + } + } + } + + bus->ChildN[dev_count] = bdf; + dev_count++; + + //这里可以将当前的device,保存到全局变量中,供别的驱动来查询。 + instance_p->scaned_bdf_array[instance_p->scaned_bdf_count] = bdf; + (instance_p->scaned_bdf_count)++; + + } + bus->ChildCount = dev_count; + + return FT_SUCCESS; + +} + +FError FPcieScanBus(FPcie *instance_p, u32 bus_num, u32 parent_bdf) +{ + int i = 0; + s32 bdf; + struct FPcieBus bus; + bus.ChildCount = 0; + + /* scan bus 0 device */ + FPcieBindBusDevices(instance_p, bus_num, parent_bdf, &bus); + + if (bus.ChildCount > 0) + { + for (i = 0; i < bus.ChildCount; i++) + { + bdf = bus.ChildN[i]; + FPcieAutoConfigDevice(instance_p, bdf); + } + } + instance_p->is_scaned = 1; //表示已经扫描完成 +} + diff --git a/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie.h b/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie.h new file mode 100644 index 0000000000..c8ca7077dc --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie.h @@ -0,0 +1,303 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fpcie.h + * Date: 2022-02-10 14:55:11 + * LastEditTime: 2022-02-18 08:59:37 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#ifndef DRIVERS_FPCIE_H +#define DRIVERS_FPCIE_H + + + + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/************************** Function Prototypes ******************************/ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ + +#include "ftypes.h" +#include "fassert.h" +#include "fpcie_dma.h" +#include "fparameters.h" + +#ifdef __aarch64__ +#define CONFIG_SYS_PCI_64BIT 1 +#endif + + +#ifdef CONFIG_SYS_PCI_64BIT +typedef u64 pci_addr_t; +typedef u64 pci_size_t; +#else +typedef u32 pci_addr_t; +typedef u32 pci_size_t; +#endif + +typedef boolean bool; +#define true TRUE +#define false FALSE + + +/* Access sizes for PCI reads and writes */ +enum pci_size_t +{ + PCI_SIZE_8, + PCI_SIZE_16, + PCI_SIZE_32, +}; + + +/***************** Macros (Inline Functions) Definitions *********************/ + +#define FPCIE_ERR_INVALID_PARAM FT_CODE_ERR(ErrModBsp, ErrPcie, 0x1u) +#define FPCIE_ERR_OUTOF_BUS FT_CODE_ERR(ErrModBsp, ErrPcie, 0x2u) +#define FPCIE_ERR_CONFIG_WRITE FT_CODE_ERR(ErrModBsp, ErrPcie, 0x3u) +#define FPCIE_ERR_TYPE0 FT_CODE_ERR(ErrModBsp, ErrPcie, 0x4u) +#define FPCIE_ERR_TIMEOUT FT_CODE_ERR(ErrModBsp, ErrPcie, 0x5u) +#define FPCIE_NEED_SKIP FT_CODE_ERR(ErrModBsp, ErrPcie, 0x6u) +#define FPCIE_NOT_FOUND FT_CODE_ERR(ErrModBsp, ErrPcie, 0x7u) + + +#define FPCIE_REGION_MEM 0x00000000 /* PCI memory space */ +#define FPCIE_REGION_IO 0x00000001 /* PCI IO space */ +#define PCI_REGION_PREFETCH 0x00000008 /* prefetchable PCI memory */ + + +#define FPCIE_BAR_0 0 +#define FPCIE_BAR_1 1 +#define FPCIE_BAR_2 2 +#define FPCIE_BAR_3 3 +#define FPCIE_BAR_4 4 +#define FPCIE_BAR_5 5 + +/* PCI-E Unit controller selection */ +#define FPCIE_PEU0_C0 0 /* pcie 0 0号控制器 */ +#define FPCIE_PEU0_C1 1 /* pcie 0 1号控制器 */ +#define FPCIE_PEU0_C2 2 /* pcie 0 2号控制器 */ +#define FPCIE_PEU1_C0 3 /* pcie 1 0号控制器 */ +#define FPCIE_PEU1_C1 4 /* pcie 1 1号控制器 */ +#define FPCIE_PEU1_C2 5 /* pcie 1 2号控制器 */ + + +#define FPCIE_REGION_EXIST_FLG 1 + +/** @name Callback identifiers + * + * These constants are used as parameters to FPcieMiscSetHandler() + * @{ + */ +#define FPCIE_HANDLER_DMASEND 1U +#define FPCIE_HANDLER_DMARECV 2U +#define FPCIE_HANDLER_DMASEND_ERROR 3U +#define FPCIE_HANDLER_DMARECV_ERROR 4U +/*@}*/ + +typedef void (*FPcieIrqCallBack)(void *args); + +#if defined(__aarch64__) +typedef u64 FPcieAddr; +typedef u64 FPcieSize; +typedef u64 FPciePhysAddr; +#else +typedef u32 FPcieAddr; +typedef u32 FPcieSize; +typedef u32 FPciePhysAddr; +#endif + + +typedef struct +{ + u16 vender_id ; + u16 device_id ; + u32 bus_num ; + u32 dev_num ; + u32 fun_num ; + u32 class_code ; +} FPcieSearchFunNode; + + +typedef struct +{ + void (*IntxCallBack)(void *args) ; + void *args ; + s32 bdf ; +} FPcieIntxFun; + +struct FPcieRegion +{ + FPcieAddr bus_start; /* Start on the bus */ + FPciePhysAddr phys_start; /* Start in physical address space */ + FPcieSize size; /* Size */ + unsigned long flags; /* Resource flags */ + FPcieAddr bus_lower; + u32 exist_flg; /* exist flg */ +}; + +typedef struct +{ + u16 vendor, device; +} FpcieId; + +typedef struct +{ + u32 instance_id; /* Id of device*/ + u32 irq_num; /* Irq number */ + uintptr_t ecam; /* The Memory way */ + uintptr_t peu0_config_address; + uintptr_t peu1_config_address; + + uintptr_t control_c0_address; //0x29900000 + uintptr_t control_c1_address; //0x29910000 + uintptr_t control_c2_address; + uintptr_t control_c3_address; + uintptr_t control_c4_address; + uintptr_t control_c5_address; + +#ifdef FT_PCI_INTX_EOI + uintptr_t intx_peux_stat_address[FT_PCI_INTX_SATA_NUM] ; + uintptr_t intx_control_eux_cx_address[FT_PCI_INTX_CONTROL_NUM] ; +#endif + + u32 io_base_addr; + u32 io_size ; + u32 npmem_base_addr; + u32 npmem_size; + u64 pmem_base_addr; /* Prefetchable memory */ + u64 pmem_size; + + u8 inta_irq_num ; + u8 intb_irq_num ; + u8 intc_irq_num ; + u8 intd_irq_num ; + u8 need_skip ; + +} FPcieConfig; + +typedef struct +{ + u32 is_ready; /* Device is ininitialized and ready*/ + FPcieConfig config; + + struct FPcieRegion mem; + struct FPcieRegion mem_prefetch; + struct FPcieRegion mem_io; + + s32 bus_max; /* 当前最大bus num */ + + FPcieIrqCallBack fpcie_dma_rx_cb; + void *dma_rx_args; + + FPcieIrqCallBack fpcie_dma_tx_cb; + void *dma_tx_args; + + FPcieIrqCallBack fpcie_dma_rx_error_cb; + void *dma_rx_error_args; + + FPcieIrqCallBack fpcie_dma_tx_error_cb; + void *dma_tx_error_args; + + FPcieIntxFun inta_fun[128]; //假设最高支持128个pcie 节点 + + FPcieIntxFun intb_fun[128]; + + FPcieIntxFun intc_fun[128]; + + FPcieIntxFun intd_fun[128]; + + s32 scaned_bdf_array[128]; + s32 scaned_bdf_count; + + u32 is_scaned; /* Device is ininitialized and ready*/ + +} FPcie; + +FPcieConfig *FPcieLookupConfig(u32 instance_id); + +FError FPcieCfgInitialize(FPcie *instance_p, FPcieConfig *config_p); + +/* dma */ +FError FPcieDmaDescSet(uintptr axi_addr, + uintptr pcie_addr, + u32 length, + struct FPcieDmaDescriptor *desc, + struct FPcieDmaDescriptor *next_desc); + + +void FPcieDmaRead(uintptr cintrol_address, struct FPcieDmaDescriptor *desc); + +void FPcieDmaWrite(uintptr cintrol_address, struct FPcieDmaDescriptor *desc); + +FError FPcieDmaPollDone(struct FPcieDmaDescriptor *desc, u32 wait_cnt); + +/* Intx Interrupt */ +void FPcieIntxIrqHandler(s32 vector, void *args) ; + +FError FPcieIntxRegiterIrqHandler(FPcie *instance_p, + u32 bdf, + FPcieIntxFun *intx_fun_p) ; + +void FPcieMiscIrqDisable(FPcie *instance_p, fsize_t peu_num) ; + +struct FPcieBus +{ + s32 ChildN[32]; + u8 ChildCount; +} ; + +typedef enum +{ + HEADER = 0, + PCIE_CAP = 1, + PCIE_ECAP = 2 +} BITFIELD_REGISTER_TYPE; + + +const char *FPcieClassStr(u8 class); +void FPcieAutoRegionAlign(struct FPcieRegion *res, pci_size_t size); +int FPcieAutoRegionAllocate(struct FPcieRegion *res, pci_size_t size, + pci_addr_t *bar, bool supports_64bit); +void FPcieAutoSetupDevice(FPcie *instance_p, u32 bdf, int bars_num, + struct FPcieRegion *mem, + struct FPcieRegion *prefetch, struct FPcieRegion *io, + bool enum_only); +void FPcieAutoPrescanSetupBridge(FPcie *instance_p, u32 bdf, int sub_bus); +void FPcieAutoPostscanSetupBridge(FPcie *instance_p, u32 bdf, int sub_bus); +int FPcieHoseProbeBus(FPcie *instance_p, u32 bdf); +int FPcieAutoConfigDevice(FPcie *instance_p, u32 bdf); +FError FPcieBindBusDevices(FPcie *instance_p, u32 bus_num, u32 parent_bdf, struct FPcieBus *bus); +FError FPcieScanBus(FPcie *instance_p, u32 bus_num, u32 parent_bdf); + + +#ifdef __cplusplus +} +#endif + +#endif // ! diff --git a/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_common.h b/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_common.h new file mode 100644 index 0000000000..3b7c1efef7 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_common.h @@ -0,0 +1,575 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fpcie_common.h + * Date: 2022-02-10 14:55:11 + * LastEditTime: 2022-02-18 08:57:24 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#ifndef DRIVERS_FPCIE_COMMON_H +#define DRIVERS_FPCIE_COMMON_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "fpcie_hw.h" +#include "fkernel.h" + +/******************** Macros (Inline Functions) Definitions *******************/ + +/* Device classes and subclasses */ + +#define FPCI_CLASS_NOT_DEFINED 0x0000 +#define FPCI_CLASS_NOT_DEFINED_VGA 0x0001 + +#define FPCI_BASE_CLASS_STORAGE 0x01 +#define FPCI_CLASS_STORAGE_SCSI 0x0100 +#define FPCI_CLASS_STORAGE_IDE 0x0101 +#define FPCI_CLASS_STORAGE_FLOPPY 0x0102 +#define FPCI_CLASS_STORAGE_IPI 0x0103 +#define FPCI_CLASS_STORAGE_RAID 0x0104 +#define FPCI_CLASS_STORAGE_SATA 0x0106 +#define FPCI_CLASS_STORAGE_SATA_AHCI 0x010601 +#define FPCI_CLASS_STORAGE_SAS 0x0107 +#define FPCI_CLASS_STORAGE_EXPRESS 0x010802 +#define FPCI_CLASS_STORAGE_OTHER 0x0180 + +#define FPCI_BASE_CLASS_NETWORK 0x02 +#define FPCI_CLASS_NETWORK_ETHERNET 0x0200 +#define FPCI_CLASS_NETWORK_TOKEN_RING 0x0201 +#define FPCI_CLASS_NETWORK_FDDI 0x0202 +#define FPCI_CLASS_NETWORK_ATM 0x0203 +#define FPCI_CLASS_NETWORK_OTHER 0x0280 + +#define FPCI_BASE_CLASS_DISPLAY 0x03 +#define FPCI_CLASS_DISPLAY_VGA 0x0300 +#define FPCI_CLASS_DISPLAY_XGA 0x0301 +#define FPCI_CLASS_DISPLAY_3D 0x0302 +#define FPCI_CLASS_DISPLAY_OTHER 0x0380 + +#define FPCI_BASE_CLASS_MULTIMEDIA 0x04 +#define FPCI_CLASS_MULTIMEDIA_VIDEO 0x0400 +#define FPCI_CLASS_MULTIMEDIA_AUDIO 0x0401 +#define FPCI_CLASS_MULTIMEDIA_PHONE 0x0402 +#define FPCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403 +#define FPCI_CLASS_MULTIMEDIA_OTHER 0x0480 + +#define FPCI_BASE_CLASS_MEMORY 0x05 +#define FPCI_CLASS_MEMORY_RAM 0x0500 +#define FPCI_CLASS_MEMORY_FLASH 0x0501 +#define FPCI_CLASS_MEMORY_OTHER 0x0580 + +#define FPCI_BASE_CLASS_BRIDGE 0x06 +#define FPCI_CLASS_BRIDGE_HOST 0x0600 +#define FPCI_CLASS_BRIDGE_ISA 0x0601 +#define FPCI_CLASS_BRIDGE_EISA 0x0602 +#define FPCI_CLASS_BRIDGE_MC 0x0603 +#define FPCI_CLASS_BRIDGE_PCI 0x0604 +#define FPCI_CLASS_BRIDGE_PCMCIA 0x0605 +#define FPCI_CLASS_BRIDGE_NUBUS 0x0606 +#define FPCI_CLASS_BRIDGE_CARDBUS 0x0607 +#define FPCI_CLASS_BRIDGE_RACEWAY 0x0608 +#define FPCI_CLASS_BRIDGE_OTHER 0x0680 + +#define FPCI_BASE_CLASS_COMMUNICATION 0x07 +#define FPCI_CLASS_COMMUNICATION_SERIAL 0x0700 +#define FPCI_CLASS_COMMUNICATION_PARALLEL 0x0701 +#define FPCI_CLASS_COMMUNICATION_MULTISERIAL 0x0702 +#define FPCI_CLASS_COMMUNICATION_MODEM 0x0703 +#define FPCI_CLASS_COMMUNICATION_OTHER 0x0780 + +#define FPCI_BASE_CLASS_SYSTEM 0x08 +#define FPCI_CLASS_SYSTEM_PIC 0x0800 +#define FPCI_CLASS_SYSTEM_PIC_IOAPIC 0x080010 +#define FPCI_CLASS_SYSTEM_PIC_IOXAPIC 0x080020 +#define FPCI_CLASS_SYSTEM_DMA 0x0801 +#define FPCI_CLASS_SYSTEM_TIMER 0x0802 +#define FPCI_CLASS_SYSTEM_RTC 0x0803 +#define FPCI_CLASS_SYSTEM_PCI_HOTPLUG 0x0804 +#define FPCI_CLASS_SYSTEM_SDHCI 0x0805 +#define FPCI_CLASS_SYSTEM_OTHER 0x0880 + +#define FPCI_BASE_CLASS_INPUT 0x09 +#define FPCI_CLASS_INPUT_KEYBOARD 0x0900 +#define FPCI_CLASS_INPUT_PEN 0x0901 +#define FPCI_CLASS_INPUT_MOUSE 0x0902 +#define FPCI_CLASS_INPUT_SCANNER 0x0903 +#define FPCI_CLASS_INPUT_GAMEPORT 0x0904 +#define FPCI_CLASS_INPUT_OTHER 0x0980 + +#define FPCI_BASE_CLASS_DOCKING 0x0a +#define FPCI_CLASS_DOCKING_GENERIC 0x0a00 +#define FPCI_CLASS_DOCKING_OTHER 0x0a80 + +#define FPCI_BASE_CLASS_PROCESSOR 0x0b +#define FPCI_CLASS_PROCESSOR_386 0x0b00 +#define FPCI_CLASS_PROCESSOR_486 0x0b01 +#define FPCI_CLASS_PROCESSOR_PENTIUM 0x0b02 +#define FPCI_CLASS_PROCESSOR_ALPHA 0x0b10 +#define FPCI_CLASS_PROCESSOR_POWERPC 0x0b20 +#define FPCI_CLASS_PROCESSOR_MIPS 0x0b30 +#define FPCI_CLASS_PROCESSOR_CO 0x0b40 + +#define FPCI_BASE_CLASS_SERIAL 0x0c +#define FPCI_CLASS_SERIAL_FIREWIRE 0x0c00 +#define FPCI_CLASS_SERIAL_FIREWIRE_OHCI 0x0c0010 +#define FPCI_CLASS_SERIAL_ACCESS 0x0c01 +#define FPCI_CLASS_SERIAL_SSA 0x0c02 +#define FPCI_CLASS_SERIAL_USB 0x0c03 +#define FPCI_CLASS_SERIAL_USB_UHCI 0x0c0300 +#define FPCI_CLASS_SERIAL_USB_OHCI 0x0c0310 +#define FPCI_CLASS_SERIAL_USB_EHCI 0x0c0320 +#define FPCI_CLASS_SERIAL_USB_XHCI 0x0c0330 +#define FPCI_CLASS_SERIAL_FIBER 0x0c04 +#define FPCI_CLASS_SERIAL_SMBUS 0x0c05 + +#define FPCI_BASE_CLASS_WIRELESS 0x0d +#define FPCI_CLASS_WIRELESS_RF_CONTROLLER 0x0d10 +#define FPCI_CLASS_WIRELESS_WHCI 0x0d1010 + +#define FPCI_BASE_CLASS_INTELLIGENT 0x0e +#define FPCI_CLASS_INTELLIGENT_I2O 0x0e00 + +#define FPCI_BASE_CLASS_SATELLITE 0x0f +#define FPCI_CLASS_SATELLITE_TV 0x0f00 +#define FPCI_CLASS_SATELLITE_AUDIO 0x0f01 +#define FPCI_CLASS_SATELLITE_VOICE 0x0f03 +#define FPCI_CLASS_SATELLITE_DATA 0x0f04 + +#define FPCI_BASE_CLASS_CRYPT 0x10 +#define FPCI_CLASS_CRYPT_NETWORK 0x1000 +#define FPCI_CLASS_CRYPT_ENTERTAINMENT 0x1001 +#define FPCI_CLASS_CRYPT_OTHER 0x1080 + +#define FPCI_BASE_CLASS_SIGNAL_PROCESSING 0x11 +#define FPCI_CLASS_SP_DPIO 0x1100 +#define FPCI_CLASS_SP_OTHER 0x1180 + +#define FPCI_CLASS_OTHERS 0xff + + +/* Command register offsets */ + +/* PCIe Configuration registers offsets */ + +/* Vendor ID/Device ID offset */ +#define FPCIE_CFG_ID_REG 0x0000 + +/* Independent element register */ + +#define FPCIE_VENDOR_REG 0x0 +#define FPCIE_DEVICE_ID_REG 0x02 +#define FPCIE_STATUS_REG 0x06 +#define FPCI_CLASS_DEVICE_REG 0x0a /* Device class */ +#define FPCIE_CACHE_LINE_SIZE_REG 0x0c /* 8 bits */ +#define FPCIE_LATENCY_TIMER_REG 0x0d /* 8 bits */ +#define FPCIE_HEADER_TYPE_REG 0x0e /* Header Type */ +#define FPCIE_BIST_REG 0x0f /* 8 bits */ + +#define FPCIE_HEADER_TYPE_NORMAL 0 +#define FPCIE_HEADER_TYPE_BRIDGE 1 +#define FPCIE_HEADER_TYPE_CARDBUS 2 +#define FPCIE_SECONDARY_BUS_REG 0x19 /* Secondary bus number */ +#define FPCIE_SUBORDINATE_BUS_REG 0x1a /* Highest bus number behind the bridge */ +#define FPCIE_SEC_LATENCY_TIMER_REG 0x1b /* Latency timer for secondary interface */ +#define FPCIE_IO_BASE_REG 0x1c /* I/O range behind the bridge */ +#define FPCIE_IO_LIMIT_REG 0x1d +#define FPCIE_SEC_STATUS_REG 0x1e /* Secondary status register, only bit 14 used */ + +#define FPCIE_IO_LIMIT_UPPER16_REG 0x32 + +#define FPCIE_MEMORY_BASE_REG 0x20 /* Memory range behind */ +#define FPCIE_MEMORY_LIMIT_REG 0x22 +#define FPCIE_PREF_BASE_UPPER32_REG 0x28 /* Upper half of prefetchable memory range */ +#define FPCIE_PREF_LIMIT_UPPER32_REG 0x2c + +#define FPCIE_IO_BASE_UPPER16_REG 0x30 /* Upper half of I/O addresses */ +#define FPCI_CLASS_REVISION 0x08 /* High 24 bits are class, low 8 */ + +#define FPCIE_CLASS_REVISION 0x0a /* High 24 bits are class, low 8 revision */ + +#define FPCIE_INTERRUPT_PIN_REG 0x3d +#define FPCIE_INTERRUPT_LINE_REG 0x3c +#define FPCIE_MIN_GNT_REG 0x3e /* 8 bits */ +#define FPCIE_MAX_LAT_REG 0x3f /* 8 bits */ + + +#define FPCIE_COMMAND_REG 0x04 /* 16 bits */ +#define FPCIE_COMMAND_IO 0x1 /* Enable response in I/O space */ +#define FPCIE_COMMAND_MEMORY 0x2 /* Enable response in Memory space */ +#define FPCIE_COMMAND_MASTER 0x4 /* Enable bus mastering */ +#define FPCIE_COMMAND_SPECIAL 0x8 /* Enable response to special cycles */ +#define FPCIE_COMMAND_INVALIDATE 0x10 /* Use memory write and invalidate */ +#define FPCIE_COMMAND_VGA_PALETTE 0x20 /* Enable palette snooping */ +#define FPCIE_COMMAND_PARITY 0x40 /* Enable parity checking */ +#define FPCIE_COMMAND_WAIT 0x80 /* Enable address/data stepping */ +#define FPCIE_COMMAND_SERR 0x100 /* Enable SERR */ +#define FPCIE_COMMAND_FAST_BACK 0x200 /* Enable back-to-back writes */ + +#define FPCIE_HEADER_TYPE_REG 0x0e /* 8 bits */ + + +#define FPCIE_REVISION_ID_REG 0x08 /* Revision ID */ +#define FPCIE_CLASS_PROG_REG 0x09 /* Reg. Level Programming Interface */ +#define FPCIE_CLASS_DEVICE_REG 0x0a /* Device class */ +#define FPCIE_CLASS_CODE_REG 0x0b /* Device class code */ + + +#define FPCIE_PREF_MEMORY_BASE_REG 0x24 /* Prefetchable memory range behind */ +#define FPCIE_PREF_MEMORY_LIMIT_REG 0x26 +#define FPCIE_PREF_LIMIT_UPPER32_REG 0x2c +#define FPCIE_PREF_RANGE_TYPE_MASK 0x0f +#define FPCIE_PREF_RANGE_TYPE_32 0x00 +#define FPCIE_PREF_RANGE_TYPE_64 0x01 +#define FPCIE_PREF_RANGE_MASK ~0x0f + + +#define FPCI_CLASS_BRIDGE_PCI 0x0604 +#define FPCI_CLASS_BRIDGE_CARDBUS 0x0607 +#define FPCI_CLASS_PROCESSOR_POWERPC 0x0b20 +#define FPCI_CLASS_DISPLAY_VGA 0x0300 + + + +#define FPCIE_CFG_FUN_NOT_IMP_MASK 0xFFFF +#define FPCIE_CFG_HEADER_TYPE_MASK 0x007F0000 + + +/* + * Base addresses specify locations in memory or I/O space. + * Decoded size can be determined by writing a value of + * 0xffffffff to the register, and reading it back. Only + * 1 bits are decoded. + */ +#define FPCIE_BASE_ADDRESS_0 0x10 /* 32 bits */ +#define FPCIE_BASE_ADDRESS_1 0x14 /* 32 bits [htype 0,1 only] */ +#define FPCIE_BASE_ADDRESS_2 0x18 /* 32 bits [htype 0 only] */ +#define FPCIE_BASE_ADDRESS_3 0x1c /* 32 bits */ +#define FPCIE_BASE_ADDRESS_4 0x20 /* 32 bits */ +#define FPCIE_BASE_ADDRESS_5 0x24 /* 32 bits */ +#define FPCIE_BASE_ADDRESS_SPACE 0x01 /* 0 = memory, 1 = I/O */ +#define FPCIE_BASE_ADDRESS_SPACE_IO 0x01 +#define FPCIE_BASE_ADDRESS_SPACE_MEMORY 0x00 +#define FPCIE_BASE_ADDRESS_MEM_TYPE_MASK 0x06 +#define FPCIE_BASE_ADDRESS_MEM_TYPE_32 0x00 /* 32 bit address */ +#define FPCIE_BASE_ADDRESS_MEM_TYPE_1M 0x02 /* Below 1M [obsolete] */ +#define FPCIE_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */ +#define FPCIE_BASE_ADDRESS_MEM_PREFETCH 0x08 /* prefetchable? */ +#define FPCIE_BASE_ADDRESS_MEM_MASK (~0x0fULL) +#define FPCIE_BASE_ADDRESS_IO_MASK (~0x03ULL) +#define FPCIE_EP_MIN_APERTURE 128 + +/* BAR control values applicable to both Endpoint Function and Root Complex */ +#define FPCIE_LM_BAR_CFG_CTRL_DISABLED 0x0 +#define FPCIE_LM_BAR_CFG_CTRL_IO_32BITS 0x1 +#define FPCIE_LM_BAR_CFG_CTRL_MEM_32BITS 0x4 +#define FPCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_32BITS 0x5 +#define FPCIE_LM_BAR_CFG_CTRL_MEM_64BITS 0x6 +#define FPCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_64BITS 0x7 + + +/* Header type 0 (normal devices) */ +#define FPCIE_CARDBUS_CIS 0x28 +#define FPCIE_SUBSYSTEM_VENDOR_ID 0x2c +#define FPCIE_SUBSYSTEM_ID 0x2e +#define FPCIE_ROM_ADDRESS 0x30 /* Bits 31..11 are address, 10..1 reserved */ +#define FPCIE_ROM_ADDRESS_ENABLE 0x01 +#define FPCIE_ROM_ADDRESS_MASK (~0x7ffULL) + + +#define FPCIE_BASE_CLASS_DISPLAY 0x03 +#define FPCIE_CLASS_DISPLAY_VGA 0x0300 +#define FPCIE_CLASS_DISPLAY_XGA 0x0301 +#define FPCIE_CLASS_DISPLAY_3D 0x0302 +#define FPCIE_CLASS_DISPLAY_OTHER 0x0380 + +/* 0x34 same as for htype 0 */ +#define FPCIE_CAPABILITY_LIST 0x34 /* Offset of first capability list entry */ +#define FPCIE_ECAP_START 0x100 /* offset of first extend capability list entry */ + +/* 0x35-0x3b is reserved */ +#define FPCIE_ROM_ADDRESS1 0x38 /* Same as PCI_ROM_ADDRESS, but for htype 1 */ +#define FPCIE_BRIDGE_CONTROL_REG 0x3e + + +#define FPCI_CLASS_SUB_CODE 0x0a /* Device sub-class code */ +#define FPCI_CLASS_SUB_CODE_TOO_OLD_NOTVGA 0x00 +#define FPCI_CLASS_SUB_CODE_TOO_OLD_VGA 0x01 +#define FPCI_CLASS_SUB_CODE_STORAGE_SCSI 0x00 +#define FPCI_CLASS_SUB_CODE_STORAGE_IDE 0x01 +#define FPCI_CLASS_SUB_CODE_STORAGE_FLOPPY 0x02 +#define FPCI_CLASS_SUB_CODE_STORAGE_IPIBUS 0x03 +#define FPCI_CLASS_SUB_CODE_STORAGE_RAID 0x04 +#define FPCI_CLASS_SUB_CODE_STORAGE_ATA 0x05 +#define FPCI_CLASS_SUB_CODE_STORAGE_SATA 0x06 +#define FPCI_CLASS_SUB_CODE_STORAGE_SAS 0x07 +#define FPCI_CLASS_SUB_CODE_STORAGE_OTHER 0x80 +#define FPCI_CLASS_SUB_CODE_NETWORK_ETHERNET 0x00 +#define FPCI_CLASS_SUB_CODE_NETWORK_TOKENRING 0x01 +#define FPCI_CLASS_SUB_CODE_NETWORK_FDDI 0x02 +#define FPCI_CLASS_SUB_CODE_NETWORK_ATM 0x03 +#define FPCI_CLASS_SUB_CODE_NETWORK_ISDN 0x04 +#define FPCI_CLASS_SUB_CODE_NETWORK_WORLDFIP 0x05 +#define FPCI_CLASS_SUB_CODE_NETWORK_PICMG 0x06 +#define FPCI_CLASS_SUB_CODE_NETWORK_OTHER 0x80 +#define FPCI_CLASS_SUB_CODE_DISPLAY_VGA 0x00 +#define FPCI_CLASS_SUB_CODE_DISPLAY_XGA 0x01 +#define FPCI_CLASS_SUB_CODE_DISPLAY_3D 0x02 +#define FPCI_CLASS_SUB_CODE_DISPLAY_OTHER 0x80 +#define FPCI_CLASS_SUB_CODE_MULTIMEDIA_VIDEO 0x00 +#define FPCI_CLASS_SUB_CODE_MULTIMEDIA_AUDIO 0x01 +#define FPCI_CLASS_SUB_CODE_MULTIMEDIA_PHONE 0x02 +#define FPCI_CLASS_SUB_CODE_MULTIMEDIA_OTHER 0x80 +#define FPCI_CLASS_SUB_CODE_MEMORY_RAM 0x00 +#define FPCI_CLASS_SUB_CODE_MEMORY_FLASH 0x01 +#define FPCI_CLASS_SUB_CODE_MEMORY_OTHER 0x80 +#define FPCI_CLASS_SUB_CODE_BRIDGE_HOST 0x00 +#define FPCI_CLASS_SUB_CODE_BRIDGE_ISA 0x01 +#define FPCI_CLASS_SUB_CODE_BRIDGE_EISA 0x02 +#define FPCI_CLASS_SUB_CODE_BRIDGE_MCA 0x03 +#define FPCI_CLASS_SUB_CODE_BRIDGE_PCI 0x04 +#define FPCI_CLASS_SUB_CODE_BRIDGE_PCMCIA 0x05 +#define FPCI_CLASS_SUB_CODE_BRIDGE_NUBUS 0x06 +#define FPCI_CLASS_SUB_CODE_BRIDGE_CARDBUS 0x07 +#define FPCI_CLASS_SUB_CODE_BRIDGE_RACEWAY 0x08 +#define FPCI_CLASS_SUB_CODE_BRIDGE_SEMI_PCI 0x09 +#define FPCI_CLASS_SUB_CODE_BRIDGE_INFINIBAND 0x0A +#define FPCI_CLASS_SUB_CODE_BRIDGE_OTHER 0x80 +#define FPCI_CLASS_SUB_CODE_COMM_SERIAL 0x00 +#define FPCI_CLASS_SUB_CODE_COMM_PARALLEL 0x01 +#define FPCI_CLASS_SUB_CODE_COMM_MULTIPORT 0x02 +#define FPCI_CLASS_SUB_CODE_COMM_MODEM 0x03 +#define FPCI_CLASS_SUB_CODE_COMM_GPIB 0x04 +#define FPCI_CLASS_SUB_CODE_COMM_SMARTCARD 0x05 +#define FPCI_CLASS_SUB_CODE_COMM_OTHER 0x80 +#define FPCI_CLASS_SUB_CODE_PERIPHERAL_PIC 0x00 +#define FPCI_CLASS_SUB_CODE_PERIPHERAL_DMA 0x01 +#define FPCI_CLASS_SUB_CODE_PERIPHERAL_TIMER 0x02 +#define FPCI_CLASS_SUB_CODE_PERIPHERAL_RTC 0x03 +#define FPCI_CLASS_SUB_CODE_PERIPHERAL_HOTPLUG 0x04 +#define FPCI_CLASS_SUB_CODE_PERIPHERAL_SD 0x05 +#define FPCI_CLASS_SUB_CODE_PERIPHERAL_OTHER 0x80 +#define FPCI_CLASS_SUB_CODE_INPUT_KEYBOARD 0x00 +#define FPCI_CLASS_SUB_CODE_INPUT_DIGITIZER 0x01 +#define FPCI_CLASS_SUB_CODE_INPUT_MOUSE 0x02 +#define FPCI_CLASS_SUB_CODE_INPUT_SCANNER 0x03 +#define FPCI_CLASS_SUB_CODE_INPUT_GAMEPORT 0x04 +#define FPCI_CLASS_SUB_CODE_INPUT_OTHER 0x80 +#define FPCI_CLASS_SUB_CODE_DOCKING_GENERIC 0x00 +#define FPCI_CLASS_SUB_CODE_DOCKING_OTHER 0x80 +#define FPCI_CLASS_SUB_CODE_PROCESSOR_386 0x00 +#define FPCI_CLASS_SUB_CODE_PROCESSOR_486 0x01 +#define FPCI_CLASS_SUB_CODE_PROCESSOR_PENTIUM 0x02 +#define FPCI_CLASS_SUB_CODE_PROCESSOR_ALPHA 0x10 +#define FPCI_CLASS_SUB_CODE_PROCESSOR_POWERPC 0x20 +#define FPCI_CLASS_SUB_CODE_PROCESSOR_MIPS 0x30 +#define FPCI_CLASS_SUB_CODE_PROCESSOR_COPROC 0x40 +#define FPCI_CLASS_SUB_CODE_SERIAL_1394 0x00 +#define FPCI_CLASS_SUB_CODE_SERIAL_ACCESSBUS 0x01 +#define FPCI_CLASS_SUB_CODE_SERIAL_SSA 0x02 +#define FPCI_CLASS_SUB_CODE_SERIAL_USB 0x03 +#define FPCI_CLASS_SUB_CODE_SERIAL_FIBRECHAN 0x04 +#define FPCI_CLASS_SUB_CODE_SERIAL_SMBUS 0x05 +#define FPCI_CLASS_SUB_CODE_SERIAL_INFINIBAND 0x06 +#define FPCI_CLASS_SUB_CODE_SERIAL_IPMI 0x07 +#define FPCI_CLASS_SUB_CODE_SERIAL_SERCOS 0x08 +#define FPCI_CLASS_SUB_CODE_SERIAL_CANBUS 0x09 +#define FPCI_CLASS_SUB_CODE_WIRELESS_IRDA 0x00 +#define FPCI_CLASS_SUB_CODE_WIRELESS_IR 0x01 +#define FPCI_CLASS_SUB_CODE_WIRELESS_RF 0x10 +#define FPCI_CLASS_SUB_CODE_WIRELESS_BLUETOOTH 0x11 +#define FPCI_CLASS_SUB_CODE_WIRELESS_BROADBAND 0x12 +#define FPCI_CLASS_SUB_CODE_WIRELESS_80211A 0x20 +#define FPCI_CLASS_SUB_CODE_WIRELESS_80211B 0x21 +#define FPCI_CLASS_SUB_CODE_WIRELESS_OTHER 0x80 +#define FPCI_CLASS_SUB_CODE_I2O_V1_0 0x00 +#define FPCI_CLASS_SUB_CODE_SATELLITE_TV 0x01 +#define FPCI_CLASS_SUB_CODE_SATELLITE_AUDIO 0x02 +#define FPCI_CLASS_SUB_CODE_SATELLITE_VOICE 0x03 +#define FPCI_CLASS_SUB_CODE_SATELLITE_DATA 0x04 +#define FPCI_CLASS_SUB_CODE_CRYPTO_NETWORK 0x00 +#define FPCI_CLASS_SUB_CODE_CRYPTO_ENTERTAINMENT 0x10 +#define FPCI_CLASS_SUB_CODE_CRYPTO_OTHER 0x80 +#define FPCI_CLASS_SUB_CODE_DATA_DPIO 0x00 +#define FPCI_CLASS_SUB_CODE_DATA_PERFCNTR 0x01 +#define FPCI_CLASS_SUB_CODE_DATA_COMMSYNC 0x10 +#define FPCI_CLASS_SUB_CODE_DATA_MGMT 0x20 +#define FPCI_CLASS_SUB_CODE_DATA_OTHER 0x80 + + +/* Header type 2 (CardBus bridges) */ +#define FPCI_CB_CAPABILITY_LIST 0x14 +/* 0x15 reserved */ +#define FPCI_CB_SEC_STATUS 0x16 /* Secondary status */ +#define FPCI_CB_PRIMARY_BUS 0x18 /* PCI bus number */ +#define FPCI_CB_CARD_BUS 0x19 /* CardBus bus number */ +#define FPCI_CB_SUBORDINATE_BUS 0x1a /* Subordinate bus number */ +#define FPCI_CB_LATENCY_TIMER 0x1b /* CardBus latency timer */ +#define FPCI_CB_MEMORY_BASE_0 0x1c +#define FPCI_CB_MEMORY_LIMIT_0 0x20 +#define FPCI_CB_MEMORY_BASE_1 0x24 +#define FPCI_CB_MEMORY_LIMIT_1 0x28 +#define FPCI_CB_IO_BASE_0 0x2c +#define FPCI_CB_IO_BASE_0_HI 0x2e +#define FPCI_CB_IO_LIMIT_0 0x30 +#define FPCI_CB_IO_LIMIT_0_HI 0x32 +#define FPCI_CB_IO_BASE_1 0x34 +#define FPCI_CB_IO_BASE_1_HI 0x36 +#define FPCI_CB_IO_LIMIT_1 0x38 +#define FPCI_CB_IO_LIMIT_1_HI 0x3a +#define FPCI_CB_IO_RANGE_MASK ~0x03 +/* 0x3c-0x3d are same as for htype 0 */ +#define FPCI_CB_BRIDGE_CONTROL 0x3e +#define FPCI_CB_BRIDGE_CTL_PARITY 0x01 /* Similar to standard bridge control register */ +#define FPCI_CB_BRIDGE_CTL_SERR 0x02 +#define FPCI_CB_BRIDGE_CTL_ISA 0x04 +#define FPCI_CB_BRIDGE_CTL_VGA 0x08 +#define FPCI_CB_BRIDGE_CTL_MASTER_ABORT 0x20 +#define FPCI_CB_BRIDGE_CTL_CB_RESET 0x40 /* CardBus reset */ +#define FPCI_CB_BRIDGE_CTL_16BIT_INT 0x80 /* Enable interrupt for 16-bit cards */ +#define FPCI_CB_BRIDGE_CTL_PREFETCH_MEM0 0x100 /* Prefetch enable for both memory regions */ +#define FPCI_CB_BRIDGE_CTL_PREFETCH_MEM1 0x200 +#define FPCI_CB_BRIDGE_CTL_POST_WRITES 0x400 +#define FPCI_CB_SUBSYSTEM_VENDOR_ID 0x40 +#define FPCI_CB_SUBSYSTEM_ID 0x42 +#define FPCI_CB_LEGACY_MODE_BASE 0x44 /* 16-bit PC Card legacy mode base address (ExCa) */ +/* 0x48-0x7f reserved */ + + +/* Capability lists */ + +#define FPCI_CAP_LIST_ID 0 /* Capability ID */ +#define FPCI_CAP_ID_PM 0x01 /* Power Management */ +#define FPCI_CAP_ID_AGP 0x02 /* Accelerated Graphics Port */ +#define FPCI_CAP_ID_VPD 0x03 /* Vital Product Data */ +#define FPCI_CAP_ID_SLOTID 0x04 /* Slot Identification */ +#define FPCI_CAP_ID_MSI 0x05 /* Message Signalled Interrupts */ +#define FPCI_CAP_ID_CHSWP 0x06 /* CompactPCI HotSwap */ +#define FPCI_CAP_ID_PCIX 0x07 /* PCI-X */ +#define FPCI_CAP_ID_HT 0x08 /* HyperTransport */ +#define FPCI_CAP_ID_VNDR 0x09 /* Vendor-Specific */ +#define FPCI_CAP_ID_DBG 0x0A /* Debug port */ +#define FPCI_CAP_ID_CCRC 0x0B /* CompactPCI Central Resource Control */ +#define FPCI_CAP_ID_SHPC 0x0C /* PCI Standard Hot-Plug Controller */ +#define FPCI_CAP_ID_SSVID 0x0D /* Bridge subsystem vendor/device ID */ +#define FPCI_CAP_ID_AGP3 0x0E /* AGP Target PCI-PCI bridge */ +#define FPCI_CAP_ID_SECDEV 0x0F /* Secure Device */ +#define FPCI_CAP_ID_EXP 0x10 /* PCI Express */ +#define FPCI_CAP_ID_MSIX 0x11 /* MSI-X */ +#define FPCI_CAP_ID_SATA 0x12 /* SATA Data/Index Conf. */ +#define FPCI_CAP_ID_AF 0x13 /* PCI Advanced Features */ +#define FPCI_CAP_ID_EA 0x14 /* PCI Enhanced Allocation */ +#define FPCI_CAP_ID_MAX PCI_CAP_ID_EA + +/* Extended Capabilities (PCI-X 2.0 and Express) */ +//#define PCI_EXT_CAP_ID(header) (header & 0x0000ffff) +//#define PCI_EXT_CAP_VER(header) ((header >> 16) & 0xf) +//#define PCI_EXT_CAP_NEXT(header) ((header >> 20) & 0xffc) + +#define FPCI_EXT_CAP_ID_ERR 0x01 /* Advanced Error Reporting */ +#define FPCI_EXT_CAP_ID_VC 0x02 /* Virtual Channel Capability */ +#define FPCI_EXT_CAP_ID_DSN 0x03 /* Device Serial Number */ +#define FPCI_EXT_CAP_ID_PWR 0x04 /* Power Budgeting */ +#define FPCI_EXT_CAP_ID_RCLD 0x05 /* Root Complex Link Declaration */ +#define FPCI_EXT_CAP_ID_RCILC 0x06 /* Root Complex Internal Link Control */ +#define FPCI_EXT_CAP_ID_RCEC 0x07 /* Root Complex Event Collector */ +#define FPCI_EXT_CAP_ID_MFVC 0x08 /* Multi-Function VC Capability */ +#define FPCI_EXT_CAP_ID_VC9 0x09 /* same as _VC */ +#define FPCI_EXT_CAP_ID_RCRB 0x0A /* Root Complex RB? */ +#define FPCI_EXT_CAP_ID_VNDR 0x0B /* Vendor-Specific */ +#define FPCI_EXT_CAP_ID_CAC 0x0C /* Config Access - obsolete */ +#define FPCI_EXT_CAP_ID_ACS 0x0D /* Access Control Services */ +#define FPCI_EXT_CAP_ID_ARI 0x0E /* Alternate Routing ID */ +#define FPCI_EXT_CAP_ID_ATS 0x0F /* Address Translation Services */ +#define FPCI_EXT_CAP_ID_SRIOV 0x10 /* Single Root I/O Virtualization */ +#define FPCI_EXT_CAP_ID_MRIOV 0x11 /* Multi Root I/O Virtualization */ +#define FPCI_EXT_CAP_ID_MCAST 0x12 /* Multicast */ +#define FPCI_EXT_CAP_ID_PRI 0x13 /* Page Request Interface */ +#define FPCI_EXT_CAP_ID_AMD_XXX 0x14 /* Reserved for AMD */ +#define FPCI_EXT_CAP_ID_REBAR 0x15 /* Resizable BAR */ +#define FPCI_EXT_CAP_ID_DPA 0x16 /* Dynamic Power Allocation */ +#define FPCI_EXT_CAP_ID_TPH 0x17 /* TPH Requester */ +#define FPCI_EXT_CAP_ID_LTR 0x18 /* Latency Tolerance Reporting */ +#define FPCI_EXT_CAP_ID_SECPCI 0x19 /* Secondary PCIe Capability */ +#define FPCI_EXT_CAP_ID_PMUX 0x1A /* Protocol Multiplexing */ +#define FPCI_EXT_CAP_ID_PASID 0x1B /* Process Address Space ID */ +#define FPCI_EXT_CAP_ID_DPC 0x1D /* Downstream Port Containment */ +#define FPCI_EXT_CAP_ID_L1SS 0x1E /* L1 PM Substates */ +#define FPCI_EXT_CAP_ID_PTM 0x1F /* Precision Time Measurement */ +#define FPCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PTM + +/* ARI capability */ +#define FPCIE_CAPABILITY_BASE_OFFSET 0x100 +#define FPCIE_CAPABILITY_ID_SRIOV_CONTROL_ARI_HIERARCHY 0x10 +#define FPCIE_CAPABILITY_DEVICE_CAPABILITIES_2_OFFSET 0x24 +#define FPCIE_CAPABILITY_DEVICE_CAPABILITIES_2_ARI_FORWARDING 0x20 +#define FPCIE_CAPABILITY_DEVICE_CONTROL_2_OFFSET 0x28 +#define FPCIE_CAPABILITY_DEVICE_CONTROL_2_ARI_FORWARDING 0x20 + + + +/* + * Address Translation Registers + */ +#define FPCIE_AT_BASE FPCIE_REG_OUTBOUND_R0_PATR0_OFFSET +/* + * Local Management Registers + */ +#define FPCIE_LM_BASE 0x2000 + +#define FPCIE_CFG_HEADER_O_TYPE 0x0000 + +/* Endpoint Function BAR Inbound PCIe to AXI Address Translation Register */ +#define FPCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar) \ + (FPCIE_AT_BASE + 0x0840 + (fn)*0x0040 + (bar)*0x0008) +#define FPCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar) \ + (FPCIE_AT_BASE + 0x0844 + (fn)*0x0040 + (bar)*0x0008) + +/* Endpoint Function f BAR b Configuration Registers */ +#define FPCIE_LM_EP_FUNC_BAR_CFG0(fn) \ + (FPCIE_LM_BASE + 0x0240 + (fn)*0x0008) +#define FPCIE_LM_EP_FUNC_BAR_CFG1(fn) \ + (FPCIE_LM_BASE + 0x0244 + (fn)*0x0008) + +#define FPCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b) \ + (GENMASK(4, 0) << ((b)*8)) +#define FPCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE(b, a) \ + (((a) << ((b)*8)) & FPCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b)) +#define FPCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b) \ + (GENMASK(7, 5) << ((b)*8)) +#define FPCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL(b, c) \ + (((c) << ((b)*8 + 5)) & FPCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b)) + +#define FPCIE_REG_EP_C0_PREF_BASE_LIMIT_OFFSET_GET(config_addr, peu_num) \ + (u32)(config_addr + FPCIE_REG_EP_C0_PREF_BASE_LIMIT_OFFSET + ((peu_num + 3) % 3) * 16) + +#define FPCIE_REG_EP_C0_MEM_BASE_LIMIT_OFFSET_GET(config_addr, peu_num) \ + (u32)(config_addr + FPCIE_REG_EP_C0_MEM_BASE_LIMIT_OFFSET + ((peu_num + 3) % 3) * 16) + +#define FPCIE_BAR_MEM_TYPE_64 1 +#define FPCIE_BAR_MEM_TYPE_32 0 +#define FPCIE_PRIMARY_BUS_REG 0x18 + +#ifdef __cplusplus +} +#endif + +#endif // ! diff --git a/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_config.c b/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_config.c new file mode 100644 index 0000000000..1fc5f4ad6a --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_config.c @@ -0,0 +1,98 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fpcie_config.c + * Date: 2022-02-10 14:55:11 + * LastEditTime: 2022-02-18 08:57:30 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "fpcie.h" +#include "fpcie_hw.h" + + +/***************************** Include Files *********************************/ + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/** + * @name: FPcieMiscIrqEnable + * @msg: 开启PCIE 子系统中对应中断源的 misc 中断 + * @param {FPcie} *instance_p + * @param {fsize_t} peu_num + */ +void FPcieMiscIrqEnable(FPcie *instance_p, fsize_t peu_num) +{ + u64 config_address; + u32 reg_value; + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + FASSERT(peu_num <= FPCIE_PEU1_C2); + + if (peu_num < FPCIE_PEU1_C0) + { + config_address = instance_p->config.peu0_config_address; + } + else + { + config_address = instance_p->config.peu1_config_address; + peu_num -= FPCIE_PEU1_C0; + } + + reg_value = FPCIE_READREG(config_address, FPCIE_REG_MISC_INT_ENALBE_OFFSET); + FPCIE_WRITEREG(config_address, FPCIE_REG_MISC_INT_ENALBE_OFFSET, (reg_value | (1 << peu_num))); +} + + + +/** + * @name: FPcieMiscIrqDisable + * @msg: 关闭PCIE 子系统中对应中断的 misc 中断 + * @param {FPcie} *instance_p is a pointer to the FPcie instance. + * @param {fsize_t} peu_num is pci-e unit controller selection + */ +void FPcieMiscIrqDisable(FPcie *instance_p, fsize_t peu_num) +{ + uintptr_t config_address; + u32 reg_value; + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + FASSERT(peu_num <= FPCIE_PEU1_C2); + + if (peu_num < FPCIE_PEU1_C0) + { + config_address = instance_p->config.peu0_config_address; + } + else + { + config_address = instance_p->config.peu1_config_address; + peu_num -= FPCIE_PEU1_C0; + } + + reg_value = FPCIE_READREG(config_address, FPCIE_REG_MISC_INT_ENALBE_OFFSET); + FPCIE_WRITEREG(config_address, FPCIE_REG_MISC_INT_ENALBE_OFFSET, (reg_value & ~(1 << peu_num))); +} + + diff --git a/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_dma.c b/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_dma.c new file mode 100644 index 0000000000..721e8d2d25 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_dma.c @@ -0,0 +1,167 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fpcie_dma.c + * Date: 2022-02-10 14:55:11 + * LastEditTime: 2022-02-18 08:57:38 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#include "fpcie_hw.h" +#include "fpcie_dma.h" +#include "fpcie.h" +#include "ftypes.h" +#include "fcache.h" +#include "fkernel.h" + +/***************************** Include Files *********************************/ + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +#include "fdebug.h" +#define FPCIE_DMA_DEBUG_TAG "FPCIE_DMA" +#define FPCIE_DMA_ERROR(format, ...) FT_DEBUG_PRINT_E(FPCIE_DMA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FPCIE_DMA_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(FPCIE_DMA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FPCIE_DMA_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(FPCIE_DMA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FPCIE_DMA_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(FPCIE_DMA_DEBUG_TAG, format, ##__VA_ARGS__) + +/************************** Function Prototypes ******************************/ + +/** + * @name: FPcieDmaDescSet + * @msg: PCIE DMA descriptor group packet + * @param {uintptr} axi_addr is memory address + * @param {uintptr} bar_addr is Base Address Register value + * @param {u32} length want to send byte length + * @param {struct FPcieDmaDescriptor *} desc Desc is the descriptor to be configured + * @param {struct FPcieDmaDescriptor *} next_desc is the next descriptor that needs to be sent + * @return {FError} + */ +FError FPcieDmaDescSet(uintptr axi_addr, + uintptr bar_addr, + u32 length, + struct FPcieDmaDescriptor *desc, + struct FPcieDmaDescriptor *next_desc) +{ + /* 设置内存地址 */ + desc->axi_base_address = axi_addr; + desc->axi_address_phase_controls = 0x00; + + /* 设置pcie空间地址 */ + desc->pcie_base_address = bar_addr; + desc->pcie_tlp_header_attributes = 0x01; + + /* 设置数据长度, 设置dma完成中断 */ + desc->transfer_control = length; + + desc->axi_bus_status = 0x00; + desc->pcie_bus_status = 0x00; + desc->channel_status = 0x00; + + if (next_desc != NULL) + { + /* 使能链表模式 */ + desc->transfer_control &= ~(BIT(24)); + desc->transfer_control |= BIT(29); + + /* 设置下一级链表地址 */ + desc->next_descriptor = (uintptr)next_desc; + } + else + { + desc->transfer_control |= BIT(24); + desc->transfer_control &= ~(BIT(29)); + desc->next_descriptor = 0; + } + + return 0; +} + +/** + * @name: FPcieDmaRead + * @msg: Pcie reads through dma + * @param {uintptr} bar_addr is Base Address Register value + * @param {FPcieDmaDescriptor} *desc is first address of the receive descriptor + */ +void FPcieDmaRead(uintptr bar_address, struct FPcieDmaDescriptor *desc) +{ + /* The enable channel is interrupted */ + FPCIE_WRITEREG(bar_address, FPCIE_REG_DMA_INT_ENABLE_OFFSET, FPCIE_CTRL_DMA_INT_ENABLE_CH0_DONE_MASK | FPCIE_CTRL_DMA_INT_ENABLE_CH0_ERR_MASK); + + FPCIE_WRITEREG(bar_address, FPCIE_REG_DMA_CH0_SP_L_OFFSET, (u32)((uintptr)desc & 0xffffffffU)); +#ifdef __aarch64__ + FPCIE_WRITEREG(bar_address, FPCIE_REG_DMA_CH0_SP_H_OFFSET, (u32)(((uintptr)desc >> 32) & 0xffffffffU)); +#else + FPCIE_WRITEREG(bar_address, FPCIE_REG_DMA_CH0_SP_H_OFFSET, 0); +#endif + + FPCIE_WRITEREG(bar_address, FPCIE_REG_DMA_CH0_CTRL_OFFSET, FPCIE_CTRL_DMA_CH0_CTRL_GO_MASK); +} + +/** + * @name: FPcieDmaWrite + * @msg: Pcie writes through dma + * @param {uintptr} bar_address is Base Address Register value + * @param {FPcieDmaDescriptor} *desc is first address of the send descriptor + */ + +void FPcieDmaWrite(uintptr bar_address, struct FPcieDmaDescriptor *desc) +{ + /* The enable channel is interrupted */ + FPCIE_WRITEREG(bar_address, FPCIE_REG_DMA_INT_ENABLE_OFFSET, FPCIE_CTRL_DMA_INT_ENABLE_CH1_DONE_MASK | FPCIE_CTRL_DMA_INT_ENABLE_CH1_ERR_MASK); + + FPCIE_WRITEREG(bar_address, FPCIE_REG_DMA_CH1_SP_L_OFFSET, (u32)((uintptr)desc & 0xffffffffU)); +#ifdef __aarch64__ + FPCIE_WRITEREG(bar_address, FPCIE_REG_DMA_CH1_SP_H_OFFSET, (u32)(((uintptr)desc >> 32) & 0xffffffffU)); +#else + FPCIE_WRITEREG(bar_address, FPCIE_REG_DMA_CH1_SP_H_OFFSET, 0); +#endif + FPCIE_WRITEREG(bar_address, FPCIE_REG_DMA_CH1_CTRL_OFFSET, FPCIE_CTRL_DMA_CH1_CTRL_GO_MASK | FPCIE_CTRL_DMA_CH1_CTRL_OBNOTIB_MASK); +} + +/** + * @name: FPcieDmaPollDone + * @msg: Polling waits for DMA to complete + * @param {FPcieDmaDescriptor *} desc is the current need to wait for dma to complete + * @param {u32} wait_cnt is the count that needs to wait to end + * @return FError + */ +FError FPcieDmaPollDone(struct FPcieDmaDescriptor *desc, u32 wait_cnt) +{ + FPCIE_DMA_DEBUG_I("desc axi_bus_status :[0x%02x]", desc->axi_bus_status); + FPCIE_DMA_DEBUG_I("desc pcie_bus_status:[0x%02x]", desc->pcie_bus_status); + FPCIE_DMA_DEBUG_I("desc channel_status :[0x%02x]", desc->channel_status); + + while (wait_cnt > 0) + { + if (desc->channel_status == 0x1) + { + FPCIE_DMA_DEBUG_I("dma channel transfer done "); + return FT_SUCCESS; + } + wait_cnt--; + } + + return FPCIE_ERR_TIMEOUT; +} diff --git a/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_dma.h b/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_dma.h new file mode 100644 index 0000000000..6e0d54bdba --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_dma.h @@ -0,0 +1,77 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fpcie_dma.h + * Date: 2022-02-10 14:55:11 + * LastEditTime: 2022-02-18 08:57:51 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef DRIVERS_FPCIE_DMA_H +#define DRIVERS_FPCIE_DMA_H + +#ifdef __cplusplus +extern "C" +{ +#endif +#include "ftypes.h" + + +/***************************** Include Files *********************************/ + +/************************** Constant Definitions *****************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define DMA_XFER_START (0x01 << 0) +#define DMA_READ (0x00 << 1) +#define DMA_WRITE (0x01 << 1) + +#define DMA_CHANNEL_READ_DONE (0x01 << 0) +#define DMA_CHANNEL_WRITE_DONE (0x01 << 1) +#define DMA_CHANNEL_READ_ERROR (0x01 << 8) +#define DMA_CHANNEL_WRITE_ERROR (0x01 << 9) + +#define DMA_CHANNEL_READ_DONE_ENABLE (0x01 << 0) +#define DMA_CHANNEL_WRITE_DONE_ENABLE (0x01 << 1) +#define DMA_CHANNEL_READ_ERROR_ENABLE (0x01 << 8) +#define DMA_CHANNEL_WRITE_ERROR_ENABLE (0x01 << 9) +/************************** Function Prototypes ******************************/ + + +/**************************** Type Definitions *******************************/ + +struct FPcieDmaDescriptor +{ + volatile u64 axi_base_address; /* 0x00 */ + volatile u32 axi_address_phase_controls; /* 0x08 */ + volatile u64 pcie_base_address; /* 0x12 */ + volatile u64 pcie_tlp_header_attributes; /* 0x20 */ + volatile u32 transfer_control; /* 0x28 */ + volatile u8 axi_bus_status; /* 0x32 */ + volatile u8 pcie_bus_status; /* 0x33 */ + volatile u8 channel_status; /* 0x34 */ + volatile u8 reserve; /* 0x35 */ + volatile u64 next_descriptor; /* 0x36 */ +} __attribute__((packed)) __attribute__((aligned(128))); + +#ifdef __cplusplus +} +#endif + +#endif // ! \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_ep.c b/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_ep.c new file mode 100644 index 0000000000..cad384e18c --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_ep.c @@ -0,0 +1,216 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fpcie_ep.c + * Date: 2022-02-10 14:55:11 + * LastEditTime: 2022-02-18 08:57:59 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "fpcie.h" +#include "fpcie_hw.h" +#include "ftypes.h" +#include "fpcie_common.h" +#include +#include +#include "fkernel.h" +#include "fdebug.h" + + +/***************************** Include Files *********************************/ + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +#define FPCIE_EP_DEBUG_TAG "FPCIE_EP" +#define FPCIE_EP_ERROR(format, ...) FT_DEBUG_PRINT_E(FPCIE_EP_DEBUG_TAG, format, ##__VA_ARGS__) +#define FPCIE_EP_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(FPCIE_EP_DEBUG_TAG, format, ##__VA_ARGS__) +#define FPCIE_EP_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(FPCIE_EP_DEBUG_TAG, format, ##__VA_ARGS__) +#define FPCIE_EP_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(FPCIE_EP_DEBUG_TAG, format, ##__VA_ARGS__) + +/************************** Function Prototypes ******************************/ + + + +int FPcieEpSetBar(FPcie *instance_p, u32 peu_num, u32 bar_num, u64 bar_mem_addr, u64 bar_mem_size, int flags) +{ + u32 addr0, addr1, reg, cfg, b, aperture, ctrl; + u64 sz; + uintptr_t base_addr; + FASSERT(instance_p != NULL); + base_addr = *(uintptr_t *)(&instance_p->config.control_c0_address + peu_num); + + /* BAR size is 2^(aperture + 7) */ + sz = max(bar_mem_size, (u64)FPCIE_EP_MIN_APERTURE); + /* + * roundup_pow_of_two() returns an unsigned long, which is not suited + * for 64bit values. + */ + + sz = 1ULL << fls(sz - 1); + aperture = log2(sz) - 7; /* 128B -> 0, 256B -> 1, 512B -> 2, ... */ + + if ((flags & FPCIE_BASE_ADDRESS_SPACE) == FPCIE_BASE_ADDRESS_SPACE) + { + ctrl = FPCIE_LM_BAR_CFG_CTRL_IO_32BITS; + } + else + { + boolean is_prefetch = !!(flags & FPCIE_BASE_ADDRESS_MEM_PREFETCH); + boolean is_64bits = sz > SZ_2G; + + if (is_64bits && (bar_num & 1)) + return FPCIE_ERR_INVALID_PARAM; + + if (is_64bits && !(flags & FPCIE_BASE_ADDRESS_MEM_TYPE_64)) + flags |= FPCIE_BASE_ADDRESS_MEM_TYPE_64; + + if (is_64bits && is_prefetch) + ctrl = FPCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_64BITS; + else if (is_prefetch) + ctrl = FPCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_32BITS; + else if (is_64bits) + ctrl = FPCIE_LM_BAR_CFG_CTRL_MEM_64BITS; + else + ctrl = FPCIE_LM_BAR_CFG_CTRL_MEM_32BITS; + } + + addr0 = LOWER_32_BITS(bar_mem_addr); + addr1 = UPPER_32_BITS(bar_mem_addr); + + FPCIE_WRITEREG(base_addr, FPCIE_AT_IB_EP_FUNC_BAR_ADDR0(0, bar_num), addr0); + FPCIE_WRITEREG(base_addr, FPCIE_AT_IB_EP_FUNC_BAR_ADDR1(0, bar_num), addr1); + + if (bar_num < FPCIE_BAR_4) + { + reg = FPCIE_LM_EP_FUNC_BAR_CFG0(0); + b = reg; + } + else + { + reg = FPCIE_LM_EP_FUNC_BAR_CFG0(0); + b = reg - FPCIE_BAR_4; + } + + cfg = FPCIE_READREG(base_addr, reg); + cfg &= ~(FPCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b) | + FPCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b)); + cfg |= (FPCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE(b, aperture) | + FPCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL(b, ctrl)); + + FPCIE_WRITEREG(base_addr, reg, cfg); + + return FT_SUCCESS; +} + +int FPcieEpCleanBar(FPcie *instance_p, u32 peu_num, u32 bar_num) +{ + u32 reg, cfg, b, ctrl; + uintptr_t base_addr; + base_addr = *(uintptr_t *)(&instance_p->config.control_c0_address + peu_num); + + if (bar_num < FPCIE_BAR_4) + { + reg = FPCIE_LM_EP_FUNC_BAR_CFG0(0); + b = bar_num; + } + else + { + reg = FPCIE_LM_EP_FUNC_BAR_CFG1(0); + b = bar_num - FPCIE_BAR_4; + } + + ctrl = FPCIE_LM_BAR_CFG_CTRL_DISABLED; + cfg = FPCIE_READREG(base_addr, reg); + cfg &= ~(FPCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b) | + FPCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b)); + cfg |= FPCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL(b, ctrl); + FPCIE_WRITEREG(base_addr, reg, cfg); + + return FT_SUCCESS; +} + +static int GetOneInData(u64 data) +{ + int n = 63; + while (!(data & GENMASK_ULL(63, 63))) + { + n--; + data <<= 1; + } + + return n; +} + +int FPcieEpMapAddr(FPcie *instance_p, u32 peu_num, u64 phy_addr, u64 pcie_addr, u64 size) +{ + u32 addr0, addr1; + u64 pcie_addr_limit; + u64 reg; + uintptr_t config_address; + uintptr_t control_address; + + FASSERT(instance_p != NULL); + + if (peu_num < FPCIE_PEU1_C0) + { + config_address = instance_p->config.peu0_config_address; + } + else + { + config_address = instance_p->config.peu1_config_address; + } + + control_address = *(uintptr_t *)(&instance_p->config.control_c0_address + peu_num); + pcie_addr_limit = pcie_addr + size; + + if (phy_addr < 0x1000000000) + { + addr0 = ((pcie_addr >> 16) & FPCIE_C0_PREF_BASE_MASK) | (pcie_addr_limit & FPCIE_C0_PREF_LIMIT_MASK); + reg = FPCIE_REG_EP_C0_PREF_BASE_LIMIT_OFFSET_GET(config_address, peu_num); + FPCIE_WRITEREG(reg, 0, addr0); + } + else + { + addr0 = (((pcie_addr & 0xFFFFFFFF) >> 15) & 0xFFF0) | (pcie_addr_limit & 0xFFF00000); + addr1 = ((pcie_addr >> 32) & 0xFF) | ((pcie_addr_limit >> 24) & 0xFF00); + reg = FPCIE_REG_EP_C0_MEM_BASE_LIMIT_OFFSET_GET(config_address, peu_num); + FPCIE_WRITEREG(reg, 0, addr0); + FPCIE_WRITEREG(reg + 4, 0, addr1); + } + + addr0 = (phy_addr & 0xFFFFFF00) | (GetOneInData(size - 1) & 0x3F); + addr1 = (phy_addr >> 32); + FPCIE_WRITEREG(control_address, FPCIE_REG_OUTBOUND_R0_PATR0_OFFSET, addr0); + FPCIE_WRITEREG(control_address, FPCIE_REG_OUTBOUND_R0_PATR1_OFFSET, addr1); + + FPCIE_WRITEREG(control_address, FPCIE_REG_OUTBOUND_R0_PHDR0_OFFSET, 2); + FPCIE_WRITEREG(control_address, FPCIE_REG_OUTBOUND_R0_PHDR1_OFFSET, 0); + FPCIE_WRITEREG(control_address, FPCIE_REG_OUTBOUND_R0_PHDR2_OFFSET, 0); + + addr0 = (pcie_addr & 0xFFFFFF00) | (GetOneInData(size - 1) & 0x3F); + addr1 = (pcie_addr >> 32); + FPCIE_WRITEREG(control_address, FPCIE_REG_OUTBOUND_R0_ARBA0_OFFSET, addr0); + FPCIE_WRITEREG(control_address, FPCIE_REG_OUTBOUND_R0_ARBA1_OFFSET, addr1); + + return FT_SUCCESS; +} diff --git a/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_g.c b/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_g.c new file mode 100644 index 0000000000..2f05d51696 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_g.c @@ -0,0 +1,74 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fpcie_g.c + * Date: 2022-02-10 14:55:11 + * LastEditTime: 2022-02-18 08:58:07 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#include "fpcie.h" +#include "fpcie_hw.h" +#include "fparameters.h" +#include "sdkconfig.h" + + +FPcieConfig FPcieConfigTable[FT_PCIE_NUM] = +{ + { + .instance_id = FT_PCIE0_ID, /* Id of device*/ + .irq_num = FT_PCIE0_MISC_IRQ_NUM, // Irq number + .ecam = FT_PCI_CONFIG_BASEADDR, /* The Memory way */ + .peu0_config_address = FT_PCI_EU0_CONFIG_BASEADDR, + .peu1_config_address = FT_PCI_EU1_CONFIG_BASEADDR, + .control_c0_address = FT_PCI_EU0_C0_CONTROL_BASEADDR, + .control_c1_address = FT_PCI_EU0_C1_CONTROL_BASEADDR, + .control_c2_address = FT_PCI_EU0_C2_CONTROL_BASEADDR, + .control_c3_address = FT_PCI_EU1_C0_CONTROL_BASEADDR, + .control_c4_address = FT_PCI_EU1_C1_CONTROL_BASEADDR, + .control_c5_address = FT_PCI_EU1_C2_CONTROL_BASEADDR, +#ifdef FT_PCI_INTX_EOI + .intx_peux_stat_address = + { + [0] = FT_PCI_INTX_PEU0_STAT, + [1] = FT_PCI_INTX_PEU1_STAT, + }, + .intx_control_eux_cx_address = + { + [0] = FT_PCI_INTX_EU0_C0_CONTROL, + [1] = FT_PCI_INTX_EU0_C1_CONTROL, + [2] = FT_PCI_INTX_EU0_C2_CONTROL, + [3] = FT_PCI_INTX_EU1_C0_CONTROL, + [4] = FT_PCI_INTX_EU1_C1_CONTROL, + [5] = FT_PCI_INTX_EU1_C2_CONTROL, + }, +#endif + .io_base_addr = FT_PCI_IO_CONFIG_BASEADDR, + .io_size = FT_PCI_IO_CONFIG_REG_LENGTH, + .npmem_base_addr = FT_PCI_MEM32_BASEADDR, + .npmem_size = FT_PCI_MEM32_REG_LENGTH, + .pmem_base_addr = FT_PCI_MEM64_BASEADDR, /* Prefetchable memory */ + .pmem_size = FT_PCI_MEM64_REG_LENGTH, + .inta_irq_num = FT_PCI_INTA_IRQ_NUM, + .intb_irq_num = FT_PCI_INTB_IRQ_NUM, + .intc_irq_num = FT_PCI_INTC_IRQ_NUM, + .intd_irq_num = FT_PCI_INTD_IRQ_NUM, + .need_skip = FT_PCI_NEED_SKIP + } +}; + diff --git a/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_hw.c b/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_hw.c new file mode 100644 index 0000000000..3850bab7c5 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_hw.c @@ -0,0 +1,312 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fpcie_hw.c + * Date: 2022-02-10 14:55:11 + * LastEditTime: 2022-02-18 08:58:12 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "fpcie_hw.h" +#include "fpcie.h" +#include "fpcie_common.h" +#include "fparameters.h" + +/***************************** Include Files *********************************/ + +/************************** Constant Definitions *****************************/ +/* Access sizes for PCI reads and writes */ +enum FPcieSize +{ + FPCIE_SIZE_8, + FPCIE_SIZE_16, + FPCIE_SIZE_32, +}; + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + + +/** + * @name: FPcieEcamConfigAddress + * @msg: 提供一个可用于ECAM 访问机制的地址 + * @param {FPcie} *instance_p is a pointer to the FPcie instance. + * @param {s32} bdf {Bus Device function} + * @param {u32} offset 配置空间中的偏移量 + * @param {void **} addrp 用于存放输出有效访问空间的地址 + * @return FError + */ +static FError FPcieEcamConfigAddress(uintptr addr, s32 bdf, u32 offset, void **addrp) +{ + u32 bus_no = FPCIE_BUS(bdf); + u32 dev_no = FPCIE_DEV(bdf); + u32 vendor = 0; + s32 bdf_parent ; + uintptr ecam_addr = addr; + addr += FPCIE_BUS(bdf) << 20; + addr += FPCIE_DEV(bdf) << 15; + addr += FPCIE_FUNC(bdf) << 12; + + + bdf_parent = FPCIE_BDF(bus_no - 1, 0, 0); + vendor = FPCIE_READREG(addr, FPCIE_CFG_ID_REG) ; + + if ((bus_no > 0) && (dev_no > 0)) + { + if ((FPCIE_READREG_BYTE(addr, FPCIE_HEADER_TYPE_REG) & 0x7f) != FPCIE_HEADER_TYPE_BRIDGE) + { + if (vendor != 0x1d17) + { + return FPCIE_NEED_SKIP; + } + + if (FPcieSkipDevice(ecam_addr, bdf_parent)) + { + return FPCIE_NEED_SKIP; + } + } + } + + addr += offset ; + *addrp = (void *)addr ; + return FT_SUCCESS; +} + + + +/** + * @name: FPcieSkipDevice + * @msg: 跳过部分无效设备 + * @param {FPcie} *instance_p is a pointer to the FPcie instance. + * @param {s32} bdf {Bus Device function} + * @return FError + */ +FError FPcieSkipDevice(uintptr ecam_addr, s32 bdf) +{ + u8 pos, id; + u16 capreg; + u8 port_type; + uintptr addr ; + addr = ecam_addr ; + addr += FPCIE_BUS(bdf) << 20; + addr += FPCIE_DEV(bdf) << 15; + addr += FPCIE_FUNC(bdf) << 12; + + pos = 0x34 ; /* Capabilites Pointer */ + while (1) + { + pos = FPCIE_READREG_BYTE(addr, pos) ; + if (pos < 0x40)/* 超过Capability Pointer所代表的空间offset最大范围 */ + break; + pos &= ~3 ; /* offset 第两位对齐 */ + id = FPCIE_READREG_BYTE(addr, pos) ; /* PCI Express Cap ID */ + if (id == 0xff) + { + break ; + } + + if (id == 0x10) /* 找到 PCIe设备的cap structure */ + { + capreg = FPCIE_READREG(addr, pos + 2) ; + port_type = (capreg >> 4) & 0xf ; /* Device/Port type */ + if ((port_type == 0x5) && (FPCIE_DEV(bdf) != 0)) + { + return FPCIE_NEED_SKIP ; + } + else + { + return FT_SUCCESS; + } + + } + pos + 1 ; + } + + return FT_SUCCESS; +} + + + + +static s32 FPcieGetFf(enum FPcieSize size) +{ + switch (size) + { + case FPCIE_SIZE_8: + return 0xff; + case FPCIE_SIZE_16: + return 0xffff; + default: + return 0xffffffff; + } +} + +/** + * @name: FPcieEcamReadConfig8bit + * @msg: 基于ECAM机制读取配置空间中偏移量的值 + * @param {FPcie} *instance_p is a pointer to the FPcie instance. + * @param {s32} bdf {Bus Device function} + * @param {u32} offset 配置空间中的偏移量 + * @param {u8} *value_p pointer store date available in the offset + * @return FError + */ + +void FPcieEcamReadConfig8bit(uintptr ecam_addr, s32 bdf, u32 offset, u8 *value_p) +{ + uintptr addr ; + + if (FPcieEcamConfigAddress(ecam_addr, bdf, offset, (void *)&addr) != FT_SUCCESS) + { + *value_p = FPcieGetFf(FPCIE_SIZE_8) ; + return ; + } + + + *value_p = FPCIE_READREG_BYTE(addr, 0); + + return ; +} + +/** + * @name: FPcieEcamReadConfig16bit + * @msg: 基于ECAM机制读取配置空间中偏移量的值 + * @param {FPcie} *instance_p is a pointer to the FPcie instance. + * @param {s32} bdf {Bus Device function} + * @param {u32} offset 配置空间中的偏移量 + * @param {u16} *value_p pointer store date available in the offset + * @return FError + */ + +void FPcieEcamReadConfig16bit(uintptr ecam_addr, s32 bdf, u32 offset, u16 *value_p) +{ + uintptr addr ; + + if (FPcieEcamConfigAddress(ecam_addr, bdf, offset, (void *)&addr) != FT_SUCCESS) + { + *value_p = FPcieGetFf(FPCIE_SIZE_16) ; + return ; + } + + + *value_p = FPCIE_READREG_SHORT(addr, 0); + + return ; +} + +/** + * @name: FPcieEcamReadConfig32bit + * @msg: 基于ECAM机制读取配置空间中偏移量的值 + * @param {FPcie} *instance_p is a pointer to the FPcie instance. + * @param {s32} bdf {Bus Device function} + * @param {u32} offset 配置空间中的偏移量 + * @param {u32} *value_p pointer store date available in the offset + * @return FError + */ +void FPcieEcamReadConfig32bit(uintptr ecam_addr, s32 bdf, u32 offset, u32 *value_p) +{ + uintptr addr ; + + if (FPcieEcamConfigAddress(ecam_addr, bdf, offset, (void *)&addr) != FT_SUCCESS) + { + *value_p = FPcieGetFf(FPCIE_SIZE_32) ; + return ; + } + + + *value_p = FPCIE_READREG(addr, 0); + + return ; +} + + +/** + * @name: FPcieEcamWriteConfig8bit + * @msg: 基于ECAM机制写入配置空间中偏移量的值 + * @param {FPcie} *instance_p is a pointer to the FPcie instance. + * @param {s32} bdf {Bus Device function} + * @param {u32} offset 配置空间中的偏移量 + * @param {u8} value to be written on to the offset +* @return FError + */ + +void FPcieEcamWriteConfig8bit(uintptr ecam_addr, s32 bdf, u32 offset, u8 value) +{ + uintptr addr ; + + if (FPcieEcamConfigAddress(ecam_addr, bdf, offset, (void *)&addr) != FT_SUCCESS) + { + return ; + } + + FPCIE_WRITEREG_BYTE(addr, 0, value) ; + + return ; +} + +/** + * @name: FPcieEcamWriteConfig16bit + * @msg: 基于ECAM机制写入配置空间中偏移量的值 + * @param {FPcie} *instance_p is a pointer to the FPcie instance. + * @param {s32} bdf {Bus Device function} + * @param {u32} offset 配置空间中的偏移量 + * @param {u16} value to be written on to the offset +* @return FError + */ + +void FPcieEcamWriteConfig16bit(uintptr ecam_addr, s32 bdf, u32 offset, u16 value) +{ + uintptr addr ; + + if (FPcieEcamConfigAddress(ecam_addr, bdf, offset, (void *)&addr) != FT_SUCCESS) + { + return ; + } + + FPCIE_WRITEREG_SHORT(addr, 0, value) ; + + return ; +} + + +/** + * @name: FPcieEcamWriteConfig32bit + * @msg: 基于ECAM机制写入配置空间中偏移量的值 + * @param {FPcie} *instance_p is a pointer to the FPcie instance. + * @param {s32} bdf {Bus Device function} + * @param {u32} offset 配置空间中的偏移量 + * @param {u32} value to be written on to the offset +* @return FError + */ + +void FPcieEcamWriteConfig32bit(uintptr ecam_addr, s32 bdf, u32 offset, u32 value) +{ + uintptr addr ; + + if (FPcieEcamConfigAddress(ecam_addr, bdf, offset, (void *)&addr) != FT_SUCCESS) + { + return ; + } + + FPCIE_WRITEREG(addr, 0, value) ; + + return ; +} diff --git a/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_hw.h b/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_hw.h new file mode 100644 index 0000000000..b6c1c1906c --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_hw.h @@ -0,0 +1,327 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fpcie_hw.h + * Date: 2022-02-10 14:55:11 + * LastEditTime: 2022-02-18 08:58:22 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#ifndef DRIVERS_FPCIE_HW_H +#define DRIVERS_FPCIE_HW_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ftypes.h" +#include "fio.h" +#include "ferror_code.h" + +/***************************** Include Files *********************************/ + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/* config register */ +#define FPCIE_REG_MISC_INT_STATE_OFFSET 0x00000008U /* 杂散中断状态寄存器 */ +#define FPCIE_REG_MISC_INT_ENALBE_OFFSET 0x0000000CU /* 杂散中断是能寄存器 */ +#define FPCIE_REG_MSI_ENABLE_OFFSET 0x000000200U /* MSI 中断使能寄存器 */ +#define FPCIE_REG_MSI_UP32_ADDR_OFFSET 0x000000208U /* 匹配 MSI 的高 32 位地址寄存器 */ +#define FPCIE_REG_MSI_LOW32_ADDR_OFFSET 0x00000020CU /* 匹配 MSI 的低 32 位地址寄存器 */ +#define FPCIE_REG_MSI_SPI_ENABLE_OFFSET 0x000000608U /* MSI 转 SPI 的使能寄存器 */ +#define FPCIE_REG_MSI_SPI_DATA_OFFSET 0x00000060CU /* MSI 转 SPI 后的 ID 和数据信息寄存器 */ +#define FPCIE_REG_EP_C0_PREF_BASE_LIMIT_OFFSET 0x000000A30U /* EP 模式下 PIO 请求译码到 C0 控制器 的可预取空间的 BASE 和 LIMIT 域 */ +#define FPCIE_REG_EP_C0_PREF_BASE_LIMIT_UP32_OFFSET 0x000000A34U /* EP 模式下 PIO 请求译码到 C0 控制器的可预取空间的高 32 位 BASE 和 sLIMIT */ +#define FPCIE_REG_EP_C0_MEM_BASE_LIMIT_OFFSET 0x000000A38U /* EP 模式下 PIO 请求译码到 C1 控制器的存储空间的 BASE 和 LIMIT 域 */ +#define FPCIE_REG_EP_C1_PREF_BASE_LIMIT_OFFSET 0x000000A40U +#define FPCIE_REG_EP_C1_PREF_BASE_LIMIT_UP32_OFFSET 0x000000A44U +#define FPCIE_REG_EP_C1_MEM_BASE_LIMIT_OFFSET 0x000000A48U +#define FPCIE_REG_EP_C2_PREF_BASE_LIMIT_OFFSET 0x000000A50U +#define FPCIE_REG_EP_C2_PREF_BASE_LIMIT_UP32_OFFSET 0x000000A54U +#define FPCIE_REG_EP_C2_MEM_BASE_LIMIT_OFFSET 0x000000A58U + +/* Controler register */ +#define FPCIE_REG_MSI_LOW_ADDRESS_OFFSET 0x94U /* MSI 事务的写地址低 32 位 */ +#define FPCIE_REG_MSI_HIGH_ADDRESS_OFFSET 0x98U /* MSI 事务的写地址高 32 位 */ +#define FPCIE_REG_MSI_DATA_OFFSET 0x9CU /* MSI 事务携带的数据信息 */ +#define FPCIE_REG_OUTBOUND_R0_PATR0_OFFSET 0x8000U /* 控制器输出方向上 region 0 的转换后地址低 32 位 */ +#define FPCIE_REG_OUTBOUND_R0_PATR1_OFFSET 0x8004U /* 控制器输出方向上 region 0 的转换后地址高 32 位 */ +#define FPCIE_REG_OUTBOUND_R0_PHDR0_OFFSET 0x8008U /* 控制器输出方向上 region 0 的转换描述符[31:0]位 */ +#define FPCIE_REG_OUTBOUND_R0_PHDR1_OFFSET 0x800CU /* 控制器输出方向上 region 0 的转换描述符[63:31]位 */ +#define FPCIE_REG_OUTBOUND_R0_PHDR2_OFFSET 0x8010U /* 控制器输出方向上 region 0 的转换描述符[95:64]位 */ +#define FPCIE_REG_OUTBOUND_R0_ARBA0_OFFSET 0x8018U /* 控制器输出方向上 region 0 的转换前的地址低 32 位 */ +#define FPCIE_REG_OUTBOUND_R0_ARBA1_OFFSET 0x801CU /* 控制器输出方向上 region 0 的转换前的地址高 32 位 */ +#define FPCIE_REG_F0_B0_ATR_L_OFFSET 0x8840U /* 控制器 FUNC 0 BAR 0 地址转换寄存器低 32 位 */ +#define FPCIE_REG_F0_B0_ATR_H_OFFSET 0x8844U /* 控制器 FUNC 0 BAR 0 地址转换寄存器高 32 位 */ +#define FPCIE_REG_F0_B2_ATR_L_OFFSET 0x8850U /* 控制器 FUNC 0 BAR 2 地址转换寄存器低 32 位 */ +#define FPCIE_REG_F0_B2_ATR_H_OFFSET 0x8854U /* 控制器 FUNC 0 BAR 2 地址转换寄存器高 32 位 */ +#define FPCIE_REG_DMA_CH0_CTRL_OFFSET 0xC000U /* DMA channel 0 的控制器寄存器 */ +#define FPCIE_REG_DMA_CH0_SP_L_OFFSET 0xC004U /* DMA channel 0 描述符存储的首地址低 32 位寄存器 */ +#define FPCIE_REG_DMA_CH0_SP_H_OFFSET 0xC008U /* DMA channel 0 描述符存储的首地址高 32 位寄存器 */ +#define FPCIE_REG_DMA_CH1_CTRL_OFFSET 0xC014U /* DMA channel 1 的控制器寄存器 */ +#define FPCIE_REG_DMA_CH1_SP_L_OFFSET 0xC018U /* DMA channel 1 描述符存储的首地址低 32 位寄存器 */ +#define FPCIE_REG_DMA_CH1_SP_H_OFFSET 0xC01CU /* DMA channel 1 描述符存储的首地址高 32 位寄存器 */ +#define FPCIE_REG_DMA_INT_STATUS_OFFSET 0xC0A0U /* DMA 中断状态寄存器 */ +#define FPCIE_REG_DMA_INT_ENABLE_OFFSET 0xC0A4U /* DMA 使能寄存器 */ + +/* REG_MISC_INT_STATE */ +#define FPCIE_MISC_STATE_C0_DMA_INT_MASK 0x1U /* c0 dma 中断 */ +#define FPCIE_MISC_STATE_C0_LOCAL_INT_MASK 0x2U /* c0 本地中断 */ +#define FPCIE_MISC_STATE_C0_POWER_STATE_CHANGE_MASK 0x4U /* c0 电源状态变化 */ +#define FPCIE_MISC_STATE_C1_DMA_INT_MASK 0x100U /* c1 dma 中断 */ +#define FPCIE_MISC_STATE_C1_LOCAL_INT_MASK 0x200U /* c1 本地中断 */ +#define FPCIE_MISC_STATE_C1_POWER_STATE_CHANGE_MASK 0x400U /* c1 电源状态变化 */ +#define FPCIE_MISC_STATE_C2_DMA_INT_MASK 0x1000U /* c2 dma 中断 */ +#define FPCIE_MISC_STATE_C2_LOCAL_INT_MASK 0x2000U /* c2 本地中断 */ +#define FPCIE_MISC_STATE_C2_POWER_STATE_CHANGE_MASK 0x4000U /* c2 电源状态变化 */ + +/* REG_MISC_INT_ENALBE */ +#define FPCIE_MISC_ENALBE_C0_MISC_INT_EN_MASK 0x1U /* c0 杂散中断使能 */ +#define FPCIE_MISC_ENALBE_C1_MISC_INT_EN_MASK 0x2U /* c1 杂散中断使能 */ +#define FPCIE_MISC_ENALBE_C2_MISC_INT_EN_MASK 0x4U /* c2 杂散中断使能 */ + +/* REG_MSI_ENABLE */ +#define FPCIE_MSI_EN_MASK 0x1U /* MSI 中断使能 */ + +/* REG_MSI_UP32_ADDR */ +#define FPCIE_MSI64_HI_ADDR_OFFSET 0xFFFFFFFFU /* MSI64 高位地址 */ + +/* REG_MSI_LOW32_ADDR */ +#define FPCIE_MSI64_LO_ADDR_MASK 0xFFFF0000U /* MSI64 低位地址 */ + +/* REG_MSI_SPI_ENABLE */ +#define FPCIE_MSI_DATA_MASK 0xFFFFU /* msi 中断的数据 */ +#define FPCIE_MSI_DEVICE_ID_MASK 0xFFFF0000U /* msi 中断的设备 id */ + +/* REG_EP_C0_PREF_BASE_LIMIT */ +#define FPCIE_C0_PREF_BASE_MASK 0xfff0U /* 可预取存储空间基址低位 */ +#define FPCIE_C0_PREF_LIMIT_MASK 0xfff00000U /* 可预取存储空间上限低位 */ + +/* REG_EP_C0_PREF_BASE_LIMIT_UP32 */ +#define FPCIE_C0_PREF_BASE_UP32_MASK 0xFFU /* 可预取存储空间基址高位 */ +#define FPCIE_C0_PREF_LIMIT_UP32_MASK 0xFF00U /* 可预取存储空间上限高位 */ + +/* REG_EP_C1_PREF_BASE_LIMIT */ +#define FPCIE_C1_PREF_BASE_MASK 0xfff0U /* 可预取存储空间基址低位 */ +#define FPCIE_C1_PREF_LIMIT_MASK 0xfff00000U /* 可预取存储空间上限低位 */ + +/* REG_EP_C1_PREF_BASE_LIMIT_UP32 */ +#define FPCIE_C1_PREF_BASE_UP32_MASK 0xFFU /* 可预取存储空间基址高位 */ +#define FPCIE_C1_PREF_LIMIT_UP32_MASK 0xFF00U /* 可预取存储空间上限高位 */ + +/* Controler register */ +/* REG_MSI_LOW_ADDRESS */ +#define FPCIE_CTRL_MSI_LOW_ADDR_MASK 0xFFFFFFFCU /* MSI 事务的写地址低位 */ + +/* REG_MSI_HIGH_ADDRESS */ +#define FPCIE_CTRL_MSI_HIGH_ADDR_MASK 0xFFFFFFFFU /* MSI 事务的写地址高 32 位 */ + +/* REG_MSI_DATA */ +#define FPCIE_CTRL_MESSAGE_DATA_MASK 0xffffU /* MSI 事务携带的数据信息 */ + +/* REG_OUTBOUND_R0_PATR0 */ +#define FPCIE_CTRL_OUTBOUND_R0_PATR0_R0_NUM_BITS_MASK 0x1fU /* 配置可通过的 AXI 域地址,例如配置为 N,那么N+1 位地址可通过。 */ +#define FPCIE_CTRL_OUTBOUND_R0_PATR0_ADDR_BITS_MASK 0xffffff00U /* 控制器输出方向 region 0 转换后的地址[31:8]位 */ + +/* REG_OUTBOUND_R0_PATR1 */ +#define FPCIE_CTRL_OUTBOUND_R0_PATR1_ADDR_BITS_MASK 0xffffffffU /* 控制器输出方向 region 0 转换后的地址[63:32]位 */ + +/* REG_OUTBOUND_R0_PHDR0 */ +#define FPCIE_CTRL_OUTBOUND_R0_PHDR0_DESCRIPTOR_MASK 0xffffffffU /* 控制器输出方向 region 0 的描述符[31:0]位 */ + +/* REG_OUTBOUND_R0_PHDR1 */ +#define FPCIE_CTRL_OUTBOUND_R0_PHDR1_DESCRIPTOR_MASK 0xffffffffU /* 控制器输出方向 region 0 的描述符[63:32]位 */ + +/* REG_OUTBOUND_R0_PHDR2 */ +#define FPCIE_CTRL_OUTBOUND_R0_PHDR2_DESCRIPTOR_MASK 0x1fffU /* 控制器输出方向 region 0 的描述符[76:64]位 */ + +/* REG_OUTBOUND_R0_ARBA0 */ +#define FPCIE_CTRL_OUTBOUND_R0_ARBA0_LOWER_MASK 0x3f /* 配置AXI域匹配地址时的mask位,例如配置为M,那么 M+1 位地址在匹配时不做比较。 */ +#define FPCIE_CTRL_OUTBOUND_R0_ARBA0_ADDR_MASK 0xFFFFFFF0U /* 控制器输出方向 region 0 转换前的地址[31:8]位 */ + +/* REG_OUTBOUND_R0_ARBA1 */ +#define FPCIE_CTRL_OUTBOUND_R0_ARBA1_ADDR_MASK 0xffffffffU /* 控制器输出方向 region 0 转换前的地址[63:32]位 */ + +/* REG_F0_B0_ATR_L */ +#define FPCIE_CTRL_F0_B0_ATR_L_ADDR_MASK 0xffffffffU /* 控制器 FUNC 0 BAR 0 地址转换寄存器低 32 位 */ + +/* REG_F0_B0_ATR_H */ +#define FPCIE_CTRL_F0_B0_ATR_H_ADDR_MASK 0xffffffffU /* 控制器 FUNC 0 BAR 0 地址转换寄存器高 32 位 */ + +/* REG_F0_B2_ATR_L */ +#define FPCIE_CTRL_F0_B2_ATR_L_ADDR_MASK 0xffffffffU /* 控制器 FUNC 0 BAR 2 地址转换寄存器低 32 位 */ + +/* REG_F0_B2_ATR_H */ +#define FPCIE_CTRL_F0_B2_ATR_H_ADDR_MASK 0xffffffffU /* 控制器 FUNC 0 BAR 2 地址转换寄存器高 32 位 */ + +/* REG_DMA_CH0_CTRL */ +#define FPCIE_CTRL_DMA_CH0_CTRL_GO_MASK 0x1U /* DMA channel 0 点火位,开始传输 */ +#define FPCIE_CTRL_DMA_CH0_CTRL_OBNOTIB_MASK 0x2U /* 配置 DMA channel 0 是读操作还是写操作 */ + +/* REG_DMA_CH0_SP_L */ +#define FPCIE_CTRL_DMA_CH0_SP_L_START_MASK 0xFFFFFFFFU /* 保存 channel 0 描述符的内存地址低 32 位 */ + +/* REG_DMA_CH0_SP_H */ +#define FPCIE_CTRL_DMA_CH0_SP_H_START_MASK 0xFFFFFFFFU /* 保存 channel 0 描述符的内存地址高 32 位 */ + +/* REG_DMA_CH1_CTRL */ +#define FPCIE_CTRL_DMA_CH1_CTRL_GO_MASK 0x1U /* DMA channel 1 点火位,开始传输 */ +#define FPCIE_CTRL_DMA_CH1_CTRL_OBNOTIB_MASK 0x2U /* 配置 DMA channel 1 是读操作还是写操作 */ + +/* REG_DMA_CH1_SP_L */ +#define FPCIE_CTRL_DMA_CH1_SP_L_START_MASK 0xFFFFFFFFU /* 保存 channel 1 描述符的内存地址低 32 位 */ + +/* REG_DMA_CH1_SP_H */ +#define FPCIE_CTRL_DMA_CH1_SP_H_START_MASK 0xFFFFFFFFU /* 保存 channel 1 描述符的内存地址高 32 位 */ + +/* REG_DMA_INT_STATUS */ +#define FPCIE_CTRL_DMA_INT_STATUS_CH0_DONE_MASK 0x1U /* channel 0 传输完成中断 */ +#define FPCIE_CTRL_DMA_INT_STATUS_CH1_DONE_MASK 0x2U /* channel 1 传输完成中断 */ +#define FPCIE_CTRL_DMA_INT_STATUS_CH0_ERR_MASK 0x10U /* channel 0 传输出错中断 */ +#define FPCIE_CTRL_DMA_INT_STATUS_CH1_ERR_MASK 0x20U /* channel 1 传输出错中断 */ +#define FPCIE_CTRL_DMA_INT_STATUS_ALL_MASK (FPCIE_CTRL_DMA_INT_STATUS_CH0_DONE_MASK | FPCIE_CTRL_DMA_INT_STATUS_CH1_DONE_MASK | FPCIE_CTRL_DMA_INT_STATUS_CH0_ERR_MASK | FPCIE_CTRL_DMA_INT_STATUS_CH1_ERR_MASK) + +/* REG_DMA_INT_ENABLE */ +#define FPCIE_CTRL_DMA_INT_ENABLE_CH0_DONE_MASK 0x1U /* 使能 channel 0 传输完成后产生中断 */ +#define FPCIE_CTRL_DMA_INT_ENABLE_CH1_DONE_MASK 0x2U /* 使能 channel 1 传输完成后产生中断 */ +#define FPCIE_CTRL_DMA_INT_ENABLE_CH0_ERR_MASK 0x10U /* 使能 channel 0 传输出错后产生中断 */ +#define FPCIE_CTRL_DMA_INT_ENABLE_CH1_ERR_MASK 0x20U /* 使能 channel 1 传输出错后产生中断 */ +#define FPCIE_CTRL_DMA_INT_ENABLE_ALL_MASK (FPCIE_CTRL_DMA_INT_ENABLE_CH0_DONE_MASK | FPCIE_CTRL_DMA_INT_ENABLE_CH1_DONE_MASK | FPCIE_CTRL_DMA_INT_ENABLE_CH0_ERR_MASK | FPCIE_CTRL_DMA_INT_ENABLE_CH1_ERR_MASK) + +/** @name ECAM Address Register bitmaps and masks + * + * @{ + */ +#define FPCIE_ECAM_MASK 0x0FFFFFFF /**< Mask of all valid bits */ +#define FPCIE_ECAM_BUS_MASK 0x0FF00000 /**< Bus Number Mask */ +#define FPCIE_ECAM_DEV_MASK 0x000F8000 /**< Device Number Mask */ +#define FPCIE_ECAM_FUN_MASK 0x00007000 /**< Function Number Mask */ +#define FPCIE_ECAM_REG_MASK 0x00000FFC /**< Register Number Mask */ +#define FPCIE_ECAM_BYT_MASK 0x00000003 /**< Byte Address Mask */ + +#define FPCIE_ECAM_BUS_SHIFT 20 /**< Bus Number Shift Value */ +#define FPCIE_ECAM_DEV_SHIFT 15 /**< Device Number Shift Value */ +#define FPCIE_ECAM_FUN_SHIFT 12 /**< Function Number Shift Value */ +#define FPCIE_ECAM_REG_SHIFT 2 /**< Register Number Shift Value */ +#define FPCIE_ECAM_BYT_SHIFT 0 /**< Byte Offset Shift Value */ +/*@}*/ + + +#define FPCIE_BUS(d) (((d) >> 16) & 0xff) +/* + * Please note the difference in DEVFN usage in U-Boot vs Linux. U-Boot + * uses DEVFN in bits 15-8 but Linux instead expects DEVFN in bits 7-0. + * Please see the Linux header include/uapi/linux/pci.h for more details. + * This is relevant for the following macros: + * FPCIE_DEV, FPCIE_FUNC, FPCIE_DEVFN + * The U-Boot macro FPCIE_DEV is equivalent to the Linux FPCIE_SLOT version with + * the remark from above (input d in bits 15-8 instead of 7-0. + */ +#define FPCIE_DEV(d) (((d) >> 11) & 0x1f) +#define FPCIE_FUNC(d) (((d) >> 8) & 0x7) +#define FPCIE_DEVFN(d, f) ((d) << 11 | (f) << 8) + +#define FPCIE_MASK_BUS(bdf) ((bdf) & 0xffff) +#define FPCIE_ADD_BUS(bus, devfn) (((bus) << 16) | (devfn)) +#define FPCIE_BDF(b, d, f) ((b) << 16 | FPCIE_DEVFN(d, f)) +#define FPCIE_VENDEV(v, d) (((v) << 16) | (d)) +#define FPCIE_ANY_ID (~0) + +/** +* +* This macro reads the given register. +* +* @param base_addr is the base address of the device. +* @param reg_offset is the register offset to be read. +* +* @return The 32-bit value of the register +* +* @note None. +* +*****************************************************************************/ +#define FPCIE_READREG(base_addr, reg_offset) \ + FtIn32((base_addr) + (u32)(reg_offset)) + +/****************************************************************************/ +/** +* +* This macro writes the given register. +* +* @param base_addr is the base address of the device. +* @param reg_offset is the register offset to be written. +* @param data is the 32-bit value to write to the register. +* +* @return None. +* +* @note None. +* +*****************************************************************************/ +#define FPCIE_WRITEREG(base_addr, reg_offset, data) \ + FtOut32((base_addr) + (u32)(reg_offset), (u32)(data)) + +#define FPCIE_READREG_BYTE(base_addr, reg_offset) \ + FtIn8((base_addr) + (u32)(reg_offset)) + +#define FPCIE_WRITEREG_BYTE(base_addr, reg_offset, data) \ + FtOut8((base_addr) + (u32)(reg_offset), (u8)(data)) + + +#define FPCIE_READREG_SHORT(base_addr, reg_offset) \ + FtIn16((base_addr) + (u32)(reg_offset)) + +#define FPCIE_WRITEREG_SHORT(base_addr, reg_offset, data) \ + FtOut16((base_addr) + (u32)(reg_offset), (u16)(data)) + + +#define FPCIE_SETBIT(base_addr, reg_offset, data) \ + FtSetBit32((base_addr) + (u32)(reg_offset), (u32)(data)) + +#define FPCIE_CLEARBIT(base_addr, reg_offset, data) \ + FtClearBit32((base_addr) + (u32)(reg_offset), (u32)(data)) + + +/************************** Function Prototypes ******************************/ + + +void FPcieEcamReadConfig8bit(uintptr ecam_addr, s32 bdf, u32 offset, u8 *value_p); + +void FPcieEcamReadConfig16bit(uintptr ecam_addr, s32 bdf, u32 offset, u16 *value_p); + +void FPcieEcamReadConfig32bit(uintptr ecam_addr, s32 bdf, u32 offset, u32 *value_p); + + +void FPcieEcamWriteConfig8bit(uintptr ecam_addr, s32 bdf, u32 offset, u8 value); + +void FPcieEcamWriteConfig16bit(uintptr ecam_addr, s32 bdf, u32 offset, u16 value); + +void FPcieEcamWriteConfig32bit(uintptr ecam_addr, s32 bdf, u32 offset, u32 value); + +FError FPcieSkipDevice(uintptr ecam_addr, s32 bdf) ; + +#ifdef __cplusplus +} +#endif + +#endif // ! \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_misc.c b/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_misc.c new file mode 100644 index 0000000000..77340b588a --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_misc.c @@ -0,0 +1,162 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fpcie_misc.c + * Date: 2022-02-10 14:55:11 + * LastEditTime: 2022-02-18 08:59:17 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#include "fpcie.h" +#include "fpcie_hw.h" +#include "fassert.h" +#include "fdebug.h" + + +/***************************** Include Files *********************************/ + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FPCIE_INTR_DEBUG_TAG "FPCIE_INTR" +#define FPCIE_INTR_ERROR(format, ...) FT_DEBUG_PRINT_E(FPCIE_INTR_DEBUG_TAG, format, ##__VA_ARGS__) +#define FPCIE_INTR_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(FPCIE_INTR_DEBUG_TAG, format, ##__VA_ARGS__) +#define FPCIE_INTR_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(FPCIE_INTR_DEBUG_TAG, format, ##__VA_ARGS__) +/************************** Function Prototypes ******************************/ + + + +FError FPcieMiscSetHandler(FPcie *instance_p, u32 handler_type, + void *func_pointer, void *call_back_ref) +{ + FError status; + FASSERT(instance_p != NULL); + FASSERT(func_pointer != NULL); + FASSERT(instance_p->is_ready == (u32)FT_COMPONENT_IS_READY); + + switch (handler_type) + { + case FPCIE_HANDLER_DMASEND: + status = FT_SUCCESS; + instance_p->fpcie_dma_tx_cb = ((FPcieIrqCallBack)(void *)func_pointer); + instance_p->dma_tx_args = call_back_ref; + break; + case FPCIE_HANDLER_DMARECV: + status = FT_SUCCESS; + instance_p->fpcie_dma_rx_cb = ((FPcieIrqCallBack)(void *)func_pointer); + instance_p->dma_rx_args = call_back_ref; + break; + case FPCIE_HANDLER_DMASEND_ERROR: + status = FT_SUCCESS; + instance_p->fpcie_dma_tx_error_cb = ((FPcieIrqCallBack)(void *)func_pointer); + instance_p->dma_tx_error_args = call_back_ref; + break; + case FPCIE_HANDLER_DMARECV_ERROR: + status = FT_SUCCESS; + instance_p->fpcie_dma_rx_error_cb = ((FPcieIrqCallBack)(void *)func_pointer); + instance_p->dma_rx_error_args = call_back_ref; + break; + default: + status = FPCIE_ERR_INVALID_PARAM; + break; + } + return status; +} + +void FPcieMiscIrq(s32 vector, void *args) +{ + FPcie *instance_p = (FPcie *)args; + uintptr_t control_address; + u32 reg_value; + (void)vector; + FASSERT(instance_p != NULL); + + if (FPCIE_READREG(instance_p->config.peu0_config_address, FPCIE_REG_MISC_INT_STATE_OFFSET) & FPCIE_MISC_STATE_C0_DMA_INT_MASK) + { + FPCIE_INTR_DEBUG_I("PEU0 C0 DMA IRQ!"); + control_address = instance_p->config.control_c0_address; + } + else if (FPCIE_READREG(instance_p->config.peu0_config_address, FPCIE_REG_MISC_INT_STATE_OFFSET) & FPCIE_MISC_STATE_C1_DMA_INT_MASK) + { + FPCIE_INTR_DEBUG_I("PEU0 C1 DMA IRQ!"); + control_address = instance_p->config.control_c1_address; + } + else if (FPCIE_READREG(instance_p->config.peu0_config_address, FPCIE_REG_MISC_INT_STATE_OFFSET) & FPCIE_MISC_STATE_C2_DMA_INT_MASK) + { + FPCIE_INTR_DEBUG_I("PEU0 C2 DMA IRQ!"); + control_address = instance_p->config.control_c2_address; + } + else if (FPCIE_READREG(instance_p->config.peu1_config_address, FPCIE_REG_MISC_INT_STATE_OFFSET) & FPCIE_MISC_STATE_C0_DMA_INT_MASK) + { + FPCIE_INTR_DEBUG_I("PEU0 C0 DMA IRQ!"); + control_address = instance_p->config.control_c3_address; + } + else if (FPCIE_READREG(instance_p->config.peu1_config_address, FPCIE_REG_MISC_INT_STATE_OFFSET) & FPCIE_MISC_STATE_C1_DMA_INT_MASK) + { + FPCIE_INTR_DEBUG_I("PEU0 C1 DMA IRQ!"); + control_address = instance_p->config.control_c4_address; + } + else if (FPCIE_READREG(instance_p->config.peu1_config_address, FPCIE_REG_MISC_INT_STATE_OFFSET) & FPCIE_MISC_STATE_C2_DMA_INT_MASK) + { + FPCIE_INTR_DEBUG_I("PEU0 C2 DMA IRQ!"); + control_address = instance_p->config.control_c5_address; + } + + FPCIE_INTR_DEBUG_I("pcie misc irq!"); + FPCIE_INTR_DEBUG_I("pcie dma irq status : 0x%08lx", FPCIE_READREG(control_address, FPCIE_REG_DMA_INT_STATUS_OFFSET)); + + reg_value = FPCIE_READREG(control_address, FPCIE_REG_DMA_INT_STATUS_OFFSET); + + if (reg_value & FPCIE_CTRL_DMA_INT_STATUS_CH0_DONE_MASK) + { + if (instance_p->fpcie_dma_rx_cb) + { + instance_p->fpcie_dma_rx_cb(instance_p->dma_rx_args); + } + } + + if (reg_value & FPCIE_CTRL_DMA_INT_STATUS_CH1_DONE_MASK) + { + if (instance_p->fpcie_dma_tx_cb) + { + instance_p->fpcie_dma_tx_cb(instance_p->dma_tx_args); + } + } + + if (reg_value & FPCIE_CTRL_DMA_INT_STATUS_CH0_ERR_MASK) + { + if (instance_p->fpcie_dma_rx_error_cb) + { + instance_p->fpcie_dma_rx_error_cb(instance_p->dma_rx_error_args); + } + } + + if (reg_value & FPCIE_CTRL_DMA_INT_STATUS_CH1_ERR_MASK) + { + if (instance_p->fpcie_dma_tx_error_cb) + { + instance_p->fpcie_dma_tx_error_cb(instance_p->dma_tx_error_args); + } + } + + FPCIE_WRITEREG(control_address, FPCIE_REG_DMA_INT_STATUS_OFFSET, FPCIE_CTRL_DMA_INT_STATUS_ALL_MASK); +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_sinit.c b/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_sinit.c new file mode 100644 index 0000000000..f972582888 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcie_sinit.c @@ -0,0 +1,46 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fpcie_sinit.c + * Date: 2022-02-10 14:55:11 + * LastEditTime: 2022-02-18 08:59:22 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#include "fpcie.h" +#include "fparameters.h" + +extern FPcieConfig FPcieConfigTable[FT_PCIE_NUM]; + + +FPcieConfig *FPcieLookupConfig(u32 instance_id) +{ + FPcieConfig *ptr = NULL; + u32 index; + + for (index = 0; index < (u32)FT_PCIE_NUM; index++) + { + if (FPcieConfigTable[index].instance_id == instance_id) + { + ptr = &FPcieConfigTable[index]; + break; + } + } + + return (FPcieConfig *)ptr; +} diff --git a/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcir_intx.c b/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcir_intx.c new file mode 100644 index 0000000000..bcf0992e11 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcir_intx.c @@ -0,0 +1,224 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fpcir_intx.c + * Date: 2022-02-10 14:55:11 + * LastEditTime: 2022-02-18 08:59:42 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + + +/***************************** Include Files *********************************/ +#include "fpcie.h" +#include "fpcie_common.h" +#include "fpcie_hw.h" +#include "fparameters.h" +#include "fdebug.h" + + +/***************** Macros (Inline Functions) Definitions *********************/ + +#define FPCIE_INTX_DEBUG_TAG "FPCIE_INTX" +#define FPCIE_INTX_ERROR(format, ...) FT_DEBUG_PRINT_E(FPCIE_INTX_DEBUG_TAG, format, ##__VA_ARGS__) +#define FPCIE_INTX_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(FPCIE_INTX_DEBUG_TAG, format, ##__VA_ARGS__) +#define FPCIE_INTX_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(FPCIE_INTX_DEBUG_TAG, format, ##__VA_ARGS__) +#define FPCIE_INTX_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(FPCIE_INTX_DEBUG_TAG, format, ##__VA_ARGS__) + +/************************** Constant Definitions *****************************/ +#define INTA 0 +#define INTB 1 +#define INTC 2 +#define INTD 3 + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/************************** Function Prototypes ******************************/ + + +/** + * @name: FPcieIntxRegiterIrqHandler + * @msg: Use bus, device, and function to register interrupt response functions with INTX + * @param {FPcie *} instance_p is a pointer to the FPcie instance. + * @param {u32} bus is the number of the bus on which you want to register the funtion + * @param {u32} device is the number of the device on which you want to register the funtion + * @param {u32} function is the number of the function on which you want to register the funtion + * @param {FPcieIntxFun *} intx_fun_p is the pointer the user uses to register the callback function + * @return {FError} + */ +FError FPcieIntxRegiterIrqHandler(FPcie *instance_p, + u32 bdf, + FPcieIntxFun *intx_fun_p) +{ + int i; + u8 interrupt_pin, interrupt_line; + u8 header_type; + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + + /* 通过ecm 直接访问 控制寄存器 */ + FPcieEcamReadConfig8bit(instance_p->config.ecam, bdf, FPCIE_HEADER_TYPE_REG, &header_type) ; + + if (header_type == 0) + { + for (i = 0; i < instance_p->scaned_bdf_count; i++) + { + if (instance_p->scaned_bdf_array[i] == (s32)bdf) + { + break; //获取到i的值,直接跳出循环 + } + } + + /* 读出 Interrupt Pin*/ + FPcieEcamReadConfig8bit(instance_p->config.ecam, bdf, FPCIE_INTERRUPT_PIN_REG, &interrupt_pin) ; + switch (interrupt_pin) + { + case 0x1: /* INTA# */ + interrupt_line = instance_p->config.inta_irq_num ; + instance_p->inta_fun[i] = *intx_fun_p; //中断函数,写入的是pcie instance的成员,一个pcie rc只有一个中断处理函数? + instance_p->inta_fun[i].bdf = bdf; //一个中断函数对应一个bdf号 + break ; + case 0x2: /* INTB# */ + interrupt_line = instance_p->config.intb_irq_num ; + instance_p->intb_fun[i] = *intx_fun_p; + instance_p->intb_fun[i].bdf = bdf; + break ; + case 0x3: /* INTC# */ + interrupt_line = instance_p->config.intc_irq_num ; + instance_p->intc_fun[i] = *intx_fun_p; + instance_p->intc_fun[i].bdf = bdf; + break ; + case 0x4: /* INTD# */ + interrupt_line = instance_p->config.intd_irq_num ; + instance_p->intd_fun[i] = *intx_fun_p; + instance_p->intd_fun[i].bdf = bdf; + break ; + default: + FPCIE_INTX_DEBUG_E("Error interrupt pin") ; + return FPCIE_NOT_FOUND; + } + + FPcieEcamWriteConfig8bit(instance_p->config.ecam, bdf, FPCIE_INTERRUPT_LINE_REG, interrupt_line) ; + } + else + { + FPCIE_INTX_DEBUG_E("Pcie intx not match header type") ; + return FPCIE_NOT_FOUND; + } + + return FT_SUCCESS; +} + + +static void FPcieIntxCallback(FPcie *instance_p, u8 INTx_NUM) +{ + int i; + u16 status ; + /* 读取对应bdf 的status */ + for (i = 0; i < instance_p->scaned_bdf_count; i++) //轮询所有扫描到的pcie节点的interrupt status + { + FPcieEcamReadConfig16bit(instance_p->config.ecam, instance_p->scaned_bdf_array[i], FPCIE_STATUS_REG, &status) ; + if (status & 0x8) /* check intrrupt status */ + { + switch (INTx_NUM) + { + case INTA: + instance_p->inta_fun[i].IntxCallBack(instance_p->inta_fun[i].args); + break; + case INTB: + instance_p->intb_fun[i].IntxCallBack(instance_p->intb_fun[i].args); + break; + case INTC: + instance_p->intc_fun[i].IntxCallBack(instance_p->intc_fun[i].args); + break; + case INTD: + instance_p->intd_fun[i].IntxCallBack(instance_p->intd_fun[i].args); + break; + default: + printf("%s: error intx num\n", __func__); + break; + + } + } + } +} + + + +static void FPcieIntxIrqEoi(FPcie *instance_p, u32 intx_idx) +{ +#ifdef FT_PCI_INTX_EOI + u32 status = 0 ; + u32 istatus = 0, imask = 0 ; + int i ; + status = FPCIE_READREG(instance_p->config.intx_peux_stat_address[0], 0) + (FPCIE_READREG(instance_p->config.intx_peux_stat_address[1], 0) << 12); + + imask = 1 << (3 - intx_idx); + istatus = (1 << intx_idx) << 24; + for (i = 0; i < FT_PCI_INTX_CONTROL_NUM; i++, status >>= 4) + { + if (imask & status) + { + FPCIE_WRITEREG(instance_p->config.intx_control_eux_cx_address[i], 0, istatus) ; + } + } +#else + (void) instance_p; + (void) intx_idx ; +#endif + return ; + +} + +/** + * @name: FPcieIntxIrqHandler + * @msg: Intx interrupt service function of pcie + * @param {s32} vector is interrupt vector number + * @param {void} *args is Pass in a pointer to be processed + */ +void FPcieIntxIrqHandler(s32 vector, void *args) //中断响应函数 +{ + FPcie *instance_p = (FPcie *)args; + FASSERT(instance_p != NULL); + u32 *reg_data = 0; + + switch (vector) + { + case FT_PCI_INTA_IRQ_NUM: //如果响应的是INTA中断,则调用pcie_obj中INTA的中断处理函数 + FPcieIntxCallback(instance_p, INTA) ; + FPcieIntxIrqEoi(instance_p, 0) ; + break; + case FT_PCI_INTB_IRQ_NUM: + FPcieIntxCallback(instance_p, INTA) ; + FPcieIntxIrqEoi(instance_p, 1) ; + break; + case FT_PCI_INTC_IRQ_NUM: + FPcieIntxCallback(instance_p, INTA) ; + FPcieIntxIrqEoi(instance_p, 2) ; + break; + case FT_PCI_INTD_IRQ_NUM: + FPcieIntxCallback(instance_p, INTA) ; + FPcieIntxIrqEoi(instance_p, 3) ; + break; + default: + break; + } +} + + diff --git a/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcir_intx.h b/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcir_intx.h new file mode 100644 index 0000000000..d2dcbc397c --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/pcie/fpcie/fpcir_intx.h @@ -0,0 +1,47 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fpcir_intx.h + * Date: 2022-02-10 14:55:11 + * LastEditTime: 2022-02-18 08:59:47 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#ifndef DIRVERS_PCIE_FPCIE_FPCIE_INTX_H +#define DIRVERS_PCIE_FPCIE_FPCIE_INTX_H + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/************************** Function Prototypes ******************************/ + +#ifdef __cplusplus +extern "C" +{ +#endif + + +#ifdef __cplusplus +} +#endif + + +#endif // ! \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/pin/fgpio/Kconfig b/bsp/phytium/libraries/standalone/drivers/pin/fgpio/Kconfig new file mode 100644 index 0000000000..02c30d70d6 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/pin/fgpio/Kconfig @@ -0,0 +1,7 @@ + +config ENABLE_FGPIO + bool + prompt "Use FGPIO" + default n + + diff --git a/bsp/phytium/libraries/standalone/drivers/pin/fgpio/fgpio.c b/bsp/phytium/libraries/standalone/drivers/pin/fgpio/fgpio.c new file mode 100644 index 0000000000..37b5eab09c --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/pin/fgpio/fgpio.c @@ -0,0 +1,460 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgpio.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:25:29 + * Description:  This files is for GPIO user API implmentation + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2022-3-1 init commit + */ + + +/***************************** Include Files *********************************/ +#include "fdebug.h" +#include "fparameters.h" + +#include "fgpio_hw.h" +#include "fgpio.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FGPIO_DEBUG_TAG "FGPIO" +#define FGPIO_ERROR(format, ...) FT_DEBUG_PRINT_E(FGPIO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGPIO_WARN(format, ...) FT_DEBUG_PRINT_W(FGPIO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGPIO_INFO(format, ...) FT_DEBUG_PRINT_I(FGPIO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGPIO_DEBUG(format, ...) FT_DEBUG_PRINT_D(FGPIO_DEBUG_TAG, format, ##__VA_ARGS__) + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + +/*****************************************************************************/ +/** + * @name: FGpioCfgInitialize + * @msg: 初始化GPIO控制器实例 + * @return {FError} FGPIO_SUCCESS 表示初始化成功 + * @param {FGpio} *instance, GPIO控制器实例 + * @param {FGpioConfig} *config, GPIO控制器配置 + */ +FError FGpioCfgInitialize(FGpio *const instance, const FGpioConfig *const config) +{ + FASSERT(instance && config); + + if (0 == config->base_addr) + { + FGPIO_ERROR("invalid base address !!!"); + return FGPIO_ERR_INVALID_PARA; + } + + if (config != &instance->config) + { + instance->config = *config; + } + + instance->is_ready = FT_COMPONENT_IS_READY; + return FGPIO_SUCCESS; +} + +/** + * @name: FGpioDeInitialize + * @msg: 去初始化GPIO控制器实例 + * @return {*} + * @param {FGpio} *instance, GPIO控制器实例 + */ +void FGpioDeInitialize(FGpio *const instance) +{ + FASSERT(instance); + u32 port_id; + u32 pin_id; + FGpioPin *pin = NULL; + + for (port_id = FGPIO_PORT_A; port_id < FGPIO_PORT_NUM; port_id++) + { + for (pin_id = FGPIO_PIN_0; pin_id < FGPIO_PIN_NUM; pin_id++) + { + pin = instance->pins[port_id][pin_id]; + if (NULL != pin) + { + FGpioPinDeInitialize(pin); + } + } + } + + instance->is_ready = 0; + return; +} + +/** + * @name: FGpioPinInitialize + * @msg: 初始化GPIO引脚实例 + * @return {FError} FGPIO_SUCCESS 表示初始化成功 + * @param {FGpio} *instance, GPIO控制器实例 + * @param {FGpioPin} *pin_instance, GPIO引脚实例 + * @param {FGpioPinId} index, GPIO引脚索引 + */ +FError FGpioPinInitialize(FGpio *const instance, FGpioPin *const pin_instance, + const FGpioPinId index) +{ + FASSERT(instance && pin_instance); + FASSERT_MSG(index.port < FGPIO_PORT_NUM, "invalid gpio port %d", index); + FASSERT_MSG(index.pin < FGPIO_PIN_NUM, "invalid gpio pin %d", index); + + if (FT_COMPONENT_IS_READY != instance->is_ready) + { + FGPIO_ERROR("gpio instance not yet init !!!"); + return FGPIO_ERR_NOT_INIT; + } + + if (FT_COMPONENT_IS_READY == pin_instance->is_ready) + { + FGPIO_ERROR("gpio pin already inited !!!"); + return FGPIO_ERR_ALREADY_INIT; + } + + pin_instance->index = index; + instance->pins[index.port][index.pin] = pin_instance; + pin_instance->instance = instance; + pin_instance->irq_cb = NULL; + pin_instance->irq_cb_params = NULL; + pin_instance->irq_one_time = FALSE; + pin_instance->is_ready = FT_COMPONENT_IS_READY; + + return FGPIO_SUCCESS; +} + +/** + * @name: FGpioPinDeInitialize + * @msg: 去初始化GPIO引脚实例 + * @return {NONE} + * @param {FGpioPin} *pin, GPIO引脚实例 + */ +void FGpioPinDeInitialize(FGpioPin *const pin) +{ + FASSERT(pin); + FGpio *const instance = pin->instance; + + if ((NULL == instance) || (FT_COMPONENT_IS_READY != instance->is_ready) || + (FT_COMPONENT_IS_READY != pin->is_ready)) + { + FGPIO_ERROR("gpio instance not yet init !!!"); + return; + } + + if (FGPIO_DIR_INPUT == FGpioGetDirection(pin)) + FGpioSetInterruptMask(pin, FALSE); /* 关闭引脚中断 */ + + FGpioPinId index = pin->index; + FASSERT_MSG(instance->pins[index.port][index.pin] == pin, "invalid pin instance"); + instance->pins[index.port][index.pin] = NULL; + pin->instance = NULL; + pin->is_ready = 0U; + + return; +} + +/** + * @name: FGpioGetPinIrqSourceType + * @msg: 获取引脚中断的上报方式 + * @return {FGpioIrqSourceType} 引脚中断的上报方式 + * @param {FGpioPin} *pin, GPIO引脚实例 + */ +FGpioIrqSourceType FGpioGetPinIrqSourceType(FGpioPinId pin_id) +{ +#if defined(FGPIO_VERSION_1) /* FT2000-4, D2000 */ + if (FGPIO_PORT_B == pin_id.port) + { + return FGPIO_IRQ_NOT_SUPPORT; + } +#endif + + if (FGPIO_PORT_A == pin_id.port) + { +#if defined(FGPIO_VERSION_2) /* E2000 GPIO 0 ~ 5 */ + if (pin_id.ctrl <= FGPIO_WITH_PIN_IRQ) /* 0 ~ 2 中断单独上报 */ + { + return FGPIO_IRQ_BY_PIN; + } +#endif + + return FGPIO_IRQ_BY_CONTROLLER; + } + + return FGPIO_IRQ_NOT_SUPPORT; +} + +/** + * @name: FGpioReadRegDir + * @msg: 从寄存器读取GPIO组的输入输出方向 + * @return {u32} GPIO组的输入输出方向, bit[8:0]有效 + * @param {uintptr} base_addr, GPIO控制器基地址 + * @param {FGpioPortIndex} port, GPIO组, A/B + */ +static u32 FGpioReadRegDir(uintptr base_addr, const FGpioPortIndex port) +{ + u32 reg_val = 0; + + if (FGPIO_PORT_A == port) + { + reg_val = FGpioReadReg32(base_addr, FGPIO_SWPORTA_DDR_OFFSET); + } +#if defined(FGPIO_VERSION_1) /* FT2000-4, D2000 */ + else if (FGPIO_PORT_B == port) + { + reg_val = FGpioReadReg32(base_addr, FGPIO_SWPORTB_DDR_OFFSET); + } +#endif + else + { + FASSERT(0); + } + + return reg_val; +} + +/** + * @name: FGpioWriteRegDir + * @msg: 向寄存器写入GPIO组的输入输出方向 + * @return {*} + * @param {uintptr} base_addr, GPIO控制器基地址 + * @param {FGpioPortIndex} port, GPIO组, A/B + * @param {u32} reg_val, GPIO组的输入输出方向, bit[8:0]有效 + */ +static void FGpioWriteRegDir(uintptr base_addr, const FGpioPortIndex port, const u32 reg_val) +{ + if (FGPIO_PORT_A == port) + { + FGpioWriteReg32(base_addr, FGPIO_SWPORTA_DDR_OFFSET, reg_val); + } +#if defined(FGPIO_VERSION_1) /* FT2000-4, D2000 */ + else if (FGPIO_PORT_B == port) + { + FGpioWriteReg32(base_addr, FGPIO_SWPORTB_DDR_OFFSET, reg_val); + } +#endif + else + { + FASSERT(0); + } + + return; +} + +/** + * @name: FGpioSetDirection + * @msg: 设置GPIO引脚的输入输出方向 + * @return {*} + * @param {FGpioPin} *instance, GPIO控制器实例 + * @param {FGpioDirection} dir, 待设置的GPIO的方向 + * @note 初始化 GPIO 实例后使用此函数 + */ +void FGpioSetDirection(FGpioPin *const pin, FGpioDirection dir) +{ + FASSERT(pin); + FGpio *const instance = pin->instance; + FASSERT(instance); + FASSERT_MSG(instance->is_ready == FT_COMPONENT_IS_READY, "gpio instance not yet init !!!"); + u32 reg_val; + FGpioPinId index = pin->index; + uintptr base_addr = instance->config.base_addr; + + reg_val = FGpioReadRegDir(base_addr, index.port); + + if (FGPIO_DIR_INPUT == dir) + { + reg_val &= ~BIT(index.pin); /* 0-Input */ + } + else if (FGPIO_DIR_OUTPUT == dir) + { + reg_val |= BIT(index.pin); /* 1-Output */ + } + else + { + FASSERT(0); + } + + FGpioWriteRegDir(base_addr, index.port, reg_val); + return; +} + +/** + * @name: FGpioGetDirection + * @msg: 获取GPIO引脚的输入输出方向 + * @return {FGpioDirection} GPIO引脚方向 + * @param {FGpioPin} *pin, GPIO引脚实例 + * @note 初始化 GPIO 实例后使用此函数 + */ +FGpioDirection FGpioGetDirection(FGpioPin *const pin) +{ + FASSERT(pin); + FGpio *const instance = pin->instance; + FASSERT(instance); + FASSERT(instance->is_ready == FT_COMPONENT_IS_READY); + + FGpioPinId index = pin->index; + uintptr base_addr = instance->config.base_addr; + u32 reg_val = FGpioReadRegDir(base_addr, index.port); + + return (BIT(index.pin) & reg_val) ? FGPIO_DIR_OUTPUT : FGPIO_DIR_INPUT; +} + +/** + * @name: FGpioReadRegVal + * @msg: 获取GPIO组的输出寄存器值 + * @return {u32} 输出寄存器值 bit[8:0]有效 + * @param {uintptr} base_addr, GPIO控制器基地址 + * @param {FGpioPortIndex} port, GPIO组 + */ +static u32 FGpioReadRegVal(uintptr base_addr, const FGpioPortIndex port) +{ + u32 reg_val = 0; + + if (FGPIO_PORT_A == port) + { + reg_val = FGpioReadReg32(base_addr, FGPIO_SWPORTA_DR_OFFSET); + } +#if defined(FGPIO_VERSION_1) /* FT2000-4, D2000 */ + else if (FGPIO_PORT_B == port) + { + reg_val = FGpioReadReg32(base_addr, FGPIO_SWPORTB_DR_OFFSET); + } +#endif + else + { + FASSERT(0); + } + + return reg_val; +} + +/** + * @name: FGpioWriteRegVal + * @msg: 设置GPIO组的输出寄存器值 + * @return {*} + * @param {uintptr} base_addr, GPIO控制器基地址 + * @param {FGpioPortIndex} port, GPIO组 + * @param {u32} reg_val, 输出寄存器值 bit[8:0]有效 + */ +void FGpioWriteRegVal(uintptr base_addr, const FGpioPortIndex port, const u32 reg_val) +{ + if (FGPIO_PORT_A == port) + { + FGpioWriteReg32(base_addr, FGPIO_SWPORTA_DR_OFFSET, reg_val); + } +#if defined(FGPIO_VERSION_1) /* FT2000-4, D2000 */ + else if (FGPIO_PORT_B == port) + { + FGpioWriteReg32(base_addr, FGPIO_SWPORTB_DR_OFFSET, reg_val); + } +#endif + else + { + FASSERT(0); + } + + return; +} + +/** + * @name: FGpioSetOutputValue + * @msg: 设置GPIO引脚的输出值 + * @return {FError} FGPIO_SUCCESS 表示设置成功 + * @param {FGpioPin} *pin, GPIO引脚实例 + * @param {FGpioPinVal} output, GPIO引脚的输出值 + * @note 初始化 GPIO 实例后使用此函数,先设置 GPIO 引脚为输出后调用此函数 + */ +FError FGpioSetOutputValue(FGpioPin *const pin, const FGpioPinVal output) +{ + FASSERT(pin); + FGpio *const instance = pin->instance; + FASSERT(instance); + FASSERT_MSG(instance->is_ready == FT_COMPONENT_IS_READY, "gpio instance not yet init !!!"); + + FGpioPinId index = pin->index; + u32 base_addr = instance->config.base_addr; + u32 reg_val; + + if (FGPIO_DIR_OUTPUT != FGpioGetDirection(pin)) + { + FGPIO_ERROR("need to set GPIO direction as OUTPUT first !!!"); + return FGPIO_ERR_INVALID_STATE; + } + + FGPIO_INFO("pin-%d at port %d", index.pin, index.port); + reg_val = FGpioReadRegVal(base_addr, index.port); + if (FGPIO_PIN_LOW == output) + { + reg_val &= ~BIT(index.pin); + } + else if (FGPIO_PIN_HIGH == output) + { + reg_val |= BIT(index.pin); + } + else + { + FASSERT(0); + } + + FGPIO_INFO("output val 0x%x", reg_val); + FGpioWriteRegVal(base_addr, index.port, reg_val); + FGPIO_INFO("output val 0x%x", FGpioReadRegVal(base_addr, index.port)); + return FGPIO_SUCCESS; +} + +/** + * @name: FGpioGetInputValue + * @msg: 获取GPIO引脚的输入值 + * @return {FGpioPinVal} 获取的输入值,高电平/低电平 + * @param {FGpioPin} *instance, GPIO引脚实例 + * @note 初始化 GPIO 实例后使用此函数,先设置 GPIO 引脚为输入后调用此函数 + */ +FGpioPinVal FGpioGetInputValue(FGpioPin *const pin) +{ + FASSERT(pin); + FGpio *const instance = pin->instance; + FASSERT(instance); + FASSERT(instance->is_ready == FT_COMPONENT_IS_READY); + FGpioPinId index = pin->index; + uintptr base_addr = instance->config.base_addr; + u32 reg_val; + + if (FGPIO_DIR_INPUT != FGpioGetDirection(pin)) + { + FGPIO_ERROR("need to set GPIO direction as INPUT first !!!"); + return FGPIO_PIN_LOW; + } + + if (FGPIO_PORT_A == index.port) + { + reg_val = FGpioReadReg32(base_addr, FGPIO_EXT_PORTA_OFFSET); + } +#if defined(FGPIO_VERSION_1) /* FT2000-4, D2000 */ + else if (FGPIO_PORT_B == index.port) + { + reg_val = FGpioReadReg32(base_addr, FGPIO_EXT_PORTB_OFFSET); + } +#endif + else + { + FASSERT(0); + } + + FGPIO_INFO("input val: 0x%x", reg_val); + return (BIT(index.pin) & reg_val) ? FGPIO_PIN_HIGH : FGPIO_PIN_LOW; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/pin/fgpio/fgpio.h b/bsp/phytium/libraries/standalone/drivers/pin/fgpio/fgpio.h new file mode 100644 index 0000000000..f41f80aaf2 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/pin/fgpio/fgpio.h @@ -0,0 +1,239 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgpio.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:25:35 + * Description:  This files is for GPIO user API definition + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2022-3-1 init commit + */ + + +#ifndef DRIVERS_FGPIO_H +#define DRIVERS_FGPIO_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ +#include "ftypes.h" +#include "fassert.h" +#include "ferror_code.h" +#include "sdkconfig.h" + +/************************** Constant Definitions *****************************/ +#define FGPIO_SUCCESS FT_SUCCESS +#define FGPIO_ERR_INVALID_PARA FT_MAKE_ERRCODE(ErrModBsp, ErrBspGpio, 0x0) +#define FGPIO_ERR_INVALID_STATE FT_MAKE_ERRCODE(ErrModBsp, ErrBspGpio, 0x1) +#define FGPIO_ERR_NOT_INIT FT_MAKE_ERRCODE(ErrModBsp, ErrBspGpio, 0x2) +#define FGPIO_ERR_ALREADY_INIT FT_MAKE_ERRCODE(ErrModBsp, ErrBspGpio, 0x3) + +#if defined(CONFIG_TARGET_F2000_4) || defined(CONFIG_TARGET_D2000) +#define FGPIO_VERSION_1 /* 用于FT2000/4和D2000平台的GPIO 0 ~ 1 */ +#elif defined(CONFIG_TARGET_E2000) +#define FGPIO_VERSION_2 /* 用于E2000平台的GPIO 3 ~ 5 */ +#else +#error "Invalid target board !!!" +#endif + +typedef enum +{ + FGPIO_PORT_A = 0, +#if defined(FGPIO_VERSION_1) /* FT2000-4, D2000 */ + FGPIO_PORT_B, +#endif + + FGPIO_PORT_NUM +} FGpioPortIndex; /* GPIO引脚所在的组 */ + +typedef enum +{ + FGPIO_PIN_0 = 0, + FGPIO_PIN_1, + FGPIO_PIN_2, + FGPIO_PIN_3, + FGPIO_PIN_4, + FGPIO_PIN_5, + FGPIO_PIN_6, + FGPIO_PIN_7, +#if defined(FGPIO_VERSION_2) /* E2000 GPIO 0 ~ 5 */ + FGPIO_PIN_8, + FGPIO_PIN_9, + FGPIO_PIN_10, + FGPIO_PIN_11, + FGPIO_PIN_12, + FGPIO_PIN_13, + FGPIO_PIN_14, + FGPIO_PIN_15, +#endif + + FGPIO_PIN_NUM +} FGpioPinIndex; /* GPIO引脚号 */ + +#if defined(FGPIO_VERSION_1) /* FT2000-4, D2000 */ +FASSERT_STATIC(8 == FGPIO_PIN_NUM); /* pin 0 ~ 7 */ +FASSERT_STATIC(2 == FGPIO_PORT_NUM); /* port a/b */ +#elif defined(FGPIO_VERSION_2) /* E2000 GPIO 0 ~ 5 */ +FASSERT_STATIC(16 == FGPIO_PIN_NUM); /* pin 0 ~ 15 */ +FASSERT_STATIC(1 == FGPIO_PORT_NUM); /* port a */ +#endif +typedef enum +{ + FGPIO_DIR_INPUT = 0, /* 输入 */ + FGPIO_DIR_OUTPUT /* 输出 */ +} FGpioDirection; /* GPIO引脚的输入输出方向 */ + +typedef enum +{ + FGPIO_IRQ_TYPE_EDGE_FALLING = 0, /* 上升沿中断,引脚检测到电平从低变高时触发 */ + FGPIO_IRQ_TYPE_EDGE_RISING, /* 下降沿中断,引脚检测到电平从高变低时触发 */ + FGPIO_IRQ_TYPE_LEVEL_LOW, /* 低电平中断,引脚电平为低时触发 */ + FGPIO_IRQ_TYPE_LEVEL_HIGH /* 高电平中断,引脚电平为高时触发 */ +} FGpioIrqType; /* GPIO引脚中断类型 */ + +typedef enum +{ + FGPIO_IRQ_NOT_SUPPORT, /* 不支持引脚中断 */ + FGPIO_IRQ_BY_CONTROLLER, /* 引脚中断控制器合并上报 */ +#if defined(FGPIO_VERSION_2) /* E2000 GPIO 0 ~ 5 */ + FGPIO_IRQ_BY_PIN, /* 引脚中断单独上报 */ +#endif +} FGpioIrqSourceType; + +typedef enum +{ + FGPIO_PIN_LOW = 0, /* 低电平 */ + FGPIO_PIN_HIGH /* 高电平 */ +} FGpioPinVal; /* GPIO引脚电平类型 */ + +/**************************** Type Definitions *******************************/ +typedef struct _FGpioPin FGpioPin; +typedef struct _FGpio FGpio; + +typedef struct +{ + u32 instance_id; /* GPIO实例ID */ + uintptr base_addr; /* GPIO控制器基地址 */ +#if defined(FGPIO_VERSION_1) /* FT2000-4, D2000 */ + u32 irq_num; /* GPIO控制器中断号 */ +#elif defined(FGPIO_VERSION_2) /* E2000 GPIO 0 ~ 5 */ + u32 irq_num[FGPIO_PIN_NUM]; /* GPIO各引脚的中断号 */ +#endif + u32 irq_priority; /* 中断优先级 */ +} FGpioConfig; /* GPIO控制器配置 */ + +typedef struct +{ + u32 ctrl; /* GPIO控制器号 */ + FGpioPortIndex port; /* GPIO引脚所在的组 */ + FGpioPinIndex pin; /* GPIO引脚号 */ +} FGpioPinId; /* GPIO引脚索引 */ + +typedef void (*FGpioInterruptCallback)(s32 vector, void *param); /* GPIO引脚中断回调函数类型 */ + +typedef struct _FGpioPin +{ + FGpioPinId index; /* 索引 */ + u32 is_ready; + FGpio *instance; + FGpioInterruptCallback irq_cb; /* 中断回调函数, Port-A有效 */ + void *irq_cb_params; /* 中断回调函数的入参, Port-A有效 */ + boolean irq_one_time; /* Port-A有效, TRUE: 进入中断后关闭该引脚的中断,用于电平敏感中断,防止一直进入中断 */ +} FGpioPin; /* GPIO引脚实例 */ + +typedef struct _FGpio +{ + FGpioConfig config; + u32 is_ready; + FGpioPin *pins[FGPIO_PORT_NUM][FGPIO_PIN_NUM]; +} FGpio; /* GPIO控制器实例 */ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +/* 生成GPIO引脚索引 */ +#define FGPIO_PIN(port, pin) \ + (FGpioPinId) { \ + (port), (pin) \ + } + +/************************** Function Prototypes ******************************/ +/* 获取GPIO控制器的默认配置 */ +const FGpioConfig *FGpioLookupConfig(u32 instance_id); + +/* 初始化GPIO控制器实例 */ +FError FGpioCfgInitialize(FGpio *const instance, const FGpioConfig *const config); + +/* 初始化GPIO引脚实例 */ +FError FGpioPinInitialize(FGpio *const instance, FGpioPin *const pin, + const FGpioPinId pin_id); + +/* 去初始化GPIO引脚实例 */ +void FGpioPinDeInitialize(FGpioPin *const pin); + +/* 获取引脚中断的上报方式 */ +FGpioIrqSourceType FGpioGetPinIrqSourceType(FGpioPinId pin_id); + +/* 去初始化GPIO控制器实例 */ +void FGpioDeInitialize(FGpio *const instance); + +/* 设置GPIO引脚的输入输出方向 */ +void FGpioSetDirection(FGpioPin *const pin, FGpioDirection dir); + +/* 获取GPIO引脚的输入输出方向 */ +FGpioDirection FGpioGetDirection(FGpioPin *const pin); + +/* 设置GPIO引脚的输出值 */ +FError FGpioSetOutputValue(FGpioPin *const pin, const FGpioPinVal output); + +/* 获取GPIO引脚的输入值 */ +FGpioPinVal FGpioGetInputValue(FGpioPin *const pin); + +/* 获取GPIO A组引脚的中断屏蔽位 */ +void FGpioGetInterruptMask(FGpio *const instance, u32 *mask, u32 *enabled); + +/* 设置GPIO A组引脚的中断屏蔽位 */ +void FGpioSetInterruptMask(FGpioPin *const pin, boolean enable); + +/* 获取GPIO A组引脚的中断类型和中断极性 */ +void FGpioGetInterruptType(FGpio *const instance, u32 *levels, u32 *polarity); + +/* 设置GPIO A组引脚的中断类型 */ +void FGpioSetInterruptType(FGpioPin *const pin, const FGpioIrqType type); + +/* GPIO控制器中断处理函数 */ +void FGpioInterruptHandler(s32 vector, void *param); + +#if defined(FGPIO_VERSION_2) /* E2000 GPIO 0 ~ 2 */ +/* GPIO引脚中断处理函数 */ +void FGpioPinInterruptHandler(s32 vector, void *param); +#endif + +/* 注册GPIO A组引脚中断回调函数 */ +void FGpioRegisterInterruptCB(FGpioPin *const pin, FGpioInterruptCallback cb, + void *cb_param, boolean irq_one_time); + +/* 打印GPIO控制寄存器信息 */ +void FGpioDumpRegisters(uintptr base_addr); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsp/phytium/libraries/standalone/drivers/pin/fgpio/fgpio_g.c b/bsp/phytium/libraries/standalone/drivers/pin/fgpio/fgpio_g.c new file mode 100644 index 0000000000..1c41a6665d --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/pin/fgpio/fgpio_g.c @@ -0,0 +1,101 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgpio_g.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:25:29 + * Description:  This files is for GPIO static configuration implementation + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2022-3-1 init commit + */ + + +/***************************** Include Files *********************************/ +#include "fparameters.h" + +#include "fgpio_hw.h" +#include "fgpio.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + +/*****************************************************************************/ +#if defined(FGPIO_VERSION_1) /* FT2000-4, D2000 */ +const FGpioConfig fgpio_cfg_tbl[FGPIO_NUM] = +{ + [FGPIO_ID_0] = + { + .instance_id = FGPIO_ID_0, + .base_addr = FGPIO_0_BASE_ADDR, + .irq_num = FGPIO_0_IRQ_NUM, + .irq_priority = 0 + }, + [FGPIO_ID_1] = + { + .instance_id = FGPIO_ID_1, + .base_addr = FGPIO_1_BASE_ADDR, + .irq_num = FGPIO_1_IRQ_NUM, + .irq_priority = 0 + } +}; +#elif defined(FGPIO_VERSION_2) /* E2000 GPIO 0 ~ 5 */ +FGpioConfig fgpio_cfg_tbl[FGPIO_NUM] = +{ + [FGPIO_ID_0] = + { + .instance_id = FGPIO_ID_0, + .base_addr = FGPIO_0_BASE_ADDR, + .irq_priority = 0 + }, + [FGPIO_ID_1] = + { + .instance_id = FGPIO_ID_1, + .base_addr = FGPIO_1_BASE_ADDR, + .irq_priority = 0 + }, + [FGPIO_ID_2] = + { + .instance_id = FGPIO_ID_2, + .base_addr = FGPIO_2_BASE_ADDR, + .irq_priority = 0 + }, + [FGPIO_ID_3] = + { + .instance_id = FGPIO_ID_3, + .base_addr = FGPIO_3_BASE_ADDR, + .irq_priority = 0 + }, + [FGPIO_ID_4] = + { + .instance_id = FGPIO_ID_4, + .base_addr = FGPIO_4_BASE_ADDR, + .irq_priority = 0 + }, + [FGPIO_ID_5] = + { + .instance_id = FGPIO_ID_5, + .base_addr = FGPIO_5_BASE_ADDR, + .irq_priority = 0 + }, +}; +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/pin/fgpio/fgpio_hw.h b/bsp/phytium/libraries/standalone/drivers/pin/fgpio/fgpio_hw.h new file mode 100644 index 0000000000..9acad53bf0 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/pin/fgpio/fgpio_hw.h @@ -0,0 +1,166 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgpio_hw.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:25:35 + * Description:  This files is for GPIO register definition + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2022-3-1 init commit + */ + + +#ifndef DRIVERS_FGPIO_HW_H +#define DRIVERS_FGPIO_HW_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ +#include "fio.h" +#include "fkernel.h" + +/************************** Constant Definitions *****************************/ +/** @name Register Map + * + * Register offsets from the base address of an GPIO device. + * @{ + */ +#define FGPIO_SWPORTA_DR_OFFSET 0x00 /* WR Port A Output Data Register */ +#define FGPIO_SWPORTA_DDR_OFFSET 0x04 /* WR Port A Data Direction Register */ +#define FGPIO_EXT_PORTA_OFFSET 0x08 /* RO Port A Input Data Register */ +#define FGPIO_SWPORTB_DR_OFFSET 0x0c /* WR Port B Output Data Register */ +#define FGPIO_SWPORTB_DDR_OFFSET 0x10 /* WR Port B Data Direction Register */ +#define FGPIO_EXT_PORTB_OFFSET 0x14 /* RO Port B Input Data Register */ + +#define FGPIO_INTEN_OFFSET 0x18 /* WR Port A Interrput Enable Register */ +#define FGPIO_INTMASK_OFFSET 0x1c /* WR Port A Interrupt Mask Register */ +#define FGPIO_INTTYPE_LEVEL_OFFSET 0x20 /* WR Port A Interrupt Level Register */ +#define FGPIO_INT_POLARITY_OFFSET 0x24 /* WR Port A Interrupt Polarity Register */ +#define FGPIO_INTSTATUS_OFFSET 0x28 /* RO Port A Interrupt Status Register */ +#define FGPIO_RAW_INTSTATUS_OFFSET 0x2c /* RO Port A Raw Interrupt Status Register */ +#define FGPIO_LS_SYNC_OFFSET 0x30 /* WR Level-sensitive Synchronization Enable Register */ +#define FGPIO_DEBOUNCE_OFFSET 0x34 /* WR Debounce Enable Register */ +#define FGPIO_PORTA_EOI_OFFSET 0x38 /* WO Port A Clear Interrupt Register */ + +/** @name FGPIO_SWPORTA_DR_OFFSET Register + */ +#define FGPIO_SWPORTA_DR_SET(dir) SET_REG32_BITS((dir), 7, 0) +#define FGPIO_SWPORTA_DR_GET(reg_val) GET_REG32_BITS((reg_val), 7, 0) +#define FGPIO_SWPORTA_DR_MASK GENMASK(7, 0) + +/** @name FGPIO_SWPORTA_DDR_OFFSET Register + */ +#define FGPIO_SWPORTA_DDR_SET(dir) SET_REG32_BITS((dir), 7, 0) +#define FGPIO_SWPORTA_DDR_GET(reg_val) GET_REG32_BITS((reg_val), 7, 0) +#define FGPIO_SWPORTA_DDR_MASK GENMASK(7, 0) + +/** @name FGPIO_EXT_PORTA_OFFSET Register + */ +#define FGPIO_EXT_PORTA_SET(dir) SET_REG32_BITS((dir), 7, 0) +#define FGPIO_EXT_PORTA_GET(reg_val) GET_REG32_BITS((reg_val), 7, 0) +#define FGPIO_EXT_PORTA_MASK GENMASK(7, 0) + +/** @name FGPIO_SWPORTB_DR_OFFSET Register + */ +#define FGPIO_SWPORTB_DR_SET(dir) SET_REG32_BITS((dir), 7, 0) +#define FGPIO_SWPORTB_DR_GET(reg_val) GET_REG32_BITS((reg_val), 7, 0) +#define FGPIO_SWPORTB_DR_MASK GENMASK(7, 0) + +/** @name FGPIO_SWPORTB_DDR_OFFSET Register + */ +#define FGPIO_SWPORTB_DDR_SET(dir) SET_REG32_BITS((dir), 7, 0) +#define FGPIO_SWPORTB_DDR_GET(reg_val) GET_REG32_BITS((reg_val), 7, 0) +#define FGPIO_SWPORTB_DDR_MASK GENMASK(7, 0) + +/** @name FGPIO_EXT_PORTB_OFFSET Register + */ +#define FGPIO_EXT_PORTB_SET(dir) SET_REG32_BITS((dir), 7, 0) +#define FGPIO_EXT_PORTB_GET(reg_val) GET_REG32_BITS((reg_val), 7, 0) +#define FGPIO_EXT_PORTB_MASK GENMASK(7, 0) + +/** @name FGPIO_INTEN_OFFSET Register + */ +#define FGPIO_INTR_PORTA_EN(n) BIT(n) /* 1: enable the intr of n-th port in group-a */ + +/** @name FGPIO_INTMASK_OFFSET Register + */ +#define FGPIO_INTR_PORTA_MASK(n) BIT(n) /* 1: disable the intr of n-th port in group-a */ + +/** @name FGPIO_INTTYPE_LEVEL_OFFSET Register + */ +#define FGPIO_INTR_PORTA_LEVEL(n) BIT(n) /* 1: intr by edge, 0: intr by level */ + +/** @name FGPIO_INT_POLARITY_OFFSET Register + */ +#define FGPIO_INTR_PORTA_POLARITY(n) BIT(n) /* 1: intr by rising-edge/high-level, 0: intr by falling-edge/low-level */ + +/** @name FGPIO_INTSTATUS_OFFSET Register + */ +#define FGPIO_INTR_PORTA_STATUS(n) BIT(n) /* intr status */ + +/** @name FGPIO_RAW_INTSTATUS_OFFSET Register + */ +#define FGPIO_INTR_PORTA_RAW_STATUS(n) BIT(n) /* intr status without masking */ + +/** @name FGPIO_LS_SYNC_OFFSET Register + */ +#define FGPIO_PCLK_INTR_SYNC(n) BIT(n) /* 1: sync to pclk_intr */ + +/** @name FGPIO_DEBOUNCE_OFFSET Register + */ +#define FGPIO_DEBOUNCE_CLK_CONFIG_SET(clk) SET_REG32_BITS((clk), 15, 7) +#define FGPIO_DEBOUNCE_CLK_CONFIG_GET(reg_val) GET_REG32_BITS((reg_val), 15, 7) +#define FGPIO_DEBOUNCE_CLK_CONFIG_MASK GENMASK(15, 7) +#define FGPIO_DEBOUNCE_EN(n) BIT(n) /* 1: enable debounce */ + +/** @name FGPIO_PORTA_EOI_OFFSET Register + */ +#define FGPIO_CLR_INTR_PORTA(n) BIT(n) /* 1: clear interrupt */ + + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +static inline u32 FGpioReadReg32(uintptr base_addr, uintptr reg_off) +{ + return FtIn32(base_addr + reg_off); +} + +static inline void FGpioWriteReg32(uintptr base_addr, uintptr reg_off, const u32 reg_val) +{ + FtOut32(base_addr + reg_off, reg_val); +} + +static inline void FGpioSetBit32(uintptr base_addr, uintptr reg_off, u32 bit) +{ + if (0 == bit) + FtClearBit32(base_addr + reg_off, bit); + else if (1 == bit) + FtSetBit32(base_addr + reg_off, bit); +} + +/************************** Function Prototypes ******************************/ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsp/phytium/libraries/standalone/drivers/pin/fgpio/fgpio_intr.c b/bsp/phytium/libraries/standalone/drivers/pin/fgpio/fgpio_intr.c new file mode 100644 index 0000000000..b453ecdb7e --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/pin/fgpio/fgpio_intr.c @@ -0,0 +1,328 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgpio_intr.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:25:29 + * Description:  This files is for GPIO interrupt function implementation + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2022-3-1 init commit + */ + + +/***************************** Include Files *********************************/ +#include "fdebug.h" +#include "fparameters.h" + +#include "fgpio_hw.h" +#include "fgpio.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FGPIO_DEBUG_TAG "FGPIO-INTR" +#define FGPIO_ERROR(format, ...) FT_DEBUG_PRINT_E(FGPIO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGPIO_WARN(format, ...) FT_DEBUG_PRINT_W(FGPIO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGPIO_INFO(format, ...) FT_DEBUG_PRINT_I(FGPIO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGPIO_DEBUG(format, ...) FT_DEBUG_PRINT_D(FGPIO_DEBUG_TAG, format, ##__VA_ARGS__) + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + +/*****************************************************************************/ +/** + * @name: FGpioGetInterruptMask + * @msg: 获取GPIO A组引脚的中断屏蔽位 + * @return {*} + * @param {FGpio} *instance, GPIO控制器实例 + * @param {u32} *mask, 返回的GPIO A组引脚中断屏蔽位 + * @param {u32} *enabled, 返回的GPIO A组中断使能位 + * @note 获取的是A组所有Pin的中断屏蔽位和中断使能位 + */ +void FGpioGetInterruptMask(FGpio *const instance, u32 *mask, u32 *enabled) +{ + FASSERT(instance); + FASSERT(instance->is_ready == FT_COMPONENT_IS_READY); + uintptr base_addr = instance->config.base_addr; + + if (NULL != mask) + { + *mask = FGpioReadReg32(base_addr, FGPIO_INTMASK_OFFSET); + } + + if (NULL != enabled) + { + *enabled = FGpioReadReg32(base_addr, FGPIO_INTEN_OFFSET); + } + + return; +} + +/** + * @name: FGpioSetInterruptMask + * @msg: 设置GPIO A组引脚的中断屏蔽位 + * @return {*} + * @param {FGpioPin} *pin, GPIO引脚实例 + * @param {boolean} enable, TRUE表示使能GPIO引脚中断,FALSE表示去使能GPIO引脚中断 + * @note index对应的引脚必须为A组引脚,B组引脚不支持中断 + */ +void FGpioSetInterruptMask(FGpioPin *const pin, boolean enable) +{ + FASSERT(pin); + FGpio *const instance = pin->instance; + FASSERT(instance); + FASSERT(instance->is_ready == FT_COMPONENT_IS_READY); + uintptr base_addr = instance->config.base_addr; + u32 mask_bits = 0; + u32 enable_bits = 0; + FGpioPinId index = pin->index; + +#if defined(FGPIO_VERSION_1) /* FT2000-4, D2000 */ + if (FGPIO_PORT_B == index.port) + { + FGPIO_ERROR("None interrupt support for PORT-B !!!"); + return; + } +#endif + + if (FGPIO_DIR_OUTPUT == FGpioGetDirection(pin)) + { + FGPIO_ERROR("None interrupt support for output GPIO !!!"); + return; + } + + FGpioGetInterruptMask(instance, &mask_bits, &enable_bits); + if (TRUE == enable) + { + mask_bits &= ~BIT(index.pin); /* not mask: 0 */ + enable_bits |= BIT(index.pin); /* enable pin irq: 1 */ + } + else + { + mask_bits |= BIT(index.pin); /* mask: 1 */ + enable_bits &= ~BIT(index.pin); /* disable pin irq: 0 */ + } + + FGpioWriteReg32(base_addr, FGPIO_INTMASK_OFFSET, mask_bits); + FGpioWriteReg32(base_addr, FGPIO_INTEN_OFFSET, enable_bits); + return; +} + +/** + * @name: FGpioGetInterruptType + * @msg: 获取GPIO A组引脚的中断类型和中断极性 + * @return {*} + * @param {FGpio} *instance, GPIO控制器实例 + * @param {u32} *levels, GPIO A组引脚中断电平类型 + * @param {u32} *polarity, GPIO A组引脚中断极性类型 + * @note 获取的是A组所有Pin的电平和极性 + */ +void FGpioGetInterruptType(FGpio *const instance, u32 *levels, u32 *polarity) +{ + FASSERT(instance); + FASSERT(instance->is_ready == FT_COMPONENT_IS_READY); + uintptr base_addr = instance->config.base_addr; + + if (NULL != levels) + { + *levels = FGpioReadReg32(base_addr, FGPIO_INTTYPE_LEVEL_OFFSET); + } + + if (NULL != polarity) + { + *polarity = FGpioReadReg32(base_addr, FGPIO_INTTYPE_LEVEL_OFFSET); + } + + return; +} + +/** + * @name: FGpioSetInterruptType + * @msg: 设置GPIO引脚的中断类型 + * @return {*} + * @param {FGpioPin} *pin, GPIO引脚实例 + * @param {FGpioIrqType} type, GPIO引脚中断触发类型 + * @note index对应的引脚必须为A组引脚,B组引脚不支持中断 + */ +void FGpioSetInterruptType(FGpioPin *const pin, const FGpioIrqType type) +{ + FASSERT(pin); + FGpio *const instance = pin->instance; + FASSERT(instance); + FASSERT(instance->is_ready == FT_COMPONENT_IS_READY); + uintptr base_addr = instance->config.base_addr; + u32 level = 0; + u32 polarity = 0; + FGpioPinId index = pin->index; + +#if defined(FGPIO_VERSION_1) /* FT2000-4, D2000 */ + if (FGPIO_PORT_B == index.port) + { + FGPIO_ERROR("None interrupt support for PORT-B !!!"); + return; + } +#endif + + FGpioGetInterruptType(instance, &level, &polarity); + + switch (type) + { + case FGPIO_IRQ_TYPE_EDGE_FALLING: + level |= BIT(index.pin); /* 边沿敏感型 */ + polarity &= ~BIT(index.pin); /* 下降沿或低电平 */ + break; + case FGPIO_IRQ_TYPE_EDGE_RISING: + level |= BIT(index.pin); /* 边沿敏感型 */ + polarity |= BIT(index.pin); /* 上升沿或高电平 */ + break; + case FGPIO_IRQ_TYPE_LEVEL_LOW: + level &= ~BIT(index.pin); /* 电平敏感型 */ + polarity &= ~BIT(index.pin); /* 下降沿或低电平 */ + break; + case FGPIO_IRQ_TYPE_LEVEL_HIGH: + level &= ~BIT(index.pin); /* 电平敏感型 */ + polarity |= BIT(index.pin); /* 上升沿或高电平 */ + break; + default: + break; + } + + FGpioWriteReg32(base_addr, FGPIO_INTTYPE_LEVEL_OFFSET, level); + FGpioWriteReg32(base_addr, FGPIO_INT_POLARITY_OFFSET, polarity); + + return; +} + +/** + * @name: FGpioInterruptHandler + * @msg: GPIO中断处理函数 + * @return {*} + * @param {s32} vector, 中断输入参数1 + * @param {void} *param, 中断输入参数2 + * @note 需要用户将此函数注册到Interrtup上,使能GPIO中断才能生效 + */ +void FGpioInterruptHandler(s32 vector, void *param) +{ + FGpio *const instance = (FGpio * const)param; + FGpioPin *pin = NULL; + FASSERT(instance); + int loop; + uintptr base_addr = instance->config.base_addr; + u32 status = FGpioReadReg32(base_addr, FGPIO_INTSTATUS_OFFSET); + u32 raw_status = FGpioReadReg32(base_addr, FGPIO_RAW_INTSTATUS_OFFSET); + +#if defined(FGPIO_VERSION_2) /* E2000 gpio 3 ~ 5 */ + FASSERT_MSG(FGPIO_WITH_PIN_IRQ < instance->config.instance_id, "handle interrupt through pin !!!") +#endif + + FGPIO_INFO("status: 0x%x, raw_status: 0x%x", status, raw_status); + for (loop = FGPIO_PIN_0; loop < FGPIO_PIN_NUM; loop++) + { + if (status & BIT(loop)) + { + pin = instance->pins[FGPIO_PORT_A][loop]; + if (NULL == pin) + continue; + + if (pin->irq_cb) + { + pin->irq_cb(0U, pin->irq_cb_params); + + /* disable pin interrupt after triggered */ + if (TRUE == pin->irq_one_time) + { + FGpioSetInterruptMask(pin, FALSE); + } + } + else + { + FGPIO_WARN("no irq handler callback for GPIO-%d-A-%d", + instance->config.instance_id, + loop); + } + } + } + + /* clear interrupt status */ + FGpioWriteReg32(base_addr, FGPIO_PORTA_EOI_OFFSET, status); + return; +} + +#if defined(FGPIO_VERSION_2) /* E2000 GPIO 0 ~ 2 */ +/** + * @name: FGpioPinInterruptHandler + * @msg: GPIO引脚中断处理函数 + * @return {NONE} + * @param {s32} vector, 中断输入参数1 + * @param {void} *param, 中断输入参数2 + */ +void FGpioPinInterruptHandler(s32 vector, void *param) +{ + FGpioPin *const pin = (FGpioPin * const)param; + FASSERT(pin); + FGpio *const instance = pin->instance; + FASSERT(instance); + uintptr base_addr = instance->config.base_addr; + + u32 status = FGpioReadReg32(base_addr, FGPIO_INTSTATUS_OFFSET); + u32 raw_status = FGpioReadReg32(base_addr, FGPIO_RAW_INTSTATUS_OFFSET); + + FGPIO_INFO("status: 0x%x, raw_status: 0x%x", status, raw_status); + if (pin->irq_cb) + { + pin->irq_cb(0U, pin->irq_cb_params); + + /* disable pin interrupt after triggered */ + if (TRUE == pin->irq_one_time) + { + FGpioSetInterruptMask(pin, FALSE); + } + } + else + { + FGPIO_WARN("no irq handler callback for GPIO-%d-A-%d", + pin->index.ctrl, + pin->index.pin); + } + + + /* clear interrupt status */ + FGpioWriteReg32(base_addr, FGPIO_PORTA_EOI_OFFSET, status); + return; +} +#endif + +/** + * @name: FGpioRegisterInterruptCB + * @msg: 注册GPIO引脚中断回调函数 + * @return {*} + * @param {FGpioPin} pin, GPIO引脚实例 + * @param {FGpioInterruptCallback} cb, GPIO引脚中断回调函数 + * @param {void} *cb_param, GPIO引脚中断回调函数输入参数 + * @param {boolean} irq_one_time, TRUE表示引脚中断触发一次后自动关闭中断,用于电平敏感中断 + * @note 注册的回调函数在FGpioInterruptHandler中被调用 + */ +void FGpioRegisterInterruptCB(FGpioPin *const pin, FGpioInterruptCallback cb, void *cb_param, boolean irq_one_time) +{ + FASSERT(pin); + pin->irq_cb = cb; + pin->irq_cb_params = cb_param; + pin->irq_one_time = irq_one_time; + return; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/pin/fgpio/fgpio_selftest.c b/bsp/phytium/libraries/standalone/drivers/pin/fgpio/fgpio_selftest.c new file mode 100644 index 0000000000..8cc5ed81e0 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/pin/fgpio/fgpio_selftest.c @@ -0,0 +1,79 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgpio_selftest.c + * Date: 2022-06-17 14:32:12 + * LastEditTime: 2022-06-17 14:32:12 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ +/***************************** Include Files *********************************/ +#include "fdebug.h" +#include "fassert.h" + +#include "fgpio_hw.h" +#include "fgpio.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FGPIO_DEBUG_TAG "FGPIO-TEST" +#define FGPIO_ERROR(format, ...) FT_DEBUG_PRINT_E(FGPIO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGPIO_WARN(format, ...) FT_DEBUG_PRINT_W(FGPIO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGPIO_INFO(format, ...) FT_DEBUG_PRINT_I(FGPIO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FGPIO_DEBUG(format, ...) FT_DEBUG_PRINT_D(FGPIO_DEBUG_TAG, format, ##__VA_ARGS__) + +#define FGPIO_DUMPER(base_addr, reg_off, reg_name) \ + FGPIO_DEBUG("\t\t[%s]@0x%x\t=\t0x%x", reg_name, (reg_off), FGpioReadReg32((base_addr), (reg_off))) +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + +/*****************************************************************************/ + +/** + * @name: FGpioDumpRegisters + * @msg: 打印GPIO控制寄存器信息 + * @return {NONE} + * @param {uintptr} base_addr, GPIO控制器基地址 + */ +void FGpioDumpRegisters(uintptr base_addr) +{ + FASSERT(0 != base_addr); + + FGPIO_DEBUG("Dump register info @0x%x", base_addr); + FGPIO_DUMPER(base_addr, FGPIO_SWPORTA_DR_OFFSET, "dr"); + FGPIO_DUMPER(base_addr, FGPIO_SWPORTA_DDR_OFFSET, "ddr"); + FGPIO_DUMPER(base_addr, FGPIO_EXT_PORTA_OFFSET, "ext_porta"); +#if defined(FGPIO_VERSION_1) /* D2000 FT2000/4 */ + FGPIO_DUMPER(base_addr, FGPIO_SWPORTB_DR_OFFSET, "portb_dr"); + FGPIO_DUMPER(base_addr, FGPIO_SWPORTB_DDR_OFFSET, "portb_ddr"); + FGPIO_DUMPER(base_addr, FGPIO_EXT_PORTB_OFFSET, "ext_portb"); +#endif + FGPIO_DUMPER(base_addr, FGPIO_INTEN_OFFSET, "inten"); + FGPIO_DUMPER(base_addr, FGPIO_INTMASK_OFFSET, "intmask"); + FGPIO_DUMPER(base_addr, FGPIO_INTTYPE_LEVEL_OFFSET, "intr_level"); + FGPIO_DUMPER(base_addr, FGPIO_INT_POLARITY_OFFSET, "intr_polarity"); + FGPIO_DUMPER(base_addr, FGPIO_INTSTATUS_OFFSET, "intr_status"); + FGPIO_DUMPER(base_addr, FGPIO_RAW_INTSTATUS_OFFSET, "raw_int_status"); + FGPIO_DUMPER(base_addr, FGPIO_LS_SYNC_OFFSET, "ls_sync"); + FGPIO_DUMPER(base_addr, FGPIO_DEBOUNCE_OFFSET, "debounce"); + FGPIO_DUMPER(base_addr, FGPIO_PORTA_EOI_OFFSET, "porta_eoi"); + + return; +} diff --git a/bsp/phytium/libraries/standalone/drivers/pin/fgpio/fgpio_sinit.c b/bsp/phytium/libraries/standalone/drivers/pin/fgpio/fgpio_sinit.c new file mode 100644 index 0000000000..2700899002 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/pin/fgpio/fgpio_sinit.c @@ -0,0 +1,150 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fgpio_sinit.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 08:25:29 + * Description:  This files is for GPIO static variables + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2022-3-1 init commit + */ + + +/***************************** Include Files *********************************/ +#include "fparameters.h" + +#include "fgpio_hw.h" +#include "fgpio.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ +#if defined(FGPIO_VERSION_1) /* FT2000-4, D2000 */ + extern const FGpioConfig fgpio_cfg_tbl[FGPIO_NUM]; +#elif defined(FGPIO_VERSION_2) /* E2000 GPIO 0 ~ 5 */ + extern FGpioConfig fgpio_cfg_tbl[FGPIO_NUM]; +#endif + +/*****************************************************************************/ + +#if defined(FGPIO_VERSION_1) /* FT2000-4, D2000 */ +/** + * @name: FGpioLookupConfig + * @msg: 获取GPIO控制器的默认配置 + * @return {const FGpioConfig *} GPIO控制器的默认配置 + * @param {u32} instance_id, GPIO控制器实例号 + */ +const FGpioConfig *FGpioLookupConfig(u32 instance_id) +{ + const FGpioConfig *ptr = NULL; + u32 index; + + for (index = 0; index < FGPIO_NUM; index++) + { + if (fgpio_cfg_tbl[index].instance_id == instance_id) + { + ptr = &fgpio_cfg_tbl[index]; + break; + } + } + + return ptr; +} +#elif defined(FGPIO_VERSION_2) /* E2000 GPIO 0 ~ 5 */ +/** + * @name: FGpioSetIrqNum + * @msg: 设置GPIO控制器各引脚的中断号 + * @return {NONE} + * @param {u32} instance_id, GPIO控制器实例号 + * @param {FGpioConfig} *ptr, GPIO控制器的默认配置 + */ +static void FGpioSetIrqNum(u32 instance_id, FGpioConfig *ptr) +{ + u32 pin_id; + u32 irq_num; + + if (FGPIO_WITH_PIN_IRQ >= instance_id) /* GPIO 0 ~ 2 */ + { + /* each pin has its own interrupt id */ + for (pin_id = FGPIO_PIN_0; pin_id < FGPIO_PIN_NUM; pin_id++) + { + ptr->irq_num[pin_id] = FGPIO_PIN_IRQ_NUM_GET(instance_id, pin_id); + } + } + else + { + if (FGPIO_ID_3 == instance_id) + { + irq_num = FGPIO_3_IRQ_NUM; + } + else if (FGPIO_4_IRQ_NUM == instance_id) + { + irq_num = FGPIO_4_IRQ_NUM; + } + else if (FGPIO_5_IRQ_NUM == instance_id) + { + irq_num = FGPIO_5_IRQ_NUM; + } + + /* all pins in the controller share the same interrupt id */ + for (pin_id = FGPIO_PIN_0; pin_id < FGPIO_PIN_NUM; pin_id++) + { + ptr->irq_num[pin_id] = irq_num; + } + } + + return; +} + +/** + * @name: FGpioLookupConfig + * @msg: 获取GPIO控制器的默认配置 + * @return {const FGpioConfig *} GPIO控制器的默认配置 + * @param {u32} instance_id, GPIO控制器实例号 + */ +const FGpioConfig *FGpioLookupConfig(u32 instance_id) +{ + const FGpioConfig *ptr = NULL; + u32 index; + static boolean irq_num_set = FALSE; + + if (FALSE == irq_num_set) /* set irq num in the first time */ + { + for (index = 0; index < FGPIO_NUM; index++) + { + FGpioSetIrqNum(index, &fgpio_cfg_tbl[index]); + } + irq_num_set = TRUE; + } + + for (index = 0; index < FGPIO_NUM; index++) /* find configs of controller */ + { + if (fgpio_cfg_tbl[index].instance_id == instance_id) + { + ptr = &fgpio_cfg_tbl[index]; + break; + } + } + + return ptr; +} +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/pwm/Kconfig b/bsp/phytium/libraries/standalone/drivers/pwm/Kconfig new file mode 100644 index 0000000000..5a6185cf4e --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/pwm/Kconfig @@ -0,0 +1,9 @@ +menu "FPWM Configuration" + config USE_FPWM + bool + prompt "Use FPWM" + default n + +endmenu + + diff --git a/bsp/phytium/libraries/standalone/drivers/pwm/fpwm/fpwm.c b/bsp/phytium/libraries/standalone/drivers/pwm/fpwm/fpwm.c new file mode 100644 index 0000000000..7c1dace19e --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/pwm/fpwm/fpwm.c @@ -0,0 +1,808 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fpwm.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-25 11:45:05 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ +#include +#include "fkernel.h" +#include "ftypes.h" +#include "ferror_code.h" +#include "fdebug.h" +#include "fpwm.h" +#include "fpwm_hw.h" +#include "fparameters.h" +#include "fsleep.h" + +#define FPWM_DEBUG_TAG "PWM" +#define FPWM_ERROR(format, ...) FT_DEBUG_PRINT_E(FPWM_DEBUG_TAG, format, ##__VA_ARGS__) +#define FPWM_WARN(format, ...) FT_DEBUG_PRINT_W(FPWM_DEBUG_TAG, format, ##__VA_ARGS__) +#define FPWM_INFO(format, ...) FT_DEBUG_PRINT_I(FPWM_DEBUG_TAG, format, ##__VA_ARGS__) +#define FPWM_DEBUG(format, ...) FT_DEBUG_PRINT_D(FPWM_DEBUG_TAG, format, ##__VA_ARGS__) + +/** + * @name: FPwmReset + * @msg: reset pwm + * @param {FPwmCtrl} *pctrl, instance of FPWM controller + * @param {u32} channel, pwm module's channel, 0/1 + * @return err code information, FPWM_SUCCESS indicates success,others indicates failed + */ +FError FPwmReset(FPwmCtrl *pctrl, u32 channel) +{ + FASSERT(pctrl != NULL); + FASSERT(channel < FPWM_CHANNEL_NUM); + + u32 reg_val = 0; + int timeout = FPWM_RESET_TIMEOUT; + uintptr base_addr = pctrl->config.pwm_base_addr + FPWM_N(channel); + + reg_val = FPWM_READ_REG32(base_addr, FPWM_TIM_CTRL_OFFSET); + reg_val |= FPWM_TIM_CTRL_RESET; + FPWM_WRITE_REG32(base_addr, FPWM_TIM_CTRL_OFFSET, reg_val); + + /* Check for the reset complete*/ + do + { + reg_val = FPWM_READ_REG32(base_addr, FPWM_TIM_CTRL_OFFSET); + } + while ((FPWM_TIM_CTRL_RESET & reg_val) && (0 <= --timeout)); + + if (0 >= timeout) + { + FPWM_ERROR("timeout when wait pwm reset complete"); + return FPWM_ERR_TIMEOUT; + } + return FPWM_SUCCESS; +} + +/** + * @name: FPwmEnable + * @msg: enable pwm + * @param {FPwmCtrl} *pctrl, instance of FPWM controller + * @param {u32} channel, pwm module's channel, 0/1 + * @return + */ +void FPwmEnable(FPwmCtrl *pctrl, u32 channel) +{ + FASSERT(pctrl != NULL); + FASSERT(channel < FPWM_CHANNEL_NUM); + u32 reg_val = 0; + uintptr base_addr = pctrl->config.pwm_base_addr + FPWM_N(channel); + + reg_val = FPWM_READ_REG32(base_addr, FPWM_TIM_CTRL_OFFSET); + reg_val |= FPWM_TIM_CTRL_ENABLE; + FPWM_WRITE_REG32(base_addr, FPWM_TIM_CTRL_OFFSET, reg_val); + + pctrl->channel_ctrl_enable[channel] = TRUE; +} + +/** + * @name: FPwmDisable + * @msg: disable pwm + * @param {FPwmCtrl} *pctrl, instance of FPWM controller + * @param {u32} channel, pwm module's channel, 0/1 + * @return + */ +void FPwmDisable(FPwmCtrl *pctrl, u32 channel) +{ + FASSERT(pctrl != NULL); + FASSERT(channel < FPWM_CHANNEL_NUM); + u32 reg_val = 0; + uintptr base_addr = pctrl->config.pwm_base_addr + FPWM_N(channel); + + reg_val = FPWM_READ_REG32(base_addr, FPWM_TIM_CTRL_OFFSET); + reg_val &= (~FPWM_TIM_CTRL_ENABLE); + FPWM_WRITE_REG32(base_addr, FPWM_TIM_CTRL_OFFSET, reg_val); + + pctrl->channel_ctrl_enable[channel] = FALSE; + +} + +/** + * @name: FPwmTimCtrlModeSet + * @msg: config pwm timer counter mode. + * @param {FPwmCtrl} *pctrl, instance of FPWM controller + * @param {u32} channel, pwm module's channel, 0/1 + * @param {FPwmTimCtrlMode} mode, counter mode parameters of FPWM + * @return + */ +static void FPwmTimCtrlModeSet(FPwmCtrl *pctrl, u32 channel, FPwmTimCtrlMode mode) +{ + FASSERT(pctrl != NULL); + FASSERT(mode < FPWM_TIM_CTRL_MODE_NUM); + /* check whether the state is disabled */ + FASSERT(pctrl->channel_ctrl_enable[channel] == FALSE); + + u32 reg_val = 0; + + uintptr base_addr = pctrl->config.pwm_base_addr + FPWM_N(channel); + reg_val = FPWM_READ_REG32(base_addr, FPWM_TIM_CTRL_OFFSET); + + switch (mode) + { + /* modulo */ + case FPWM_MODULO: + reg_val &= (~FPWM_TIM_CTRL_MODE_UD); + break; + /* up-and-down */ + case FPWM_UP_DOWN: + reg_val |= FPWM_TIM_CTRL_MODE_UD; + break; + default: + break; + } + + FPWM_WRITE_REG32(base_addr, FPWM_TIM_CTRL_OFFSET, reg_val); +} + +/** + * @name: FPwmTimInterruptEnable + * @msg: enable pwm timer global and counter-overflow interrupt + * @param {FPwmCtrl} *pctrl, instance of FPWM controller + * @param {u32} channel, pwm module's channel, 0/1 + * @return + */ +static void FPwmTimInterruptEnable(FPwmCtrl *pctrl, u32 channel) +{ + FASSERT(pctrl != NULL); + u32 reg_val = 0; + uintptr base_addr = pctrl->config.pwm_base_addr + FPWM_N(channel); + + reg_val = FPWM_READ_REG32(base_addr, FPWM_TIM_CTRL_OFFSET); + reg_val &= (~(FPWM_TIM_CTRL_OVFIF_ENABLE | FPWM_TIM_CTRL_GIE)); + reg_val |= (FPWM_TIM_CTRL_OVFIF_ENABLE | FPWM_TIM_CTRL_GIE); + + FPWM_WRITE_REG32(base_addr, FPWM_TIM_CTRL_OFFSET, reg_val); +} + +/** + * @name: FPwmTimInterruptDisable + * @msg: disable pwm timer global and counter-overflow interrupt + * @param {FPwmCtrl} *pctrl, instance of FPWM controller + * @param {u32} channel, pwm module's channel, 0/1 + * @return + */ +static void FPwmTimInterruptDisable(FPwmCtrl *pctrl, u32 channel) +{ + FASSERT(pctrl != NULL); + u32 reg_val = 0; + uintptr base_addr = pctrl->config.pwm_base_addr + FPWM_N(channel); + + reg_val = FPWM_READ_REG32(base_addr, FPWM_TIM_CTRL_OFFSET); + reg_val &= (~(FPWM_TIM_CTRL_OVFIF_ENABLE | FPWM_TIM_CTRL_GIE)); + + FPWM_WRITE_REG32(base_addr, FPWM_TIM_CTRL_OFFSET, reg_val); +} + +/** + * @name: FPwmDivSet + * @msg: config pwm div, div range:0~4095, corresponding to 1~4096 + * @param {FPwmCtrl} *pctrl, instance of FPWM controller + * @param {u32} channel, pwm module's channel, 0/1 + * @param {u16} div, div parameters + * @return + */ +static void FPwmDivSet(FPwmCtrl *pctrl, u32 channel, u16 div) +{ + u32 reg_val = 0; + FASSERT(pctrl != NULL); + FASSERT(div < FPWM_TIM_CTRL_DIV_MAX); + uintptr base_addr = pctrl->config.pwm_base_addr + FPWM_N(channel); + + reg_val = FPWM_READ_REG32(base_addr, FPWM_TIM_CTRL_OFFSET); + reg_val |= (FPWM_TIM_CTRL_DIV_MASK & FPWM_TIM_CTRL_DIV(div)); + + FPWM_WRITE_REG32(base_addr, FPWM_TIM_CTRL_OFFSET, reg_val); +} + +/** + * @name: FPwmPeriodSet + * @msg: config pwm period, range:1~0xffff + * @param {FPwmCtrl} *pctrl, instance of FPWM controller + * @param {u32} channel, pwm module's channel, 0/1 + * @param {u16} pwm_period, period parameters + * @return err code information, FPWM_SUCCESS indicates success,others indicates failed + */ +static void FPwmPeriodSet(FPwmCtrl *pctrl, u32 channel, u16 pwm_period) +{ + FASSERT(pctrl != NULL); + u32 reg_val = 0; + u64 cycles = 0; + uintptr base_addr = pctrl->config.pwm_base_addr + FPWM_N(channel); + + reg_val = FPWM_READ_REG32(base_addr, FPWM_PERIOD_OFFSET); + reg_val &= (~FPWM_PERIOD_CCR_MASK); + reg_val |= (pwm_period & FPWM_PERIOD_CCR_MASK); + + FPWM_WRITE_REG32(base_addr, FPWM_PERIOD_OFFSET, reg_val); +} + +/** + * @name: FPwmDutySourceSet + * @msg: config pwm duty source, from pwm_ccr register or fifo + * @param {FPwmCtrl} *pctrl, instance of FPWM controller + * @param {u32} channel, pwm module's channel, 0/1 + * @param {FPwmDutySourceMode} duty_source, duty source parameters + * @return + */ +static void FPwmDutySourceSet(FPwmCtrl *pctrl, u32 channel, FPwmDutySourceMode duty_source) +{ + FASSERT(pctrl != NULL); + FASSERT(duty_source < FPWM_DUTY_SEL_MODE_NUM); + u32 reg_val = 0; + uintptr base_addr = pctrl->config.pwm_base_addr + FPWM_N(channel); + + reg_val = FPWM_READ_REG32(base_addr, FPWM_CTRL_OFFSET); + switch (duty_source) + { + /* duty from PWM_CCR */ + case FPWM_DUTY_CCR: + reg_val &= (~FPWM_CTRL_DUTY_SOURCE_FIFO); + break; + /* from FIFO */ + case FPWM_DUTY_FIFO: + reg_val |= FPWM_CTRL_DUTY_SOURCE_FIFO; + break; + default: + break; + } + + FPWM_WRITE_REG32(base_addr, FPWM_CTRL_OFFSET, reg_val); +} + +/** + * @name: FPwmPulseSet + * @msg: config pwm duty, pwm_ccr is less than pwm_period + * @param {FPwmCtrl} *pctrl, instance of FPWM controller + * @param {u32} channel, pwm module's channel, 0/1 + * @param {u16} pwm_ccr, pwm_ccr parameters + * @return err code information, FPWM_SUCCESS indicates success,others indicates failed + */ +FError FPwmPulseSet(FPwmCtrl *pctrl, u32 channel, u16 pwm_ccr) +{ + FASSERT(pctrl != NULL); + FASSERT(channel < FPWM_CHANNEL_NUM); + u32 reg_val = 0; + u64 cycles = 0; + u32 state = 0; + u16 pwm_period_ccr = 0; + + uintptr base_addr = pctrl->config.pwm_base_addr + FPWM_N(channel); + + /* Check the pwm_ccr < pwm_period_ccr */ + pwm_period_ccr = (u16)FPWM_READ_REG32(base_addr, FPWM_PERIOD_OFFSET); + if (pwm_ccr > pwm_period_ccr) + { + FPWM_ERROR("pwm ccr is bigger than period"); + return FPWM_ERR_INVAL_PARM; + } + + reg_val = FPWM_READ_REG32(base_addr, FPWM_CTRL_OFFSET); + + /* Check the duty source */ + if (reg_val & FPWM_CTRL_DUTY_SOURCE_FIFO) + { + /* Check the duty fifo is not full */ + state = FPWM_READ_REG32(base_addr, FPWM_STATE_OFFSET); + if (state & FPWM_STATE_FIFO_FULL) + { + FPWM_ERROR("pwm state fifo full"); + return FPWM_ERR_CMD_FAILED; + } + } + + reg_val = FPWM_READ_REG32(base_addr, FPWM_CCR_OFFSET); + reg_val &= (~FPWM_CCR_MASK); + reg_val |= (pwm_ccr & FPWM_CCR_MASK); + + FPWM_WRITE_REG32(base_addr, FPWM_CCR_OFFSET, reg_val); + + return FPWM_SUCCESS; +} + +/** + * @name: FPwmCtrlModeSet + * @msg: config pwm mode, currently only support compare output + * @param {FPwmCtrl} *pctrl, instance of FPWM controller + * @param {u32} channel, pwm module's channel, 0/1 + * @return + */ +static void FPwmCtrlModeSet(FPwmCtrl *pctrl, u32 channel) +{ + FASSERT(pctrl != NULL); + u32 reg_val = 0; + uintptr base_addr = pctrl->config.pwm_base_addr + FPWM_N(channel); + + reg_val = FPWM_READ_REG32(base_addr, FPWM_CTRL_OFFSET); + + reg_val |= FPWM_CTRL_MODE_OUTPUT; + + FPWM_WRITE_REG32(base_addr, FPWM_CTRL_OFFSET, reg_val); +} + +/** + * @name: FPwmPolaritySet + * @msg: config pwm compare output polarity + * @param {FPwmCtrl} *pctrl, instance of FPWM controller + * @param {u32} channel, pwm module's channel, 0/1 + * @param {FPwmPolarity} polarity, compare output polarity parameters + * @return + */ +static void FPwmPolaritySet(FPwmCtrl *pctrl, u32 channel, FPwmPolarity polarity) +{ + FASSERT(pctrl != NULL); + FASSERT(polarity < FPWM_POLARITY_NUM); + /* check whether the state is disabled */ + FASSERT(pctrl->channel_ctrl_enable[channel] == FALSE); + uintptr base_addr = pctrl->config.pwm_base_addr + FPWM_N(channel); + + u32 reg_val = 0; + reg_val = FPWM_READ_REG32(base_addr, FPWM_CTRL_OFFSET); + reg_val &= (~FPWM_CTRL_CMP_MASK); + reg_val |= FPWM_CTRL_CMP(polarity); + FPWM_WRITE_REG32(base_addr, FPWM_CTRL_OFFSET, reg_val); +} + +/** + * @name: FPwmDbReset + * @msg: reset pwm db + * @param {FPwmCtrl} *pctrl, instance of FPWM controller + * @return err code information, FPWM_SUCCESS indicates success,others indicates failed + */ +static FError FPwmDbReset(FPwmCtrl *pctrl) +{ + FASSERT(pctrl != NULL); + + u32 reg_val = 0; + int timeout = FPWM_RESET_TIMEOUT; + uintptr base_addr = pctrl->config.db_base_addr; + + reg_val = FPWM_READ_REG32(base_addr, FPWM_DB_CTRL_OFFSET); + reg_val |= FPWM_DB_CTRL_RESET; + FPWM_WRITE_REG32(base_addr, FPWM_DB_CTRL_OFFSET, reg_val); + + /* Check for the db reset complete*/ + do + { + reg_val = FPWM_READ_REG32(base_addr, FPWM_DB_CTRL_OFFSET); + } + while ((FPWM_DB_CTRL_RESET & reg_val) && (0 <= --timeout)); + + if (0 >= timeout) + { + FPWM_ERROR("timeout when wait pwm db reset complete"); + return FPWM_ERR_TIMEOUT; + } + return FPWM_SUCCESS; +} + +/** + * @name: FPwmDbPolaritySet + * @msg: config pwm db polarity + * @param {FPwmCtrl} *pctrl, instance of FPWM controller + * @param {FPwmDbPolarity} db_polarity, db polarity parameters + * @return err code information, FPWM_SUCCESS indicates success,others indicates failed + */ +FError FPwmDbPolaritySet(FPwmCtrl *pctrl, FPwmDbPolarity db_polarity) +{ + FASSERT(pctrl != NULL); + FASSERT(db_polarity < FPWM_DB_POLARITY_NUM); + u32 reg_val = 0; + uintptr base_addr = pctrl->config.db_base_addr; + + reg_val = FPWM_READ_REG32(base_addr, FPWM_DB_CTRL_OFFSET); + reg_val &= (~FPWM_DB_CTRL_POLSEL_MASK); + reg_val |= FPWM_DB_CTRL_POLSEL(db_polarity); + FPWM_WRITE_REG32(base_addr, FPWM_DB_CTRL_OFFSET, reg_val); + + return FPWM_SUCCESS; +} + +/** + * @name: FPwmDbFallCycleSet + * @msg: config pwm db fall cycle + * @param {FPwmCtrl} *pctrl, instance of FPWM controller + * @param {u16} fall_edge_delay, db fall cycle parameters + * @return + */ +static void FPwmDbFallCycleSet(FPwmCtrl *pctrl, u16 fall_edge_delay) +{ + FASSERT(pctrl != NULL); + FASSERT(fall_edge_delay < FPWM_DB_DLY_MAX); + u32 reg_val = 0; + uintptr pwm_base_addr = pctrl->config.pwm_base_addr; + uintptr db_base_addr = pctrl->config.db_base_addr; + + u32 pwm_period = FPWM_READ_REG32(pwm_base_addr, FPWM_PERIOD_OFFSET); + + reg_val = FPWM_READ_REG32(db_base_addr, FPWM_DB_DLY_OFFSET); + reg_val &= (~FPWM_DB_DLY_FALL_MASK); + reg_val |= FPWM_DB_DLY_FALL(fall_edge_delay); + FPWM_WRITE_REG32(db_base_addr, FPWM_DB_DLY_OFFSET, reg_val); +} + +/** + * @name: FPwmDbRiseCycleSet + * @msg: config pwm db rise cycle + * @param {FPwmCtrl} *pctrl, instance of FPWM controller + * @param {u16} rise_edge_delay, db rise cycle parameters + * @return + */ +static void FPwmDbRiseCycleSet(FPwmCtrl *pctrl, u16 rise_edge_delay) +{ + FASSERT(pctrl != NULL); + FASSERT(rise_edge_delay < FPWM_DB_DLY_MAX); + u32 reg_val = 0; + uintptr pwm_base_addr = pctrl->config.pwm_base_addr; + uintptr db_base_addr = pctrl->config.db_base_addr; + + u32 pwm_period = FPWM_READ_REG32(pwm_base_addr, FPWM_PERIOD_OFFSET); + + reg_val = FPWM_READ_REG32(db_base_addr, FPWM_DB_DLY_OFFSET); + reg_val &= (~FPWM_DB_DLY_RISE_MASK); + reg_val |= (u32)(rise_edge_delay & FPWM_DB_DLY_RISE_MASK); + FPWM_WRITE_REG32(db_base_addr, FPWM_DB_DLY_OFFSET, reg_val); +} + +/** + * @name: FPwmDbInModeSet + * @msg: config pwm db in source mode, pwm0 or pwm1 + * @param {FPwmCtrl} *pctrl, instance of FPWM controller + * @param {FPwmDbInMode} db_in_mode, db in mode parameters + * @return void + */ +static void FPwmDbInModeSet(FPwmCtrl *pctrl, FPwmDbInMode db_in_mode) +{ + FASSERT(db_in_mode < FPWM_DB_IN_MODE_NUM); + u32 reg_val = 0; + uintptr base_addr = pctrl->config.db_base_addr; + + reg_val = FPWM_READ_REG32(base_addr, FPWM_DB_CTRL_OFFSET); + switch (db_in_mode) + { + case FPWM_DB_IN_MODE_PWM0: + reg_val &= (~FPWM_DB_CTRL_IN_MODE); + break; + case FPWM_DB_IN_MODE_PWM1: + reg_val |= FPWM_DB_CTRL_IN_MODE; + break; + default: + break; + } + + FPWM_WRITE_REG32(base_addr, FPWM_DB_CTRL_OFFSET, reg_val); +} + +static void FPwmDbOutModeSet(FPwmCtrl *pctrl, FPwmDbOutMode db_out_mode) +{ + FASSERT(db_out_mode < FPWM_DB_OUT_MODE_NUM); + u32 reg_val = 0; + uintptr base_addr = pctrl->config.db_base_addr; + + reg_val = FPWM_READ_REG32(base_addr, FPWM_DB_CTRL_OFFSET); + reg_val &= (~FPWM_DB_CTRL_OUT_MODE_MASK); + reg_val |= FPWM_DB_CTRL_OUT_MODE(db_out_mode); + FPWM_WRITE_REG32(base_addr, FPWM_DB_CTRL_OFFSET, reg_val); +} + +/** + * @name: FPwmInterruptEnable + * @msg: enable pwm compare output interrupt + * @param {FPwmCtrl} *pctrl, instance of FPWM controller + * @param {u32} channel, pwm module's channel, 0/1 + * @param {FPwmIntrEventType} intr_type, pwm interrupt event type + * @return + */ +void FPwmInterruptEnable(FPwmCtrl *pctrl, u32 channel, FPwmIntrEventType intr_type) +{ + FASSERT(pctrl != NULL); + FASSERT(channel < FPWM_CHANNEL_NUM); + u32 reg_val = 0; + uintptr base_addr = pctrl->config.pwm_base_addr + FPWM_N(channel); + + reg_val = FPWM_READ_REG32(base_addr, FPWM_CTRL_OFFSET); + + if (intr_type == FPWM_INTR_EVENT_COUNTER) + { + reg_val &= (~FPWM_CTRL_INTR_COUNTER_ENABLE); + reg_val |= FPWM_CTRL_INTR_COUNTER_ENABLE; + } + + if (intr_type == FPWM_INTR_EVENT_FIFO_EMPTY) + { + reg_val &= (~FPWM_CTRL_INTR_FIFO_EMPTY_ENABLE); + reg_val |= FPWM_CTRL_INTR_FIFO_EMPTY_ENABLE; + } + + FPWM_WRITE_REG32(base_addr, FPWM_CTRL_OFFSET, reg_val); +} + +/** + * @name: FPwmInterruptDisable + * @msg: disable pwm compare output interrupt + * @param {FPwmCtrl} *pctrl, instance of FPWM controller + * @param {u32} channel, pwm module's channel, 0/1 + * @param {FPwmIntrEventType} intr_type, pwm interrupt event type + * @return + */ +void FPwmInterruptDisable(FPwmCtrl *pctrl, u32 channel, FPwmIntrEventType intr_type) +{ + FASSERT(pctrl != NULL); + FASSERT(channel < FPWM_CHANNEL_NUM); + u32 reg_val = 0; + uintptr base_addr = pctrl->config.pwm_base_addr + FPWM_N(channel); + + reg_val = FPWM_READ_REG32(base_addr, FPWM_CTRL_OFFSET); + + if (intr_type == FPWM_INTR_EVENT_COUNTER) + { + reg_val &= (~FPWM_CTRL_INTR_COUNTER_ENABLE); + } + + if (intr_type == FPWM_INTR_EVENT_FIFO_EMPTY) + { + reg_val &= (~FPWM_CTRL_INTR_FIFO_EMPTY_ENABLE); + } + + FPWM_WRITE_REG32(base_addr, FPWM_CTRL_OFFSET, reg_val); +} + +/** + * @name: FPwmDbVariableSet + * @msg: set pwm db Variable config, users need call this function before + * FPwmVariableSet if you want to use deadband function. + * @param {FPwmCtrl} *pctrl, instance of FPWM controller + * @param {FPwmDbVariableConfig} db_cfg_p, db config parameters, include mode and cycles + * @return err code information, FPWM_SUCCESS indicates success,others indicates failed + */ +FError FPwmDbVariableSet(FPwmCtrl *pctrl, FPwmDbVariableConfig *db_cfg_p) +{ + FASSERT(pctrl != NULL); + FASSERT(db_cfg_p != NULL); + + FError ret = FPWM_SUCCESS; + + ret = FPwmDbReset(pctrl); + if (ret != FPWM_SUCCESS) + { + FPWM_ERROR("FPwmDbVariableSet FPwmDbReset failed"); + return FPWM_ERR_CMD_FAILED; + } + + FPwmDbRiseCycleSet(pctrl, db_cfg_p->db_rise_cycle); + + FPwmDbFallCycleSet(pctrl, db_cfg_p->db_fall_cycle); + + ret = FPwmDbPolaritySet(pctrl, db_cfg_p->db_polarity_sel); + if (ret != FPWM_SUCCESS) + { + FPWM_ERROR("FPwmDbVariableSet FPwmDbPolaritySet failed"); + return FPWM_ERR_CMD_FAILED; + } + + FPwmDbInModeSet(pctrl, db_cfg_p->db_in_mode); + + FPwmDbOutModeSet(pctrl, db_cfg_p->db_out_mode); + + return ret; +} + +/** + * @name: FPwmDbVariableGet + * @msg: get pwm deadband variable configuration. + * @param {FPwmCtrl} *pctrl, instance of FPWM controller + * @param {FPwmDbVariableConfig} *db_cfg_p, pwm deadband config parameters + * @return err code information, FPWM_SUCCESS indicates success,others indicates failed + */ +FError FPwmDbVariableGet(FPwmCtrl *pctrl, FPwmDbVariableConfig *db_cfg_p) +{ + FASSERT(pctrl != NULL); + FASSERT(db_cfg_p != NULL); + FError ret = FPWM_SUCCESS; + + uintptr db_base_addr = pctrl->config.db_base_addr; + + u32 db_ctrl = FPWM_READ_REG32(db_base_addr, FPWM_DB_CTRL_OFFSET); + /* db input source*/ + db_cfg_p->db_in_mode = + (db_ctrl & FPWM_DB_CTRL_IN_MODE) ? FPWM_DB_IN_MODE_PWM1 : FPWM_DB_IN_MODE_PWM0; + + /* db polarity select*/ + db_cfg_p->db_polarity_sel = FPWM_DB_CTRL_POLSEL_GET(db_ctrl & FPWM_DB_CTRL_POLSEL_MASK); + /* db output mode*/ + db_cfg_p->db_out_mode = FPWM_DB_CTRL_OUT_MODE_GET(db_ctrl & FPWM_DB_CTRL_OUT_MODE_MASK); + + u32 db_dly = FPWM_READ_REG32(db_base_addr, FPWM_DB_DLY_OFFSET); + /* db falling edge delay cycle */ + db_cfg_p->db_fall_cycle = FPWM_DB_DLY_FALL_GET(db_dly & FPWM_DB_DLY_FALL_MASK); + /* db rising edge delay cycle */ + db_cfg_p->db_rise_cycle = (db_dly & FPWM_DB_DLY_RISE_MASK); + + return ret; + +} + +/** + * @name: FPwmVariableSet + * @msg: set pwm channel variable configuration, users need call this function after + * FPwmDbVariableSet if you want to use deadband function. + * @param {FPwmCtrl} *pctrl, instance of FPWM controller + * @param {u32} channel, pwm module's channel, 0/1 + * @param {FPwmVariableConfig} pwm_cfg_p, pwm config parameters, include mode and duty + * @return err code information, FPWM_SUCCESS indicates success,others indicates failed + */ +FError FPwmVariableSet(FPwmCtrl *pctrl, u32 channel, FPwmVariableConfig *pwm_cfg_p) +{ + FASSERT(pctrl != NULL); + FASSERT(channel < FPWM_CHANNEL_NUM); + FASSERT(pwm_cfg_p != NULL); + FError ret = FPWM_SUCCESS; + + /* enable lsd pwm syn */ + FPwmLsdEnable(FLSD_CONFIG_BASE, pctrl->config.instance_id); + + /* bit[0]:set pwm_tim_ctrl SW_RST */ + ret = FPwmReset(pctrl, channel); + if (ret != FPWM_SUCCESS) + { + FPWM_ERROR("FPwmVariableSet FPwmReset failed"); + return FPWM_ERR_CMD_FAILED; + } + + /* pwm timer ctrl disable, before can config */ + FPwmDisable(pctrl, channel); + + /* bit[2]:set tim_ctrl Mode */ + FPwmTimCtrlModeSet(pctrl, channel, pwm_cfg_p->tim_ctrl_mode); + + /* bit[4,5]:set tim_ctrl interrput */ + FPwmTimInterruptEnable(pctrl, channel); + + /*bit[16~27]: set tim_ctrl DIV 0~4095 */ + FPwmDivSet(pctrl, channel, pwm_cfg_p->tim_ctrl_div); + + /*bit[0~15]: set pwm_period */ + FPwmPeriodSet(pctrl, channel, pwm_cfg_p->pwm_period); + + /*bit[2]:pwm control mode, input capture or output compare */ + FPwmCtrlModeSet(pctrl, channel); + + /*bit[3]:pwm control mode irq */ + FPwmInterruptEnable(pctrl, channel, FPWM_INTR_EVENT_COUNTER); + FPwmInterruptEnable(pctrl, channel, FPWM_INTR_EVENT_FIFO_EMPTY); + + /*bit[4~6]:pwm ctrl polarity config CMP:0b100*/ + FPwmPolaritySet(pctrl, channel, pwm_cfg_p->pwm_polarity); + + /*bit[8]:pwm duty source , duty from ccr or fifo */ + FPwmDutySourceSet(pctrl, channel, pwm_cfg_p->pwm_duty_source_mode); + + /*pwm pulse set, duty */ + ret = FPwmPulseSet(pctrl, channel, pwm_cfg_p->pwm_pulse); + if (ret != FPWM_SUCCESS) + { + FPWM_ERROR("FPwmVariableSet FPwmPulseSet failed"); + return FPWM_ERR_CMD_FAILED; + } + + return ret; +} + +/** + * @name: FPwmVariableGet + * @msg: get pwm channel variable configuration. + * @param {FPwmCtrl} *pctrl, instance of FPWM controller + * @param {u32} channel, pwm module's channel, 0/1 + * @param {FPwmVariableConfig} *pwm_cfg_p, pwm config parameters, include mode and duty + * @return err code information, FPWM_SUCCESS indicates success,others indicates failed + */ +FError FPwmVariableGet(FPwmCtrl *pctrl, u32 channel, FPwmVariableConfig *pwm_cfg_p) +{ + FASSERT(pctrl != NULL); + FASSERT(channel < FPWM_CHANNEL_NUM); + FASSERT(pwm_cfg_p != NULL); + FError ret = FPWM_SUCCESS; + + uintptr pwm_base_addr = pctrl->config.pwm_base_addr + FPWM_N(channel); + + u32 tim_ctrl = FPWM_READ_REG32(pwm_base_addr, FPWM_TIM_CTRL_OFFSET); + /* tim_ctrl mode, counter mode */ + pwm_cfg_p->tim_ctrl_mode = + (tim_ctrl & FPWM_TIM_CTRL_MODE_UD) ? FPWM_UP_DOWN : FPWM_MODULO; + /* pwm divider */ + pwm_cfg_p->tim_ctrl_div = FPWM_TIM_CTRL_DIV_GET(tim_ctrl & FPWM_TIM_CTRL_DIV_MASK); + + /* pwm period value */ + u32 pwm_period = FPWM_READ_REG32(pwm_base_addr, FPWM_PERIOD_OFFSET) ; + pwm_cfg_p->pwm_period = (u16)(pwm_period & FPWM_PERIOD_CCR_MASK); + + u32 pwm_ctrl = FPWM_READ_REG32(pwm_base_addr, FPWM_CTRL_OFFSET); + /* pwm mode, compare output */ + pwm_cfg_p->pwm_mode = + (pwm_ctrl & FPWM_CTRL_MODE_OUTPUT) ? FPWM_OUTPUT_COMPARE : FPWM_NONE_MODE; + + /* pwm compare output polarity */ + pwm_cfg_p->pwm_polarity = FPWM_CTRL_CMP_GET(pwm_ctrl & FPWM_CTRL_CMP_MASK); + + /* pwm duty value source */ + pwm_cfg_p->pwm_duty_source_mode = + (pwm_ctrl & FPWM_CTRL_DUTY_SOURCE_FIFO) ? FPWM_DUTY_FIFO : FPWM_DUTY_CCR; + + /* pwm pulse value */ + u32 pwm_ccr = FPWM_READ_REG32(pwm_base_addr, FPWM_CCR_OFFSET); + pwm_cfg_p->pwm_pulse = (u16)(pwm_ccr & FPWM_CCR_MASK); + + return ret; +} + +/** + * @name: FPwmDeInitialize + * @msg: DeInitialization function for the device instance + * @param {FPwmCtrl} *pctrl, instance of FPWM controller + * @return {*} + */ +void FPwmDeInitialize(FPwmCtrl *pctrl) +{ + FASSERT(pctrl); + + pctrl->is_ready = 0; + memset(pctrl, 0, sizeof(*pctrl)); + + return; +} + +/** + * @name: FPwmCfgInitialize + * @msg: Initializes a specific instance such that it is ready to be used. + * @param {FPwmCtrl} *pctrl, instance of FPWM controller + * @param {FPwmConfig} *input_config_p, Configuration parameters of FPWM + * @return err code information, FPWM_SUCCESS indicates success,others indicates failed + */ +FError FPwmCfgInitialize(FPwmCtrl *pctrl, const FPwmConfig *input_config_p) +{ + FASSERT(pctrl && input_config_p); + + FError ret = FPWM_SUCCESS; + /* + * If the device is started, disallow the initialize and return a Status + * indicating it is started. This allows the user to de-initialize the device + * and reinitialize, but prevents a user from inadvertently + * initializing. + */ + if (FT_COMPONENT_IS_READY == pctrl->is_ready) + { + FPWM_WARN("device is already initialized!!!"); + } + + /*Set default values and configuration data */ + FPwmDeInitialize(pctrl); + + pctrl->config = *input_config_p; + + ret = FPwmDbReset(pctrl); + if (ret != FPWM_SUCCESS) + { + FPWM_ERROR("FPwmDbVariableSet FPwmDbReset failed"); + return FPWM_ERR_CMD_FAILED; + } + + pctrl->is_ready = FT_COMPONENT_IS_READY; + + return ret; +} diff --git a/bsp/phytium/libraries/standalone/drivers/pwm/fpwm/fpwm.h b/bsp/phytium/libraries/standalone/drivers/pwm/fpwm/fpwm.h new file mode 100644 index 0000000000..80e62ae896 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/pwm/fpwm/fpwm.h @@ -0,0 +1,220 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fpwm.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-25 11:45:05 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef BSP_DRIVERS_FPWM_H +#define BSP_DRIVERS_FPWM_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ftypes.h" +#include "fdebug.h" +#include "ferror_code.h" +#include "fkernel.h" +#include "fassert.h" +#include "fparameters.h" + +#define FPWM_SUCCESS FT_SUCCESS +#define FPWM_ERR_INVAL_PARM FT_MAKE_ERRCODE(ErrModBsp, ErrBspPwm, 1) +#define FPWM_ERR_NOT_READY FT_MAKE_ERRCODE(ErrModBsp, ErrBspPwm, 2) +#define FPWM_ERR_TIMEOUT FT_MAKE_ERRCODE(ErrModBsp, ErrBspPwm, 3) +#define FPWM_ERR_NOT_SUPPORT FT_MAKE_ERRCODE(ErrModBsp, ErrBspPwm, 4) +#define FPWM_ERR_CMD_FAILED FT_MAKE_ERRCODE(ErrModBsp, ErrBspPwm, 5) + +typedef enum +{ + FPWM_INTR_EVENT_COUNTER = 0, /**< Handler type for counter interrupt */ + FPWM_INTR_EVENT_FIFO_EMPTY = 1, /**< Handler type for fifo empty interrupt*/ + FPWM_INTR_EVENT_NUM +} FPwmIntrEventType; + +/* duty sel */ +typedef enum +{ + FPWM_DUTY_CCR = 0, /* duty value from pwm ccr register */ + FPWM_DUTY_FIFO = 1, /* duty value from fifo */ + FPWM_DUTY_SEL_MODE_NUM +} FPwmDutySourceMode; + +/* tim_ctrl mode, counter mode */ +typedef enum +{ + FPWM_MODULO = 0, /* count from 0~period */ + FPWM_UP_DOWN = 1, /* count from 0~period~0 */ + FPWM_TIM_CTRL_MODE_NUM +} FPwmTimCtrlMode; + +/* pwm mode, only the compare output mode is supported currently*/ +typedef enum +{ + FPWM_NONE_MODE = 0,/* no mode */ + FPWM_OUTPUT_COMPARE = 1,/* compare output mode */ + FPWM_CTRL_MODE_NUM +} FPwmCtrlMode; + +/** + * enum pwm_polarity - polarity of a PWM compare signal + * @FPWM_POLARITY_NORMAL: a high signal for the duration of the duty- + * cycle, followed by a low signal for the remainder of the pulse + * period + * @FPWM_POLARITY_INVERSED: a low signal for the duration of the duty- + * cycle, followed by a high signal for the remainder of the pulse + * period + */ +typedef enum +{ + FPWM_POLARITY_OUTPUT_HIGH = 0b000, + FPWM_POLARITY_OUTPUT_LOW = 0b001, + FPWM_POLARITY_OUTPUT_FLIP = 0b010, + FPWM_POLARITY_INVERSED = 0b011, + FPWM_POLARITY_NORMAL = 0b100, + FPWM_POLARITY_CCR_LOW = 0b101, + FPWM_POLARITY_CCR_HIGH = 0b110, + FPWM_POLARITY_INIT = 0b111, + FPWM_POLARITY_NUM +} FPwmPolarity; + + +/* db polarity select */ +typedef enum +{ + FPWM_DB_AH = 0, /* no flip */ + FPWM_DB_ALC = 1,/* pwm0 flip */ + FPWM_DB_AHC, /* pwm1 flip */ + FPWM_DB_AL, /* pwm0 and pwm1 flip */ + FPWM_DB_POLARITY_NUM +} FPwmDbPolarity; + +/* db out mode */ +typedef enum +{ + FPWM_DB_OUT_MODE_BYPASS = 0b00, /* by pass */ + FPWM_DB_OUT_MODE_FORBID_RISE = 0b01,/* forbid rise delay */ + FPWM_DB_OUT_MODE_FORBID_FALL = 0b10,/* forbid fall delay */ + FPWM_DB_OUT_MODE_ENABLE_RISE_FALL = 0b11,/* enable rise and fall delay */ + FPWM_DB_OUT_MODE_NUM +} FPwmDbOutMode; + +/* db input source select, channel 0 or 1 */ +typedef enum +{ + FPWM_DB_IN_MODE_PWM0 = 0,/* db input source choose pwm0 */ + FPWM_DB_IN_MODE_PWM1 = 1,/* db input source choose pwm1 */ + FPWM_DB_IN_MODE_NUM +} FPwmDbInMode; + +typedef struct +{ + FPwmDbPolarity db_polarity_sel;/* db polarity select*/ + FPwmDbOutMode db_out_mode;/* db output mode*/ + FPwmDbInMode db_in_mode;/* db input source*/ + u16 db_fall_cycle;/* db falling edge delay cycle */ + u16 db_rise_cycle;/* db rising edge delay cycle */ +} FPwmDbVariableConfig; + +typedef struct +{ + FPwmTimCtrlMode tim_ctrl_mode;/* tim_ctrl mode, counter mode */ + u16 tim_ctrl_div;/* pwm divider */ + u16 pwm_period;/* pwm period value */ + FPwmCtrlMode pwm_mode;/* pwm mode, compare output */ + FPwmPolarity pwm_polarity;/* pwm compare output polarity */ + FPwmDutySourceMode pwm_duty_source_mode;/* pwm duty value source */ + u16 pwm_pulse;/* pwm pulse value */ + +} FPwmVariableConfig; + +typedef struct +{ + u8 instance_id;/* pwm id */ + uintptr db_base_addr; + uintptr pwm_base_addr; + + u64 base_clk; + u32 irq_num[FPWM_CHANNEL_NUM]; /* pwm irq num */ + u32 irq_prority[FPWM_CHANNEL_NUM]; /* pwm irq priority */ + const char *instance_name;/* instance name */ + +} FPwmConfig; /* Pwm配置 */ + +typedef void (*FPwmIntrEventHandler)(void *param); + +typedef struct +{ + FPwmConfig config;/* Pwm配置 */ + u32 is_ready;/* Pwm初始化完成标志 */ + + u8 channel_ctrl_enable[FPWM_CHANNEL_NUM]; /* pwm channel ctrl enable state */ + + FPwmIntrEventHandler event_handler[FPWM_INTR_EVENT_NUM]; /* event handler for interrupt */ + void *event_param[FPWM_INTR_EVENT_NUM]; /* parameters ptr of event handler */ + +} FPwmCtrl; + + +/* interrupt handler function */ +void FPwmIntrHandler(s32 vector, void *args); + +/* register the handler function */ +void FPwmRegisterInterruptHandler(FPwmCtrl *instance_p, FPwmIntrEventType event_type, FPwmIntrEventHandler handler, void *param); + +/* get pwm configs by id */ +const FPwmConfig *FPwmLookupConfig(FPwmInstance instance_id); + +/* DeInitialization function for the device instance */ +void FPwmDeInitialize(FPwmCtrl *pctrl); + +/*Initializes a specific instance such that it is ready to be used*/ +FError FPwmCfgInitialize(FPwmCtrl *pctrl, const FPwmConfig *input_config_p); + +/* set pwm db configuration */ +FError FPwmDbVariableSet(FPwmCtrl *pctrl, FPwmDbVariableConfig *db_cfg_p); + +/* get pwm db configuration */ +FError FPwmDbVariableGet(FPwmCtrl *pctrl, FPwmDbVariableConfig *db_cfg_p); + +/* set pwm channel configuration */ +FError FPwmVariableSet(FPwmCtrl *pctrl, u32 channel, FPwmVariableConfig *pwm_cfg_p); + +/* get pwm channel configuration */ +FError FPwmVariableGet(FPwmCtrl *pctrl, u32 channel, FPwmVariableConfig *pwm_cfg_p); + +/* config pwm pulse, pwm_ccr is less than pwm_period */ +FError FPwmPulseSet(FPwmCtrl *pctrl, u32 channel, u16 pwm_ccr); + +/* disable pwm */ +void FPwmDisable(FPwmCtrl *pctrl, u32 channel); + +/* enable pwm */ +void FPwmEnable(FPwmCtrl *pctrl, u32 channel); + +/* dump some pwm registers value */ +void FPwmDump(uintptr base_addr); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/pwm/fpwm/fpwm_g.c b/bsp/phytium/libraries/standalone/drivers/pwm/fpwm/fpwm_g.c new file mode 100644 index 0000000000..b1bcc554e7 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/pwm/fpwm/fpwm_g.c @@ -0,0 +1,129 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fpwm_g.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-25 11:45:05 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "fparameters.h" +#include "fpwm.h" +#include "fpwm_hw.h" + +/* default configs of pwm ctrl */ +const FPwmConfig FPwmConfigTbl[FPWM_INSTANCE_NUM] = +{ + [FPWM_INSTANCE_0] = + { + .instance_id = FPWM_INSTANCE_0, + .db_base_addr = FPWM0_BASE_ADR, + .pwm_base_addr = FPWM0_BASE_ADR + FPWM_OFFSET, + .base_clk = FPWM_CLK, + .irq_num[FPWM_CHANNEL_0] = FPWM0_INTR_IRQ, + .irq_num[FPWM_CHANNEL_1] = FPWM1_INTR_IRQ, + .irq_prority[FPWM_CHANNEL_0] = 0, + .irq_prority[FPWM_CHANNEL_1] = 0, + .instance_name = "PWM_CTRL0", + }, + [FPWM_INSTANCE_1] = + { + .instance_id = FPWM_INSTANCE_1, + .db_base_addr = FPWM1_BASE_ADR, + .pwm_base_addr = FPWM1_BASE_ADR + FPWM_OFFSET, + .base_clk = FPWM_CLK, + .irq_num[FPWM_CHANNEL_0] = FPWM2_INTR_IRQ, + .irq_num[FPWM_CHANNEL_1] = FPWM3_INTR_IRQ, + .irq_prority[FPWM_CHANNEL_0] = 0, + .irq_prority[FPWM_CHANNEL_1] = 0, + .instance_name = "PWM_CTRL1", + }, + [FPWM_INSTANCE_2] = + { + .instance_id = FPWM_INSTANCE_2, + .db_base_addr = FPWM2_BASE_ADR, + .pwm_base_addr = FPWM2_BASE_ADR + FPWM_OFFSET, + .base_clk = FPWM_CLK, + .irq_num[FPWM_CHANNEL_0] = FPWM4_INTR_IRQ, + .irq_num[FPWM_CHANNEL_1] = FPWM5_INTR_IRQ, + .irq_prority[FPWM_CHANNEL_0] = 0, + .irq_prority[FPWM_CHANNEL_1] = 0, + .instance_name = "PWM_CTRL2", + }, + [FPWM_INSTANCE_3] = + { + .instance_id = FPWM_INSTANCE_3, + .db_base_addr = FPWM3_BASE_ADR, + .pwm_base_addr = FPWM3_BASE_ADR + FPWM_OFFSET, + .base_clk = FPWM_CLK, + .irq_num[FPWM_CHANNEL_0] = FPWM6_INTR_IRQ, + .irq_num[FPWM_CHANNEL_1] = FPWM7_INTR_IRQ, + .irq_prority[FPWM_CHANNEL_0] = 0, + .irq_prority[FPWM_CHANNEL_1] = 0, + .instance_name = "PWM_CTRL3", + }, + [FPWM_INSTANCE_4] = + { + .instance_id = FPWM_INSTANCE_4, + .db_base_addr = FPWM4_BASE_ADR, + .pwm_base_addr = FPWM4_BASE_ADR + FPWM_OFFSET, + .base_clk = FPWM_CLK, + .irq_num[FPWM_CHANNEL_0] = FPWM8_INTR_IRQ, + .irq_num[FPWM_CHANNEL_1] = FPWM9_INTR_IRQ, + .irq_prority[FPWM_CHANNEL_0] = 0, + .irq_prority[FPWM_CHANNEL_1] = 0, + .instance_name = "PWM_CTRL4", + }, + [FPWM_INSTANCE_5] = + { + .instance_id = FPWM_INSTANCE_5, + .db_base_addr = FPWM5_BASE_ADR, + .pwm_base_addr = FPWM5_BASE_ADR + FPWM_OFFSET, + .base_clk = FPWM_CLK, + .irq_num[FPWM_CHANNEL_0] = FPWM10_INTR_IRQ, + .irq_num[FPWM_CHANNEL_1] = FPWM11_INTR_IRQ, + .irq_prority[FPWM_CHANNEL_0] = 0, + .irq_prority[FPWM_CHANNEL_1] = 0, + .instance_name = "PWM_CTRL5", + }, + [FPWM_INSTANCE_6] = + { + .instance_id = FPWM_INSTANCE_6, + .db_base_addr = FPWM6_BASE_ADR, + .pwm_base_addr = FPWM6_BASE_ADR + FPWM_OFFSET, + .base_clk = FPWM_CLK, + .irq_num[FPWM_CHANNEL_0] = FPWM12_INTR_IRQ, + .irq_num[FPWM_CHANNEL_1] = FPWM13_INTR_IRQ, + .irq_prority[FPWM_CHANNEL_0] = 0, + .irq_prority[FPWM_CHANNEL_1] = 0, + .instance_name = "PWM_CTRL6", + }, + [FPWM_INSTANCE_7] = + { + .instance_id = FPWM_INSTANCE_7, + .db_base_addr = FPWM7_BASE_ADR, + .pwm_base_addr = FPWM7_BASE_ADR + FPWM_OFFSET, + .base_clk = FPWM_CLK, + .irq_num[FPWM_CHANNEL_0] = FPWM14_INTR_IRQ, + .irq_num[FPWM_CHANNEL_1] = FPWM15_INTR_IRQ, + .irq_prority[FPWM_CHANNEL_0] = 0, + .irq_prority[FPWM_CHANNEL_1] = 0, + .instance_name = "PWM_CTRL7", + }, + + +}; \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/pwm/fpwm/fpwm_hw.c b/bsp/phytium/libraries/standalone/drivers/pwm/fpwm/fpwm_hw.c new file mode 100644 index 0000000000..33bce2b6c1 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/pwm/fpwm/fpwm_hw.c @@ -0,0 +1,103 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fpwm_hw.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-25 11:45:05 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include +#include "fparameters.h" +#include "fpwm_hw.h" +#include "fassert.h" + +/** + * @name: FPwmLsdEnable + * @msg: enable pwm lsd syn + * @param {uintptr} lsd_addr, base address of the lsd_pwm_syn + * @param {u8} pwm_id, pwm id parameters of FPWM + * @return + */ +void FPwmLsdEnable(uintptr lsd_addr, u8 pwm_id) +{ + FASSERT(pwm_id < FPWM_INSTANCE_NUM); + u32 reg_val = 0; + + reg_val = FPWM_READ_REG32(lsd_addr, FLSD_MIO_PWM_SYN_OFFSET); + + reg_val |= (FLSD_MIO_PWM_SYN_MASK & (1 << pwm_id)); + + FPWM_WRITE_REG32(lsd_addr, FLSD_MIO_PWM_SYN_OFFSET, reg_val); +} + +/** + * @name: FPwmLsdDisable + * @msg: disable pwm lsd syn + * @param {uintptr} lsd_addr, base address of the lsd_pwm_syn + * @param {u8} pwm_id, pwm id parameters of FPWM + * @return + */ +void FPwmLsdDisable(uintptr lsd_addr, u8 pwm_id) +{ + FASSERT(pwm_id < FPWM_INSTANCE_NUM); + u32 reg_val = 0; + + reg_val = FPWM_READ_REG32(lsd_addr, FLSD_MIO_PWM_SYN_OFFSET); + + reg_val &= (~(FLSD_MIO_PWM_SYN_MASK & (1 << pwm_id))); + + FPWM_WRITE_REG32(lsd_addr, FLSD_MIO_PWM_SYN_OFFSET, reg_val); +} + + +/** + * @name: FPwmDump + * @msg: dump some pwm registers value. + * @param {FPwmCtrl} *pctrl, instance of FPWM controller + * @return + */ +void FPwmDump(uintptr base_addr) +{ + uintptr db_base_addr = base_addr; + uintptr pwm_base_addr = base_addr + FPWM_OFFSET; + + printf("Off[0x%x]: FPWM_DB_CTRL_OFFSET = 0x%08x\r\n", db_base_addr + FPWM_DB_CTRL_OFFSET, FPWM_READ_REG32(db_base_addr, FPWM_DB_CTRL_OFFSET)); + printf("Off[0x%x]: FPWM_DB_DLY_OFFSET = 0x%08x\r\n", db_base_addr + FPWM_DB_DLY_OFFSET, FPWM_READ_REG32(db_base_addr, FPWM_DB_DLY_OFFSET)); + printf("\r\n"); + printf("Off[0x%x]: FPWM_TIM_CNT_OFFSET = 0x%08x\r\n", pwm_base_addr + FPWM_TIM_CNT_OFFSET, FPWM_READ_REG32(pwm_base_addr, FPWM_TIM_CNT_OFFSET)); + printf("Off[0x%x]: FPWM_TIM_CTRL_OFFSET = 0x%08x\r\n", pwm_base_addr + FPWM_TIM_CTRL_OFFSET, FPWM_READ_REG32(pwm_base_addr, FPWM_TIM_CTRL_OFFSET)); + printf("Off[0x%x]: FPWM_STATE_OFFSET = 0x%08x\r\n", pwm_base_addr + FPWM_STATE_OFFSET, FPWM_READ_REG32(pwm_base_addr, FPWM_STATE_OFFSET)); + printf("Off[0x%x]: FPWM_PERIOD_OFFSET = 0x%08x\r\n", pwm_base_addr + FPWM_PERIOD_OFFSET, FPWM_READ_REG32(pwm_base_addr, FPWM_PERIOD_OFFSET)); + printf("Off[0x%x]: FPWM_CTRL_OFFSET = 0x%08x\r\n", pwm_base_addr + FPWM_CTRL_OFFSET, FPWM_READ_REG32(pwm_base_addr, FPWM_CTRL_OFFSET)); + printf("Off[0x%x]: FPWM_CCR_OFFSET = 0x%08x\r\n", pwm_base_addr + FPWM_CCR_OFFSET, FPWM_READ_REG32(pwm_base_addr, FPWM_CCR_OFFSET)); + + pwm_base_addr = pwm_base_addr + FPWM_OFFSET; + printf("\r\n"); + printf("Off[0x%x]: FPWM_TIM_CNT_OFFSET = 0x%08x\r\n", pwm_base_addr + FPWM_TIM_CNT_OFFSET, FPWM_READ_REG32(pwm_base_addr, FPWM_TIM_CNT_OFFSET)); + printf("Off[0x%x]: FPWM_TIM_CTRL_OFFSET = 0x%08x\r\n", pwm_base_addr + FPWM_TIM_CTRL_OFFSET, FPWM_READ_REG32(pwm_base_addr, FPWM_TIM_CTRL_OFFSET)); + printf("Off[0x%x]: FPWM_STATE_OFFSET = 0x%08x\r\n", pwm_base_addr + FPWM_STATE_OFFSET, FPWM_READ_REG32(pwm_base_addr, FPWM_STATE_OFFSET)); + printf("Off[0x%x]: FPWM_PERIOD_OFFSET = 0x%08x\r\n", pwm_base_addr + FPWM_PERIOD_OFFSET, FPWM_READ_REG32(pwm_base_addr, FPWM_PERIOD_OFFSET)); + printf("Off[0x%x]: FPWM_CTRL_OFFSET = 0x%08x\r\n", pwm_base_addr + FPWM_CTRL_OFFSET, FPWM_READ_REG32(pwm_base_addr, FPWM_CTRL_OFFSET)); + printf("Off[0x%x]: FPWM_CCR_OFFSET = 0x%08x\r\n", pwm_base_addr + FPWM_CCR_OFFSET, FPWM_READ_REG32(pwm_base_addr, FPWM_CCR_OFFSET)); + + + printf("Off[0x%x]: FPWM_LSD_OFFSET = 0x%08x\r\n", FLSD_CONFIG_BASE + FLSD_MIO_PWM_SYN_OFFSET, FPWM_READ_REG32(FLSD_CONFIG_BASE, FLSD_MIO_PWM_SYN_OFFSET)); + + printf("\r\n"); + +} diff --git a/bsp/phytium/libraries/standalone/drivers/pwm/fpwm/fpwm_hw.h b/bsp/phytium/libraries/standalone/drivers/pwm/fpwm/fpwm_hw.h new file mode 100644 index 0000000000..c1efe992bd --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/pwm/fpwm/fpwm_hw.h @@ -0,0 +1,143 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fpwm_hw.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-25 11:45:05 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef BSP_DRIVERS_FPWM_HW_H +#define BSP_DRIVERS_FPWM_HW_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "fkernel.h" +#include "ftypes.h" +#include "fio.h" +#include "fparameters.h" + +/* pwm register definitions */ +#define FPWM_BASE_ADR(n) ((FPWM_CONTROL_BASE)+(n<<12)) /* 0<=n<=7 */ + +#define FPWM0_BASE_ADR FPWM_BASE_ADR(0) /* PWM 0 base address */ +#define FPWM1_BASE_ADR FPWM_BASE_ADR(1) /* PWM 1 base address */ +#define FPWM2_BASE_ADR FPWM_BASE_ADR(2) +#define FPWM3_BASE_ADR FPWM_BASE_ADR(3) +#define FPWM4_BASE_ADR FPWM_BASE_ADR(4) +#define FPWM5_BASE_ADR FPWM_BASE_ADR(5) +#define FPWM6_BASE_ADR FPWM_BASE_ADR(6) +#define FPWM7_BASE_ADR FPWM_BASE_ADR(7) + +/* DB register */ +#define FPWM_DB_CTRL_OFFSET 0x00 +#define FPWM_DB_DLY_OFFSET 0x04 +#define FPWM_OFFSET 0x400 +#define FPWM_TIM_CNT_OFFSET 0x00 +#define FPWM_TIM_CTRL_OFFSET 0x04 +#define FPWM_STATE_OFFSET 0x08 +#define FPWM_PERIOD_OFFSET 0x0C +#define FPWM_CTRL_OFFSET 0x10 +#define FPWM_CCR_OFFSET 0x14 + +#define FPWM_MODE_CHANNEL 2 +#define FPWM_N(x) ((FPWM_OFFSET)*(x)) + +#define FPWM_RESET_TIMEOUT 10 + +#define NSEC_PER_SEC (1000000000ULL) + +/* pwm db_ctrl field */ +#define FPWM_DB_CTRL_RESET BIT(0) +#define FPWM_DB_CTRL_IN_MODE BIT(1) +#define FPWM_DB_CTRL_POLSEL(data) ((data) << 2) +#define FPWM_DB_CTRL_POLSEL_MASK GENMASK(3, 2) +#define FPWM_DB_CTRL_POLSEL_GET(data) ((data) >> 2) +#define FPWM_DB_CTRL_OUT_MODE(data) ((data) << 4) +#define FPWM_DB_CTRL_OUT_MODE_MASK GENMASK(5, 4) +#define FPWM_DB_CTRL_OUT_MODE_GET(data) ((data) >> 4) + +/* pwm db_ctrl field */ +#define FPWM_DB_DLY_MAX 1024 +#define FPWM_DB_DLY_RISE_MASK GENMASK(9, 0) +#define FPWM_DB_DLY_FALL_MASK GENMASK(19, 10) +#define FPWM_DB_DLY_FALL(data) ((data) << 10) +#define FPWM_DB_DLY_FALL_GET(data) ((data) >> 10) + +/* pwm tim_ctrl field */ +#define FPWM_TIM_CTRL_DIV_MAX 4096 + +#define FPWM_TIM_CTRL_RESET BIT(0) +#define FPWM_TIM_CTRL_ENABLE BIT(1) +#define FPWM_TIM_CTRL_MODE_UD BIT(2) /* mode, modulo or up-and-down */ +#define FPWM_TIM_CTRL_OVFIF_ENABLE BIT(4) /* counter-overflow intr enable */ +#define FPWM_TIM_CTRL_GIE BIT(5) /* overall intr enable */ +#define FPWM_TIM_CTRL_DIV(data) ((data) << 16) +#define FPWM_TIM_CTRL_DIV_MASK GENMASK(27, 16) +#define FPWM_TIM_CTRL_DIV_GET(data) ((data) >> 16) + +/* pwm_state field */ +#define FPWM_STATE_COUNTER_CLEAR BIT(0) +#define FPWM_STATE_OVFIF_COUNTER BIT(1) +#define FPWM_STATE_FIFO_EMPTY BIT(2) +#define FPWM_STATE_FIFO_FULL BIT(3) + +/* pwm_period field */ +#define FPWM_PERIOD_CCR_MASK GENMASK(15, 0) + +/* pwm_ctrl field */ +#define FPWM_CTRL_MODE_OUTPUT BIT(2) +#define FPWM_CTRL_INTR_COUNTER_ENABLE BIT(3) +#define FPWM_CTRL_CMP(data) ((data) << 4) +#define FPWM_CTRL_CMP_MASK GENMASK(6, 4) +#define FPWM_CTRL_CMP_GET(data) ((data) >> 4) +#define FPWM_CTRL_DUTY_SOURCE_FIFO BIT(8) +#define FPWM_CTRL_INTR_FIFO_EMPTY_ENABLE BIT(9) + +/* pwm_ccr field */ +#define FPWM_CCR_MASK GENMASK(15, 0) + +/* pwm lsd cfg, lsd pwm sync control */ +#define FLSD_MIO_PWM_SYN_OFFSET 0x20 +#define FLSD_MIO_PWM_SYN_MASK GENMASK(7, 0) + +/***************** Macros (Inline Functions) Definitions *********************/ + +/* 读FPWM寄存器 */ +#define FPWM_READ_REG32(addr, reg_offset) FtIn32((addr) + (u32)reg_offset) + +/* 写FPWM寄存器 */ +#define FPWM_WRITE_REG32(addr, reg_offset, reg_value) FtOut32((addr) + (u32)reg_offset, (u32)reg_value) + +#define FPWM_SETBIT(base_addr, reg_offset, data) FtSetBit32((base_addr) + (u32)(reg_offset), (u32)(data)) + +#define FPWM_CLEARBIT(base_addr, reg_offset, data) FtClearBit32((base_addr) + (u32)(reg_offset), (u32)(data)) + +/* enable pwm lsd syn */ +void FPwmLsdEnable(uintptr lsd_addr, u8 pwm_id); + +/* disable pwm lsd syn */ +void FPwmLsdDisable(uintptr lsd_addr, u8 pwm_id); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/pwm/fpwm/fpwm_intr.c b/bsp/phytium/libraries/standalone/drivers/pwm/fpwm/fpwm_intr.c new file mode 100644 index 0000000000..2439328f5f --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/pwm/fpwm/fpwm_intr.c @@ -0,0 +1,103 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fpwm_intr.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-25 11:45:05 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ +#include "fparameters.h" +#include "fassert.h" +#include "finterrupt.h" +#include "fpwm.h" +#include "fpwm_hw.h" + +#define FT_PWM_DEBUG_TAG "FT_PWM_INTR" +#define FPWM_DEBUG(format, ...) FT_DEBUG_PRINT_D(FT_PWM_DEBUG_TAG, format, ##__VA_ARGS__) +#define FPWM_INFO(format, ...) FT_DEBUG_PRINT_I(FT_PWM_DEBUG_TAG, format, ##__VA_ARGS__) +#define FPWM_WARN(format, ...) FT_DEBUG_PRINT_W(FT_PWM_DEBUG_TAG, format, ##__VA_ARGS__) +#define FPWM_ERROR(format, ...) FT_DEBUG_PRINT_E(FT_PWM_DEBUG_TAG, format, ##__VA_ARGS__) + +#define FPWM_CALL_INTR_EVENT_HANDLDER(instance_p, event) \ + if (instance_p->event_handler[event]) \ + instance_p->event_handler[event](instance_p->event_param[event]) + +/** + * @name: FPwmRegisterInterruptHandler + * @msg: register FPwm interrupt handler function + * @param {FPwm} *instance_p, pointer to the pwm instance + * @param {FPwmIntrEvtType} event_type, interrupt event type + * @param {FPwmEvtHandler} handler, interrupt event handler + * @param {void} *param, contains a pointer to the driver instance + * @return {*} + */ +void FPwmRegisterInterruptHandler(FPwmCtrl *instance_p, FPwmIntrEventType event_type, FPwmIntrEventHandler handler, void *param) +{ + FASSERT(instance_p); + FASSERT(event_type < FPWM_INTR_EVENT_NUM); + instance_p->event_handler[event_type] = handler; + instance_p->event_param[event_type] = param; +} + +/** + * @name: FPwmIntrHandler + * @msg: This function is the interrupt handler for the driver. + * It must be connected to an interrupt system by the application such that it + * can be called when an interrupt occurs. + * @param vector Irq num ,Don't need attention . + * @param args contains a pointer to the driver instance + */ +void FPwmIntrHandler(s32 vector, void *args) +{ + u32 status; + static int i = 0; + FPwmCtrl *pctrl = (FPwmCtrl *)args; + + FASSERT(pctrl != NULL); + FASSERT(pctrl->is_ready == FT_COMPONENT_IS_READY); + uintptr pwm_base_addr = 0; + u8 channel = 0; + + for (channel = 0; channel < FPWM_MODE_CHANNEL; channel++) + { + pwm_base_addr = pctrl->config.pwm_base_addr + FPWM_N(channel); + + status = FPWM_READ_REG32(pwm_base_addr, FPWM_CTRL_OFFSET); + if (!(status & (FPWM_CTRL_INTR_COUNTER_ENABLE | FPWM_CTRL_INTR_FIFO_EMPTY_ENABLE))) + continue; + + status = FPWM_READ_REG32(pwm_base_addr, FPWM_STATE_OFFSET); + if (0 == status) + continue; + + /* Check for the type of error interrupt and Processing it */ + if (status & FPWM_STATE_OVFIF_COUNTER) + { + status &= (~FPWM_STATE_OVFIF_COUNTER); + FPWM_SETBIT(pwm_base_addr, FPWM_STATE_OFFSET, FPWM_STATE_COUNTER_CLEAR); + FPWM_CALL_INTR_EVENT_HANDLDER(pctrl, FPWM_INTR_EVENT_COUNTER); + } + + if (status & FPWM_STATE_FIFO_EMPTY) + { + FPWM_SETBIT(pwm_base_addr, FPWM_STATE_OFFSET, FPWM_STATE_FIFO_EMPTY); + FPWM_CALL_INTR_EVENT_HANDLDER(pctrl, FPWM_INTR_EVENT_FIFO_EMPTY); + } + } + +} + diff --git a/bsp/phytium/libraries/standalone/drivers/pwm/fpwm/fpwm_sinit.c b/bsp/phytium/libraries/standalone/drivers/pwm/fpwm/fpwm_sinit.c new file mode 100644 index 0000000000..a4d13ec00b --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/pwm/fpwm/fpwm_sinit.c @@ -0,0 +1,66 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fpwm_sinit.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-25 11:45:05 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +/***************************** Include Files *********************************/ + +#include "fpwm.h" +#include "fparameters.h" +#include "fassert.h" + +extern FPwmConfig FPwmConfigTbl[FPWM_INSTANCE_NUM]; + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/*****************************************************************************/ + +/** + * @name: FPwmLookupConfig + * @msg: get pwm configs by id + * @return {*} + * @param {u32} instanceId, id of pwm ctrl + */ +const FPwmConfig *FPwmLookupConfig(FPwmInstance instance_id) +{ + const FPwmConfig *pconfig = NULL; + FASSERT(instance_id < FPWM_INSTANCE_NUM); + + u32 index = 0; + + for (index = 0; index < (u32)FPWM_INSTANCE_NUM; index++) + { + if (FPwmConfigTbl[index].instance_id == instance_id) + { + pconfig = &FPwmConfigTbl[index]; + break; + } + } + + return (FPwmConfig *)pconfig; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/qspi/Kconfig b/bsp/phytium/libraries/standalone/drivers/qspi/Kconfig new file mode 100644 index 0000000000..a16b375482 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/qspi/Kconfig @@ -0,0 +1,8 @@ +menu "Qspi Configuration" + config USE_FQSPI + bool + prompt "Use FQSPI" + default n + +endmenu + diff --git a/bsp/phytium/libraries/standalone/drivers/qspi/fqspi/fqspi.c b/bsp/phytium/libraries/standalone/drivers/qspi/fqspi/fqspi.c new file mode 100644 index 0000000000..fc42e8535d --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/qspi/fqspi/fqspi.c @@ -0,0 +1,312 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fqspi.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-03-28 09:00:41 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.1 wangxiaodong 2021.11.12 re-construct + * 1.2 wangxiaodong 2022.3.27 re-construct + */ + +#include +#include "fkernel.h" +#include "fassert.h" +#include "fqspi.h" +#include "fqspi_hw.h" +#include "fsleep.h" + +#define FQSPI_DEBUG_TAG "FQSPI" +#define FQSPI_ERROR(format, ...) FT_DEBUG_PRINT_E(FQSPI_DEBUG_TAG, format, ##__VA_ARGS__) +#define FQSPI_WARN(format, ...) FT_DEBUG_PRINT_W(FQSPI_DEBUG_TAG, format, ##__VA_ARGS__) +#define FQSPI_INFO(format, ...) FT_DEBUG_PRINT_I(FQSPI_DEBUG_TAG, format, ##__VA_ARGS__) +#define FQSPI_DEBUG(format, ...) FT_DEBUG_PRINT_D(FQSPI_DEBUG_TAG, format, ##__VA_ARGS__) + + +/** + * @name: FQspiCfgInitialize + * @msg: Initializes a specific instance such that it is ready to be used. + * @param {FQspiCtrl} *pctrl, instance of FQSPI controller + * @param {FQspiConfig} *input_config_p, Configuration parameters of FQSPI + * @return err code information, FQSPI_SUCCESS indicates success,others indicates failed + */ +FError FQspiCfgInitialize(FQspiCtrl *pctrl, const FQspiConfig *input_config_p) +{ + FASSERT(pctrl && input_config_p); + + FError ret = FQSPI_SUCCESS; + /* + * If the device is started, disallow the initialize and return a Status + * indicating it is started. This allows the user to de-initialize the device + * and reinitialize, but prevents a user from inadvertently + * initializing. + */ + if (FT_COMPONENT_IS_READY == pctrl->is_ready) + { + FQSPI_WARN("device is already initialized!!!"); + } + + /*Set default values and configuration data */ + FQspiDeInitialize(pctrl); + + pctrl->config = *input_config_p; + + pctrl->is_ready = FT_COMPONENT_IS_READY; + + return ret; +} + +/** + * @name: FQspiDeInitialize + * @msg: DeInitialization function for the device instance + * @param {FQspiCtrl} *pctrl, instance of FQSPI controller + * @return {*} + */ +void FQspiDeInitialize(FQspiCtrl *pctrl) +{ + FASSERT(pctrl); + + pctrl->is_ready = 0; + memset(pctrl, 0, sizeof(*pctrl)); + + return; +} + +/** + * @name: FQspiSetCapacityAndNum + * @msg: Initializes the capacity and number of flash connect to specific instance. + * @param {FQspiCtrl} *pctrl, instance of FQSPI controller + * @return void + */ +void FQspiSetCapacityAndNum(FQspiCtrl *pctrl) +{ + FASSERT(pctrl); + u32 reg_val = 0; + FQspiConfig *config_p = &pctrl->config; + uintptr base_addr = pctrl->config.base_addr; + + switch (config_p->capacity) + { + case FQSPI_FLASH_CAP_4MB: + pctrl->flash_size = SZ_4M; + break; + case FQSPI_FLASH_CAP_8MB: + pctrl->flash_size = SZ_8M; + break; + case FQSPI_FLASH_CAP_16MB: + pctrl->flash_size = SZ_16M; + break; + case FQSPI_FLASH_CAP_32MB: + pctrl->flash_size = SZ_32M; + break; + case FQSPI_FLASH_CAP_64MB: + pctrl->flash_size = SZ_64M; + break; + case FQSPI_FLASH_CAP_128MB: + pctrl->flash_size = SZ_128M; + break; + case FQSPI_FLASH_CAP_256MB: + pctrl->flash_size = SZ_256M; + break; + default: + pctrl->flash_size = SZ_4M; + break; + } + + /* Write flash capacity and numbers information to qspi Capacity register */ + reg_val = (FQSPI_CAP_FLASH_NUM_MASK & FQSPI_CAP_FLASH_NUM(config_p->dev_num)) | + (FQSPI_CAP_FLASH_CAP_MASK & FQSPI_CAP_FLASH_CAP(config_p->capacity)); + + /*write value to flash capacity register 0x00 */ + FQSPI_WRITE_REG32(base_addr, FQSPI_REG_CAP_OFFSET, reg_val); + +} + + +/** + * @name: FQspiRdCfgConfig + * @msg: config read config register + * @param {FQspiCtrl} *pctrl, instance of FQSPI controller + * @return err code information, FQSPI_SUCCESS indicates success,others indicates failed + */ +FError FQspiRdCfgConfig(FQspiCtrl *pctrl) +{ + FASSERT(pctrl); + FError ret = FQSPI_SUCCESS; + u32 cmd_reg = 0; + uintptr base_addr = pctrl->config.base_addr; + + FQspiRdCfgDef rd_config = pctrl->rd_cfg; + + cmd_reg |= FQSPI_RD_CFG_CMD(rd_config.rd_cmd); + cmd_reg |= FQSPI_RD_CFG_THROUGH(rd_config.rd_through); + cmd_reg |= FQSPI_RD_CFG_TRANSFER(rd_config.rd_transfer); + cmd_reg |= FQSPI_RD_CFG_ADDR_SEL(rd_config.rd_addr_sel); + cmd_reg |= FQSPI_RD_CFG_LATENCY(rd_config.rd_latency); + cmd_reg |= FQSPI_RD_CFG_MODE_BYTE(rd_config.mode_byte); + + if ((rd_config.mode_byte) || (rd_config.cmd_sign == 0)) + { + cmd_reg |= FQSPI_RD_CFG_CMD_SIGN(rd_config.cmd_sign); + } + else + { + FQSPI_ERROR("rd_cfg mode_byte disable !!!"); + return FQSPI_INVAL_PARAM; + } + + if ((rd_config.rd_latency == FQSPI_CMD_LATENCY_ENABLE) || (rd_config.dummy == 0)) + { + rd_config.dummy = rd_config.dummy ? rd_config.dummy : 1; + cmd_reg |= FQSPI_RD_CFG_DUMMY(rd_config.dummy); + } + else + { + FQSPI_ERROR("rd_cfg latency disable !!!"); + return FQSPI_INVAL_PARAM; + } + + cmd_reg |= FQSPI_RD_CFG_D_BUFFER(rd_config.d_buffer); + cmd_reg |= FQSPI_RD_CFG_SCK_SEL(rd_config.rd_sck_sel); + + FQSPI_WRITE_REG32(base_addr, FQSPI_REG_RD_CFG_OFFSET, cmd_reg); + + return ret; +} + +/** + * @name: FQspiWrCfgConfig + * @msg: config write config register + * @param {FQspiCtrl} *pctrl, instance of FQSPI controller + * @return err code information, FQSPI_SUCCESS indicates success,others indicates failed + */ +FError FQspiWrCfgConfig(FQspiCtrl *pctrl) +{ + FASSERT(pctrl); + FError ret = FQSPI_SUCCESS; + u32 cmd_reg = 0; + uintptr base_addr = pctrl->config.base_addr; + + FQspiWrCfgDef wr_config = pctrl->wr_cfg; + + cmd_reg |= FQSPI_WR_CFG_CMD(wr_config.wr_cmd); + cmd_reg |= FQSPI_WR_CFG_WAIT(wr_config.wr_wait); + cmd_reg |= FQSPI_WR_CFG_THROUGH(wr_config.wr_through); + cmd_reg |= FQSPI_WR_CFG_TRANSFER(wr_config.wr_transfer); + cmd_reg |= FQSPI_WR_CFG_ADDRSEL(wr_config.wr_addr_sel); + cmd_reg |= FQSPI_WR_CFG_MODE(wr_config.wr_mode); + cmd_reg |= FQSPI_WR_CFG_SCK_SEL(wr_config.wr_sck_sel); + + FQSPI_WRITE_REG32(base_addr, FQSPI_REG_WR_CFG_OFFSET, cmd_reg); + + return ret; +} + + +/** + * @name: FQspiCommandPortConfig + * @msg: config command port register + * @param {FQspiCtrl} *pctrl, instance of FQSPI controller + * @return err code information, FQSPI_SUCCESS indicates success,others indicates failed + */ +FError FQspiCommandPortConfig(FQspiCtrl *pctrl) +{ + FASSERT(pctrl); + FError ret = FQSPI_SUCCESS; + u32 cmd_reg = 0; + uintptr base_addr = pctrl->config.base_addr; + + FQspiCommandPortDef cmd_port_config = pctrl->cmd_def; + + cmd_reg |= FQSPI_CMD_PORT_CMD_MASK & FQSPI_CMD_PORT_CMD(cmd_port_config.cmd); + + cmd_reg |= FQSPI_CMD_PORT_WAIT(cmd_port_config.wait); + + cmd_reg |= FQSPI_CMD_PORT_THROUGH(cmd_port_config.through); + + cmd_reg |= FQSPI_CMD_PORT_CS_MASK & FQSPI_CMD_PORT_CS(cmd_port_config.cs); + + cmd_reg |= FQSPI_CMD_PORT_TRANSFER(cmd_port_config.transfer); + + cmd_reg |= FQSPI_CMD_PORT_CMD_ADDR(cmd_port_config.cmd_addr); + + cmd_reg |= FQSPI_CMD_PORT_LATENCY(cmd_port_config.latency); + + cmd_reg |= FQSPI_CMD_PORT_DATA_TRANS(cmd_port_config.data_transfer); + + cmd_reg |= FQSPI_CMD_PORT_ADDR_SEL(cmd_port_config.addr_sel); + + if ((cmd_port_config.latency == FQSPI_CMD_LATENCY_ENABLE) || (cmd_port_config.dummy == 0)) + { + cmd_port_config.dummy = cmd_port_config.dummy ? cmd_port_config.dummy : 1; + cmd_reg |= FQSPI_CMD_PORT_DUMMY(cmd_port_config.dummy); + } + else + { + FQSPI_ERROR("cmd_port latency disable !!!"); + return FQSPI_INVAL_PARAM; + } + + cmd_reg |= FQSPI_CMD_PORT_P_BUFFER(cmd_port_config.p_buffer); + + /* read data num */ + cmd_reg |= FQSPI_CMD_PORT_RW_NUM_MASK & FQSPI_CMD_PORT_RW_NUM(cmd_port_config.rw_num); + + cmd_reg |= FQSPI_CMD_PORT_CLK_SEL_MASK & FQSPI_CMD_PORT_CLK_SEL(cmd_port_config.sck_sel); + + FQSPI_WRITE_REG32(base_addr, FQSPI_REG_CMD_PORT_OFFSET, cmd_reg); + + return ret; +} + + +/** + * @name: FQspiChannelSet + * @msg: config qspi cs num + * @param {FQspiCtrl} *pctrl, instance of FQSPI controller + * @param {FQspiChipCS} channel, cs number + * @return + */ +void FQspiChannelSet(FQspiCtrl *pctrl, FQspiChipCS channel) +{ + FASSERT(pctrl); + FASSERT(channel < FQSPI_CS_NUM); + pctrl->config.channel = channel; +} + +/** + * @name: FQspiCsTimingSet + * @msg: config qspi cs timing + * @param {FQspiCtrl} *pctrl, instance of FQSPI controller + * @param {FQspiCsTimingCfgDef} cs_timing_cfg, cs timing + * @return err code information, FQSPI_SUCCESS indicates success,others indicates failed + */ +void FQspiCsTimingSet(FQspiCtrl *pctrl, FQspiCsTimingCfgDef *cs_timing_cfg) +{ + FASSERT(pctrl); + u32 cmd_reg = 0; + uintptr base_addr = pctrl->config.base_addr; + + cmd_reg |= FQSPI_FUN_SET_CS_HOLD(cs_timing_cfg->cs_hold); + + cmd_reg |= FQSPI_FUN_SET_CS_SETUP(cs_timing_cfg->cs_setup); + + cmd_reg |= FQSPI_FUN_SET_CS_DELAY(cs_timing_cfg->cs_delay); + + FQSPI_WRITE_REG32(base_addr, FQSPI_REG_CS_TIMING_SET_OFFSET, cmd_reg); + +} diff --git a/bsp/phytium/libraries/standalone/drivers/qspi/fqspi/fqspi.h b/bsp/phytium/libraries/standalone/drivers/qspi/fqspi/fqspi.h new file mode 100644 index 0000000000..407187ec26 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/qspi/fqspi/fqspi.h @@ -0,0 +1,238 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fqspi.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:00:55 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.1 wangxiaodong 2021.11.12 re-construct + */ + +#ifndef BSP_DRIVERS_FQSPI_H +#define BSP_DRIVERS_FQSPI_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "fkernel.h" +#include "ftypes.h" +#include "ferror_code.h" +#include "fdebug.h" +#include "fparameters.h" + +#define FQSPI_SUCCESS FT_SUCCESS +#define FQSPI_INVAL_PARAM FT_MAKE_ERRCODE(ErrModBsp, ErrBspQSpi, 1) +#define FQSPI_NOT_READY FT_MAKE_ERRCODE(ErrModBsp, ErrBspQSpi, 2) +#define FQSPI_NOT_ALLIGN FT_MAKE_ERRCODE(ErrModBsp, ErrBspQSpi, 3) +#define FQSPI_NOT_SUPPORT FT_MAKE_ERRCODE(ErrModBsp, ErrBspQSpi, 4) +#define FQSPI_TIMEOUT FT_MAKE_ERRCODE(ErrModBsp, ErrBspQSpi, 5) + +/* FQSPI Transfer mode, command-addr-data protocols */ +typedef enum +{ + FQSPI_TRANSFER_1_1_1 = 0x0, + FQSPI_TRANSFER_1_1_2 = 0x1, + FQSPI_TRANSFER_1_1_4 = 0x2, + FQSPI_TRANSFER_1_2_2 = 0x3, + FQSPI_TRANSFER_1_4_4 = 0x4, + FQSPI_TRANSFER_2_2_2 = 0x5, + FQSPI_TRANSFER_4_4_4 = 0x6 +} FQspiTransferMode; + +/* FQSPI Flash Capcity type */ +typedef enum +{ + FQSPI_FLASH_CAP_4MB = 0b000, + FQSPI_FLASH_CAP_8MB = 0b001, + FQSPI_FLASH_CAP_16MB = 0b010, + FQSPI_FLASH_CAP_32MB = 0b011, + FQSPI_FLASH_CAP_64MB = 0b100, + FQSPI_FLASH_CAP_128MB = 0b101, + FQSPI_FLASH_CAP_256MB = 0b110, +} FQspiFlashCapcityType; + +/* FQSPI pclk divider type */ +typedef enum +{ + FQSPI_SCK_DIV_128 = 0x0, + FQSPI_SCK_DIV_2 = 0x1, + FQSPI_SCK_DIV_4 = 0x2, + FQSPI_SCK_DIV_8 = 0x3, + FQSPI_SCK_DIV_16 = 0x4, + FQSPI_SCK_DIV_32 = 0x5, + FQSPI_SCK_DIV_64 = 0x6 +} FQspiSckDivType; + +/* FQSPI Address type */ +typedef enum +{ + FQSPI_ADDR_SEL_3 = 0x0, + FQSPI_ADDR_SEL_4 = 0x1 +} FQspiAddrType; + +/* Specifies if the Instruction need transfer address */ +typedef enum +{ + FQSPI_CMD_ADDR_DISABLE = 0x0, + FQSPI_CMD_ADDR_ENABLE = 0x1 +} FQspiCmdAddrType; + +/* Specifies if the Instruction have latency */ +typedef enum +{ + FQSPI_CMD_LATENCY_DISABLE = 0x0, + FQSPI_CMD_LATENCY_ENABLE = 0x1 +} FQspiCmdLatencyType; + +/* Specifies if the Instruction need transfer data */ +typedef enum +{ + FQSPI_CMD_DATA_DISABLE = 0x0, + FQSPI_CMD_DATA_ENABLE = 0x1 +} FQspiCmdDataType; + +/* Specifies if the Instruction use buffer */ +typedef enum +{ + FQSPI_USE_BUFFER_DISABLE = 0x0, + FQSPI_USE_BUFFER_ENABLE = 0x1 +} FQspiUseBufferType; + +/* Specifies if the Instruction need some execution time */ +typedef enum +{ + FQSPI_WAIT_DISABLE = 0x0, + FQSPI_WAIT_ENABLE = 0x1 +} FQspiWaitType; + + +typedef enum +{ + FQSPI_XIP_EXIT = 0x0, + FQSPI_XIP_ENTER = 0x1 +} FQspiXIPState; + +typedef struct +{ + u32 instance_id; /* Id of device */ + uintptr base_addr; /* Base address of qspi */ + uintptr mem_start; /* Start address of qspi memory */ + u32 capacity; /* Flash capacity */ + u32 dev_num; /* Qspi device number */ + u32 channel; /* channel number, cs number */ +} FQspiConfig; + +/* rd_cfg register */ +typedef struct +{ + u8 rd_cmd : 8; /* Specifies the Instruction to be sent */ + u8 rd_through : 1; + u8 rd_transfer : 3;/* Specifies the Instruction tranfer Mode 1-1-1~4-4-4*/ + u8 rd_addr_sel : 1;/* Specifies the Instruction addr mode 3 byte addr or 4 byte addr*/ + u8 rd_latency : 1; /* Specifies if the Instruction need read latency*/ + u8 mode_byte : 1; /* Specifies if the Instruction need modifier*/ + u8 cmd_sign : 8; /* Specifies the Instruction modifier*/ + u8 dummy : 5; /* Specifies the Number of Dummy Cycles.*/ + u8 d_buffer : 1; /* Specifies if the Instruction use buffer to read data*/ + u8 rd_sck_sel : 3; /* Specifies the pclk division .*/ +} FQspiRdCfgDef; + +/* wr_cfg register */ +typedef struct +{ + u8 wr_cmd : 8; /* Specifies the Instruction to be sent */ + u16 reserved : 14; + u8 wr_wait : 1; + u8 wr_through : 1; + u8 wr_transfer : 3;/* Specifies the Instruction tranfer Mode 1-1-1~4-4-4*/ + u8 wr_addr_sel : 1;/* Specifies the Instruction addr mode 3 byte addr or 4 byte addr*/ + u8 wr_mode : 1; /* Specifies if the Instruction need modifier*/ + u8 wr_sck_sel : 3; /* Specifies the pclk division .*/ +} FQspiWrCfgDef; + +/* cmd_port register */ +typedef struct +{ + u8 cmd : 8; /* Specifies the Instruction to be sent */ + u8 reserved : 1; + u8 wait : 1; + u8 through : 1; + u8 cs : 2; + u8 transfer : 3;/* Specifies the Instruction tranfer Mode 1-1-1~4-4-4*/ + u8 cmd_addr : 1; /* Specifies if the Instruction need transfer address*/ + u8 latency : 1; /* Specifies if the Instruction need read latency*/ + u8 data_transfer : 1; /* Specifies if the Instruction need tranfer data*/ + u8 addr_sel : 1; /* Specifies the Instruction addr mode 3 byte addr or 4 byte addr*/ + u8 dummy : 5; /* Specifies the Number of Dummy Cycles.*/ + u8 p_buffer : 1; /* Specifies if the Instruction use buffer to read data*/ + u8 rw_num : 3; /* Specifies the read or write bytes number.*/ + u8 sck_sel : 3; /* Specifies the pclk division .*/ +} FQspiCommandPortDef; + +typedef struct +{ + u8 cs_hold; /* Specifies the cs valid hold time */ + u8 cs_setup; /* Specifies the cs valid setup time */ + u16 cs_delay; /* Specifies the cs delay time */ +} FQspiCsTimingCfgDef; + +typedef struct +{ + FQspiConfig config; + FQspiRdCfgDef rd_cfg; + FQspiWrCfgDef wr_cfg; + FQspiCommandPortDef cmd_def; + FQspiCsTimingCfgDef cs_timing_cfg; + u32 is_ready; /**< Device is initialized and ready */ + u32 flash_size; /* size of QSPI flash */ + u8 mf_id; /* manufacturer information */ +} FQspiCtrl; + +/* lookup FQSPI default Configuration parameters */ +const FQspiConfig *FQspiLookupConfig(u32 instance_id); + +/* set capacity and number of flash connect to qspi */ +void FQspiSetCapacityAndNum(FQspiCtrl *pctrl); + +/* qspi instance initialization */ +FError FQspiCfgInitialize(FQspiCtrl *pctrl, const FQspiConfig *input_config_p); + +/* qspi instance de-initialization */ +void FQspiDeInitialize(FQspiCtrl *pctrl); + +/* command port register config */ +FError FQspiCommandPortConfig(FQspiCtrl *pctrl); + +/* read register config */ +FError FQspiRdCfgConfig(FQspiCtrl *pctrl); + +/* write register config */ +FError FQspiWrCfgConfig(FQspiCtrl *pctrl); + +/* qspi cs number set */ +void FQspiChannelSet(FQspiCtrl *pctrl, FQspiChipCS channel); + +/* qspi cs timing set */ +void FQspiCsTimingSet(FQspiCtrl *pctrl, FQspiCsTimingCfgDef *cs_timing_cfg); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsp/phytium/libraries/standalone/drivers/qspi/fqspi/fqspi_flash.c b/bsp/phytium/libraries/standalone/drivers/qspi/fqspi/fqspi_flash.c new file mode 100644 index 0000000000..a3e5fbf0c8 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/qspi/fqspi/fqspi_flash.c @@ -0,0 +1,1039 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fqspi_flash.c + * Date: 2022-07-12 15:42:55 + * LastEditTime: 2022-07-12 15:42:56 + * Description: This file is for + * + * Modify History: + * Ver Who Date Changes + * ----- ------ -------- -------------------------------------- + */ + +#include +#include "fkernel.h" +#include "fassert.h" +#include "fqspi_flash.h" +#include "fqspi_hw.h" +#include "fqspi.h" +#include "sdkconfig.h" + +#define FQSPI_DEBUG_TAG "FQSPI_FLASH" +#define FQSPI_ERROR(format, ...) FT_DEBUG_PRINT_E(FQSPI_DEBUG_TAG, format, ##__VA_ARGS__) +#define FQSPI_WARN(format, ...) FT_DEBUG_PRINT_W(FQSPI_DEBUG_TAG, format, ##__VA_ARGS__) +#define FQSPI_INFO(format, ...) FT_DEBUG_PRINT_I(FQSPI_DEBUG_TAG, format, ##__VA_ARGS__) +#define FQSPI_DEBUG(format, ...) FT_DEBUG_PRINT_D(FQSPI_DEBUG_TAG, format, ##__VA_ARGS__) + +/* When entering direct address access mode, + read and write memory addresses need to be accessed in 4-byte alignment */ +#define FQSPI_ALIGNED_BYTE 4 + +typedef struct +{ + char *name; + u8 mf_id; + u8 type_id; + u8 capacity_id; + u32 capacity; +} FQspiFlashInfo; + +/* supported manufacturer information table */ +static const FQspiFlashInfo flash_info_table[] = FQSPI_FLASH_INFO_TABLE; + +/* + * @name: FQspiFlashDetect + * @msg: detect qspi flash information, include id, type, capacity, set qspi capacity register. + * @param {FQspiCtrl} *pctrl, instance of FQSPI controller + * @return {FError} err code information, FQSPI_SUCCESS indicates success,others indicates failed + */ +FError FQspiFlashDetect(FQspiCtrl *pctrl) +{ + FASSERT(pctrl); + FError ret = FQSPI_SUCCESS; + u8 flash_id[3] = {0}; + u8 i = 0; + + /* read id to flash_id */ + ret = FQspiFlashSpecialInstruction(pctrl, FQSPI_FLASH_CMD_RDID, flash_id, sizeof(flash_id)); + if (FQSPI_SUCCESS != ret) + { + FQSPI_ERROR("read flash id failed, ret 0x%x\r\n", ret); + return ret; + } + + FQSPI_INFO("flash id = 0x%x, 0x%x, 0x%x\r\n", flash_id[0], flash_id[1], flash_id[2]); + + for (i = 0; i < sizeof(flash_info_table) / sizeof(FQspiFlashInfo); i++) + { + if ((flash_info_table[i].mf_id == flash_id[0]) && (flash_info_table[i].type_id == flash_id[1]) + && (flash_info_table[i].capacity_id == flash_id[2])) + { + pctrl->mf_id = flash_info_table[i].mf_id; + pctrl->config.capacity = flash_info_table[i].capacity; + break; + } + } + + if (i == sizeof(flash_info_table) / sizeof(FQspiFlashInfo)) + { + FQSPI_ERROR("The Detected flash is not matched, id = 0x%x, 0x%x, 0x%x\r\n", flash_id[0], flash_id[1], flash_id[2]); + return FQSPI_NOT_SUPPORT; + } + + /* set flash num and flash capacity */ + FQspiSetCapacityAndNum(pctrl); + FQSPI_INFO("Find a %s flash chip. Size is %ld bytes.\n", flash_info_table[i].name, pctrl->flash_size); + + return ret; +} + +/* + * @name: FQspiFlashReset + * @msg: qspi Flash soft reset, FQSPI_CMD_ENABLE_RESET and FQSPI_CMD_RESET. + * @param {FQspiCtrl} *pctrl, instance of FQSPI controller + * @return {FError} err code information, FQSPI_SUCCESS indicates success,others indicates failed + */ +static FError FQspiFlashReset(FQspiCtrl *pctrl) +{ + FASSERT(pctrl); + FError ret = FQSPI_SUCCESS; + ret = FQspiFlashWriteReg(pctrl, FQSPI_CMD_ENABLE_RESET, NULL, 0); + if (FQSPI_SUCCESS != ret) + { + FQSPI_ERROR("failed to enable reset, test result 0x%x\r\n", ret); + return ret; + } + + ret = FQspiFlashWriteReg(pctrl, FQSPI_CMD_RESET, NULL, 0); + if (FQSPI_SUCCESS != ret) + { + FQSPI_ERROR("failed to reset, test result 0x%x\r\n", ret); + return ret; + } + + return ret; +} + +/** + * @name: FQspiFlashSpecialInstruction + * @msg: Read some flash information by different cmd + * @param {FQspiCtrl} *pctrl, instance of FQSPI controller + * @param {u8} cmd, read register value command, include RDID, RDSR1, RDSR2, RDCR... + * @param {u8} *buf, read buffer + * @param {size_t} len, read length + * @return {FError} err code information, FQSPI_SUCCESS indicates success,others indicates failed + */ +FError FQspiFlashSpecialInstruction(FQspiCtrl *pctrl, u8 cmd, u8 *buf, size_t len) +{ + FASSERT(pctrl && buf); + FError ret = FQSPI_SUCCESS; + if (FT_COMPONENT_IS_READY != pctrl->is_ready) + { + FQSPI_ERROR("Nor flash not ready !!!"); + return FQSPI_NOT_READY; + } + + uintptr base_addr = pctrl->config.base_addr; + + memset(&pctrl->cmd_def, 0, sizeof(pctrl->cmd_def)); + pctrl->cmd_def.cmd = cmd; + pctrl->cmd_def.wait = FQSPI_WAIT_DISABLE; + pctrl->cmd_def.through = 0; + pctrl->cmd_def.cs = pctrl->config.channel; + pctrl->cmd_def.transfer = FQSPI_TRANSFER_1_1_1; + pctrl->cmd_def.cmd_addr = FQSPI_CMD_ADDR_DISABLE; + pctrl->cmd_def.latency = FQSPI_CMD_LATENCY_DISABLE; + pctrl->cmd_def.data_transfer = FQSPI_CMD_DATA_ENABLE; + pctrl->cmd_def.addr_sel = FQSPI_ADDR_SEL_3; + pctrl->cmd_def.dummy = 0; + pctrl->cmd_def.p_buffer = FQSPI_USE_BUFFER_ENABLE; + pctrl->cmd_def.rw_num = (len - 1); + pctrl->cmd_def.sck_sel = FQSPI_SCK_DIV_128; + + ret = FQspiCommandPortConfig(pctrl); + if (ret != FT_SUCCESS) + { + FQSPI_ERROR("FQspiFlashSpecialInstruction FQspiCommandPortConfig failed!"); + return ret; + } + + FQspiCommandPortSend(base_addr); + + FQspiGetLdPortData(base_addr, buf, len); + + return ret; +} + +/** + * @name: FQspiFlashReadSfdp + * @msg: Read flash Serial Flash Discoverable Parameters + * @param {FQspiCtrl} *pctrl, instance of FQSPI controller + * @param {u32} offset,Relative Byte Address Offset + * @param {u8} *buf, read buffer + * @param {size_t} len, read length + * @return {FError} err code information, FQSPI_SUCCESS indicates success,others indicates failed + */ +FError FQspiFlashReadSfdp(FQspiCtrl *pctrl, u32 offset, u8 *buf, size_t len) +{ + FASSERT(pctrl && buf); + FError ret = FQSPI_SUCCESS; + if (FT_COMPONENT_IS_READY != pctrl->is_ready) + { + FQSPI_ERROR("Nor flash not ready !!!"); + return FQSPI_NOT_READY; + } + + uintptr base_addr = pctrl->config.base_addr; + + memset(&pctrl->cmd_def, 0, sizeof(pctrl->cmd_def)); + pctrl->cmd_def.cmd = FQSPI_FLASH_CMD_SFDP; + pctrl->cmd_def.wait = FQSPI_WAIT_DISABLE; + pctrl->cmd_def.through = 0; + pctrl->cmd_def.cs = pctrl->config.channel; + pctrl->cmd_def.transfer = FQSPI_TRANSFER_1_1_1; + pctrl->cmd_def.cmd_addr = FQSPI_CMD_ADDR_ENABLE; + pctrl->cmd_def.latency = FQSPI_CMD_LATENCY_ENABLE; + pctrl->cmd_def.data_transfer = FQSPI_CMD_DATA_ENABLE; + pctrl->cmd_def.addr_sel = FQSPI_ADDR_SEL_3; + pctrl->cmd_def.dummy = 8; + pctrl->cmd_def.p_buffer = FQSPI_USE_BUFFER_ENABLE; + pctrl->cmd_def.rw_num = (len - 1); + pctrl->cmd_def.sck_sel = FQSPI_SCK_DIV_128; + + ret = FQspiCommandPortConfig(pctrl); + if (ret != FT_SUCCESS) + { + FQSPI_ERROR("FQspiFlashReadSfdp FQspiCommandPortConfig failed!"); + return ret; + } + + /* write addr port register */ + FQspiAddrPortConfig(base_addr, offset); + + FQspiCommandPortSend(base_addr); + + FQspiGetLdPortData(base_addr, buf, len); + + return ret; +} + +/** + * @name: FQspiFlashReadReg + * @msg: Read Qspi register value + * @param {FQspiCtrl} *pctrl, instance of FQSPI controller + * @param {u32} offset,Relative Byte Address Offset + * @param {u8} *buf, read buffer + * @param {size_t} len, read length + * @return {FError} err code information, FQSPI_SUCCESS indicates success,others indicates failed + */ +FError FQspiFlashReadReg(FQspiCtrl *pctrl, u32 offset, u8 *buf, size_t len) +{ + FASSERT(pctrl && buf); + FError ret = FQSPI_SUCCESS; + u32 cmd_reg = 0; + uintptr base_addr = pctrl->config.base_addr; + + if (FT_COMPONENT_IS_READY != pctrl->is_ready) + { + FQSPI_ERROR("Nor flash not ready !!!"); + return FQSPI_NOT_READY; + } + + memset(&pctrl->cmd_def, 0, sizeof(pctrl->cmd_def)); + pctrl->cmd_def.cmd = FQSPI_FLASH_CMD_RDAR; + pctrl->cmd_def.wait = FQSPI_WAIT_DISABLE; + pctrl->cmd_def.through = 0; + pctrl->cmd_def.cs = pctrl->config.channel; + pctrl->cmd_def.transfer = FQSPI_TRANSFER_1_1_1; + pctrl->cmd_def.cmd_addr = FQSPI_CMD_ADDR_ENABLE; + pctrl->cmd_def.latency = FQSPI_CMD_LATENCY_ENABLE; + pctrl->cmd_def.data_transfer = FQSPI_CMD_DATA_ENABLE; + pctrl->cmd_def.addr_sel = FQSPI_ADDR_SEL_3; + pctrl->cmd_def.dummy = 8; + pctrl->cmd_def.p_buffer = FQSPI_USE_BUFFER_ENABLE; + pctrl->cmd_def.rw_num = (len - 1); + pctrl->cmd_def.sck_sel = FQSPI_SCK_DIV_128; + + ret = FQspiCommandPortConfig(pctrl); + if (ret != FT_SUCCESS) + { + FQSPI_ERROR("FQspiFlashReadReg FQspiCommandPortConfig failed!"); + return ret; + } + + /* write addr port register */ + FQspiAddrPortConfig(base_addr, offset); + + FQspiCommandPortSend(base_addr); + + FQspiGetLdPortData(base_addr, buf, len); + + /* wait SR1V bit0 WIP is ready, not device busy */ + ret = FQspiFlashWaitForCmd(pctrl); + if (ret != FT_SUCCESS) + { + FQSPI_ERROR("FQspiFlashReadReg FQspiCommandPortConfig failed!"); + return ret; + } + + return ret; +} + +/** + * @name: FQspiFlashReadData + * @msg: read flash data + * @param {FQspiCtrl} *pctrl, instance of FQSPI controller + * @param {u32} chip_addr, The start address of the chip to read + * @param {u8} *buf, read buffer + * @param {size_t} len, read length + * @return size_t Indicates the length of the data read, zero indicates read fails + */ +size_t FQspiFlashReadData(FQspiCtrl *pctrl, u32 chip_addr, u8 *buf, size_t len) +{ + /* addr of copy dst or src might be zero */ + FASSERT(pctrl && buf); + size_t loop = 0; + const size_t cnt = len / FQSPI_ALIGNED_BYTE; /* cnt number of 4-bytes need copy */ + const size_t remain = len % FQSPI_ALIGNED_BYTE; /* remain number of 1-byte not aligned */ + u8 align_buf[FQSPI_ALIGNED_BYTE]; + size_t copy_len = 0; + u32 addr = pctrl->config.mem_start + pctrl->config.channel * pctrl->flash_size + chip_addr; + intptr src_addr = (intptr)addr; /* conver to 32/64 bit addr */ + intptr dst_addr = (intptr)buf; + + if (FT_COMPONENT_IS_READY != pctrl->is_ready) + { + FQSPI_ERROR("Nor flash not ready !!!"); + return 0; + } + if (0 == pctrl->rd_cfg.rd_cmd) + { + FQSPI_ERROR("Nor flash read command is not ready !!!"); + return 0; + } + + if (0 == len) + { + return 0; + } + + if (IS_ALIGNED(src_addr, FQSPI_ALIGNED_BYTE)) /* if copy src is aligned by 4 bytes */ + { + /* read 4-bytes aligned buf part */ + for (loop = 0; loop < cnt; loop++) + { + *(u32 *)dst_addr = *(volatile u32 *)(src_addr); + src_addr += FQSPI_ALIGNED_BYTE; + dst_addr += FQSPI_ALIGNED_BYTE; + } + + copy_len += (loop << 2); + + if (remain > 0) + { + *(u32 *)align_buf = *(volatile u32 *)(src_addr); + } + + /* read remain un-aligned buf byte by byte */ + for (loop = 0; loop < remain; loop++) + { + *(u8 *)dst_addr = align_buf[loop]; + dst_addr += 1; + } + + copy_len += loop; + + } + else /* if copy src is not aligned */ + { + /* read byte by byte */ + for (loop = 0; loop < len; loop++) + { + *(u8 *)dst_addr = *(volatile u8 *)(src_addr); + dst_addr += 1; + src_addr += 1; + } + copy_len += loop; + + } + + return copy_len; +} + +/** + * @name: FQspiFlashReadDataConfig + * @msg: read flash data configuration + * @param {FQspiCtrl} *pctrl, instance of FQSPI controller + * @param {u8} command, command to read flash,see the Flash manual for details + * @return {FError} err code information, FQSPI_SUCCESS indicates success,others indicates failed 表示配置成功,其它返回值表示配置失败 + */ +FError FQspiFlashReadDataConfig(FQspiCtrl *pctrl, u8 command) +{ + FASSERT(pctrl); + FError ret = FQSPI_SUCCESS; + + if (FT_COMPONENT_IS_READY != pctrl->is_ready) + { + FQSPI_ERROR("Nor flash not ready !!!"); + ret |= FQSPI_NOT_READY; + return ret; + } + + uintptr base_addr = pctrl->config.base_addr; + + /* clear sr1 = 0, set config register1 bit1 quad = 1 */ + u8 wrr_buf[2] = {0x0, 0x02}; + + FQspiXIPModeSet(base_addr, FQSPI_XIP_ENTER); + + /* set cmd region, command */ + memset(&pctrl->rd_cfg, 0, sizeof(pctrl->rd_cfg)); + pctrl->rd_cfg.rd_cmd = command; + + /* read buffer */ + pctrl->rd_cfg.d_buffer = FQSPI_USE_BUFFER_ENABLE; + pctrl->rd_cfg.rd_sck_sel = FQSPI_SCK_DIV_128; + + switch (command) + { + case FQSPI_FLASH_CMD_READ: + FQspiFlashReset(pctrl); + pctrl->rd_cfg.rd_addr_sel = FQSPI_ADDR_SEL_3; + pctrl->rd_cfg.rd_transfer = FQSPI_TRANSFER_1_1_1; + break; + + case FQSPI_FLASH_CMD_4READ: + pctrl->rd_cfg.rd_addr_sel = FQSPI_ADDR_SEL_4; + pctrl->rd_cfg.rd_transfer = FQSPI_TRANSFER_1_1_1; + break; + + case FQSPI_FLASH_CMD_FAST_READ: + pctrl->rd_cfg.rd_addr_sel = FQSPI_ADDR_SEL_3; + pctrl->rd_cfg.rd_transfer = FQSPI_TRANSFER_1_1_1; + pctrl->rd_cfg.dummy = 8; + pctrl->rd_cfg.rd_latency = FQSPI_CMD_LATENCY_ENABLE; + break; + + case FQSPI_FLASH_CMD_4FAST_READ: + pctrl->rd_cfg.rd_addr_sel = FQSPI_ADDR_SEL_4; + pctrl->rd_cfg.rd_transfer = FQSPI_TRANSFER_1_1_1; + pctrl->rd_cfg.dummy = 8; + pctrl->rd_cfg.rd_latency = FQSPI_CMD_LATENCY_ENABLE; + break; + + case FQSPI_FLASH_CMD_DUAL_READ: + pctrl->rd_cfg.rd_addr_sel = FQSPI_ADDR_SEL_3; + pctrl->rd_cfg.rd_transfer = FQSPI_TRANSFER_1_2_2; + pctrl->rd_cfg.rd_latency = FQSPI_CMD_LATENCY_ENABLE; + + if (pctrl->mf_id == FQSPI_FLASH_MF_ID_CYPRESS) + { + pctrl->rd_cfg.mode_byte = 0x1; + pctrl->rd_cfg.cmd_sign = FQSPI_QUAD_READ_MODE_CMD; + pctrl->rd_cfg.dummy = 8; + } + else if (pctrl->mf_id == FQSPI_FLASH_MF_ID_GIGADEVICE) + { + pctrl->rd_cfg.dummy = 4; + } + else if (pctrl->mf_id == FQSPI_FLASH_MF_ID_BOYA) + { + pctrl->rd_cfg.dummy = 4; + } + break; + + case FQSPI_FLASH_CMD_QIOR: + /* set SR1V and CR1V */ + FQspiFlashEnableWrite(pctrl); + + pctrl->rd_cfg.rd_addr_sel = FQSPI_ADDR_SEL_3; + pctrl->rd_cfg.rd_transfer = FQSPI_TRANSFER_1_4_4; + pctrl->rd_cfg.rd_latency = FQSPI_CMD_LATENCY_ENABLE; + + if (pctrl->mf_id == FQSPI_FLASH_MF_ID_CYPRESS) + { + pctrl->rd_cfg.dummy = 10; + /* use wrr write config register 1 */ + ret = FQspiFlashWriteReg(pctrl, FQSPI_FLASH_CMD_WRR, wrr_buf, sizeof(wrr_buf)); + if (FQSPI_SUCCESS != ret) + { + FQSPI_ERROR("failed to write cmd wrr, test result 0x%x", ret); + return 0; + } + } + else if (pctrl->mf_id == FQSPI_FLASH_MF_ID_GIGADEVICE) + { + pctrl->rd_cfg.dummy = 6; + /* use wrr write config register 1 */ + ret = FQspiFlashWriteReg(pctrl, FQSPI_FLASH_CMD_WRR, wrr_buf, sizeof(wrr_buf)); + if (FQSPI_SUCCESS != ret) + { + FQSPI_ERROR("failed to write cmd wrr, test result 0x%x", ret); + return 0; + } + } + else if (pctrl->mf_id == FQSPI_FLASH_MF_ID_BOYA) + { + pctrl->rd_cfg.dummy = 6; + ret = FQspiFlashWriteReg(pctrl, FQSPI_FLASH_CMD_WRITE_SR2, &wrr_buf[1], 1); + if (FQSPI_SUCCESS != ret) + { + FQSPI_ERROR("failed to write cmd wrr, test result 0x%x", ret); + return 0; + } + } + + break; + + case FQSPI_FLASH_CMD_4QIOR: + pctrl->rd_cfg.rd_addr_sel = FQSPI_ADDR_SEL_4; + pctrl->rd_cfg.rd_transfer = FQSPI_TRANSFER_1_4_4; + pctrl->rd_cfg.mode_byte = 0x1; + pctrl->rd_cfg.cmd_sign = FQSPI_QUAD_READ_MODE_CMD; + pctrl->rd_cfg.rd_latency = FQSPI_CMD_LATENCY_ENABLE; + pctrl->rd_cfg.dummy = 8; + + /* set SR1V and CR1V */ + FQspiFlashEnableWrite(pctrl); + /* use wrr write config register 1 */ + ret = FQspiFlashWriteReg(pctrl, FQSPI_FLASH_CMD_WRR, wrr_buf, sizeof(wrr_buf)); + if (FQSPI_SUCCESS != ret) + { + FQSPI_ERROR("failed to write cmd wrr, test result 0x%x\r\n", ret); + return ret; + } + break; + + default: + return FQSPI_INVAL_PARAM; + break; + } + + ret = FQspiRdCfgConfig(pctrl); + + return ret; +} + +/** + * @name: FQspiFlashWriteData + * @msg: write flash data + * @param {FQspiCtrl} *pctrl, instance of FQSPI controller + * @param {u8} command, command to write flash,see the Flash manual for details + * @param {u32} chip_addr, The start address of the chip to write + * @param {u8} *buf, write buffer + * @param {size_t} len, write length + * @return {FError} err code information, FQSPI_SUCCESS indicates success,others indicates failed 表示写入成功,其它返回值表示写入失败 + */ +FError FQspiFlashWriteData(FQspiCtrl *pctrl, u8 command, u32 chip_addr, const u8 *buf, size_t len) +{ + FASSERT(pctrl && buf); + FError ret = FQSPI_SUCCESS; + u32 loop = 0; + const u32 mask = (u32)GENMASK(1, 0); + u32 reg_val = 0; + u32 val = 0; + u32 aligned_bit = 0; + + u8 tmp[FQSPI_ALIGNED_BYTE] = {0xff, 0xff, 0xff, 0xff}; + u32 addr = pctrl->config.mem_start + pctrl->config.channel * pctrl->flash_size + chip_addr; + uintptr base_addr = pctrl->config.base_addr; + + if (FT_COMPONENT_IS_READY != pctrl->is_ready) + { + FQSPI_ERROR("Nor flash not ready !!!"); + return FQSPI_NOT_READY; + } + + /* Flash write enable */ + FQspiFlashEnableWrite(pctrl); + + memset(&pctrl->wr_cfg, 0, sizeof(pctrl->wr_cfg)); + /* set cmd region, command */ + pctrl->wr_cfg.wr_cmd = command; + pctrl->wr_cfg.wr_wait = FQSPI_WAIT_ENABLE; + /* clear addr select bit */ + pctrl->wr_cfg.wr_addr_sel = 0; + /* set wr mode, use buffer */ + pctrl->wr_cfg.wr_mode = FQSPI_USE_BUFFER_ENABLE; + /* set sck_sel region, clk_div */ + pctrl->wr_cfg.wr_sck_sel = FQSPI_SCK_DIV_128; + + /* set addr_sel region, FQSPI_ADDR_SEL_3 or FQSPI_ADDR_SEL_4 */ + switch (command) + { + case FQSPI_FLASH_CMD_PP: + case FQSPI_FLASH_CMD_QPP: + pctrl->wr_cfg.wr_addr_sel = FQSPI_ADDR_SEL_3; + break; + case FQSPI_FLASH_CMD_4PP: + case FQSPI_FLASH_CMD_4QPP: + pctrl->wr_cfg.wr_addr_sel = FQSPI_ADDR_SEL_4; + break; + default: + ret |= FQSPI_NOT_SUPPORT; + return ret; + break; + } + + /*write wr_cfg to Write config register 0x08 */ + FQspiWrCfgConfig(pctrl); + + if (IS_ALIGNED(addr, FQSPI_ALIGNED_BYTE)) /* if copy src is aligned by 4 bytes */ + { + /* write alligned data into memory space */ + for (loop = 0; loop < (len >> 2); loop++) + { + FQSPI_DAT_WRITE(addr + FQSPI_ALIGNED_BYTE * loop, *(u32 *)(buf + FQSPI_ALIGNED_BYTE * loop)); + } + /* write not alligned data into memory space */ + if (len & mask) + { + addr = addr + (len & ~mask); + memcpy(tmp, buf + (len & ~mask), len & mask); + FQSPI_DAT_WRITE(addr, *(u32 *)(tmp)); + } + } + else + { + aligned_bit = (addr & mask); + addr = addr - aligned_bit; + reg_val = FQSPI_READ_REG32(addr, 0); + + for (loop = 0; loop < (FQSPI_ALIGNED_BYTE - aligned_bit); loop++) + { + val = (val << 8) | (buf[loop]); + reg_val &= (~(0xff << (loop * 8))); + } + + reg_val |= val; + reg_val = __builtin_bswap32(reg_val); + FQSPI_DAT_WRITE(addr, reg_val); + + buf = buf + loop; + len = len - loop; + addr = addr + FQSPI_ALIGNED_BYTE; + + FQSPI_DEBUG("addr=%p, buf=%p, len=%d, value=%#x\r\n", addr, buf, len, *(u32 *)(buf)); + + for (loop = 0; loop < (len >> 2); loop++) + { + FQSPI_DAT_WRITE(addr + FQSPI_ALIGNED_BYTE * loop, *(u32 *)(buf + FQSPI_ALIGNED_BYTE * loop)); + } + + if (!IS_ALIGNED(len, FQSPI_ALIGNED_BYTE)) + { + buf = buf + FQSPI_ALIGNED_BYTE * loop; + len = len - FQSPI_ALIGNED_BYTE * loop; + addr = addr + FQSPI_ALIGNED_BYTE * loop; + memcpy(tmp, buf, len); + FQSPI_DAT_WRITE(addr, *(u32 *)(tmp)); + } + } + + /* flush buffer data to Flash */ + FQspiWriteFlush(base_addr); + + ret = FQspiFlashWaitForCmd(pctrl); + + return ret; +} + +/** + * @name: FQspiFlashPortReadData + * @msg: read flash data use register port + * @param {FQspiCtrl} *pctrl, instance of FQSPI controller + * @param {u8} cmd, command to read flash,see the Flash manual for details + * @param {u32} chip_addr, The start address of the chip to read + * @param {u8} *buf, read buffer + * @param {size_t} len, read length + * @return {FError} err code information, FQSPI_SUCCESS indicates success,others indicates failed + */ +FError FQspiFlashPortReadData(FQspiCtrl *pctrl, u8 cmd, u32 chip_addr, u8 *buf, size_t len) +{ + FASSERT(pctrl && buf); + if (FT_COMPONENT_IS_READY != pctrl->is_ready) + { + FQSPI_ERROR("Nor flash not ready !!!"); + return FQSPI_NOT_READY; + } + + FError ret = FQSPI_SUCCESS; + u32 addr = chip_addr + pctrl->config.channel * pctrl->flash_size; + uintptr base_addr = pctrl->config.base_addr; + + FQspiXIPModeSet(base_addr, FQSPI_XIP_EXIT); + + memset(&pctrl->cmd_def, 0, sizeof(pctrl->cmd_def)); + pctrl->cmd_def.cmd = cmd; + pctrl->cmd_def.wait = FQSPI_WAIT_ENABLE; + pctrl->cmd_def.through = 0; + pctrl->cmd_def.cs = pctrl->config.channel; + pctrl->cmd_def.transfer = FQSPI_TRANSFER_1_1_1; + pctrl->cmd_def.cmd_addr = FQSPI_CMD_ADDR_ENABLE; + pctrl->cmd_def.latency = FQSPI_CMD_LATENCY_DISABLE; + pctrl->cmd_def.data_transfer = FQSPI_CMD_DATA_ENABLE; + pctrl->cmd_def.addr_sel = FQSPI_ADDR_SEL_3; + pctrl->cmd_def.dummy = 0; + pctrl->cmd_def.p_buffer = FQSPI_USE_BUFFER_ENABLE; + pctrl->cmd_def.rw_num = (len - 1); + pctrl->cmd_def.sck_sel = FQSPI_SCK_DIV_128; + + ret = FQspiCommandPortConfig(pctrl); + if (ret != FT_SUCCESS) + { + FQSPI_ERROR("FQspiFlashPortReadData FQspiCommandPortConfig failed!"); + return ret; + } + + /* write addr port register */ + FQspiAddrPortConfig(base_addr, addr); + + FQspiCommandPortSend(base_addr); + + FQspiGetLdPortData(base_addr, buf, len); + + /* wait SR1V bit0 WIP is ready, not device busy */ + ret = FQspiFlashWaitForCmd(pctrl); + + return ret; +} + +/** + * @name: FQspiFlashPortWriteData + * @msg: write flash data use register port + * @param {FQspiCtrl} *pctrl, instance of FQSPI controller + * @param {u8} cmd, command to write flash,see the Flash manual for details + * @param {u32} chip_addr, The start address of the chip to write + * @param {u8} *buf, write buffer + * @param {size_t} len, write length + * @return {FError} err code information, FQSPI_SUCCESS indicates success,others indicates failed + */ +FError FQspiFlashPortWriteData(FQspiCtrl *pctrl, u8 cmd, u32 chip_addr, u8 *buf, size_t len) +{ + FASSERT(pctrl && buf); + FASSERT(len <= FQSPI_CMD_PORT_CMD_RW_MAX); + + if (FT_COMPONENT_IS_READY != pctrl->is_ready) + { + FQSPI_ERROR("Nor flash not ready !!!"); + return FQSPI_NOT_READY; + } + + FError ret = FQSPI_SUCCESS; + u32 addr = chip_addr + pctrl->config.channel * pctrl->flash_size; + uintptr base_addr = pctrl->config.base_addr; + + /* Flash write enable */ + FQspiFlashEnableWrite(pctrl); + + memset(&pctrl->cmd_def, 0, sizeof(pctrl->cmd_def)); + pctrl->cmd_def.cmd = cmd; + pctrl->cmd_def.wait = FQSPI_WAIT_ENABLE; + pctrl->cmd_def.through = 0; + pctrl->cmd_def.cs = pctrl->config.channel; + pctrl->cmd_def.transfer = FQSPI_TRANSFER_1_1_1; + pctrl->cmd_def.cmd_addr = FQSPI_CMD_ADDR_ENABLE; + pctrl->cmd_def.latency = FQSPI_CMD_LATENCY_DISABLE; + pctrl->cmd_def.data_transfer = FQSPI_CMD_DATA_ENABLE; + pctrl->cmd_def.addr_sel = FQSPI_ADDR_SEL_3; + pctrl->cmd_def.dummy = 0; + pctrl->cmd_def.p_buffer = FQSPI_USE_BUFFER_DISABLE; + pctrl->cmd_def.rw_num = (len - 1); + pctrl->cmd_def.sck_sel = FQSPI_SCK_DIV_128; + + /*write cmd_reg to Command port register 0x10 */ + ret = FQspiCommandPortConfig(pctrl); + if (ret != FT_SUCCESS) + { + FQSPI_ERROR("FQspiFlashPortWriteData FQspiCommandPortConfig failed!"); + return ret; + } + + /* write addr port register */ + FQspiAddrPortConfig(base_addr, addr); + + FQspiSetLdPortData(base_addr, buf, len); + + /* wait SR1V bit0 WIP is ready, not device busy */ + ret = FQspiFlashWaitForCmd(pctrl); + + return ret; +} + + +/** + * @name: FQspiFlashErase + * @msg: erase flash data + * @param {FQspiCtrl} *pctrl, instance of FQSPI controller + * @param {u8} command, command to erase flash, see the Flash manual for details + * @param {u32} offset,Relative Byte Address Offset + * @return {FError} err code information, FQSPI_SUCCESS indicates success,others indicates failed 表示擦除成功,其它返回值表示擦除失败 + */ +FError FQspiFlashErase(FQspiCtrl *pctrl, u8 command, u32 offset) +{ + FASSERT(pctrl); + FError ret = FQSPI_SUCCESS; + if (FT_COMPONENT_IS_READY != pctrl->is_ready) + { + FQSPI_ERROR("Nor flash not ready !!!"); + return FQSPI_NOT_READY; + } + + uintptr base_addr = pctrl->config.base_addr; + + /* Flash write enable */ + FQspiFlashEnableWrite(pctrl); + + memset(&pctrl->cmd_def, 0, sizeof(pctrl->cmd_def)); + pctrl->cmd_def.cmd = command; + pctrl->cmd_def.cs = pctrl->config.channel; + pctrl->cmd_def.sck_sel = FQSPI_SCK_DIV_128; + + switch (command) + { + case FQSPI_FLASH_CMD_SE: + /* set addr_sel region, FQSPI_ADDR_SEL_3 or FQSPI_ADDR_SEL_4 */ + pctrl->cmd_def.addr_sel = FQSPI_ADDR_SEL_3; + + /* set cmd_addr region, by command, have addr transfer */ + pctrl->cmd_def.cmd_addr = FQSPI_CMD_ADDR_ENABLE; + + /* need some execution time */ + pctrl->cmd_def.wait = FQSPI_WAIT_ENABLE; + + break; + case FQSPI_FLASH_CMD_4SE: + pctrl->cmd_def.addr_sel = FQSPI_ADDR_SEL_4; + pctrl->cmd_def.cmd_addr = FQSPI_CMD_ADDR_ENABLE; + pctrl->cmd_def.wait = FQSPI_WAIT_ENABLE; + + break; + case FQSPI_FLASH_CMD_P4E: + pctrl->cmd_def.addr_sel = FQSPI_ADDR_SEL_3; + pctrl->cmd_def.cmd_addr = FQSPI_CMD_ADDR_ENABLE; + + break; + case FQSPI_FLASH_CMD_4P4E: + pctrl->cmd_def.addr_sel = FQSPI_ADDR_SEL_4; + pctrl->cmd_def.cmd_addr = FQSPI_CMD_ADDR_ENABLE; + + break; + case FQSPI_FLASH_CMD_BE: + pctrl->cmd_def.addr_sel = FQSPI_ADDR_SEL_3; + break; + case FQSPI_FLASH_CMD_4BE: + pctrl->cmd_def.addr_sel = FQSPI_ADDR_SEL_3; + break; + default: + return FQSPI_NOT_SUPPORT; + } + + /*write cmd_reg to Command port register 0x10 */ + ret = FQspiCommandPortConfig(pctrl); + if (ret != FT_SUCCESS) + { + FQSPI_ERROR("FQspiFlashErase FQspiCommandPortConfig failed!"); + return ret; + } + + /* set addr port register, specify addr transfer */ + FQspiAddrPortConfig(base_addr, offset); + + /*write value to low bit port register 0x1c, make command valid */ + FQspiCommandPortSend(base_addr); + + /* wait command perform end */ + ret = FQspiFlashWaitForCmd(pctrl); + + return ret; +} + +/** + * @name: FQspiFlashEnableWrite + * @msg: Flash write enable + * @param {FQspiCtrl} *pctrl, instance of FQSPI controller + * @return {FError} err code information, FQSPI_SUCCESS indicates success,others indicates failed 表示执行成功,其它返回值表示执行失败 + */ +FError FQspiFlashEnableWrite(FQspiCtrl *pctrl) +{ + FASSERT(pctrl); + FError ret = FQSPI_SUCCESS; + u32 timeout = FQSPI_BUSY_TIMEOUT_US; + + if (FT_COMPONENT_IS_READY != pctrl->is_ready) + { + FQSPI_ERROR("Nor flash not ready !!!"); + return FQSPI_NOT_READY; + } + uintptr base_addr = pctrl->config.base_addr; + + memset(&pctrl->cmd_def, 0, sizeof(pctrl->cmd_def)); + pctrl->cmd_def.cmd = FQSPI_FLASH_CMD_WREN; + pctrl->cmd_def.cs = pctrl->config.channel; + pctrl->cmd_def.sck_sel = FQSPI_SCK_DIV_128; + + /*write cmd_reg to Command port register 0x10 */ + ret = FQspiCommandPortConfig(pctrl); + if (ret != FT_SUCCESS) + { + FQSPI_ERROR("FQspiFlashEnableWrite FQspiCommandPortConfig failed!"); + return ret; + } + + /*write value to low bit port register 0x1c, make command valid */ + FQspiCommandPortSend(base_addr); + + /* wait SR1V bit0 WIP is ready, not device busy */ + ret = FQspiFlashWaitForCmd(pctrl); + + return ret; +} + +/** + * @name: FQspiFlashDisableWrite + * @msg: Flash write disable + * @param {FQspiCtrl} *pctrl, instance of FQSPI controller + * @return {FError} err code information, FQSPI_SUCCESS indicates success,others indicates failed 表示执行成功,其它返回值表示执行失败 + */ +FError FQspiFlashDisableWrite(FQspiCtrl *pctrl) +{ + FASSERT(pctrl); + FError ret = FQSPI_SUCCESS; + if (FT_COMPONENT_IS_READY != pctrl->is_ready) + { + FQSPI_ERROR("Nor flash not ready !!!"); + return FQSPI_NOT_READY; + } + uintptr base_addr = pctrl->config.base_addr; + + memset(&pctrl->cmd_def, 0, sizeof(pctrl->cmd_def)); + pctrl->cmd_def.cmd = FQSPI_FLASH_CMD_WRDI; + pctrl->cmd_def.cs = pctrl->config.channel; + pctrl->cmd_def.sck_sel = FQSPI_SCK_DIV_128; + + /*write cmd_reg to Command port register 0x10 */ + ret = FQspiCommandPortConfig(pctrl); + if (ret != FT_SUCCESS) + { + FQSPI_ERROR("FQspiFlashDisableWrite FQspiCommandPortConfig failed!"); + return ret; + } + + /*write value to low bit port register 0x1c, make command valid */ + FQspiCommandPortSend(base_addr); + + return ret; +} + +/** + * @name: FQspiFlashWriteReg + * @msg: write flash register + * @param {FQspiCtrl} *pctrl, instance of FQSPI controller + * @param {u8} command, command to write flash register,see the Flash manual for details + * @param {u8} *buf, write buffer + * @param {size_t} len, write length + * @return {FError} err code information, FQSPI_SUCCESS indicates success,others indicates failed 表示写入成功,其它返回值表示写入失败 + */ +FError FQspiFlashWriteReg(FQspiCtrl *pctrl, u8 command, const u8 *buf, size_t len) +{ + FASSERT(pctrl); + FError ret = FQSPI_SUCCESS; + u8 sr1_v = 0; + + if (FT_COMPONENT_IS_READY != pctrl->is_ready) + { + FQSPI_ERROR("Nor flash not ready !!!"); + return FQSPI_NOT_READY; + } + + uintptr base_addr = pctrl->config.base_addr; + + memset(&pctrl->cmd_def, 0, sizeof(pctrl->cmd_def)); + pctrl->cmd_def.cmd = command; + pctrl->cmd_def.cs = pctrl->config.channel; + pctrl->cmd_def.data_transfer = FQSPI_CMD_DATA_ENABLE; + pctrl->cmd_def.p_buffer = FQSPI_USE_BUFFER_ENABLE; + pctrl->cmd_def.sck_sel = FQSPI_SCK_DIV_128; + + if (len > 4) + { + FQSPI_ERROR("data length exceed. commad 0x%lx, len:%d \n", command, len); + return FQSPI_INVAL_PARAM; + } + else if ((len > 0) && (buf != NULL)) + { + /* set rw_num region, len - 1 */ + pctrl->cmd_def.rw_num = (len - 1); + + /*write cmd_reg to Command port register 0x10 */ + FQspiCommandPortConfig(pctrl); + + /* set ld port data(buf) and make command valid */ + FQspiSetLdPortData(base_addr, buf, len); + } + else + { + /*write cmd_reg to Command port register 0x10 */ + FQspiCommandPortConfig(pctrl); + + FQspiCommandPortSend(base_addr); + } + + /* wait SR1V bit0 WIP is ready, not device busy */ + ret = FQspiFlashWaitForCmd(pctrl); + + return ret; +} + +/** + * @name: FQspiFlashWaitForCmd + * @msg: wait flash command execution complete + * @param {FQspiCtrl} *pctrl, instance of FQSPI controller + * @return {FError} err code information, FQSPI_SUCCESS indicates success,others indicates failed 表示成功完成,其它返回值表示失败 + */ +FError FQspiFlashWaitForCmd(FQspiCtrl *pctrl) +{ + FASSERT(pctrl); + u32 timeout = FQSPI_BUSY_TIMEOUT_US; + FError ret = FQSPI_SUCCESS; + u8 sr1 = 0; + + uintptr base_addr = pctrl->config.base_addr; + + ret = FQspiFlashSpecialInstruction(pctrl, FQSPI_FLASH_CMD_RDSR1, &sr1, sizeof(sr1)); + if (FQSPI_SUCCESS != ret) + { + FQSPI_ERROR("failed to read sr1, result 0x%x\r\n", ret); + return ret; + } + + do + { + timeout--; + /* read value from low bit port register 0x1c, + Read Status Register 1 is related to SR1V WIP field (bit0) */ + FQspiGetLdPortData(base_addr, &sr1, 1); + + if (!timeout) + { + FQSPI_ERROR("wait cmd timeout !!!"); + ret = FQSPI_TIMEOUT; + break; + } + + } + while (sr1 & FQSPI_NOR_FLASH_STATE_BUSY); + + return ret; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/qspi/fqspi/fqspi_flash.h b/bsp/phytium/libraries/standalone/drivers/qspi/fqspi/fqspi_flash.h new file mode 100644 index 0000000000..26712ca9b3 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/qspi/fqspi/fqspi_flash.h @@ -0,0 +1,147 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fqspi_flash.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-07-12 16:20:55 + * Description:   + * This file is for S25FS256, GD25Q256, GD25Q64 norflash program, includes reading and writing registers and data, + * Users can refer to this file to adapt chips from other manufacturers. + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.1 wangxiaodong 2021.11.12 re-construct + * 1.2 wangxiaodong 2022.3.27 re-construct + * 1.3 wangxiaodong 2022.7.5 adapt to e2000 + */ + +#ifndef BSP_DRIVERS_FQSPI_FLASH_H +#define BSP_DRIVERS_FQSPI_FLASH_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "fkernel.h" +#include "ftypes.h" +#include "ferror_code.h" +#include "fqspi.h" + +/* qspi flash support manufacturer JEDEC ID */ +#define FQSPI_FLASH_MF_ID_CYPRESS 0x01 +#define FQSPI_FLASH_MF_ID_GIGADEVICE 0xC8 +#define FQSPI_FLASH_MF_ID_BOYA 0x68 + +/* qspi flash supported information table */ +#define FQSPI_FLASH_INFO_TABLE \ +{ \ + {"S25FS256S", FQSPI_FLASH_MF_ID_CYPRESS, 0x02, 0x19, FQSPI_FLASH_CAP_32MB}, \ + {"GD25Q32C", FQSPI_FLASH_MF_ID_GIGADEVICE, 0x40, 0x16, FQSPI_FLASH_CAP_4MB}, \ + {"GD25Q32E", FQSPI_FLASH_MF_ID_GIGADEVICE, 0x60, 0x16, FQSPI_FLASH_CAP_4MB}, \ + {"GD25Q64B", FQSPI_FLASH_MF_ID_GIGADEVICE, 0x40, 0x17, FQSPI_FLASH_CAP_8MB}, \ + {"GD25LQ128E", FQSPI_FLASH_MF_ID_GIGADEVICE, 0x40, 0x18, FQSPI_FLASH_CAP_16MB}, \ + {"GD25QL256D", FQSPI_FLASH_MF_ID_GIGADEVICE, 0x60, 0x19, FQSPI_FLASH_CAP_32MB}, \ + {"BY25Q64BS", FQSPI_FLASH_MF_ID_BOYA, 0x40, 0x17, FQSPI_FLASH_CAP_8MB}, \ + {"BY25Q32BS", FQSPI_FLASH_MF_ID_BOYA, 0x40, 0x16, FQSPI_FLASH_CAP_4MB} \ +} + +#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_DUAL_READ 0xBB /* Dual 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_CMD_SFDP 0x5A /* Read JEDEC Serial Manu ID */ +#define FQSPI_CMD_ENABLE_RESET 0x66 /* Software Reset Enable */ +#define FQSPI_CMD_RESET 0x99 /* Software Reset */ + +/* boya flash */ +#define FQSPI_FLASH_CMD_WRITE_SR2 0x31 /* Write status register 2 */ + + +#define FQSPI_BUSY_TIMEOUT_US 1000000 +#define FQSPI_NOR_FLASH_STATE_BUSY BIT(0) + +/* Read some flash information */ +FError FQspiFlashSpecialInstruction(FQspiCtrl *pctrl, u8 cmd, u8 *buf, size_t len); + +/* read flash sfdp-Serial Flash Discoverable Parameter */ +FError FQspiFlashReadSfdp(FQspiCtrl *pctrl, u32 offset, u8 *buf, size_t len); + +/* read flash register */ +FError FQspiFlashReadReg(FQspiCtrl *pctrl, u32 offset, u8 *buf, size_t len); + +/* write flash register */ +FError FQspiFlashWriteReg(FQspiCtrl *pctrl, u8 command, const u8 *buf, size_t len); + +/* read flash data config */ +FError FQspiFlashReadDataConfig(FQspiCtrl *pctrl, u8 command); + +/* read flash data */ +size_t FQspiFlashReadData(FQspiCtrl *pctrl, u32 chip_addr, u8 *buf, size_t len); + +/* write flash data */ +FError FQspiFlashWriteData(FQspiCtrl *pctrl, u8 command, u32 chip_addr, const u8 *buf, size_t len); + +/* flash erase */ +FError FQspiFlashErase(FQspiCtrl *pctrl, u8 command, u32 offset); + +/* flash write enable */ +FError FQspiFlashEnableWrite(FQspiCtrl *pctrl); + +/* flash write disable */ +FError FQspiFlashDisableWrite(FQspiCtrl *pctrl); + +/* wait flash command execution complete */ +FError FQspiFlashWaitForCmd(FQspiCtrl *pctrl); + +/* read flash data use register port */ +FError FQspiFlashPortReadData(FQspiCtrl *pctrl, u8 cmd, u32 chip_addr, u8 *buf, size_t len); + +/* write flash data use register port */ +FError FQspiFlashPortWriteData(FQspiCtrl *pctrl, u8 cmd, u32 chip_addr, u8 *buf, size_t len); + +/* detect flash information */ +FError FQspiFlashDetect(FQspiCtrl *pctrl); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsp/phytium/libraries/standalone/drivers/qspi/fqspi/fqspi_g.c b/bsp/phytium/libraries/standalone/drivers/qspi/fqspi/fqspi_g.c new file mode 100644 index 0000000000..e152ccd9f0 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/qspi/fqspi/fqspi_g.c @@ -0,0 +1,42 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fqspi_g.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:00:41 + * Description:   + * This file is for + * + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.1 wangxiaodong 2021.11.12 re-construct + * 1.2 wangxiaodong 2022.3.27 re-construct + */ + +#include "fparameters.h" +#include "fqspi.h" +#include "sdkconfig.h" + +FQspiConfig FQspiConfigTbl[FQSPI_INSTANCE_NUM] = +{ + { + .instance_id = FQSPI_INSTANCE_0, + .base_addr = FQSPI_BASEADDR, + .mem_start = FQSPI_MEM_START_ADDR, + .capacity = 0, + .dev_num = 0, + .channel = 0, + } +}; \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/qspi/fqspi/fqspi_hw.c b/bsp/phytium/libraries/standalone/drivers/qspi/fqspi/fqspi_hw.c new file mode 100644 index 0000000000..e759c661a5 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/qspi/fqspi/fqspi_hw.c @@ -0,0 +1,159 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fqspi_hw.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:00:41 + * Description:   + * This file is for + * + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ +#include "ftypes.h" +#include "ferror_code.h" +#include "fassert.h" +#include "fdebug.h" +#include "fqspi_hw.h" + +#define FQSPI_DEBUG_TAG "FQSPI-HW" +#define FQSPI_ERROR(format, ...) FT_DEBUG_PRINT_E(FQSPI_DEBUG_TAG, format, ##__VA_ARGS__) +#define FQSPI_WARN(format, ...) FT_DEBUG_PRINT_W(FQSPI_DEBUG_TAG, format, ##__VA_ARGS__) +#define FQSPI_INFO(format, ...) FT_DEBUG_PRINT_I(FQSPI_DEBUG_TAG, format, ##__VA_ARGS__) +#define FQSPI_DEBUG(format, ...) FT_DEBUG_PRINT_D(FQSPI_DEBUG_TAG, format, ##__VA_ARGS__) + + +/** + * @name: FQspiGetLdPortData + * @msg: read low data port register data + * @param {uintptr} base_addr, FQSPI controller base address + * @param {u8} *buf, read buffer + * @param {size_t} len, read length + * @return + */ +void FQspiGetLdPortData(uintptr base_addr, u8 *buf, size_t len) +{ + FASSERT(buf); + u32 loop = 0; + u32 reg_val = 0; + + for (loop = 0; loop < len; loop++) + { + /* read 4 bytes one time */ + if (0 == loop % 4) + { + reg_val = FQSPI_READ_REG32(base_addr, FQSPI_REG_LD_PORT_OFFSET); + } + + /* assign buf byte by byte */ + buf[loop] = (u8)((reg_val >> (loop % 4) * 8) & 0xFF); + } + +} + +/** + * @name: FQspiSetLdPortData + * @msg: set low data port register data + * @param {uintptr} base_addr, FQSPI controller base address + * @param {u8} *buf, write buffer + * @param {size_t} len, write length + * @return + */ +void FQspiSetLdPortData(uintptr base_addr, const u8 *buf, size_t len) +{ + FASSERT(buf); + FASSERT((len < 5) && (len)); + u32 reg_val = 0; + + if (1 == len) + { + reg_val = buf[0]; + } + else if (2 == len) + { + reg_val = buf[1]; + reg_val = (reg_val << 8) + buf[0]; + } + else if (3 == len) + { + reg_val = buf[2]; + reg_val = (reg_val << 8) + buf[1]; + reg_val = (reg_val << 8) + buf[0]; + } + else + { + reg_val = buf[3]; + reg_val = (reg_val << 8) + buf[2]; + reg_val = (reg_val << 8) + buf[1]; + reg_val = (reg_val << 8) + buf[0]; + } + + /*write value to low bit port register 0x1c, make command valid */ + FQSPI_WRITE_REG32(base_addr, FQSPI_REG_LD_PORT_OFFSET, reg_val); +} + + +/** + * @name: FQspiWriteFlush + * @msg: config write flush register to make wr_cfg complete program + * @param {uintptr} base_addr, FQSPI controller base address + * @return + */ +void FQspiWriteFlush(uintptr base_addr) +{ + FQSPI_WRITE_REG32(base_addr, FQSPI_REG_FLUSH_OFFSET, 0x1); +} + +/** + * @name: FQspiCommandPortSend + * @msg: send command port register value + * @param {uintptr} base_addr, FQSPI controller base address + * @return void + */ +void FQspiCommandPortSend(uintptr base_addr) +{ + FQSPI_WRITE_REG32(base_addr, FQSPI_REG_LD_PORT_OFFSET, 0x0); +} + +/** + * @name: FQspiAddrPortConfig + * @msg: config address port register value + * @param {uintptr} base_addr, FQSPI controller base address + * @param {u32} addr addresss value write to register + * @return + */ +void FQspiAddrPortConfig(uintptr base_addr, u32 addr) +{ + FQSPI_WRITE_REG32(base_addr, FQSPI_REG_ADDR_PORT_OFFSET, addr); +} + +/** + * @name: FQspiXIPModeSet + * @msg: config qspi xip mode + * @param {uintptr} base_addr, FQSPI controller base address + * @param {u8} enable enable or disable xip mode + * @return + */ +void FQspiXIPModeSet(uintptr base_addr, u8 enable) +{ + if (enable) + { + FQSPI_WRITE_REG32(base_addr, FQSPI_REG_MODE_OFFSET, FQSPI_QUAD_READ_MODE_ENABLE); + } + else + { + FQSPI_WRITE_REG32(base_addr, FQSPI_REG_MODE_OFFSET, FQSPI_QUAD_READ_MODE_DISABLE); + } +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/qspi/fqspi/fqspi_hw.h b/bsp/phytium/libraries/standalone/drivers/qspi/fqspi/fqspi_hw.h new file mode 100644 index 0000000000..d80c0a1398 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/qspi/fqspi/fqspi_hw.h @@ -0,0 +1,189 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fqspi_hw.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:00:23 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.1 wangxiaodong 2021.11.12 re-construct + */ + +#ifndef BSP_DRIVERS_FQSPI_HW_H +#define BSP_DRIVERS_FQSPI_HW_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "fio.h" +#include "fkernel.h" + +/* register definition */ +#define FQSPI_REG_CAP_OFFSET (0x00) /* Flash capacity setting register */ +#define FQSPI_REG_RD_CFG_OFFSET (0x04) /* Address access reads configuration registers */ +#define FQSPI_REG_WR_CFG_OFFSET (0x08) /* Write buffer flush register */ +#define FQSPI_REG_FLUSH_OFFSET (0x0C) /* Write buffer flush register */ +#define FQSPI_REG_CMD_PORT_OFFSET (0x10) /* Command port register */ +#define FQSPI_REG_ADDR_PORT_OFFSET (0x14) /* Address port register */ +#define FQSPI_REG_HD_PORT_OFFSET (0x18) /* Upper bit port register */ +#define FQSPI_REG_LD_PORT_OFFSET (0x1C) /* low bit port register */ +#define FQSPI_REG_CS_TIMING_SET_OFFSET (0x20) /* CS setting register */ +#define FQSPI_REG_WIP_RD_OFFSET (0x24) /* WIP reads the Settings register */ +#define FQSPI_REG_WP_OFFSET (0x28) /* WP register */ +#define FQSPI_REG_MODE_OFFSET (0x2C) /* Mode setting register */ + +/* FQSPI_CAP */ +#define FQSPI_CAP_FLASH_NUM(data) ((data) << 3) /* Flash number */ +#define FQSPI_CAP_FLASH_CAP(data) ((data) << 0) /* The flash capacity */ + +#define FQSPI_CAP_FLASH_NUM_MASK GENMASK(4, 3) +#define FQSPI_CAP_FLASH_CAP_MASK GENMASK(2, 0) + +/* RD_CFG */ +#define FQSPI_RD_CFG_CMD(data) ((data) << 24) /* Read Command */ +#define FQSPI_RD_CFG_THROUGH(data) ((data) << 23) /* The programming flag in the status register */ +#define FQSPI_RD_CFG_TRANSFER(data) ((data) << 20) /* rd_tranfer region */ +#define FQSPI_RD_CFG_ADDR_SEL(data) ((data) << 19) /* rd_addr_sel region*/ +#define FQSPI_RD_CFG_LATENCY(data) ((data) << 18) /* rd_latency region*/ +#define FQSPI_RD_CFG_MODE_BYTE(data) ((data) << 17) /* mode byte region*/ +#define FQSPI_RD_CFG_CMD_SIGN(data) ((data) << 9) /* cmd_sign region*/ +#define FQSPI_RD_CFG_DUMMY(data) ((data-1) << 4) /* dummy region*/ +#define FQSPI_RD_CFG_D_BUFFER(data) ((data) << 3) /* d_buffer region*/ +#define FQSPI_RD_CFG_SCK_SEL(data) ((data) << 0) /* rd_sck_sel region*/ + +#define FQSPI_RD_CFG_CMD_MASK GENMASK(31, 24) +#define FQSPI_RD_CFG_SCK_SEL_MASK GENMASK(2, 0) +#define FQSPI_RD_CFG_TRANSFER_MASK GENMASK(22, 20) +#define FQSPI_RD_CFG_ADDR_SEL_MASK FQSPI_RD_CFG_ADDR_SEL(0x1) +#define FQSPI_RD_CFG_DUMMY_MASK GENMASK(8, 4) + +/* FQSPI_WR_CFG */ +#define FQSPI_WR_CFG_CMD(data) ((data) << 24) +#define FQSPI_WR_CFG_WAIT(data) ((data) << 9) +#define FQSPI_WR_CFG_THROUGH(data) ((data) << 8) +#define FQSPI_WR_CFG_TRANSFER(data) ((data) << 5) +#define FQSPI_WR_CFG_ADDRSEL(data) ((data) << 4) +#define FQSPI_WR_CFG_MODE(data) ((data) << 3) +#define FQSPI_WR_CFG_SCK_SEL(data) ((data) << 0) + +#define FQSPI_WR_CFG_CMD_MASK GENMASK(31, 24) +#define FQSPI_WR_CFG_SCK_SEL_MASK GENMASK(2, 0) +#define FQSPI_WR_CFG_ADDRSEL_MASK FQSPI_WR_CFG_ADDRSEL(0x1) + +/* FQSPI_CMD_PORT */ +#define FQSPI_CMD_PORT_CMD(data) ((data) << 24) +#define FQSPI_CMD_PORT_WAIT(data) ((data) << 22) +#define FQSPI_CMD_PORT_THROUGH(data) ((data) << 21) +#define FQSPI_CMD_PORT_CS(data) ((data) << 19) +#define FQSPI_CMD_PORT_TRANSFER(data) ((data) << 16) +#define FQSPI_CMD_PORT_CMD_ADDR(data) ((data) << 15) +#define FQSPI_CMD_PORT_LATENCY(data) ((data) << 14) +#define FQSPI_CMD_PORT_DATA_TRANS(data) ((data) << 13) +#define FQSPI_CMD_PORT_ADDR_SEL(data) ((data) << 12) +#define FQSPI_CMD_PORT_DUMMY(data) ((data-1) << 7) +#define FQSPI_CMD_PORT_P_BUFFER(data) ((data) << 6) +#define FQSPI_CMD_PORT_RW_NUM(data) ((data) << 3) +#define FQSPI_CMD_PORT_CLK_SEL(data) ((data) << 0) + +#define FQSPI_CMD_PORT_RW_NUM_MASK GENMASK(5, 3) +#define FQSPI_CMD_PORT_CLK_SEL_MASK GENMASK(2, 0) +#define FQSPI_CMD_PORT_CS_MASK GENMASK(20, 19) +#define FQSPI_CMD_PORT_CMD_MASK GENMASK(31, 24) +#define FQSPI_CMD_PORT_ADDR_SEL_MASK FQSPI_CMD_PORT_ADDR_SEL(0x1) + +#define FQSPI_CMD_PORT_CMD_RW_MAX 8 + +/* FQSPI_CS_TIMING_SET */ +#define FQSPI_FUN_SET_CS_HOLD(data) ((data) << 24) +#define FQSPI_FUN_SET_CS_SETUP(data) ((data) << 16) +#define FQSPI_FUN_SET_CS_DELAY(data) ((data) << 0) + +/* FQSPI_WIP_RD */ +#define FQSPI_WIP_RD_CMD(data) ((data) << 24) +#define FQSPI_WIP_RD_TRANSFER(data) ((data) << 3) +#define FQSPI_WIP_RD_SCK_SEL(data) ((data) << 0) + +/* FQSPI_WP */ +#define FQSPI_WP_EN(data) ((data) << 17) +#define FQSPI_WP_WP(data) ((data) << 16) +#define FQSPI_WP_HOLD(data) ((data) << 8) +#define FQSPI_WP_SETUP(data) ((data) << 0) + +/* FQSPI_MODE */ +#define FQSPI_MODE_VALID(data) ((data) << 8) +#define FQSPI_MODE_MODE(data) ((data) << 0) + +#define FQSPI_QUAD_READ_MODE_ENABLE 0xF0A0 /* enable FLASH XIP MODE */ +#define FQSPI_QUAD_READ_MODE_DISABLE 0xF0BF /* disable FLASH XIP MODE */ +#define FQSPI_QUAD_READ_MODE_CMD 0xA0 /* FLASH XIP MODE CMD SIGN */ + + +typedef enum +{ + FQSPI_CMD_READ = 0x01, + FQSPI_CMD_WRITE = 0x02, +} FQspiCmdFlags; + +/** + * @name: FQSPI_READ_REG32 + * @msg: read FQSPI register + * @param {u32} addr, base address of FQSPI + * @param {u32} reg_offset, offset of register + * @return {u32} register value + */ +#define FQSPI_READ_REG32(addr, reg_offset) FtIn32((addr) + (u32)(reg_offset)) + +/** + * @name: FQSPI_WRITE_REG32 + * @msg: write FQSPI register + * @param {u32} addr, base address of FQSPI + * @param {u32} reg_offset, offset of register + * @param {u32} reg_value, set register value + * @return {void} + */ +#define FQSPI_WRITE_REG32(addr, reg_offset, reg_value) FtOut32(addr + (u32)reg_offset, (u32)reg_value) + +/* FQSPI Data Operations */ +#define FQSPI_DAT_WRITE(addr, dat) FtOut32((addr), (u32)(dat)) + + +/* read ld port data */ +void FQspiGetLdPortData(uintptr base_addr, u8 *buf, size_t len); + +/* set ld port data */ +void FQspiSetLdPortData(uintptr base_addr, const u8 *buf, size_t len); + +/* send command port register config */ +void FQspiCommandPortSend(uintptr base_addr); + +/* address port register config */ +void FQspiAddrPortConfig(uintptr base_addr, u32 addr); + +/* write flush register */ +void FQspiWriteFlush(uintptr base_addr); + +/* qspi xip mode set */ +void FQspiXIPModeSet(uintptr base_addr, u8 enable); + + + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/qspi/fqspi/fqspi_sinit.c b/bsp/phytium/libraries/standalone/drivers/qspi/fqspi/fqspi_sinit.c new file mode 100644 index 0000000000..d6636990a8 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/qspi/fqspi/fqspi_sinit.c @@ -0,0 +1,47 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fqspi_sinit.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:01:10 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.1 wangxiaodong 2021.11.12 re-construct + */ + +#include "fparameters.h" +#include "fassert.h" +#include "fqspi.h" + +extern FQspiConfig FQspiConfigTbl[FQSPI_INSTANCE_NUM]; + +const FQspiConfig *FQspiLookupConfig(u32 instance_id) +{ + FASSERT(instance_id < FQSPI_INSTANCE_NUM); + const FQspiConfig *pconfig = NULL; + u32 index; + + for (index = 0; index < (u32)FQSPI_INSTANCE_NUM; index++) + { + if (FQspiConfigTbl[index].instance_id == instance_id) + { + pconfig = &FQspiConfigTbl[index]; + break; + } + } + + return (const FQspiConfig *)pconfig; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/rtc/Kconfig b/bsp/phytium/libraries/standalone/drivers/rtc/Kconfig new file mode 100644 index 0000000000..63cedcbe5c --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/rtc/Kconfig @@ -0,0 +1,9 @@ +menu "FRTC Configuration" + config ENABLE_FRTC + bool + prompt "Use FRTC" + default n + +endmenu + + diff --git a/bsp/phytium/libraries/standalone/drivers/rtc/frtc/frtc.c b/bsp/phytium/libraries/standalone/drivers/rtc/frtc/frtc.c new file mode 100644 index 0000000000..8ee7ad3484 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/rtc/frtc/frtc.c @@ -0,0 +1,231 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: frtc.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:02:33 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Wangxiaodong 2021/11/5 init + */ + +#include +#include "fgeneric_timer.h" +#include "ftypes.h" +#include "fassert.h" + +#include "fdebug.h" +#include "fsleep.h" + +#include "frtc.h" +#include "frtc_hw.h" + + +#define FRTC_DEBUG_TAG "RTC" +#define FRTC_ERROR(format, ...) FT_DEBUG_PRINT_E(FRTC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FRTC_INFO(format, ...) FT_DEBUG_PRINT_I(FRTC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FRTC_DEBUG(format, ...) FT_DEBUG_PRINT_D(FRTC_DEBUG_TAG, format, ##__VA_ARGS__) + +#define FRTC_IS_LEAP_YEAR(year) ((((year) % 4 == 0 ) && ((year) %100 != 0))||( (year) % 400 == 0)) + +/** + * @name: FRtcCheckDateTime + * @msg: check if the date year, month, day, hour, ... is valid + * @return {u32} whether the date is valid + * @param {FRtcDate} *date, pointer to a FRtcDate structure that contains year, month and day + */ +static FError FRtcCheckDateTime(const FRtcDateTime *date_time) +{ + FASSERT(date_time != NULL); + u8 w_hour = date_time->hour; + u8 w_minute = date_time->minute; + u8 w_second = date_time->second; + u8 w_year = date_time->year; + u8 w_month = date_time->month; + u8 w_day = date_time->mday; + u8 days_of_month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; /* 月份天数表 */ + + /* 闰年2月+1天 */ + if ((w_month == 2) && (FRTC_IS_LEAP_YEAR(w_year))) + days_of_month[w_month - 1] += 1; + + /* 判断月份日期是否合法 */ + if ((w_month > 12) || (w_month < 1) || (w_day > days_of_month[w_month - 1]) || (w_day < 1)) + { + FRTC_ERROR("invalid input date: month: %d, day: %d", w_month, w_day); + return FRTC_ERR_DATE_INVALID; + } + + /* 判断时分秒是否合法 */ + if ((w_hour > 23) || (w_minute > 59) || (w_second > 59)) + { + FRTC_ERROR("invalid input time: hour: %d, minute: %d, second: %d", + w_hour, w_minute, w_second); + return FRTC_ERR_TIME_INVALID; + } + + return FRTC_SUCCESS; +} + +/** + * @name: FRtcSetDateTime + * @msg: Set current time in FRtcDateTime + * @return {u32} whether the time setting is successful + * @param {FRtcCtrl} *pctrl, pointer to a FRtcCtrl structure that contains + * the configuration information for the specified rtc module. + * @param {FRtcDate} *date, pointer to a FRtcDate structure that contains year, month and day + * @param {FRtcTime} *time, pointer to a FRtcTime structure that contains hour, minute and second + */ +FError FRtcSetDateTime(FRtcCtrl *pctrl, const FRtcDateTime *date_time) +{ + FASSERT(pctrl != NULL); + FASSERT(date_time != NULL); + uintptr base_addr = pctrl->config.control_base_addr; + struct tm tm; + time_t t; + u32 ret = 0; + + ret = FRtcCheckDateTime(date_time); + if (ret != FRTC_SUCCESS) + { + return ret; + } + + tm.tm_sec = date_time->second; + tm.tm_min = date_time->minute; + tm.tm_hour = date_time->hour; + /* tm->tm_wday */ + tm.tm_mday = date_time->mday; + tm.tm_mon = (date_time->month - 1); + tm.tm_year = (date_time->year - 1900); + + t = mktime(&tm); + + FRTC_WRITE_AES_SEL(base_addr, FRTC_AES_SEL_COUNTER); + + /* write low 32 bit first */ + FRTC_WRITE_CLR_LOW(base_addr, 0); + + /* write low 32 bit next */ + FRTC_WRITE_CLR(base_addr, t); + + return FT_SUCCESS; +} + +/** + * @name: FRtcGetDateTime + * @msg: Get current time in FRtcDateTime + * @return {*} + * @param {FRtcCtrl} *pctrl + * @param {FRtcDateTime} *date_time + */ +FError FRtcGetDateTime(FRtcCtrl *pctrl, FRtcDateTime *date_time) +{ + FASSERT(pctrl != NULL); + FASSERT(date_time != NULL); + time_t seconds = 0; + struct tm *time_p; + + FRtcReadTimeStamp(pctrl, &seconds, NULL); + time_p = localtime(&seconds); + + date_time->year = time_p->tm_year + 1900; + date_time->month = time_p->tm_mon + 1; + date_time->mday = time_p->tm_mday; + date_time->hour = time_p->tm_hour; + date_time->minute = time_p->tm_min; + date_time->second = time_p->tm_sec; + + return FRTC_SUCCESS; +} + +/** + * @name: FRtcReadTimeStamp + * @msg: Read time stamp in seconds and milliseconds + * @return {*} none + * @param {FRtcCtrl} *pctrl + * @param {time_t} *sec_p + * @param {time_t} *msec_p + */ +void FRtcReadTimeStamp(FRtcCtrl *pctrl, time_t *sec_p, time_t *msec_p) +{ + FASSERT(pctrl != NULL); + time_t sec = 0; + u32 msec = 0; + u32 tick = 0; + + uintptr base_addr = pctrl->config.control_base_addr; + + /* tick = 1/32.768k = 0.03ms = 30us, delay more than 4 ticks */ + fsleep_microsec(FRTC_COUNTER_DELAY); + + /* write AES_SEL register, to read CCVR and CDR register */ + FRTC_WRITE_AES_SEL(base_addr, FRTC_AES_SEL_COUNTER); + + /* tick = 1/32.768k = 0.03ms = 30us, delay more than 4 ticks */ + fsleep_microsec(FRTC_COUNTER_DELAY); + + /* read high 32 bit first */ + sec = FRTC_READ_CCVR(base_addr); + + /* read low 32 bit next, The lower 15 bits are valid */ + tick = (FRTC_READ_CDR_LOW(base_addr) & FRTC_COUNTER_LB_MASK); + + /* convert 15 bits tick to milliseconds, count by 32.768k */ + msec = ((tick * 1000) >> FRTC_COUNTER_HB_OFFSET); + + if (sec_p) + *sec_p = sec; + + if (msec_p) + *msec_p = msec; + + return; +} + +/** + * @name: FRtcCfgInitialize + * @msg: Initialize RTC ctrl + * @return {*} + * @param {FRtcCtrl} *instance_p + * @param {FRtcConfig} *input_config_p + */ +FError FRtcCfgInitialize(FRtcCtrl *instance_p, const FRtcConfig *input_config_p) +{ + FASSERT(instance_p && input_config_p); + uintptr base_addr = instance_p->config.control_base_addr; + + instance_p->config = *input_config_p; + instance_p->is_ready = FT_COMPONENT_IS_READY; + + return FRTC_SUCCESS; +} + +/** + * @name: FRtcCfgDeInitialize + * @msg: DeInitialization function for the device instance + * @param {FRtcCtrl} *instance_p FRTC驱动控制数据 + * @return {*} + */ +void FRtcCfgDeInitialize(FRtcCtrl *pctrl) +{ + FASSERT(pctrl); + + pctrl->is_ready = 0; + memset(pctrl, 0, sizeof(*pctrl)); + + return; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/rtc/frtc/frtc.h b/bsp/phytium/libraries/standalone/drivers/rtc/frtc/frtc.h new file mode 100644 index 0000000000..25fdd276d6 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/rtc/frtc/frtc.h @@ -0,0 +1,92 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: frtc.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:02:51 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Wangxiaodong 2021/8/26 init + */ + +#ifndef BSP_DRIVERS_FRTC_H +#define BSP_DRIVERS_FRTC_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +#include "ftypes.h" +#include "ferror_code.h" + +typedef struct +{ + uintptr control_base_addr; /* rtc控制寄存器基地址 */ + const char *instance_name; /* instance name */ +} FRtcConfig; /* rtc配置 */ + +typedef struct +{ + FRtcConfig config; /* rtc配置 */ + u32 is_ready; /* rtc初始化完成标志 */ +} FRtcCtrl; + +typedef struct +{ + u16 year; /*Specifies the RTC Date Year. + This parameter must be a number between Min_Data = 2000 and Max_Data = 2099 */ + u8 month; /*Specifies the RTC Date Month. + This parameter must be a number between Min_Data = 1 and Max_Data = 12 */ + u8 mday; /*Specifies the RTC day of Month. + This parameter must be a number between Min_Data = 1 and Max_Data = 31 */ + u8 hour; /*Specifies the RTC Time Hour. + This parameter must be a number between Min_Data = 0 and Max_Data = 23 */ + u8 minute; /*Specifies the RTC Time Minute. + This parameter must be a number between Min_Data = 0 and Max_Data = 59 */ + u8 second; /*Specifies the RTC Time Second. + This parameter must be a number between Min_Data = 0 and Max_Data = 59 */ +} FRtcDateTime; + +#define FRTC_SUCCESS FT_SUCCESS +#define FRTC_ERR_DATE_INVALID FT_MAKE_ERRCODE(ErrModBsp, ErrBspRtc, BIT(1)) +#define FRTC_ERR_TIME_INVALID FT_MAKE_ERRCODE(ErrModBsp, ErrBspRtc, BIT(2)) + +/* rtc config init */ +const FRtcConfig *FRtcLookupConfig(void); + +/* initialize rtc ctrl */ +FError FRtcCfgInitialize(FRtcCtrl *instance_p, const FRtcConfig *config_p); + +/* deinitialize rtc ctrl */ +void FRtcCfgDeInitialize(FRtcCtrl *pctrl); + +/* set rtc time */ +FError FRtcSetDateTime(FRtcCtrl *pctrl, const FRtcDateTime *date_time); + +/* get rtc time */ +FError FRtcGetDateTime(FRtcCtrl *pctrl, FRtcDateTime *date_time); + +/* read rtc time in secs and mesc */ +void FRtcReadTimeStamp(FRtcCtrl *pctrl, time_t *sec, time_t *msec); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/rtc/frtc/frtc_g.c b/bsp/phytium/libraries/standalone/drivers/rtc/frtc/frtc_g.c new file mode 100644 index 0000000000..e3b54ab488 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/rtc/frtc/frtc_g.c @@ -0,0 +1,34 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: frtc_g.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:01:26 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Wangxiaodong 2021/8/25 init + */ + +#include "fparameters.h" +#include "frtc.h" + +/* default configs of rtc ctrl */ +const FRtcConfig FRtcConfigTbl = +{ + .control_base_addr = RTC_CONTROL_BASE, + .instance_name = "RTC" +}; + diff --git a/bsp/phytium/libraries/standalone/drivers/rtc/frtc/frtc_hw.c b/bsp/phytium/libraries/standalone/drivers/rtc/frtc/frtc_hw.c new file mode 100644 index 0000000000..b7d0772096 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/rtc/frtc/frtc_hw.c @@ -0,0 +1,36 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: frtc_hw.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:01:40 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +/***************************** Include Files *********************************/ + +#include "frtc_hw.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ diff --git a/bsp/phytium/libraries/standalone/drivers/rtc/frtc/frtc_hw.h b/bsp/phytium/libraries/standalone/drivers/rtc/frtc/frtc_hw.h new file mode 100644 index 0000000000..d104692667 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/rtc/frtc/frtc_hw.h @@ -0,0 +1,99 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: frtc_hw.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:01:54 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 wangxiaodong 2021/11/5 init + */ + +#ifndef BSP_DRIVERS_FRTC_HW_H +#define BSP_DRIVERS_FRTC_HW_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "fkernel.h" +#include "ftypes.h" +#include "fio.h" + +/* Rtc register definitions */ + +#define FRTC_CMR 0x04 +#define FRTC_AES_SEL 0x08 +#define FRTC_AES_SEL_COUNTER 0x100 + +#define FRTC_CCR 0x0C +#define FRTC_STAT 0x10 +#define FRTC_RSTAT 0x14 +#define FRTC_EOI 0x18 +#define FRTC_VER 0x1C +#define FRTC_CDR_LOW 0x20 +#define FRTC_CCVR 0x24 +#define FRTC_CLR_LOW 0x28 +#define FRTC_CLR 0x2c + +#define FRTC_COUNTER_HB_OFFSET 15 +#define FRTC_COUNTER_LB_MASK 0x7fff +#define FRTC_COUNTER_DELAY 150 + +/***************** Macros (Inline Functions) Definitions *********************/ + +/** + * @name: RTC_READ_REG32 + * @msg: 读取RTC寄存器 + * @param {u32} addr 定时器的基地址 + * @param {u32} reg_offset 定时器的寄存器的偏移 + * @return {u32} 寄存器参数 + */ +#define FRTC_READ_REG32(addr, reg_offset) FtIn32((addr) + (u32)reg_offset) + +/** + * @name: RTC_READ_REG64 + * @msg: 读取RTC寄存器 + * @param {u32} addr 定时器的基地址 + * @param {u32} reg_offset 定时器的寄存器的偏移 + * @return {u64} 寄存器参数 + */ +#define FRTC_READ_REG64(addr, reg_offset) FtIn64((addr) + (u64)reg_offset) + +/** + * @name: RTC_WRITE_REG32 + * @msg: 写入RTC寄存器 + * @param {u32} addr 定时器的基地址 + * @param {u32} reg_offset 定时器的寄存器的偏移 + * @param {u32} reg_value 写入寄存器参数 + * @return {void} + */ +#define FRTC_WRITE_REG32(addr, reg_offset, reg_value) FtOut32((addr) + (u32)reg_offset, (u32)reg_value) + +#define FRTC_WRITE_AES_SEL(addr, regVal) FRTC_WRITE_REG32((addr), FRTC_AES_SEL, (regVal)) +#define FRTC_WRITE_CLR_LOW(addr, regVal) FRTC_WRITE_REG32((addr), FRTC_CLR_LOW, (u32)(regVal)) +#define FRTC_WRITE_CLR(addr, regVal) FRTC_WRITE_REG32((addr), FRTC_CLR, (u32)(regVal)) + +#define FRTC_READ_CDR_LOW(addr) FRTC_READ_REG32((addr), FRTC_CDR_LOW) +#define FRTC_READ_CCVR(addr) FRTC_READ_REG32((addr), FRTC_CCVR) + + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/rtc/frtc/frtc_intr.c b/bsp/phytium/libraries/standalone/drivers/rtc/frtc/frtc_intr.c new file mode 100644 index 0000000000..9b7803214b --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/rtc/frtc/frtc_intr.c @@ -0,0 +1,30 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: frtc_intr.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:02:07 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Wangxiaodong 2021/8/3 init + */ + +#include "fparameters.h" +#include "fassert.h" +#include "frtc.h" +#include "finterrupt.h" + + diff --git a/bsp/phytium/libraries/standalone/drivers/rtc/frtc/frtc_sinit.c b/bsp/phytium/libraries/standalone/drivers/rtc/frtc/frtc_sinit.c new file mode 100644 index 0000000000..7ded7bdbcc --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/rtc/frtc/frtc_sinit.c @@ -0,0 +1,51 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: frtc_sinit.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:02:24 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +/***************************** Include Files *********************************/ + +#include "frtc.h" + +extern const FRtcConfig FRtcConfigTbl; +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/*****************************************************************************/ +/** + * @name: RtcLookupConfig + * @msg: get rtc configs by id + * @return {*} + * @param {u32} instanceId, id of rtc ctrl + */ +const FRtcConfig *FRtcLookupConfig(void) +{ + const FRtcConfig *pconfig = NULL; + + pconfig = &FRtcConfigTbl; + + return (const FRtcConfig *)pconfig; +} diff --git a/bsp/phytium/libraries/standalone/drivers/sata/Kconfig b/bsp/phytium/libraries/standalone/drivers/sata/Kconfig new file mode 100644 index 0000000000..f2c51a66d4 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/sata/Kconfig @@ -0,0 +1,9 @@ +menu "FSATA Configuration" + config ENABLE_FSATA + bool + prompt "Use FSATA" + default n + +endmenu + + diff --git a/bsp/phytium/libraries/standalone/drivers/sata/fsata/fsata.c b/bsp/phytium/libraries/standalone/drivers/sata/fsata/fsata.c new file mode 100644 index 0000000000..c9fa80705b --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/sata/fsata/fsata.c @@ -0,0 +1,916 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsata.c + * Date: 2022-02-10 14:55:11 + * LastEditTime: 2022-02-18 09:05:09 + * Description:  This files is for sata ctrl function implementation + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include +#include +#include "ftypes.h" +#include "fassert.h" +#include "fcache.h" +#include "fdebug.h" +#include "fsleep.h" +#include "fswap.h" +#include "fsata.h" +#include "fsata_hw.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + +#define FSATA_DEBUG_TAG "SATA" +#define FSATA_ERROR(format, ...) FT_DEBUG_PRINT_E(FSATA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSATA_WARN(format, ...) FT_DEBUG_PRINT_W(FSATA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSATA_INFO(format, ...) FT_DEBUG_PRINT_I(FSATA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSATA_DEBUG(format, ...) FT_DEBUG_PRINT_D(FSATA_DEBUG_TAG, format, ##__VA_ARGS__) + +#ifndef MAX_SATA_BLOCKS_READ_WRITE + #define MAX_SATA_BLOCKS_READ_WRITE 0x80 +#endif + +/* Maximum timeouts for each event */ +#define WAIT_MS_RESET 1000 +#define WAIT_MS_TFD 20000 /* task file data transfer is not busy */ +#define WAIT_MS_DATAIO 20000 +#define WAIT_MS_LINKUP 200 + + +static FError FSataAhciDataIO(FSataCtrl *instance_p, u8 port, u8 *fis, + int fis_len, u8 *buf, int buf_len, boolean is_ncq, boolean is_write); + +/** + * @name: FSataAhciPortBase + * @msg: get port x base address + * @param {uintptr} base_address FSata base address + * @param {u32} port sata port number + * @return {uintptr} port x base address + */ +static uintptr FSataAhciPortBase(uintptr base_address, u32 port) +{ + return (base_address + 0x100 + (port * 0x80)); +} + +/** + * @name: FSataWaitCmdCompleted + * @msg: read register status and wait command execution is completed + * @param {uintptr} reg FSata register + * @param {int} timeout_msec wait timeout value + * @param {u32} sign register status flag bit + * @return {int} return 0 if command execute success, return -1 if command execute timeout + */ +static int FSataWaitCmdCompleted(uintptr reg, int timeout_msec, u32 sign) +{ + int i; + + for (i = 0; (FtIn32(reg)& sign) && (i < timeout_msec); i++) + { + fsleep_millisec(1); + } + + return (i < timeout_msec) ? 0 : -1; +} + +/** + * @name: FSataAhciLinkUp + * @msg: check sata ahci port link status + * @param {FSataCtrl} *instance_p, pointer to the FSataCtrl instance. + * @param {u8} port, port number + * @return {int} return FSATA_SUCCESS if successful, return others if failed + */ +static FError FSataAhciLinkUp(FSataCtrl *instance_p, u8 port) +{ + u32 reg_val; + int i = 0; + uintptr port_base_addr = instance_p->port[port].port_base_addr; + + /* + * Bring up SATA link. + * SATA link bringup time is usually less than 1 ms; only very + * rarely has it taken between 1-2 ms. Never seen it above 2 ms. + */ + while (i < WAIT_MS_LINKUP) + { + reg_val = FSATA_READ_REG32(port_base_addr, FSATA_PORT_SCR_STAT); + if ((reg_val & FSATA_PORT_SCR_STAT_DET_MASK) == FSATA_PORT_SCR_STAT_DET_PHYRDY) + return FSATA_SUCCESS; + fsleep_microsec(1000); + i++; + } + + return FSATA_ERR_TIMEOUT; +} + +/** + * @name: FSataAhciInquiry + * @msg: inquiry sata information + * @param {FSataCtrl} *instance_p, pointer to the FSataCtrl instance. + * @param {u8} port, port number + * @return {FError} return FSATA_SUCCESS if successful, return others if failed + */ +static FError FSataAhciInquiry(FSataCtrl *instance_p, u8 port) +{ + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + FError ret = FSATA_SUCCESS; + u16 *idbuf; + + /* 64位需要预留给内存池更大的空间 */ + static u16 tmpid[FSATA_ID_WORDS] __attribute__((aligned(128))) = {0}; + + u8 fis[20]; + /* Preset the FIS */ + memset(fis, 0, sizeof(fis)); + + fis[0] = FSATA_FIS_REG_HOST_TO_DEVICE; /* Register Host to Device FIS */ + fis[1] = FSATA_FIS_REG_HOST_TO_DEVICE_C; + fis[2] = FSATA_CMD_IDENTIFY_DEVICE; /* Command byte. */ + ret = FSataAhciDataIO(instance_p, port, fis, sizeof(fis), + (u8 *)tmpid, FSATA_ID_WORDS * 2, FALSE, FALSE); + if (ret != FSATA_SUCCESS) + { + FSATA_ERROR("FSataAhciInquiry: command failure. ret = %#x", ret); + return FSATA_ERR_OPERATION; + } + + instance_p->ataid[port] = tmpid; + + return FSATA_SUCCESS; +} + +/** + * @name: FSataIdentityCopy + * @msg: parse sata Identity information to vendor, product, revision + * @param {unsigned char } *dest, pointer to the destination address. + * @param {unsigned char } *src, pointer to the source address. + * @param {u32} len, parse length. + * @return {void} + */ +static void FSataIdentityCopy(unsigned char *dest, unsigned char *src, u32 len) +{ + FASSERT(dest != NULL); + FASSERT(src != NULL); + FASSERT(len != 0); + + u32 start, end; + + start = 0; + while (start < len) + { + if (src[start] != 0x20)/* character is not sapce */ + break; + start++; + } + end = len - 1; + while (end > start) + { + if (src[end] != 0x20)/* character is not sapce */ + break; + end--; + } + for (; start <= end; start++) + *dest ++ = src[start]; + *dest = '\0'; +} + +/** + * @name: FSataIdToSectors + * @msg: parse sata Identity information to capacity. + * @param {u16} *id, pointer to Identity information . + * @return {u64} capacity + */ +static u64 FSataIdToSectors(u16 *id) +{ + if (FSataIdHasLba(id)) + { + if (FSataIdHasLba48(id)) + return FSATA_ID_U64(id, FSATA_ID_LBA48_SECTORS); + else + return (u64)(FSATA_ID_U32(id, FSATA_ID_LBA_SECTORS)); + } + else + { + return 0; + } +} + +/** + * @name: FSataIdStrCopy + * @msg: swap sata Identity information + * @param {u16} *dest, pointer to the destination address. + * @param {u16} *src, pointer to the source address. + * @param {int} len, swap length. + * @return {void} + */ +static void FSataIdStrCopy(u16 *dest, u16 *src, int len) +{ + int i; + for (i = 0; i < len / 2; i++) + dest[i] = __swab16(src[i]); +} + +/** + * @name: FSataBlockToMB + * @msg: Converts the number of blocks in 512 byte to 0.1MB + * @param {unsigned long} block_count, block count. + * @param {unsigned long} mul_by, multiple value. + * @param {int} div_by, divided value. + * @return {unsigned long} convert value + */ +static unsigned long FSataBlockToMB(unsigned long block_count, unsigned long mul, int div) +{ + unsigned long bc_quot, bc_rem; + + /* x * m / d == x / d * m + (x % d) * m / d */ + bc_quot = (block_count >> div); /* upper > div bit */ + bc_rem = block_count - (bc_quot << div); /* low div bit */ + return (bc_quot * mul + ((bc_rem * mul) >> div)); +} + +/** + * @name: FSataInfoPrint + * @msg: printf sata information + * @param {FSataInfo} *dev_desc, pointer to the FSata information. + * @return {void} + */ +void FSataInfoPrint(FSataInfo *dev_desc) +{ + unsigned long lba512; /* number of blocks if 512bytes block size */ + + if (dev_desc->type == FSATA_DEV_TYPE_UNKNOWN) + { + FSATA_INFO("not available"); + return; + } + + if (dev_desc->if_type == FSATA_IF_TYPE_SCSI) + { + FSATA_INFO("Vendor: %s Prod: %s Rev: %s", + dev_desc->vendor, + dev_desc->product, + dev_desc->revision); + } + + if (dev_desc->type == FSATA_DEV_TYPE_HARDDISK) + { + FSATA_INFO("Type: Hard Disk"); + } + + if ((dev_desc->lba > 0L) && (dev_desc->blksz > 0L)) + { + unsigned long mb, mb_quot, mb_rem, gb, gb_quot, gb_rem; + unsigned long lba; + + lba = dev_desc->lba; + + lba512 = (lba * (dev_desc->blksz / 512)); + /* round to 1 digit */ + /* 2048 = (1024 * 1024) / 512 MB */ + mb = FSataBlockToMB(lba512, 10, 11); + + dev_desc->lba512 = lba512; + + FSATA_INFO("lba512=%lu, mb=%lu", lba512, mb); + + mb_quot = mb / 10; + mb_rem = mb - (10 * mb_quot); + + gb = mb / 1024; + gb_quot = gb / 10; + gb_rem = gb - (10 * gb_quot); + + FSATA_INFO("Capacity: %lu.%lu MB = %lu.%lu GB (%lu x %lu)", + mb_quot, mb_rem, + gb_quot, gb_rem, + (unsigned long)lba, + dev_desc->blksz); + } + else + { + FSATA_INFO("Capacity: not available"); + } +} + +/** + * @name: FSataAhciReadCapacity + * @msg: get sata capacity by parse instance_p ataid + * @param {FSataCtrl} *instance_p, pointer to the FSataCtrl instance. + * @param {u8} port, port number + * @param {unsigned long} *capacity, pointer to capacity value + * @param {unsigned long} *blksz, pointer to block size + * @return {FError} return FSATA_SUCCESS if successful, return others if failed + */ +static FError FSataAhciReadCapacity(FSataCtrl *instance_p, u8 port, + unsigned long *capacity, unsigned long *blksz) +{ + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + FError ret = FSATA_SUCCESS; + + u32 transfer_size; /* number of bytes per iteration */ + + if (!instance_p->ataid[port]) + { + FSATA_ERROR("READ CAPACITY10 command failure. " + "\tNo ATA info!\n" + "\tPlease run command INQUIRY first!"); + return FSATA_ERR_OPERATION; + } + + u64 cap64 = FSataIdToSectors(instance_p->ataid[port]); + if (cap64 > 0x100000000ULL) + cap64 = 0xffffffff; + + *capacity = (unsigned long)(cap64); + if (*capacity != 0xffffffff) + { + /* Read capacity (10) was sufficient for this drive. */ + *blksz = 512; + return FSATA_SUCCESS; + } + else + { + FSATA_DEBUG("should read capacity 16?"); + } + + return FSATA_SUCCESS; +} + +/** + * @name: FSataAhciReadInfo + * @msg: get sata information + * @param {FSataCtrl} *instance_p, pointer to the FSataCtrl instance. + * @param {u8} port, port number + * @return {FError} return FSATA_SUCCESS if read successful, return others if read failed + */ +FError FSataAhciReadInfo(FSataCtrl *instance_p, u8 port) +{ + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + uintptr base_addr = instance_p->config.base_addr; + FError ret = FSATA_SUCCESS; + + u16 *idbuf; + unsigned long capacity, blksz; + unsigned char info_data[50]; + memset(info_data, 0, sizeof(info_data)); + + ret = FSataAhciInquiry(instance_p, port); + if (ret != FSATA_SUCCESS) + { + FSATA_ERROR("FSataAhciInquiry!"); + return FSATA_ERR_OPERATION; + } + + /* Parse SATA Information */ + idbuf = instance_p->ataid[port]; + FSataIdStrCopy((u16 *)&info_data[16], &idbuf[FSATA_ID_PROD], 16); + FSataIdStrCopy((u16 *)&info_data[32], &idbuf[FSATA_ID_FW_REV], 4); + + /* is ata device */ + if (!((__swab16(idbuf[0]) & FSATA_ID_ATA_DEVICE))) + { + instance_p->port[port].dev_info.type = FSATA_DEV_TYPE_HARDDISK; + memcpy(&info_data[8], "ATA ", 8); /* copy 8 bytes */ + } + else + { + instance_p->port[port].dev_info.type = FSATA_DEV_TYPE_UNKNOWN; + } + + /* get info for this device */ + FSataIdentityCopy((unsigned char *)instance_p->port[port].dev_info.vendor, &info_data[8], 8); + FSataIdentityCopy((unsigned char *)instance_p->port[port].dev_info.product, &info_data[16], 16); + FSataIdentityCopy((unsigned char *)instance_p->port[port].dev_info.revision, &info_data[32], 4); + + /* get sata capacity by parse ataid */ + ret = FSataAhciReadCapacity(instance_p, port, &capacity, &blksz); + if (ret != FSATA_SUCCESS) + { + FSATA_ERROR("FSataAhciReadCapacity error!"); + return FSATA_ERR_OPERATION; + } + + instance_p->port[port].dev_info.lba = capacity; + instance_p->port[port].dev_info.blksz = blksz; + instance_p->port[port].dev_info.if_type = FSATA_IF_TYPE_SCSI; + + FSataInfoPrint(&(instance_p->port[port].dev_info)); + + return ret; +} + +/** + * @name: FSataAhciReset + * @msg: reset ahci / hba(host bus adapter) + * @param {FSataCtrl} *instance_p, pointer to the FSataCtrl instance + * @return {FError} return FSATA_SUCCESS if successful, return others if failed + */ +static FError FSataAhciReset(FSataCtrl *instance_p) +{ + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + int i = WAIT_MS_RESET; + uintptr base_addr = instance_p->config.base_addr; + + u32 reg_val = 0; + + /* read host control register and reset */ + reg_val = FSATA_READ_REG32(base_addr, FSATA_HOST_CTL); + + if ((reg_val & FSATA_HOST_RESET) == 0) + { + FSATA_SETBIT(base_addr, FSATA_HOST_CTL, FSATA_HOST_RESET); + } + + /* reset must complete within 1 millisecond, or the hardware should be considered fried.*/ + do + { + fsleep_microsec(1000); + reg_val = FSATA_READ_REG32(base_addr, FSATA_HOST_CTL); + i--; + } + while ((i > 0) && (reg_val & FSATA_HOST_RESET)); + + if (i == 0) + { + FSATA_ERROR("controller reset failed (0x%x)", reg_val); + return FSATA_ERR_TIMEOUT; + } + + return FSATA_SUCCESS; +} + +/** + * @name: FSataAhciInit + * @msg: init ahci / hba(host bus adapter) + * @param {FSataCtrl} *instance_p, pointer to the FSataCtrl instance + * @return {FError} return FSATA_SUCCESS if successful, return others if failed + */ +FError FSataAhciInit(FSataCtrl *instance_p) +{ + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + uintptr base_addr = instance_p->config.base_addr; + FError ret = FSATA_SUCCESS; + u32 i; + uintptr port_base_addr = 0; /* ahci port x base address */ + u32 reg_val = 0; + u32 port_num = 0; + + /* reset host control */ + ret = FSataAhciReset(instance_p); + if (ret != FSATA_SUCCESS) + return ret; + + /* ahci enable */ + FSATA_WRITE_REG32(base_addr, FSATA_HOST_CTL, FSATA_HOST_AHCI_EN); + FSATA_READ_REG32(base_addr, FSATA_HOST_CTL); + + /* read cap.np, set the ports bit which are available for software to use. */ + port_num = FSATA_READ_REG32(base_addr, FSATA_HOST_CAP) & FSATA_HOST_CAP_NP_MASK; + FSATA_WRITE_REG32(base_addr, FSATA_HOST_PORTS_IMPL, FSATA_HOST_PORTS_IMPL_MASK(port_num)); + + /* set instance_p paramameters */ + instance_p->n_ports = port_num + 1; + instance_p->port_map = FSATA_READ_REG32(base_addr, FSATA_HOST_PORTS_IMPL); + FSATA_DEBUG("port_map 0x%x n_ports %d", instance_p->port_map, instance_p->n_ports); + + for (i = 0; i < instance_p->n_ports; i++) + { + if (!(instance_p->port_map & BIT(i))) + continue; + /* set ports base address */ + instance_p->port[i].port_base_addr = FSataAhciPortBase(base_addr, i); + port_base_addr = instance_p->port[i].port_base_addr; + + /* make sure port is not active */ + reg_val = FSATA_READ_REG32(port_base_addr, FSATA_PORT_CMD); + if (reg_val & (FSATA_PORT_CMD_LIST_ON | FSATA_PORT_CMD_FIS_ON | + FSATA_PORT_CMD_FIS_RX | FSATA_PORT_CMD_START)) + { + FSATA_DEBUG("Port %d is active, reg = %#x. Deactivating.", i, reg_val); + reg_val &= ~(FSATA_PORT_CMD_LIST_ON | FSATA_PORT_CMD_FIS_ON | + FSATA_PORT_CMD_FIS_RX | FSATA_PORT_CMD_START); + FSATA_WRITE_REG32(port_base_addr, FSATA_PORT_CMD, reg_val); + + /* spec says 500 msecs for each bit, so this is slightly incorrect.*/ + fsleep_millisec(500); + } + + /* Add the spinup command to whatever mode bits may + * already be on in the command register, set not support staggered spin-up */ + reg_val = FSATA_READ_REG32(port_base_addr, FSATA_PORT_CMD); + reg_val |= FSATA_PORT_CMD_SPIN_UP; + FSATA_WRITE_REG32(port_base_addr, FSATA_PORT_CMD, reg_val); + + /* check sata port is linked */ + ret = FSataAhciLinkUp(instance_p, i); + if (ret) + { + FSATA_DEBUG("sata host %d, port %d link timeout", instance_p->config.instance_id, i); + continue; + } + else + { + FSATA_DEBUG("sata host %d , port %d link ok.", instance_p->config.instance_id, i) ; + } + + /* Clear error status */ + reg_val = FSATA_READ_REG32(port_base_addr, FSATA_PORT_SCR_ERR); + if (reg_val) + FSATA_WRITE_REG32(port_base_addr, FSATA_PORT_SCR_ERR, reg_val); + + /* Device presence detected but Phy communication not established, retry once more */ + reg_val = FSATA_READ_REG32(port_base_addr, FSATA_PORT_SCR_STAT) & FSATA_PORT_SCR_STAT_DET_MASK; + if (reg_val == FSATA_PORT_SCR_STAT_DET_COMINIT) + { + FSATA_INFO("sata link %d down, retrying...", i); + i--; + continue; + } + + /* Clear error status */ + reg_val = FSATA_READ_REG32(port_base_addr, FSATA_PORT_SCR_ERR); + if (reg_val) + FSATA_WRITE_REG32(port_base_addr, FSATA_PORT_SCR_ERR, reg_val); + + /* clear port irq status */ + reg_val = FSATA_READ_REG32(port_base_addr, FSATA_PORT_IRQ_STAT); + if (reg_val) + FSATA_WRITE_REG32(port_base_addr, FSATA_PORT_IRQ_STAT, reg_val); + + /* clear host corresponding port interrupt status register */ + FSATA_WRITE_REG32(base_addr, FSATA_HOST_IRQ_STAT, BIT(i)); + + /* register linkup ports */ + reg_val = FSATA_READ_REG32(port_base_addr, FSATA_PORT_SCR_STAT); + if ((reg_val & FSATA_PORT_SCR_STAT_DET_MASK) == FSATA_PORT_SCR_STAT_DET_PHYRDY) + instance_p->link_port_map |= BIT(i); + } + + /* host interrupt enable */ + reg_val = FSATA_READ_REG32(base_addr, FSATA_HOST_CTL); + FSATA_WRITE_REG32(base_addr, FSATA_HOST_CTL, reg_val | FSATA_HOST_IRQ_EN); + + return FSATA_SUCCESS; +} + +/** + * @name: FSataAhciPortStart + * @msg: init ahci port, allocate Port Memory Usage + * @param {FSataCtrl} *instance_p, pointer to the FSataCtrl instance + * @param {u8} port, port number + * @param {uintptr} mem, Memory start address allocated to port + * @return {FError} return FSATA_SUCCESS if successful, return others if failed + */ +FError FSataAhciPortStart(FSataCtrl *instance_p, u8 port, uintptr mem) +{ + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + FASSERT(mem); + uintptr base_addr = instance_p->config.base_addr; + + FError ret = FSATA_SUCCESS; + FSataAhciPorts *port_info = &(instance_p->port[port]); + uintptr port_base_addr = port_info->port_base_addr; + + u32 reg_val = FSATA_READ_REG32(port_base_addr, FSATA_PORT_SCR_STAT); + if ((reg_val & FSATA_PORT_SCR_STAT_DET_MASK) != FSATA_PORT_SCR_STAT_DET_PHYRDY) + { + FSATA_ERROR("No Link on host %d port %d!", instance_p->config.instance_id, port); + return FSATA_ERR_OPERATION; + } + + memset((void *)mem, 0, FSATA_AHCI_PORT_PRIV_DMA_SZ); + + /* First item in chunk of DMA memory: 32 command lists, 32 bytes each in size */ + port_info->cmd_list = (FSataAhciCommandList *)(mem); + mem += FSATA_AHCI_CMD_LIST_HEADER_SIZE * FSATA_AHCI_CMD_LIST_HEADER_NUM; + + /* Second item: Received-FIS area */ + port_info->rx_fis = (FSataAhciRecvFis *)(mem); + mem += FSATA_AHCI_RX_FIS_SZ; + + /* Third item: data area for storing a single command and its scatter-gather table */ + port_info->cmd_tbl_base_addr = (uintptr)mem; + mem += FSATA_AHCI_CMD_TABLE_HEADER_SIZE; + + /* command table prdt */ + port_info->cmd_tbl_prdt = (FSataAhciCommandTablePrdt *)mem; + + /* set ahci port registers */ + FSATA_WRITE_REG32(port_base_addr, FSATA_PORT_LST_ADDR, + (uintptr)port_info->cmd_list & FSATA_PORT_CMD_LIST_ADDR_MASK); + + FSATA_WRITE_REG32(port_base_addr, FSATA_PORT_LST_ADDR_HI, 0); + + FSATA_WRITE_REG32(port_base_addr, FSATA_PORT_FIS_ADDR, + (uintptr)port_info->rx_fis & FSATA_PORT_CMD_FIS_ADDR_MASK); + + FSATA_WRITE_REG32(port_base_addr, FSATA_PORT_FIS_ADDR_HI, 0); + + FSATA_WRITE_REG32(port_base_addr, FSATA_PORT_CMD, FSATA_PORT_CMD_ICC_ACTIVE | FSATA_PORT_CMD_FIS_RX | + FSATA_PORT_CMD_POWER_ON | FSATA_PORT_CMD_SPIN_UP | FSATA_PORT_CMD_START); + + /* + * Make sure interface is not busy based on error and status + * information from task file data register before proceeding + */ + if (FSataWaitCmdCompleted(port_base_addr + FSATA_PORT_TFDATA, WAIT_MS_TFD, FSATA_BUSY)) + { + FSATA_DEBUG("timeout exit!"); + return FSATA_ERR_TIMEOUT; + } + + instance_p->private_data |= BIT(port); + + return ret; +} + +/** + * @name: FSataAhciFillCmdTablePrdt + * @msg: allocate ahci command table prdt information + * @param {FSataCtrl} *instance_p, pointer to the FSataCtrl instance + * @param {u8} port, port number + * @param {unsigned char} *buffer, data buffer address + * @param {int} buf_len, data length + * @return {int} return item_count if successful, return -1 if failed + */ +static int FSataAhciFillCmdTablePrdt(FSataCtrl *instance_p, u8 port, + unsigned char *buf, int buf_len) +{ + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + + FSataAhciPorts *port_info = &(instance_p->port[port]); + FSataAhciCommandTablePrdt *command_table_prdt = port_info->cmd_tbl_prdt; + int item_count; + int i; + + item_count = ((buf_len - 1) / MAX_DATA_BYTE_COUNT) + 1; + if (item_count > FSATA_AHCI_PRTD_ITEM_NUM) + { + FSATA_ERROR("Too much command table prdt items %d!", item_count); + return -1; + } + + for (i = 0; i < item_count; i++) + { + command_table_prdt->addr_low = ((unsigned long) buf + i * MAX_DATA_BYTE_COUNT); + command_table_prdt->addr_high = 0; + command_table_prdt->data_byte = (0x3fffff & + (buf_len < MAX_DATA_BYTE_COUNT + ? (buf_len - 1) + : (MAX_DATA_BYTE_COUNT - 1))); + command_table_prdt++; + buf_len -= MAX_DATA_BYTE_COUNT; + } + + return item_count; +} + +/** + * @name: FSataAhciFillCmdList + * @msg: allocate ahci command list information + * @param {FSataAhciPorts} *port_info is a pointer to the FSataAhciPorts instance + * @param {u32} description_info, prdtl+flag+cfl + * @return {void} + */ +static void FSataAhciFillCmdList(FSataAhciPorts *port_info, u32 description_info) +{ + FASSERT(port_info != NULL); + + port_info->cmd_list->description_info = description_info; + port_info->cmd_list->status = 0; + port_info->cmd_list->tbl_addr = ((u32)port_info->cmd_tbl_base_addr & FSATA_PORT_CMD_TABLE_ADDR_MASK); +#ifdef __aarch64__ + port_info->cmd_list->tbl_addr_hi = (u32)(((port_info->cmd_tbl_base_addr) >> 16) >> 16); +#endif +} + +/** + * @name: FSataAhciDataIO + * @msg: transfer ahci command fis and data buffer + * @param {FSataCtrl} *instance_p, pointer to the FSataCtrl instance + * @param {u8} port number + * @param {u8} *fis, command fis buffer + * @param {int} fis_len, command fis length + * @param {u8} *buf, data read/write buffer + * @param {int} buf_len, data length + * @param {u8} is_write, 0-read, 1-write + * @return {FError} return FSATA_SUCCESS if successful, return others if failed + */ +static FError FSataAhciDataIO(FSataCtrl *instance_p, u8 port, u8 *fis, + int fis_len, u8 *buf, int buf_len, boolean is_ncq, boolean is_write) +{ + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + FASSERT(fis != NULL); + + FSataAhciPorts *port_info = &(instance_p->port[port]); + + uintptr base_addr = instance_p->config.base_addr; + uintptr port_base_addr = instance_p->port[port].port_base_addr; + + if (port >= instance_p->n_ports) + { + FSATA_DEBUG("Invalid port number %d", port); + return FSATA_ERR_INVAILD_PARAMETER; + } + + u32 reg_val = FSATA_READ_REG32(port_base_addr, FSATA_PORT_SCR_STAT); + if ((reg_val & FSATA_PORT_SCR_STAT_DET_MASK) != FSATA_PORT_SCR_STAT_DET_PHYRDY) + { + FSATA_ERROR("No Link on host %d port %d!", instance_p->config.instance_id, port); + return FSATA_ERR_OPERATION; + } + + /* copy fis command to command table CFIS */ + memcpy((unsigned char *)port_info->cmd_tbl_base_addr, fis, fis_len); + + /* copy data buffer address to command table prdt item */ + int prdt_length = FSataAhciFillCmdTablePrdt(instance_p, port, buf, buf_len); + if (prdt_length == -1) + { + FSATA_ERROR("FSataAhciFillCmdTablePrdt failed, buf_len = %d\n", buf_len); + return FSATA_ERR_INVAILD_PARAMETER; + } + + /* command list DW0: PRDTL(buf len) + W/R + CFL(fis len, 4 Byte(Dword) aligned) */ + u32 description_info = (prdt_length << 16) | (is_write << 6) | (fis_len >> 2); + + /* copy data to command list struct */ + FSataAhciFillCmdList(port_info, description_info); + + FCacheDCacheFlushRange((unsigned long)port_info->cmd_list, FSATA_AHCI_PORT_PRIV_DMA_SZ); + FCacheDCacheFlushRange((unsigned long)buf, (unsigned long)buf_len); + + /* set tag bit in SACT register before write CI register when use native cmd */ + if (is_ncq == TRUE) + FSATA_WRITE_REG32(port_base_addr, FSATA_PORT_SCR_ACT, FSATA_PORT_SCR_ACT_ENABLE); + + /* send cmd */ + FSATA_WRITE_REG32(port_base_addr, FSATA_PORT_CMD_ISSUE, FSATA_PORT_CMD_ISSUE_ENABLE); + + if (FSataWaitCmdCompleted(port_base_addr + FSATA_PORT_CMD_ISSUE, WAIT_MS_DATAIO, FSATA_PORT_CMD_ISSUE_ENABLE)) + { + FSATA_ERROR("timeout exit!"); + return FSATA_ERR_TIMEOUT; + } + + return FSATA_SUCCESS; +} + +/** + * @name: FSataReadWrite + * @msg: read or write sata block data, choose if use ncq + * @param {FSataCtrl} *instance_p, pointer to the FSataCtrl instance + * @param {u8} port, port number + * @param {u32} start, start block + * @param {u32} blk_cnt, block count + * @param {u8} *buffer, data buffer + * @param {boolean} is_ncq, FALSE-not support ncq, TRUE-support ncq + * @param {boolean} is_write, read or write, FALSE-read, TRUE-write + * @return {FError} return FSATA_SUCCESS if successful, return others if failed + */ +FError FSataReadWrite(FSataCtrl *instance_p, u8 port, u32 start, + u16 blk_cnt, u8 *buffer, boolean is_ncq, boolean is_write) +{ + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + FASSERT(blk_cnt); + uintptr base_addr = instance_p->config.base_addr; + FError ret = FSATA_SUCCESS; + + u16 now_blocks; /* number of blocks per iteration */ + u32 transfer_size; /* number of bytes per iteration */ + + u8 fis[20]; + /* Preset the FIS */ + memset(fis, 0, sizeof(fis)); + + fis[0] = FSATA_FIS_REG_HOST_TO_DEVICE;/* fis type */ + fis[1] = FSATA_FIS_REG_HOST_TO_DEVICE_C; /* C and PM Port */ + if (is_ncq == FALSE) + fis[2] = is_write ? FSATA_CMD_WRITE_EXT : FSATA_CMD_READ_EXT; /* Command */ + else + fis[2] = is_write ? FSATA_CMD_FPDMA_WRITE : FSATA_CMD_FPDMA_READ; /* Command */ + + while (blk_cnt) + { + now_blocks = min((u16)MAX_SATA_BLOCKS_READ_WRITE, blk_cnt); + transfer_size = FSATA_SECT_SIZE * now_blocks; + if (is_ncq == FALSE) + { + /* FEATURE Reserved */ + fis[3] = 0; /* features 7:0 */ + fis[11] = 0; /* features 15:8 */ + + /* LBA of first logical sector to be transferred */ + fis[4] = ((start >> 0) & 0xff); /* lba 7:0 */ + fis[5] = ((start >> 8) & 0xff); /* lba 15:8 */ + fis[6] = ((start >> 16) & 0xff); /* lba 23:16 */ + fis[8] = ((start >> 24) & 0xff); /* lba 31:24 */ + + /* device reg, bit 6 Shall be set to one in read command */ + fis[7] = FSATA_CMD_EXT_DEVICE; + + /* The number of logical sectors to be transferred. */ + fis[12] = (now_blocks >> 0) & 0xff;/* count 7:0 */ + fis[13] = (now_blocks >> 8) & 0xff;/* count 15:8 */ + } + else + { + /* FEATURE:The number of logical sectors to be transferred. */ + fis[3] = (now_blocks >> 0) & 0xff; /* features 7:0 */ + fis[11] = (now_blocks >> 8) & 0xff; /* features 15:8 */ + + /* LBA of first logical sector to be transferred */ + fis[4] = ((start >> 0) & 0xff); /* lba 7:0 */ + fis[5] = ((start >> 8) & 0xff); /* lba 15:8 */ + fis[6] = ((start >> 16) & 0xff); /* lba 23:16 */ + fis[8] = ((start >> 24) & 0xff); /* lba 31:24 */ + + /* device reg, bit 6 Shall be set to one */ + fis[7] = FSATA_CMD_EXT_DEVICE; + + /* count */ + fis[12] = 0;/* count 7:0, NCQ TAG field */ + fis[13] = 0;/* count 15:8, Normal priority */ + } + + ret = FSataAhciDataIO(instance_p, port, fis, sizeof(fis), + buffer, transfer_size, is_ncq, is_write); + if (ret) + { + FSATA_ERROR("scsi_ahci: SCSI command failure. ret = %#x", ret); + return FSATA_ERR_OPERATION; + } + + buffer += transfer_size; + blk_cnt -= now_blocks; + start += now_blocks; + } + return ret; +} + +/** + * @name: FSataCfgInitialize + * @msg: Initialize Sata ctrl + * @param {FSataCtrl} *instance_p, pointer to the FSataCtrl instance. + * @param {FSataConfig} *input_config_p, Default configuration parameters of FSata + * @return {FError} return FSATA_SUCCESS if successful, return others if failed + */ +FError FSataCfgInitialize(FSataCtrl *instance_p, const FSataConfig *input_config_p) +{ + FASSERT(instance_p); + + /*Set default values and configuration data */ + FSataCfgDeInitialize(instance_p); + + instance_p->config = *input_config_p; + + instance_p->is_ready = FT_COMPONENT_IS_READY; + + return FSATA_SUCCESS; +} + +/** + * @name: FSataCfgDeInitialize + * @msg: DeInitialization function for the device instance + * @param {FSataCtrl} *instance_p, pointer to the FSataCtrl instance. + * @return {*} + */ +void FSataCfgDeInitialize(FSataCtrl *pctrl) +{ + FASSERT(pctrl); + + pctrl->is_ready = 0; + memset(pctrl, 0, sizeof(*pctrl)); + + return; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/sata/fsata/fsata.h b/bsp/phytium/libraries/standalone/drivers/sata/fsata/fsata.h new file mode 100644 index 0000000000..d901b34485 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/sata/fsata/fsata.h @@ -0,0 +1,277 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsata.h + * Date: 2022-02-10 14:55:11 + * LastEditTime: 2022-02-18 09:05:24 + * Description:  This files is for sata ctrl function definition + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef BSP_DRIVERS_FSATA_H +#define BSP_DRIVERS_FSATA_H + +#ifdef __cplusplus +extern "C" +{ +#endif +/***************************** Include Files *********************************/ +#include "ftypes.h" +#include "ferror_code.h" +#include "fkernel.h" + +#define FSATA_SUCCESS FT_SUCCESS +#define FSATA_ERR_INVAILD_PARAMETER FT_MAKE_ERRCODE(ErrModBsp, ErrBspSata, 1) +#define FSATA_ERR_TIMEOUT FT_MAKE_ERRCODE(ErrModBsp, ErrBspSata, 2) +#define FSATA_ERR_OPERATION FT_MAKE_ERRCODE(ErrModBsp, ErrBspSata, 3) +#define FSATA_UNKNOWN_DEVICE FT_MAKE_ERRCODE(ErrModBsp, ErrBspSata, 4) + +/************************** Constant Definitions *****************************/ +#define FSATA_AHCI_MAX_PORTS 32 +#define FSATA_AHCI_MAX_PRD_ENTRIES 16 + +#define MAX_DATA_BYTE_COUNT SZ_4M + +#define FSATA_AHCI_CMD_LIST_HEADER_SIZE 0x20 +#define FSATA_AHCI_CMD_LIST_HEADER_NUM 32 +#define FSATA_AHCI_RX_FIS_SZ 0x100 +#define FSATA_AHCI_CMD_TABLE_HEADER_SIZE 0x80 +#define FSATA_AHCI_PRTD_ITEM_SIZE 0x10 +#define FSATA_AHCI_PRTD_ITEM_NUM 0x40 /*set 64 item, hardware max is 64K */ + +#define FSATA_AHCI_CMD_TABLE_SIZE (FSATA_AHCI_CMD_TABLE_HEADER_SIZE + (FSATA_AHCI_PRTD_ITEM_NUM * FSATA_AHCI_PRTD_ITEM_SIZE)) +#define FSATA_AHCI_PORT_PRIV_DMA_SZ (FSATA_AHCI_CMD_LIST_HEADER_SIZE * FSATA_AHCI_CMD_LIST_HEADER_NUM + \ + FSATA_AHCI_CMD_TABLE_SIZE + FSATA_AHCI_RX_FIS_SZ) + +#define FSATA_AHCI_CMD_ATAPI BIT(5) +#define FSATA_AHCI_CMD_WRITE BIT(6) +#define FSATA_AHCI_CMD_PREFETCH BIT(7) +#define FSATA_AHCI_CMD_RESET BIT(8) +#define FSATA_AHCI_CMD_CLR_BUSY BIT(10) + +#define FSATA_ID_LBA48_SECTORS 100 +#define FSATA_ID_LBA_SECTORS 60 + +#define FSATA_ID_ATA_DEVICE BIT(15) /* IDENTIFY DEVICE word 0, if ATA device */ +#define FSATA_ID_COMPLETE BIT(2) /* IDENTIFY DEVICE word 0, if the content of the IDENTIFY DEVICE data is incomplete */ + +#define FSATA_ID_FW_REV 23 /* firmware revision position */ +#define FSATA_ID_PROD 27 /* Model number position */ +#define FSATA_ID_WORDS 256 /* IDENTIFY DEVICE data length */ + +enum +{ + FSATA_FIS_REG_HOST_TO_DEVICE = 0x27, + FSATA_FIS_REG_DEVICE_TO_HOST = 0x34, + FSATA_FIS_DMA_SETUP = 0x41 +}; + +#define FSATA_FIS_REG_HOST_TO_DEVICE_C BIT(7) /* update of the command register */ +#define FSATA_CMD_EXT_DEVICE BIT(6) /* command device byte requirement */ + +enum +{ + FSATA_CMD_READ_EXT = 0x25, + FSATA_CMD_WRITE_EXT = 0x35, + FSATA_CMD_IDENTIFY_DEVICE = 0xEC, + FSATA_CMD_FPDMA_READ = 0x60, + FSATA_CMD_FPDMA_WRITE = 0x61 +}; + +#define FSATA_BUSY BIT(7) /* BSY status bit */ + +#define FSATA_SECT_SIZE 512 /* sata sector size */ + +#define FSATA_BLK_VEN_SIZE 40 /* device vendor string size */ +#define FSATA_BLK_PRD_SIZE 20 /* device product number size */ +#define FSATA_BLK_REV_SIZE 8 /* firmware revision size */ + +#define FSATA_DEV_TYPE_UNKNOWN 0xff /* not connected */ +#define FSATA_DEV_TYPE_HARDDISK 0x00 /* harddisk */ + +#define FSATA_IF_TYPE_UNKNOWN 0xff +#define FSATA_IF_TYPE_SCSI 0x00 + +enum +{ + FSATA_TYPE_PCIE = 0, + FSATA_TYPE_CONTROLLER = 1 +}; + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/* Number of User Addressable Logical Sectors lba28 */ +#define FSATA_ID_U32(id,n) \ + (((u32) (id)[(n) + 1] << 16) | ((u32) (id)[(n)])) + +/* Number of User Addressable Logical Sectors lba48 */ +#define FSATA_ID_U64(id,n) \ + ( ((u64) (id)[(n) + 3] << 48) | \ + ((u64) (id)[(n) + 2] << 32) | \ + ((u64) (id)[(n) + 1] << 16) | \ + ((u64) (id)[(n) + 0]) ) + +/* if sata id is support lba */ +#define FSataIdHasLba(id) ((id)[49] & BIT(9)) + +/* if sata id is support lba48 */ +static inline int FSataIdHasLba48(const u16 *id) +{ + if ((id[83] & 0xC000) != 0x4000) + return 0; + if (!FSATA_ID_U64(id, 100)) + return 0; + return id[83] & BIT(10); +} + +/**************************** Type Definitions *******************************/ +typedef void (*FSataIrqCallBack)(void *args); + +/* sata info */ +typedef struct +{ + unsigned char if_type; /* type of the interface */ + unsigned char type; /* device type */ + char vendor[FSATA_BLK_VEN_SIZE + 1]; /* device vendor string */ + char product[FSATA_BLK_PRD_SIZE + 1]; /* device product number */ + char revision[FSATA_BLK_REV_SIZE + 1]; /* firmware revision */ + unsigned long lba; /* number of blocks */ + unsigned long lba512; /* number of blocks of 512 bytes */ + unsigned long blksz; /* block size */ +} FSataInfo; + +/* Received FIS Structure */ +typedef struct __attribute__((__packed__)) +{ + u8 dma_setup_fis[28]; + u8 reserved0[4]; + u8 pio_setup_fis[20]; + u8 reserved1[12]; + u8 d2h_register_fis[20]; + u8 reserved2[4]; + u8 set_device_bits_fis[8]; + u8 unknown_fis[64]; + u8 reserved3[96]; +} +FSataAhciRecvFis; + +/* command list structure - command header */ +typedef struct +{ + u32 description_info;/* DW 0 – Description Information */ + u32 status; /* DW 1 - Command Status */ + u32 tbl_addr; /* DW 2 – Command Table Base Address */ + u32 tbl_addr_hi; /* DW 3 – Command Table Base Address Upper */ + u32 reserved[4]; +} FSataAhciCommandList; + +/* command table - PRDT */ +typedef struct +{ + u32 addr_low; /* DW 0 – Data Base Address */ + u32 addr_high; /* DW 1 – Data Base Address Upper */ + u32 reserved; /* DW 2 – Reserved */ + u32 data_byte; /* DW 3 – Description Information */ +} FSataAhciCommandTablePrdt; + +/* ahci port information structure */ +typedef struct +{ + uintptr port_base_addr; /* port base address */ + FSataAhciCommandList *cmd_list; /* Command List structure, will include cmd_tbl's address */ + FSataAhciRecvFis *rx_fis; /* Received FIS Structure */ + uintptr cmd_tbl_base_addr; /* command table addr, also the command table's first part */ + FSataAhciCommandTablePrdt *cmd_tbl_prdt;/* command table's second part , cmd_tbl + cmd_tbl_prdt = command table*/ + FSataInfo dev_info; +} FSataAhciPorts; + +typedef struct +{ + u32 instance_id; /* Device instance id */ + uintptr base_addr; /* sata base address */ + char *instance_name; /* instance name */ + u32 irq_num; /* Irq number */ +} FSataConfig; /* sata config */ + +typedef struct +{ + FSataConfig config; + u32 is_ready; + + FSataAhciPorts port[FSATA_AHCI_MAX_PORTS]; + u16 *ataid[FSATA_AHCI_MAX_PORTS]; + u32 n_ports; /* maximum number of ports supported by the ahci, Number of Ports (NP)*/ + u32 port_map; /* each bit indicate port can be used, If a bit is set to ‘1’, the corresponding port is available for software to use. */ + u32 link_port_map; /* each bit indicate port linkup sata device */ + u32 private_data; /* each bit indicate port sata achi started */ + + FSataIrqCallBack fsata_dhrs_cb; /* device-to-host register fis interrupt */ + void *dhrs_args; + FSataIrqCallBack fsata_pss_cb; /* pio setup fis interrupt */ + void *pss_args; + FSataIrqCallBack fsata_dss_cb; /* dma setup fis interrupt */ + void *dss_args; + FSataIrqCallBack fsata_sdbs_cb; /* set device bits interrupt */ + void *sdbs_args; + FSataIrqCallBack fsata_pcs_cb; /* port connect change status interrupt */ + void *pcs_args; + + volatile u8 dhrs_flag; + volatile u8 sdb_flag; +} FSataCtrl; + + +/************************** Function Prototypes ******************************/ + +/* sata config init */ +const FSataConfig *FSataLookupConfig(u32 instance_id, u8 type); + +/* initialize sata ctrl */ +FError FSataCfgInitialize(FSataCtrl *instance_p, const FSataConfig *input_config_p); + +/* deinitialize sata ctrl */ +void FSataCfgDeInitialize(FSataCtrl *pctrl); + +/* read sata info */ +FError FSataAhciReadInfo(FSataCtrl *instance_p, u8 port); + +/* init ahci */ +FError FSataAhciInit(FSataCtrl *instance_p); + +/* init ahci port */ +FError FSataAhciPortStart(FSataCtrl *instance_p, u8 port, uintptr mem); + +/* read or write sata data */ +FError FSataReadWrite(FSataCtrl *instance_p, u8 port, u32 start, + u16 blk_cnt, u8 *buffer, boolean is_ncq, boolean is_write); + +/* sata all irq handler entry */ +void FSataIrqHandler(s32 vector, void *param); + +/* set specific sata irq function entry */ +FError FSataSetHandler(FSataCtrl *instance_p, u32 handler_type, + void *func_pointer, void *call_back_ref); + +/* set sata irq mask */ +void FSataIrqEnable(FSataCtrl *instance_p, u32 int_mask); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/sata/fsata/fsata_g.c b/bsp/phytium/libraries/standalone/drivers/sata/fsata/fsata_g.c new file mode 100644 index 0000000000..dc90afa636 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/sata/fsata/fsata_g.c @@ -0,0 +1,103 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsata_g.c + * Date: 2022-02-10 14:55:11 + * LastEditTime: 2022-02-18 09:03:08 + * Description:  This files is for static config of sata ctrl + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "fparameters.h" +#include "fsata.h" +#include "sdkconfig.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + + +/* configs of pcie ahci ctrl */ +const FSataConfig FSataPcieConfigTbl[PLAT_AHCI_HOST_MAX_COUNT] = +{ + [0] = + { + .base_addr = AHCI_BASE_0, + .instance_name = "sata0", + .irq_num = AHCI_IRQ_0 /* Irq number */ + + }, + [1] = + { + .base_addr = AHCI_BASE_1, + .instance_name = "sata1", + .irq_num = AHCI_IRQ_1 /* Irq number */ + + }, + [2] = + { + .base_addr = AHCI_BASE_2, + .instance_name = "sata2", + .irq_num = AHCI_IRQ_2 /* Irq number */ + + }, + [3] = + { + .base_addr = AHCI_BASE_3, + .instance_name = "sata3", + .irq_num = AHCI_IRQ_3 /* Irq number */ + + }, + [4] = + { + .base_addr = AHCI_BASE_4, + .instance_name = "sata4", + .irq_num = AHCI_IRQ_4 /* Irq number */ + + }, +}; + +#if defined(CONFIG_TARGET_E2000) + +/* configs of controller ahci ctrl */ +const FSataConfig FSataControllerConfigTbl[FSATA_INSTANCE_NUM] = +{ + [0] = + { + .instance_id = FSATA_INSTANCE_0, + .base_addr = FSATA0_BASEADDR, + .instance_name = "sata0", + .irq_num = FSATA0_IRQNUM /* Irq number */ + + }, + [1] = + { + .instance_id = FSATA_INSTANCE_1, + .base_addr = FSATA1_BASEADDR, + .instance_name = "sata1", + .irq_num = FSATA1_IRQNUM /* Irq number */ + + }, + +}; + +#endif diff --git a/bsp/phytium/libraries/standalone/drivers/sata/fsata/fsata_hw.c b/bsp/phytium/libraries/standalone/drivers/sata/fsata/fsata_hw.c new file mode 100644 index 0000000000..278636bc93 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/sata/fsata/fsata_hw.c @@ -0,0 +1,36 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsata_hw.c + * Date: 2022-02-10 14:55:11 + * LastEditTime: 2022-02-18 09:03:23 + * Description:  This files is for sata register function + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +/***************************** Include Files *********************************/ + +#include "fsata_hw.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ diff --git a/bsp/phytium/libraries/standalone/drivers/sata/fsata/fsata_hw.h b/bsp/phytium/libraries/standalone/drivers/sata/fsata/fsata_hw.h new file mode 100644 index 0000000000..39c1b8001f --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/sata/fsata/fsata_hw.h @@ -0,0 +1,164 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsata_hw.h + * Date: 2022-02-10 14:55:11 + * LastEditTime: 2022-02-18 09:03:41 + * Description:  This files is for ctrl of sata functions + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef BSP_DRIVERS_FSATA_HW_H +#define BSP_DRIVERS_FSATA_HW_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ +#include "fkernel.h" +#include "ftypes.h" +#include "fio.h" + +/************************** Constant Definitions *****************************/ +/* SATA register definitions */ + +/* Global controller registers */ +#define FSATA_HOST_CAP 0x00 /* host capabilities */ +#define FSATA_HOST_CTL 0x04 /* global host control */ +#define FSATA_HOST_IRQ_STAT 0x08 /* interrupt status */ +#define FSATA_HOST_PORTS_IMPL 0x0c /* bitmap of implemented ports */ +#define FSATA_HOST_VERSION 0x10 /* AHCI spec. version compliancy */ +#define FSATA_HOST_CAP2 0x24 /* host capabilities, extended */ + +/* FSATA_HOST_CTL bits */ +#define FSATA_HOST_CAP_NP_MASK GENMASK(4, 0) /* Number of Ports (NP) */ +#define FSATA_HOST_AHCI_EN BIT(31) /* AHCI enabled */ +#define FSATA_HOST_CAP_SMPS BIT(28) /* AHCI Supports Mechanical Presence Switch */ +#define FSATA_HOST_CAP_SSS BIT(27) /* AHCI staggered spin-up */ +#define FSATA_HOST_CAP_SPM BIT(17) /* AHCI port multiplier */ +#define FSATA_HOST_IRQ_EN BIT(1) /* global IRQ enable */ +#define FSATA_HOST_RESET BIT(0) /* reset controller; self-clear */ +#define FSATA_HOST_PORTS_IMPL_MASK(x) GENMASK(x, 0) /* Ports Implemented */ + +/* Registers for each SATA port */ +#define FSATA_PORT_LST_ADDR 0x00 /* command list DMA addr */ +#define FSATA_PORT_LST_ADDR_HI 0x04 /* command list DMA addr hi */ +#define FSATA_PORT_FIS_ADDR 0x08 /* FIS rx buf addr */ +#define FSATA_PORT_FIS_ADDR_HI 0x0c /* FIS rx buf addr hi */ +#define FSATA_PORT_IRQ_STAT 0x10 /* interrupt status */ +#define FSATA_PORT_IRQ_MASK 0x14 /* interrupt enable/disable mask */ +#define FSATA_PORT_CMD 0x18 /* port command */ +#define FSATA_PORT_TFDATA 0x20 /* taskfile data */ +#define FSATA_PORT_SIG 0x24 /* device TF signature */ +#define FSATA_PORT_SCR_STAT 0x28 /* SATA phy register: SStatus */ +#define FSATA_PORT_SCR_CTL 0x2c /* SATA phy register: SControl */ +#define FSATA_PORT_SCR_ERR 0x30 /* SATA phy register: SError */ +#define FSATA_PORT_SCR_ACT 0x34 /* SATA phy register: SActive */ +#define FSATA_PORT_CMD_ISSUE 0x38 /* command issue */ + +/* PORT_SCR_STAT bits */ +#define FSATA_PORT_SCR_STAT_DET_MASK GENMASK(3, 0) +#define FSATA_PORT_SCR_STAT_DET_COMINIT 0x1 +#define FSATA_PORT_SCR_STAT_DET_PHYRDY 0x3 /* SATA exist and phy connected */ + +/* PORT_CMD bits */ +#define FSATA_PORT_CMD_LIST_ADDR_MASK GENMASK(31, 10) +#define FSATA_PORT_CMD_FIS_ADDR_MASK GENMASK(31, 8) +#define FSATA_PORT_CMD_TABLE_ADDR_MASK GENMASK(31, 7) + +#define FSATA_PORT_CMD_ATAPI BIT(24) /* Device is ATAPI */ +#define FSATA_PORT_CMD_LIST_ON BIT(15) /* cmd list DMA engine running */ +#define FSATA_PORT_CMD_FIS_ON BIT(14) /* FIS DMA engine running */ +#define FSATA_PORT_CMD_FIS_RX BIT(4) /* Enable FIS receive DMA engine */ +#define FSATA_PORT_CMD_CLO BIT(3) /* Command list override */ +#define FSATA_PORT_CMD_POWER_ON BIT(2) /* Power up device */ +#define FSATA_PORT_CMD_SPIN_UP BIT(1) /* Spin up device */ +#define FSATA_PORT_CMD_START BIT(0) /* Enable port DMA engine */ + +#define FSATA_PORT_CMD_ICC_ACTIVE BIT(28) /* Put i/f in active state */ +#define FSATA_PORT_CMD_ICC_PARTIAL BIT(29) /* Put i/f in partial state */ +#define FSATA_PORT_CMD_ICC_SLUMBER (0x6 << 28) /* Put i/f in slumber state */ + +#define FSATA_PORT_TFDATA_ATA_BUSY BIT(7) /* BSY status bit */ +#define FSATA_PORT_TFDATA_ATA_DRDY BIT(6) /* device ready */ +#define FSATA_PORT_TFDATA_ATA_DF BIT(5) /* device fault */ +#define FSATA_PORT_TFDATA_ATA_DRQ BIT(3) /* data request i/o */ +#define FSATA_PORT_TFDATA_ATA_ERR BIT(0) /* have an error */ + +/* PORT_IRQ_{STAT,MASK} bits */ +#define FSATA_PORT_IRQ_COLD_PRES BIT(31) /* cold presence detect */ +#define FSATA_PORT_IRQ_TF_ERR BIT(30) /* task file error */ +#define FSATA_PORT_IRQ_HBUS_ERR BIT(29) /* host bus fatal error */ +#define FSATA_PORT_IRQ_HBUS_DATA_ERR BIT(28) /* host bus data error */ +#define FSATA_PORT_IRQ_IF_ERR BIT(27) /* interface fatal error */ +#define FSATA_PORT_IRQ_IF_NONFATAL BIT(26) /* interface non-fatal error */ +#define FSATA_PORT_IRQ_OVERFLOW BIT(24) /* xfer exhausted available S/G */ +#define FSATA_PORT_IRQ_BAD_PMP BIT(23) /* incorrect port multiplier */ + +#define FSATA_PORT_IRQ_PHYRDY BIT(22) /* PhyRdy changed */ +#define FSATA_PORT_IRQ_DEV_ILCK BIT(7) /* device interlock */ +#define FSATA_PORT_IRQ_CONNECT BIT(6) /* port connect change status */ +#define FSATA_PORT_IRQ_SG_DONE BIT(5) /* descriptor processed */ +#define FSATA_PORT_IRQ_UNK_FIS BIT(4) /* unknown FIS rx'd */ +#define FSATA_PORT_IRQ_SDB_FIS BIT(3) /* Set Device Bits FIS rx'd */ +#define FSATA_PORT_IRQ_DMAS_FIS BIT(2) /* DMA Setup FIS rx'd */ +#define FSATA_PORT_IRQ_PIOS_FIS BIT(1) /* PIO Setup FIS rx'd */ +#define FSATA_PORT_IRQ_D2H_REG_FIS BIT(0) /* D2H Register FIS rx'd */ + +#define FSATA_PORT_IRQ_FREEZE FSATA_PORT_IRQ_CONNECT | FSATA_PORT_IRQ_SDB_FIS | \ + FSATA_PORT_IRQ_D2H_REG_FIS | FSATA_PORT_IRQ_PIOS_FIS + +#define FSATA_PORT_SCR_ACT_ENABLE BIT(0) /* Port Serial ATA Active */ +#define FSATA_PORT_CMD_ISSUE_ENABLE BIT(0) /* Port Command Issue enable */ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/** + * @name: SATA_READ_REG32 + * @msg: 读取SATA寄存器 + * @param {u32} addr 定时器的基地址 + * @param {u32} reg_offset 定时器的寄存器的偏移 + * @return {u32} 寄存器参数 + */ +#define FSATA_READ_REG32(addr, reg_offset) FtIn32((addr) + (u32)reg_offset) + +/** + * @name: SATA_WRITE_REG32 + * @msg: 写入SATA寄存器 + * @param {u32} addr 定时器的基地址 + * @param {u32} reg_offset 定时器的寄存器的偏移 + * @param {u32} reg_value 写入寄存器参数 + * @return {void} + */ +#define FSATA_WRITE_REG32(addr, reg_offset, reg_value) FtOut32((addr) + (u32)reg_offset, (u32)reg_value) + +#define FSATA_SETBIT(base_addr, reg_offset, data) \ + FtSetBit32((base_addr) + (u32)(reg_offset), (u32)(data)) + +#define FSATA_CLEARBIT(base_addr, reg_offset, data) \ + FtClearBit32((base_addr) + (u32)(reg_offset), (u32)(data)) + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/sata/fsata/fsata_intr.c b/bsp/phytium/libraries/standalone/drivers/sata/fsata/fsata_intr.c new file mode 100644 index 0000000000..56fc9dc401 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/sata/fsata/fsata_intr.c @@ -0,0 +1,223 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsata_intr.c + * Date: 2022-02-10 14:55:11 + * LastEditTime: 2022-02-18 09:03:57 + * Description:  This files is for intrrupt function of Sata ctrl + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "fassert.h" +#include "fdebug.h" +#include "fsata.h" +#include "fsata_hw.h" +#include "finterrupt.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + +#define FSATA_DEBUG_TAG "FSATA_INTR" +#define FSATA_ERROR(format, ...) FT_DEBUG_PRINT_E(FSATA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSATA_WARN(format, ...) FT_DEBUG_PRINT_W(FSATA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSATA_INFO(format, ...) FT_DEBUG_PRINT_I(FSATA_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSATA_DEBUG(format, ...) FT_DEBUG_PRINT_D(FSATA_DEBUG_TAG, format, ##__VA_ARGS__) + +/** + * @name: FSataIrqEnable + * @msg: enable sata interrupt mask + * @param {FSataCtrl} *instance_p, pointer to the FSataCtrl instance + * @param {u32} int_mask, interrupt enable mask + * @return {void} + */ +void FSataIrqEnable(FSataCtrl *instance_p, u32 int_mask) +{ + u32 reg_value; + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + u32 port = instance_p->private_data; + u32 i; + for (i = 0; i < instance_p->n_ports; i++) + { + if (!(port & BIT(i))) + continue; + + uintptr port_base_addr = instance_p->port[i].port_base_addr; + FSATA_SETBIT(port_base_addr, FSATA_PORT_IRQ_MASK, int_mask); + } +} + +/** + * @name: FSataIrqDisable + * @msg: disable sata interrupt mask + * @param {FSataCtrl} *instance_p, pointer to the FSataCtrl instance + * @param {u32} int_mask, interrupt disable mask + * @return {void} + */ +void FSataIrqDisable(FSataCtrl *instance_p, u32 int_mask) +{ + u32 reg_value; + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + u32 port = instance_p->private_data; + u32 i; + for (i = 0; i < instance_p->n_ports; i++) + { + if (!(port & BIT(i))) + continue; + + uintptr port_base_addr = instance_p->port[i].port_base_addr; + + FSATA_CLEARBIT(port_base_addr, FSATA_PORT_IRQ_MASK, int_mask); + } +} + +/** + * @name: FSataSetHandler + * @msg: set sata interrupt handler function + * @param {FSataCtrl} *instance_p, pointer to the FSataCtrl instance + * @param {u32} irq_type, interrupt type + * @param {void} *func_pointer, interrupt handler function + * @param {void} *call_back_ref, interrupt handler function argument + * @return {FError} return FSATA_SUCCESS if successful, return others if failed + */ +FError FSataSetHandler(FSataCtrl *instance_p, u32 irq_type, void *func_pointer, void *call_back_ref) +{ + FError status = FT_SUCCESS; + FASSERT(instance_p != NULL); + FASSERT(func_pointer != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + + switch (irq_type) + { + case FSATA_PORT_IRQ_D2H_REG_FIS: + instance_p->fsata_dhrs_cb = ((FSataIrqCallBack)(void *)func_pointer); + instance_p->dhrs_args = call_back_ref; + break; + + case FSATA_PORT_IRQ_SDB_FIS: + instance_p->fsata_sdbs_cb = ((FSataIrqCallBack)(void *)func_pointer); + instance_p->sdbs_args = call_back_ref; + break; + + case FSATA_PORT_IRQ_CONNECT: + instance_p->fsata_pcs_cb = ((FSataIrqCallBack)(void *)func_pointer); + instance_p->pcs_args = call_back_ref; + break; + + case FSATA_PORT_IRQ_PIOS_FIS: + instance_p->fsata_pss_cb = ((FSataIrqCallBack)(void *)func_pointer); + instance_p->pss_args = call_back_ref; + break; + + default: + status = (FSATA_ERR_OPERATION); + break; + } + return status; +} + +/** + * @name: FSataIrqHandler + * @msg: sata interrupt handler entry + * @param {void} *param, function parameters, users can set + * @return {void} + */ +void FSataIrqHandler(s32 vector, void *param) +{ + FSataCtrl *instance_p = (FSataCtrl *)param; + FSataConfig *config_p; + + FASSERT(instance_p != NULL); + FASSERT(instance_p->is_ready == FT_COMPONENT_IS_READY); + u32 port = instance_p->private_data; + u32 status; + u32 mask_status = 0; + uintptr base_addr = instance_p->config.base_addr; + u32 irq_state = 0; + u32 i = 0; + + uintptr port_base_addr = 0; + + for (i = 0; i < instance_p->n_ports; i++) + { + if (!(port & BIT(i))) + continue; + + port_base_addr = instance_p->port[i].port_base_addr; + irq_state = FSATA_READ_REG32(base_addr, FSATA_HOST_IRQ_STAT); + status = FSATA_READ_REG32(port_base_addr, FSATA_PORT_IRQ_STAT); + mask_status = FSATA_READ_REG32(port_base_addr, FSATA_PORT_IRQ_MASK); + + /* clear port first, host second */ + FSATA_WRITE_REG32(port_base_addr, FSATA_PORT_IRQ_STAT, status); + FSATA_WRITE_REG32(base_addr, FSATA_HOST_IRQ_STAT, irq_state); + + if (status & mask_status & FSATA_PORT_IRQ_D2H_REG_FIS) + { + if (instance_p->fsata_dhrs_cb) + { + instance_p->fsata_dhrs_cb(instance_p->dhrs_args); + } + } + + if (status & mask_status & FSATA_PORT_IRQ_PIOS_FIS) + { + if (instance_p->fsata_pss_cb) + { + instance_p->fsata_pss_cb(instance_p->pss_args); + } + } + + if (status & mask_status & FSATA_PORT_IRQ_SDB_FIS) + { + if (instance_p->fsata_sdbs_cb) + { + instance_p->fsata_sdbs_cb(instance_p->sdbs_args); + } + } + + if (status & mask_status & FSATA_PORT_IRQ_DMAS_FIS) + { + if (instance_p->fsata_dss_cb) + { + instance_p->fsata_dss_cb(instance_p->dss_args); + } + } + + if (status & mask_status & FSATA_PORT_IRQ_CONNECT) + { + if (instance_p->fsata_pcs_cb) + { + instance_p->fsata_pcs_cb(instance_p->pcs_args); + } + + /* reset hba */ + FSATA_WRITE_REG32(base_addr, FSATA_HOST_CTL, FSATA_HOST_RESET); + FSataAhciInit(instance_p); + FSataIrqEnable(instance_p, FSATA_PORT_IRQ_FREEZE); + } + + } +} + diff --git a/bsp/phytium/libraries/standalone/drivers/sata/fsata/fsata_sinit.c b/bsp/phytium/libraries/standalone/drivers/sata/fsata/fsata_sinit.c new file mode 100644 index 0000000000..8f0af5c686 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/sata/fsata/fsata_sinit.c @@ -0,0 +1,72 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fsata_sinit.c + * Date: 2022-02-10 14:55:11 + * LastEditTime: 2022-02-18 09:04:15 + * Description:  This files is for sata static init + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +/***************************** Include Files *********************************/ + +#include "fsata.h" +#include "fparameters.h" +#include "sdkconfig.h" +#include "fassert.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + +extern const FSataConfig FSataPcieConfigTbl[PLAT_AHCI_HOST_MAX_COUNT]; + +#if defined(CONFIG_TARGET_E2000) + extern const FSataConfig FSataControllerConfigTbl[FSATA_INSTANCE_NUM]; +#endif + +/*****************************************************************************/ +/** + * @name: FSataLookupConfig + * @msg: get sata configs by id and type, Support both pcie and SATA controllers + * @return {FSataConfig *} + * @param {u32} instance_id, id of sata ctrl + */ +const FSataConfig *FSataLookupConfig(u32 instance_id, u8 type) +{ + const FSataConfig *pconfig = NULL; + if (type == FSATA_TYPE_CONTROLLER) + { +#if defined(CONFIG_TARGET_E2000) + FASSERT(instance_id < FSATA_INSTANCE_NUM); + pconfig = &FSataControllerConfigTbl[instance_id]; +#endif + } + else + { + FASSERT(instance_id < PLAT_AHCI_HOST_MAX_COUNT); + pconfig = &FSataPcieConfigTbl[instance_id]; + } + + return (const FSataConfig *)pconfig; +} + diff --git a/bsp/phytium/libraries/standalone/drivers/serial/Kconfig b/bsp/phytium/libraries/standalone/drivers/serial/Kconfig new file mode 100644 index 0000000000..e84c41301c --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/serial/Kconfig @@ -0,0 +1,10 @@ + +menu "Usart Configuration" + config ENABLE_Pl011_UART + bool + prompt "Use Pl011 uart" + default n + +endmenu + + diff --git a/bsp/phytium/libraries/standalone/drivers/serial/fpl011/fpl011.c b/bsp/phytium/libraries/standalone/drivers/serial/fpl011/fpl011.c new file mode 100644 index 0000000000..f7023b1b2f --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/serial/fpl011/fpl011.c @@ -0,0 +1,287 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fpl011.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:07:24 + * Description:  This files is for uart functions + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +/***************************** Include Files ********************************/ + +#include "fpl011.h" +#include "fio.h" +#include "ferror_code.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +u32 FPl011SendBuffer(FPl011 *uart_p); +u32 FPl011ReceiveBuffer(FPl011 *uart_p); + +static void FPl011StubHandler(void *Args, u32 Event, + u32 ByteCount); +/*****************************************************************************/ + + +FError FPl011CfgInitialize(FPl011 *uart_p, FPl011Config *config) +{ + u32 reg_value = 0; + FError ret ; + FASSERT(uart_p != NULL); + FASSERT(config != NULL); + + uart_p->config.instance_id = config->instance_id; + uart_p->config.base_address = config->base_address; + uart_p->config.ref_clock_hz = config->ref_clock_hz; + uart_p->config.irq_num = config->irq_num; + uart_p->config.baudrate = config->baudrate; + + uart_p->handler = FPl011StubHandler; + + uart_p->send_buffer.byte_p = NULL; + uart_p->send_buffer.requested_bytes = 0; + uart_p->send_buffer.remaining_bytes = 0; + + uart_p->receive_buffer.byte_p = NULL; + uart_p->receive_buffer.requested_bytes = 0; + uart_p->receive_buffer.remaining_bytes = 0; + uart_p->rxbs_error = 0; + + uart_p->is_ready = FT_COMPONENT_IS_READY; + ret = FPl011SetBaudRate(uart_p, uart_p->config.baudrate); + if (ret != FT_SUCCESS) + { + uart_p->is_ready = 0U; + } + else + { + /* + * Set up the default data format: 8 bit data, 1 stop bit, no + * parity + */ + reg_value = ((FPL011_FORMAT_WORDLENGTH_8BIT << 5) & FPL011LCR_H_WLEN); //config.base_address, FPL011LCR_H_OFFSET, reg_value); + + /* Set the RX FIFO trigger at 8 data bytes.Tx FIFO trigger is 8 data bytes*/ + reg_value = (1 << 3) | (1 << 0); + FUART_WRITEREG32(uart_p->config.base_address, FPL011IFLS_OFFSET, reg_value); + + /* Disable all interrupts, polled mode is the default */ + reg_value = 0; + FUART_WRITEREG32(uart_p->config.base_address, FPL011IMSC_OFFSET, reg_value); + } + + return FT_SUCCESS; +} + + +/** + * @name: FPl011Send + * @msg: This functions sends the specified buffer using the device in either + * polled or interrupt driven mode. + * @return The number of bytes actually sent. + * @param uart_p is a pointer to the FPl011 instance. + * @param byte_p is pointer to a buffer of data to be sent. + * @param length ontains the number of bytes to be sent. Any data that was already put into the + * transmit FIFO will be sent. + */ +u32 FPl011Send(FPl011 *uart_p, u8 *byte_p, u32 length) +{ + u32 sent_count = 0; + FASSERT(uart_p != NULL); + FASSERT(byte_p != NULL); + FASSERT(uart_p->is_ready == FT_COMPONENT_IS_READY); + + uart_p->send_buffer.byte_p = byte_p; + uart_p->send_buffer.requested_bytes = length; + uart_p->send_buffer.remaining_bytes = length; + sent_count = FPl011SendBuffer(uart_p); + return sent_count; +} + + + +static void FPl011StubHandler(void *args, u32 event, + u32 byte_count) +{ + (void)args; + (void)event; + (void)byte_count; + + FASSERT(0); +} + +/** + * @name: FPl011SendBuffer + * @msg: send data buffer through uart + * @return {*} + * @param uart_p + */ +u32 FPl011SendBuffer(FPl011 *uart_p) +{ + u32 sent_count = 0U; + u32 isbusy; + + isbusy = (u32)FUART_ISTRANSMITBUSY(uart_p->config.base_address); + while (isbusy == TRUE) + { + isbusy = (u32)FUART_ISTRANSMITBUSY(uart_p->config.base_address); + } + + /* + * 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 ((!FUART_ISTRANSMITFULL(uart_p->config.base_address)) && (uart_p->send_buffer.remaining_bytes > sent_count)) + { + FUART_WRITEREG32(uart_p->config.base_address, FPL011DR_OFFSET, (u32)uart_p->send_buffer.byte_p[sent_count]); + sent_count++; + } + /* Update the buffer to reflect the bytes that were sent from it */ + uart_p->send_buffer.byte_p += sent_count; + uart_p->send_buffer.remaining_bytes -= sent_count; + + return sent_count; +} + +/** + * @name: FPl011Receive + * @msg: This function attempts to receive a specified number of bytes of data + * from the device and store it into the specified buffer. + * @param uart_p is a pointer to the FPl011 instance + * @param byte_p is pointer to buffer for data to be received into + * @param length is the number of bytes to be received. + * @return The number of bytes received. + */ +u32 FPl011Receive(FPl011 *uart_p, u8 *byte_p, u32 length) +{ + u32 received; + + FASSERT(uart_p != NULL); + FASSERT(byte_p != NULL); + FASSERT(uart_p->is_ready == FT_COMPONENT_IS_READY); + + uart_p->receive_buffer.byte_p = byte_p; + uart_p->receive_buffer.requested_bytes = length; + uart_p->receive_buffer.remaining_bytes = length; + + received = FPl011ReceiveBuffer(uart_p); + + return received; +} + + +u32 FPl011ReceiveBuffer(FPl011 *uart_p) +{ + + u32 received_count = 0U; + u32 event; + u32 event_data; + u32 byte_value; + + while ((received_count < uart_p->receive_buffer.remaining_bytes) && !FUART_ISRECEIVEDATA(uart_p->config.base_address)) + { + byte_value = FUART_READREG32(uart_p->config.base_address, FPL011DR_OFFSET); + + if (uart_p->rxbs_error) + { + if ((byte_value & FPL011DR_ALLE) != 0) + { + event_data = byte_value; + event = FPL011_EVENT_PARE_FRAME_BRKE; + + if (uart_p->handler) + { + uart_p->handler(uart_p->args, event, event_data); + } + } + } + uart_p->receive_buffer.byte_p[received_count] = (u8)(byte_value & 0xff); + received_count++; + } + + uart_p->rxbs_error = 0; + + if (uart_p->receive_buffer.byte_p != NULL) + { + uart_p->receive_buffer.byte_p += received_count; + } + uart_p->receive_buffer.remaining_bytes -= received_count; + + return received_count; +} + +/** + * @name: FPl011BlockSend + * @msg: initiate uart block send + * @return {*} + * @param uart_p + * @param byte_p + * @param length + */ +void FPl011BlockSend(FPl011 *uart_p, u8 *byte_p, u32 length) +{ + u32 index; + + FASSERT(uart_p != NULL); + FASSERT(byte_p != NULL); + FASSERT(uart_p->is_ready == FT_COMPONENT_IS_READY); + + for (index = 0; index < length; index++) + { + FPl011SendByte(uart_p->config.base_address, byte_p[index]); + } +} + +/** + * @name: FPl011BlockReceive + * @msg: initiate uart block receive + * @return {*} + * @param uart_p + */ +u8 FPl011BlockReceive(FPl011 *uart_p) +{ + FASSERT(uart_p != NULL); + FASSERT(uart_p->is_ready == FT_COMPONENT_IS_READY); + return FPl011RecvByte(uart_p->config.base_address); +} + +void FPl011IrqClearReciveTimeOut(FPl011 *uart_p) +{ + u32 reg_temp; + reg_temp = FPl011GetInterruptMask(uart_p); + reg_temp &= ~FPL011MIS_RTMIS; + FPl011SetInterruptMask(uart_p, reg_temp); +} + +void FPl011IrqEnableReciveTimeOut(FPl011 *uart_p) +{ + u32 reg_temp; + reg_temp = FPl011GetInterruptMask(uart_p); + reg_temp |= FPL011MIS_RTMIS; + FPl011SetInterruptMask(uart_p, reg_temp); +} diff --git a/bsp/phytium/libraries/standalone/drivers/serial/fpl011/fpl011.h b/bsp/phytium/libraries/standalone/drivers/serial/fpl011/fpl011.h new file mode 100644 index 0000000000..349b6a352e --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/serial/fpl011/fpl011.h @@ -0,0 +1,182 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fpl011.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:07:38 + * Description:  This files is for uart functions + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef BSP_DRIVERS_SERIAL_PL011_UART_H +#define BSP_DRIVERS_SERIAL_PL011_UART_H + + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ + +#include "ftypes.h" +#include "fassert.h" +#include "fpl011_hw.h" +#include "sdkconfig.h" +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +#define FPL011_ERROR_PARAM FT_CODE_ERR(ErrModBsp, ErrBspUart, 0x1u) + +#define FPL011_BAUDRATE 115200U + +/* Config options */ +#define FPL011_OPTION_UARTEN 0x1U +#define FPL011_OPTION_RXEN 0x2U +#define FPL011_OPTION_TXEN 0x4U +#define FPL011_OPTION_FIFOEN 0x8U +#define FPL011_OPTION_CTS 0x10U +#define FPL011_OPTION_RTS 0x20U +#define FPL011_OPTION_DTR 0x40U +#define FPL011_OPTION_RTSEN 0x80U +#define FPL011_OPTION_CTSEN 0x100U +#define FPL011_OPTION_TXDMAEN 0x200U +#define FPL011_OPTION_RXDMAEN 0x400U + +/* Channel Operational Mode */ +#define FPL011_OPER_MODE_NORMAL (u8)0x00U /* Normal Mode */ +#define FPL011_OPER_MODE_LOCAL_LOOP (u8)0x01U /* Local Loop back Mode */ + + +/* Data format values */ +#define FPL011_FORMAT_WORDLENGTH_8BIT 0x3 +#define FPL011_FORMAT_WORDLENGTH_7BIT 0x2 +#define FPL011_FORMAT_WORDLENGTH_6BIT 0x1 +#define FPL011_FORMAT_WORDLENGTH_5BIT 0x0 + +#define FPL011_FORMAT_NO_PARITY 0U /* No parity */ +#define FPL011_FORMAT_EN_PARITY 1U /* Enable parity */ +#define FPL011_FORMAT_EVEN_PARITY 2U /* Even parity */ +#define FPL011_FORMAT_ODD_PARITY 0U /* Odd parity */ +#define FPL011_FORMAT_EN_STICK_PARITY 4U /* Stick parity */ +#define FPL011_FORMAT_NO_STICK_PARITY 0U /* Stick parity */ + +#define FPL011_FORMAT_PARITY_MASK 7U /* Format parity mask */ + +#define FPL011_FORMAT_EVEN_PARITY_SHIFT 1U /* Even parity shift */ +#define FPL011_FORMAT_EN_STICK_PARITY_SHIFT 5U /* Stick parity shift */ + +#define FPL011_FORMAT_2_STOP_BIT 1U +#define FPL011_FORMAT_1_STOP_BIT 0U + + +/* Callback events */ +#define FPL011_EVENT_RECV_DATA 1U /* Data receiving done */ +#define FPL011_EVENT_RECV_TOUT 2U /* A receive timeout occurred */ +#define FPL011_EVENT_SENT_DATA 3U /* Data transmission done */ +#define FPL011_EVENT_RECV_ERROR 4U /* A receive error detected */ +#define FPL011_EVENT_MODEM 5U /* Modem status changed */ +#define FPL011_EVENT_PARE_FRAME_BRKE 6U /* A receive parity, frame, break \ + * error detected */ +#define FPL011_EVENT_RECV_ORERR 7U /* A receive overrun error detected */ + + +/**************************** Type Definitions ******************************/ + +/** + * Keep track of data format setting of a device. + */ +typedef struct +{ + u32 baudrate ; /* In bps, ie 1200 */ + u32 data_bits ; /* Number of data bits */ + u32 parity ; /* Parity */ + u8 stopbits ; /* Number of stop bits */ +} FPl011Format ; + + +typedef struct +{ + u32 instance_id; /* Id of device*/ + u32 base_address; + u32 ref_clock_hz; + u32 irq_num; + u32 baudrate; +} FPl011Config; + +typedef struct +{ + u8 *byte_p; + u32 requested_bytes; + u32 remaining_bytes; +} FPl011Buffer; + +typedef void (*FPl011EventHandler)(void *args, u32 event, u32 event_data); + +typedef struct +{ + FPl011Config config; /* Configuration data structure */ + u32 is_ready; /* Device is ininitialized and ready*/ + + FPl011Buffer send_buffer; + FPl011Buffer receive_buffer; + + FPl011EventHandler handler; + void *args; + uint8_t rxbs_error; /* An error occurs during receiving. 0 has no error and 1 has an error */ + +} FPl011; + + +/************************** Function Prototypes ******************************/ + +/* FPl011_uart_sinit.c */ +const FPl011Config *FPl011LookupConfig(u32 instance_id); +/* FPl011_uart.c */ +FError FPl011CfgInitialize(FPl011 *uart_p, FPl011Config *config); +void FPl011BlockSend(FPl011 *uart_p, u8 *byte_p, u32 length); +u32 FPl011Send(FPl011 *uart_p, u8 *byte_p, u32 length); +u32 FPl011Receive(FPl011 *uart_p, u8 *byte_p, u32 length); +u8 FPl011BlockReceive(FPl011 *uart_p); +void FPl011ProgramCtlReg(FPl011 *uart_p, u32 ctrl_reg); + +/* FPl011_uart_options.c */ +void FPl011SetOperMode(FPl011 *uart_p, u8 operation_mode); +void FPl011SetOptions(FPl011 *uart_p, u32 options); +void FPl011SetSpecificOptions(FPl011 *uart_p, u32 options); +void FPl011ClearSpecificOptions(FPl011 *uart_p, u32 options); +FError FPl011SetBaudRate(FPl011 *uart_p, u32 baudrate) ; +void FPl011GetDataFormat(FPl011 *uart_p, FPl011Format *format_p) ; +FError FPl011SetDataFormat(FPl011 *uart_p, FPl011Format *format_p) ; +void FPl011SetTxFifoThreadHold(FPl011 *uart_p, u8 trigger_level) ; +void FPl011SetRxFifoThreadhold(FPl011 *uart_p, u8 trigger_level) ; + +/* FPl011_uart_intr.c */ +u32 FPl011GetInterruptMask(FPl011 *uart_p) ; +void FPl011InterruptHandler(s32 vector, void *param); +void FPl011SetHandler(FPl011 *uart_p, FPl011EventHandler fun_p, void *args); +void FPl011SetInterruptMask(FPl011 *uart_p, u32 mask); + + + +#ifdef __cplusplus +} +#endif + +#endif // ! diff --git a/bsp/phytium/libraries/standalone/drivers/serial/fpl011/fpl011_g.c b/bsp/phytium/libraries/standalone/drivers/serial/fpl011/fpl011_g.c new file mode 100644 index 0000000000..2717101988 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/serial/fpl011/fpl011_g.c @@ -0,0 +1,70 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fpl011_g.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:05:41 + * Description:  This files is for uart config + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +/***************************** Include Files *********************************/ + +#include "fpl011.h" +#include "fparameters.h" +#include "sdkconfig.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + +const FPl011Config FPl011ConfigTable[FUART_NUM] = +{ + { + .instance_id = FUART0_ID, + .base_address = FUART0_BASE_ADDR, + .ref_clock_hz = FUART0_CLK_FREQ_HZ, + .irq_num = FUART0_IRQ_NUM, + .baudrate = 115200 + }, + { + .instance_id = FUART1_ID, + .base_address = FUART1_BASE_ADDR, + .ref_clock_hz = FUART1_CLK_FREQ_HZ, + .irq_num = FUART1_IRQ_NUM, + .baudrate = 115200 + }, + { + .instance_id = FUART2_ID, + .base_address = FUART2_BASE_ADDR, + .ref_clock_hz = FUART2_CLK_FREQ_HZ, + .irq_num = FUART2_IRQ_NUM, + .baudrate = 115200 + }, + { + .instance_id = FUART3_ID, + .base_address = FUART3_BASE_ADDR, + .ref_clock_hz = FUART3_CLK_FREQ_HZ, + .irq_num = FUART3_IRQ_NUM, + .baudrate = 115200 + } +}; diff --git a/bsp/phytium/libraries/standalone/drivers/serial/fpl011/fpl011_hw.c b/bsp/phytium/libraries/standalone/drivers/serial/fpl011/fpl011_hw.c new file mode 100644 index 0000000000..9b468f2439 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/serial/fpl011/fpl011_hw.c @@ -0,0 +1,73 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fpl011_hw.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:05:56 + * Description:  This files is for uart register function + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +/***************************** Include Files *********************************/ + +#include "fpl011_hw.h" + + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/** + * @name: FPl011SendByte + * @msg: This function sends one byte using the device.This function operates in polled mode and blocks + * until the data has been put into the TX FIFO register. + * @param addr contains the base address of the device. + * @param Byte contains the byte to be sent. + */ +void FPl011SendByte(u32 addr, u8 byte) +{ + + while (FUART_ISTRANSMITFULL(addr)) + { + ; + } + FUART_WRITEREG32(addr, FPL011DR_OFFSET, (u32)byte); +} + +/** + * @name: FPl011RecvByte + * @msg: This function receives a byte from the device. It operates in polled mode + * and blocks until a byte has received. + * @param addr contains the base address of the device. + */ +u8 FPl011RecvByte(u32 addr) +{ + u32 recieved_byte; + + while (FUART_ISRECEIVEDATA(addr)) + { + ; + } + recieved_byte = FUART_READREG32(addr, FPL011DR_OFFSET); + return recieved_byte; +} + diff --git a/bsp/phytium/libraries/standalone/drivers/serial/fpl011/fpl011_hw.h b/bsp/phytium/libraries/standalone/drivers/serial/fpl011/fpl011_hw.h new file mode 100644 index 0000000000..cec10c1fa0 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/serial/fpl011/fpl011_hw.h @@ -0,0 +1,255 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fpl011_hw.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:06:10 + * Description:  This files is for definition of uart register + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef BSP_DRIVERS_SERIAL_PL011_UART_HW_H +#define BSP_DRIVERS_SERIAL_PL011_UART_HW_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ftypes.h" +#include "fassert.h" +#include "fio.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/** @name Register Map + * + * Register offsets for the UART. + */ +#define FPL011DR_OFFSET 0U /* Data */ +#define FPL011RSR_OFFSET 4U /* Receive Status Register/Error Clear */ +#define FPL011ECR_OFFSET FPL011RSR_OFFSET +#define FPL011FTR_OFFSET 0x18U /* Flag Register */ +#define FPL011ILPR_OFFSET 0x020U /* IrDA Low-Power Counter */ +#define FPL011IBRD_OFFSET 0x024U /* Integer Baud Rate */ +#define FPL011FBRD_OFFSET 0x028U /* Fractional Baud Rate */ +#define FPL011LCR_H_OFFSET 0x02cU /* Line Control */ +#define FPL011CR_OFFSET 0x030U /* Control */ +#define FPL011IFLS_OFFSET 0x034U /* Interrupt FIFO Level Select */ +#define FPL011IMSC_OFFSET 0x038U /* Interrupt Mask Set/Clear */ +#define FPL011RIS_OFFSET 0x03cU /* Raw interrupt Status */ +#define FPL011MIS_OFFSET 0x040U /* Mask interrupt Status */ +#define FPL011ICR_OFFSET 0x044U /* Interrupt Clear */ +#define FPL011DMACR_OFFSET 0x048U /* DMA Control */ + +/* Data RW */ +#define FPL011DR_OE 0x800U /* This bit is set to 1 if data is received and the received FIFO is full */ +#define FPL011DR_BE 0x400U /* Break error */ +#define FPL011DR_PE 0x200U /* Parity error */ +#define FPL011DR_FE 0x100U /* Frame error */ +#define FPL011DR_ALLE (FPL011DR_OE | FPL011DR_BE | FPL011DR_PE | FPL011DR_FE) +#define FPL011DR_DATA 0xffU + +/* Receive Status Register/Error Clear RW */ +#define FPL011RSR_OE 0x8U /* overflow error */ +#define FPL011RSR_BE 0x4U /* Break error */ +#define FPL011RSR_PE 0x2U /* Parity error */ +#define FPL011RSR_FE 0x1U /* Frame error */ + +#define FPL011ECR_CLE 0xffU /* Clear */ + +/* Flag Register RO */ +#define FPL011FTR_RI 0x100U /* Ring indicator */ +#define FPL011FTR_TXFE 0x80U /* Transmit FIFO empty */ +#define FPL011FTR_RXFF 0x40U /* Receive FIFO full */ +#define FPL011FTR_TXFF 0x20U /* Transmit FIFO full. */ +#define FPL011FTR_RXFE 0x10U /* Receive FIFO empty */ +#define FPL011FTR_BUSY 0x08U /* UART busy */ +#define FPL011FTR_DCD 0x04U /* Data carrier detect. */ +#define FPL011FTR_DSR 0x02U /* Data set ready. */ +#define FPL011FTR_CTS 0x1U /* Clear to send */ + +/* IrDA Low-Power Counter RW */ +#define FPL011ILPR_ILPDVSR 0xffU /* 8-bit low-power divisor value. These bits are cleared to 0 at reset */ + +/* Integer Baud Rate RW */ +#define FPL011IBRD_BAUD_DIVFRAC 0xffffU /* The fractional baud rate divisor. */ + +/* Fractional Baud Rate RW */ +#define FPL011FBRD_BAUD_DIVFRAC 0x3fU /* The fractional baud rate divisor. */ + +/* Line Control RW */ +#define FPL011LCR_H_SPS 0x80U /* Stick parity select. */ +#define FPL011LCR_H_WLEN 0x60U /* Word length. */ +#define FPL011LCR_H_FEN 0x10U /* Enable FIFOs. */ +#define FPL011LCR_H_STP2 0x08U /* Two stop bits select. */ +#define FPL011LCR_H_EPS 0x04U /* Even parity select. */ +#define FPL011LCR_H_PEN 0x02U /* Parity enable. */ +#define FPL011LCR_H_BRK 0x01U /* send break */ + +#define FPL011LCR_H_WLEN_SHIFT 0x00000005U /* Word length shift */ +#define FPL011LCR_H_WLEN_5_BIT 0x00000000U /* 5 bits data */ +#define FPL011LCR_H_WLEN_6_BIT 0x00000020U /* 6 bits data */ +#define FPL011LCR_H_WLEN_7_BIT 0x00000040U /* 7 bits data */ +#define FPL011LCR_H_WLEN_8_BIT 0x00000060U /* 8 bits data */ +#define FPL011LCR_H_STP_1_BIT 0x00000000U + +#define FPL011LCR_H_STP_MASK 0x00000008U /* Stop bits mask */ +#define FPL011LCR_H_STP_SHIFT 0x00000003U /* Stop bits shift */ +#define FPL011LCR_H_PARITY_EVEN 0x00000004U /* Even parity mode */ +#define FPL011LCR_H_PARITY_MASK 0x00000002U /* Parity mask */ +#define FPL011LCR_H_PARITY_SHIFT 0x00000001U /* Parity shift */ +#define FPL011LCR_H_PARITY_NONE 0x00000000U /* No parity mode */ +#define FPL011LCR_H_PARITY_ODD 0x00000000U /* Odd parity mode */ + +/* Control RW */ +#define FPL011CR_CTSEN 0x8000U /* CTS hardware flow control enable. */ +#define FPL011CR_RTSEN 0x4000U /* RTS hardware flow control enable. */ +#define FPL011CR_OUT2 0x2000U /* This bit is the complement of the UART Out2 (nUARTOut2) modem status output. */ +#define FPL011CR_OUT1 0x1000U /* This bit is the complement of the UART Out1 (nUARTOut1) modem status output. */ +#define FPL011CR_RTS 0x0800U /* Request to send. */ +#define FPL011CR_DTR 0x0400U /* Data transmit ready */ +#define FPL011CR_RXE 0x0200U /* Receive enable. */ +#define FPL011CR_TXE 0x0100U /* Transmit enable. */ +#define FPL011CR_LBE 0x0080U /* Loop back enable.*/ +#define FPL011CR_SIRLP 0x4U /* IrDA SIR low power mode. */ +#define FPL011CR_SIREN 0x2U /* SIR enable. */ +#define FPL011CR_UARTEN 0x1U /* UART enable. */ +#define FPL011CR_MODE_NORMAL 0x00000000U /* Normal Mode */ + + +/* Interrupt FIFO Level Select RW */ +#define FPL011IFLS_RXIFLSEL_MASK 0x00000038U /* Receive interrupt FIFO level select mask */ +#define FPL011IFLS_TXIFLSEL_MASK 0x00000007U /* Receive interrupt FIFO level select mask */ +#define FPL011IFLS_RXIFLSEL_1_8 0x00000000U /* Receive FIFO becomes . 1/8 full */ +#define FPL011IFLS_RXIFLSEL_1_4 0x00000008U /* Receive FIFO becomes . 1/4 full */ +#define FPL011IFLS_RXIFLSEL_1_2 0x00000010U /* Receive FIFO becomes * . 1/2 full */ +#define FPL011IFLS_RXIFLSEL_3_4 0x00000018U /* Receive FIFO becomes * . 3/4 full */ +#define FPL011IFLS_RXIFLSEL_7_8 0x00000020U /* Receive FIFO becomes * . 7/8 full */ +#define FPL011IFLS_TXIFLSEL_1_8 0x00000000U /* Transmit FIFO becomes * . 1/8 full */ +#define FPL011IFLS_TXIFLSEL_1_4 0x00000001U /* Transmit FIFO becomes * . 1/4 full */ +#define FPL011IFLS_TXIFLSEL_1_2 0x00000002U /* Transmit FIFO becomes * . 1/2 full */ +#define FPL011IFLS_TXIFLSEL_3_4 0x00000003U /* Transmit FIFO becomes * . 3/4 full */ +#define FPL011IFLS_TXIFLSEL_7_8 0x00000004U /* Transmit FIFO becomes * . 7/8 full */ + +/* Interrupt Mask Set/Clear RW */ +#define FPL011IMSC_OEIM 0x400U /* Overrun error interrupt mask. */ +#define FPL011IMSC_BEIM 0x200U /* Break error interrupt mask */ +#define FPL011IMSC_PEIM 0x100U /* Parity error interrupt mask. */ +#define FPL011IMSC_FEIM 0x80U /* Framing error interrupt mask. */ +#define FPL011IMSC_RTIM 0x40U /* Receive timeout interrupt mask. */ +#define FPL011IMSC_TXIM 0x20U /* Transmit interrupt mask. */ +#define FPL011IMSC_RXIM 0x10U /* Receive interrupt mask. */ +#define FPL011IMSC_DSRMIM 0x8U /* nUARTDSR modem interrupt mask. */ +#define FPL011IMSC_DCDMIM 0x4U /* nUARTDCD modem interrupt mask. */ +#define FPL011IMSC_CTSMIM 0x2U /* nUARTCTS modem interrupt mask. */ +#define FPL011IMSC_RIMIM 0x1U /* nUARTRI modem interrupt mask. */ +#define FPL011IMSC_ALLM 0x7ffU /* all interrupt mask */ + +/* Raw interrupt Status RO */ + +#define FPL011RIS_OEIS 0x400U /* Overrun error interrupt mask. */ +#define FPL011RIS_BEIS 0x200U /* Break error interrupt mask */ +#define FPL011RIS_PEIS 0x100U /* Parity error interrupt mask. */ +#define FPL011RIS_FEIS 0x80U /* Framing error interrupt mask. */ +#define FPL011RIS_RTIS 0x40U /* Receive timeout interrupt mask. */ +#define FPL011RIS_TXIS 0x20U /* Transmit interrupt mask. */ +#define FPL011RIS_RXIS 0x10U /* Receive interrupt mask. */ +#define FPL011RIS_DSRMIS 0x8U /* nUARTDSR modem interrupt mask. */ +#define FPL011RIS_DCDMIS 0x4U /* nUARTDCD modem interrupt mask. */ +#define FPL011RIS_CTSMIS 0x2U /* nUARTCTS modem interrupt mask. */ +#define FPL011RIS_RIMIS 0x1U /* nUARTRI modem interrupt mask. */ + +/* Mask interrupt Status R0 */ + +#define FPL011MIS_OEMIS 0x400U /* Overrun error interrupt mask. */ +#define FPL011MIS_BEMIS 0x200U /* Break error interrupt mask */ +#define FPL011MIS_PEMIS 0x100U /* Parity error interrupt mask. */ +#define FPL011MIS_FEMIS 0x80U /* Framing error interrupt mask. */ +#define FPL011MIS_RTMIS 0x40U /* Receive timeout interrupt mask. */ +#define FPL011MIS_TXMIS 0x20U /* Transmit interrupt mask. */ +#define FPL011MIS_RXMIS 0x10U /* Receive interrupt mask. */ +#define FPL011MIS_DSRMMIS 0x8U /* nUARTDSR modem interrupt mask. */ +#define FPL011MIS_DCDMMIS 0x4U /* nUARTDCD modem interrupt mask. */ +#define FPL011MIS_CTSMMIS 0x2U /* nUARTCTS modem interrupt mask. */ +#define FPL011MIS_RIMMIS 0x1U /* nUARTRI modem interrupt mask. */ + +/* Interrupt Clear WO */ +#define FPL011ICR_OEIC 0x400U /* Overrun error interrupt mask. */ +#define FPL011ICR_BEIC 0x200U /* Break error interrupt mask */ +#define FPL011ICR_PEIC 0x100U /* Parity error interrupt mask. */ +#define FPL011ICR_FEIC 0x80U /* Framing error interrupt mask. */ +#define FPL011ICR_RTIC 0x40U /* Receive timeout interrupt mask. */ +#define FPL011ICR_TXIC 0x20U /* Transmit interrupt mask. */ +#define FPL011ICR_RXIC 0x10U /* Receive interrupt mask. */ +#define FPL011ICR_DSRMIC 0x8U /* nUARTDSR modem interrupt mask. */ +#define FPL011ICR_DCDMIC 0x4U /* nUARTDCD modem interrupt mask. */ +#define FPL011ICR_CTSMIC 0x2U /* nUARTCTS modem interrupt mask. */ +#define FPL011ICR_RIMIC 0x1U /* nUARTRI modem interrupt mask. */ +#define FPL011ICR_ALL_CLEAR (FPL011ICR_OEIC |FPL011ICR_BEIC |FPL011ICR_PEIC |FPL011ICR_FEIC |FPL011ICR_RTIC |FPL011ICR_TXIC |FPL011ICR_RXIC |FPL011ICR_DSRMIC |FPL011ICR_DCDMIC |FPL011ICR_CTSMIC |FPL011ICR_RIMIC ) + +/* DMA Control RW */ +#define FPL011DMACR_DMAONERR 0x4U /* DMA on error. */ +#define FPL011DMACR_TXDMAE 0x2U /* Transmit DMA enable. */ +#define FPL011DMACR_RXDMAE 0x1U /* Receive DMA enable. */ + +/***************** Macros (Inline Functions) Definitions *********************/ + +#define FUART_READREG32(addr, reg_offset) FtIn32(addr + (u32)reg_offset) +#define FUART_WRITEREG32(addr, reg_offset, reg_value) FtOut32(addr + (u32)reg_offset, (u32)reg_value) + +/** + * @name: FUART_ISRECEIVEDATA + * @msg: Used to confirm whether data has been received + * @param addr contains the base address of the device. + * @return {bool} true 是存在数据 , false 是不存在数据 + * + */ +#define FUART_ISRECEIVEDATA(addr) (FtIn32(addr + FPL011FTR_OFFSET) & FPL011FTR_RXFE) + +/** + * @name: FUART_ISTRANSMITFULL + * @msg: Used to confirm whether data can be sent + * @param addr contains the base address of the device. + * @return {bool} true 是数据已满 , false 可以发送数据 + */ +#define FUART_ISTRANSMITFULL(addr) ((FtIn32(addr + FPL011FTR_OFFSET) & (u32)FPL011FTR_TXFF) == FPL011FTR_TXFF) + + +/** + * @name: FUART_ISTRANSMITBUSY + * @msg: Determine if a byte of data can be sent with the transmitter. + * @return TRUE if the TX is busy, FALSE if a byte can be put in the + * FIFO. + */ +#define FUART_ISTRANSMITBUSY(addr) ((FtIn32(addr + FPL011FTR_OFFSET) & (u32)FPL011FTR_BUSY) == FPL011FTR_BUSY) + +/************************** Function Prototypes ******************************/ + +void FPl011SendByte(u32 addr, u8 byte); +u8 FPl011RecvByte(u32 addr); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsp/phytium/libraries/standalone/drivers/serial/fpl011/fpl011_intr.c b/bsp/phytium/libraries/standalone/drivers/serial/fpl011/fpl011_intr.c new file mode 100644 index 0000000000..de69f982ae --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/serial/fpl011/fpl011_intr.c @@ -0,0 +1,234 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fpl011_intr.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:06:30 + * Description:  This files is for uart irq functions + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +/***************************** Include Files *********************************/ + +#include "fpl011.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Variable Definitions *****************************/ + +/************************** Function Prototypes ******************************/ +/*****************************************************************************/ + +extern u32 FPl011SendBuffer(FPl011 *uart_p); +extern u32 FPl011ReceiveBuffer(FPl011 *uart_p); + +static void FPl011ReceiveErrorHandler(FPl011 *uart_p, u32 InterruptStatus); +static void FPl011ReceiveDataHandler(FPl011 *uart_p); +static void FPl011ReceiveTimeoutHandler(FPl011 *uart_p); +static void FPl011SendDataHandler(FPl011 *uart_p, u32 InterruptStatus); + +/** + * @name: FPl011GetInterruptMask + * @msg: 此函数获取所有串口中断的mask。 + * @param uart_p + * @return mask + */ +/** + * @name: FPl011GetInterruptMask + * @msg: This function gets the interrupt mask. + * @param uart_p is a pointer to the uart instance + * @return {*} + */ +u32 FPl011GetInterruptMask(FPl011 *uart_p) +{ + FASSERT(uart_p != NULL); + return FUART_READREG32(uart_p->config.base_address, FPL011IMSC_OFFSET); +} + + +/** + * @name: FPl011SetInterruptMask + * @msg: This function sets the interrupt mask. + * @param uart_p is a pointer to the uart instance + * @param mask contains the interrupts to be enabled or disabled. + * A '1' enables an interrupt, and a '0' disables. + */ +void FPl011SetInterruptMask(FPl011 *uart_p, u32 mask) +{ + u32 temp_mask = mask; + FASSERT(uart_p != NULL); + + temp_mask &= FPL011IMSC_ALLM; + + FUART_WRITEREG32(uart_p->config.base_address, FPL011IMSC_OFFSET, temp_mask); +} + +/** + * @name: FPl011SetHandler + * @msg: This function sets the handler that will be called when an event (interrupt) + * occurs that needs application's attention. + * @param uart_p is a pointer to the uart instance + * @param fun_p is the pointer to the callback function. + * @param args is the upper layer callback reference passed back + * when the callback function is invoked. + * @return {*} + */ +void FPl011SetHandler(FPl011 *uart_p, FPl011EventHandler fun_p, void *args) +{ + FASSERT(uart_p != NULL); + FASSERT(fun_p != NULL); + FASSERT(uart_p->is_ready == FT_COMPONENT_IS_READY); + + uart_p->handler = fun_p; + uart_p->args = args; +} + + +/** + * @name: FPl011InterruptHandler + * @msg: This function is the interrupt handler for the driver. + * It must be connected to an interrupt system by the application such that it + * can be called when an interrupt occurs. + * @param vector Irq num ,Don't need attention . + * @param param contains a pointer to the driver instance + */ +void FPl011InterruptHandler(s32 vector, void *param) +{ + FPl011 *uart_p = (FPl011 *)param; + u32 reg_value = 0; + FASSERT(uart_p != NULL); + FASSERT(uart_p->is_ready == FT_COMPONENT_IS_READY); + + reg_value = FUART_READREG32(uart_p->config.base_address, FPL011IMSC_OFFSET); + reg_value &= FUART_READREG32(uart_p->config.base_address, FPL011MIS_OFFSET); + + if ((reg_value & ((u32)FPL011MIS_RXMIS)) != (u32)0) + { + /* Received data interrupt */ + FPl011ReceiveDataHandler(uart_p); + } + + if ((reg_value & ((u32)FPL011MIS_TXMIS)) != (u32)0) + { + /* Transmit data interrupt */ + FPl011SendDataHandler(uart_p, reg_value); + } + + if (((reg_value) & ((u32)FPL011MIS_OEMIS | (u32)FPL011MIS_BEMIS | (u32)FPL011MIS_PEMIS | (u32)FPL011MIS_FEMIS)) != (u32)0) + { + /* Received Error Status interrupt */ + FPl011ReceiveErrorHandler(uart_p, reg_value); + } + + if ((reg_value & ((u32)FPL011MIS_RTMIS)) != (u32)0) + { + /* Received Timeout interrupt */ + FPl011ReceiveTimeoutHandler(uart_p); + } + + if (((reg_value) & ((u32)FPL011MIS_DSRMMIS | (u32)FPL011MIS_DCDMMIS | (u32)FPL011MIS_CTSMMIS | (u32)FPL011MIS_RIMMIS)) != (u32)0) + { + /* Modem status interrupt */ + } + + /* Clear the interrupt status. */ + FUART_WRITEREG32(uart_p->config.base_address, FPL011ICR_OFFSET, + reg_value); + +} + +static void FPl011ReceiveErrorHandler(FPl011 *uart_p, u32 InterruptStatus) +{ + uart_p->rxbs_error = 0; + + if (((InterruptStatus) & ((u32)FPL011MIS_OEMIS | (u32)FPL011MIS_BEMIS | (u32)FPL011MIS_PEMIS | (u32)FPL011MIS_FEMIS)) != 0) + { + uart_p->rxbs_error = 1; + } + + (void)FPl011ReceiveBuffer(uart_p); + + if (0 == uart_p->rxbs_error) + { + if (uart_p->handler) + { + uart_p->handler(uart_p->args, FPL011_EVENT_RECV_ERROR, uart_p->receive_buffer.requested_bytes - uart_p->receive_buffer.remaining_bytes); + } + } +} + +/** + * @name: FPl011ReceiveDataHandler + * @msg: + * @param {*} + * @return {*} + */ +static void FPl011ReceiveDataHandler(FPl011 *uart_p) +{ + if ((u32)0 != uart_p->receive_buffer.remaining_bytes) + { + (void)FPl011ReceiveBuffer(uart_p); + } + + if ((u32)0 == uart_p->receive_buffer.remaining_bytes) + { + if (uart_p->handler) + { + uart_p->handler(uart_p->args, FPL011_EVENT_RECV_DATA, uart_p->receive_buffer.requested_bytes - uart_p->receive_buffer.remaining_bytes); + } + } +} + +static void FPl011ReceiveTimeoutHandler(FPl011 *uart_p) +{ + u32 event; + + if ((u32)0 != uart_p->receive_buffer.remaining_bytes) + { + (void)FPl011ReceiveBuffer(uart_p); + } + + if ((u32)0 == uart_p->receive_buffer.remaining_bytes) + { + event = FPL011_EVENT_RECV_TOUT; + } + else + { + event = FPL011_EVENT_RECV_DATA; + } + + if (uart_p->handler) + { + uart_p->handler(uart_p->args, event, uart_p->receive_buffer.requested_bytes - uart_p->receive_buffer.remaining_bytes); + } +} + +static void FPl011SendDataHandler(FPl011 *uart_p, u32 InterruptStatus) +{ + FPl011SendBuffer(uart_p); + if (uart_p->send_buffer.remaining_bytes == (u32)0) + { + if (uart_p->handler) + { + uart_p->handler(uart_p->args, FPL011_EVENT_SENT_DATA, uart_p->send_buffer.requested_bytes); + } + } +} diff --git a/bsp/phytium/libraries/standalone/drivers/serial/fpl011/fpl011_options.c b/bsp/phytium/libraries/standalone/drivers/serial/fpl011/fpl011_options.c new file mode 100644 index 0000000000..4e4584d077 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/serial/fpl011/fpl011_options.c @@ -0,0 +1,482 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fpl011_options.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:06:45 + * Description:  This files is for uart option setting + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +/***************************** Include Files *********************************/ + +#include "fpl011.h" +#include "fpl011_hw.h" +#include "ftypes.h" + +/************************** Variable Definitions ****************************/ +/************************** Constant Definitions *****************************/ +/**************************** Type 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 register_offset; + u32 mask; +} Mapping; + +static Mapping option_table[] = +{ + {FPL011_OPTION_UARTEN, FPL011CR_OFFSET, FPL011CR_UARTEN}, + {FPL011_OPTION_RXEN, FPL011CR_OFFSET, FPL011CR_RXE}, + {FPL011_OPTION_TXEN, FPL011CR_OFFSET, FPL011CR_TXE}, + {FPL011_OPTION_FIFOEN, FPL011LCR_H_OFFSET, FPL011LCR_H_FEN}, + {FPL011_OPTION_RTS, FPL011CR_OFFSET, FPL011CR_RTS}, + {FPL011_OPTION_DTR, FPL011CR_OFFSET, FPL011CR_DTR}, + {FPL011_OPTION_RTSEN, FPL011CR_OFFSET, FPL011CR_RTSEN}, + {FPL011_OPTION_CTSEN, FPL011CR_OFFSET, FPL011CR_CTSEN}, + {FPL011_OPTION_TXDMAEN, FPL011DMACR_OFFSET, FPL011DMACR_TXDMAE}, + {FPL011_OPTION_RXDMAEN, FPL011DMACR_OFFSET, FPL011DMACR_RXDMAE} +}; + +/***************** Macros (Inline Functions) Definitions *********************/ + +#define FUART_NUM_OPITIONS (sizeof(option_table) / sizeof(Mapping)) +/************************** Function Prototypes ******************************/ +/*****************************************************************************/ +/** + * @name: FPl011SetOptions + * @msg: Sets the options for the specified driver instance. The options are implemented as bit masks such that multiple options may be enabled or disabled simultaneously. + * @param uart_p is a pointer to the uart instance. + * @param options contains the options to be set which are bit masks + * contained in the file FPl011_uart.h and named FUART_OPTION_*. + */ +void FPl011SetOptions(FPl011 *uart_p, u32 options) +{ + u32 index; + u32 reg_value; + FASSERT(uart_p != NULL); + FASSERT(uart_p->is_ready == FT_COMPONENT_IS_READY); + + for (index = 0; index < FUART_NUM_OPITIONS; index++) + { + reg_value = FUART_READREG32(uart_p->config.base_address, option_table[index].register_offset); + + if ((options & option_table[index].option) != (u32)(0)) + { + reg_value |= option_table[index].mask; + } + else + { + reg_value &= ~option_table[index].mask; + } + + FUART_WRITEREG32(uart_p->config.base_address, option_table[index].register_offset, reg_value); + } +} + +/** + * @name: FPl011SetSpecificOptions + * @msg: Sets the options for the specified driver instance. + * @param {FPl011} *uart_p is a pointer to the uart instance. + * @param {u32} options contains the options to be set which are bit masks + * contained in the file FPl011_uart.h and named FUART_OPTION_*. + */ +void FPl011SetSpecificOptions(FPl011 *uart_p, u32 options) +{ + u32 index; + u32 reg_value; + FASSERT(uart_p != NULL); + + for (index = 0; index < FUART_NUM_OPITIONS; index++) + { + if ((options & option_table[index].option) == (u32)(0)) + continue; + reg_value = FUART_READREG32(uart_p->config.base_address, option_table[index].register_offset); + + /* set specific options */ + reg_value |= option_table[index].mask; + FUART_WRITEREG32(uart_p->config.base_address, option_table[index].register_offset, reg_value); + } +} + +/** + * @name: FPl011SetSpecificOptions + * @msg: Clear the options for the specified driver instance. + * @param uart_p is a pointer to the uart instance. + * @param options contains the options to be set which are bit masks + * contained in the file FPl011_uart.h and named FUART_OPTION_*. + */ +void FPl011ClearSpecificOptions(FPl011 *uart_p, u32 options) +{ + u32 index; + u32 reg_value; + FASSERT(uart_p != NULL); + + for (index = 0; index < FUART_NUM_OPITIONS; index++) + { + if ((options & option_table[index].option) == (u32)(0)) + continue; + reg_value = FUART_READREG32(uart_p->config.base_address, option_table[index].register_offset); + + /* remove specific options */ + reg_value &= ~option_table[index].mask; + FUART_WRITEREG32(uart_p->config.base_address, option_table[index].register_offset, reg_value); + } +} + + + +/** + * @name: FPl011SetDataFormat + * @msg: Sets the data format for the specified UART. + * @param uart_p is a pointer to the uart instance. + * @param format_p is a pointer to a format structure that will + * contain the data format after this call completes. + * @return + * FT_SUCCESS if everything configured as expected + * FPL011_ERROR_PARAM if one of the parameters was not valid. + */ +FError FPl011SetDataFormat(FPl011 *uart_p, FPl011Format *format_p) +{ + FError ret ; + u32 line_ctrl_reg ; + FPl011Config *config_p; + FASSERT(uart_p != NULL); + FASSERT(uart_p->is_ready == FT_COMPONENT_IS_READY); + FASSERT(format_p != NULL) ; + + config_p = &uart_p->config; + + if ((format_p->data_bits > ((u32)(FPL011_FORMAT_WORDLENGTH_8BIT))) || + (format_p->parity > ((u32)(FPL011_FORMAT_PARITY_MASK))) || + (format_p->stopbits > ((u32)(FPL011_FORMAT_PARITY_MASK))) + ) + { + return FPL011_ERROR_PARAM ; + } + else + { + /* + * Try to set the baud rate and if it's not successful then + * don't continue altering the data format, this is done + * first to avoid the format from being altered when an + * error occurs + */ + ret = FPl011SetBaudRate(uart_p, format_p->baudrate) ; + if (ret != FT_SUCCESS) + { + + } + else + { + line_ctrl_reg = FUART_READREG32(config_p->base_address, FPL011LCR_H_OFFSET); + + /* + * Set the length of data (8,7,6) by first clearing + * out the bits that control it in the register, + * then set the length in the register + */ + line_ctrl_reg &= ~(u32)FPL011LCR_H_WLEN ; + line_ctrl_reg |= (format_p->data_bits << + FPL011LCR_H_WLEN_SHIFT); + + /* + * Set the number of stop bits in the mode register by + * first clearing out the bits that control it in the + * register, then set the number of stop bits in the + * register. + */ + line_ctrl_reg &= ~FPL011LCR_H_STP_MASK; + line_ctrl_reg |= (format_p->stopbits << + FPL011LCR_H_STP_SHIFT); + + /* + * Set the parity by first clearing out the bits that + * control it in the register, then set the bits in + * the register, the default is no parity after + * clearing the register bits + */ + line_ctrl_reg &= ~FPL011LCR_H_PARITY_MASK; + line_ctrl_reg |= ((format_p->parity & + FPL011_FORMAT_EN_PARITY) << + FPL011LCR_H_PARITY_SHIFT); + + /* Even/Odd parity set */ + line_ctrl_reg |= ((format_p->parity & + FPL011_FORMAT_EVEN_PARITY) << + FPL011_FORMAT_EVEN_PARITY_SHIFT); + + /* Stick parity enable/disable */ + line_ctrl_reg |= ((format_p->parity & + FPL011_FORMAT_EN_STICK_PARITY) << + FPL011_FORMAT_EN_STICK_PARITY_SHIFT); + + /* Update the Line control register */ + FUART_WRITEREG32(config_p->base_address, FPL011LCR_H_OFFSET, line_ctrl_reg) ; + + return FT_SUCCESS ; + } + } + + return ret ; +} + +/** + * @name: FPl011GetDataFormat + * @msg: Gets the data format for the specified UART. + * @param uart_p is a pointer to the uart instance. + * @param format_p is a pointer to a format structure that will + * contain the data format after this call completes. + */ +void FPl011GetDataFormat(FPl011 *uart_p, FPl011Format *format_p) +{ + u32 line_ctrl_reg ; + FPl011Config *config_p; + /* Assert validates the input arguments */ + FASSERT(uart_p != NULL); + FASSERT(uart_p->is_ready == FT_COMPONENT_IS_READY); + FASSERT(format_p != NULL) ; + + config_p = &uart_p->config; + /* + * Get the baud rate from the instance, this is not retrieved from + * the hardware because it is only kept as a divisor such that it + * is more difficult to get back to the baud rate + */ + format_p->baudrate = uart_p->config.baudrate ; + + line_ctrl_reg = FUART_READREG32(config_p->base_address, FPL011LCR_H_OFFSET); + + /* Get the length of data (8,7,6,5) */ + format_p->data_bits = ((line_ctrl_reg & FPL011LCR_H_WLEN) >> FPL011LCR_H_WLEN_SHIFT) ; + + /* Get the number of stop bits */ + format_p->stopbits = (u8)((line_ctrl_reg & FPL011LCR_H_STP_MASK) >> FPL011LCR_H_STP_SHIFT) ; + + /* Determine what parity is */ + format_p->parity = (u32)((line_ctrl_reg & (u32)FPL011LCR_H_PARITY_MASK) >> FPL011LCR_H_PARITY_SHIFT) ; +} + + + +/** + * @name: FPl011SetTxFifoThreadHold + * @msg: This functions sets the Tx FIFO trigger level to the 'TriggerLevel' + * argument. + * @param uart_p is a pointer to the uart instance. + * @param trigger_level contains the trigger level to set. This is a value + * from 0-32 (FPL011IFLS_TXIFLSEL_1_8 - FPL011IFLS_TXIFLSEL_7_8) + */ +void FPl011SetTxFifoThreadHold(FPl011 *uart_p, u8 trigger_level) +{ + u32 fifo_trig_reg; + FPl011Config *config_p; + FASSERT(uart_p != NULL); + FASSERT(trigger_level <= (u8)FPL011IFLS_TXIFLSEL_MASK) ; + FASSERT(uart_p->is_ready == FT_COMPONENT_IS_READY); + config_p = &uart_p->config; + + trigger_level = trigger_level & (u8)FPL011IFLS_TXIFLSEL_MASK; + + fifo_trig_reg = FUART_READREG32(config_p->base_address, + FPL011IFLS_OFFSET); + + fifo_trig_reg &= ~(FPL011IFLS_TXIFLSEL_MASK | FPL011IFLS_RXIFLSEL_MASK); + + fifo_trig_reg |= (u32)trigger_level; + + /* + * Write the new value for the FIFO control register to it such that + * the threshold is changed + */ + FUART_WRITEREG32(config_p->base_address, + FPL011IFLS_OFFSET, fifo_trig_reg); +} + + + +/** + * @name: FPl011SetRxFifoThreadhold + * @msg: This functions sets the Rx FIFO trigger level to the 'TriggerLevel' + * argument. + * @param uart_p is a pointer to the uart instance. + * @param trigger_level contains the trigger level to set. This is a value + * from 0-32 (FPL011IFLS_RXIFLSEL_1_8 - FPL011IFLS_RXIFLSEL_7_8) + */ +void FPl011SetRxFifoThreadhold(FPl011 *uart_p, u8 trigger_level) +{ + u32 fifo_trig_reg; + FPl011Config *config_p; + FASSERT(uart_p != NULL); + FASSERT(trigger_level <= (u8)FPL011IFLS_RXIFLSEL_MASK) ; + FASSERT(uart_p->is_ready == FT_COMPONENT_IS_READY); + config_p = &uart_p->config; + + trigger_level = trigger_level & (u8)FPL011IFLS_RXIFLSEL_MASK; + + fifo_trig_reg = FUART_READREG32(config_p->base_address, + FPL011IFLS_OFFSET); + + fifo_trig_reg &= ~FPL011IFLS_RXIFLSEL_MASK; + + fifo_trig_reg |= (u32)trigger_level ; + + /* + * Write the new value for the FIFO control register to it such that + * the threshold is changed + */ + FUART_WRITEREG32(config_p->base_address, + FPL011IFLS_OFFSET, fifo_trig_reg); +} + +/** + * @name: FPl011SetBaudRate + * @msg: Sets the baud rate for the device. + * @param uart_p is a pointer to the FPl011 instance + * @param BaudRate to be set + * @return + * FT_SUCCESS if everything configured as expected + * FPL011_ERROR_PARAM if the requested rate is not available + * because there was too much error + */ +FError FPl011SetBaudRate(FPl011 *uart_p, u32 baudrate) +{ + u32 temp; + u32 divider; + u32 remainder; + u32 fraction; + + FASSERT(NULL != uart_p); + if ((baudrate * 2) > uart_p->config.ref_clock_hz) + { + return FPL011_ERROR_PARAM; + } + + /* calculate baud rate divisor */ + temp = 16 * baudrate; + divider = uart_p->config.ref_clock_hz / temp; + remainder = uart_p->config.ref_clock_hz % temp; + temp = (128 * remainder) / temp; + fraction = temp / 2; + + if (0 != (temp & 1)) + { + fraction++; + } + + FPl011ClearSpecificOptions(uart_p, FPL011_OPTION_RXEN | FPL011_OPTION_TXEN); + /* set baud register */ + FUART_WRITEREG32(uart_p->config.base_address, FPL011IBRD_OFFSET, divider); + FUART_WRITEREG32(uart_p->config.base_address, FPL011FBRD_OFFSET, fraction); + FPl011SetSpecificOptions(uart_p, FPL011_OPTION_RXEN | FPL011_OPTION_TXEN); + uart_p->config.baudrate = baudrate; + return FT_SUCCESS; +} + +/** + * @name: FPl011ProgramCtlReg + * @msg: This function reprograms the control register according to the following + * sequence mentioned in the TRM + * @param uart_p is a pointer to the FPl011 instance + * @param ctrl_reg value to be written + */ +void FPl011ProgramCtlReg(FPl011 *uart_p, u32 ctrl_reg) +{ + u32 line_ctrl_reg; + u32 temp_ctrl_reg; + u32 isbusy; + u32 addr = uart_p->config.base_address; + FASSERT(uart_p); + + /* + * Check is TX completed. If Uart is disabled in the middle, cannot + * recover. So, keep this check before disable. + */ + isbusy = FUART_ISTRANSMITBUSY(addr); + while (isbusy == (u32)TRUE) + { + isbusy = (u32)FUART_ISTRANSMITBUSY(addr); + } + + /* Disable UART */ + temp_ctrl_reg = FUART_READREG32(addr, FPL011CR_OFFSET); + temp_ctrl_reg &= (~FPL011CR_UARTEN); + FUART_WRITEREG32(addr, FPL011CR_OFFSET, temp_ctrl_reg); + + /* + * Flush the transmit FIFO by setting the FEN bit to 0 in the + * Line Control Register + */ + line_ctrl_reg = FUART_READREG32(addr, FPL011LCR_H_OFFSET); + line_ctrl_reg &= ~FPL011LCR_H_FEN; + FUART_WRITEREG32(addr, FPL011LCR_H_OFFSET, line_ctrl_reg); + + /* Setup the Control Register with the passed argument.*/ + FUART_WRITEREG32(addr, FPL011CR_OFFSET, ctrl_reg); + + /* By default, driver works in FIFO mode, so set FEN as it is + * cleared above + */ + line_ctrl_reg |= FPL011LCR_H_FEN; + FUART_WRITEREG32(addr, FPL011LCR_H_OFFSET, line_ctrl_reg); + + /* Enable UART */ + temp_ctrl_reg = FUART_READREG32(addr, FPL011CR_OFFSET); + temp_ctrl_reg |= FPL011CR_UARTEN; + FUART_WRITEREG32(addr, FPL011CR_OFFSET, temp_ctrl_reg); +} + +/** + * @name: FPl011SetOperMode + * @msg: This function sets the operational mode of the UART. The UART can operate + * in one of four modes: Normal, Local Loopback. + * @param uart_p is a pointer to the FPl011 instance. + * @param operation_mode is the mode of the UART. + */ +void FPl011SetOperMode(FPl011 *uart_p, u8 operation_mode) +{ + u32 ctrl_reg; + FPl011Config *config_p; + FASSERT(uart_p != NULL); + FASSERT(operation_mode <= (u8)FPL011_OPER_MODE_LOCAL_LOOP) ; + FASSERT(uart_p->is_ready == FT_COMPONENT_IS_READY); + config_p = &uart_p->config; + + ctrl_reg = FUART_READREG32(config_p->base_address, FPL011CR_OFFSET) ; + + /* Set the correct value by masking the bits, then ORing the const. */ + ctrl_reg &= ~(u32)FPL011CR_LBE; + + switch (operation_mode) + { + case FPL011_OPER_MODE_NORMAL: + ctrl_reg |= FPL011CR_MODE_NORMAL; + break; + case FPL011_OPER_MODE_LOCAL_LOOP: + ctrl_reg |= FPL011CR_LBE; + break; + default: + break; + } + + /* Setup the Control Register with the passed argument.*/ + FPl011ProgramCtlReg(uart_p, ctrl_reg); +} diff --git a/bsp/phytium/libraries/standalone/drivers/serial/fpl011/fpl011_sinit.c b/bsp/phytium/libraries/standalone/drivers/serial/fpl011/fpl011_sinit.c new file mode 100644 index 0000000000..ae9f4834e7 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/serial/fpl011/fpl011_sinit.c @@ -0,0 +1,62 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fpl011_sinit.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:07:00 + * Description:  This files is for uart static init + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +/***************************** Include Files *********************************/ + +#include "fpl011.h" +#include "fparameters.h" +#include "sdkconfig.h" + +extern FPl011Config FPl011ConfigTable[FUART_NUM]; +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/*****************************************************************************/ +/** + * @name: FPl011LookupConfig + * @msg: Initializes a specific FPl011 instance such that it is ready to be used. + * @param contains the ID of the device + * @return {FPl011Config *} A pointer to the configuration structure or NULL if the + * specified device is not in the system. + */ +const FPl011Config *FPl011LookupConfig(u32 instance_id) +{ + const FPl011Config *cfg_ptr = NULL; + u32 index; + + for (index = 0; index < (u32)FUART_NUM; index++) + { + if (FPl011ConfigTable[index].instance_id == instance_id) + { + cfg_ptr = &FPl011ConfigTable[index]; + break; + } + } + + return (const FPl011Config *)cfg_ptr; +} diff --git a/bsp/phytium/libraries/standalone/drivers/spi/Kconfig b/bsp/phytium/libraries/standalone/drivers/spi/Kconfig new file mode 100644 index 0000000000..0e35640055 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/spi/Kconfig @@ -0,0 +1,11 @@ + +config USE_FSPIM + bool + prompt "Use FSPIM" + default n + depends on USE_SPI + depends on TARGET_F2000_4 || TARGET_D2000 || TARGET_E2000S || TARGET_E2000D || TARGET_E2000Q + help + Select FSPIM driver component + + diff --git a/bsp/phytium/libraries/standalone/drivers/spi/fspim/fspim.c b/bsp/phytium/libraries/standalone/drivers/spi/fspim/fspim.c new file mode 100644 index 0000000000..b2694483ad --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/spi/fspim/fspim.c @@ -0,0 +1,620 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fspim.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:08:32 + * Description:  This files is for spim api implementation + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2021-12-3 init commit + * 1.1 zhugengyu 2022-4-15 support test mode + * 1.2 zhugengyu 2022-5-13 support spi dma + */ + + +/***************************** Include Files *********************************/ + +#include +#include "fio.h" +#include "ferror_code.h" +#include "ftypes.h" +#include "fdebug.h" +#include "fspim_hw.h" +#include "fspim.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FSPIM_DEBUG_TAG "SPIM" +#define FSPIM_ERROR(format, ...) FT_DEBUG_PRINT_E(FSPIM_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSPIM_WARN(format, ...) FT_DEBUG_PRINT_W(FSPIM_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSPIM_INFO(format, ...) FT_DEBUG_PRINT_I(FSPIM_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSPIM_DEBUG(format, ...) FT_DEBUG_PRINT_D(FSPIM_DEBUG_TAG, format, ##__VA_ARGS__) + +/************************** Function Prototypes ******************************/ +FError FSpimReset(FSpim *instance_p); + +/************************** Variable Definitions *****************************/ +static const char *FSPIM_ERROR_CODE_MSG[FSPIM_NUM_OF_ERR_CODE] = +{ + "FSPIM_SUCCESS : fspim success", + "FSPIM_ERR_INVAL_STATE : fspim invalid state", + "FSPIM_ERR_NOT_READY : fspim driver not ready", + "FSPIM_ERR_INVAL_PARAM : fspim invalid input parameters", + "FSPIM_ERR_BUS_BUSY : fspim bus is busy", + "FSPIM_ERR_NOT_SUPPORT : fspim not support operation", + "FSPIM_ERR_TIMEOUT : fspim wait timeout", + "FSPIM_ERR_TRANS_FAIL : fspim data transfer failed", +}; + + +/*****************************************************************************/ + +/* 此文件主要为了完成用户对外接口,用户可以使用这些接口直接开始工作 */ + +/* - 包括用户API的定义和实现 + - 同时包含必要的OPTION方法,方便用户进行配置 + - 如果驱动可以直接进行I/O操作,在此源文件下可以将API 进行实现 */ + +/* + * @name: FSpimCfgInitialize + * @msg: Initializes a specific instance such that it is ready to be used. + * @param {FSpim} *instance_p FSPIM驱动控制数据 + * @param {FSpimConfig} *config_p FSPIM驱动配置数据 + * @return 驱动初始化的错误码信息,FSPIM_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + */ +FError FSpimCfgInitialize(FSpim *instance_p, const FSpimConfig *input_config_p) +{ + FASSERT(instance_p && input_config_p); + uintptr base_addr = instance_p->config.base_addr; + + FError ret = FSPIM_SUCCESS; + /* + * If the device is started, disallow the initialize and return a Status + * indicating it is started. This allows the user to de-initialize the device + * and reinitialize, but prevents a user from inadvertently + * initializing. + */ + if (FT_COMPONENT_IS_READY == instance_p->is_ready) + { + FSPIM_WARN("device is already initialized!!!"); + } + + /* + * Set default values and configuration data, including setting the + * callback handlers to stubs so the system will not crash should the + * application not assign its own callbacks. + */ + FSpimDeInitialize(instance_p); + instance_p->config = *input_config_p; + + /* + * Reset the device. + */ + ret = FSpimReset(instance_p); + if (FSPIM_SUCCESS == ret) + { + instance_p->is_ready = FT_COMPONENT_IS_READY; + } + + return ret; +} + +/** + * @name: FSpimDeInitialize + * @msg: DeInitialization function for the device instance + * @return {无} + * @param {FSpim} *instance_p FSPIM驱动控制数据 + */ +void FSpimDeInitialize(FSpim *instance_p) +{ + FASSERT(instance_p); + + instance_p->is_ready = 0; + memset(instance_p, 0, sizeof(*instance_p)); + + return; +} + +/** + * @name: FSpimReset + * @msg: 重置FSPIM控制器 + * @return {FError} FSPIM_SUCCESS表示重置成功,其它返回值表示重置失败 + * @param {FSpim} *instance_p + */ +FError FSpimReset(FSpim *instance_p) +{ + FASSERT(instance_p); + uintptr base_addr = instance_p->config.base_addr; + FError ret = FSPIM_SUCCESS; + u32 reg_val; + u32 fifo; + + /* 禁用SPI控制器 */ + FSpimSetEnable(base_addr, FALSE); + + /* 选择数据长度和帧格式 */ + reg_val = FSPIM_CTRL_R0_DFS(FSPIM_DEFAULT_DFS) | + FSPIM_CTRL_R0_FRF(FSPIM_DEFAULT_FRF) | + FSPIM_CTRL_R0_CFS(FSPIM_DEFAULT_CFS); + + if (instance_p->config.en_test) + { + reg_val |= FSPIM_CTRL_R0_SLV_SRL(FSPIM_SRL_TEST); /* 设置测试模式,TX Fifo和RX Fifo内部短接 */ + } + else + { + reg_val |= FSPIM_CTRL_R0_SLV_SRL(FSPIM_SRL_NORAML); /* 设置为正常模式 */ + } + + FSpimSetCtrlR0(base_addr, reg_val); + + /* 选择串行时钟极性和相位 */ + FSpimSetCpha(base_addr, instance_p->config.cpha); + FSpimSetCpol(base_addr, instance_p->config.cpol); + + /* 设置传输模式 */ + FSpimSetTransMode(base_addr, FSPIM_TRANS_MODE_RX_TX); + + /* 禁用slave */ + FSpimSetSlaveEnable(base_addr, FALSE); + + /* 禁用SPI 中断,设置slave设备 */ + FSpimMaskIrq(base_addr, FSPIM_IMR_ALL_BITS); + FSpimSelSlaveDev(base_addr, instance_p->config.slave_dev_id); + + /* 获取SPI RX/TX FIFO 深度 */ + if (0 == instance_p->tx_fifo_len) + { + fifo = FSpimGetTxFifoDepth(base_addr); + instance_p->tx_fifo_len = ((fifo == 1) ? 0 : fifo); + FSPIM_INFO("fifo depth %d tx_fifo_len %d", fifo, instance_p->tx_fifo_len); + } + + if (0 == instance_p->rx_fifo_len) + { + fifo = FSpimGetRxFifoDepth(base_addr); + instance_p->rx_fifo_len = ((fifo == 1) ? 0 : fifo); + FSPIM_INFO("fifo depth %d tx_fifo_len %d", fifo, instance_p->tx_fifo_len); + } + + FSPIM_WRITE_REG32(base_addr, FSPIM_DMA_CR_OFFSET, 0x0); /* disable ddma */ + + if (instance_p->config.en_dma) + { + /* recv data in continuous way */ + FSpimSetCtrlR1(base_addr, FSPIM_CTRL_R1_NDF_64KB); + + /* setup fifo threshold */ + FSpimSetRxFifoThreshold(base_addr, instance_p->rx_fifo_len); + FSpimSetTxFifoThreshold(base_addr, instance_p->tx_fifo_len); + + /* setup fifo DMA level to trigger interrupt */ + FSpimSetRxDMALevel(base_addr, FSPIM_RX_DMA_LEVEL); + FSpimSetTxDMALevel(base_addr, FSPIM_TX_DMA_LEVEL); + } + else + { + FSpimSetCtrlR1(base_addr, 0); + + FSpimSetRxFifoThreshold(base_addr, 0); + FSpimSetTxFifoThreshold(base_addr, 0); + FSpimSetRxDMALevel(base_addr, 0); + FSpimSetTxDMALevel(base_addr, 0); + } + + + ret = FSpimSetSpeed(base_addr, instance_p->config.max_freq_hz); + if (FSPIM_SUCCESS != ret) + return ret; + + FSPIM_WRITE_REG32(base_addr, FSPIM_RX_SAMPLE_DLY_OFFSET, FSPIM_DEFAULT_RSD); + + /* 使能SPI控制器 */ + FSpimSetEnable(base_addr, TRUE); + + return ret; +} + +/** + * @name: FSpimGetTxRound + * @msg: 计算当前FIFO支持的发送字节数 + * @return {fsize_t} 当前TX FIFO可以容纳的字节数 + * @param {FSpim} *instance_p + */ +static fsize_t FSpimGetTxRound(FSpim *instance_p) +{ + fsize_t data_width = instance_p->config.n_bytes; + uintptr base_addr = instance_p->config.base_addr; + fsize_t tx_left_round, tx_fifo_room, rx_tx_gap; + + tx_left_round = (fsize_t)(instance_p->tx_buff_end - instance_p->tx_buff) / data_width; + tx_fifo_room = instance_p->tx_fifo_len - + FSpimGetTxFifoLevel(base_addr); + rx_tx_gap = ((fsize_t)(instance_p->rx_buff_end - instance_p->rx_buff) - + (fsize_t)(instance_p->tx_buff_end - instance_p->tx_buff)) / data_width; + + FSPIM_DEBUG("tx_left_round: %d, tx_fifo_room: %d, gap: %d", + tx_left_round, + tx_fifo_room, + ((fsize_t)(instance_p->tx_fifo_len) - rx_tx_gap)); + return min3(tx_left_round, + tx_fifo_room, + ((fsize_t)(instance_p->tx_fifo_len) - rx_tx_gap)); +} + +/** + * @name: FSpimFifoTx + * @msg: 利用Fifo进行发送 + * @return {无} + * @param {FSpim} *instance_p + */ +void FSpimFifoTx(FSpim *instance_p) +{ + FASSERT(instance_p); + fsize_t tx_round = FSpimGetTxRound(instance_p); + FSPIM_DEBUG("tx round: %d", tx_round); + uintptr base_addr = instance_p->config.base_addr; + u32 data_width = instance_p->config.n_bytes; + u16 data = 0xff; + + while (tx_round) + { + if (instance_p->tx_buff_end - instance_p->length) + { + if (FSPIM_1_BYTE == data_width) + { + /* + * Data Transfer Width is Byte (8 bit). + */ + data = *(u8 *)(instance_p->tx_buff); + } + else if (FSPIM_2_BYTE == data_width) + { + /* + * Data Transfer Width is Half Word (16 bit). + */ + data = *(u16 *)(instance_p->tx_buff); + } + else + { + FASSERT(0); + } + } + + FSpimWriteData(base_addr, data); + FSPIM_DEBUG(" send 0x%x", data); + instance_p->tx_buff += data_width; + tx_round--; + } +} + +/** + * @name: FSpimGetRxRound + * @msg: 获取当前Fifo支持的接收字节数 + * @return {fsize_t} 当前RX FIFO可以容纳的字节数 + * @param {FSpim} *instance_p + */ +static fsize_t FSpimGetRxRound(FSpim *instance_p) +{ + fsize_t data_width = instance_p->config.n_bytes; + uintptr base_addr = instance_p->config.base_addr; + + fsize_t rx_left_round = (fsize_t)(instance_p->rx_buff_end - instance_p->rx_buff) / data_width; + + FSPIM_DEBUG("left round %d, rx level %d", rx_left_round, FSpimGetRxFifoLevel(base_addr)); + return min(rx_left_round, (fsize_t)FSpimGetRxFifoLevel(base_addr)); +} + +/** + * @name: FSpimFifoRx + * @msg: 利用Fifo进行接收 + * @return {无} + * @param {FSpim} *instance_p + */ +void FSpimFifoRx(FSpim *instance_p) +{ + FASSERT(instance_p); + fsize_t rx_round = FSpimGetRxRound(instance_p); + FSPIM_DEBUG("rx round: %d", rx_round); + uintptr base_addr = instance_p->config.base_addr; + u32 data_width = instance_p->config.n_bytes; + u16 data; + + while (rx_round) + { + data = FSpimReadData(base_addr); + if ((fsize_t)(instance_p->rx_buff_end - instance_p->length)) + { + if (FSPIM_1_BYTE == data_width) + { + /* + * Data Transfer Width is Byte (8 bit). + */ + *(u8 *)(instance_p->rx_buff) = (u8)data; + FSPIM_DEBUG(" recv 0x%x", *(u8 *)(instance_p->rx_buff)); + } + else if (FSPIM_2_BYTE == data_width) + { + /* + * Data Transfer Width is Half Word (16 bit). + */ + *(u16 *)(instance_p->rx_buff) = (u16)data; + FSPIM_DEBUG(" recv 0x%x", *(u16 *)(instance_p->rx_buff)); + } + else + { + FASSERT(0); + } + + } + + instance_p->rx_buff += data_width; + rx_round--; + } + + return; +} + +/** + * @name: FSpimTransferPollFifo + * @msg: 先发送后接收数据 (阻塞处理),利用Fifo进行处理 + * @return {FError} FSPIM_SUCCESS表示处理成功,其它返回值表示处理失败 + * @param {FSpim} *instance_p 驱动控制数据 + * @param {void} *tx_buf 写缓冲区,可以为空,为空时表示只关注读数据,此时驱动会发送0xff读数据 + * @param {void} *rx_buf 读缓冲区, 可以为空,为空时表示值关注写数据,此时SPI总线上返回的数据会被抛弃 + * @param {fsize_t} len 进行传输的长度,如果tx_buf或者rx_buf不为空,则两个buf的长度必须为len + - 使用此函数前需要确保FSPIM驱动初始化成功 + - 从函数不会使用中断,会按照TX FIFO的深度进行传输,每次发送填满TX FIFO后触发发送/接收动作 + */ +FError FSpimTransferPollFifo(FSpim *instance_p, const void *tx_buf, void *rx_buf, fsize_t len) +{ + FASSERT(instance_p); + u32 reg_val; + uintptr base_addr = instance_p->config.base_addr; + u32 data_width = instance_p->config.n_bytes; + u32 tx_level; + FError ret = FSPIM_SUCCESS; + + if (FT_COMPONENT_IS_READY != instance_p->is_ready) + { + FSPIM_ERROR("device is already initialized!!!"); + return FSPIM_ERR_NOT_READY; + } + + FSpimSetEnable(base_addr, FALSE); + + reg_val = FSpimGetCtrlR0(base_addr); + + reg_val &= ~FSPIM_CTRL_R0_DFS_MASK; + reg_val |= FSPIM_CTRL_R0_DFS((data_width << 3) - 1); + + reg_val &= ~FSPIM_CTRL_R0_TMOD_MASK; + if (tx_buf && rx_buf) + reg_val |= FSPIM_CTRL_R0_TMOD(FSPIM_TMOD_RX_TX); + else if (rx_buf) + reg_val |= FSPIM_CTRL_R0_TMOD(FSPIM_TMOD_RX_ONLY); + else + reg_val |= FSPIM_CTRL_R0_TMOD(FSPIM_TMOD_RX_TX); + + FSpimSetCtrlR0(base_addr, reg_val); + + FSpimMaskIrq(base_addr, FSPIM_IMR_ALL_BITS); + + instance_p->length = len; + instance_p->tx_buff = tx_buf; + instance_p->tx_buff_end = tx_buf + len; + instance_p->rx_buff = rx_buf; + instance_p->rx_buff_end = rx_buf + len; + FSPIM_DEBUG("tx buff@%p-%d, rx buff@%p-%d", + instance_p->tx_buff, len, + instance_p->rx_buff, len); + + FSpimSetEnable(base_addr, TRUE); + + do + { + FSpimFifoTx(instance_p); + FSpimFifoRx(instance_p); + } + while (instance_p->rx_buff_end > instance_p->rx_buff); + + return ret; +} + +/** + * @name: FSpimTransferByInterrupt + * @msg: 先发送后接收数据 (中断处理),利用Fifo进行处理 + * @return {FError} FSPIM_SUCCESS表示处理成功,其它返回值表示处理失败 + * @param {FSpim} *instance_p 驱动控制数据 + * @param {void} *tx_buf 写缓冲区 + * @param {void} *rx_buf 读缓冲区 + * @param {fsize_t} len 读写缓冲区长度 (必须相等) + */ +FError FSpimTransferByInterrupt(FSpim *instance_p, const void *tx_buf, void *rx_buf, fsize_t len) +{ + FASSERT(instance_p); + u32 reg_val; + uintptr base_addr = instance_p->config.base_addr; + u32 data_width = instance_p->config.n_bytes; + u32 tx_level; + + if (FT_COMPONENT_IS_READY != instance_p->is_ready) + { + FSPIM_ERROR("device is already initialized!!!"); + return FSPIM_ERR_NOT_READY; + } + + FSpimSetEnable(base_addr, FALSE); + + reg_val = FSpimGetCtrlR0(base_addr); + + reg_val &= ~FSPIM_CTRL_R0_DFS_MASK; + reg_val |= FSPIM_CTRL_R0_DFS((data_width << 3) - 1); + + reg_val &= ~FSPIM_CTRL_R0_TMOD_MASK; + if (tx_buf && rx_buf) + reg_val |= FSPIM_CTRL_R0_TMOD(FSPIM_TMOD_RX_TX); + else if (rx_buf) + reg_val |= FSPIM_CTRL_R0_TMOD(FSPIM_TMOD_RX_ONLY); + else + reg_val |= FSPIM_CTRL_R0_TMOD(FSPIM_TMOD_RX_TX); + + FSpimSetCtrlR0(base_addr, reg_val); + + FSpimMaskIrq(base_addr, FSPIM_IMR_ALL_BITS); + + instance_p->length = len; + instance_p->tx_buff = tx_buf; + instance_p->tx_buff_end = instance_p->tx_buff + len; + instance_p->rx_buff = rx_buf; + instance_p->rx_buff_end = instance_p->rx_buff + len; + + /* 设置中断触发的时机,fifo填满一半,或者所有的数据填完 */ + tx_level = min(instance_p->tx_fifo_len / 2, instance_p->length / data_width); + FSpimSetTxFifoThreshold(base_addr, tx_level); + FSpimUmaskIrq(base_addr, FSPIM_IMR_TXEIS | FSPIM_IMR_TXOIS | FSPIM_IMR_RXUIS | FSPIM_IMR_RXOIS); + + FSpimSetEnable(base_addr, TRUE); + + return FSPIM_SUCCESS; +} + +#ifdef FSPIM_VERSION_2 /* E2000 */ + +/** + * @name: FSpimTransferDMA + * @msg: 启动SPIM DMA数据传输 + * @return {FError} FSPIM_SUCCESS表示启动DMA传输成功,其它值表示失败 + * @param {FSpim} *instance_p, 驱动控制数据 + * @param {boolean} tx, TRUE: 启动发送DMA + * @param {boolean} rx, TRUE: 启动接收DMA + */ +FError FSpimTransferDMA(FSpim *instance_p, boolean tx, boolean rx) +{ + FASSERT(instance_p); + u32 reg_val; + uintptr base_addr = instance_p->config.base_addr; + u32 data_width = instance_p->config.n_bytes; + + if (FT_COMPONENT_IS_READY != instance_p->is_ready) + { + FSPIM_ERROR("device is not yet initialized!!!"); + return FSPIM_ERR_NOT_READY; + } + + FSpimSetEnable(base_addr, FALSE); + + /* set up spim transfer mode */ + reg_val = FSpimGetCtrlR0(base_addr); + reg_val &= ~FSPIM_CTRL_R0_DFS_MASK; + reg_val |= FSPIM_CTRL_R0_DFS((data_width << 3) - 1); + + reg_val &= ~FSPIM_CTRL_R0_TMOD_MASK; + if (tx && rx) + reg_val |= FSPIM_CTRL_R0_TMOD(FSPIM_TMOD_RX_TX); + else if (rx) + reg_val |= FSPIM_CTRL_R0_TMOD(FSPIM_TMOD_RX_ONLY); + else + reg_val |= FSPIM_CTRL_R0_TMOD(FSPIM_TMOD_RX_TX); + + FSpimSetCtrlR0(base_addr, reg_val); + + FSpimMaskIrq(base_addr, FSPIM_IMR_ALL_BITS); /* mask all interrupts */ + + FSpimSetEnable(base_addr, TRUE); + + /* enable DMA tx / rx */ + reg_val = FSPIM_READ_REG32(base_addr, FSPIM_DMA_CR_OFFSET); + if (tx) + reg_val |= FSPIM_DMA_CR_TDMAE; + else + reg_val &= ~FSPIM_DMA_CR_TDMAE; + + if (rx) + reg_val |= FSPIM_DMA_CR_RDMAE; + else + reg_val &= ~FSPIM_DMA_CR_RDMAE; + FSPIM_WRITE_REG32(base_addr, FSPIM_DMA_CR_OFFSET, reg_val); + + FSpimSelSlaveDev(base_addr, instance_p->config.slave_dev_id); + + return FSPIM_SUCCESS; +} + +/** + * @name: FSpimSetChipSelection + * @msg: 设置片选信号 + * @return {NONE} + * @param {FSpim} *instance_p, 驱动控制数据 + * @param {boolean} on, TRUE: 片选打开, FALSE: 片选关闭 + */ +void FSpimSetChipSelection(FSpim *instance_p, boolean on) +{ + FASSERT(instance_p); + u32 reg_val; + FSpimSlaveDevice cs_n = instance_p->config.slave_dev_id; + uintptr base_addr = instance_p->config.base_addr; + if (FT_COMPONENT_IS_READY != instance_p->is_ready) + { + FSPIM_ERROR("device is not yet initialized!!!"); + return; + } + + reg_val = FSPIM_READ_REG32(base_addr, FSPIM_CS_OFFSET); + + if (on) + { + reg_val |= FSPIM_CHIP_SEL_EN((u32)cs_n); + reg_val |= FSPIM_CHIP_SEL((u32)cs_n); + } + else + { + reg_val &= ~FSPIM_CHIP_SEL_EN((u32)cs_n); + reg_val &= ~FSPIM_CHIP_SEL((u32)cs_n); + } + + FSPIM_WRITE_REG32(base_addr, FSPIM_CS_OFFSET, reg_val); + + return; +} +#endif + +/** + * @name: FSpimErrorToMessage + * @msg: 获取FSPIM模块错误码对应的错误信息 + * @return {const char *}, 错误码信息,NULL表示失败 + * @param {FError} error, FSPIM输入错误码 + */ +const char *FSpimErrorToMessage(FError error) +{ + const char *msg = NULL; + if (FSPIM_SUCCESS != error && (FSPIM_ERR_CODE_PREFIX != error & (FT_ERRCODE_SYS_MODULE_MASK | FT_ERRCODE_SUB_MODULE_MASK))) + { + /* if input error do not belong to this module */ + return msg; + } + u32 index = error & FT_ERRCODE_TAIL_VALUE_MASK; + + if (index < FSPIM_NUM_OF_ERR_CODE) + { + msg = FSPIM_ERROR_CODE_MSG[index]; + } + + return msg; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/spi/fspim/fspim.h b/bsp/phytium/libraries/standalone/drivers/spi/fspim/fspim.h new file mode 100644 index 0000000000..cb3ffb346e --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/spi/fspim/fspim.h @@ -0,0 +1,214 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fspim.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:08:38 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2021-12-3 init commit + * 1.1 zhugengyu 2022-4-15 support test mode + * 1.2 zhugengyu 2022-5-13 support spi dma + */ + + +#ifndef DRIVERS_FSPI_M_H +#define DRIVERS_FSPI_M_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ + +#include "ftypes.h" +#include "ferror_code.h" +#include "fassert.h" +#include "sdkconfig.h" + +/************************** Constant Definitions *****************************/ + +#define FSPIM_SUCCESS FT_SUCCESS +#define FSPIM_ERR_INVAL_STATE FT_MAKE_ERRCODE(ErrModBsp, ErrBspSpi, 0) +#define FSPIM_ERR_NOT_READY FT_MAKE_ERRCODE(ErrModBsp, ErrBspSpi, 1) +#define FSPIM_ERR_INVAL_PARAM FT_MAKE_ERRCODE(ErrModBsp, ErrBspSpi, 2) +#define FSPIM_ERR_BUS_BUSY FT_MAKE_ERRCODE(ErrModBsp, ErrBspSpi, 3) +#define FSPIM_ERR_NOT_SUPPORT FT_MAKE_ERRCODE(ErrModBsp, ErrBspSpi, 4) +#define FSPIM_ERR_TIMEOUT FT_MAKE_ERRCODE(ErrModBsp, ErrBspSpi, 5) +#define FSPIM_ERR_TRANS_FAIL FT_MAKE_ERRCODE(ErrModBsp, ErrBspSpi, 6) +#define FSPIM_ERR_DMA_INIT FT_MAKE_ERRCODE(ErrModBsp, ErrBspSpi, 6) + +#if defined(CONFIG_TARGET_F2000_4) || defined(CONFIG_TARGET_D2000) +#define FSPIM_VERSION_1 /* 用于FT2000/4和D2000平台的SPIM */ +#elif defined(CONFIG_TARGET_E2000) +#define FSPIM_VERSION_2 /* 用于E2000平台的SPIM */ +#else +#error "Invalid target board !!!" +#endif + +/* add up new error code above and plust FSPIM_ERR_CODE_MAX by ONE*/ +#define FSPIM_ERR_CODE_PREFIX FSPIM_ERR_TRANS_FAIL & (FT_ERRCODE_SYS_MODULE_MASK | FT_ERRCODE_SUB_MODULE_MASK) +#define FSPIM_NUM_OF_ERR_CODE 8 + +typedef enum +{ + FSPIM_SLAVE_DEV_0 = 0, + FSPIM_SLAVE_DEV_1, + FSPIM_SLAVE_DEV_2, + FSPIM_SLAVE_DEV_3, + + FSPIM_NUM_OF_SLAVE_DEV +} FSpimSlaveDevice; + +typedef enum +{ + FSPIM_1_BYTE = 1, + FSPIM_2_BYTE = 2, + + FSPIM_MAX_BYTES_NUM +} FSpimTransByte; + +typedef enum +{ + FSPIM_TRANS_MODE_RX_TX = 0x0, + FSPIM_TRANS_MODE_TX_ONLY = 0x1, + FSPIM_TRANS_MODE_RX_ONLY = 0x2, + FSPIM_TRANS_MODE_READ_EEPROM = 0x3, + + FSPIM_TRANS_MODE_MAX +} FSpimTransMode; + +/* + CPOL = 0, CPHA = 0, sample at the first rising edge + CPOL = 1, CPHA = 1, sample at the second rising edge + CPOL = 1, CPHA = 0, sample at the second falling edge + CPOL = 0, CPHA = 1, sample at the first falling edge +*/ + +typedef enum +{ + FSPIM_CPOL_LOW = 0, /* pharse 0 CPOL=0 */ + FSPIM_CPOL_HIGH /* pharse 1 CPOL=1 */ +} FSpimCpolType; + +typedef enum +{ + FSPIM_CPHA_1_EDGE = 0, /* sample at the 1st edge, CPHA=0 */ + FSPIM_CPHA_2_EDGE /* sample at the 2nd edge, CPHA=1 */ +} FSpimCphaType; + +typedef enum +{ + FSPIM_INTR_EVT_RX_DONE = 0, /* 接收完成事件 */ + FSPIM_INTR_EVT_TX_OVERFLOW, /* 发送FIFO上溢事件 */ + FSPIM_INTR_EVT_RX_UNDERFLOW, /* 接收FIFO下溢事件 */ + FSPIM_INTR_EVT_RX_OVERFLOW, /* 接收FIFO上溢事件 */ + + FSPIM_INTR_EVT_NUM +} FSpimIntrEvtType; + +/**************************** Type Definitions *******************************/ + +/** + * This typedef contains configuration information for the device. + */ +typedef struct +{ + u32 instance_id; /* Device instance id */ + uintptr base_addr; /* Device base address */ + u32 irq_num; /* Device intrrupt id */ + u32 irq_prority; /* Device intrrupt priority */ + FSpimSlaveDevice slave_dev_id; /* Slave device id */ + u32 max_freq_hz; /* Clock frequency in Hz */ + FSpimTransByte n_bytes; /* Bytes in transfer */ + FSpimCpolType cpol; /* Polarity of the clock */ + FSpimCphaType cpha; /* Phase of the clock */ + boolean en_test; /* Enable test mode */ + boolean en_dma; /* Enable DMA */ +} FSpimConfig; + +typedef void (*FSpimEvtHandler)(void *instance_p, void *param); + +/** + * This typedef contains driver instance data. The user is required to allocate a + * variable of this type for every device in the system. A pointer + * to a variable of this type is then passed to the driver API functions. + */ +typedef struct +{ + FSpimConfig config; /* Current active configs */ + u32 is_ready; /* Device is initialized and ready */ + u32 length; /* Data length in transfer */ + const void *tx_buff; /* Tx buffer beg */ + void *rx_buff; /* Rx buffer beg */ + const void *tx_buff_end; /* Tx buffer end */ + void *rx_buff_end; /* Rx buffer end */ + u32 tx_fifo_len; /* Depth of tx fifo */ + u32 rx_fifo_len; /* Depth of rx fifo */ + FSpimEvtHandler evt_handler[FSPIM_INTR_EVT_NUM]; /* event handler for interrupt */ + void *evt_param[FSPIM_INTR_EVT_NUM]; /* parameters ptr of event handler */ +} FSpim; + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ +/* fspim_sinit.c */ +/* 获取FSPIM驱动的默认配置参数 */ +const FSpimConfig *FSpimLookupConfig(u32 instance_id); + +/* fspim.c */ +/* 完成FSPIM驱动实例的初始化,使之可以使用*/ +FError FSpimCfgInitialize(FSpim *instance_p, const FSpimConfig *cofig_p); + +/* 完成I2C驱动实例去使能,清零实例数据 */ +void FSpimDeInitialize(FSpim *instance_p); + +/* 先发送后接收数据 (阻塞处理),利用Fifo进行处理 */ +FError FSpimTransferPollFifo(FSpim *instance_p, const void *tx_buf, void *rx_buf, fsize_t len); + +#ifdef FSPIM_VERSION_2 /* E2000 */ +/* 启动SPIM DMA数据传输 */ +FError FSpimTransferDMA(FSpim *instance_p, boolean tx, boolean rx); + +/* 设置片选信号 */ +void FSpimSetChipSelection(FSpim *instance_p, boolean on); +#endif + +/* 获取FSPIM模块错误码对应的错误信息 */ +const char *FSpimErrorToMessage(FError error); + +/* fspim_intr.c */ +/* 先发送后接收数据 (中断处理),利用Fifo进行处理 */ +FError FSpimTransferByInterrupt(FSpim *instance_p, const void *tx_buf, void *rx_buf, fsize_t len); + +/* SPIM中断处理函数 */ +void FSpimInterruptHandler(s32 vector, void *param); + +/* 注册FSPIM中断事件处理函数 */ +void FSpimRegisterIntrruptHandler(FSpim *instance_p, FSpimIntrEvtType evt, FSpimEvtHandler handler, void *param); + +/* 打印SPIM控制寄存器信息 */ +void FSpimDumpRegister(uintptr base_addr); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/bsp/phytium/libraries/standalone/drivers/spi/fspim/fspim_g.c b/bsp/phytium/libraries/standalone/drivers/spi/fspim/fspim_g.c new file mode 100644 index 0000000000..5094ff58ea --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/spi/fspim/fspim_g.c @@ -0,0 +1,100 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fspim_g.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:07:55 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2021-12-3 init commit + * 1.1 zhugengyu 2022-4-15 support test mode + */ + +/***************************** Include Files *********************************/ + +#include "ftypes.h" +#include "fparameters.h" +#include "sdkconfig.h" + +#include "fspim.h" +#include "fspim_hw.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + +const FSpimConfig FSPIM_CONFIG_TBL[FSPI_DEVICE_NUM] = +{ + [FSPI0_ID] = + { + .instance_id = FSPI0_ID, /* Id of device*/ + .base_addr = FSPI0_BASE, + .irq_num = FSPI0_IRQ_NUM, + .irq_prority = 0, + .slave_dev_id = FSPIM_SLAVE_DEV_0, + .max_freq_hz = 4000000, + .n_bytes = 1, + .en_test = FALSE, + .en_dma = FALSE + }, + [FSPI1_ID] = + { + .instance_id = FSPI1_ID, /* Id of device*/ + .base_addr = FSPI1_BASE, + .irq_num = FSPI1_IRQ_NUM, + .irq_prority = 0, + .slave_dev_id = FSPIM_SLAVE_DEV_0, + .max_freq_hz = 4000000, + .n_bytes = 1, + .en_test = FALSE, + .en_dma = FALSE + }, +#if defined(CONFIG_TARGET_E2000S) || defined(CONFIG_TARGET_E2000D) || defined(CONFIG_TARGET_E2000Q) + [FSPI2_ID] = + { + .instance_id = FSPI2_ID, /* Id of device*/ + .base_addr = FSPI2_BASE, + .irq_num = FSPI2_IRQ_NUM, + .irq_prority = 0, + .slave_dev_id = FSPIM_SLAVE_DEV_0, + .max_freq_hz = 4000000, + .n_bytes = 1, + .en_test = FALSE, + .en_dma = FALSE + }, + [FSPI3_ID] = + { + .instance_id = FSPI3_ID, /* Id of device*/ + .base_addr = FSPI3_BASE, + .irq_num = FSPI3_IRQ_NUM, + .irq_prority = 0, + .slave_dev_id = FSPIM_SLAVE_DEV_0, + .max_freq_hz = 4000000, + .n_bytes = 1, + .en_test = FALSE, + .en_dma = FALSE + }, +#endif +}; + + +/*****************************************************************************/ diff --git a/bsp/phytium/libraries/standalone/drivers/spi/fspim/fspim_hw.c b/bsp/phytium/libraries/standalone/drivers/spi/fspim/fspim_hw.c new file mode 100644 index 0000000000..c1004bea23 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/spi/fspim/fspim_hw.c @@ -0,0 +1,256 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fspim_hw.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:08:00 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2021-12-3 init commit + * 1.1 zhugengyu 2022-4-15 support test mode + */ + + +#include "fassert.h" +#include "fdebug.h" +#include "fspim_hw.h" +#include "fspim.h" + +/***************************** Include Files *********************************/ + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FSPIM_DEBUG_TAG "SPIM-HW" +#define FSPIM_ERROR(format, ...) FT_DEBUG_PRINT_E(FSPIM_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSPIM_WARN(format, ...) FT_DEBUG_PRINT_W(FSPIM_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSPIM_INFO(format, ...) FT_DEBUG_PRINT_I(FSPIM_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSPIM_DEBUG(format, ...) FT_DEBUG_PRINT_D(FSPIM_DEBUG_TAG, format, ##__VA_ARGS__) + +/************************** Function Prototypes ******************************/ + +/** + * @name: FSpimGetTxFifoDepth + * @msg: 获取TX Fifo可以设置的最大深度 + * @return {u32} TX Fifo的深度 + * @param {uintptr} base_addr, SPI控制器基地址 + */ +u32 FSpimGetTxFifoDepth(uintptr base_addr) +{ + u32 fifo_depth; + for (fifo_depth = 1; fifo_depth < FSPIM_MAX_FIFO_DEPTH; fifo_depth++) + { + FSpimSetTxFifoThreshold(base_addr, fifo_depth); + if (fifo_depth != FSpimGetTxFifoThreshold(base_addr)) + { + FSPIM_INFO("Tx fifo threshold is %d", fifo_depth); + break; + } + } + + FSpimSetTxFifoThreshold(base_addr, 0); + return fifo_depth; +} + +/** + * @name: FSpimGetRxFifoDepth + * @msg: 获取RX Fifo可以设置的最大深度 + * @return {u32} Rx Fifo的深度 + * @param {uintptr} base_addr, SPI控制器基地址 + */ +u32 FSpimGetRxFifoDepth(uintptr base_addr) +{ + u32 fifo_depth = FSPIM_MIN_FIFO_DEPTH; + while (FSPIM_MAX_FIFO_DEPTH >= fifo_depth) + { + FSpimSetRxFifoThreshold(base_addr, fifo_depth); + if (fifo_depth != FSpimGetRxFifoThreshold(base_addr)) + { + FSPIM_INFO("Rx fifo threshold is %d", fifo_depth); + break; + } + + fifo_depth++; + } + + return fifo_depth; +} + +/** + * @name: FSpimSelSlaveDev + * @msg: 选择SPI从设备 + * @return {无} + * @param {uintptr} base_addr, SPI控制器基地址 + * @param {u32} slave_dev_id, 从设备ID + */ +void FSpimSelSlaveDev(uintptr base_addr, u32 slave_dev_id) +{ + FASSERT(slave_dev_id < FSPIM_NUM_OF_SLAVE_DEV); + u32 reg_val; + + reg_val = (FSPIM_SER_SELECT << slave_dev_id); + FSPIM_WRITE_REG32(base_addr, FSPIM_SER_OFFSET, reg_val); + return; +} + +/** + * @name: FSpimSetSpeed + * @msg: 设置SPI传输速度 + * @return {FError} + * @param {uintptr} base_addr, SPI控制器基地址 + * @param {u32} speed, SPI传输速度设置 + */ +FError FSpimSetSpeed(uintptr base_addr, u32 speed) +{ + u32 clk_div; + boolean enabled = FSpimGetEnable(base_addr); + + if (enabled) + FSpimSetEnable(base_addr, FALSE); + + clk_div = FSPI_FREQ / speed; + FSPIM_INFO("set clk div as %d", clk_div); + FSPIM_WRITE_REG32(base_addr, FSPIM_BAUD_R_OFFSET, clk_div); + + if (enabled) + FSpimSetEnable(base_addr, TRUE); + + return FSPIM_SUCCESS; +} + +/** + * @name: FSpimSetTransMode + * @msg: 设置SPI传输模式 + * @return {无} + * @param {uintptr} base_addr, SPI控制器基地址 + * @param {u32} trans_mode, SPI传输模式设置 + */ +void FSpimSetTransMode(uintptr base_addr, u32 trans_mode) +{ + FASSERT(trans_mode < FSPIM_TRANS_MODE_MAX); + u32 reg_val; + boolean enabled = FSpimGetEnable(base_addr); + + if (enabled) + FSpimSetEnable(base_addr, FALSE); + + reg_val = FSpimGetCtrlR0(base_addr); + reg_val &= ~FSPIM_CTRL_R0_TMOD_MASK; /* clear trans mode bits */ + switch (trans_mode) + { + case FSPIM_TRANS_MODE_RX_TX: + reg_val |= FSPIM_CTRL_R0_TMOD(FSPIM_TMOD_RX_TX); + break; + case FSPIM_TRANS_MODE_TX_ONLY: + reg_val |= FSPIM_CTRL_R0_TMOD(FSPIM_TMOD_TX_ONLY); + break; + case FSPIM_TRANS_MODE_RX_ONLY: + reg_val |= FSPIM_CTRL_R0_TMOD(FSPIM_TMOD_RX_ONLY); + break; + case FSPIM_TRANS_MODE_READ_EEPROM: + reg_val |= FSPIM_CTRL_R0_TMOD(FSPIM_TMOD_RD_EEPROM); + break; + default: + FASSERT(0); + break; + } + + FSpimSetCtrlR0(base_addr, reg_val); + + if (enabled) + FSpimSetEnable(base_addr, TRUE); + + return; +} + +/** + * @name: FSpimSetCpha + * @msg: 设置串行时钟相位 + * @return {无} + * @param {uintptr} base_addr, SPI控制器基地址 + * @param {u32} cpha_mode, SPI控制器的相位设置 + */ +void FSpimSetCpha(uintptr base_addr, u32 cpha_mode) +{ + u32 reg_val = FSpimGetCtrlR0(base_addr); + + reg_val &= ~FSPIM_CTRL_R0_SCPHA_MASK; /* clear bits */ + if (FSPIM_CPHA_1_EDGE == cpha_mode) + reg_val |= FSPIM_CTRL_R0_SCPHA(FSPIM_SCPHA_SWITCH_DATA_MID); + else if (FSPIM_CPHA_2_EDGE == cpha_mode) + reg_val |= FSPIM_CTRL_R0_SCPHA(FSPIM_SCPHA_SWITCH_DATA_BEG); + else + FASSERT(0); + + FSpimSetCtrlR0(base_addr, reg_val); +} + +/** + * @name: FSpimSetCpol + * @msg: 设置串行时钟极性 + * @return {无} + * @param {uintptr} base_addr, SPI控制器基地址 + * @param {u32} cpol_mode, SPI控制器的极性设置 + */ +void FSpimSetCpol(uintptr base_addr, u32 cpol_mode) +{ + u32 reg_val = FSpimGetCtrlR0(base_addr); + + reg_val &= ~FSPIM_CTRL_R0_SCPOL_MASK; /* clear bits */ + if (FSPIM_CPOL_LOW == cpol_mode) + reg_val |= FSPIM_CTRL_R0_SCPOL(FSPIM_SCPOL_INACTIVE_LOW); + else if (FSPIM_CPOL_HIGH == cpol_mode) + reg_val |= FSPIM_CTRL_R0_SCPOL(FSPIM_SCPOL_INACTIVE_HIGH); + else + FASSERT(0); + + FSpimSetCtrlR0(base_addr, reg_val); +} + +/** + * @name: FSpimSetSlaveEnable + * @msg: 使能/去使能和从设备的连接 + * @return {无} + * @param {uintptr} base_addr, SPI控制器基地址 + * @param {boolean} enable, TRUE: 使能从设备, FALSE: 去使能从设备 + */ +void FSpimSetSlaveEnable(uintptr base_addr, boolean enable) +{ + u32 reg_val; + boolean enabled = FSpimGetEnable(base_addr); + + if (enabled) + FSpimSetEnable(base_addr, FALSE); + + reg_val = FSpimGetCtrlR0(base_addr); + + reg_val &= ~FSPIM_CTRL_R0_SLV_OE_MASK; + if (enable) + reg_val |= FSPIM_CTRL_R0_SLV_OE(FSPIM_SLAVE_TX_ENABLE); + else + reg_val |= FSPIM_CTRL_R0_SLV_OE(FSPIM_SLAVE_TX_DISALE); + + FSpimSetCtrlR0(base_addr, reg_val); + + if (enabled) + FSpimSetEnable(base_addr, TRUE); + + return; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/spi/fspim/fspim_hw.h b/bsp/phytium/libraries/standalone/drivers/spi/fspim/fspim_hw.h new file mode 100644 index 0000000000..d66f3bfa30 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/spi/fspim/fspim_hw.h @@ -0,0 +1,588 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fspim_hw.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:08:05 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2021-12-3 init commit + * 1.1 zhugengyu 2022-4-15 support test mode + * 1.2 zhugengyu 2022-5-13 support spi dma + */ + + +#ifndef DRIVERS_FSPIM_M_HW_H +#define DRIVERS_FSPIM_M_HW_H + +#ifdef __cplusplus +extern "C" +{ +#endif +/***************************** Include Files *********************************/ + +#include "fparameters.h" +#include "fkernel.h" +#include "fio.h" +#include "ftypes.h" + +/************************** Constant Definitions *****************************/ +/** @name Register Map + * + * Register offsets from the base address of an SD device. + * @{ + */ +/* offset map of SPI register */ +#define FSPIM_CTRL_R0_OFFSET 0x00 /* Ctrl register 0 */ +#define FSPIM_CTRL_R1_OFFSET 0x04 /* Ctrl register 1 */ +#define FSPIM_SSIENR_OFFSET 0x08 /* SPI enable register */ +#define FSPIM_MWCR_OFFSET 0x0c /* Microwire ctrl register */ +#define FSPIM_SER_OFFSET 0x10 /* Slave enable register */ +#define FSPIM_BAUD_R_OFFSET 0x14 /* Baudrate set register */ +#define FSPIM_TXFTL_R_OFFSET 0x18 /* Tx threshold register */ +#define FSPIM_RXFTL_R_OFFSET 0x1c /* Rx threshold register */ +#define FSPIM_TXFLR_OFFSET 0x20 /* Tx level register */ +#define FSPIM_RXFLR_OFFSET 0x24 /* Rx level register */ +#define FSPIM_SR_OFFSET 0x28 /* Status register */ +#define FSPIM_IMR_OFFSET 0x2c /* Intr mask register */ +#define FSPIM_ISR_OFFSET 0x30 /* Irq Status register */ +#define FSPIM_RIS_R_OFFSET 0x34 /* Intr status register */ +#define FSPIM_TXOI_CR_OFFSET 0x38 /* TX FIFO overflow intr clear register */ +#define FSPIM_RXOI_CR_OFFSET 0x3c /* RX FIFO overflow intr clear register */ +#define FSPIM_RXUI_CR_OFFSET 0x40 /* TX FIFO underflow intr clear register */ +#define FSPIM_MSTI_CR_OFFSET 0x44 /* Multi slave intr clear register */ +#define FSPIM_ICR_OFFSET 0x48 /* Intr clear register */ +#define FSPIM_DMA_CR_OFFSET 0x4c /* DMA ctrl register */ +#define FSPIM_DMA_TDLR_OFFSET 0x50 /* DMA TX Data level register */ +#define FSPIM_DMA_RDLR_OFFSET 0x54 /* DMA RX Data level register */ +#define FSPIM_IDR_OFFSET 0x58 /* Identification register */ +#define FSPIM_DR_OFFSET 0x60 /* Data register */ +#define FSPIM_RX_SAMPLE_DLY_OFFSET 0xfc /* RX Data delay register */ +#define FSPIM_CS_OFFSET 0x100 /* Chip selection register */ + +/** @name FSPIM_CTRL_R0_OFFSET Register + */ + +#define FSPIM_CTRL_R0_DFS_MASK GENMASK(3, 0) +#define FSPIM_CTRL_R0_DFS(x) (FSPIM_CTRL_R0_DFS_MASK & ((x) << 0)) /* 选择数据长度 */ +#define FSPIM_CTRL_R0_FRF(x) (GENMASK(5, 4) & ((x) << 4)) /* 选择传输模式 */ +#define FSPIM_CTRL_R0_SCPHA(x) ((x) << 6) /* 串行时钟相位 */ +#define FSPIM_CTRL_R0_SCPHA_MASK BIT(6) +enum +{ + FSPIM_SCPHA_SWITCH_DATA_MID = 0x0, + FSPIM_SCPHA_SWITCH_DATA_BEG = 0x1 +}; +#define FSPIM_CTRL_R0_SCPOL(x) ((x) << 7) /* 串行时钟极性 */ +#define FSPIM_CTRL_R0_SCPOL_MASK BIT(7) +enum +{ + FSPIM_SCPOL_INACTIVE_LOW = 0, + FSPIM_SCPOL_INACTIVE_HIGH = 1 +}; +#define FSPIM_CTRL_R0_TMOD_MASK GENMASK(9, 8) +#define FSPIM_CTRL_R0_TMOD(x) (FSPIM_CTRL_R0_TMOD_MASK & ((x) << 8)) /* 传输模式控制位 */ +#define FSPIM_CTRL_R0_TMOD_SHIFT 8 +enum +{ + FSPIM_TMOD_RX_TX = 0b00, + FSPIM_TMOD_TX_ONLY = 0b01, + FSPIM_TMOD_RX_ONLY = 0b10, + FSPIM_TMOD_RD_EEPROM = 0b11 +}; +#define FSPIM_CTRL_R0_SLV_OE(x) ((x) << 10) /* 从机发送逻辑使能位 */ +#define FSPIM_CTRL_R0_SLV_OE_MASK BIT(10) +enum +{ + FSPIM_SLAVE_TX_ENABLE = 0, + FSPIM_SLAVE_TX_DISALE = 1 +}; +#define FSPIM_CTRL_R0_SLV_SRL(x) ((x) << 11) /* 移位寄存器回环 */ +enum +{ + FSPIM_SRL_NORAML = 0, + FSPIM_SRL_TEST = 1 +}; +#define FSPIM_CTRL_R0_CFS(x) (GENMASK(5, 12) & ((x) << 12)) /* 数据大小控制位,用于 Microwire 模式 */ + +/** @name FSPIM_CTRL_R1_OFFSET Register + */ +/* FSPIM_TMOD_RX_ONLY 或 FSPIM_TMOD_RD_EEPROM 该字段设置为 SPI 连续接收的数据量 */ +#define FSPIM_CTRL_R1_NDF(x) (GENMASK(15, 0) & ((x) << 0)) +#define FSPIM_CTRL_R1_NDF_64KB 0b11 + +/** @name FSPIM_SSIENR_OFFSET Register + */ +#define FSPIM_SSIENR_SSI_EN(x) ((x) << 0) /* SPI 使能启用和禁用所有 SPI 操作 */ + +/** @name FSPIM_MWCR_OFFSET Register + */ +#define FSPIM_MWCR_MW_MOD(x) ((x) << 0) /* Microwire 传输模式 */ +enum +{ + FSPIM_MWMODE_NO_CONT_TRANS = 0, /* 非连续传输 */ + FSPIM_MWMODE_CONT_TRANS = 1 /* 连续传输 */ +}; + +#define FSPIM_MWCR_MDD(x) ((x) << 1) /* Microwire 控制位 */ +enum +{ + FSPIM_MWCR_RX_EXT = 0, /* 从外部串行设备接收数据 */ + FSPIM_MWCR_TX_EXT /* 数据发送到外部串行设备 */ +}; + +#define FSPIM_MWCR_MHS(x) ((x) << 2) /* Microwire 握手 */ +enum +{ + FSPU_MWCR_DISABLE_HANDSHAKING = 0, + FSPU_MWCR_ENABLE_HANDSHAKING = 1 +}; + +/** @name FSPIM_SER_OFFSET Register + */ +#define FSPIM_SER(x) (GENMASK(3, 0) & ((x) << 0)) /* 从机选择信号启动标志 */ +/* 寄存器中的每一个位都对应来自 SPI 主机的从选信号(ss_x_n]).当此寄 + 存器中的某个位被置为 1 时,串行口传输开始时 */ +enum +{ + FSPIM_SER_UNSELECT = 0x0, + FSPIM_SER_SELECT = 0x1 +}; + +/** @name FSPIM_BAUD_R_OFFSET Register + */ +#define FSPIM_BAUD_R_SCKDV(x) (GENMASK(15, 0) & ((x) << 0)) /* SCKDV 为 2 ~ 65534 之间的任何偶数值 */ +#define FSPIM_BAUD_R_SCKDV_MIN 2 +#define FSPIM_BAUD_R_SCKDV_MAX 65534 +#define FSPIM_BAUD_R_SCKDV_IS_VALID(x) (0 == (x) % 2) + +/** @name FSPIM_TXFTL_R_OFFSET Register + */ +#define FSPIM_TXFTL_R_TFT(x) (GENMASK(2, 0) & ((x) << 0)) /* 发送 FIFO 阙值 */ + +/** @name FSPIM_RXFTL_R_OFFSET Register + */ +#define FSPIM_RXFTL_R_RFT(x) (GENMASK(3, 0) & ((x) << 0)) /* 接收 FIFO 阙值 */ + +/** @name FSPIM_TXFLR_OFFSET Register + */ +#define FSPIM_TXFLR_TXTFL(x) (GENMASK(3, 0) & ((x) << 0)) /* 发送 FIFO 等级 */ + +/** @name FSPIM_RXFLR_OFFSET Register + */ +#define FSPIM_RXFLR_RXTFL(x) (GENMASK(3, 0) & ((x) << 0)) /* 接收 FIFO 等级 */ + +/** @name FSPIM_SR_OFFSET Register, RO + */ +#define FSPIM_SR_BUSY BIT(0) /* SPI 总线繁忙标志位 */ +#define FSPIM_SR_TFNF BIT(1) /* 发送 FIFO 不满 */ +#define FSPIM_SR_TFE BIT(2) /* 发送 FIFO 为空 */ +#define FSPIM_SR_RFNE BIT(3) /* 接收 FIFO 不为空 */ +#define FSPIM_SR_RFF BIT(4) /* 接收 FIFO 满 */ +#define FSPIM_SR_TXE BIT(5) /* 传输错误 */ +#define FSPIM_SR_DCOL BIT(6) /* 传输数据冲突错误 */ +#define FSPIM_SR_ALL_BITS GENMASK(0, 6) + +/** @name FSPIM_IMR_OFFSET Register + */ +#define FSPIM_IMR_TXEIS BIT(0) /* 发送 FIFO 空中断 */ +#define FSPIM_IMR_TXOIS BIT(1) /* 发送 FIFO 上溢中断 */ +#define FSPIM_IMR_RXUIS BIT(2) /* 接收 FIFO 下溢中断 */ +#define FSPIM_IMR_RXOIS BIT(3) /* 接收 FIFO 上溢中断 */ +#define FSPIM_IMR_RXFIS BIT(4) /* 接收 FIFO 满中断 */ +#define FSPIM_IMR_ALL_BITS GENMASK(4, 0) + +/** @name FSPIM_ISR_OFFSET Register + */ +#define FSPIM_ISR_TXEIS BIT(0) /* 发送 FIFO 空中断状态 */ +#define FSPIM_ISR_TXOIS BIT(1) /* 发送 FIFO 上溢中断状态 */ +#define FSPIM_ISR_RXUIS BIT(2) /* 接收 FIFO 下溢中断状态 */ +#define FSPIM_ISR_RXOIS BIT(3) /* 接收 FIFO 上溢中断状态 */ +#define FSPIM_ISR_RXFIS BIT(4) /* 接收 FIFO 满中断状态 */ +#define FSPIM_ISR_MSTIS BIT(5) /* 多主机竞争中断状态 */ + +/** @name FSPIM_RIS_R_OFFSET Register + */ +#define FSPIM_RIS_R_TXEIR BIT(0) /* 传输 FIFO 空生成中断状态 */ +#define FSPIM_RIS_R_TXOIR BIT(1) /* 传输 FIFO 上溢生成中断状态 */ +#define FSPIM_RIS_R_RXUIR BIT(2) /* 接收 FIFO 下溢生成中断状态 */ +#define FSPIM_RIS_R_RXOIR BIT(3) /* 接收 FIFO 上溢生成中断状态 */ +#define FSPIM_RIS_R_RXFIR BIT(4) /* 接收 FIFO 满生成中断状态 */ +#define FSPIM_RIS_R_MSTIR BIT(5) /* 多主机冲突生成中断状态 */ +#define FSPIM_RIS_R_ALL_BITS GENMASK(5, 0) + +/** @name FSPIM_TXOI_CR_OFFSET Register + */ +#define FSPIM_TXOICR BIT(0) /* 清除传输 FIFO 溢出中断 */ + +/** @name FSPIM_RXOI_CR_OFFSET Register + */ +#define FSPIM_RXOICR BIT(0) /* 清除传输 FIFO 溢出中断 */ + +/** @name FSPIM_RXUI_CR_OFFSET Register + */ +#define FSPIM_RXUICR BIT(0) /* 清除传输 FIFO 下溢中断 */ + +/** @name FSPIM_MSTI_CR_OFFSET Register + */ +#define FSPIM_MSTICR BIT(0) /* 清除多主争用中断 */ + +/** @name FSPIM_ICR_OFFSET Register + */ +#define FSPIM_ICR BIT(0) /* 清除中断 */ + +/** @name FSPIM_DMA_CR_OFFSET Register + */ +#define FSPIM_DMA_CR_RDMAE BIT(0) /* DMA 接收使能 */ +#define FSPIM_DMA_CR_TDMAE BIT(1) /* DMA 发送使能 */ + +/** @name FSPIM_DMA_TDLR_OFFSET Register + */ +#define FSPIM_DMATDL(x) (GENMASK(2, 0) & ((x) << 0)) /* 发送数据等级 */ + +/** @name FSPIM_DMA_RDLR_OFFSET Register + */ +#define FSPIM_DMARDL(x) (GENMASK(2, 0) & ((x) << 0)) /* 接收数据等级 */ + +/** @name FSPIM_IDR_OFFSET Register + */ +#define FSPIM_IDCODE(x) (GENMASK(31, 0) & ((x) << 0)) /* 识别码。即外部设备标识代码 */ + +/** @name FSPIM_DR_OFFSET Register + */ +#define FSPIM_DR(x) (GENMASK(15, 0) & ((x) << 0)) /* 数据寄存器 */ + +/** @name FSPIM_RX_SAMPLE_DLY_OFFSET Register + */ +#define FSPIM_RSD(x) (GENMASK(7, 0) & ((x) << 0)) /* 接收数据延时 */ + +/** @name FSPIM_CS_OFFSET Register + */ +#define FSPIM_NUM_OF_CS 4U +#define FSPIM_CHIP_SEL_EN(cs) BIT((cs) + FSPIM_NUM_OF_CS) /* 1: enable chip selection */ +#define FSPIM_CHIP_SEL(cs) BIT(cs) + +#define FSPIM_DEFAULT_DFS 0x7 +#define FSPIM_DEFAULT_FRF 0x0 +#define FSPIM_DEFAULT_RSD 0x6 +#define FSPIM_DEFAULT_CFS 0x0 + +#define FSPIM_MIN_FIFO_DEPTH 0 +#define FSPIM_MAX_FIFO_DEPTH 256 +#define FSPIM_TIMEOUT 256 + +#define FSPIM_TX_DMA_LEVEL 0x10 +#define FSPIM_RX_DMA_LEVEL 0xf + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FSPIM_READ_REG32(addr, reg_offset) FtIn32((addr) + (u32)(reg_offset)) +#define FSPIM_WRITE_REG32(addr, reg_offset, reg_value) FtOut32((addr) + (u32)(reg_offset), (u32)(reg_value)) + +/** + * @name: FSpimSetCtrlR0 + * @msg: 设置CTRL_R0寄存器 + * @return {*} + * @param {uintptr} base_addr + * @param {u32} val + */ +static inline void FSpimSetCtrlR0(uintptr base_addr, u32 val) +{ + FSPIM_WRITE_REG32(base_addr, FSPIM_CTRL_R0_OFFSET, val); +} + +/** + * @name: FSpimGetCtrlR0 + * @msg: 获取CTRL_R0寄存器的值 + * @return {*} + * @param {uintptr} base_addr + */ +static inline u32 FSpimGetCtrlR0(uintptr base_addr) +{ + return FSPIM_READ_REG32(base_addr, FSPIM_CTRL_R0_OFFSET); +} + +/** + * @name: FSpimSetCtrlR0 + * @msg: 设置CTRL_R1寄存器 + * @return {*} + * @param {uintptr} base_addr + * @param {u32} val + */ +static inline void FSpimSetCtrlR1(uintptr base_addr, u32 val) +{ + FSPIM_WRITE_REG32(base_addr, FSPIM_CTRL_R1_OFFSET, val); +} + +/** + * @name: FSpimSetTxFifoThreshold + * @msg: 设置TX Fifo阈值 + * @return {*} + * @param {uintptr} base_addr + * @param {u32} val + */ +static inline void FSpimSetTxFifoThreshold(uintptr base_addr, u32 val) +{ + FSPIM_WRITE_REG32(base_addr, FSPIM_TXFTL_R_OFFSET, val); +} + +/** + * @name: FSpimGetTxFifoThreshold + * @msg: 获取TX Fifo阈值 + * @return {*} + * @param {uintptr} base_addr + */ +static inline u32 FSpimGetTxFifoThreshold(uintptr base_addr) +{ + return FSPIM_READ_REG32(base_addr, FSPIM_TXFTL_R_OFFSET); +} + +/** + * @name: FSpimSetRxFifoThreshold + * @msg: 设置RX Fifo阈值 + * @return {*} + * @param {uintptr} base_addr + * @param {u32} val + */ +static inline void FSpimSetRxFifoThreshold(uintptr base_addr, u32 val) +{ + FSPIM_WRITE_REG32(base_addr, FSPIM_RXFTL_R_OFFSET, val); +} + +/** + * @name: FSpimGetRxFifoThreshold + * @msg: 获取RX Fifo阈值 + * @return {*} + * @param {uintptr} base_addr + */ +static inline u32 FSpimGetRxFifoThreshold(uintptr base_addr) +{ + return FSPIM_READ_REG32(base_addr, FSPIM_RXFTL_R_OFFSET); +} + +/** + * @name: FSpimGetTxFifoLevel + * @msg: 获取当前TX Fifo等级 + * @return {*} + * @param {uintptr} base_addr + */ +static inline u32 FSpimGetTxFifoLevel(uintptr base_addr) +{ + return FSPIM_READ_REG32(base_addr, FSPIM_TXFLR_OFFSET); +} + +/** + * @name: FSpimGetRxFifoLevel + * @msg: 获取当前RX Fifo等级 + * @return {*} + * @param {uintptr} base_addr + */ +static inline u32 FSpimGetRxFifoLevel(uintptr base_addr) +{ + return FSPIM_READ_REG32(base_addr, FSPIM_RXFLR_OFFSET); +} + +/** + * @name: FSpimGetTxDMALevel + * @msg: 获取当前的TX DMA等级 + * @return {*} + * @param {uintptr} base_addr + */ +static inline u32 FSpimGetTxDMALevel(uintptr base_addr) +{ + return FSPIM_READ_REG32(base_addr, FSPIM_DMA_TDLR_OFFSET); +} + +/** + * @name: FSpimGetRxDMALevel + * @msg: 获取当前的RX DMA等级 + * @return {*} + * @param {uintptr} base_addr + */ +static inline u32 FSpimGetRxDMALevel(uintptr base_addr) +{ + return FSPIM_READ_REG32(base_addr, FSPIM_DMA_RDLR_OFFSET); +} + +/** + * @name: FSpimSetTxDMALevel + * @msg: 设置TX DMA等级 + * @return {*} + * @param {uintptr} base_addr + * @param {u32} level, TX DMA等级, 应该与FIFO阈值保持一致 + */ +static inline void FSpimSetTxDMALevel(uintptr base_addr, u32 level) +{ + FSPIM_WRITE_REG32(base_addr, FSPIM_DMA_TDLR_OFFSET, level); +} + +/** + * @name: FSpimSetRxDMALevel + * @msg: 设置RX DMA等级 + * @return {*} + * @param {uintptr} base_addr + * @param {u32} level, RX DMA等级, 应该与FIFO阈值保持一致 + */ +static inline void FSpimSetRxDMALevel(uintptr base_addr, u32 level) +{ + FSPIM_WRITE_REG32(base_addr, FSPIM_DMA_RDLR_OFFSET, level); +} + + +/** + * @name: FSpimGetEnable + * @msg: 获取FSPIM控制器的使能状态 + * @return {*} + * @param {uintptr} base_addr + */ +static inline boolean FSpimGetEnable(uintptr base_addr) +{ + return FSPIM_READ_REG32(base_addr, FSPIM_SSIENR_OFFSET); +} + +/** + * @name: FSpimSetEnable + * @msg: 使能/去使能FSPIM控制器 + * @return {*} + * @param {uintptr} base_addr + * @param {boolean} enable + */ +static inline void FSpimSetEnable(uintptr base_addr, boolean enable) +{ + if (enable) + FSPIM_WRITE_REG32(base_addr, FSPIM_SSIENR_OFFSET, FSPIM_SSIENR_SSI_EN(1)); + else + FSPIM_WRITE_REG32(base_addr, FSPIM_SSIENR_OFFSET, FSPIM_SSIENR_SSI_EN(0)); +} + +/** + * @name: FSpimMaskIrq + * @msg: 屏蔽指定的中断位,去使能中断 + * @return {*} + * @param {uintptr} base_addr + * @param {u32} mask + */ +static inline void FSpimMaskIrq(uintptr base_addr, u32 mask) +{ + u32 curr_mask; + curr_mask = FSPIM_READ_REG32(base_addr, FSPIM_IMR_OFFSET) & ~mask; /* = 0 中断不活动*/ + FSPIM_WRITE_REG32(base_addr, FSPIM_IMR_OFFSET, curr_mask); +} + +/** + * @name: FSpimUmaskIrq + * @msg: 取消屏蔽指定的中断位,使能中断 + * @return {*} + * @param {uintptr} base_addr + * @param {u32} mask + */ +static inline void FSpimUmaskIrq(uintptr base_addr, u32 mask) +{ + u32 curr_mask; + curr_mask = FSPIM_READ_REG32(base_addr, FSPIM_IMR_OFFSET) | mask; /* = 1 中断活动 */ + FSPIM_WRITE_REG32(base_addr, FSPIM_IMR_OFFSET, curr_mask); +} + + +/** + * @name: FSpimGetMask + * @msg: 获取当前的中断屏蔽位 + * @return {*} + * @param {uintptr} base_addr + */ +static inline u32 FSpimGetMask(uintptr base_addr) +{ + return FSPIM_IMR_ALL_BITS & FSPIM_READ_REG32(base_addr, FSPIM_IMR_OFFSET); +} + +/** + * @name: FSpimGetTransMode + * @msg: 获取当前的传输模式 + * @return {*} + * @param {uintptr} base_addr + */ +static inline u32 FSpimGetTransMode(uintptr base_addr) +{ + return ((FSpimGetCtrlR0(base_addr) & FSPIM_CTRL_R0_TMOD_MASK) >> FSPIM_CTRL_R0_TMOD_SHIFT); +} + +/** + * @name: FSpimGetStatus + * @msg: 获取当前的FSPIM控制器状态 + * @return {*} + * @param {uintptr} base_addr + */ +static inline u32 FSpimGetStatus(uintptr base_addr) +{ + return FSPIM_READ_REG32(base_addr, FSPIM_SR_OFFSET); +} + +/** + * @name: FSpimWriteData + * @msg: 写SPI数据 + * @return {*} + * @param {uintptr} base_addr + * @param {u16} dat + */ +static inline void FSpimWriteData(uintptr base_addr, u16 dat) +{ + FSPIM_WRITE_REG32(base_addr, FSPIM_DR_OFFSET, FSPIM_DR(dat)); +} + +/** + * @name: FSpimReadData + * @msg: 读SPI数据 + * @return {*} + * @param {uintptr} base_addr + */ +static inline u16 FSpimReadData(uintptr base_addr) +{ + return (u16)(FSPIM_READ_REG32(base_addr, FSPIM_DR_OFFSET)); +} + +/************************** Function Prototypes ******************************/ +/* 使能/去使能和从设备的连接 */ +void FSpimSetSlaveEnable(uintptr base_addr, boolean enable); + +/* 获取TX Fifo可以设置的最大深度 */ +u32 FSpimGetTxFifoDepth(uintptr base_addr); + +/* 获取RX Fifo可以设置的最大深度 */ +u32 FSpimGetRxFifoDepth(uintptr base_addr); + +/* 选择SPI从设备 */ +void FSpimSelSlaveDev(uintptr base_addr, u32 slave_dev_id); + +/* 设置SPI传输速度 */ +FError FSpimSetSpeed(uintptr base_addr, u32 speed); + +/* 设置SPI传输模式 */ +void FSpimSetTransMode(uintptr base_addr, u32 trans_mode); + +/* 设置串行时钟相位 */ +void FSpimSetCpha(uintptr base_addr, u32 cpha_mode); + +/* 设置串行时钟极性 */ +void FSpimSetCpol(uintptr base_addr, u32 cpol_mode); + +#ifdef __cplusplus +} +#endif + +#endif // ! \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/spi/fspim/fspim_intr.c b/bsp/phytium/libraries/standalone/drivers/spi/fspim/fspim_intr.c new file mode 100644 index 0000000000..6d1e85df4d --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/spi/fspim/fspim_intr.c @@ -0,0 +1,133 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fspim_intr.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:08:10 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2021-12-3 init commit + * 1.1 zhugengyu 2022-4-15 support test mode + */ + +/***************************** Include Files *********************************/ +#include +#include "fio.h" +#include "ferror_code.h" +#include "ftypes.h" +#include "fassert.h" +#include "fdebug.h" +#include "fspim_hw.h" +#include "fspim.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +/* be very careful to use print log in intrrupt handler */ +#define FSPIM_DEBUG_TAG "SPIM-INTR" +#define FSPIM_ERROR(format, ...) FT_DEBUG_PRINT_E(FSPIM_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSPIM_WARN(format, ...) FT_DEBUG_PRINT_W(FSPIM_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSPIM_INFO(format, ...) FT_DEBUG_PRINT_I(FSPIM_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSPIM_DEBUG(format, ...) FT_DEBUG_PRINT_D(FSPIM_DEBUG_TAG, format, ##__VA_ARGS__) + +#define FSPIM_CALL_INTR_EVT_HANDLDER(instance_p, evt) \ + if (instance_p->evt_handler[evt]) \ + instance_p->evt_handler[evt](instance_p, instance_p->evt_param[evt]) + +/************************** Function Prototypes ******************************/ +extern void FSpimFifoTx(FSpim *instance_p); +extern void FSpimFifoRx(FSpim *instance_p); +extern FError FSpimReset(FSpim *instance_p); + +/************************** Variable Definitions *****************************/ + +/*****************************************************************************/ +/** + * @name: FSpimInterruptHandler + * @msg: SPIM中断处理函数 + * @return {无} + * @param {s32} vector,中断向量号,此处不关心此参数 + * @param {void} *param, 中断输入参数, 指向FSPIM的驱动控制实例 + */ +void FSpimInterruptHandler(s32 vector, void *param) +{ + FASSERT(param); + FSpim *instance_p = (FSpim *)param; + uintptr base_addr = instance_p->config.base_addr; + u32 intr_status = FSPIM_RIS_R_ALL_BITS & FSPIM_READ_REG32(base_addr, FSPIM_RIS_R_OFFSET); + + if (0 == intr_status) + { + return; + } + + if ((FSPIM_RIS_R_TXOIR | FSPIM_RIS_R_RXOIR | FSPIM_RIS_R_RXUIR) & intr_status) /* 发送FIFO溢出 */ + { + FSPIM_WARN("Fifo overflow or underflow"); + if (FSPIM_RIS_R_TXOIR & intr_status) + { + FSPIM_CALL_INTR_EVT_HANDLDER(instance_p, FSPIM_INTR_EVT_TX_OVERFLOW); + } + + if (FSPIM_RIS_R_RXUIR & intr_status) + { + FSPIM_CALL_INTR_EVT_HANDLDER(instance_p, FSPIM_INTR_EVT_RX_UNDERFLOW); + } + + if (FSPIM_RIS_R_RXOIR & intr_status) + { + FSPIM_CALL_INTR_EVT_HANDLDER(instance_p, FSPIM_INTR_EVT_RX_OVERFLOW); + } + + FSpimReset(instance_p); + return; + } + + FSpimFifoRx(instance_p); /* 检查 RX Fifo是否为空,如果不为空则接收数据 */ + + if (instance_p->rx_buff_end == instance_p->rx_buff) /* RX 缓冲区已满,停止填入发送数据 */ + { + FSpimMaskIrq(base_addr, FSPIM_IMR_TXEIS); + FSPIM_CALL_INTR_EVT_HANDLDER(instance_p, FSPIM_INTR_EVT_RX_DONE); + return; + } + + if (FSPIM_RIS_R_TXEIR & intr_status) /* TX Fifo为空,填入待发送数据 */ + { + FSpimMaskIrq(base_addr, FSPIM_IMR_TXEIS); + FSpimFifoTx(instance_p); + FSpimUmaskIrq(base_addr, FSPIM_IMR_TXEIS); + } + + return; +} + +/** + * @name: FSpimRegisterIntrruptHandler + * @msg: 注册FSPIM中断事件处理函数 + * @return {*} + * @param {FI2c} *instance_p + * @param {u32} evt + * @param {FI2cEvtHandler} handler + */ +void FSpimRegisterIntrruptHandler(FSpim *instance_p, FSpimIntrEvtType evt, FSpimEvtHandler handler, void *param) +{ + FASSERT(instance_p && evt < FSPIM_INTR_EVT_NUM); + instance_p->evt_handler[evt] = handler; + instance_p->evt_param[evt] = param; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/spi/fspim/fspim_selftest.c b/bsp/phytium/libraries/standalone/drivers/spi/fspim/fspim_selftest.c new file mode 100644 index 0000000000..1040178d77 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/spi/fspim/fspim_selftest.c @@ -0,0 +1,73 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fspim_selftest.c + * Date: 2022-07-21 13:21:43 + * LastEditTime: 2022-07-21 13:21:44 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ +/***************************** Include Files *********************************/ +#include "fio.h" +#include "fdebug.h" +#include "fassert.h" +#include "ftypes.h" + +#include "fspim_hw.h" +#include "fspim.h" +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FSPIM_DEBUG_TAG "SPIM-TEST" +#define FSPIM_ERROR(format, ...) FT_DEBUG_PRINT_E(FSPIM_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSPIM_WARN(format, ...) FT_DEBUG_PRINT_W(FSPIM_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSPIM_INFO(format, ...) FT_DEBUG_PRINT_I(FSPIM_DEBUG_TAG, format, ##__VA_ARGS__) +#define FSPIM_DEBUG(format, ...) FT_DEBUG_PRINT_D(FSPIM_DEBUG_TAG, format, ##__VA_ARGS__) + +#define FSPIM_DUMPER(base_addr, reg_off, reg_name) \ + FSPIM_DEBUG("\t\t[%s]@0x%x\t=\t0x%x", reg_name, (reg_off), FSPIM_READ_REG32((base_addr), (reg_off))) +/************************** Function Prototypes ******************************/ + +/*****************************************************************************/ + +void FSpimDumpRegister(uintptr base_addr) +{ + FSPIM_DEBUG("Dump register info @0x%x", base_addr); + FSPIM_DUMPER(base_addr, FSPIM_CTRL_R0_OFFSET, "ctrl_r0"); + FSPIM_DUMPER(base_addr, FSPIM_CTRL_R1_OFFSET, "ctrl_r1"); + FSPIM_DUMPER(base_addr, FSPIM_SSIENR_OFFSET, "ssienr"); + FSPIM_DUMPER(base_addr, FSPIM_MWCR_OFFSET, "mwcr"); + FSPIM_DUMPER(base_addr, FSPIM_SER_OFFSET, "ser"); + FSPIM_DUMPER(base_addr, FSPIM_BAUD_R_OFFSET, "baud"); + FSPIM_DUMPER(base_addr, FSPIM_TXFTL_R_OFFSET, "txftl"); + FSPIM_DUMPER(base_addr, FSPIM_RXFTL_R_OFFSET, "rxftl"); + FSPIM_DUMPER(base_addr, FSPIM_TXFLR_OFFSET, "txflr"); + FSPIM_DUMPER(base_addr, FSPIM_RXFLR_OFFSET, "rxflr"); + FSPIM_DUMPER(base_addr, FSPIM_SR_OFFSET, "sr"); + FSPIM_DUMPER(base_addr, FSPIM_IMR_OFFSET, "imr"); + FSPIM_DUMPER(base_addr, FSPIM_ISR_OFFSET, "isr"); + FSPIM_DUMPER(base_addr, FSPIM_RIS_R_OFFSET, "ris_r"); + FSPIM_DUMPER(base_addr, FSPIM_DMA_CR_OFFSET, "cr"); + FSPIM_DUMPER(base_addr, FSPIM_DMA_TDLR_OFFSET, "tdlr"); + FSPIM_DUMPER(base_addr, FSPIM_DMA_RDLR_OFFSET, "rdlr"); + FSPIM_DUMPER(base_addr, FSPIM_IDR_OFFSET, "idr"); + FSPIM_DUMPER(base_addr, FSPIM_RX_SAMPLE_DLY_OFFSET, "rx_sample"); + FSPIM_DUMPER(base_addr, FSPIM_CS_OFFSET, "cs"); +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/spi/fspim/fspim_sinit.c b/bsp/phytium/libraries/standalone/drivers/spi/fspim/fspim_sinit.c new file mode 100644 index 0000000000..13b2d17666 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/spi/fspim/fspim_sinit.c @@ -0,0 +1,64 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fspim_sinit.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:08:24 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 zhugengyu 2021-12-3 init commit + * 1.1 zhugengyu 2022-4-15 support test mode + */ + +/***************************** Include Files *********************************/ + +#include "ftypes.h" +#include "fparameters.h" +#include "fspim.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Variable Definitions *****************************/ + +extern const FSpimConfig FSPIM_CONFIG_TBL[FSPI_DEVICE_NUM]; + +/************************** Function Prototypes ******************************/ +/** + * @name: FSpimLookupConfig + * @msg: 获取FSPIM实例的默认配置 + * @return {const FSpimConfig *} FSPIM实例的默认配置 + * @param {u32} instance_id, SPI控制器ID + */ +const FSpimConfig *FSpimLookupConfig(u32 instance_id) +{ + const FSpimConfig *ptr = NULL; + u32 index; + + for (index = 0; index < (u32)FSPI_DEVICE_NUM; index++) + { + if (FSPIM_CONFIG_TBL[index].instance_id == instance_id) + { + ptr = &FSPIM_CONFIG_TBL[index]; + break; + } + } + + return (const FSpimConfig *)ptr; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/timer/Kconfig b/bsp/phytium/libraries/standalone/drivers/timer/Kconfig new file mode 100644 index 0000000000..2ba3ac26d8 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/timer/Kconfig @@ -0,0 +1,9 @@ +menu "Hardware Timer Configuration" + config ENABLE_TIMER_TACHO + bool + prompt "Use Timer Tacho" + depends on TARGET_E2000S || TARGET_E2000D || TARGET_E2000Q + + default n +endmenu + diff --git a/bsp/phytium/libraries/standalone/drivers/timer/ftimer_tacho/ftacho.c b/bsp/phytium/libraries/standalone/drivers/timer/ftimer_tacho/ftacho.c new file mode 100644 index 0000000000..87087e6caa --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/timer/ftimer_tacho/ftacho.c @@ -0,0 +1,298 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: ftacho.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-05-20 09:08:52 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +/***************************** Include Files *********************************/ + +#include +#include "fassert.h" +#include "fkernel.h" +#include "fsleep.h" +#include "ftimer_tacho_hw.h" +#include "ftimer_tacho.h" +#include "fparameters.h" + +/************************** Function Prototypes ******************************/ +/** + * @name: FTachoInit + * @msg: 初始化Tacho,并且使能计数器和tachometer + * @return {FError} 驱动初始化的错误码信息,FTIMER_TACHO_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + * @param {FTimerTachoCtrl} *instance_p 驱动控制数据结构 + * @param {FTimerTachoConfig} *config_p 驱动配置数据结构 + */ +FError FTachoInit(FTimerTachoCtrl *instance_p, const FTimerTachoConfig *config_p) +{ + FASSERT(instance_p && config_p); + + u32 reg_val = 0; + + if (instance_p->isready == FT_COMPONENT_IS_READY) + { + FTIMER_INFO("device is already initialized.!!!\r\n"); + return FTIMER_TACHO_ERR_IS_READ; + } + + /* set work mode */ + if (FTIMER_WORK_MODE_TACHO == config_p->work_mode) + { + reg_val |= FTIMER_REG_TACHO_MODE_TACHO; + reg_val |= FTACHO_REG_CAP_IN_ENABLE; + /* plus num of rpm calculate period */ + FTIMER_CMPL_WRITE(instance_p, config_p->plus_num); + } + else if (FTIMER_WORK_MODE_CAPTURE == config_p->work_mode) + { + reg_val |= FTIMER_REG_TACHO_MODE_CAPTURE; + reg_val |= FTIMER_REG_TACHO_CAPTURE_ENABLE; + /* set capture cnt to assert capture intr */ + reg_val |= FTIMER_REG_TACHO_CAPTURE_CNT_MASK & (config_p->captue_cnt << FTIMER_REG_TACHO_CAPTURE_CNT_SHIFT); + reg_val |= FTIMER_REG_ENABLE; + } + else + { + FTACHO_ERROR("not support work_mode."); + return FTIMER_TACHO_ERR_INVAL_PARM; + } + + /* set timer bits */ + if (FTIMER_32_BITS == config_p->timer_bits) + { + reg_val &= (~FTIMER_REG_CNT_SERIES_64BIT); + } + else if (FTIMER_64_BITS == config_p->timer_bits) + { + reg_val |= FTIMER_REG_CNT_SERIES_64BIT; + } + else + { + FTACHO_ERROR("invalid input 32/64bits."); + return FTIMER_TACHO_ERR_INVAL_PARM; + } + + /* set edge mode */ + if (FTACHO_FALLING_EDGE == config_p->edge_mode) + { + reg_val &= ~FTACHO_REG_MODE_MASK; + reg_val |= FTACHO_REG_MODE_FALLING_EDGE; + } + else if (FTACHO_RISING_EDGE == config_p->edge_mode) + { + reg_val &= ~FTACHO_REG_MODE_MASK; + reg_val |= FTACHO_REG_MODE_RISING_EDGE; + } + else if (FTACHO_DOUBLE_EDGE == config_p->edge_mode) + { + reg_val &= ~FTACHO_REG_MODE_MASK; + reg_val |= FTACHO_REG_MODE_DOUBLE_EDGE; + } + else + { + FTACHO_ERROR("invalid input edge."); + return FTIMER_TACHO_ERR_INVAL_PARM; + } + + /* set jitter level */ + reg_val |= FTACHO_REG_ANTI_JITTER_MASK & + (config_p->jitter_level << FTACHO_REG_ANTI_JITTER_SHIFT); + + //use input config + if (config_p != &instance_p->config) + { + instance_p->config = *config_p; + } + + FTIMER_CTRL_WRITE(instance_p, reg_val); + instance_p->isready = FT_COMPONENT_IS_READY; + + return FTIMER_TACHO_SUCCESS; +} + +/** + * @name: FTachoGetFanRPM + * @msg: 获取风扇的转速值 + * @return {FError} 驱动初始化的错误码信息,FTIMER_TACHO_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + * @param {FTimerTachoCtrl} *instance_p 驱动控制数据结构 + * @param {u32} *rpm 将获取到的数值写入到此地址 + */ +FError FTachoGetFanRPM(FTimerTachoCtrl *instance_p, u32 *rpm) +{ + u32 loop_cnt; + u32 raw_dat; + + FASSERT(instance_p); + + if (instance_p->isready != FT_COMPONENT_IS_READY || instance_p->config.work_mode != FTIMER_WORK_MODE_TACHO) + { + FTIMER_ERROR("device is not already or not work on TACHO_MODE!!!"); + return FTIMER_TACHO_ERR_NOT_READY; + } + + u32 cnt_num = FTIMER_CMPL_READ(instance_p); + + for (loop_cnt = 0;; loop_cnt++) + { + raw_dat = FTACHO_RESU_READ(instance_p); + /* wait for tacho result */ + if (raw_dat & FTACHO_REG_RESU_ISVALID) + { + break; + } + + if (loop_cnt < 300) + { + fsleep_millisec(20); //20ms + } + else + { + return FTIMER_TACHO_ERR_ABORT; + } + } + + raw_dat &= FTACHO_REG_RESU_MASK; + if (0 == raw_dat) + { + *rpm = 0; + } + else + { + /* calculate rpm */ + /* (60(second) * freq * tacho) / (2 * (cmp_l + 1)) cmp_l */ + *rpm = (TIMER_CLK_FREQ_HZ * 60 * raw_dat) / (2 * (cnt_num + 1)); + } + + return FTIMER_TACHO_SUCCESS; +} + +/** + * @name: FTachoGetCaptureCnt + * @msg: 获取capture模式下,tacho输入脉冲的个数 + * @return {u32}返回获取到的数值 + * @param {FTimerTachoCtrl} *instance_p 驱动控制数据结构 + */ +u32 FTachoGetCaptureCnt(FTimerTachoCtrl *instance_p) +{ + u32 cap_cnt = FTIMER_TACHO_SUCCESS; + + FASSERT(instance_p); + + if (instance_p->isready != FT_COMPONENT_IS_READY || instance_p->config.work_mode != FTIMER_WORK_MODE_CAPTURE) + { + FTIMER_ERROR("device is not already or not work on CAPTURE_MODE!!!"); + return FTIMER_TACHO_ERR_NOT_READY; + } + + /* read cap cnt */ + cap_cnt = FTIMER_CNTL_READ(instance_p); + + return cap_cnt; +} + +/** + * @name: FTimerSwithMode + * @msg: 切换定时器模式和tachometer-capture模式 + * @return {FError} 驱动初始化的错误码信息,FTIMER_TACHO_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + * @param {FTimerTachoCtrl} *instance_p 驱动控制数据结构 + * @param {FTimerTachoConfig} *pNewConfig 新的驱动配置数据结构 + */ +FError FTimerSwithMode(FTimerTachoCtrl *instance_p, FTimerTachoConfig *pNewConfig) +{ + FASSERT(instance_p && pNewConfig); + + u32 ret = FTIMER_TACHO_SUCCESS; + + if (instance_p->config.work_mode == pNewConfig->work_mode) + { + return FTIMER_TACHO_SUCCESS; + } + + /* disable and clear timer */ + u32 reg_val = FTIMER_CTRL_READ(instance_p); + reg_val &= (~FTIMER_REG_ENABLE); + reg_val |= FTIMER_REG_CNT_CLR; + FTIMER_CTRL_WRITE(instance_p, reg_val); + + if (FTIMER_WORK_MODE_TIMER == pNewConfig->work_mode) + { + ret = FTimerInit(instance_p, pNewConfig); + } + else + { + ret = FTachoInit(instance_p, pNewConfig); + } + + return ret; +} + +/** + * @name: FTachoSetCntPeriod + * @msg: 配置 tach计数周期 = pulse_num + * @return {void} + * @param {FTimerTachoCtrl} *instance_p 驱动控制数据结构 + * @param {u32}计数ticks + */ +void FTachoSetCntPeriod(FTimerTachoCtrl *instance_p, u32 ticks) +{ + FTIMER_CMPL_WRITE(instance_p, ticks); +} + +/** + * @name: FTachoSetOverLimit + * @msg: 预设tacho的最大值 + * @return {void} + * @param {FTimerTachoCtrl} *instance_p 驱动控制数据结构 + * @param {u32}上限值 + */ +void FTachoSetOverLimit(FTimerTachoCtrl *instance_p, u32 overLim) +{ + FTACHO_OVER_WRITE(instance_p, overLim); +} + +/** + * @name: FTachoSetUnderLimit + * @msg: 预设tacho的最小值 + * @return {void} + * @param {FTimerTachoCtrl} *instance_p 驱动控制数据结构 + * @param {u32}下限值 + */ +void FTachoSetUnderLimit(FTimerTachoCtrl *instance_p, u32 underLim) +{ + FTACHO_UNDER_WRITE(instance_p, underLim); +} + +/** + * @name: FTachoDeInit + * @msg: 去初始化,寄存器复位,结构体参数置0 + * @return {void} + * @param {FTimerTachoCtrl} *instance_p 驱动控制数据结构 + */ +void FTachoDeInit(FTimerTachoCtrl *instance_p) +{ + FASSERT(instance_p); + + /* reset reg*/ + FTimerSoftwareReset(instance_p); + + instance_p->isready = 0; + memset(instance_p, 0, sizeof(*instance_p)); + + return; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/timer/ftimer_tacho/ftimer.c b/bsp/phytium/libraries/standalone/drivers/timer/ftimer_tacho/ftimer.c new file mode 100644 index 0000000000..3b10410b4c --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/timer/ftimer_tacho/ftimer.c @@ -0,0 +1,459 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: ftimer.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:09:49 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +/***************************** Include Files *********************************/ +#include +#include "fassert.h" +#include "ftimer_tacho_hw.h" +#include "ftimer_tacho.h" + + +/************************** Function Prototypes ******************************/ +/** + * @name: FTimerSoftwareReset + * @msg: 将控制器复位 + * @return {FError} 驱动初始化的错误码信息,FTIMER_TACHO_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + * @param {FTimerTachoCtrl} *instance_p 驱动控制数据结构 + */ +FError FTimerSoftwareReset(FTimerTachoCtrl *instance_p) +{ + FASSERT(instance_p); + + if (instance_p->isready != FT_COMPONENT_IS_READY) + { + FTIMER_ERROR("device is not already!!!"); + return FTIMER_TACHO_ERR_NOT_READY; + } + + u32 reg_val = FTIMER_CTRL_READ(instance_p); + u32 Timeout = 0; + reg_val |= FTIMER_REG_TACHO_RESET; + FTIMER_CTRL_WRITE(instance_p, reg_val); + + do + { + reg_val = FTIMER_CTRL_READ(instance_p); + Timeout++; + } + while ((reg_val & FTIMER_REG_TACHO_RESET) && (Timeout < FTIMER_TIMEOUT)); + + if (Timeout >= FTIMER_TIMEOUT) + { + FTIMER_ERROR("Software Reset Failed!!!"); + return FTIMER_TACHO_ERR_FAILED; + } + + return FTIMER_TACHO_SUCCESS; +} + +/** + * @name: FTimerStart + * @msg: 启动timer_tacho外设,根据不同的功能,开启使能位 + * @return {FError} 驱动初始化的错误码信息,FTIMER_TACHO_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + * @param {FTimerTachoCtrl} *instance_p 驱动控制数据结构 + */ +FError FTimerStart(FTimerTachoCtrl *instance_p) +{ + FASSERT(instance_p); + FASSERT(FT_COMPONENT_IS_READY == instance_p->isready); + + u32 reg_val; + reg_val = FTIMER_CTRL_READ(instance_p); + if (FTIMER_WORK_MODE_TIMER == instance_p->config.work_mode) + { + reg_val |= FTIMER_REG_ENABLE; + } + else + { + /* for tacho mode and capture mode */ + reg_val |= FTIMER_REG_ENABLE | FTACHO_REG_CAP_IN_ENABLE; + } + + FTIMER_CTRL_WRITE(instance_p, reg_val); + + return FTIMER_TACHO_SUCCESS; +} + +/** + * @name: FTimerStop + * @msg: 停止timer外设,根据不同的功能,关闭使能位,计数值停止并冻结 + * @return {FError} 驱动初始化的错误码信息,FTIMER_TACHO_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + * @param {FTimerTachoCtrl} *instance_p 驱动控制数据结构 + */ +FError FTimerStop(FTimerTachoCtrl *instance_p) +{ + FASSERT(instance_p); + + FASSERT(FT_COMPONENT_IS_READY == instance_p->isready); + + u32 reg_val = FTIMER_CTRL_READ(instance_p); + + if (FTIMER_WORK_MODE_TIMER == instance_p->config.work_mode) + { + reg_val &= (~FTIMER_REG_ENABLE); + } + else + { + /* for tacho mode and capture mode */ + reg_val &= (~FTIMER_REG_ENABLE) & (~FTACHO_REG_CAP_IN_ENABLE); + } + + FTIMER_CTRL_WRITE(instance_p, reg_val); + + return FTIMER_TACHO_SUCCESS; +} + +/** + * @name: TimerSwithBits + * @msg: 计数器32/64切换 + * @return {FError} 驱动初始化的错误码信息,FTIMER_TACHO_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + * @param {FTimerTachoCtrl} *instance_p 驱动控制数据结构 + */ +static FError TimerSwithBits(FTimerTachoCtrl *instance_p) +{ + u32 reg_val; + + FASSERT(instance_p); + + FASSERT(FT_COMPONENT_IS_READY == instance_p->isready); + + reg_val = FTIMER_CTRL_READ(instance_p); + + if (FTIMER_32_BITS == instance_p->config.timer_bits) + { + reg_val &= (~FTIMER_REG_CNT_SERIES_64BIT); + } + else if (FTIMER_64_BITS == instance_p->config.timer_bits) + { + reg_val |= FTIMER_REG_CNT_SERIES_64BIT; + } + else + { + FTIMER_ERROR("invalid input"); + return FTIMER_TACHO_ERR_INVAL_PARM; + } + + FTIMER_CTRL_WRITE(instance_p, reg_val); + + return FTIMER_TACHO_SUCCESS; +} + +/** + * @name: TimerForceLoad + * @msg: 强制更新位置位 + * @return {void} 无 + * @param {FTimerTachoCtrl} *instance_p 驱动控制数据结构 + */ +static void TimerForceLoad(FTimerTachoCtrl *instance_p) +{ + u32 reg_val; + + FASSERT(instance_p); + + FASSERT(FT_COMPONENT_IS_READY == instance_p->isready); + + reg_val = FTIMER_CTRL_READ(instance_p); + reg_val |= FTIMER_REG_TACHO_FORCE_LOAD; + FTIMER_CTRL_WRITE(instance_p, reg_val); + + return; +} + +/** + * @name: FTimerSetPeriod32 + * @msg: 设置32位计数模式下,计数器的compare的值,达到此值,如果开启中断,则开启中断 + * @return {FError} 驱动初始化的错误码信息,FTIMER_TACHO_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + * @param {FTimerTachoCtrl} *instance_p 驱动控制数据结构 + * @param {u32}NewCmpL + */ +FError FTimerSetPeriod32(FTimerTachoCtrl *instance_p, u32 new_cmp_l) +{ + FASSERT(instance_p); + + FASSERT(FT_COMPONENT_IS_READY == instance_p->isready); + + if (FTIMER_64_BITS == instance_p->config.timer_bits) + { + return FTIMER_TACHO_ERR_INVAL_PARM; + } + + /* update cmp val */ + FTIMER_CMPL_WRITE(instance_p, new_cmp_l); + + return FTIMER_TACHO_SUCCESS; +} + +/** + * @name: FTimerSetPeriod64 + * @msg: 设置64位计数模式下,计数器的compare的值,达到此值,如果开启中断,则开启中断 + * @return {FError} 驱动初始化的错误码信息,FTIMER_TACHO_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + * @param {FTimerTachoCtrl} *instance_p 驱动控制数据结构 + * @param {u64}ticks + */ +FError FTimerSetPeriod64(FTimerTachoCtrl *instance_p, u64 ticks) +{ + u32 low_cmp; + u32 up_cmp; + FASSERT(instance_p); + + FASSERT(FT_COMPONENT_IS_READY == instance_p->isready); + + if (FTIMER_32_BITS == instance_p->config.timer_bits) + { + return FTIMER_TACHO_ERR_INVAL_PARM; + } + + low_cmp = (u32)(GENMASK_ULL(31, 0) & ticks); + up_cmp = (u32)((GENMASK_ULL(63, 32) & ticks) >> 32); + + /* MUST write low 32 bit first !!! */ + FTIMER_CMPL_WRITE(instance_p, low_cmp); + FTIMER_CMPU_WRITE(instance_p, up_cmp); + + return FTIMER_TACHO_SUCCESS; +} + +/** + * @name: FTimerSetStartVal + * @msg: 设置计数器初始值 + * @return {FError} 驱动初始化的错误码信息,FTIMER_TACHO_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + * @param {FTimerTachoCtrl} *instance_p 驱动控制数据结构 + * @param {u32} cnt + */ +inline FError FTimerSetStartVal(FTimerTachoCtrl *instance_p, u32 cnt) +{ + u32 ret = FTIMER_TACHO_SUCCESS; + FASSERT(instance_p); + + FASSERT(FT_COMPONENT_IS_READY == instance_p->isready); + + ret = TimerSwithBits(instance_p); + if (FTIMER_TACHO_SUCCESS != ret) + { + return ret; + } + + FTIMER_STAR_WRITE(instance_p, cnt); + /* set force_load=1,invalid previous cmp val, + otherwise the previous cmp val still work */ + TimerForceLoad(instance_p); + + FTIMER_INFO("set start val 0x%x", FTIMER_STAR_READ(instance_p)); + + return ret; +} + + +/** + * @name: FTimerGetCurCnt32 + * @msg: 32位模式下,获取计数器当前计数值 + * @return {u32}返回当前计数器的值 + * @param {FTimerTachoCtrl} *instance_p 驱动控制数据结构 + */ +u32 FTimerGetCurCnt32(FTimerTachoCtrl *instance_p) +{ + FASSERT(instance_p); + + FASSERT(FT_COMPONENT_IS_READY == instance_p->isready); + + return FTIMER_CNTL_READ(instance_p); +} + +/** + * @name: FTimerGetCurCnt64 + * @msg: 64位模式下,获取计数器当前计数值 + * @return {u64}返回当前计数器的值 + * @param {FTimerTachoCtrl} *instance_p 驱动控制数据结构 + */ +inline u64 FTimerGetCurCnt64(FTimerTachoCtrl *instance_p) +{ + u64 cnt = 0; + FASSERT(instance_p); + + FASSERT(FT_COMPONENT_IS_READY == instance_p->isready); + + /* must read lower 32 bits first */ + cnt |= (u64)FTIMER_CNTL_READ(instance_p); + cnt |= (u64)(((u64)FTIMER_CNTU_READ(instance_p)) << 32); + return cnt; +} + +/** + * @name: FTimerInit + * @msg: 完成TimerTacho驱动实例的初始化,使之在就绪状态 + * @return {FError} 驱动初始化的错误码信息,FTIMER_TACHO_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + * @param {FTimerTachoCtrl} *instance_p 驱动控制数据结构 + * @param {FTimerTachoConfig} *config_p 驱动配置数据结构 + */ +FError FTimerInit(FTimerTachoCtrl *instance_p, const FTimerTachoConfig *config_p) +{ + FASSERT(instance_p && config_p); + + u32 reg_val = 0; + u32 Ret = FTIMER_TACHO_SUCCESS; + + if ((FTIMER_ONCE_CMP == config_p->cmp_type) && + (FTIMER_FREE_RUN != config_p->timer_mode)) + { + FTIMER_ERROR("time mode shall be free-run when use once timer!!"); + return FTIMER_TACHO_ERR_INVAL_PARM; + } + + if (instance_p->isready == FT_COMPONENT_IS_READY) + { + FTIMER_INFO("device is already initialized.!!!\r\n"); + return FTIMER_TACHO_ERR_IS_READ; + } + + if (FTIMER_WORK_MODE_TIMER == config_p->work_mode) + { + reg_val |= FTIMER_REG_TACHO_MODE_TIMER; + } + else + { + FTIMER_ERROR("not support"); + return FTIMER_TACHO_ERR_INVAL_PARM; + } + + if (FTIMER_FREE_RUN == config_p->timer_mode) + { + reg_val &= (~FTIMER_REG_CNT_RESTART); + } + else if (FTIMER_RESTART == config_p->timer_mode) + { + reg_val |= FTIMER_REG_CNT_RESTART; + } + else + { + FTIMER_ERROR("invalid input"); + return FTIMER_TACHO_ERR_INVAL_PARM; + } + + if (FTIMER_32_BITS == config_p->timer_bits) + { + reg_val &= (~FTIMER_REG_CNT_SERIES_64BIT); + } + else if (FTIMER_64_BITS == config_p->timer_bits) + { + reg_val |= FTIMER_REG_CNT_SERIES_64BIT; + } + else + { + FTIMER_ERROR("invalid input"); + return FTIMER_TACHO_ERR_INVAL_PARM; + } + + if (FTIMER_ONCE_CMP == config_p->cmp_type) + { + reg_val |= FTIMER_REG_MODE_ONCE; + } + else if (FTIMER_CYC_CMP == config_p->cmp_type) + { + reg_val &= (~FTIMER_REG_MODE_ONCE); + } + else + { + FTIMER_ERROR("invalid input"); + return FTIMER_TACHO_ERR_INVAL_PARM; + } + + if (TRUE == config_p->force_load) + { + reg_val |= FTIMER_REG_TACHO_FORCE_LOAD; + } + + if (TRUE == config_p->clear_cnt) + { + reg_val |= FTIMER_REG_CNT_CLR; + } + + /*use input config*/ + if (config_p != &instance_p->config) + { + instance_p->config = *config_p; + } + + FTIMER_CTRL_WRITE(instance_p, reg_val); + instance_p->isready = FT_COMPONENT_IS_READY; + + return FTIMER_TACHO_SUCCESS; +} + +void FTimerDeInit(FTimerTachoCtrl *instance_p) +{ + FASSERT(instance_p); + + /* stop timer first */ + FTimerStop(instance_p); + /* reset reg*/ + FTimerSoftwareReset(instance_p); + + instance_p->isready = 0; + memset(instance_p, 0, sizeof(*instance_p)); + + return; +} + +/** + * @name: FTimeSettingDump + * @msg: 打印寄存器信息 + * @return {FError} 驱动初始化的错误码信息,FTIMER_TACHO_SUCCESS 表示初始化成功,其它返回值表示初始化失败 + * @param {FTimerTachoCtrl} *instance_p 驱动控制数据结构 + */ +FError FTimeSettingDump(const FTimerTachoCtrl *instance_p) +{ + FASSERT(instance_p); + u32 CtrlReg = FTIMER_CTRL_READ(instance_p); + boolean is64Bit = ((CtrlReg & FTIMER_REG_CNT_SERIES_64BIT) != 0); + + FASSERT(FT_COMPONENT_IS_READY == instance_p->isready); + + printf("ctrl: \r\n"); + printf("===%d-bit timer\r\n", is64Bit ? 64 : 32); + printf("===timer enabled: %d\r\n", (CtrlReg & FTIMER_REG_ENABLE) ? 1 : 0); + printf("===timer mode: %d\r\n", (CtrlReg & FTIMER_REG_TACHO_MODE_TIMER) ? 1 : 0); + printf("===once timer: %d\r\n", (CtrlReg & FTIMER_REG_MODE_ONCE) ? 1 : 0); + printf("===restart mode: %d\r\n", (CtrlReg & FTIMER_REG_CNT_RESTART) ? 1 : 0); + printf("===in reset: %d\r\n", (CtrlReg & FTIMER_REG_TACHO_RESET) ? 1 : 0); + printf("===force load: %d\r\n", (CtrlReg & FTIMER_REG_TACHO_FORCE_LOAD) ? 1 : 0); + printf("===clear cnt: %d\r\n", (CtrlReg & FTIMER_REG_CNT_CLR) ? 1 : 0); + + printf("start cnt: 0x%08x\r\n", FTIMER_STAR_READ(instance_p)); + if (is64Bit) + { + printf("cmp low: 0x%08x", FTIMER_CMPL_READ(instance_p)); + printf("high: 0x%08x\r\n", FTIMER_CMPU_READ(instance_p)); + printf("cur cnt: low: 0x%08x", FTIMER_CNTL_READ(instance_p)); + printf("high: 0x%08x\r\n", FTIMER_CNTU_READ(instance_p)); + } + else + { + printf("cmp low: 0x%08x\r\n", FTIMER_CMPL_READ(instance_p)); + printf("cur cnt: low: 0x%08x", FTIMER_CNTL_READ(instance_p)); + } + + printf("intr mask: 0x%08x\r\n", FTIMER_INTR_M_READ(instance_p)); + printf("intr status: 0x%08x\r\n", FTIMER_INTR_S_READ(instance_p)); + + return FTIMER_TACHO_SUCCESS; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/timer/ftimer_tacho/ftimer_tacho.h b/bsp/phytium/libraries/standalone/drivers/timer/ftimer_tacho/ftimer_tacho.h new file mode 100644 index 0000000000..8f8b4a9edf --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/timer/ftimer_tacho/ftimer_tacho.h @@ -0,0 +1,227 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: ftimer_tacho.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:09:43 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef BSP_DRIVERS_E2000_TIMER_TACHO_H +#define BSP_DRIVERS_E2000_TIMER_TACHO_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ftypes.h" +#include "fdebug.h" +#include "ferror_code.h" + +#define FTIMER_TACHO_SUCCESS FT_SUCCESS +#define FTIMER_TACHO_ERR_INVAL_PARM FT_MAKE_ERRCODE(ErrModBsp, ErrBspTimer, 1) +#define FTIMER_TACHO_ERR_NOT_READY FT_MAKE_ERRCODE(ErrModBsp, ErrBspTimer, 2) +#define FTIMER_TACHO_ERR_INIT_FAILED FT_MAKE_ERRCODE(ErrModBsp, ErrBspTimer, 3) +#define FTIMER_TACHO_ERR_NOT_SUPPORT FT_MAKE_ERRCODE(ErrModBsp, ErrBspTimer, 4) +#define FTIMER_TACHO_ERR_INVAL_STATE FT_MAKE_ERRCODE(ErrModBsp, ErrBspTimer, 5) +#define FTIMER_TACHO_ERR_IS_READ FT_MAKE_ERRCODE(ErrModBsp, ErrBspTimer, 6) +#define FTIMER_TACHO_ERR_ABORT FT_MAKE_ERRCODE(ErrModBsp, ErrBspTimer, 7) +#define FTIMER_TACHO_ERR_FAILED FT_MAKE_ERRCODE(ErrModBsp, ErrBspTimer, 8) + +#define FTIMER_DEBUG_TAG "TIMER" +#define FTIMER_ERROR(format, ...) FT_DEBUG_PRINT_E(FTIMER_DEBUG_TAG, format, ##__VA_ARGS__) +#define FTIMER_INFO(format, ...) FT_DEBUG_PRINT_I(FTIMER_DEBUG_TAG, format, ##__VA_ARGS__) +#define FTIMER_DEBUG(format, ...) FT_DEBUG_PRINT_D(FTIMER_DEBUG_TAG, format, ##__VA_ARGS__) + +#define FTACHO_DEBUG_TAG "TACHO" +#define FTACHO_ERROR(format, ...) FT_DEBUG_PRINT_E(FTACHO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FTACHO_INFO(format, ...) FT_DEBUG_PRINT_I(FTACHO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FTACHO_DEBUG(format, ...) FT_DEBUG_PRINT_D(FTACHO_DEBUG_TAG, format, ##__VA_ARGS__) + +typedef enum +{ + /*TimerTacho mode */ + FTIMER_WORK_MODE_TIMER = 0, + FTIMER_WORK_MODE_TACHO, + FTIMER_WORK_MODE_CAPTURE +} FTimerTachoModeType; + +typedef enum +{ + /*Timer count mode*/ + FTIMER_FREE_RUN = 0, + FTIMER_RESTART +} FTimerCntModeType; + +typedef enum +{ + FTIMER_32_BITS = 0, + FTIMER_64_BITS +} FTimerBitsType; + +typedef enum +{ + FTIMER_ONCE_CMP = 0, + FTIMER_CYC_CMP +} FTimerCmpType; + +typedef enum +{ + FTACHO_EVENT_OVER = 0, /*tacho超速事件*/ + FTACHO_EVENT_UNDER, /*tacho低速事件*/ + FTIMER_EVENT_ROLL_OVER, /*计数器翻转事件*/ + FTIMER_EVENT_ONCE_CMP, /*单次定时输出事件*/ + FTIMER_EVENT_CYC_CMP, /*重复定时输出事件*/ + FTACHO_EVENT_CAPTURE, /*tacho输入捕获事件*/ + + FMAX_TIMER_TACHO_EVENT +} FTimerTachoEventType; + +typedef enum +{ + FTACHO_FALLING_EDGE = 0, /*下降沿触发模式*/ + FTACHO_RISING_EDGE, /*上升沿触发模式*/ + FTACHO_DOUBLE_EDGE /*双边沿触发模式*/ +} FTachoEdgeType; + +typedef enum +{ + FTACHO_JITTER_LEVEL0 = 0, /*消抖等级*/ + FTACHO_JITTER_LEVEL1, + FTACHO_JITTER_LEVEL2, + FTACHO_JITTER_LEVEL3, +} FTachoJitterLevelType; + +typedef struct +{ + u32 id; /* id of timer tacho */ + char name[12]; /* instance name */ + u32 irq_priority; /* intr priority */ + u32 work_mode; /* timer/tacho/capture mode */ + /* for timer function */ + u32 timer_mode; /* free-run/restart */ + u32 timer_bits; /* 32/64 bits */ + u32 cmp_type; /* once/cycle cmp */ + boolean clear_cnt; /* clear timer counts */ + boolean force_load; /* start count from start val */ + /* for tacho function */ + u32 edge_mode; /* rising/falling/double */ + u32 jitter_level; /* jitter level */ + u32 plus_num; /* plus_num of period to calculate rpm */ + u32 captue_cnt; /* in capture mode, when cnt reach this val, intr asserted */ +} FTimerTachoConfig; + +typedef void (*FTimerEventHandler)(void *instance_p); + +typedef struct +{ + FTimerTachoConfig config; /* Current active configs */ + boolean isready; /* Device is initialized and ready */ + FTimerEventHandler evt_handlers[FMAX_TIMER_TACHO_EVENT];/* event handler for interrupt */ +} FTimerTachoCtrl; + +/* Time & Tacho API */ +/*将控制器复位*/ +FError FTimerSoftwareReset(FTimerTachoCtrl *instance_p); + +/*获取中断设置*/ +u32 FTimerGetInterruptMask(FTimerTachoCtrl *instance_p); + +/*设置中断,根据不同的intrType,将对于的中断mask置位*/ +void FTimerSetInterruptMask(FTimerTachoCtrl *instance_p, + FTimerTachoEventType intrType, + boolean enable); + +/*启动timer_tacho外设,根据不同的功能,开启使能位*/ +FError FTimerStart(FTimerTachoCtrl *instance_p); + +/*停止timer外设,根据不同的功能,关闭使能位,计数值停止并冻结*/ +FError FTimerStop(FTimerTachoCtrl *instance_p); + +/*用于timer 与 tacho-capture两种模式的切换,切换需要失能和清除计数器*/ +FError FTimerSwithMode(FTimerTachoCtrl *instance_p, FTimerTachoConfig *new_config_p); + +/* 注册中断事件处理回调函数 */ +void FTimerRegisterEvtCallback(FTimerTachoCtrl *instance_p, + FTimerTachoEventType evt, + FTimerEventHandler callback); + +/*打印寄存器信息*/ +FError FTimeSettingDump(const FTimerTachoCtrl *instance_p); + +/*TimerTacho中断处理函数,如果注册回调函数,则跳转到回调函数*/ +void FTimerTachoIntrHandler(s32 vector, void *param); + +/*根据工作模式和状态设置相应的中断*/ +void FTimerTachoSetIntr(FTimerTachoCtrl *instance_p); + +/* Timer API */ +/*完成TimerTacho驱动实例的初始化,使之在就绪状态*/ +FError FTimerInit(FTimerTachoCtrl *instance_p, const FTimerTachoConfig *config_p); + +/*获取Timer驱动的默认配置参数*/ +void FTimerGetDefConfig(u32 timer_id, FTimerTachoConfig *config_p); + +/*设置32位计数模式下,计数器的compare的值,达到此值,如果开启中断,则开启中断*/ +FError FTimerSetPeriod32(FTimerTachoCtrl *instance_p, u32 ticks); + +/*设置64位计数模式下,计数器的compare的值,达到此值,如果开启中断,则开启中断*/ +FError FTimerSetPeriod64(FTimerTachoCtrl *instance_p, u64 ticks); + +/*32位模式下,获取计数器当前计数值*/ +u32 FTimerGetCurCnt32(FTimerTachoCtrl *instance_p); + +/*64位模式下,获取计数器当前计数值*/ +u64 FTimerGetCurCnt64(FTimerTachoCtrl *instance_p); + +/*设置计数器初始值*/ +FError FTimerSetStartVal(FTimerTachoCtrl *instance_p, u32 cnt); + +/*完成Timer驱动实例去使能,清零实例数据*/ +void FTimerDeInit(FTimerTachoCtrl *instance_p); + +/* Tacho API */ +/*完成Tacho驱动实例的初始化,使之在就绪状态*/ +FError FTachoInit(FTimerTachoCtrl *instance_p, const FTimerTachoConfig *config_p); + +/*获取Tacho驱动的默认配置参数*/ +void FTachoGetDefConfig(u32 tacho_id, FTimerTachoConfig *config_p); + +/*配置tach转速周期= pulse_num*/ +void FTachoSetCntPeriod(FTimerTachoCtrl *instance_p, u32 ticks); + +/*预设的 tach 最大值*/ +void FTachoSetOverLimit(FTimerTachoCtrl *instance_p, u32 overLim); + +/*预设的 tach 最小值*/ +void FTachoSetUnderLimit(FTimerTachoCtrl *instance_p, u32 underLim); + +/*根据预设采样周期的值,来获取风扇的转速值*/ +FError FTachoGetFanRPM(FTimerTachoCtrl *instance_p, u32 *rpm); + +/*获取capture模式下tacho输入脉冲的个数*/ +u32 FTachoGetCaptureCnt(FTimerTachoCtrl *instance_p); + +/*完成Tacho驱动实例去使能,清零实例数据*/ +void FTachoDeInit(FTimerTachoCtrl *instance_p); + +#ifdef __cplusplus +} +#endif + +#endif // ! \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/timer/ftimer_tacho/ftimer_tacho_g.c b/bsp/phytium/libraries/standalone/drivers/timer/ftimer_tacho/ftimer_tacho_g.c new file mode 100644 index 0000000000..8c4868c3af --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/timer/ftimer_tacho/ftimer_tacho_g.c @@ -0,0 +1,62 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: ftimer_tacho_g.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:09:07 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +/***************************** Include Files *********************************/ + +#include +#include "fparameters.h" +#include "fassert.h" +#include "ftimer_tacho_hw.h" +#include "ftimer_tacho.h" + +#define TACHO_PERIOD 1000000 /* 1000000/50000000 = 0.02s*/ +/************************** Function Prototypes ******************************/ +void FTimerGetDefConfig(u32 timer_id, FTimerTachoConfig *config_p) +{ + FASSERT((timer_id < TIMER_NUM) && (NULL != config_p)); + + memset(config_p, 0, sizeof(FTimerTachoConfig)); + config_p->id = timer_id; + config_p->work_mode = FTIMER_WORK_MODE_TIMER; + config_p->timer_mode = FTIMER_RESTART; + config_p->timer_bits = FTIMER_32_BITS; + config_p->cmp_type = FTIMER_CYC_CMP; + config_p->clear_cnt = FALSE; + config_p->force_load = TRUE; +} + +void FTachoGetDefConfig(u32 tacho_id, FTimerTachoConfig *config_p) +{ + FASSERT((tacho_id < TACHO_NUM) && (NULL != config_p)); + + memset(config_p, 0, sizeof(FTimerTachoConfig)); + config_p->id = tacho_id; + config_p->timer_bits = FTIMER_32_BITS; + config_p->work_mode = FTIMER_WORK_MODE_TACHO; + config_p->timer_mode = FTIMER_RESTART; + config_p->edge_mode = FTACHO_RISING_EDGE; + config_p->jitter_level = 0; + config_p->plus_num = TACHO_PERIOD;/*采样周期越长,能够检测到的单位时间脉冲越多,能够检测更小的频率*/ + config_p->clear_cnt = FALSE; + config_p->force_load = TRUE; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/timer/ftimer_tacho/ftimer_tacho_hw.h b/bsp/phytium/libraries/standalone/drivers/timer/ftimer_tacho/ftimer_tacho_hw.h new file mode 100644 index 0000000000..e9bc4d6bd1 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/timer/ftimer_tacho/ftimer_tacho_hw.h @@ -0,0 +1,163 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: ftimer_tacho_hw.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:09:15 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef BSP_DRIVERS_E2000_TIMER_HW_H +#define BSP_DRIVERS_E2000_TIMER_HW_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "fio.h" +#include "ftypes.h" +#include "fkernel.h" +#include "fparameters.h" + +/* register offset */ +#define FTIMER_CTRL_REG_OFFSET (0x0) /*Timer or Tachometer 控制寄存器*/ +#define FTACHO_RESULT_REG_OFFSET (0x4) /*一个转速周期内的时钟周期计数结果*/ +#define FTIMER_COMP_VALU_OFFSET (0x8) /*定时计数值高 32 位*/ +#define FTIMER_COMP_VALL_OFFSET (0x1c) /*timer模式下:定时计数值低32位,tacho模式下:配置tach转速周期 = pulse_num*/ +#define FTIMER_CNT_VALU_OFFSET (0x20) /*计数器当前计数值高 32 位*/ +#define FTIMER_CNT_VALL_OFFSET (0x24) /*计数器当前计数值低 32 位*/ +#define FTIMER_INTR_MASK_OFFSET (0x28) /*中断使能寄存器*/ +#define FTIMER_INTR_STATUS_OFFSET (0x2c) /*中断状态寄存器*/ +#define FTACHO_OVER_LIM_OFFSET (0x30) /*预设的 tach 最大值*/ +#define FTACHO_UNDER_LIM_OFFSET (0x34) /*预设的 tach 最小值*/ +#define FTIMER_START_VAL_OFFSET (0x38) /*计数器初始值*/ + +/* bit set */ +/* timer ctrl */ +#define FTIMER_REG_TACHO_MODE_MASK (0x3 << 0) /*bit [1:0] RW*/ +#define FTIMER_REG_TACHO_MODE_TIMER (0x0 << 0) /*定时器功能*/ +#define FTIMER_REG_TACHO_MODE_TACHO (0x1 << 0) /*tachometer 功能*/ +#define FTIMER_REG_TACHO_MODE_CAPTURE (0x2 << 0) /*输入 capture 功能*/ + +#define FTIMER_REG_TACHO_RESET (0x1 << 2) /*in reset status*/ + +#define FTIMER_REG_TACHO_FORCE_LOAD (0x1 << 3) /*force to update*/ +#define FTIMER_REG_TACHO_CAPTURE_ENABLE (0x1 << 4) /*enable input capture*/ +#define FTIMER_REG_TACHO_CAPTURE_CNT_MASK (0x7f << 5) /*in capture mode, cause intr when egde counting reach this val*/ +#define FTIMER_REG_TACHO_CAPTURE_CNT_SHIFT 5 /*cap_cnt shift*/ + +#define FTACHO_REG_ANTI_JITTER_MASK (0x3 << 18) /*anti jitter num = N + 1*/ +#define FTACHO_REG_ANTI_JITTER_SHIFT 18 /*anti_jitter_number shift*/ + +#define FTACHO_REG_MODE_MASK (0x3 << 20) /*select tacho input mode*/ +#define FTACHO_REG_MODE_FALLING_EDGE (0x0 << 20) /*select falling edge*/ +#define FTACHO_REG_MODE_RISING_EDGE (0x1 << 20) /*select rising edge*/ +#define FTACHO_REG_MODE_DOUBLE_EDGE (0x2 << 20) /*select both falling edge and rising*/ + +#define FTIMER_REG_CNT_RESTART (0x1 << 22) /*select timer restart mode*/ +#define FTIMER_REG_CNT_FREERUN (0x0 << 22) /*select timer free_run mode*/ + +#define FTIMER_REG_CNT_SERIES_64BIT (0x1 << 24) /*select counter bit 64 */ +#define FTIMER_REG_CNT_SERIES_32BIT (0x0 << 24) /*select counter bit 32 */ + +#define FTIMER_REG_ENABLE (0x1 << 25) /*enable timer count*/ + +#define FTIMER_REG_CNT_CLR (0x1 << 26) /*clear counter*/ +#define FTIMER_REG_CNT_NOCLR (0x0 << 26) /*don't clear counter*/ + +#define FTIMER_REG_MODE_ONCE (0x1 << 27) /*one time timer*/ +#define FTIMER_REG_MODE_CYC (0x0 << 27) /*cycle timer*/ + +#define FTACHO_REG_CAP_IN_ENABLE (0x1 << 31) /*enable tacho capture input*/ + +/* tacho result */ +#define FTACHO_REG_RESU_MASK GENMASK(30, 0) /*bit [30:0], tacho result*/ +#define FTACHO_REG_RESU_ISVALID (0x1 << 31) /*tacho result is valid*/ + +/* tacho over */ +#define FTACHO_REG_OVER_MASK GENMASK(30, 0) /*tacho max value mask*/ + +/* tacho under */ +#define FTACHO_REG_UNDER_MASK GENMASK(30, 0) /*tacho min value mask*/ + +/* intr mask */ +#define FTACHO_OVER_INTR_EN (0x1 << 0) /*tach 超转速中断使能*/ +#define FTACHO_UNDER_INTR_EN (0x1 << 1) /*tach 低于转速中断使能*/ +#define FTIMER_ROLLOVER_INTR_EN (0x1 << 2) /*计数器翻转中断使能*/ +#define FTIMER_ONCECMP_INTR_EN (0x1 << 3) /*一次定时输出中断使能*/ +#define FTIMER_CYCCMP_INTR_EN (0x1 << 4) /*重复定时输出中断使能*/ +#define FTACHO_CAPTURE_INTR_EN (0x1 << 5) /*tach 输入捕获中断使能*/ + +#define FTIMER_ALL_INTR_EN (FTIMER_ROLLOVER_INTR_EN | FTIMER_ONCECMP_INTR_EN | FTIMER_CYCCMP_INTR_EN) + +/* intr status */ +#define FTACHO_OVER_INTR_STATUS (0x1 << 0) /*tach 超转速中断*/ +#define FTACHO_UNDER_INTR_STATUS (0x1 << 1) /*tach 低于转速中断*/ +#define FTIMER_ROLLOVER_INTR_STATUS (0x1 << 2) /*计数器翻转中断*/ +#define FTIMER_ONCECMP_INTR_STATUS (0x1 << 3) /*一次定时输出中断*/ +#define FTIMER_CYCCMP_INTR_STATUS (0x1 << 4) /*重复定时输出中断*/ +#define FTACHO_CAPTURE_INTR_STATUS (0x1 << 5) /*tach 输入捕获中断*/ + +/** + * @name: FTIMER_READ_REG32 + * @msg: 读取定时器寄存器 + * @param {u32} addr 定时器的基地址 + * @param {u32} reg_offset 定时器的寄存器的偏移 + * @return {u32} 寄存器参数 + */ +#define FTIMER_READ_REG32(addr, reg_offset) FtIn32(addr + (u32)reg_offset) + +/** + * @name: FTIMER_WRITE_REG32 + * @msg: 写入定时器寄存器 + * @param {u32} addr 定时器的基地址 + * @param {u32} reg_offset 定时器的寄存器的偏移 + * @param {u32} reg_value 写入寄存器参数 + * @return {void} + */ +#define FTIMER_WRITE_REG32(addr, reg_offset, reg_value) FtOut32(addr + (u32)reg_offset, (u32)reg_value) + +#define FTIMER_TIMEOUT 3000 /*超时时间*/ + +#define FTIMER_BASE_ADDR(instance_p) TIMER_TACHO_BASE_ADDR((instance_p)->config.id) /*获取设备基地址*/ +/*read and write reg value*/ +#define FTIMER_CTRL_READ(instance_p) FTIMER_READ_REG32(FTIMER_BASE_ADDR(instance_p), FTIMER_CTRL_REG_OFFSET) +#define FTIMER_CTRL_WRITE(instance_p, regVal) FTIMER_WRITE_REG32(FTIMER_BASE_ADDR(instance_p), FTIMER_CTRL_REG_OFFSET, (regVal)) +#define FTIMER_CMPL_READ(instance_p) FTIMER_READ_REG32(FTIMER_BASE_ADDR(instance_p), FTIMER_COMP_VALL_OFFSET) +#define FTIMER_CMPL_WRITE(instance_p, cmpL) FTIMER_WRITE_REG32(FTIMER_BASE_ADDR(instance_p), FTIMER_COMP_VALL_OFFSET, (cmpL)) +#define FTIMER_CMPU_READ(instance_p) FTIMER_READ_REG32(FTIMER_BASE_ADDR(instance_p), FTIMER_COMP_VALU_OFFSET) +#define FTIMER_CMPU_WRITE(instance_p, cmpU) FTIMER_WRITE_REG32(FTIMER_BASE_ADDR(instance_p), FTIMER_COMP_VALU_OFFSET, (cmpU)) +#define FTIMER_STAR_WRITE(instance_p, cnt) FTIMER_WRITE_REG32(FTIMER_BASE_ADDR(instance_p), FTIMER_START_VAL_OFFSET, (cnt)) +#define FTIMER_STAR_READ(instance_p) FTIMER_READ_REG32(FTIMER_BASE_ADDR(instance_p), FTIMER_START_VAL_OFFSET) +#define FTIMER_CNTL_READ(instance_p) FTIMER_READ_REG32(FTIMER_BASE_ADDR(instance_p), FTIMER_CNT_VALL_OFFSET) +#define FTIMER_CNTU_READ(instance_p) FTIMER_READ_REG32(FTIMER_BASE_ADDR(instance_p), FTIMER_CNT_VALU_OFFSET) +#define FTACHO_RESU_READ(instance_p) FTIMER_READ_REG32(FTIMER_BASE_ADDR(instance_p), FTACHO_RESULT_REG_OFFSET) +#define FTACHO_OVER_WRITE(instance_p, over) FTIMER_WRITE_REG32(FTIMER_BASE_ADDR(instance_p), FTACHO_OVER_LIM_OFFSET, (over)) +#define FTACHO_UNDER_WRITE(instance_p, under) FTIMER_WRITE_REG32(FTIMER_BASE_ADDR(instance_p), FTACHO_UNDER_LIM_OFFSET, (under)) + +#define FTIMER_INTR_M_READ(instance_p) FTIMER_READ_REG32(FTIMER_BASE_ADDR(instance_p), FTIMER_INTR_MASK_OFFSET) +#define FTIMER_INTR_M_WRITE(instance_p, mask) FTIMER_WRITE_REG32(FTIMER_BASE_ADDR(instance_p), FTIMER_INTR_MASK_OFFSET, (mask)) +#define FTIMER_INTR_S_READ(instance_p) FTIMER_READ_REG32(FTIMER_BASE_ADDR(instance_p), FTIMER_INTR_STATUS_OFFSET) +/* write 1 clear */ +#define FTIMER_INTR_S_CLEAR(instance_p, status) FTIMER_WRITE_REG32(FTIMER_BASE_ADDR(instance_p), FTIMER_INTR_STATUS_OFFSET, (status)) + +#ifdef __cplusplus +} +#endif + +#endif // ! \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/timer/ftimer_tacho/ftimer_tacho_intr.c b/bsp/phytium/libraries/standalone/drivers/timer/ftimer_tacho/ftimer_tacho_intr.c new file mode 100644 index 0000000000..015a0799df --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/timer/ftimer_tacho/ftimer_tacho_intr.c @@ -0,0 +1,211 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: ftimer_tacho_intr.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:09:36 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +/***************************** Include Files *********************************/ +#include "fassert.h" +#include "ftimer_tacho.h" +#include "ftimer_tacho_hw.h" +#include "finterrupt.h" + +/************************** Constant Definitions *****************************/ + +static const u32 g_intrBits[FMAX_TIMER_TACHO_EVENT] = +{ + FTACHO_OVER_INTR_EN, FTACHO_UNDER_INTR_EN, + FTIMER_ROLLOVER_INTR_EN, FTIMER_ONCECMP_INTR_EN, FTIMER_CYCCMP_INTR_EN, + FTACHO_CAPTURE_INTR_EN +}; + +static const u32 g_intrStats[FMAX_TIMER_TACHO_EVENT] = +{ + FTACHO_OVER_INTR_STATUS, FTACHO_UNDER_INTR_STATUS, + FTIMER_ROLLOVER_INTR_STATUS, FTIMER_ONCECMP_INTR_STATUS, FTIMER_CYCCMP_INTR_STATUS, + FTACHO_CAPTURE_INTR_STATUS +}; + +/************************** Function Prototypes ******************************/ +/** + * @name: FTimerGetInterruptMask + * @msg: 获取中断设置 + * @return {u32} 返回中断寄存器的值 + * @param {FTimerTachoCtrl} *instance_p 驱动控制数据结构 + */ +u32 FTimerGetInterruptMask(FTimerTachoCtrl *instance_p) +{ + FASSERT(instance_p); + return FTIMER_INTR_M_READ(instance_p); +} + +/** + * @name: FTimerSetInterruptMask + * @msg: 设置中断 + * @return {void} + * @param {FTimerTachoCtrl} *instance_p 驱动控制数据结构 + * @param {enum} intrType 中断枚举类型 + * @param {boolean} enable 使能或者失能 + */ +void FTimerSetInterruptMask(FTimerTachoCtrl *instance_p, + FTimerTachoEventType intrType, + boolean enable) +{ + FASSERT(instance_p && intrType < FMAX_TIMER_TACHO_EVENT); + u32 mask = FTIMER_INTR_M_READ(instance_p); + + if (enable) + { + mask |= g_intrBits[intrType]; + } + else + { + mask &= ~g_intrBits[intrType]; + } + + FTIMER_INTR_M_WRITE(instance_p, mask); +} + +/** + * @name: FTimerTachoIntrHandler + * @msg: 中断服务函数,跳转到自己注册的回调函数(如果注册,没有注册则跳转到FTimerDefaultEvtCallback) + * @return {void} + * @param {s32} vector,中断向量号,此处不关心此参数 + * @param {void} *param, 中断输入参数, 指向FTimerTachoCtrl的驱动控制实例 + */ +void FTimerTachoIntrHandler(s32 vector, void *param) +{ + FASSERT(param); + + FTimerTachoCtrl *instance_p = (FTimerTachoCtrl *)param; + const u32 intr_status = FTIMER_INTR_S_READ(instance_p); + u32 loop; + FTimerEventHandler evtHandler; + + FTIMER_INFO("intr entered cause: 0x%x.\r\n", intr_status); + + /* check intr status bit by bit */ + for (loop = 0; loop < FMAX_TIMER_TACHO_EVENT; loop++) + { + evtHandler = instance_p->evt_handlers[loop]; + + if ((g_intrStats[loop] & intr_status) && (NULL != evtHandler)) + { + evtHandler((void *)instance_p); + } + } + + FTIMER_INTR_S_CLEAR(instance_p, intr_status); +} + +/** + * @name: FTimerDefaultEvtCallback + * @msg: 默认中断回调函数 + * @return {void} + * @param {void} *param,函数输入参数指向FTimerTachoCtrl的驱动控制实例 + */ +static void FTimerDefaultEvtCallback(void *param) +{ + FASSERT(param); + FTimerTachoCtrl *instance_p = (FTimerTachoCtrl *)param; + const u32 intr_status = FTIMER_INTR_S_READ(instance_p); + + FTIMER_INFO("timer id: 0x%x, intr cause: 0x%x.\r\n", intr_status); +} + +/** + * @name: FTimerRegisterEvtCallback + * @msg: 回调函数的注册函数 + * @return {void} + * @param {FTimerTachoCtrl} *instance_p 驱动控制数据结构 + * @param {enum} intrType 中断枚举类型 + * @param {FTimerEventHandler} + */ +void FTimerRegisterEvtCallback(FTimerTachoCtrl *instance_p, + FTimerTachoEventType evt, + FTimerEventHandler callback) +{ + FASSERT(instance_p && evt < FMAX_TIMER_TACHO_EVENT); + instance_p->evt_handlers[evt] = callback; +} + +/** + * @name: FTimerTachoSetIntr + * @msg: 根据工作模式和状态设置相应的中断 + * @return {void} + * @param {FTimerTachoCtrl} *instance_p 驱动控制数据结构 + */ +void FTimerTachoSetIntr(FTimerTachoCtrl *instance_p) +{ + FASSERT(instance_p); + + if (FTIMER_WORK_MODE_TIMER == instance_p->config.work_mode) + { + if (FTIMER_ONCE_CMP == instance_p->config.cmp_type) + { + FTimerSetInterruptMask(instance_p, FTIMER_EVENT_ONCE_CMP, TRUE); + if (instance_p->evt_handlers[FTIMER_EVENT_ONCE_CMP] == NULL) + { + FTimerRegisterEvtCallback(instance_p, FTIMER_EVENT_ONCE_CMP, FTimerDefaultEvtCallback); + } + } + else + { + FTimerSetInterruptMask(instance_p, FTIMER_EVENT_CYC_CMP, TRUE); + if (instance_p->evt_handlers[FTIMER_EVENT_CYC_CMP] == NULL) + { + FTimerRegisterEvtCallback(instance_p, FTIMER_EVENT_CYC_CMP, FTimerDefaultEvtCallback); + } + } + /*暂时屏蔽不开启翻转中断,因为cmp设置触发就是最大值,等同于翻转中断*/ + /*FTimerSetInterruptMask(instance_p, FTIMER_EVENT_ROLL_OVER, TRUE);*/ + if (instance_p->evt_handlers[FTIMER_EVENT_ROLL_OVER] == NULL) + { + FTimerRegisterEvtCallback(instance_p, FTIMER_EVENT_ROLL_OVER, FTimerDefaultEvtCallback); + } + } + else if (FTIMER_WORK_MODE_TACHO == instance_p->config.work_mode) + { + FTimerSetInterruptMask(instance_p, FTACHO_EVENT_OVER, TRUE); + FTimerSetInterruptMask(instance_p, FTACHO_EVENT_UNDER, TRUE); + if (instance_p->evt_handlers[FTACHO_EVENT_OVER] == NULL) + { + FTimerRegisterEvtCallback(instance_p, FTACHO_EVENT_OVER, FTimerDefaultEvtCallback); + } + if (instance_p->evt_handlers[FTACHO_EVENT_UNDER] == NULL) + { + FTimerRegisterEvtCallback(instance_p, FTACHO_EVENT_UNDER, FTimerDefaultEvtCallback); + } + } + else if (FTIMER_WORK_MODE_CAPTURE == instance_p->config.work_mode) + { + FTimerSetInterruptMask(instance_p, FTACHO_EVENT_CAPTURE, TRUE); + if (instance_p->evt_handlers[FTACHO_EVENT_CAPTURE] == NULL) + { + FTimerRegisterEvtCallback(instance_p, FTACHO_EVENT_CAPTURE, FTimerDefaultEvtCallback); + } + } + else + { + FASSERT(0); + } + FTIMER_INFO("mask:0x%x.\r\n", FTIMER_INTR_M_READ(instance_p)); + return; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/usb/Kconfig b/bsp/phytium/libraries/standalone/drivers/usb/Kconfig new file mode 100644 index 0000000000..18d0f459d8 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/usb/Kconfig @@ -0,0 +1,9 @@ +config ENABLE_USB_FXHCI + bool + prompt "Use USB3.0 Host(FUSB_HC_XHCI)" + default n + depends on USE_USB + help + Select USB FUSB_HC_XHCI Host driver component + + diff --git a/bsp/phytium/libraries/standalone/drivers/usb/fusb.c b/bsp/phytium/libraries/standalone/drivers/usb/fusb.c new file mode 100644 index 0000000000..f5e0818757 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/usb/fusb.c @@ -0,0 +1,1024 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fusb.c + * Date: 2022-02-11 13:33:11 + * LastEditTime: 2022-02-18 09:22:06 + * Description:  This files is for implmentation of USB user API + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/2/8 init commit + */ + +#include + +#include "fdebug.h" +#include "fsleep.h" + +#include "fusb_private.h" +#include "fxhci.h" + +#define FUSB_DEBUG_TAG "FUSB" +#define FUSB_ERROR(format, ...) FT_DEBUG_PRINT_E(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_WARN(format, ...) FT_DEBUG_PRINT_W(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_INFO(format, ...) FT_DEBUG_PRINT_I(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_DEBUG(format, ...) FT_DEBUG_PRINT_D(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) + +#define FUSB_DR_DESC FUsbGenerateReqType(FUSB_REQ_DEVICE_TO_HOST, FUSB_REQ_TYPE_STANDARD, FUSB_REQ_RECP_DEV) + +/* + * Certain Lexar / Micron USB 2.0 disks will fail the FUsbGetDescriptor(FUSB_DESC_TYPE_CONFIG) + * call due to timing issues. Work around this by making extra attempts on + * failure. + */ +#define FUSB_GET_DESCRIPTOR_RETRIES 3 + +/** + * @name: FUsbCfgInitialize + * @msg: 初始化USB实例 + * @return {FError} 初始化错误码 + * @param {FUsb} *instance, USB实例 + * @param {const FUsbConfig} *input_config, USB输入配置 + * @note 在PCIE模式下,USB Hc实例在PCIE总线发现控制器后创建 + */ +FError FUsbCfgInitialize(FUsb *instance, const FUsbConfig *input_config) +{ + FASSERT(instance && input_config); + FError ret = FUSB_SUCCESS; + + if (input_config != &instance->config) + instance->config = *input_config; + + instance->hc = NULL; /* non usb host attached */ + + /* create usb hc instance, which will be add as the head of hc list */ + if (NULL == FXhciHcInit(instance, instance->config.base_addr)) + ret = FUSB_ERR_ALLOCATE_FAIL; + + if (FUSB_SUCCESS == ret) + { + instance->is_ready = FT_COMPONENT_IS_READY; + } + + return ret; +} + +/** + * @name: FUsbDeInitialize + * @msg: 去初始化USB实例 + * @return {*} + * @param {FUsb} *instance, USB实例 + */ +void FUsbDeInitialize(FUsb *instance) +{ + FASSERT(instance); + + if (FT_COMPONENT_IS_READY != instance->is_ready) + { + FUSB_ERROR("USB not ready !!!"); + return; + } + + instance->is_ready = 0; + + return; +} + +/** + * @name: FUsbPoll + * @msg: 轮询所有的USB控制器连接的所有设备, 更新设备拓扑 + * @return {*} + * @param {FUsb} *instance, USB实例 + */ +void FUsbPoll(FUsb *instance) +{ + FASSERT(instance); + + if (FT_COMPONENT_IS_READY != instance->is_ready) + { + FUSB_ERROR("USB not ready !!!"); + return; + } + + if (FUsbPollPrepare) + FUsbPollPrepare(instance); + + FUsbHc *controller = instance->hc; + if (controller != NULL) + { + int i; + for (i = 0; i < FUSB_MAX_DEV_NUM; i++) + { + if (controller->devices[i] != NULL) + { + controller->devices[i]->poll(controller->devices[i]); + } + } + } + + return; +} + +/** + * @name: FUsbExit + * @msg: 关闭所有的USB控制器,移除所有连接的设备 + * @return {*} + * @param {FUsb} *instance, USB实例 + */ +void FUsbExit(FUsb *instance) +{ + FASSERT(instance); + + if (FT_COMPONENT_IS_READY != instance->is_ready) + { + FUSB_ERROR("USB not ready !!!"); + return; + } + + if (FUsbExitPrepare) + FUsbExitPrepare(instance); + + FUsbHc *controller = instance->hc; + if (controller != NULL) + { + FASSERT(controller->shutdown); + controller->shutdown(controller); + FUSB_FREE(instance, instance->hc); + instance->hc = NULL; + } + + + return; +} + +/** + * @name: FUsbMempAllocate + * @msg: 从USB内存池分配一块内存,并清零分配的空间 + * @return {void *} 分配的内存空间,如果失败返回NULL + * @param {FUsb} *instance, USB实例 + * @param {size_t} size, 请求分配的字节数 + * @param {size_t} align, 分配空间的对齐方式,起始地址按align字节对齐 + */ +void *FUsbMempAllocate(FUsb *instance, size_t size, size_t align) +{ + FASSERT(instance); + void *result = NULL; + FUsbMemAllocator *allocator = &(instance->config.allocator); + + if (allocator->malloc_align) + { + result = allocator->malloc_align(size, align); + } + + return result; +} + +/** + * @name: FUsbMempFree + * @msg: 释放从USB内存池分配的空间 + * @return {*} + * @param {FUsb} *instance, USB实例 + * @param {void} *ptr, 待释放空间的首地址 + */ +void FUsbMempFree(FUsb *instance, void *ptr) +{ + FASSERT(instance); + FUsbMemAllocator *allocator = &(instance->config.allocator); + + if ((NULL != ptr) && (allocator->free)) + { + allocator->free(ptr); + } + + return; +} + +#ifdef FMEMP_TAG_DEBUG +void *FUsbMempAllocateTag(FUsb *instance, size_t size, size_t align, const char *file, unsigned long line, const char *msg) +{ + FASSERT(instance); + void *result = NULL; + FMemp *memp = &instance->memp; + + if (FUSB_DEFAULT_ALIGN == align) + { + result = FMempCallocTag(memp, 1, size, file, line, msg); + } + else + { + result = FMempMallocAlignTag(memp, size, align, file, line, msg); + if (NULL != result) + { + memset(result, 0, size); + } + } + + return result; +} + +void FUsbMempFreeTag(FUsb *instance, void *ptr) +{ + FASSERT(instance); + if (NULL != ptr) + FMempFreeTag(&instance->memp, ptr); + + return; +} +#endif + +/** + * @name: FUsbAllocateHc + * @msg: 创建USB控制器实例,添加到USB实例的Hc链表中 + * @return {*} + * @param {FUsb} *instance, USB实例 + */ +FUsbHc *FUsbAllocateHc(FUsb *instance) +{ + FASSERT(instance); + FUsbHc *controller = FUSB_ALLOCATE(instance, sizeof(FUsbHc), FUSB_DEFAULT_ALIGN); + instance->hc = controller; + + + return controller; +} + +/** + * @name: FUsbDetachHc + * @msg: 删除USB控制器实例,从USB实例的Hc链表中删去 + * @return {*} + * @param {FUsbHc} *controller, USB控制器实例 + */ +void FUsbDetachHc(FUsbHc *controller) +{ + if (controller == NULL) + return; + + FUsb *instance = controller->usb; + FUsbDetachDev(controller, 0); /* tear down root hub tree */ + + return; +} + +/** + * @name: FUsbFindValidInitFunc + * @msg: 寻找特定USB设备的初始化函数 + * @return {*} + * @param {FUsb} *instance, USB实例 + * @param {FUsbDevIndex} *index, 特定USB设备的索引 + */ +static FUsbDevInitHandler FUsbFindValidInitFunc(FUsb *instance, const FUsbDevIndex *index) +{ + FASSERT(instance); + u32 loop; + FUsbDevInitFunc *func; + FUsbDevInitHandler handler = NULL; + + for (loop = 0; loop < instance->dev_init_num; loop++) + { + func = &instance->dev_init[loop]; + if ((index->category == func->index.category) && + (index->class == func->index.class) && + (index->sub_class == func->index.sub_class) && + (index->protocol == func->index.protocol)) + { + handler = func->handler; + } + } + + return handler; +} + +/** + * @name: FUsbAssignDevInitFunc + * @msg: 指定特定USB设备的初始化函数,供创建USB设备实例时使用 + * @return {FError} 处理返回错误码 + * @param {FUsb} *instance, USB实例 + * @param {FUsbDevIndex} *index, 特定USB设备的索引 + * @param {FUsbDevInitHandler} handler, 特定USB设备的初始化函数 + */ +FError FUsbAssignDevInitFunc(FUsb *instance, const FUsbDevIndex *index, FUsbDevInitHandler handler) +{ + FASSERT(instance && index && handler); + if (FUSB_MAX_DEV_TYPE_NUM == instance->dev_init_num) + return FUSB_ERR_INVALID_PARA; + + if (NULL != FUsbFindValidInitFunc(instance, index)) + { + FUSB_WARN("Will remove device init for class 0x%x", index->class); + } + + instance->dev_init[instance->dev_init_num].index = *index; + instance->dev_init[instance->dev_init_num].handler = handler; + instance->dev_init_num++; + + return FUSB_SUCCESS; +} + +/** + * @name: FUsbInitDevEntry + * @msg: 初始化USB设备 + * @return {*} + * @param {FUsbHc} *controller, USB Hc + * @param {int} slot_id,slot号 + */ +FUsbDev *FUsbInitDevEntry(FUsbHc *controller, int slot_id) +{ + FASSERT(controller && controller->usb); + FASSERT(FUSB_SLOT_ID_VALID(slot_id)); + + FUsb *instace = controller->usb; + FUsbDev *dev = FUSB_ALLOCATE(instace, sizeof(FUsbDev), FUSB_DEFAULT_ALIGN); + + if (NULL == dev) + { + FUSB_ERROR("no memory to allocate device structure "); + return NULL; + } + + if (controller->devices[slot_id] != NULL) + { + FUSB_WARN("warning: device %d reassigned? ", slot_id); + } + + controller->devices[slot_id] = dev; + dev->controller = controller; + dev->address = FUSB_NO_DEV_ADDR; + dev->hub = FUSB_NO_HUB; + dev->port = FUSB_NO_PORT; + dev->init = FUsbNopDevInit; + dev->init(controller->devices[slot_id]); + + return dev; +} + +/** + * @name: FUsbGetAllDevEntries + * @msg: 获取USB控制器上连接的所有USB设备实例 + * @return {size_t} 实际获取的USB设备实例数目 + * @param {FUsbHc} *controller, USB控制器实例 + * @param {FUsbDev} *devs, 放置USB设备实例的缓冲区 + * @param {size_t} max_dev_num, 最多可以获取的USB设备实例数目 + */ +size_t FUsbGetAllDevEntries(FUsbHc *controller, FUsbDev *devs[], size_t max_dev_num) +{ + FASSERT(controller && devs && max_dev_num > 0); + size_t loop; + size_t num = 0; + + /* loop over all dev address in case there are holes */ + for (loop = 0; loop < FUSB_MAX_DEV_NUM; loop++) + { + if (NULL != controller->devices[loop]) + { + devs[num] = controller->devices[loop]; + num++; + + /* get at most max_dev_num device entry before exit */ + if (num >= max_dev_num) + break; + } + } + + return num; +} + +/** + * @name: FUsbDecodeMaxPacketSz0 + * @msg: 根据USB设备速度,选择最大包长度 + * @return {*} 输出最大包长度 + * @param {FUsbSpeed} speed, USB设备速度类型 + * @param {u8} bMaxPacketSize0, 输入最大包长度 + */ +int FUsbDecodeMaxPacketSz0(FUsbSpeed speed, u8 bMaxPacketSize0) +{ + switch (speed) + { + case FUSB_LOW_SPEED: + if (bMaxPacketSize0 != 8) + { + FUSB_ERROR("Invalid MPS0: 0x%02x ", bMaxPacketSize0); + bMaxPacketSize0 = 8; + } + return bMaxPacketSize0; + case FUSB_FULL_SPEED: + switch (bMaxPacketSize0) + { + case 8: + case 16: + case 32: + case 64: + return bMaxPacketSize0; + default: + FUSB_ERROR("Invalid MPS0: 0x%02x ", bMaxPacketSize0); + return 8; + } + case FUSB_HIGH_SPEED: + if (bMaxPacketSize0 != 64) + { + FUSB_ERROR("Invalid MPS0: 0x%02x ", bMaxPacketSize0); + bMaxPacketSize0 = 64; + } + return bMaxPacketSize0; + case FUSB_SUPER_SPEED: + /* Intentional fallthrough */ + case FUSB_SUPER_SPEED_PLUS: + if (bMaxPacketSize0 != 9) + { + FUSB_ERROR("Invalid MPS0: 0x%02x ", bMaxPacketSize0); + bMaxPacketSize0 = 9; + } + return 1 << bMaxPacketSize0; + default: + return 8; + } +} + +/** + * @name: FUsbSetFeature + * @msg: 标准USB主机请求,使能设备/接口/端点的某个特性 + * @return {FUsbTransCode} 控制传输的返回值,小于0表示失败,大于0表示成功传输的字节数目 + * @param {FUsbDev} *dev, USB设备实例 + * @param {int} endp, 设备号(0x00)/接口号/端点号 + * @param {int} feature, 待使能的特性 + * @param {int} rtype, 请求类型,由FUsbGenerateReqType生成 + */ +FUsbTransCode FUsbSetFeature(FUsbDev *dev, int endp, int feature, int rtype) +{ + FASSERT(dev && dev->controller && dev->controller->control); + FUsbDevReq dr; + + dr.bmRequestType = rtype; + dr.data_dir = FUSB_REQ_HOST_TO_DEVICE; + dr.bRequest = FUSB_SET_FEATURE; + dr.wValue = feature; + dr.wIndex = endp; + dr.wLength = 0; + + return dev->controller->control(dev, FUSB_OUT, sizeof(dr), &dr, 0, NULL); +} + +/** + * @name: FUsbGetStatus + * @msg: 标准USB主机请求,获取设备/接口/端点的状态 + * @return {FUsbTransCode} 控制传输的返回值,小于0表示失败,大于0表示成功传输的字节数目 + * @param {FUsbDev} *dev, USB设备实例 + * @param {int} intf,设备号(0x00)/接口号/端点号 + * @param {int} rtype, 请求类型,由FUsbGenerateReqType生成 + * @param {int} len, Data Stage的数据长度 + * @param {void} *data, Data Stage的数据缓冲区 + */ +FUsbTransCode FUsbGetStatus(FUsbDev *dev, int intf, int rtype, int len, void *data) +{ + FASSERT(dev && dev->controller && dev->controller->control); + FUsbDevReq dr; + + dr.bmRequestType = rtype; + dr.data_dir = FUSB_REQ_DEVICE_TO_HOST; + dr.bRequest = FUSB_GET_STATUS; + dr.wValue = 0; + dr.wIndex = intf; + dr.wLength = len; + + return dev->controller->control(dev, FUSB_IN, sizeof(dr), &dr, len, data); +} + +/** + * @name: FUsbGetDescriptor + * @msg: 标准USB主机请求,获取指定描述符 + * @return {FUsbTransCode} 控制传输的返回值,小于0表示失败,大于0表示成功传输的字节数目 + * @param {FUsbDev} *dev, USB设备实例 + * @param {int} rtype, 请求类型,由FUsbGenerateReqType生成 + * @param {FUsbDescriptorType} desc_type, 描述符类型 + * @param {int} desc_idx, 描述符索引 + * @param {void} *data, Data Stage的数据缓冲区 + * @param {size_t} len, Data Stage的数据长度 + */ +FUsbTransCode FUsbGetDescriptor(FUsbDev *dev, int rtype, FUsbDescriptorType desc_type, int desc_idx, void *data, + size_t len) +{ + FASSERT(dev && dev->controller && dev->controller->control); + FUsbDevReq dr; + int fail_tries = 0; + FUsbTransCode ret = FUSB_CC_ZERO_BYTES; + + while (fail_tries++ < FUSB_GET_DESCRIPTOR_RETRIES) + { + dr.bmRequestType = rtype; + dr.bRequest = FUSB_GET_DESCRIPTOR; + dr.wValue = desc_type << 8 | desc_idx; + dr.wIndex = 0; + dr.wLength = len; + + ret = dev->controller->control(dev, FUSB_IN, + sizeof(dr), &dr, len, data); + + if (ret == (int)len) + break; + + fsleep_microsec(10); + } + + return ret; +} + +/** + * @name: FUsbGetStringDescriptor + * @msg: USB主机请求,获取字符串描述符 + * @return {int} 控制传输的返回值,小于0表示失败,大于0表示成功传输的字节数目 + * @param {FUsbDev} *dev, USB设备实例 + * @param {int} rtype, 请求类型,由FUsbGenerateReqType生成 + * @param {int} desc_type, 描述符类型 + * @param {int} desc_idx, 描述符索引 + * @param {int} lang_id, 语言类型 + * @param {void} *data, Data Stage的数据缓冲区 + * @param {size_t} len, Data Stage的数据长度 + */ +FUsbTransCode FUsbGetStringDescriptor(FUsbDev *dev, int rtype, FUsbDescriptorType desc_type, int desc_idx, int lang_id, void *data, size_t len) +{ + FASSERT(dev && dev->controller && dev->controller->control); + FUsbDevReq dr; + int fail_tries = 0; + FUsbTransCode ret = FUSB_CC_ZERO_BYTES; + + while (fail_tries++ < FUSB_GET_DESCRIPTOR_RETRIES) + { + dr.bmRequestType = rtype; + dr.bRequest = FUSB_GET_DESCRIPTOR; + dr.wValue = desc_type << 8 | desc_idx; + dr.wIndex = lang_id; + dr.wLength = len; + + ret = dev->controller->control(dev, FUSB_IN, sizeof(dr), &dr, len, data); + if (ret == (int)len) + break; + + fsleep_microsec(10); + } + + return ret; +} + +/** + * @name: FUsbSetConfiguration + * @msg: 标准USB主机请求,设置配置值 + * @return {FUsbTransCode} 控制传输的返回值,小于0表示失败,大于0表示成功传输的字节数目 + * @param {FUsbDev} *dev, USB设备实例 + */ +FUsbTransCode FUsbSetConfiguration(FUsbDev *dev) +{ + FASSERT(dev && dev->controller && dev->controller->control); + FUsbDevReq dr; + + dr.bmRequestType = 0; + dr.bRequest = FUSB_SET_CONFIGURATION; + dr.wValue = dev->configuration->bConfigurationValue; + dr.wIndex = 0; + dr.wLength = 0; + + return dev->controller->control(dev, FUSB_OUT, sizeof(dr), &dr, 0, NULL); +} + +/** + * @name: FUsbClearFeature + * @msg: 标准USB主机请求,去使能设备/接口/端点的某个特性 + * @return {FUsbTransCode} 控制传输的返回值,小于0表示失败,大于0表示成功传输的字节数目 + * @param {FUsbDev} *dev, USB设备实例 + * @param {int} endp, 设备号(0x00)/接口号/端点号 + * @param {int} feature,待去除的特性 + * @param {int} rtype, 请求类型,由FUsbGenerateReqType生成 + */ +FUsbTransCode FUsbClearFeature(FUsbDev *dev, int endp, int feature, int rtype) +{ + FASSERT(dev && dev->controller && dev->controller->control); + FUsbDevReq dr; + + dr.bmRequestType = rtype; + dr.data_dir = FUSB_REQ_HOST_TO_DEVICE; + dr.bRequest = FUSB_CLEAR_FEATURE; + dr.wValue = feature; + dr.wIndex = endp; + dr.wLength = 0; + + return dev->controller->control(dev, FUSB_OUT, sizeof(dr), &dr, 0, NULL) < 0; +} + +/** + * @name: FUsbSpeedtoDefaultMaxPacketSz + * @msg: 根据设备速度获取最大包长度 + * @return {int} 最大包长度 + * @param {FUsbSpeed} speed, 设备速度类型 + */ +int FUsbSpeedtoDefaultMaxPacketSz(FUsbSpeed speed) +{ + switch (speed) + { + case FUSB_LOW_SPEED: + return 8; + case FUSB_FULL_SPEED: + case FUSB_HIGH_SPEED: + return 64; + case FUSB_SUPER_SPEED: + /* Intentional fallthrough */ + case FUSB_SUPER_SPEED_PLUS: + default: + return 512; + } +} + +/** + * @name: FUsbDecodeInterval + * @msg: 获取USB传输间隔时间 + * @return {int} 传输间隔时间, 0表示失败 + * @param {FUsbSpeed} speed, USB设备速度类型 + * @param {FUsbEpType} type, 端点类型 + * @param {unsigned char} bInterval, 设置的间隔时间 + */ +static int FUsbDecodeInterval(FUsbSpeed speed, const FUsbEpType type, const unsigned char bInterval) +{ + /* Normalize bInterval to log2 of microframes */ +#define LOG2(a) ((sizeof(unsigned) << 3) - __builtin_clz(a) - 1) + switch (speed) + { + case FUSB_LOW_SPEED: + switch (type) + { + case FUSB_ISOCHRONOUS_EP: + case FUSB_INTERRUPT_EP: + return LOG2(bInterval) + 3; + default: + return 0; + } + case FUSB_FULL_SPEED: + switch (type) + { + case FUSB_ISOCHRONOUS_EP: + return (bInterval - 1) + 3; + case FUSB_INTERRUPT_EP: + return LOG2(bInterval) + 3; + default: + return 0; + } + case FUSB_HIGH_SPEED: + switch (type) + { + case FUSB_ISOCHRONOUS_EP: + case FUSB_INTERRUPT_EP: + return bInterval - 1; + default: + return LOG2(bInterval); + } + case FUSB_SUPER_SPEED: + /* Intentional fallthrough */ + case FUSB_SUPER_SPEED_PLUS: + switch (type) + { + case FUSB_ISOCHRONOUS_EP: + case FUSB_INTERRUPT_EP: + return bInterval - 1; + default: + return 0; + } + default: + return 0; + } +#undef LOG2 +} + +/** + * @name: FUsbSetAddress + * @msg: 获取USB设备的描述符信息,根据USB设备类型完成配置和初始化 + * @return {FUsbDevAddr} 为USB设备分配的地址,-1表示USB设备初始化失败 + * @param {FUsbHc} *controller, USB控制器实例 + * @param {FUsbSpeed} speed, USB设备速度类型 + * @param {int} hubport, USB设备连接的Hub端口号 + * @param {int} hubaddr, USB设备连接Hub的地址 + */ +static FUsbDevAddr FUsbSetAddress(FUsbHc *controller, FUsbSpeed speed, int hubport, int hubaddr) +{ + FASSERT(controller); + FUsbDev *dev = controller->set_address(controller, speed, + hubport, hubaddr); + FUsbDevIndex index; + FUsbDevInitHandler init_handler = NULL; + + FUsb *instace = controller->usb; + FASSERT(instace); + if (NULL == dev) + { + FUSB_INFO("set_address failed "); + return FUSB_NO_DEV_ADDR; + } + + FASSERT(NULL == dev->descriptor); + dev->descriptor = FUSB_ALLOCATE(instace, sizeof(*dev->descriptor), FUSB_DEFAULT_ALIGN); + if ((NULL == dev->descriptor) || FUsbGetDescriptor(dev, FUSB_DR_DESC, FUSB_DESC_TYPE_DEVICE, 0, + dev->descriptor, sizeof(*dev->descriptor)) != sizeof(*dev->descriptor)) + { + FUSB_INFO("FUsbGetDescriptor(FUSB_DESC_TYPE_DEVICE) failed "); + FUsbDetachDev(controller, dev->address); + return FUSB_NO_DEV_ADDR; + } + + FUSB_INFO("* found device (0x%04x:0x%04x, USB %x.%x, MPS0: %d) ", + dev->descriptor->idVendor, dev->descriptor->idProduct, + dev->descriptor->bcdUSB >> 8, dev->descriptor->bcdUSB & 0xff, + dev->endpoints[0].maxpacketsize); + + FUSB_INFO("device has %d configurations ", + dev->descriptor->bNumConfigurations); + if (dev->descriptor->bNumConfigurations == 0) + { + /* device isn't usable */ + FUSB_INFO("... no usable configuration! "); + FUsbDetachDev(controller, dev->address); + return FUSB_NO_DEV_ADDR; + } + + u16 buf[2]; + if (FUsbGetDescriptor(dev, FUSB_DR_DESC, FUSB_DESC_TYPE_CONFIG, 0, buf, sizeof(buf)) != sizeof(buf)) + { + FUSB_INFO("first FUsbGetDescriptor(FUSB_DESC_TYPE_CONFIG) failed "); + FUsbDetachDev(controller, dev->address); + return FUSB_NO_DEV_ADDR; + } + + /* workaround for some USB devices: wait until they're ready, or + * they send a NAK when they're not allowed to do. 1ms is enough */ + fsleep_millisec(1); + FASSERT(NULL == dev->configuration); + dev->configuration = FUSB_ALLOCATE(instace, buf[1], FUSB_DEFAULT_ALIGN); + if (NULL == dev->configuration) + { + FUSB_INFO("could not allocate %d bytes for FUSB_DESC_TYPE_CONFIG ", buf[1]); + FUsbDetachDev(controller, dev->address); + return FUSB_NO_DEV_ADDR; + } + + if (FUsbGetDescriptor(dev, FUSB_DR_DESC, FUSB_DESC_TYPE_CONFIG, 0, dev->configuration, + buf[1]) != buf[1]) + { + FUSB_INFO("FUsbGetDescriptor(FUSB_DESC_TYPE_CONFIG) failed "); + FUsbDetachDev(controller, dev->address); + return FUSB_NO_DEV_ADDR; + } + + FUsbConfigurationDescriptor *cd = dev->configuration; + if (cd->wTotalLength != buf[1]) + { + FUSB_INFO("configuration descriptor size changed, aborting "); + FUsbDetachDev(controller, dev->address); + return FUSB_NO_DEV_ADDR; + } + + /* + * If the device is not well known (ifnum == -1), we use the first + * interface we encounter, as there was no need to implement something + * else for the time being. If you need it, see the SetInterface and + * GetInterface functions in the USB specification and set it yourself. + */ + FUSB_INFO("device has %x interfaces ", cd->bNumInterfaces); + + u8 *end = (void *)dev->configuration + cd->wTotalLength; + FUsbInterfaceDescriptor *intf; + u8 *ptr; + + /* Find our interface (or the first good one if we don't know) */ + for (ptr = (void *)dev->configuration + sizeof(*cd);; ptr += ptr[0]) + { + if (ptr + 2 > end || !ptr[0] || ptr + ptr[0] > end) + { + FUSB_INFO("Couldn't find usable FUSB_DESC_TYPE_INTERFACE "); + FUsbDetachDev(controller, dev->address); + return FUSB_NO_DEV_ADDR; + } + + if (ptr[1] != FUSB_DESC_TYPE_INTERFACE) + continue; + + intf = (void *)ptr; + if (intf->bLength != sizeof(*intf)) + { + FUSB_INFO("Skipping broken FUSB_DESC_TYPE_INTERFACE "); + continue; + } + + FUSB_INFO("Interface %d: class 0x%x, sub 0x%x. proto 0x%x ", + intf->bInterfaceNumber, intf->bInterfaceClass, + intf->bInterfaceSubClass, intf->bInterfaceProtocol); + ptr += sizeof(*intf); + + break; + } + + /* Gather up all endpoints belonging to this interface */ + dev->num_endp = 1; + for (; ptr + 2 <= end && ptr[0] && ptr + ptr[0] <= end; ptr += ptr[0]) + { + if (ptr[1] == FUSB_DESC_TYPE_INTERFACE || ptr[1] == FUSB_DESC_TYPE_CONFIG || + (size_t)dev->num_endp >= ARRAY_SIZE(dev->endpoints)) + break; + + if (ptr[1] != FUSB_DESC_TYPE_ENDPOINT) + continue; + + FUsbEndpointDescriptor *desc = (void *)ptr; + static const char *transfertypes[4] = + { + "control", "isochronous", "bulk", "interrupt" + }; + FUSB_INFO(" #Endpoint %d (%s), max packet size %x, type %s ", + desc->bEndpointAddress & 0x7f, + (desc->bEndpointAddress & 0x80) ? "in" : "out", + desc->wMaxPacketSize, + transfertypes[desc->bmAttributes & 0x3]); + + FUsbEndpoint *ep = &dev->endpoints[dev->num_endp++]; + ep->dev = dev; + ep->endpoint = desc->bEndpointAddress; + ep->toggle = 0; + ep->maxpacketsize = desc->wMaxPacketSize; + ep->direction = (desc->bEndpointAddress & 0x80) ? FUSB_IN : FUSB_OUT; + ep->type = desc->bmAttributes & 0x3; + ep->interval = FUsbDecodeInterval(dev->speed, ep->type, + desc->bInterval); + } + + if ((controller->finish_device_config && + controller->finish_device_config(dev)) || + FUsbSetConfiguration(dev) < 0) + { + FUSB_INFO("Could not finalize device configuration "); + FUsbDetachDev(controller, dev->address); + return FUSB_NO_DEV_ADDR; + } + + int class = dev->descriptor->bDeviceClass; + if (class == 0) + class = intf->bInterfaceClass; + + switch (class) + { + case FUSB_AUDIO_DEVICE: + FUSB_INFO("Audio Class "); + break; + case FUSB_COMM_DEVICE: + FUSB_INFO("Communication Class "); + break; + case FUSB_HID_DEVICE: + FUSB_INFO("HID Class "); + break; + case FUSB_PHYSICAL_DEVICE: + FUSB_INFO("Physical Class"); + break; + case FUSB_IMAGE_DEVICE: + FUSB_INFO("Camera Class "); + break; + case FUSB_PRINTER_DEVICE: + FUSB_INFO("Printer Class"); + break; + case FUSB_MASS_STORAGE_DEVICE: + FUSB_INFO("Mass Storage Class "); + break; + case FUSB_HUB_DEVICE: + FUSB_INFO("Hub Class "); + break; + default: + FUSB_ERROR("Unsupported Class %x ", class); + break; + } + + index.category = FUSB_STANDARD_INTERFACE; + index.class = class; + index.sub_class = intf->bInterfaceSubClass; + index.protocol = intf->bInterfaceProtocol; + + FUSB_INFO("class: 0x%x sub-class: 0x%x, protocol: 0x%x", + index.class, index.sub_class, index.protocol); + + init_handler = FUsbFindValidInitFunc(instace, &index); + if (NULL != init_handler) + { + dev->init = init_handler; + dev->class = (FUsbDevClass)class; + } + else + { + FUSB_WARN("Init function for the device not found, use generic one instead !!!"); + dev->init = FUsbGenericDevInit; + } + + return dev->address; +} + +/** + * @name: FUsbDetachDev + * @msg: 从USB主机移除指定USB设备(USB设备驱动使用) + * @return {*} + * @param {FUsbHc} *controller, USB控制器实例 + * @param {int} devno, USB设备索引 + * @note Should be called by the hub drivers whenever a physical detach occurs + * and can be called by USB class drivers if they are unsatisfied with a + * malfunctioning device. + */ +void FUsbDetachDev(FUsbHc *controller, int devno) +{ + FUsb *instace = controller->usb; + /* check if device exists, as we may have + been called yet by the USB class driver */ + if (controller->devices[devno]) + { + controller->devices[devno]->destroy(controller->devices[devno]); + + if (controller->destroy_device) + controller->destroy_device(controller, devno); + + FUSB_FREE(instace, controller->devices[devno]->descriptor); + controller->devices[devno]->descriptor = NULL; + FUSB_FREE(instace, controller->devices[devno]->configuration); + controller->devices[devno]->configuration = NULL; + + /* Tear down the device itself *after* destroy_device() + * has had a chance to interrogate it. */ + FUSB_FREE(instace, controller->devices[devno]); + controller->devices[devno] = NULL; + } + + return; +} + +/** + * @name: FUsbAttachDev + * @msg: 向USB主机添加USB设备(USB设备驱动使用) + * @return {FUsbDevAddr} 分配的USB设备地址 + * @param {FUsbHc} *controller, USB控制器实例 + * @param {int} hubaddress, Hub地址 + * @param {int} port, 连接的Port + * @param {FUsbSpeed} speed, USB设备的设置速度类型 + */ +FUsbDevAddr FUsbAttachDev(FUsbHc *controller, int hubaddress, int port, FUsbSpeed speed) +{ + static const char *speeds[] = {"FULL", "LOW", "HIGH", "SUPER", "ULTRA-SUPER"}; + FUSB_INFO("%s-Speed Device ", ((size_t)speed < sizeof(speeds) / sizeof(char *)) + ? speeds[speed] + : "Unkonwn"); + FUsbDevAddr newdev = FUsbSetAddress(controller, speed, port, hubaddress); + if (newdev == FUSB_NO_DEV_ADDR) + return FUSB_NO_DEV_ADDR; + + FUsbDev *newdev_t = controller->devices[newdev]; + + /* determine responsible driver - current done in set_address */ + newdev_t->init(newdev_t); + + /* init() may have called FUsbDetachDev() yet, so check */ + return controller->devices[newdev] ? newdev : FUSB_NO_DEV_ADDR; +} + +/** + * @name: FUsbGenericDestory + * @msg: 一般USB设备去初始化函数 + * @return {*} + * @param {FUsbDev} *dev, USB设备实例 + */ +static void FUsbGenericDestory(FUsbDev *dev) +{ + if (FUsbGenericRemove) + FUsbGenericRemove(dev); + + return; +} + +/** + * @name: FUsbGenericDevInit + * @msg: 默认的USB设备初始化函数 + * @return {*} + * @param {FUsbDev} *dev, USB设备实例 + */ +void FUsbGenericDevInit(FUsbDev *dev) +{ + dev->data = NULL; + dev->destroy = FUsbGenericDestory; + + if (FUsbGenericCreate) + FUsbGenericCreate(dev); + + if (dev->data == NULL) + { + FUSB_INFO("Detaching device not used by payload "); + FUsbDetachDev(dev->controller, dev->address); + } + + return; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/usb/fusb.h b/bsp/phytium/libraries/standalone/drivers/usb/fusb.h new file mode 100644 index 0000000000..7967cb1b49 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/usb/fusb.h @@ -0,0 +1,398 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fusb.h + * Date: 2022-02-11 13:33:11 + * LastEditTime: 2022-02-18 09:22:25 + * Description:  This files is for definition of FUSB user interface + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/2/7 init commit + */ + +#ifndef DRIVERS_FUSB_H +#define DRIVERS_FUSB_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ +#include "ftypes.h" +#include "ferror_code.h" +#include "fassert.h" +#include "fusb_def.h" + +/************************** Constant Definitions *****************************/ +#define FUSB_SUCCESS FT_SUCCESS +#define FUSB_ERR_WAIT_TIMEOUT FT_MAKE_ERRCODE(ErrModBsp, ErrUsb, 0x0) +#define FUSB_ERR_INVALID_PARA FT_MAKE_ERRCODE(ErrModBsp, ErrUsb, 0x1) +#define FUSB_ERR_NOT_SUPPORT FT_MAKE_ERRCODE(ErrModBsp, ErrUsb, 0x2) +#define FUSB_ERR_NON_INSTANCE FT_MAKE_ERRCODE(ErrModBsp, ErrUsb, 0x3) +#define FUSB_ERR_INVALID_DATA FT_MAKE_ERRCODE(ErrModBsp, ErrUsb, 0x4) +#define FUSB_ERR_DESC_PARSE_ERR FT_MAKE_ERRCODE(ErrModBsp, ErrUsb, 0x5) +#define FUSB_ERR_ALLOCATE_FAIL FT_MAKE_ERRCODE(ErrModBsp, ErrUsb, 0x6) +#define FUSB_ERR_TRANS_FAIL FT_MAKE_ERRCODE(ErrModBsp, ErrUsb, 0x7) + +/* SetAddress() recovery interval (USB 2.0 specification 9.2.6.3 */ +#define FUSB_SET_ADDRESS_MDELAY 2 + +/* + * USB sets an upper limit of 5 seconds for any transfer to be completed. + * + * Data originally from FUSB_HC_EHCI driver: + * Tested with some USB2.0 flash sticks: + * TUR turn around took about 2.2s for the slowest (13fe:3800), maximum + * of 250ms for the others. + * + * SET ADDRESS on xHCI controllers. + * The USB specification indicates that devices must complete processing + * of a SET ADDRESS request within 50 ms. However, some hubs were found + * to take more than 100 ms to complete a SET ADDRESS request on a + * downstream port. + */ +#define FUSB_USB_MAX_PROCESSING_TIME_US (5 * 1000 * 1000) +#define FUSB_FULL_LOW_SPEED_FRAME_US 1000 +#define FUSB_MAX_CTRL_NUM 1 +#define FUSB_MAX_DEV_TYPE_NUM 8 +#define FUSB_MAX_DEV_NUM 128 +#define FUSB_MAX_EP_NUM 32 +#define FUSB_MAX_SLOT_NUM FUSB_MAX_DEV_NUM + +#define FUSB_SLOT_ID_VALID(slot) ((0 <= (slot)) && (FUSB_MAX_SLOT_NUM > (slot))) + + +#define FUSB_DEFAULT_ALIGN 1 +#define FUSB_NO_DEV_ADDR -1 +#define FUSB_NO_HUB -1 +#define FUSB_NO_PORT -1 + +/**************************** Type Definitions *******************************/ + +typedef struct _FUsbDev FUsbDev; +typedef struct _FUsbHc FUsbHc; +typedef struct _FUsb FUsb; + +/* Transfer complete code for USB */ +enum +{ + FUSB_CC_ZERO_BYTES = 0, + FUSB_CC_SUCCESS = 1 + + /* for XHCI transfer complete code, please refer to e.g. FXHCI_CC_SUCCESS */ + /* be careful not define conflict CC code */ +}; + +/* less than 0 means error (implemented by usb Hc, e.g. FXhciTransCode), + greater or equal than 0 means bytes transfered */ +typedef int FUsbTransCode; + +typedef struct +{ + FUsbDev *dev; /* device instance of this endpoint */ + int endpoint; /* endpoint address ep0 = 0, epn = n */ + FUsbDirection direction; /* type or direction of ep */ + int toggle; /* ep state for some device to toggle */ + int maxpacketsize; /* max packet size for ep transfer */ + FUsbEpType type; /* transfer type of ep, control, bulk or so on */ + int interval; /* expressed as binary logarithm of the number + of microframes (i.e. t = 125us * 2^interval) */ +} FUsbEndpoint; /* encapsulates a single endpoint of an USB device */ +typedef struct +{ + const FUsbDescriptor *buf; + u32 buf_len; + boolean is_valid; + const FUsbDescriptor *end_pos; + const FUsbDescriptor *next_pos; + const FUsbDescriptor *cur_desc; + const FUsbDescriptor *err_pos; +} FUsbConfigParser; /* parser for configure descriptor */ + +typedef struct +{ +#define FUSB_USBSTR_MIN_LEN 4 + FUsbStringDescriptor *usb_str; +#define FUSB_STRDESC_BUF_MAX 64 + char str_buf[FUSB_STRDESC_BUF_MAX]; +} FUsbStringParser; /* parser for string descriptor */ + +typedef int FUsbDevAddr; + +typedef struct _FUsbDev +{ + FUsbHc *controller; /* Hc instance where device attached */ + FUsbEndpoint endpoints[FUSB_MAX_EP_NUM]; /* all Ep instance of device */ + int num_endp; /* num of Ep in use */ + FUsbDevAddr address; /* USB address */ + FUsbDevClass class; /* USB device class, e.g hid */ + int hub; /* hub where device is attached to */ + int port; /* port where device is attached */ + FUsbSpeed speed; /* speed type of device */ + void *data; /* private data for specific type of device */ + FUsbDeviceDescriptor *descriptor; /* device descriptor ever get from device hw */ + FUsbConfigurationDescriptor *configuration; /* configure descriptor followed with interface descriptor ever get from device hw */ + FUsbConfigParser config_parser; /* parser for configure descriptor */ + FUsbStringParser string_parser; /* parser for string descriptor */ + void (*init)(FUsbDev *dev); /* device init function of specific device type for register */ + void (*destroy)(FUsbDev *dev); /* device deinit function of specific device type for register */ + void (*poll)(FUsbDev *dev); /* device poll function of specific device type for register */ +} FUsbDev; /* encapsulates a single USB device */ + +typedef enum +{ + FUSB_HC_OHCI = 0, + FUSB_HC_UHCI = 1, + FUSB_HC_EHCI = 2, + FUSB_HC_XHCI = 3, + FUSB_HC_DWC2 = 4 +} FUsbHcType; + +typedef struct _FUsbHc +{ + uintptr reg_base; /* base address of Hc register */ + FUsb *usb; /* instance of USB system */ + FUsbHcType type; /* type of Hc, e.g XHCI */ + FUsbDev *devices[FUSB_MAX_DEV_NUM]; /* dev 0 is root hub, 127 is last addressable */ + + /* start(): Resume operation. */ + void (*start)(FUsbHc *controller); + /* stop(): Stop operation but keep controller initialized. */ + void (*stop)(FUsbHc *controller); + /* reset(): Perform a controller reset. The controller needs to + be (re)initialized afterwards to work (again). */ + FUsbTransCode(*reset)(FUsbHc *controller); + /* init(): Initialize a (previously reset) controller + to a working state. */ + void (*init)(FUsbHc *controller); + /* shutdown(): Stop operation, detach host controller and shutdown + this driver instance. After calling shutdown() any + other usage of this hci_t* is invalid. */ + void (*shutdown)(FUsbHc *controller); + + FUsbTransCode(*bulk)(FUsbEndpoint *ep, int size, u8 *data, int finalize); + FUsbTransCode(*control)(FUsbDev *dev, FUsbDirection pid, int dr_length, + void *devreq, int data_length, u8 *data); + void *(*create_intr_queue)(FUsbEndpoint *ep, int reqsize, int reqcount, int reqtiming); + void (*destroy_intr_queue)(FUsbEndpoint *ep, void *queue); + u8 *(*poll_intr_queue)(void *queue); + void *instance; /* instance to specific Hc implementation, e.g XHCI */ + + /* set_address(): Tell the USB device its address (xHCI + controllers want to do this by + themselves). Also, allocate the FUsbDev + structure, initialize enpoint 0 + (including MPS) and return it. */ + FUsbDev *(*set_address)(FUsbHc *controller, FUsbSpeed speed, + int hubport, int hubaddr); + /* finish_device_config(): Another hook for xHCI, returns 0 on success. */ + int (*finish_device_config)(FUsbDev *dev); + /* destroy_device(): Finally, destroy all structures that + were allocated during set_address() + and finish_device_config(). */ + void (*destroy_device)(FUsbHc *controller, int devaddr); +} FUsbHc; /* encapsulates a single USB host */ + +typedef struct +{ + void *(*malloc_align)(size_t size, size_t align); + void (*free)(void *mem); +} FUsbMemAllocator; /* memory allocator used in USB system */ + +typedef struct +{ + u32 instance_id; /* id for this USB system */ + uintptr base_addr; /* base addr of Hc register, set as 0 for pci-usb */ + u32 irq_num; + u32 irq_priority; + FUsbMemAllocator allocator; /* memory allocator to support dynamic memory */ +} FUsbConfig; /* configure data of the USB system */ + +typedef enum +{ + FUSB_STANDARD_INTERFACE, + FUSB_VENDOR_SPECIFIED +} FUsbDevCategory; + +typedef struct +{ + FUsbDevCategory category; + FUsbDevClass class; + u32 sub_class; + u32 protocol; +} FUsbDevIndex; + +typedef void (* FUsbDevInitHandler)(FUsbDev *dev); + +typedef struct +{ + FUsbDevIndex index; + FUsbDevInitHandler handler; +} FUsbDevInitFunc; + +typedef struct _FUsb +{ + FUsbConfig config; /* configuration of USB system */ + void *pcie_instance; /* NULL if unused */ + void *pcie_info[FUSB_MAX_CTRL_NUM]; /* NULL if unused */ + FUsbHc *hc; /* first hc, there might have multiple hc in pcie-mode */ + /* hook to set init function for specific device type */ + FUsbDevInitFunc dev_init[FUSB_MAX_DEV_TYPE_NUM]; + u32 dev_init_num; /* number of init function in used */ + u32 is_ready; /* indicator of system okay */ +} FUsb; /* instance of the USB system */ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +static FUsbDev *FUsbGetDevEntry(FUsbHc *controller, int dev_address) +{ + FASSERT(controller && controller->devices); + FUsbDev *result = NULL; + u32 loop; + + for (loop = 0; loop < FUSB_MAX_DEV_NUM; loop++) + { + if ((NULL != controller->devices[loop]) && (dev_address == controller->devices[loop]->address)) + { + result = controller->devices[loop]; + break; + } + } + + return result; +} + +/* + * returns the speed is above FUSB_SUPER_SPEED or not + */ +static inline boolean FUsbIsSuperSpeed(FUsbSpeed speed) +{ + return (speed == FUSB_SUPER_SPEED || speed == FUSB_SUPER_SPEED_PLUS); +} + +static inline unsigned char FUsbGenerateReqType(FUsbReqDirection dir, FUsbReqType type, FUsbReqRecpient recp) +{ + return (dir << 7) | (type << 5) | recp; +} + +/************************** Function Prototypes ******************************/ +/* 获取USB的默认配置 */ +const FUsbConfig *FUsbLookupConfig(u32 instance_id); + +/* 初始化USB实例 */ +FError FUsbCfgInitialize(FUsb *instance, const FUsbConfig *config); + +/* 去初始化USB实例 */ +void FUsbDeInitialize(FUsb *instance); + +/* 轮询所有的USB控制器连接的所有设备, 更新设备拓扑 */ +void FUsbPoll(FUsb *instance); + +/* 关闭所有的USB控制器,移除所有连接的设备 */ +void FUsbExit(FUsb *instance); + +/* 从USB内存池分配一块内存,并清零分配的空间 */ +void *FUsbMempAllocate(FUsb *instance, size_t size, size_t align); + +/* 释放从USB内存池分配的空间 */ +void FUsbMempFree(FUsb *instance, void *ptr); + +/* 指定特定USB设备的初始化函数,供创建USB设备实例时使用 */ +FError FUsbAssignDevInitFunc(FUsb *instance, const FUsbDevIndex *index, FUsbDevInitHandler handler); + +/* 获取USB控制器上连接的所有USB设备实例 */ +size_t FUsbGetAllDevEntries(FUsbHc *controller, FUsbDev *devs[], size_t max_dev_num); + +/* 标准USB主机请求,使能设备/接口/端点的某个特性 */ +FUsbTransCode FUsbSetFeature(FUsbDev *dev, int endp, int feature, int rtype); + +/* 标准USB主机请求,获取设备/接口/端点的状态 */ +FUsbTransCode FUsbGetStatus(FUsbDev *dev, int endp, int rtype, int len, void *data); + +/* 标准USB主机请求,获取指定描述符 */ +FUsbTransCode FUsbGetDescriptor(FUsbDev *dev, int rtype, FUsbDescriptorType descType, int descIdx, + void *data, size_t len); + +/* USB主机请求,获取字符串描述符 */ +FUsbTransCode FUsbGetStringDescriptor(FUsbDev *dev, int rtype, FUsbDescriptorType desc_type, int desc_idx, int lang_id, + void *data, size_t len); + +/* 标准USB主机请求,设置配置值 */ +FUsbTransCode FUsbSetConfiguration(FUsbDev *dev); + +/* 标准USB主机请求,去使能设备/接口/端点的某个特性 */ +FUsbTransCode FUsbClearFeature(FUsbDev *dev, int endp, int feature, int rtype); + +/* 打印描述符信息 */ +void FUsbDumpAllDescriptors(FUsbDev *dev); + +/* 从USB主机移除指定USB设备(USB设备驱动使用) */ +void FUsbDetachDev(FUsbHc *controller, int devno); + +/* 向USB主机添加USB设备(USB设备驱动使用) */ +FUsbDevAddr FUsbAttachDev(FUsbHc *controller, int hubaddress, int port, + FUsbSpeed speed); + +/** + * To be implemented by application. It's called by the USB + * stack just before iterating over known devices to poll them for + * status change. + */ +void __attribute__((weak)) FUsbPollPrepare(FUsb *instance); + +/** + * To be implemented by application. It's called by the USB + * stack just before exit known Hc. + */ +void __attribute__((weak)) FUsbExitPrepare(FUsb *instance); + +/** + * To be implemented by application. It's called by the USB stack + * when a new USB device is found which isn't claimed by a built in driver, + * so the client has the chance to know about it. + * + * @param dev descriptor for the USB device + */ +void __attribute__((weak)) FUsbGenericCreate(FUsbDev *dev); + +/** + * To be implemented by application. It's called by the USB stack + * when it finds out that a USB device is removed which wasn't claimed by a + * built in driver. + * + * @param dev descriptor for the USB device + */ +void __attribute__((weak)) FUsbGenericRemove(FUsbDev *dev); + +/* 支持带TAG的内存分配,用于跟踪动态内存使用 */ +#ifdef FMEMP_TAG_DEBUG +void *FUsbMempAllocateTag(FUsb *instance, size_t size, size_t align, const char *file, unsigned long line, const char *msg); +void FUsbMempFreeTag(FUsb *instance, void *ptr); + +#define FUSB_ALLOCATE(instance, size, align) FUsbMempAllocateTag((instance), (size), (align), __FILE__, __LINE__, "") +#define FUSB_FREE(instance, ptr) FUsbMempFreeTag((instance), (ptr)) +#else +#define FUSB_ALLOCATE(instance, size, align) FUsbMempAllocate((instance), (size), (align)) +#define FUSB_FREE(instance, ptr) FUsbMempFree((instance), (ptr)) +#endif + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/bsp/phytium/libraries/standalone/drivers/usb/fusb_debug.c b/bsp/phytium/libraries/standalone/drivers/usb/fusb_debug.c new file mode 100644 index 0000000000..7870df0f4d --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/usb/fusb_debug.c @@ -0,0 +1,230 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fusb_debug.c + * Date: 2022-02-11 13:33:11 + * LastEditTime: 2022-02-18 09:18:04 + * Description:  This files is for implmentation of USB debug utilities + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/2/7 init commit + */ + +#include "fdebug.h" + +#include "fusb_private.h" + +#define FUSB_DEBUG_TAG "FUSB_DEBUG" +#define FUSB_ERROR(format, ...) FT_DEBUG_PRINT_E(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_WARN(format, ...) FT_DEBUG_PRINT_W(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_INFO(format, ...) FT_DEBUG_PRINT_I(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_DEBUG(format, ...) FT_DEBUG_PRINT_D(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) + +static inline boolean FUsbIsValidStringIndex(u8 id) +{ + return (0 != id) && (0xff != id); +} + +/** + * @name: FUsbDumpAllDescriptors + * @msg: 打印USB设备的描述符信息(设备描述符,配置描述符和接口描述符) + * @return {*} + * @param {FUsbDev} *dev, USB设备实例,已完成初始化 + */ +void FUsbDumpAllDescriptors(FUsbDev *dev) +{ + FError ret = FUSB_SUCCESS; + if ((NULL == dev) || (NULL == dev->configuration)) + return; + + const FUsbDeviceDescriptor *dev_desc = NULL; + const FUsbConfigurationDescriptor *config_desc = NULL; + const FUsbInterfaceDescriptor *if_desc = NULL; + FASSERT(dev->controller && dev->controller->usb); + FUsb *instance = dev->controller->usb; + u8 if_num = 0; + u8 func_id = 0; + FUsbConfigParser *parser = &dev->config_parser; + + dev_desc = dev->descriptor; + config_desc = dev->configuration; + + /* init descriptor parser in each dump */ + ret = FUsbSetupConfigParser(dev, config_desc, config_desc->wTotalLength); + FUsbSetupStringParser(dev); + if (FUSB_SUCCESS != ret) + return; + + if (FUsbIsValidStringIndex(dev_desc->iManufacturer)) + { + ret = FUsbSearchStringDescriptor(instance, dev, dev_desc->iManufacturer); + if (FUSB_SUCCESS == ret) + printf(" Manufacturer: %s\r\n", FUsbGetString(dev)); + } + + if (FUsbIsValidStringIndex(dev_desc->iProduct)) + { + ret = FUsbSearchStringDescriptor(instance, dev, dev_desc->iProduct); + if (FUSB_SUCCESS == ret) + printf(" Product: %s\r\n", FUsbGetString(dev)); + } + + if (FUsbIsValidStringIndex(dev_desc->iSerialNumber)) + { + ret = FUsbSearchStringDescriptor(instance, dev, dev_desc->iSerialNumber); + if (FUSB_SUCCESS == ret) + printf(" Serial No.: %s\r\n", FUsbGetString(dev)); + } + + while (NULL != (if_desc = (const FUsbInterfaceDescriptor *)FUsbGetDescriptorFromParser(parser, FUSB_DESC_TYPE_INTERFACE))) + { + if (if_desc->bInterfaceNumber > if_num) + { + if_num = if_desc->bInterfaceNumber; + } + + if (if_desc->bInterfaceNumber != if_num) + { + FUSB_INFO("Alternate setting %u ignored", if_desc->bInterfaceNumber); + continue; + } + + if (FUsbIsValidStringIndex(if_desc->iInterface)) + { + ret = FUsbSearchStringDescriptor(instance, dev, if_desc->iInterface); + if (FUSB_SUCCESS == ret) + printf(" Interface: %s\r\n", FUsbGetString(dev)); + } + + } + + /* revoke descriptor parser after used */ + FUsbRevokeConfigParser(dev); + FUsbRevokeStringParser(dev); + + return; +} + +/** + * @name: FUsbDumpDeviceDescriptor + * @msg: 打印设备描述符信息 + * @return {*} + * @param {FUsbDeviceDescriptor} *descriptor, 设备描述符 + */ +void FUsbDumpDeviceDescriptor(const FUsbDeviceDescriptor *descriptor) +{ + if (NULL != descriptor) + { + FUSB_INFO(""); + FUSB_INFO("===Device Descriptor"); + FUSB_INFO(" bLength: %d", descriptor->bLength); + FUSB_INFO(" bDescriptorType: %d", descriptor->bDescriptorType); + FUSB_INFO(" bcdUSB: 0x%x", descriptor->bcdUSB); + FUSB_INFO(" bDeviceClass: %d", descriptor->bDeviceClass); + FUSB_INFO(" bDeviceSubClass: %d", descriptor->bDeviceSubClass); + FUSB_INFO(" bDeviceProtocol: %d", descriptor->bDeviceProtocol); + FUSB_INFO(" bMaxPacketSize0: %d", descriptor->bMaxPacketSize0); + FUSB_INFO(" idVendor: 0x%x", descriptor->idVendor); + FUSB_INFO(" idProduct: 0x%x", descriptor->idProduct); + FUSB_INFO(" bcdDevice: 0x%x", descriptor->bcdDevice); + FUSB_INFO(" iManufacturer: %d", descriptor->iManufacturer); + FUSB_INFO(" iSerialNumber: %d", descriptor->iSerialNumber); + FUSB_INFO(" bNumConfigurations: %d", descriptor->bNumConfigurations); + FUSB_INFO(""); + } +} + +/** + * @name: FUsbDumpConfigDescriptor + * @msg: 打印配置描述符信息 + * @return {*} + * @param {FUsbConfigurationDescriptor} *descriptor, 配置描述符 + */ +void FUsbDumpConfigDescriptor(const FUsbConfigurationDescriptor *descriptor) +{ + if (NULL != descriptor) + { + FUSB_INFO(""); + FUSB_INFO("===Configure Descriptor"); + FUSB_INFO(" bLength: %d", descriptor->bLength); + FUSB_INFO(" bDescriptorType: %d", descriptor->bDescriptorType); + FUSB_INFO(" wTotalLength: %d", descriptor->wTotalLength); + FUSB_INFO(" bNumInterfaces: %d", descriptor->bNumInterfaces); /* 该配置下有多少个接口描述符 */ + FUSB_INFO(" bConfigurationValue: %d", descriptor->bConfigurationValue); /* 该配置描述符的配置号信息 */ + FUSB_INFO(" iConfiguration: %d", descriptor->iConfiguration); + FUSB_INFO(" bmAttributes: 0x%x", descriptor->bmAttributes); + FUSB_INFO(" remote-weakup: %s", + (descriptor->bmAttributes & FUSB_CONFIG_DESC_ATTR_REMOTE_WEAKUP) ? "yes" : "no"); + FUSB_INFO(" self-power: %s", + (descriptor->bmAttributes & FUSB_CONFIG_DESC_ATTR_SELF_POWER) ? "yes" : "no"); + FUSB_INFO(" usb1.0-compatible: %s", + (descriptor->bmAttributes & FUSB_CONFIG_DESC_ATTR_USB1_COMPATIABLE) ? "yes" : "no"); + FUSB_INFO(" max power: %dmA", 2 * (descriptor->bMaxPower)); + FUSB_INFO(" "); + } +} + +/** + * @name: FUsbDumpInterfaceDescriptor + * @msg: 打印接口描述符信息 + * @return {*} + * @param {FUsbInterfaceDescriptor} *descriptor, 接口描述符 + */ +void FUsbDumpInterfaceDescriptor(const FUsbInterfaceDescriptor *descriptor) +{ + if (NULL != descriptor) + { + FUSB_INFO(""); + FUSB_INFO("===Interface Descriptor"); + FUSB_INFO(" bLength: %d", descriptor->bLength); + FUSB_INFO(" bDescriptorType: %d", descriptor->bDescriptorType); + FUSB_INFO(" bInterfaceNumber: %d", descriptor->bInterfaceNumber); + FUSB_INFO(" bAlternateSetting: %d", descriptor->bAlternateSetting); + FUSB_INFO(" bNumEndpoints: %d", descriptor->bNumEndpoints); + FUSB_INFO(" bInterfaceClass: %d", descriptor->bInterfaceClass); + FUSB_INFO(" bInterfaceSubClass: %d", descriptor->bInterfaceSubClass); + FUSB_INFO(" bInterfaceProtocol: %d", descriptor->bInterfaceProtocol); + FUSB_INFO(" iInterface: %d", descriptor->iInterface); + FUSB_INFO(" "); + } +} + +/** + * @name: FUsbDumpEndpointDescriptor + * @msg: 打印端点描述符信息 + * @return {*} + * @param {FUsbEndpointDescriptor} *descriptor, 端点描述符 + */ +void FUsbDumpEndpointDescriptor(const FUsbEndpointDescriptor *descriptor) +{ + if (NULL != descriptor) + { + FUSB_INFO(""); + FUSB_INFO("===Endpoint Descriptor"); + FUSB_INFO(" bLength: %d", descriptor->bLength); + FUSB_INFO(" bDescriptorType: %d", descriptor->bDescriptorType); + FUSB_INFO(" bEndpointAddress: %d", descriptor->bEndpointAddress); + FUSB_INFO(" ep num: %d", FUSB_EP_DESC_EP_NUM(descriptor->bEndpointAddress)); + FUSB_INFO(" ep dir: %s", + (FUSB_EP_DESC_EP_DIR_IN & descriptor->bEndpointAddress) ? "IN" : "OUT"); + FUSB_INFO(" bmAttributes: 0x%x", descriptor->bmAttributes); + FUSB_INFO(" trans type: %d ([0]-%s, [1]-%s, [2]-%s, [3]-%s)", + FUSB_EP_DESC_TRANS_TYPE(descriptor->bmAttributes), + "control", "isochronous", "bulk", "interrupt"); + FUSB_INFO(" wMaxPacketSize: %d", descriptor->wMaxPacketSize); + FUSB_INFO(" bInterval: %d", descriptor->bInterval); + FUSB_INFO(" "); + } +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/usb/fusb_def.h b/bsp/phytium/libraries/standalone/drivers/usb/fusb_def.h new file mode 100644 index 0000000000..5d66390d21 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/usb/fusb_def.h @@ -0,0 +1,288 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fusb_def.h + * Date: 2022-02-11 13:33:11 + * LastEditTime: 2022-02-18 09:18:24 + * Description:  This files is for definition of spec defined USB data structure + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/2/7 init commit + */ + +#ifndef DRIVERS_FUSB_DEF_H +#define DRIVERS_FUSB_DEF_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ + +#include "ftypes.h" + +/************************** Constant Definitions *****************************/ +typedef enum +{ + FUSB_UNKNOWN_SPEED = -1, + FUSB_FULL_SPEED = 0, + FUSB_LOW_SPEED = 1, + FUSB_HIGH_SPEED = 2, + FUSB_SUPER_SPEED = 3, + FUSB_SUPER_SPEED_PLUS = 4, +} FUsbSpeed; + +typedef enum +{ + FUSB_REQ_HOST_TO_DEVICE = 0, + FUSB_REQ_DEVICE_TO_HOST = 1 +} FUsbReqDirection; + +typedef enum +{ + FUSB_REQ_TYPE_STANDARD = 0, + FUSB_REQ_TYPE_CLASS = 1, + FUSB_REQ_TYPE_VENDOR = 2, + FUSB_REQ_TYPE_RESERVED = 3 +} FUsbReqType; + +typedef enum +{ + FUSB_REQ_RECP_DEV = 0, + FUSB_REQ_RECP_IF = 1, + FUSB_REQ_RECP_EP = 2, + FUSB_REQ_RECP_OTHER = 3 +} FUsbReqRecpient; + +/* refer to Table 9-5. Descriptor Types in USB spec. for details */ +typedef enum +{ + FUSB_DESC_TYPE_NONE = 0, + FUSB_DESC_TYPE_DEVICE = 1, + FUSB_DESC_TYPE_CONFIG = 2, + FUSB_DESC_TYPE_STRING = 3, + FUSB_DESC_TYPE_INTERFACE = 4, + FUSB_DESC_TYPE_ENDPOINT = 5, + FUSB_DESC_TYPE_HUB = 41, + FUSB_DESC_TYPE_SUPER_SPEED_HUB = 42 +} FUsbDescriptorType; + +typedef enum +{ + FUSB_GET_STATUS = 0, + FUSB_CLEAR_FEATURE = 1, + FUSB_SET_FEATURE = 3, + FUSB_SET_ADDRESS = 5, + FUSB_GET_DESCRIPTOR = 6, + FUSB_SET_DESCRIPTOR = 7, + FUSB_GET_CONFIGURATION = 8, + FUSB_SET_CONFIGURATION = 9, + FUSB_GET_INTERFACE = 10, + FUSB_SET_INTERFACE = 11, + FUSB_SYNCH_FRAME = 12 +} FUsbRequestCode; + +typedef enum +{ + FUSB_ENDPOINT_HALT = 0, + FUSB_DEVICE_REMOTE_WAKEUP = 1, + FUSB_TEST_MODE = 2 +} FUsbFeatureSelectors; + +typedef enum +{ + FUSB_SETUP, + FUSB_IN, + FUSB_OUT +} FUsbDirection; + +typedef enum +{ + FUSB_CONTROL_EP = 0, + FUSB_ISOCHRONOUS_EP = 1, + FUSB_BULK_EP = 2, + FUSB_INTERRUPT_EP = 3 +} FUsbEpType; + +typedef enum +{ + FUSB_UNKOWN_DEVICE = 0x0, + FUSB_AUDIO_DEVICE = 0x01, + FUSB_COMM_DEVICE = 0x02, + FUSB_HID_DEVICE = 0x03, + FUSB_PHYSICAL_DEVICE = 0x05, + FUSB_IMAGE_DEVICE = 0x06, + FUSB_PRINTER_DEVICE = 0x07, + FUSB_MASS_STORAGE_DEVICE = 0x08, + FUSB_HUB_DEVICE = 0x09, +} FUsbDevClass; /* definition of device class */ + +/**************************** Type Definitions *******************************/ + +/* following data structure is defined according to spec. name their member + may not compliant with code convention */ +typedef struct +{ + unsigned char bDescLength; + unsigned char bDescriptorType; + unsigned char bNbrPorts; /* Number of downstream facing ports supports */ + union + { + struct + { + unsigned long logicalPowerSwitchingMode: 2; /* BIT[1:0] */ + unsigned long isCompoundDevice: 1; /* BIT[2] */ + unsigned long overcurrentProtectionMode: 2; /* BIT[4:3] */ + unsigned long ttThinkTime: 2; /* BIT[6:5] */ + unsigned long arePortIndicatorsSupported: 1; /* BIT[7] */ + unsigned long: 8; + } __attribute__((packed)); + unsigned short wHubCharacteristics; + } __attribute__((packed)); + unsigned char bPowerOn2PwrGood; /* in 2 ms intervals */ + unsigned char bHubContrCurrent; /* max current requirements */ + char DeviceRemovable[]; /* indicates if a port has a removable device attached */ +} __attribute__((packed)) FUsbHubDescriptor; + +typedef struct +{ + unsigned char bLength; + unsigned char bDescriptorType; + unsigned short bcdUSB; + unsigned char bDeviceClass; + unsigned char bDeviceSubClass; + unsigned char bDeviceProtocol; + unsigned char bMaxPacketSize0; + unsigned short idVendor; + unsigned short idProduct; + unsigned short bcdDevice; + unsigned char iManufacturer; + unsigned char iProduct; + unsigned char iSerialNumber; + unsigned char bNumConfigurations; +} __attribute__((packed)) FUsbDeviceDescriptor; + +typedef struct +{ + unsigned char bLength; + unsigned char bDescriptorType; + unsigned short wTotalLength; + unsigned char bNumInterfaces; + unsigned char bConfigurationValue; + unsigned char iConfiguration; + unsigned char bmAttributes; +#define FUSB_CONFIG_DESC_ATTR_REMOTE_WEAKUP BIT(5) /* 1: remote wakeup feature */ +#define FUSB_CONFIG_DESC_ATTR_SELF_POWER BIT(6) /* 1: self-powered 0: bus-powered */ +#define FUSB_CONFIG_DESC_ATTR_USB1_COMPATIABLE BIT(7) /* 1: compatibility with USB 1.0 */ + unsigned char bMaxPower; + /* configuration descriptor may follow more buffers, need to allocate dynamic memory for all contents */ +} __attribute__((packed)) FUsbConfigurationDescriptor; + +typedef struct +{ + unsigned char bLength; + unsigned char bDescriptorType; + unsigned char bInterfaceNumber; + unsigned char bAlternateSetting; + unsigned char bNumEndpoints; + unsigned char bInterfaceClass; + unsigned char bInterfaceSubClass; + unsigned char bInterfaceProtocol; + unsigned char iInterface; +} __attribute__((packed)) FUsbInterfaceDescriptor; + +typedef struct +{ + unsigned char bLength; + unsigned char bDescriptorType; + unsigned char bEndpointAddress; + /* Low-speed devices can have a maximum of 3 endpoint, other devices can have 16 (0–15) */ +#define FUSB_EP_DESC_EP_NUM(x) (GENMASK(3, 0) & (x)) /* endpoint number */ +#define FUSB_EP_DESC_EP_DIR_IN BIT(7) /* direction, 0: OUT, 1: IN */ + unsigned char bmAttributes; +#define FUSB_EP_DESC_TRANS_TYPE(x) (GENMASK(1, 0) & (x)) +#define FUSB_EP_DESC_TRANS_CTRL 0b00 /* control */ +#define FUSB_EP_DESC_TRANS_ISOC 0b01 /* isochronous */ +#define FUSB_EP_DESC_TRANS_BULK 0b10 /* bulk */ +#define FUSB_EP_DESC_TRANS_INTR 0b11 /* interrupt */ + unsigned short wMaxPacketSize; +#define FUSB_EP_DESC_MAX_PACKET_SZ GENMASK(10, 0) + unsigned char bInterval; +} __attribute__((packed)) FUsbEndpointDescriptor; + +typedef union +{ + struct + { + u8 len; + u8 type; + } header; +#define FUSB_DESCRIPTOR_HEADER_SIZE 2 + FUsbConfigurationDescriptor configuration; + FUsbInterfaceDescriptor interface; + FUsbEndpointDescriptor endpoint; +} __attribute__((packed)) FUsbDescriptor; + +typedef struct +{ + unsigned char bLength; + unsigned char bDescriptorType; + unsigned short bcdHID; + unsigned char bCountryCode; + unsigned char bNumDescriptors; + unsigned char bReportDescriptorType; + unsigned short wReportDescriptorLength; +} __attribute__((packed)) FUsbHidDescriptor; + +typedef struct +{ + u8 len; /* Descriptor size in bytes (variable) */ + u8 type; /* The constant String (0x03 DESCRIPTOR_STRING) */ + u16 string[0]; /* Unicode UTF- 16LE string */ +} __attribute__((packed)) FUsbStringDescriptor; + +typedef struct +{ + union + { + struct + { + FUsbReqRecpient req_recp: 5; + FUsbReqType req_type: 2; + FUsbReqDirection data_dir: 1; + } __attribute__((packed)); + unsigned char bmRequestType; + } __attribute__((packed)); + unsigned char bRequest; + unsigned short wValue; + unsigned short wIndex; + unsigned short wLength; +} __attribute__((packed)) FUsbDevReq; + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FUSB_DEFAULT_LANG_ID 0x409 /* English */ + +/************************** Function Prototypes ******************************/ + + +#ifdef __cplusplus +} +#endif + + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/usb/fusb_dev.c b/bsp/phytium/libraries/standalone/drivers/usb/fusb_dev.c new file mode 100644 index 0000000000..2d3fdf0dbd --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/usb/fusb_dev.c @@ -0,0 +1,439 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fusb_dev.c + * Date: 2022-02-11 13:33:11 + * LastEditTime: 2022-02-18 09:18:45 + * Description:  This files is for USB device function implementation + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/2/7 init commit + */ + +#include + +#include "fdebug.h" + +#include "fusb_private.h" + +#define FUSB_DEBUG_TAG "FUSB-DEV" +#define FUSB_ERROR(format, ...) FT_DEBUG_PRINT_E(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_WARN(format, ...) FT_DEBUG_PRINT_W(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_INFO(format, ...) FT_DEBUG_PRINT_I(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_DEBUG(format, ...) FT_DEBUG_PRINT_D(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) + +#define FUSB_DR_DESC FUsbGenerateReqType(FUSB_REQ_DEVICE_TO_HOST, FUSB_REQ_TYPE_STANDARD, FUSB_REQ_RECP_DEV) + +#define FUSB_SKIP_BYTES(desc, bytes) ((FUsbDescriptor *) ((u8 *) (desc) + (bytes))) +#define FUSB_CONFIG_DESC_SIZE 512 /* best guess */ + +static void FUsbNopDevDestory(FUsbDev *dev) +{ + FUsbNopDevInit(dev); + dev->address = FUSB_NO_DEV_ADDR; + dev->hub = FUSB_NO_HUB; + dev->port = FUSB_NO_PORT; +} + +static void FUsbNopDevPoll(FUsbDev *dev) +{ + return; +} + +/** + * @name: FUsbNopDevInit + * @msg: 默认的USB设备初始化函数 + * @return {*} + * @param {FUsbDev} *dev, USB设备实例 + */ +void FUsbNopDevInit(FUsbDev *dev) +{ + dev->descriptor = NULL; + dev->configuration = NULL; + dev->destroy = FUsbNopDevDestory; + dev->poll = FUsbNopDevPoll; +} + +static inline boolean FUsbParserIsValid(const FUsbConfigParser *parser) +{ + FASSERT(parser); + return parser->is_valid; +} + +static FError FUsbParserDescriptor(FUsbConfigParser *parser) +{ + FASSERT(parser && parser->buf); + const FUsbDescriptor *cur_pos = parser->buf; + const FUsbDescriptor *desc_end; + FUsbDescriptorType last_desc_type = FUSB_DESC_TYPE_NONE; + FUsbDescriptorType desc_type; + u8 desc_len, exp_len, alt_len; + FError ret = FUSB_SUCCESS; + + while (FUSB_SKIP_BYTES(cur_pos, FUSB_DESCRIPTOR_HEADER_SIZE) < parser->end_pos) + { + /* get length and type of descriptor */ + desc_len = cur_pos->header.len; + desc_type = cur_pos->header.type; + + desc_end = FUSB_SKIP_BYTES(cur_pos, desc_len); + if (desc_end > parser->end_pos) + { + FUSB_ERROR("Parse descriptor out of boundary !!!"); + parser->err_pos = cur_pos; + ret = FUSB_ERR_DESC_PARSE_ERR; + goto err_handle; + } + + exp_len = 0; + alt_len = 0; + switch (desc_type) + { + case FUSB_DESC_TYPE_CONFIG: + if (FUSB_DESC_TYPE_NONE != last_desc_type) + { + FUSB_ERROR("Configuration descriptor must be the first !!!"); + parser->err_pos = cur_pos; + ret = FUSB_ERR_DESC_PARSE_ERR; + goto err_handle; + } + exp_len = sizeof(FUsbConfigurationDescriptor); + break; + case FUSB_DESC_TYPE_INTERFACE: + if (FUSB_DESC_TYPE_NONE == last_desc_type) + { + FUSB_ERROR("Interface descriptor must not be the first !!!"); + parser->err_pos = cur_pos; + ret = FUSB_ERR_DESC_PARSE_ERR; + goto err_handle; + } + exp_len = sizeof(FUsbInterfaceDescriptor); + break; + case FUSB_DESC_TYPE_ENDPOINT: + if ((FUSB_DESC_TYPE_NONE == last_desc_type) || + (FUSB_DESC_TYPE_CONFIG == last_desc_type)) + { + FUSB_ERROR("Endpoint descriptor must follow interface descriptor !!!"); + parser->err_pos = cur_pos; + ret = FUSB_ERR_DESC_PARSE_ERR; + goto err_handle; + } + break; + default: + FUSB_DEBUG("Descriptor %d not handled !!!", desc_type); + break; + } + + if (((exp_len != 0) && (desc_len != exp_len)) && + ((alt_len == 0) || (desc_len != alt_len))) + { + FUSB_ERROR("Descriptor %d invalid !!!", desc_type); + parser->err_pos = cur_pos; + ret = FUSB_ERR_DESC_PARSE_ERR; + goto err_handle; + } + + last_desc_type = desc_type; + cur_pos = desc_end; + } + + if (cur_pos != parser->end_pos) + { + parser->err_pos = cur_pos; + ret = FUSB_ERR_DESC_PARSE_ERR; + goto err_handle; + } + +err_handle: + if (FUSB_SUCCESS == ret) + { + parser->is_valid = TRUE; + } + + return ret; +} + +/** + * @name: FUsbSetupConfigParser + * @msg: 配置USB配置描述符解析器 + * @return {*} + * @param {FUsbDev} *dev, USB设备实例 + * @param {void} *buf, 配置描述符缓冲区 + * @param {u32} buf_len, 配置描述符缓冲区长度 + */ +FError FUsbSetupConfigParser(FUsbDev *dev, const void *buf, u32 buf_len) +{ + FASSERT(dev && buf && (buf_len > 0)); + const FUsbConfigurationDescriptor *config_desc; + FUsbConfigParser *parser = &dev->config_parser; + + memset(parser, 0, sizeof(*parser)); + + parser->buf = buf; + parser->buf_len = buf_len; + parser->is_valid = FALSE; + parser->end_pos = FUSB_SKIP_BYTES(parser->buf, parser->buf_len); + parser->next_pos = parser->buf; + parser->cur_desc = NULL; + parser->err_pos = parser->buf; + + if ((parser->buf_len < sizeof(u32)) || (parser->buf_len > FUSB_CONFIG_DESC_SIZE)) + { + FUSB_ERROR("Invalid buffer length !!!"); + return FUSB_ERR_INVALID_DATA; + } + + /* input buffer must start with config desc */ + config_desc = (FUsbConfigurationDescriptor *)parser->buf; + if ((config_desc->bLength != sizeof(FUsbConfigurationDescriptor)) || + (config_desc->bDescriptorType != FUSB_DESC_TYPE_CONFIG) || + (config_desc->wTotalLength > parser->buf_len)) + { + FUSB_ERROR("Invalid configuration descriptor !!!"); + return FUSB_ERR_INVALID_DATA; + } + + /* adjust end position */ + if (config_desc->wTotalLength < parser->buf_len) + { + parser->end_pos = FUSB_SKIP_BYTES(parser->buf, config_desc->wTotalLength); + } + + return FUsbParserDescriptor(parser); +} + +/** + * @name: FUsbRevokeConfigParser + * @msg: 去初始化USB配置描述符解析器 + * @return {*} + * @param {FUsbDev} *dev, USB设备实例 + */ +void FUsbRevokeConfigParser(FUsbDev *dev) +{ + FASSERT(dev); + memset(&dev->config_parser, 0, sizeof(dev->config_parser)); + return; +} + +/** + * @name: FUsbGetDescriptorFromParser + * @msg: 从配置描述符解析器中获取指定类型的描述符(端点描述符/接口描述符) + * @return {const FUsbDescriptor *} 获取的描述符 + * @param {FUsbConfigParser} *parser, 配置描述符解析器 + * @param {FUsbDescriptorType} type, 获取描述符的类型 + */ +const FUsbDescriptor *FUsbGetDescriptorFromParser(FUsbConfigParser *parser, FUsbDescriptorType type) +{ + FASSERT(parser); + if (!FUsbParserIsValid(parser)) + { + FUSB_ERROR("Config parse is not valid !!!"); + return NULL; + } + + const FUsbDescriptor *result = NULL; + FUsbDescriptorType desc_type; + u8 desc_len; + const FUsbDescriptor *desc_end; + + /* travesal all descriptors */ + while (parser->next_pos < parser->end_pos) + { + desc_len = parser->next_pos->header.len; + desc_type = parser->next_pos->header.type; + + desc_end = FUSB_SKIP_BYTES(parser->next_pos, desc_len); + if (desc_end > parser->end_pos) + break; + + if ((FUSB_DESC_TYPE_ENDPOINT == type) && + (FUSB_DESC_TYPE_INTERFACE == desc_type)) + break; /* there is no chance to find endpoint desc after interface desc */ + + if (type == desc_type) + { + /* target desc found !!! */ + result = parser->next_pos; + parser->next_pos = desc_end; + break; + } + + parser->next_pos = desc_end;/* check next one */ + } + + if (NULL != result) + { + parser->err_pos = result; + } + + parser->cur_desc = result; + return result; +} + +/** + * @name: FUsbSetupStringParser + * @msg: 初始化字符串描述符解析器 + * @return {*} + * @param {FUsbDev} *dev, USB设备实例 + */ +void FUsbSetupStringParser(FUsbDev *dev) +{ + FASSERT(dev); + FUsbStringParser *parser = &dev->string_parser; + + if (NULL != parser->usb_str) + { + FUSB_WARN("String descriptor exists, might cause memory leakage !!!"); + } + + parser->usb_str = NULL; + memset(parser->str_buf, 0, sizeof(parser->str_buf)); + + return; +} + +/** + * @name: FUsbRevokeStringParser + * @msg: 去初始化字符串描述符解析器 + * @return {*} + * @param {FUsbDev} *dev, USB设备实例 + */ +void FUsbRevokeStringParser(FUsbDev *dev) +{ + FASSERT(dev); + FUsbStringParser *parser = &dev->string_parser; + FASSERT(dev->controller && dev->controller->usb); + FUsb *instance = dev->controller->usb; + + if (NULL != parser->usb_str) + { + FUSB_FREE(instance, parser->usb_str); + parser->usb_str = NULL; + } + + return; +} + +/** + * @name: FUsbSearchStringDescriptor + * @msg: 检索字符串描述符,保存在FUsbStringParser结构中 + * @return {*} + * @param {FUsb} *instance, USB实例 + * @param {FUsbDev} *dev, USB设备实例 + * @param {u8} id, 要获取字符串描述符的ID + */ +FError FUsbSearchStringDescriptor(FUsb *instance, FUsbDev *dev, u8 id) +{ + FASSERT(instance && dev); + FUsbStringParser *parser = &dev->string_parser; + const FUsbStringDescriptor *usb_str = NULL; + u8 total_len; + u8 char_num; + u16 character; + + /* re-malloc usb string desc buffer with length 4 */ + if (NULL != parser->usb_str) + { + FUSB_FREE(instance, parser->usb_str); + parser->usb_str = NULL; + } + + parser->usb_str = FUSB_ALLOCATE(instance, FUSB_USBSTR_MIN_LEN, FUSB_DEFAULT_ALIGN); + if (NULL == parser->usb_str) + return FUSB_ERR_ALLOCATE_FAIL; + + /* get header of string for the full length */ + if (FUsbGetStringDescriptor(dev, FUSB_DR_DESC, FUSB_DESC_TYPE_STRING, id, FUSB_DEFAULT_LANG_ID, + parser->usb_str, FUSB_USBSTR_MIN_LEN) < 0) + { + FUSB_ERROR("Parse string descriptor failed (len: %d) !!!", FUSB_USBSTR_MIN_LEN); + return FUSB_ERR_DESC_PARSE_ERR; + } + + /* check if string descriptor header is valid */ + total_len = parser->usb_str->len; + if ((total_len < FUSB_DESCRIPTOR_HEADER_SIZE) || + ((total_len & 1) != 0) || + (parser->usb_str->type != FUSB_DESC_TYPE_STRING)) + { + FUSB_ERROR("Get invalid string descriptor (len: %d) !!!", FUSB_USBSTR_MIN_LEN); + return FUSB_ERR_DESC_PARSE_ERR; + } + + /* return if no need to get more */ + if (total_len <= FUSB_USBSTR_MIN_LEN) + return FUSB_SUCCESS; + + /* re-malloc usb string desc buffer with full length */ + FASSERT(parser->usb_str); + FUSB_FREE(instance, parser->usb_str); + parser->usb_str = NULL; + + parser->usb_str = FUSB_ALLOCATE(instance, total_len, FUSB_DEFAULT_ALIGN); + if (NULL == parser->usb_str) + return FUSB_ERR_ALLOCATE_FAIL; + + /* get the whole string descriptor */ + if (FUsbGetStringDescriptor(dev, FUSB_DR_DESC, FUSB_DESC_TYPE_STRING, id, FUSB_DEFAULT_LANG_ID, + parser->usb_str, total_len) < 0) + { + FUSB_ERROR("Parse string descriptor failed (len: %d)!!!", total_len); + return FUSB_ERR_DESC_PARSE_ERR; + } + + if ((parser->usb_str->len < FUSB_DESCRIPTOR_HEADER_SIZE) || + ((parser->usb_str->len & 1) != 0) || + (parser->usb_str->type != FUSB_DESC_TYPE_STRING)) + { + FUSB_ERROR("Get invalid string descriptor (len: %d) !!!", total_len); + return FUSB_ERR_DESC_PARSE_ERR; + } + + /* convert into ASCII string */ + usb_str = parser->usb_str; + char_num = (usb_str->len - FUSB_DESCRIPTOR_HEADER_SIZE) / 2; /* in 16-bit way */ + + if (char_num >= FUSB_STRDESC_BUF_MAX - 1) + { + return FUSB_ERR_NOT_SUPPORT; + } + + for (u8 i = 0; i < char_num; i++) + { + character = usb_str->string[i]; + if (character < ' ' /* 0x20 */ + || character > '~') /* 0x7E */ + { + character = '_'; + } + + parser->str_buf[i] = (char)character; + } + + parser->str_buf[char_num] = '\0'; + return FUSB_SUCCESS; +} + +/** + * @name: FUsbGetString + * @msg: 获取刚刚检索到的字符串描述符内容 + * @return {const char *}, 字符串描述符中的内容 + * @param {FUsbDev} *dev, USB设备实例 + */ +const char *FUsbGetString(const FUsbDev *const dev) +{ + FASSERT(dev); + return (const char *)dev->string_parser.str_buf; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/usb/fusb_g.c b/bsp/phytium/libraries/standalone/drivers/usb/fusb_g.c new file mode 100644 index 0000000000..dd088f21bd --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/usb/fusb_g.c @@ -0,0 +1,60 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fusb_g.c + * Date: 2022-02-11 13:33:11 + * LastEditTime: 2022-02-18 09:19:07 + * Description:  This files is for gloabl parameters + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/2/7 init commit + */ + +/***************************** Include Files *********************************/ +#include "fparameters.h" +#include "fusb_private.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ +const FUsbConfig FUSBHC_CONFIG_TBL[FUSB3_NUM] = +{ + [FUSB3_ID_0] = + { + .instance_id = FUSB3_ID_0, + .base_addr = FUSB3_0_BASE_ADDR, + .irq_num = FUSB3_0_IRQ_NUM, + .irq_priority = 0U, + .allocator = NULL + }, + + [FUSB3_ID_1] = + { + .instance_id = FUSB3_ID_1, + .base_addr = FUSB3_1_BASE_ADDR, + .irq_num = FUSB3_1_IRQ_NUM, + .irq_priority = 0U, + .allocator = NULL + } +}; + + +/*****************************************************************************/ diff --git a/bsp/phytium/libraries/standalone/drivers/usb/fusb_generic_hub.c b/bsp/phytium/libraries/standalone/drivers/usb/fusb_generic_hub.c new file mode 100644 index 0000000000..5aa2236124 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/usb/fusb_generic_hub.c @@ -0,0 +1,295 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fusb_generic_hub.c + * Date: 2022-02-11 13:33:11 + * LastEditTime: 2022-02-18 09:19:27 + * Description:  This files is for implmentation of generic hub function + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/2/7 init commit + */ + +#include "fsleep.h" +#include "fdebug.h" +#include "fusb.h" +#include "fusb_generic_hub.h" + +#define FUSB_DEBUG_TAG "FUSB_HUB" +#define FUSB_ERROR(format, ...) FT_DEBUG_PRINT_E(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_WARN(format, ...) FT_DEBUG_PRINT_W(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_INFO(format, ...) FT_DEBUG_PRINT_I(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_DEBUG(format, ...) FT_DEBUG_PRINT_D(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) + +void FUsbGenericHubDestory(FUsbDev *const dev) +{ + FUsbGenericHub *const hub = FUSB_GEN_HUB_GET(dev); + FUsb *instace = dev->controller->usb; + if (!hub) + return; + + /* First, detach all devices behind this hub */ + int port; + for (port = 1; port <= hub->num_ports; ++port) + { + if (hub->ports[port] >= 0) + { + FUSB_INFO("generic_hub: Detachment at port %d ", port); + FUsbDetachDev(dev->controller, hub->ports[port]); + hub->ports[port] = FUSB_NO_DEV_ADDR; + } + } + + /* Disable all ports */ + if (hub->ops->disable_port) + { + for (port = 1; port <= hub->num_ports; ++port) + hub->ops->disable_port(dev, port); + } + + FUSB_FREE(instace, hub->ports); // free(hub->ports); + FUSB_FREE(instace, hub); // free(hub); +} + +static int FUsbGenericHubDebounce(FUsbDev *const dev, const int port) +{ + FUsbGenericHub *const hub = FUSB_GEN_HUB_GET(dev); + + const int step_ms = 1; /* linux uses 25ms, we're busy anyway */ + const int at_least_ms = 100; /* 100ms as in usb20 spec 9.1.2 */ + const int timeout_ms = 1500; /* linux uses this value */ + + int total_ms = 0; + int stable_ms = 0; + while (stable_ms < at_least_ms && total_ms < timeout_ms) + { + fsleep_millisec(step_ms); + + const int changed = hub->ops->port_status_changed(dev, port); + const int connected = hub->ops->port_connected(dev, port); + if (changed < 0 || connected < 0) + return -1; + + if (!changed && connected) + { + stable_ms += step_ms; + } + else + { + FUSB_INFO("generic_hub: Unstable connection at %d ", + port); + stable_ms = 0; + } + total_ms += step_ms; + } + if (total_ms >= timeout_ms) + FUSB_INFO("generic_hub: Debouncing timed out at %d ", port); + return 0; /* ignore timeouts, try to always go on */ +} + +int FUsbGenericHubWaitForPort(FUsbDev *const dev, const int port, + const int wait_for, + int (*const port_op)(FUsbDev *, int), + int timeout_steps, const int step_us) +{ + int state; + do + { + state = port_op(dev, port); + if (state < 0) + return -1; + else if (!!state == wait_for) + return timeout_steps; + fsleep_microsec(step_us); + --timeout_steps; + } + while (timeout_steps); + + return 0; +} + +int FUsbGenericHubResetPort(FUsbDev *const dev, const int port) +{ + FUsbGenericHub *const hub = FUSB_GEN_HUB_GET(dev); + + if (hub->ops->start_port_reset(dev, port) < 0) + return -1; + + /* wait for 10ms (usb20 spec 11.5.1.5: reset should take 10 to 20ms) */ + fsleep_millisec(10); + + /* now wait 12ms for the hub to finish the reset */ + const int ret = FUsbGenericHubWaitForPort( + /* time out after 120 * 100us = 12ms */ + dev, port, 0, hub->ops->port_in_reset, 120, 100); + if (ret < 0) + return -1; + else if (!ret) + FUSB_INFO("generic_hub: Reset timed out at port %d ", port); + + return 0; /* ignore timeouts, try to always go on */ +} + +static int FUsbGenericHubDetachDev(FUsbDev *const dev, const int port) +{ + FUsbGenericHub *const hub = FUSB_GEN_HUB_GET(dev); + + FUsbDetachDev(dev->controller, hub->ports[port]); + hub->ports[port] = FUSB_NO_DEV_ADDR; + + return 0; +} + +static int FUsbGenericHubAttachDev(FUsbDev *const dev, const int port) +{ + FUsbGenericHub *const hub = FUSB_GEN_HUB_GET(dev); + + if (FUsbGenericHubDebounce(dev, port) < 0) + return -1; + + if (hub->ops->reset_port) + { + if (hub->ops->reset_port(dev, port) < 0) + return -1; + + if (!hub->ops->port_connected(dev, port)) + { + FUSB_INFO( + "generic_hub: Port %d disconnected after " + "reset. Possibly upgraded, rescan required. ", + port); + return 0; + } + + /* after reset the port will be enabled automatically */ + const int ret = FUsbGenericHubWaitForPort( + /* time out after 1,000 * 10us = 10ms */ + dev, port, 1, hub->ops->port_enabled, 1000, 10); + if (ret < 0) + return -1; + else if (!ret) + FUSB_INFO("generic_hub: Port %d still " + "disabled after 10ms ", + port); + } + + const FUsbSpeed speed = hub->ops->port_speed(dev, port); + if (speed >= 0) + { + FUSB_DEBUG("generic_hub: Success at port %d ", port); + if (hub->ops->reset_port) + fsleep_millisec(10); /* Reset recovery time + (usb20 spec 7.1.7.5) */ + hub->ports[port] = FUsbAttachDev( + dev->controller, dev->address, port, speed); + } + return 0; +} + +int FUsbGenericHubScanPort(FUsbDev *const dev, const int port) +{ + FUsbGenericHub *const hub = FUSB_GEN_HUB_GET(dev); + + if (hub->ports[port] >= 0) + { + FUSB_INFO("generic_hub: Detachment at port %d ", port); + const int ret = FUsbGenericHubDetachDev(dev, port); + if (ret < 0) + return ret; + } + + if (hub->ops->port_connected(dev, port)) + { + FUSB_INFO("generic_hub: Attachment at port %d ", port); + return FUsbGenericHubAttachDev(dev, port); + } + + return 0; +} + +static void FUsbGenericHubPoll(FUsbDev *const dev) +{ + FUsbGenericHub *const hub = FUSB_GEN_HUB_GET(dev); + if (!hub) + return; + + if (hub->ops->hub_status_changed && + hub->ops->hub_status_changed(dev) != FUSB_CC_SUCCESS) + { + return; + } + + int port; + for (port = 1; port <= hub->num_ports; ++port) + { + const FUsbTransCode ret = hub->ops->port_status_changed(dev, port); + if (ret < 0) + { + FUSB_WARN("Transcode %d", ret); + return; + } + else if (ret == FUSB_CC_SUCCESS) + { + FUSB_INFO("generic_hub: Port change at %d ", port); + if (FUsbGenericHubScanPort(dev, port) < 0) + return; + } + } +} + +int FUsbGenericHubInit(FUsbDev *const dev, const int num_ports, + const FUsbGenericHubOps *const ops) +{ + int port; + FUsb *instance = dev->controller->usb; + + dev->destroy = FUsbGenericHubDestory; + dev->poll = FUsbGenericHubPoll; + FASSERT(NULL == dev->data); + dev->data = FUSB_ALLOCATE(instance, sizeof(FUsbGenericHub), FUSB_DEFAULT_ALIGN); + if (NULL == dev->data) + { + FUSB_ERROR("generic_hub: ERROR: Out of memory "); + return -1; + } + + FUsbGenericHub *const hub = FUSB_GEN_HUB_GET(dev); + hub->num_ports = num_ports; + FASSERT(NULL == hub->ports); + hub->ports = FUSB_ALLOCATE(instance, sizeof(*hub->ports) * (num_ports + 1), FUSB_DEFAULT_ALIGN); + hub->ops = ops; + if (NULL == hub->ports) + { + FUSB_ERROR("generic_hub: ERROR: Out of memory "); + FUSB_FREE(instance, dev->data); + dev->data = NULL; + return -1; + } + + for (port = 1; port <= num_ports; ++port) + hub->ports[port] = FUSB_NO_DEV_ADDR; + + /* Enable all ports */ + if (ops->enable_port) + { + for (port = 1; port <= num_ports; ++port) + ops->enable_port(dev, port); + + /* wait once for all ports */ + fsleep_millisec(20); + } + + return 0; +} diff --git a/bsp/phytium/libraries/standalone/drivers/usb/fusb_generic_hub.h b/bsp/phytium/libraries/standalone/drivers/usb/fusb_generic_hub.h new file mode 100644 index 0000000000..da3bd6cc2d --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/usb/fusb_generic_hub.h @@ -0,0 +1,93 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fusb_generic_hub.h + * Date: 2022-02-11 13:33:11 + * LastEditTime: 2022-02-18 09:20:23 + * Description:  This files is for definition of generic hub function + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/2/7 init commit + */ + +#ifndef DRIVERS_FUSB_GENERIC_HUB_H +#define DRIVERS_FUSB_GENERIC_HUB_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "fusb_private.h" + +typedef struct +{ + /* negative results denote an error */ + + /* returns 1 if the hub's status changed since the last call (optional) */ + FUsbTransCode(*hub_status_changed)(FUsbDev *); + /* returns 1 if the port's status changed since the last call */ + FUsbTransCode(*port_status_changed)(FUsbDev *, int port); + /* returns 1 if something is connected to the port */ + FUsbTransCode(*port_connected)(FUsbDev *, int port); + /* returns 1 if port is currently resetting */ + FUsbTransCode(*port_in_reset)(FUsbDev *, int port); + /* returns 1 if the port is enabled */ + FUsbTransCode(*port_enabled)(FUsbDev *, int port); + /* returns speed if port is enabled, negative value if not */ + FUsbSpeed(*port_speed)(FUsbDev *, int port); + + /* enables (powers up) a port (optional) */ + FUsbTransCode(*enable_port)(FUsbDev *, int port); + /* disables (powers down) a port (optional) */ + FUsbTransCode(*disable_port)(FUsbDev *, int port); + /* starts a port reset (required if reset_port is set to a generic one from below) */ + FUsbTransCode(*start_port_reset)(FUsbDev *, int port); + + /* performs a port reset (optional, generic implementations below) */ + FUsbTransCode(*reset_port)(FUsbDev *, int port); +} FUsbGenericHubOps; + +typedef struct +{ + int num_ports; + /* port numbers are always 1 based, + so we waste one int for convenience */ + int *ports; /* allocated to sizeof(*ports)*(num_ports+1) */ +#define FUSB_NO_DEV_ADDR -1 + + const FUsbGenericHubOps *ops; + + void *data; +} FUsbGenericHub; + +void FUsbGenericHubDestory(FUsbDev *); +int FUsbGenericHubWaitForPort(FUsbDev *const dev, const int port, + const int wait_for, + int (*const port_op)(FUsbDev *, int), + int timeout_steps, const int step_us); +int FUsbGenericHubResetPort(FUsbDev *, int port); +int FUsbGenericHubScanPort(FUsbDev *, int port); +/* the provided generic_hub_ops struct has to be static */ +int FUsbGenericHubInit(FUsbDev *, int num_ports, const FUsbGenericHubOps *); + +#define FUSB_GEN_HUB_GET(FUsbDev) ((FUsbGenericHub *)(FUsbDev)->data) + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/bsp/phytium/libraries/standalone/drivers/usb/fusb_hid.c b/bsp/phytium/libraries/standalone/drivers/usb/fusb_hid.c new file mode 100644 index 0000000000..a2df9e6e13 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/usb/fusb_hid.c @@ -0,0 +1,554 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fusb_hid.c + * Date: 2022-09-28 18:26:42 + * LastEditTime: 2022-09-29 14:50:09 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include + +#include "fkernel.h" +#include "fdebug.h" +#include "fsleep.h" + +#include "fusb.h" +#include "fusb_hid.h" + +enum FUsbHidRequests +{ + GET_REPORT = 0x1, + GET_IDLE = 0x2, + GET_PROTOCOL = 0x3, + SET_REPORT = 0x9, + SET_IDLE = 0xa, + SET_PROTOCOL = 0xb +}; + +enum FUsbHidKeyboardModifiers +{ + KB_MOD_SHIFT = (1 << 0), + KB_MOD_ALT = (1 << 1), + KB_MOD_CTRL = (1 << 2), + KB_MOD_CAPSLOCK = (1 << 3), +}; + +typedef union +{ + struct + { + u8 modifiers; + u8 repeats; + u8 keys[6]; + }; + u8 buffer[8]; +} FUsbHidKeyboardEvent; + +typedef struct +{ + void *queue; + FUsbHidDescriptor *descriptor; + + FUsbHidKeyboardEvent previous; + int lastkeypress; + int repeat_delay; +} FUsbHid; + +#define KEYBOARD_REPEAT_MS 30 +#define INITIAL_REPEAT_DELAY 10 +#define REPEAT_DELAY 2 + +#define FUSB_HID_INST(dev) ((FUsbHid*)(dev)->data) + +#define FUSB_DEBUG_TAG "FUSB_HID" +#define FUSB_ERROR(format, ...) FT_DEBUG_PRINT_E(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_WARN(format, ...) FT_DEBUG_PRINT_W(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_INFO(format, ...) FT_DEBUG_PRINT_I(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_DEBUG(format, ...) FT_DEBUG_PRINT_D(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) + +static const char *boot_protos[3] = { "(none)", "keyboard", "mouse" }; + +static void FUsbHidDestory(FUsbDev *dev) +{ + FUsb *instance = dev->controller->usb; + + if (FUSB_HID_INST(dev)->queue) + { + int i; + for (i = 0; i <= dev->num_endp; i++) + { + if (dev->endpoints[i].endpoint == 0) + continue; + if (dev->endpoints[i].type != FUSB_INTERRUPT_EP) + continue; + if (dev->endpoints[i].direction != FUSB_IN) + continue; + break; + } + dev->controller->destroy_intr_queue( + &dev->endpoints[i], FUSB_HID_INST(dev)->queue); + FUSB_HID_INST(dev)->queue = NULL; + } + FUSB_FREE(instance, FUSB_HID_INST(dev)->descriptor); + FUSB_HID_INST(dev)->descriptor = NULL; + + FUSB_FREE(instance, dev->data); +} + +/* keybuffer is global to all USB keyboards */ +static int keycount; +#define KEYBOARD_BUFFER_SIZE 16 +static short keybuffer[KEYBOARD_BUFFER_SIZE]; +static int modifiers; + +static const char *countries[36][2] = +{ + { "not supported", "us" }, + { "Arabic", "ae" }, + { "Belgian", "be" }, + { "Canadian-Bilingual", "ca" }, + { "Canadian-French", "ca" }, + { "Czech Republic", "cz" }, + { "Danish", "dk" }, + { "Finnish", "fi" }, + { "French", "fr" }, + { "German", "de" }, + { "Greek", "gr" }, + { "Hebrew", "il" }, + { "Hungary", "hu" }, + { "International (ISO)", "iso" }, + { "Italian", "it" }, + { "Japan (Katakana)", "jp" }, + { "Korean", "us" }, + { "Latin American", "us" }, + { "Netherlands/Dutch", "nl" }, + { "Norwegian", "no" }, + { "Persian (Farsi)", "ir" }, + { "Poland", "pl" }, + { "Portuguese", "pt" }, + { "Russia", "ru" }, + { "Slovakia", "sl" }, + { "Spanish", "es" }, + { "Swedish", "se" }, + { "Swiss/French", "ch" }, + { "Swiss/German", "ch" }, + { "Switzerland", "ch" }, + { "Taiwan", "tw" }, + { "Turkish-Q", "tr" }, + { "UK", "uk" }, + { "US", "us" }, + { "Yugoslavia", "yu" }, + { "Turkish-F", "tr" }, + /* 36 - 255: Reserved */ +}; + +struct FUsbHidLayoutMaps +{ + const char *country; + const short map[4][0x80]; +}; + +static const struct FUsbHidLayoutMaps *map; + +static const struct FUsbHidLayoutMaps keyboard_layouts[] = +{ + { + .country = "us", + .map = { + { /* No modifier */ + -1, -1, -1, -1, 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', + /* 0x10 */ + 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', '1', '2', + /* 0x20 */ + '3', '4', '5', '6', '7', '8', '9', '0', + '\n', '\e', '\b', '\t', ' ', '-', '=', '[', + /* 0x30 */ + ']', '\\', -1, ';', '\'', '`', ',', '.', + '/', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6), + /* 0x40 */ + KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), KEY_F(11), KEY_F(12), KEY_PRINT, -1 /* ScrLk */, + KEY_BREAK, KEY_IC, KEY_HOME, KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT, + /* 50 */ + KEY_LEFT, KEY_DOWN, KEY_UP, -1 /*NumLck*/, '/', '*', '-' /* = ? */, '+', + KEY_ENTER, KEY_END, KEY_DOWN, KEY_NPAGE, KEY_LEFT, -1, KEY_RIGHT, KEY_HOME, + /* 60 */ + KEY_UP, KEY_PPAGE, -1, KEY_DC, -1 /* < > | */, -1 /* Win Key Right */, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + /* 70 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + }, + { /* Shift modifier */ + -1, -1, -1, -1, 'A', 'B', 'C', 'D', + 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', + /* 0x10 */ + 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@', + /* 0x20 */ + '#', '$', '%', '^', '&', '*', '(', ')', + '\n', '\e', '\b', '\t', ' ', '_', '+', '[', + /* 0x30 */ + ']', '\\', -1, ':', '\'', '`', ',', '.', + '/', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6), + /* 0x40 */ + KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), KEY_F(11), KEY_F(12), KEY_PRINT, -1 /* ScrLk */, + KEY_BREAK, KEY_IC, KEY_HOME, KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT, + /* 50 */ + KEY_LEFT, KEY_DOWN, KEY_UP, -1 /*NumLck*/, '/', '*', '-' /* = ? */, '+', + KEY_ENTER, KEY_END, KEY_DOWN, KEY_NPAGE, KEY_LEFT, -1, KEY_RIGHT, KEY_HOME, + /* 60 */ + KEY_UP, KEY_PPAGE, -1, KEY_DC, -1 /* < > | */, -1 /* Win Key Right */, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + /* 70 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + }, + { /* Alt */ + -1, -1, -1, -1, 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', + /* 0x10 */ + 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z', '1', '2', + /* 0x20 */ + '3', '4', '5', '6', '7', '8', '9', '0', + '\n', '\e', '\b', '\t', ' ', '-', '=', '[', + /* 0x30 */ + ']', '\\', -1, ';', '\'', '`', ',', '.', + '/', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6), + /* 0x40 */ + KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), KEY_F(11), KEY_F(12), KEY_PRINT, -1 /* ScrLk */, + KEY_BREAK, KEY_IC, KEY_HOME, KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT, + /* 50 */ + KEY_LEFT, KEY_DOWN, KEY_UP, -1 /*NumLck*/, '/', '*', '-' /* = ? */, '+', + KEY_ENTER, KEY_END, KEY_DOWN, KEY_NPAGE, KEY_LEFT, -1, KEY_RIGHT, KEY_HOME, + /* 60 */ + KEY_UP, KEY_PPAGE, -1, KEY_DC, -1 /* < > | */, -1 /* Win Key Right */, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + /* 70 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + }, + { /* Shift+Alt modifier */ + -1, -1, -1, -1, 'A', 'B', 'C', 'D', + 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', + /* 0x10 */ + 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@', + /* 0x20 */ + '#', '$', '%', '^', '&', '*', '(', ')', + '\n', '\e', '\b', '\t', ' ', '-', '=', '[', + /* 0x30 */ + ']', '\\', -1, ':', '\'', '`', ',', '.', + '/', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6), + /* 0x40 */ + KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), KEY_F(11), KEY_F(12), KEY_PRINT, -1 /* ScrLk */, + KEY_BREAK, KEY_IC, KEY_HOME, KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT, + /* 50 */ + KEY_LEFT, KEY_DOWN, KEY_UP, -1 /*NumLck*/, '/', '*', '-' /* = ? */, '+', + KEY_ENTER, KEY_END, KEY_DOWN, KEY_NPAGE, KEY_LEFT, -1, KEY_RIGHT, KEY_HOME, + /* 60 */ + KEY_UP, KEY_PPAGE, -1, KEY_DC, -1 /* < > | */, -1 /* Win Key Right */, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + /* 70 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + } + } + }, +}; + +static void FUsbHidKeyboardQueue(int ch) +{ + /* ignore key presses if buffer full */ + if (keycount < KEYBOARD_BUFFER_SIZE) + keybuffer[keycount++] = ch; +} + +/* handle hid received data */ +static void FUsbHidProcessKeyboardEvent(FUsbHid *const inst, + const FUsbHidKeyboardEvent *const current) +{ + const FUsbHidKeyboardEvent *const previous = &inst->previous; + + int i, keypress = 0; + + modifiers = 0; + + if (current->modifiers & 0x01) /* Left-Ctrl */ + modifiers |= KB_MOD_CTRL; + if (current->modifiers & 0x02) /* Left-Shift */ + modifiers |= KB_MOD_SHIFT; + if (current->modifiers & 0x04) /* Left-Alt */ + modifiers |= KB_MOD_ALT; + if (current->modifiers & 0x08) /* Left-GUI */ + { + + } + + if (current->modifiers & 0x10) /* Right-Ctrl */ + modifiers |= KB_MOD_CTRL; + if (current->modifiers & 0x20) /* Right-Shift */ + modifiers |= KB_MOD_SHIFT; + if (current->modifiers & 0x40) /* Right-AltGr */ + modifiers |= KB_MOD_ALT; + if (current->modifiers & 0x80) /* Right-GUI */ + { + + } + + if ((current->modifiers & 0x05) && ((current->keys[0] == 0x4c) || + (current->keys[0] == 0x63))) + { + /* todo, Reboot here */ + } + + /* Did the event change at all? */ + if (inst->lastkeypress && + !memcmp(current, previous, sizeof(*current))) + { + /* No. Then it's a key repeat event. */ + if (inst->repeat_delay) + { + inst->repeat_delay--; + } + else + { + FUsbHidKeyboardQueue(inst->lastkeypress); + inst->repeat_delay = REPEAT_DELAY; + } + + return; + } + + inst->lastkeypress = 0; + + for (i = 0; i < 6; i++) + { + int j; + int skip = 0; + /* No more keys? skip */ + if (current->keys[i] == 0) + return; + + for (j = 0; j < 6; j++) + { + if (current->keys[i] == previous->keys[j]) + { + skip = 1; + break; + } + } + + if (skip) + continue; + + /* Mask off KB_MOD_CTRL */ + keypress = map->map[modifiers & 0x03][current->keys[i]]; + + if (modifiers & KB_MOD_CTRL) + { + switch (keypress) + { + case 'a' ... 'z': + keypress &= 0x1f; + break; + default: + continue; + } + } + + if (keypress == -1) + { + /* Debug: Print unknown keys */ + FUSB_INFO("usbhid: <%x> %x [ %x %x %x %x %x %x ] %d\n", + current->modifiers, current->repeats, + current->keys[0], current->keys[1], + current->keys[2], current->keys[3], + current->keys[4], current->keys[5], i); + + /* Unknown key? Try next one in the queue */ + continue; + } + + FUsbHidKeyboardQueue(keypress); + + /* Remember for authentic key repeat */ + inst->lastkeypress = keypress; + inst->repeat_delay = INITIAL_REPEAT_DELAY; + } +} + +static void FUsbHidPoll(FUsbDev *dev) +{ + FUsbHidKeyboardEvent current; + const u8 *buf; + + while ((buf = dev->controller->poll_intr_queue(FUSB_HID_INST(dev)->queue))) + { + memcpy(¤t.buffer, buf, 8); + FUsbHidProcessKeyboardEvent(FUSB_HID_INST(dev), ¤t); + FUSB_HID_INST(dev)->previous = current; + } +} + +static void FUsbHidSetIdle(FUsbDev *dev, FUsbInterfaceDescriptor *interface, u16 duration) +{ + FUsbDevReq dr; + dr.data_dir = FUSB_REQ_HOST_TO_DEVICE; + dr.req_type = FUSB_REQ_TYPE_CLASS; + dr.req_recp = FUSB_REQ_RECP_IF; + dr.bRequest = SET_IDLE; + dr.wValue = (duration >> 2) << 8; + dr.wIndex = interface->bInterfaceNumber; + dr.wLength = 0; + dev->controller->control(dev, FUSB_OUT, sizeof(FUsbDevReq), &dr, 0, NULL); +} + +static void FUsbHidSetProtocol(FUsbDev *dev, FUsbInterfaceDescriptor *interface, FUsbHidProtocol proto) +{ + FUsbDevReq dr; + dr.data_dir = FUSB_REQ_HOST_TO_DEVICE; + dr.req_type = FUSB_REQ_TYPE_CLASS; + dr.req_recp = FUSB_REQ_RECP_IF; + dr.bRequest = SET_PROTOCOL; + dr.wValue = proto; + dr.wIndex = interface->bInterfaceNumber; + dr.wLength = 0; + dev->controller->control(dev, FUSB_OUT, sizeof(FUsbDevReq), &dr, 0, 0); +} + +static int FUsbHidSetLayout(const char *country) +{ + /* FIXME should be per keyboard */ + for (fsize_t i = 0; i < ARRAY_SIZE(keyboard_layouts); i++) + { + if (strncmp(keyboard_layouts[i].country, country, + strlen(keyboard_layouts[i].country))) + continue; + + /* Found, changing keyboard layout */ + map = &keyboard_layouts[i]; + FUSB_DEBUG(" Keyboard layout '%s'\n", map->country); + return 0; + } + + FUSB_DEBUG(" Keyboard layout '%s' not found, using '%s'\n", + country, map->country); + + /* Nothing found, not changed */ + return -1; +} + +void FUsbHidInit(FUsbDev *dev) +{ + FUsb *instance = dev->controller->usb; + + FUsbConfigurationDescriptor *cd = (FUsbConfigurationDescriptor *)dev->configuration; + FUsbInterfaceDescriptor *interface = (FUsbInterfaceDescriptor *)(((char *) cd) + cd->bLength); + + if (interface->bInterfaceSubClass == FUSB_HID_SUBCLASS_BOOT) + { + u8 countrycode; + FUSB_DEBUG(" supports boot interface..\n"); + FUSB_DEBUG(" it's a %s\n", + boot_protos[interface->bInterfaceProtocol]); + switch (interface->bInterfaceProtocol) + { + case FUSB_HID_BOOT_PROTOCOL_KEYBOARD: + dev->data = FUSB_ALLOCATE(instance, sizeof(FUsbHid), FUSB_DEFAULT_ALIGN); + FUSB_DEBUG(" configuring...\n"); + FUsbHidSetProtocol(dev, interface, FUSB_HID_PROTOCOL_BOOT); + FUsbHidSetIdle(dev, interface, KEYBOARD_REPEAT_MS); + FUSB_DEBUG(" activating...\n"); + + FUsbHidDescriptor *desc = FUSB_ALLOCATE(instance, sizeof(FUsbHidDescriptor), FUSB_DEFAULT_ALIGN); + if (!desc || FUsbGetDescriptor(dev, FUsbGenerateReqType( + FUSB_REQ_DEVICE_TO_HOST, FUSB_REQ_TYPE_STANDARD, FUSB_REQ_RECP_IF), + 0x21, 0, desc, sizeof(*desc)) != sizeof(*desc)) + { + FUSB_DEBUG("FUsbGetDescriptor(HID) failed\n"); + FUsbDetachDev(dev->controller, dev->address); + return; + } + FUSB_HID_INST(dev)->descriptor = desc; + countrycode = desc->bCountryCode; + /* 35 countries defined: */ + if (countrycode >= ARRAY_SIZE(countries)) + countrycode = 0; + printf(" Keyboard has %s layout (country code %02x)\n", + countries[countrycode][0], countrycode); + + /* Set keyboard layout accordingly */ + FUsbHidSetLayout(countries[countrycode][1]); + + // only add here, because we only support boot-keyboard HID devices + dev->destroy = FUsbHidDestory; + dev->poll = FUsbHidPoll; + int i; + for (i = 1; i < dev->num_endp; i++) + { + if (dev->endpoints[i].type != FUSB_INTERRUPT_EP) + continue; + if (dev->endpoints[i].direction != FUSB_IN) + continue; + break; + } + + if (i >= dev->num_endp) + { + FUSB_DEBUG("Could not find HID endpoint\n"); + FUsbDetachDev(dev->controller, dev->address); + return; + } + + FUSB_DEBUG(" found endpoint %x for interrupt-in\n", i); + /* 20 buffers of 8 bytes, for every 10 msecs */ + FUSB_HID_INST(dev)->queue = dev->controller->create_intr_queue(&dev->endpoints[i], 8, 20, 10); + keycount = 0; + FUSB_DEBUG(" configuration done.\n"); + break; + case FUSB_HID_BOOT_PROTOCOL_MOUSE: + FUSB_DEBUG("NOTICE: USB mice are not supported.\n"); + break; + } + } +} + +int FUsbHidCheckInput(FUsbDev *dev, int times) +{ + short ret; + FUsb *instance = dev->controller->usb; + + for (int i = 0; i < times; i++) + { + FUsbPoll(instance); + + while (keycount != 0) + { + ret = keybuffer[0]; + memmove(keybuffer, keybuffer + 1, --keycount); + printf("%c", ret); + } + + fsleep_millisec(10); + } + + printf("\r\n"); +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/usb/fusb_hid.h b/bsp/phytium/libraries/standalone/drivers/usb/fusb_hid.h new file mode 100644 index 0000000000..e4bc50e363 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/usb/fusb_hid.h @@ -0,0 +1,328 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fusb_hid.h + * Date: 2022-02-11 13:33:09 + * LastEditTime: 2022-02-17 17:49:20 + * Description:  This files is for definition of USB hub device function + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/9/28 init commit + */ + +#ifndef DRIVERS_USB_HID_H +#define DRIVERS_USB_HID_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ + +#include "fusb.h" + +/************************** Constant Definitions *****************************/ +/*---------------------------------------------------------------------- + * + * Function and Keypad Key Definitions. + * Many are just for compatibility. + * + */ + +#define KEY_CODE_YES 0x100 /* If get_wch() gives a key code */ + +#define KEY_BREAK 0x101 /* Not on PC KBD */ +#define KEY_DOWN 0x102 /* Down arrow key */ +#define KEY_UP 0x103 /* Up arrow key */ +#define KEY_LEFT 0x104 /* Left arrow key */ +#define KEY_RIGHT 0x105 /* Right arrow key */ +#define KEY_HOME 0x106 /* home key */ +#define KEY_BACKSPACE 0x107 /* not on pc */ +#define KEY_F0 0x108 /* function keys; 64 reserved */ + +#define KEY_DL 0x148 /* delete line */ +#define KEY_IL 0x149 /* insert line */ +#define KEY_DC 0x14a /* delete character */ +#define KEY_IC 0x14b /* insert char or enter ins mode */ +#define KEY_EIC 0x14c /* exit insert char mode */ +#define KEY_CLEAR 0x14d /* clear screen */ +#define KEY_EOS 0x14e /* clear to end of screen */ +#define KEY_EOL 0x14f /* clear to end of line */ +#define KEY_SF 0x150 /* scroll 1 line forward */ +#define KEY_SR 0x151 /* scroll 1 line back (reverse) */ +#define KEY_NPAGE 0x152 /* next page */ +#define KEY_PPAGE 0x153 /* previous page */ +#define KEY_STAB 0x154 /* set tab */ +#define KEY_CTAB 0x155 /* clear tab */ +#define KEY_CATAB 0x156 /* clear all tabs */ +#define KEY_ENTER 0x157 /* enter or send (unreliable) */ +#define KEY_SRESET 0x158 /* soft/reset (partial/unreliable) */ +#define KEY_RESET 0x159 /* reset/hard reset (unreliable) */ +#define KEY_PRINT 0x15a /* print/copy */ +#define KEY_LL 0x15b /* home down/bottom (lower left) */ +#define KEY_ABORT 0x15c /* abort/terminate key (any) */ +#define KEY_SHELP 0x15d /* short help */ +#define KEY_LHELP 0x15e /* long help */ +#define KEY_BTAB 0x15f /* Back tab key */ +#define KEY_BEG 0x160 /* beg(inning) key */ +#define KEY_CANCEL 0x161 /* cancel key */ +#define KEY_CLOSE 0x162 /* close key */ +#define KEY_COMMAND 0x163 /* cmd (command) key */ +#define KEY_COPY 0x164 /* copy key */ +#define KEY_CREATE 0x165 /* create key */ +#define KEY_END 0x166 /* end key */ +#define KEY_EXIT 0x167 /* exit key */ +#define KEY_FIND 0x168 /* find key */ +#define KEY_HELP 0x169 /* help key */ +#define KEY_MARK 0x16a /* mark key */ +#define KEY_MESSAGE 0x16b /* message key */ +#define KEY_MOVE 0x16c /* move key */ +#define KEY_NEXT 0x16d /* next object key */ +#define KEY_OPEN 0x16e /* open key */ +#define KEY_OPTIONS 0x16f /* options key */ +#define KEY_PREVIOUS 0x170 /* previous object key */ +#define KEY_REDO 0x171 /* redo key */ +#define KEY_REFERENCE 0x172 /* ref(erence) key */ +#define KEY_REFRESH 0x173 /* refresh key */ +#define KEY_REPLACE 0x174 /* replace key */ +#define KEY_RESTART 0x175 /* restart key */ +#define KEY_RESUME 0x176 /* resume key */ +#define KEY_SAVE 0x177 /* save key */ +#define KEY_SBEG 0x178 /* shifted beginning key */ +#define KEY_SCANCEL 0x179 /* shifted cancel key */ +#define KEY_SCOMMAND 0x17a /* shifted command key */ +#define KEY_SCOPY 0x17b /* shifted copy key */ +#define KEY_SCREATE 0x17c /* shifted create key */ +#define KEY_SDC 0x17d /* shifted delete char key */ +#define KEY_SDL 0x17e /* shifted delete line key */ +#define KEY_SELECT 0x17f /* select key */ +#define KEY_SEND 0x180 /* shifted end key */ +#define KEY_SEOL 0x181 /* shifted clear line key */ +#define KEY_SEXIT 0x182 /* shifted exit key */ +#define KEY_SFIND 0x183 /* shifted find key */ +#define KEY_SHOME 0x184 /* shifted home key */ +#define KEY_SIC 0x185 /* shifted input key */ + +#define KEY_SLEFT 0x187 /* shifted left arrow key */ +#define KEY_SMESSAGE 0x188 /* shifted message key */ +#define KEY_SMOVE 0x189 /* shifted move key */ +#define KEY_SNEXT 0x18a /* shifted next key */ +#define KEY_SOPTIONS 0x18b /* shifted options key */ +#define KEY_SPREVIOUS 0x18c /* shifted prev key */ +#define KEY_SPRINT 0x18d /* shifted print key */ +#define KEY_SREDO 0x18e /* shifted redo key */ +#define KEY_SREPLACE 0x18f /* shifted replace key */ +#define KEY_SRIGHT 0x190 /* shifted right arrow */ +#define KEY_SRSUME 0x191 /* shifted resume key */ +#define KEY_SSAVE 0x192 /* shifted save key */ +#define KEY_SSUSPEND 0x193 /* shifted suspend key */ +#define KEY_SUNDO 0x194 /* shifted undo key */ +#define KEY_SUSPEND 0x195 /* suspend key */ +#define KEY_UNDO 0x196 /* undo key */ + +/* PDCurses-specific key definitions -- PC only */ + +#define ALT_0 0x197 +#define ALT_1 0x198 +#define ALT_2 0x199 +#define ALT_3 0x19a +#define ALT_4 0x19b +#define ALT_5 0x19c +#define ALT_6 0x19d +#define ALT_7 0x19e +#define ALT_8 0x19f +#define ALT_9 0x1a0 +#define ALT_A 0x1a1 +#define ALT_B 0x1a2 +#define ALT_C 0x1a3 +#define ALT_D 0x1a4 +#define ALT_E 0x1a5 +#define ALT_F 0x1a6 +#define ALT_G 0x1a7 +#define ALT_H 0x1a8 +#define ALT_I 0x1a9 +#define ALT_J 0x1aa +#define ALT_K 0x1ab +#define ALT_L 0x1ac +#define ALT_M 0x1ad +#define ALT_N 0x1ae +#define ALT_O 0x1af +#define ALT_P 0x1b0 +#define ALT_Q 0x1b1 +#define ALT_R 0x1b2 +#define ALT_S 0x1b3 +#define ALT_T 0x1b4 +#define ALT_U 0x1b5 +#define ALT_V 0x1b6 +#define ALT_W 0x1b7 +#define ALT_X 0x1b8 +#define ALT_Y 0x1b9 +#define ALT_Z 0x1ba + +#define CTL_LEFT 0x1bb /* Control-Left-Arrow */ +#define CTL_RIGHT 0x1bc +#define CTL_PGUP 0x1bd +#define CTL_PGDN 0x1be +#define CTL_HOME 0x1bf +#define CTL_END 0x1c0 + +#define KEY_A1 0x1c1 /* upper left on Virtual keypad */ +#define KEY_A2 0x1c2 /* upper middle on Virt. keypad */ +#define KEY_A3 0x1c3 /* upper right on Vir. keypad */ +#define KEY_B1 0x1c4 /* middle left on Virt. keypad */ +#define KEY_B2 0x1c5 /* center on Virt. keypad */ +#define KEY_B3 0x1c6 /* middle right on Vir. keypad */ +#define KEY_C1 0x1c7 /* lower left on Virt. keypad */ +#define KEY_C2 0x1c8 /* lower middle on Virt. keypad */ +#define KEY_C3 0x1c9 /* lower right on Vir. keypad */ + +#define PADSLASH 0x1ca /* slash on keypad */ +#define PADENTER 0x1cb /* enter on keypad */ +#define CTL_PADENTER 0x1cc /* ctl-enter on keypad */ +#define ALT_PADENTER 0x1cd /* alt-enter on keypad */ +#define PADSTOP 0x1ce /* stop on keypad */ +#define PADSTAR 0x1cf /* star on keypad */ +#define PADMINUS 0x1d0 /* minus on keypad */ +#define PADPLUS 0x1d1 /* plus on keypad */ +#define CTL_PADSTOP 0x1d2 /* ctl-stop on keypad */ +#define CTL_PADCENTER 0x1d3 /* ctl-enter on keypad */ +#define CTL_PADPLUS 0x1d4 /* ctl-plus on keypad */ +#define CTL_PADMINUS 0x1d5 /* ctl-minus on keypad */ +#define CTL_PADSLASH 0x1d6 /* ctl-slash on keypad */ +#define CTL_PADSTAR 0x1d7 /* ctl-star on keypad */ +#define ALT_PADPLUS 0x1d8 /* alt-plus on keypad */ +#define ALT_PADMINUS 0x1d9 /* alt-minus on keypad */ +#define ALT_PADSLASH 0x1da /* alt-slash on keypad */ +#define ALT_PADSTAR 0x1db /* alt-star on keypad */ +#define ALT_PADSTOP 0x1dc /* alt-stop on keypad */ +#define CTL_INS 0x1dd /* ctl-insert */ +#define ALT_DEL 0x1de /* alt-delete */ +#define ALT_INS 0x1df /* alt-insert */ +#define CTL_UP 0x1e0 /* ctl-up arrow */ +#define CTL_DOWN 0x1e1 /* ctl-down arrow */ +#define CTL_TAB 0x1e2 /* ctl-tab */ +#define ALT_TAB 0x1e3 +#define ALT_MINUS 0x1e4 +#define ALT_EQUAL 0x1e5 +#define ALT_HOME 0x1e6 +#define ALT_PGUP 0x1e7 +#define ALT_PGDN 0x1e8 +#define ALT_END 0x1e9 +#define ALT_UP 0x1ea /* alt-up arrow */ +#define ALT_DOWN 0x1eb /* alt-down arrow */ +#define ALT_RIGHT 0x1ec /* alt-right arrow */ +#define ALT_LEFT 0x1ed /* alt-left arrow */ +#define ALT_ENTER 0x1ee /* alt-enter */ +#define ALT_ESC 0x1ef /* alt-escape */ +#define ALT_BQUOTE 0x1f0 /* alt-back quote */ +#define ALT_LBRACKET 0x1f1 /* alt-left bracket */ +#define ALT_RBRACKET 0x1f2 /* alt-right bracket */ +#define ALT_SEMICOLON 0x1f3 /* alt-semi-colon */ +#define ALT_FQUOTE 0x1f4 /* alt-forward quote */ +#define ALT_COMMA 0x1f5 /* alt-comma */ +#define ALT_STOP 0x1f6 /* alt-stop */ +#define ALT_FSLASH 0x1f7 /* alt-forward slash */ +#define ALT_BKSP 0x1f8 /* alt-backspace */ +#define CTL_BKSP 0x1f9 /* ctl-backspace */ +#define PAD0 0x1fa /* keypad 0 */ + +#define CTL_PAD0 0x1fb /* ctl-keypad 0 */ +#define CTL_PAD1 0x1fc +#define CTL_PAD2 0x1fd +#define CTL_PAD3 0x1fe +#define CTL_PAD4 0x1ff +#define CTL_PAD5 0x200 +#define CTL_PAD6 0x201 +#define CTL_PAD7 0x202 +#define CTL_PAD8 0x203 +#define CTL_PAD9 0x204 + +#define ALT_PAD0 0x205 /* alt-keypad 0 */ +#define ALT_PAD1 0x206 +#define ALT_PAD2 0x207 +#define ALT_PAD3 0x208 +#define ALT_PAD4 0x209 +#define ALT_PAD5 0x20a +#define ALT_PAD6 0x20b +#define ALT_PAD7 0x20c +#define ALT_PAD8 0x20d +#define ALT_PAD9 0x20e + +#define CTL_DEL 0x20f /* clt-delete */ +#define ALT_BSLASH 0x210 /* alt-back slash */ +#define CTL_ENTER 0x211 /* ctl-enter */ + +#define SHF_PADENTER 0x212 /* shift-enter on keypad */ +#define SHF_PADSLASH 0x213 /* shift-slash on keypad */ +#define SHF_PADSTAR 0x214 /* shift-star on keypad */ +#define SHF_PADPLUS 0x215 /* shift-plus on keypad */ +#define SHF_PADMINUS 0x216 /* shift-minus on keypad */ +#define SHF_UP 0x217 /* shift-up on keypad */ +#define SHF_DOWN 0x218 /* shift-down on keypad */ +#define SHF_IC 0x219 /* shift-insert on keypad */ +#define SHF_DC 0x21a /* shift-delete on keypad */ + +#define KEY_MOUSE 0x21b /* "mouse" key */ +#define KEY_SHIFT_L 0x21c /* Left-shift */ +#define KEY_SHIFT_R 0x21d /* Right-shift */ +#define KEY_CONTROL_L 0x21e /* Left-control */ +#define KEY_CONTROL_R 0x21f /* Right-control */ +#define KEY_ALT_L 0x220 /* Left-alt */ +#define KEY_ALT_R 0x221 /* Right-alt */ +#define KEY_RESIZE 0x222 /* Window resize */ +#define KEY_SUP 0x223 /* Shifted up arrow */ +#define KEY_SDOWN 0x224 /* Shifted down arrow */ + +#define KEY_MIN KEY_BREAK /* Minimum curses key value */ +#define KEY_MAX KEY_SDOWN /* Maximum curses key */ + +#define KEY_F(n) (KEY_F0 + (n)) + +/**************************** Type Definitions *******************************/ +enum +{ + FUSB_HID_SUBCLASS_NONE = 0, + FUSB_HID_SUBCLASS_BOOT = 1 +}; + +typedef enum +{ + FUSB_HID_PROTOCOL_BOOT = 0, + FUSB_HID_PROTOCOL_REPORT = 1 +} FUsbHidProtocol; + +enum +{ + FUSB_HID_BOOT_PROTOCOL_NONE = 0, + FUSB_HID_BOOT_PROTOCOL_KEYBOARD = 1, + FUSB_HID_BOOT_PROTOCOL_MOUSE = 2 +}; + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ +/* USB HID的初始化函数,由应用程序注册到FUSB框架中 */ +void FUsbHidInit(FUsbDev *dev); +int FUsbHidCheckInput(FUsbDev *dev, int times); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/bsp/phytium/libraries/standalone/drivers/usb/fusb_hub.c b/bsp/phytium/libraries/standalone/drivers/usb/fusb_hub.c new file mode 100644 index 0000000000..a18cd4e09f --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/usb/fusb_hub.c @@ -0,0 +1,493 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fusb_hub.c + * Date: 2022-02-11 13:33:07 + * LastEditTime: 2022-02-17 17:48:52 + * Description:  This files is for implmentation of USB hub function + * you may refer to chapter 11 Hub specification for details + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/2/7 init commit + */ + +#include "fkernel.h" +#include "fdebug.h" +#include "fassert.h" + +#include "fusb_private.h" +#include "fusb_generic_hub.h" + +#define FUSB_DEBUG_TAG "FUSB_HUB" +#define FUSB_ERROR(format, ...) FT_DEBUG_PRINT_E(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_WARN(format, ...) FT_DEBUG_PRINT_W(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_INFO(format, ...) FT_DEBUG_PRINT_I(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_DEBUG(format, ...) FT_DEBUG_PRINT_D(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) + +/* assume that FUSB_REQ_HOST_TO_DEVICE is overwritten if necessary */ +#define DR_PORT FUsbGenerateReqType(FUSB_REQ_HOST_TO_DEVICE, FUSB_REQ_TYPE_CLASS, FUSB_REQ_RECP_OTHER) /* 10100011B */ +#define DR_HUB FUsbGenerateReqType(FUSB_REQ_HOST_TO_DEVICE, FUSB_REQ_TYPE_CLASS, FUSB_REQ_RECP_DEV) /* 10100000B */ + +#define FUSB_HUB_PORT_STATUS 0 +#define FUSB_HUB_PORT_CHANGE 1 + +#define FUSB_HUB_PORT_BUF_LEN 2 + +/* status (and status change) bits, refer to Table 10-10, Port Status Field in USB spec */ +#define FUSB_HUB_STATUS_PORT_CONNECTION BIT(0) /* reflects if device is currently connected to this port */ +#define FUSB_HUB_STATUS_PORT_ENABLE BIT(1) /* reflects if this port is enabled */ +#define FUSB_HUB_STATUS_PORT_SUSPEND BIT(2) /* reflects if this port is suspend, only for USB2 */ +#define FUSB_HUB_STATUS_PORT_OVER_CURRENT BIT(3) /* reports over-current conditions in this port */ +#define FUSB_HUB_STATUS_PORT_RESET BIT(4) /* reset signaling asserted */ +#define FUSB_HUB_STATUS_BH_PORT_RESET BIT(5) /* warm reset completed */ +#define FUSB_HUB_STATUS_PORT_LINK_STATE BIT(6) /* link state changed */ +#define FUSB_HUB_STATUS_PORT_PORT_CONFIG_ERROR BIT(7) /* port fails to config */ + +/* feature selectors (for setting / clearing features), refer to USB spec. Table 10-17. Hub Class Feature Selectors for details */ +#define FUSB_HUB_SEL_PORT_RESET 4 +#define FUSB_HUB_SEL_PORT_POWER 8 +#define FUSB_HUB_SEL_C_PORT_CONNECTION 16 +#define FUSB_HUB_SEL_C_PORT_ENABLE 17 /* USB2 only */ +#define FUSB_HUB_SEL_C_PORT_SUSPEND 18 /* USB2 only */ +#define FUSB_HUB_SEL_C_PORT_OVER_CURRENT 19 +#define FUSB_HUB_SEL_C_PORT_RESET 20 +#define FUSB_HUB_SEL_C_PORT_LINK_STATE 25 +#define FUSB_HUB_SEL_C_PORT_CONFIG_ERROR 26 +#define FUSB_HUB_SEL_C_BH_PORT_RESET 27 + +/* request type (USB 3.0 hubs only) */ +#define SET_HUB_DEPTH 12 + +/** + * @name: FUsbHubIntrEp + * @msg: 获取USB Hub的中断端点 + * @return {FUsbEndpoint *} 中断类型的功能端点 + * @param {FUsbDev} *dev, Hub实例 + */ +static FUsbEndpoint *FUsbHubIntrEp(FUsbDev *const dev) +{ + FASSERT(dev); + int i; + + for (i = 0; i < dev->num_endp; ++i) + { + if (dev->endpoints[i].type == FUSB_INTERRUPT_EP && + dev->endpoints[i].direction == FUSB_IN) + return &dev->endpoints[i]; + } + + return NULL; +} + +/** + * @name: FUsbHubPortStatusChange + * @msg: Usb Hub的Port状态变化回调函数 + * @return {FUsbTransCode} USB请求返回值 + * @param {FUsbDev} *dev, Hub实例 + * @param {int} port, Port号 + */ +static FUsbTransCode FUsbHubPortStatusChange(FUsbDev *const dev, const int port) +{ + FASSERT(dev); + unsigned short buf[FUSB_HUB_PORT_BUF_LEN]; /* Hub Status and Change Status */ + FUsbTransCode ret = FUsbGetStatus(dev, port, DR_PORT, sizeof(buf), buf); + + if (ret >= FUSB_CC_ZERO_BYTES) + { + ret = buf[FUSB_HUB_PORT_CHANGE] & FUSB_HUB_STATUS_PORT_CONNECTION; + if (ret) + { + ret = FUsbClearFeature(dev, port, FUSB_HUB_SEL_C_PORT_CONNECTION, DR_PORT); + } + } + + return ret; +} + +/** + * @name: FUsbHubPortConnected + * @msg: Usb Hub的Port连接回调函数 + * @return {FUsbTransCode} USB请求返回值 + * @param {FUsbDev} *dev, Hub实例 + * @param {int} port, Port号 + */ +static FUsbTransCode FUsbHubPortConnected(FUsbDev *const dev, const int port) +{ + FASSERT(dev); + unsigned short buf[FUSB_HUB_PORT_BUF_LEN]; /* Hub Status and Change Status */ + FUsbTransCode ret = FUsbGetStatus(dev, port, DR_PORT, sizeof(buf), buf); + + if (ret >= FUSB_CC_ZERO_BYTES) + { + ret = buf[FUSB_HUB_PORT_STATUS] & FUSB_HUB_STATUS_PORT_CONNECTION; + } + + return ret; +} + +/** + * @name: FUsbHubPortInReset + * @msg: 检查Hub port是否处于Reset状态 + * @return {FUsbTransCode} USB请求返回值 + * @param {FUsbDev} *dev, Hub实例 + * @param {int} port, Port号 + */ +static FUsbTransCode FUsbHubPortInReset(FUsbDev *const dev, const int port) +{ + FASSERT(dev); + unsigned short buf[FUSB_HUB_PORT_BUF_LEN]; /* Hub Status and Change Status */ + FUsbTransCode ret = FUsbGetStatus(dev, port, DR_PORT, sizeof(buf), buf); + + if (ret >= FUSB_CC_ZERO_BYTES) + ret = buf[FUSB_HUB_PORT_STATUS] & FUSB_HUB_STATUS_PORT_RESET; + + return ret; +} + +/** + * @name: FUsbHubPortEnabled + * @msg: 检查Hub port是否已使能 + * @return {FUsbTransCode} USB请求返回值 + * @param {FUsbDev} *dev, Hub实例 + * @param {int} port, Port号 + */ +static FUsbTransCode FUsbHubPortEnabled(FUsbDev *const dev, const int port) +{ + FASSERT(dev); + unsigned short buf[FUSB_HUB_PORT_BUF_LEN]; /* Hub Status and Change Status */ + FUsbTransCode ret = FUsbGetStatus(dev, port, DR_PORT, sizeof(buf), buf); + + if (ret >= FUSB_CC_ZERO_BYTES) + ret = buf[FUSB_HUB_PORT_STATUS] & FUSB_HUB_STATUS_PORT_ENABLE; + + return ret; +} + +/** + * @name: FUsbHubPortSpeed + * @msg: 获取Hub port的速度类型 + * @return {FUsbSpeed} Port的速度类型,支持SuperSpeed和HighSpeed + * @param {FUsbDev} *dev, Hub实例 + * @param {int} port, Port号 + */ +static FUsbSpeed FUsbHubPortSpeed(FUsbDev *const dev, const int port) +{ + FASSERT(dev); + unsigned short buf[FUSB_HUB_PORT_BUF_LEN]; /* Hub Status and Change Status */ + FUsbTransCode ret = FUsbGetStatus(dev, port, DR_PORT, sizeof(buf), buf); + int speed; + + if (ret >= FUSB_CC_ZERO_BYTES && (buf[FUSB_HUB_PORT_STATUS] & FUSB_HUB_STATUS_PORT_ENABLE)) + { + /* SuperSpeed hubs can only have SuperSpeed devices. */ + if (FUsbIsSuperSpeed(dev->speed)) + return dev->speed; + + /*[bit] 10 9 (USB 2.0 port status word) + * 0 0 full speed + * 0 1 low speed + * 1 0 high speed + * 1 1 invalid + */ + speed = (buf[FUSB_HUB_PORT_STATUS] >> 9) & 0x3; + if (speed != 0x3) /* high-speed device */ + return speed; + } + + return FUSB_UNKNOWN_SPEED; +} + +/** + * @name: FUsbHubEnablePort + * @msg: 使能Hub port + * @return {FUsbTransCode} USB请求返回值 + * @param {FUsbDev} *dev, Hub实例 + * @param {int} port, port号 + */ +static FUsbTransCode FUsbHubEnablePort(FUsbDev *const dev, const int port) +{ + FASSERT(dev); + return FUsbSetFeature(dev, port, FUSB_HUB_SEL_PORT_POWER, DR_PORT); +} + +/** + * @name: FUsbHubStartPortReset + * @msg: 开始Reset Hub port + * @return {FUsbTransCode} USB请求返回值 + * @param {FUsbDev} *dev, Hub实例 + * @param {int} port, port号 + */ +static FUsbTransCode FUsbHubStartPortReset(FUsbDev *const dev, const int port) +{ + FASSERT(dev); + return FUsbSetFeature(dev, port, FUSB_HUB_SEL_PORT_RESET, DR_PORT); +} + +static void FUsbHubSetHubDepth(FUsbDev *const dev) +{ + FASSERT(dev); + FUsbDevReq dr = + { + .bmRequestType = FUsbGenerateReqType(FUSB_REQ_HOST_TO_DEVICE, + FUSB_REQ_TYPE_CLASS, FUSB_REQ_RECP_DEV), + .bRequest = SET_HUB_DEPTH, + .wValue = 0, + .wIndex = 0, + .wLength = 0, + }; + FUsbDev *parent = dev; + + FASSERT(dev->controller); + while (parent->hub > 0) + { + FASSERT(dev->controller->devices[parent->hub]); + parent = dev->controller->devices[parent->hub]; + dr.wValue++; + } + + FUsbTransCode ret = dev->controller->control(dev, FUSB_OUT, sizeof(dr), &dr, 0, NULL); + if (ret < FUSB_CC_ZERO_BYTES) + { + FUSB_ERROR("Failed SET_HUB_DEPTH(%d) on hub %d: %d ", + dr.wValue, dev->address, ret); + } + + return; +} + +static const FUsbGenericHubOps FUSB_HUB_OPS = +{ + .hub_status_changed = NULL, + .port_status_changed = FUsbHubPortStatusChange, + .port_connected = FUsbHubPortConnected, + .port_in_reset = FUsbHubPortInReset, + .port_enabled = FUsbHubPortEnabled, + .port_speed = FUsbHubPortSpeed, + .enable_port = FUsbHubEnablePort, + .disable_port = NULL, + .start_port_reset = FUsbHubStartPortReset, + .reset_port = FUsbGenericHubResetPort, +}; + +/* Clear CSC if set and enumerate port if it's connected regardless of change + bits. Some broken hubs don't set CSC if already connected during reset. */ +static void FUsbHubPortInit(FUsbDev *const dev, const int port) +{ + FASSERT(dev); + unsigned short buf[FUSB_HUB_PORT_BUF_LEN]; /* Hub Status and Change Status */ + FUsbTransCode ret = FUsbGetStatus(dev, port, DR_PORT, sizeof(buf), buf); + + if (ret < FUSB_CC_ZERO_BYTES) + return; + + if (buf[FUSB_HUB_PORT_CHANGE] & FUSB_HUB_STATUS_PORT_CONNECTION) + FUsbClearFeature(dev, port, FUSB_HUB_SEL_C_PORT_CONNECTION, DR_PORT); + + if (buf[FUSB_HUB_PORT_STATUS] & FUSB_HUB_STATUS_PORT_CONNECTION) + { + FUSB_INFO("usbhub: Port coldplug at %d ", port); + FUsbGenericHubScanPort(dev, port); + } + + return; +} + +/** + * @name: FUsbHubHandlePortChange + * @msg: Hub端口状态变化的处理回调函数 + * @return {*} + * @param {FUsbDev} *dev + * @param {int} port + */ +static FUsbTransCode FUsbHubHandlePortChange(FUsbDev *const dev, const int port) +{ + FASSERT(dev); + static const struct + { + unsigned short change_bit; + unsigned short clear_sel; + } change_bits[] = + { + {FUSB_HUB_STATUS_PORT_CONNECTION, FUSB_HUB_SEL_C_PORT_CONNECTION}, + {FUSB_HUB_STATUS_PORT_ENABLE, FUSB_HUB_SEL_C_PORT_ENABLE}, + {FUSB_HUB_STATUS_PORT_SUSPEND, FUSB_HUB_SEL_C_PORT_SUSPEND}, + {FUSB_HUB_STATUS_PORT_OVER_CURRENT, FUSB_HUB_SEL_C_PORT_OVER_CURRENT}, + {FUSB_HUB_STATUS_PORT_RESET, FUSB_HUB_SEL_C_PORT_RESET}, + {FUSB_HUB_STATUS_BH_PORT_RESET, FUSB_HUB_SEL_C_BH_PORT_RESET}, + {FUSB_HUB_STATUS_PORT_LINK_STATE, FUSB_HUB_SEL_C_PORT_LINK_STATE}, + {FUSB_HUB_STATUS_PORT_PORT_CONFIG_ERROR, FUSB_HUB_SEL_C_PORT_CONFIG_ERROR}, + }; + + FUsbTransCode ret = 0; + unsigned int i; + unsigned short checked_bits = 0; + unsigned short buf[FUSB_HUB_PORT_BUF_LEN] = {0, 0}; /* Hub Status and Change Status */ + + ret = FUsbGetStatus(dev, port, DR_PORT, sizeof(buf), buf); + if (ret < FUSB_CC_ZERO_BYTES) + return ret; + + /* + * Second word holds the change bits. The interrupt transfer shows + * a logical or of these bits, so we have to clear them all. + */ + for (i = 0; i < ARRAY_SIZE(change_bits); ++i) + { + if (buf[FUSB_HUB_PORT_CHANGE] & change_bits[i].change_bit) + { + /* clear feature if specific change bit = 1 */ + FUsbClearFeature(dev, port, change_bits[i].clear_sel, DR_PORT); + } + + checked_bits |= change_bits[i].change_bit; + } + + if (buf[FUSB_HUB_PORT_CHANGE] & ~checked_bits) + FUSB_DEBUG("Spurious change bit at port %d ", port); + + /* Now, handle connection changes. */ + if (buf[FUSB_HUB_PORT_CHANGE] & FUSB_HUB_STATUS_PORT_CONNECTION) + { + FUSB_DEBUG("Port change at %d ", port); + ret = FUsbGenericHubScanPort(dev, port); + } + + return ret; +} + +/** + * @name: FUsbHubPoll + * @msg: 轮询Hub的所有端口,检查端口状态变化 + * @return {*} + * @param {FUsbDev} *dev, Hub设备实例 + */ +static void FUsbHubPoll(FUsbDev *const dev) +{ + FASSERT(dev); + int port, i; + u8 buf[32] = {0}; + const u8 *ibuf; + + /* First, gather all change bits from finished interrupt transfers. */ + const size_t port_bytes = min(ARRAY_SIZE(buf), + (size_t)DIV_ROUND_UP(FUSB_GEN_HUB_GET(dev)->num_ports + 1, 8)); + + while (NULL != (ibuf = dev->controller->poll_intr_queue(FUSB_GEN_HUB_GET(dev)->data))) + { + for (i = 0; (size_t)i < port_bytes; ++i) + buf[i] |= ibuf[i]; + } + + for (port = 1; port <= FUSB_GEN_HUB_GET(dev)->num_ports; ++port) + { + /* ports start at bit1; bit0 is hub status change */ + if (buf[port / 8] & (1 << (port % 8))) + { + if (FUsbHubHandlePortChange(dev, port) < 0) + return; + } + } + + return; +} + +/** + * @name: FUsbHubDestory + * @msg: USB Hub的去初始化函数 + * @return {*} + * @param {FUsbDev} *dev, Hub设备实例 + */ +static void FUsbHubDestory(FUsbDev *const dev) +{ + FASSERT(dev); + FUsbEndpoint *const intr_ep = FUsbHubIntrEp(dev); + FASSERT(intr_ep); /* interrupt ep must exists */ + dev->controller->destroy_intr_queue(intr_ep, FUSB_GEN_HUB_GET(dev)->data); + FUsbGenericHubDestory(dev); +} + +/** + * @name: FUsbHubInit + * @msg: USB Hub的初始化函数,由应用程序注册到FUSB框架中 + * @return {*} + * @param {FUsbDev} *dev, Hub设备实例 + */ +void FUsbHubInit(FUsbDev *dev) +{ + FASSERT(dev); + FUsbEndpoint *const intr_ep = FUsbHubIntrEp(dev); /* get the first intrrupt ep found */ + if (NULL == intr_ep) + { + FUSB_ERROR("No interrupt-in endpoint found "); + return; + } + + FASSERT(dev->controller); + + /* Get number of ports from hub descriptor */ + int type = FUsbIsSuperSpeed(dev->speed) ? FUSB_DESC_TYPE_SUPER_SPEED_HUB : FUSB_DESC_TYPE_HUB; /* similar enough */ + FUsbHubDescriptor desc; /* won't fit the whole thing, we don't care */ + if (FUsbGetDescriptor(dev, FUsbGenerateReqType(FUSB_REQ_DEVICE_TO_HOST, FUSB_REQ_TYPE_CLASS, FUSB_REQ_RECP_DEV), type, 0, &desc, sizeof(desc)) != sizeof(desc)) + { + FUSB_ERROR("FUsbGetDescriptor(HUB) failed "); + FUsbDetachDev(dev->controller, dev->address); + return; + } + + if (FUsbIsSuperSpeed(dev->speed)) + { + FUsbHubSetHubDepth(dev); + } + + /* + * Register interrupt transfer: + * one bit per port + one bit for the hub, + * 20 transfers in the queue, like our HID driver, + * one transfer per 256ms + */ + void *const intrq = dev->controller->create_intr_queue( + intr_ep, intr_ep->maxpacketsize, 20, 256); + if (NULL == intrq) + { + FUsbDetachDev(dev->controller, dev->address); + return; + } + + /* + * Limit the number of ports by the max packet size of + * the interrupt endpoint. This shouldn't be necessary + * but prevents a potential overflow in FUsbHubPoll(). + */ + const unsigned int num_ports = + min((int)desc.bNbrPorts, intr_ep->maxpacketsize * 8 - 1); + if (FUsbGenericHubInit(dev, num_ports, &FUSB_HUB_OPS)) + { + dev->controller->destroy_intr_queue(intr_ep, intrq); + FUsbDetachDev(dev->controller, dev->address); + return; + } + + unsigned int port; + for (port = 1; port <= num_ports; ++port) + { + FUsbHubPortInit(dev, port); + } + + FUSB_GEN_HUB_GET(dev)->data = intrq; + dev->poll = FUsbHubPoll; + dev->destroy = FUsbHubDestory; + + return; +} diff --git a/bsp/phytium/libraries/standalone/drivers/usb/fusb_hub.h b/bsp/phytium/libraries/standalone/drivers/usb/fusb_hub.h new file mode 100644 index 0000000000..5e6235a11f --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/usb/fusb_hub.h @@ -0,0 +1,65 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fusb_hub.h + * Date: 2022-02-11 13:33:09 + * LastEditTime: 2022-02-17 17:49:20 + * Description:  This files is for definition of USB hub device function + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/2/7 init commit + */ + +#ifndef DRIVERS_USB_HUB_H +#define DRIVERS_USB_HUB_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ + +#include "fusb.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ +enum +{ + FUSB_HUB_SUBCLASS_DEFAULT = 0x0 +}; + +enum +{ + FUSB_HUB_PROTOCOL_FULL_SPEED = 0x0, + FUSB_HUB_PROTOCOL_HIGH_SPEED_WITH_SINGLE_TT = 0x1, + FUSB_HUB_PROTOCOL_HIGH_SPEED_WITH_MULTIPLE_TT = 0x2 +}; + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ +/* USB Hub的初始化函数,由应用程序注册到FUSB框架中 */ +void FUsbHubInit(FUsbDev *dev); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/bsp/phytium/libraries/standalone/drivers/usb/fusb_msc.c b/bsp/phytium/libraries/standalone/drivers/usb/fusb_msc.c new file mode 100644 index 0000000000..0e6a8a64ef --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/usb/fusb_msc.c @@ -0,0 +1,859 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fusb_msc.c + * Date: 2022-02-11 13:33:09 + * LastEditTime: 2022-02-17 17:49:43 + * Description:  This files is for implementation of USB mass storage function + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/2/7 init commit + */ + +#include +#include "fswap.h" +#include "fsleep.h" +#include "fassert.h" +#include "fgeneric_timer.h" +#include "fdebug.h" + +#include "fusb.h" +#include "fusb_msc.h" + + +#define FUSB_DEBUG_TAG "FUSB_MSC" +#define FUSB_ERROR(format, ...) FT_DEBUG_PRINT_E(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_WARN(format, ...) FT_DEBUG_PRINT_W(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_INFO(format, ...) FT_DEBUG_PRINT_I(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_DEBUG(format, ...) FT_DEBUG_PRINT_D(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) + +static inline tick_t FUsbMscGetTick(void) +{ + return GenericTimerRead(); +} + +static inline tick_t FUsbMscStartTick(void) +{ + GenericTimerStart(); + return FUsbMscGetTick(); +} + +static inline void FUsbMscStopTick(void) +{ + GenericTimerStop(); +} + +static inline boolean FUsbMscTimeout(tick_t start_tick, tick_t timeout_tick) +{ + return (FUsbMscGetTick() - start_tick) > timeout_tick; +} + +static void FUsbMassStorageForceInit(FUsbDev *dev, u32 quirks); +static int FUsbMscRwBlks(FUsbDev *dev, int start, int n, FUsbMassStorageDirection dir, u8 *buf); + +static const char *FUsbMscSubClassString[7] = +{ + "(none)", + "RBC", + "MMC-2", + "QIC-157", + "UFI", + "SFF-8070i", + "SCSI transparent" +}; + +static const char *FUsbMscProtocolStrings[0x51] = +{ + "Control/Bulk/Interrupt protocol (with command completion interrupt)", + "Control/Bulk/Interrupt protocol (with no command completion interrupt)", + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "Bulk-Only Transport" +}; + +/** + * @name: FUsbMscCreateDisk + * @msg: 调用应用程序实现的钩子函数,创建USB Disk + * @return {*} + * @param {FUsbDev} *dev, USB大容量存储设备实例 + */ +static void FUsbMscCreateDisk(FUsbDev *dev) +{ + FASSERT(dev); + + if (FUsbDiskCreate) + { + FUsbDiskCreate(dev); + MSC_INST(dev)->usbdisk_created = 1; + } + + return; +} + +/** + * @name: FUsbMscRemoveDisk + * @msg: 调用应用程序实现的钩子函数,移除USB Disk + * @return {*} + * @param {FUsbDev} *dev, USB大容量存储设备实例 + */ +static void FUsbMscRemoveDisk(FUsbDev *dev) +{ + FASSERT(dev); + + if (MSC_INST(dev)->usbdisk_created && FUsbDiskRemove) + FUsbDiskRemove(dev); + + return; +} + +/** + * @name: FUsbMscDestory + * @msg: 移除USB大容量存储设备实例 + * @return {*} + * @param {FUsbDev} *dev, USB大容量存储设备实例 + */ +static void FUsbMscDestory(FUsbDev *dev) +{ + FASSERT(dev && dev->controller); + FUsb *instance = dev->controller->usb; + FASSERT(instance); + + if (NULL != dev->data) + { + FUsbMscRemoveDisk(dev); + FUSB_FREE(instance, dev->data); + } + + dev->data = NULL; + return; +} + +const int DEV_RESET = 0xff; +const int GET_MAX_LUN = 0xfe; +/* Many USB3 devices do not work with large transfer requests. + * Limit the request size to 64KB chunks to ensure maximum compatibility. */ +const int MAX_CHUNK_BYTES = 1024 * 64; + +const unsigned int cbw_signature = 0x43425355; /* according to USB mass-bulk spec. helps to identify data packets */ +const unsigned int csw_signature = 0x53425355; + +/* following data structure following name convention in USB mass-bulk spec., which may not compiliance to other code */ + +/* A packet containing a command block and associated information. */ +typedef struct +{ + unsigned int dCBWSignature; + unsigned int dCBWTag; + unsigned int dCBWDataTransferLength; + unsigned char bmCBWFlags; + unsigned long bCBWLUN : 4; /* device Logical Unit Number (LUN) */ + unsigned long : 4; + unsigned long bCBWCBLength : 5; + unsigned long : 3; + unsigned char CBWCB[31 - 15]; +} __attribute__((packed)) FUsbMscCbw; /* Command Block Wrapper (CBW) */ + +/* A packet containing the status of a command block */ +typedef struct +{ + unsigned int dCSWSignature; + unsigned int dCSWTag; + unsigned int dCSWDataResidue; + unsigned char bCSWStatus; +} __attribute__((packed)) FUsbMscCsw; + +enum +{ + /* + * MSC commands can be + * successful, + * fail with proper response or + * fail totally, which results in detaching of the USB device + * and immediate cleanup of the FUsbDev structure. + * In the latter case the caller has to make sure, that he won't + * use the device any more. + */ + MSC_COMMAND_OK = 0, + MSC_COMMAND_FAIL, + MSC_COMMAND_DETACHED +}; + +static int FUsbMscRequestSense(FUsbDev *dev); +static int FUsbMscRequestNoMedia(FUsbDev *dev); +static void FUsbMscPoll(FUsbDev *dev); + +static int FUsbClearStall(FUsbEndpoint *ep) +{ + FASSERT(ep); + int ret = FUsbClearFeature(ep->dev, ep->endpoint, FUSB_ENDPOINT_HALT, + FUsbGenerateReqType(FUSB_REQ_HOST_TO_DEVICE, FUSB_REQ_TYPE_STANDARD, FUSB_REQ_RECP_EP)); + + ep->toggle = 0; + + return ret; +} + +static int FUsbMscResetTransport(FUsbDev *dev) +{ + FUsbDevReq dr; + memset(&dr, 0, sizeof(dr)); + dr.bmRequestType = 0; + dr.data_dir = FUSB_REQ_HOST_TO_DEVICE; + dr.req_type = FUSB_REQ_TYPE_CLASS; + dr.req_recp = FUSB_REQ_RECP_IF; + dr.bRequest = DEV_RESET; + dr.wValue = 0; + dr.wIndex = 0; + dr.wLength = 0; + + if (MSC_INST(dev)->quirks & FUSB_MSC_QUIRK_NO_RESET) + return MSC_COMMAND_FAIL; + + /* if any of these fails, detach device, as we are lost */ + if (dev->controller->control(dev, FUSB_OUT, sizeof(dr), &dr, 0, 0) < 0 || + FUsbClearStall(MSC_INST(dev)->bulk_in) || + FUsbClearStall(MSC_INST(dev)->bulk_out)) + { + FUSB_INFO("Detaching unresponsive device. "); + FUsbDetachDev(dev->controller, dev->address); + return MSC_COMMAND_DETACHED; + } + /* return fail as we are only called in case of failure */ + return MSC_COMMAND_FAIL; +} + +/* device may stall this command, so beware! */ +static void FUsbMscInitLuns(FUsbDev *dev) +{ + FUsbMassStorage *msc = MSC_INST(dev); + FUsbDevReq dr; + dr.bmRequestType = 0; + dr.data_dir = FUSB_REQ_DEVICE_TO_HOST; + dr.req_type = FUSB_REQ_TYPE_CLASS; + dr.req_recp = FUSB_REQ_RECP_IF; + dr.bRequest = GET_MAX_LUN; + dr.wValue = 0; + dr.wIndex = 0; + dr.wLength = 1; + + /* send class-spefic request Get Max Lun */ + if ((MSC_INST(dev)->quirks & FUSB_MSC_QUIRK_NO_LUNS) || + (dev->controller->control(dev, FUSB_IN, sizeof(dr), &dr, + sizeof(msc->num_luns), &msc->num_luns) < FUSB_CC_ZERO_BYTES)) + { + msc->num_luns = 0; /* assume only 1 lun if req fails */ + } + + msc->num_luns++; /* Get Max LUN returns number of last LUN */ + msc->lun = 0; + + return; +} + +unsigned int tag; +static void FUsbMscWrapCbw(FUsbMscCbw *cbw, int datalen, FUsbMassStorageDirection dir, const u8 *cmd, + int cmdlen, u8 lun) +{ + memset(cbw, 0, sizeof(FUsbMscCbw)); + + /* commands are typically shorter, but we don't want overflows */ + if ((size_t)cmdlen > sizeof(cbw->CBWCB)) + { + cmdlen = (int)sizeof(cbw->CBWCB); + } + + cbw->dCBWSignature = cbw_signature; + cbw->dCBWTag = ++tag; /* command block tag to device */ + cbw->bCBWLUN = lun; /* logic unit number to send */ + + cbw->dCBWDataTransferLength = datalen; /* number of bytes of data expect to transfer */ + cbw->bmCBWFlags = dir; + + memcpy(cbw->CBWCB, cmd, cmdlen); /* the command block to be executed by the device */ + + cbw->bCBWCBLength = cmdlen; /* the valid length of the CBWCB in bytes */ + return; +} + +static int FUsbMscGetCsw(FUsbEndpoint *ep, FUsbMscCsw *csw) +{ + FUsbHc *ctrlr = ep->dev->controller; + int ret = ctrlr->bulk(ep, sizeof(FUsbMscCsw), (u8 *)csw, 1); + + /* Some broken sticks send a zero-length packet at the end of their data + transfer which would show up here. Skip it to get the actual CSW. */ + if (ret == 0) + ret = ctrlr->bulk(ep, sizeof(FUsbMscCsw), (u8 *)csw, 1); + + if (ret < 0) + { + FUsbClearStall(ep); + ret = ctrlr->bulk(ep, sizeof(FUsbMscCsw), (u8 *)csw, 1); + if (ret < 0) + return FUsbMscResetTransport(ep->dev); + } + + if (ret != sizeof(FUsbMscCsw) || csw->dCSWTag != tag || + csw->dCSWSignature != csw_signature) + { + FUSB_INFO("MSC: received malformed CSW "); + return FUsbMscResetTransport(ep->dev); + } + + return MSC_COMMAND_OK; +} + +static int FUsbMscExecCmd(FUsbDev *dev, FUsbMassStorageDirection dir, const u8 *cb, int cblen, + u8 *buf, int buflen, int residue_ok) +{ + FUsbMscCbw cbw; + FUsbMscCsw csw; + + int always_succeed = 0; + if ((cb[0] == 0x1b) && (cb[4] == 1)) /* check if it is Bulk-Only Mass Storage Reset request with reques type 00100001b */ + { + /* start command, always succeed */ + always_succeed = 1; + } + + FUsbMscWrapCbw(&cbw, buflen, dir, cb, cblen, MSC_INST(dev)->lun); + + if (dev->controller->bulk(MSC_INST(dev)->bulk_out, sizeof(cbw), (u8 *)&cbw, 0) < 0) + { + return FUsbMscResetTransport(dev); + } + + if (buflen > 0) + { + if (dir == FUSB_DIR_DATA_IN) + { + if (dev->controller->bulk(MSC_INST(dev)->bulk_in, buflen, buf, 0) < 0) + FUsbClearStall(MSC_INST(dev)->bulk_in); + } + else + { + if (dev->controller->bulk(MSC_INST(dev)->bulk_out, buflen, buf, 0) < 0) + FUsbClearStall(MSC_INST(dev)->bulk_out); + } + } + + int ret = FUsbMscGetCsw(MSC_INST(dev)->bulk_in, &csw); + + if (ret) + { + return ret; + } + else if (always_succeed == 1) + { + /* return success, regardless of message */ + return MSC_COMMAND_OK; + } + else if (csw.bCSWStatus == 2) + { + /* phase error, reset transport */ + return FUsbMscResetTransport(dev); + } + else if (csw.bCSWStatus == 0) + { + if ((csw.dCSWDataResidue == 0) || residue_ok) + /* no error, exit */ + return MSC_COMMAND_OK; + else + /* missed some bytes */ + return MSC_COMMAND_FAIL; + } + else + { + if (cb[0] == 0x03) + /* requesting sense failed, that's bad */ + return MSC_COMMAND_FAIL; + else if (cb[0] == 0) + /* If command was TEST UNIT READY determine if the + * device is of removable type indicating no media + * found. */ + return FUsbMscRequestNoMedia(dev); + /* error "check condition" or reserved error */ + ret = FUsbMscRequestSense(dev); + /* return fail or the status of FUsbMscRequestSense if it's worse */ + return ret ? ret : MSC_COMMAND_FAIL; + } +} + +typedef struct +{ + unsigned char command; /* 0 */ + unsigned char res1; /* 1 */ + unsigned int block; /* 2-5 */ + unsigned char res2; /* 6 */ + unsigned short numblocks; /* 7-8 */ + unsigned char control; /* 9 - the block is 10 bytes long */ +} __attribute__((packed)) FUsbMscCmdBlk; + +typedef struct +{ + unsigned char command; /* 0 */ + unsigned char res1; /* 1 */ + unsigned char res2; /* 2 */ + unsigned char res3; /* 3 */ + union + { + /* 4 */ + struct + { + unsigned long start : 1; /* for START STOP UNIT */ + unsigned long : 7; + }; + unsigned char length; /* for REQUEST SENSE */ + }; + unsigned char control; /* 5 */ +} __attribute__((packed)) FUsbMscCmdBlk6; + +/** + * Like FUsbMscRwBlks, but for soft-sectors of 512b size. Converts the + * start and count from 512b units. + * Start and count must be aligned so that they match the native + * sector size. + * + */ +/** + * @name: FUsbMscRwBlk512 + * @msg: 读写USB大容量存储设备,以512字节为一块 + * @param dev device to access + * @param start first sector to access + * @param n number of sectors to access + * @param dir direction of access: FUSB_DIR_DATA_IN == read, FUSB_DIR_DATA_OUT == write + * @param buf buffer to read into or write from. Must be at least n*512 bytes + * @return 0 on success, 1 on failure + */ +int FUsbMscRwBlk512(FUsbDev *dev, int start, int n, + FUsbMassStorageDirection dir, u8 *buf) +{ + int blocksize_divider = MSC_INST(dev)->blocksize / 512; + return FUsbMscRwBlks(dev, start / blocksize_divider, + n / blocksize_divider, dir, buf); +} + +/** + * Reads or writes a number of sequential blocks on a USB storage device. + * As it uses the READ(10) SCSI-2 command, it's limited to storage devices + * of at most 2TB. It assumes sectors of 512 bytes. + * + * @param dev device to access + * @param start first sector to access + * @param n number of sectors to access + * @param dir direction of access: FUSB_DIR_DATA_IN == read, FUSB_DIR_DATA_OUT == write + * @param buf buffer to read into or write from. Must be at least n*sectorsize bytes + * @return 0 on success, 1 on failure + */ +static int FUsbMscRwChunk(FUsbDev *dev, int start, int n, FUsbMassStorageDirection dir, u8 *buf) +{ + FUsbMscCmdBlk cb; + memset(&cb, 0, sizeof(cb)); + if (dir == FUSB_DIR_DATA_IN) + { + /* read */ + cb.command = 0x28; + } + else + { + /* write */ + cb.command = 0x2a; + } + cb.block = htonl(start); + cb.numblocks = htons(n); + + return FUsbMscExecCmd(dev, dir, (u8 *)&cb, sizeof(cb), buf, + n * MSC_INST(dev)->blocksize, 0) != MSC_COMMAND_OK + ? 1 + : 0; +} + +/** + * Reads or writes a number of sequential blocks on a USB storage device + * that is split into MAX_CHUNK_BYTES size requests. + * + * As it uses the READ(10) SCSI-2 command, it's limited to storage devices + * of at most 2TB. It assumes sectors of 512 bytes. + * + * @param dev device to access + * @param start first sector to access + * @param n number of sectors to access + * @param dir direction of access: FUSB_DIR_DATA_IN == read, + * FUSB_DIR_DATA_OUT == write + * @param buf buffer to read into or write from. + * Must be at least n*sectorsize bytes + * @return 0 on success, 1 on failure + */ +static int FUsbMscRwBlks(FUsbDev *dev, int start, int n, FUsbMassStorageDirection dir, u8 *buf) +{ + int chunk_size = MAX_CHUNK_BYTES / MSC_INST(dev)->blocksize; + int chunk; + + /* Read as many full chunks as needed. */ + for (chunk = 0; chunk < (n / chunk_size); chunk++) + { + if (FUsbMscRwChunk(dev, start + (chunk * chunk_size), + chunk_size, dir, + buf + (chunk * MAX_CHUNK_BYTES)) != MSC_COMMAND_OK) + return 1; + } + + /* Read any remaining partial chunk at the end. */ + if (n % chunk_size) + { + if (FUsbMscRwChunk(dev, start + (chunk * chunk_size), + n % chunk_size, dir, + buf + (chunk * MAX_CHUNK_BYTES)) != MSC_COMMAND_OK) + return 1; + } + + return 0; +} + +/* Only request it, we don't interpret it. + On certain errors, that's necessary to get devices out of + a special state called "Contingent Allegiance Condition" */ +static int FUsbMscRequestSense(FUsbDev *dev) +{ + u8 buf[19]; + FUsbMscCmdBlk6 cb; + memset(&cb, 0, sizeof(cb)); + cb.command = 0x3; + cb.length = sizeof(buf); + + return FUsbMscExecCmd(dev, FUSB_DIR_DATA_IN, (u8 *)&cb, + sizeof(cb), buf, sizeof(buf), 1); +} + +static int FUsbMscRequestNoMedia(FUsbDev *dev) +{ + u8 buf[19]; + int ret; + FUsbMscCmdBlk6 cb; + memset(&cb, 0, sizeof(cb)); + cb.command = 0x3; + cb.length = sizeof(buf); + + ret = FUsbMscExecCmd(dev, FUSB_DIR_DATA_IN, (u8 *)&cb, + sizeof(cb), buf, sizeof(buf), 1); + + if (ret) + return ret; + + /* Check if sense key is set to NOT READY. */ + if ((buf[2] & 0xf) != 2) + return MSC_COMMAND_FAIL; + + /* Check if additional sense code is 0x3a. */ + if (buf[12] != 0x3a) + return MSC_COMMAND_FAIL; + + /* No media is present. Return MSC_COMMAND_OK while marking the disk + * not ready. */ + FUSB_INFO("Empty media found. "); + MSC_INST(dev)->ready = FUSB_MSC_NOT_READY; + return MSC_COMMAND_OK; +} + +static int FUsbMscCheckIfReady(FUsbDev *dev) +{ + FUsbMscCmdBlk6 cb; + memset(&cb, 0, sizeof(cb)); /* full initialization for T-U-R */ + + /* Bulk-Only Mass Storage Reset, class-specific request */ + return FUsbMscExecCmd(dev, FUSB_DIR_DATA_OUT, (u8 *)&cb, + sizeof(cb), 0, 0, 0); +} + +static int FUsbMscSpinUp(FUsbDev *dev) +{ + FUsbMscCmdBlk6 cb; + memset(&cb, 0, sizeof(cb)); + cb.command = 0x1b; + cb.start = 1; + return FUsbMscExecCmd(dev, FUSB_DIR_DATA_OUT, (u8 *)&cb, + sizeof(cb), 0, 0, 0); +} + +static int FUsbMscReadCapcity(FUsbDev *dev) +{ + FUsbMscCmdBlk cb; + memset(&cb, 0, sizeof(cb)); + cb.command = 0x25; /* read capacity */ + u32 buf[2]; + + FUSB_INFO("Reading capacity of mass storage device. "); + int count = 0, ret; + while (count++ < 20) + { + switch (ret = FUsbMscExecCmd(dev, FUSB_DIR_DATA_IN, (u8 *)&cb, + sizeof(cb), (u8 *)buf, 8, 0)) + { + case MSC_COMMAND_OK: + break; + case MSC_COMMAND_FAIL: + continue; + default: /* if it's worse return */ + return ret; + } + break; + } + if (count >= 20) + { + /* still not successful, assume 2tb in 512byte sectors, which is just the same garbage as any other number, but probably more usable. */ + FUSB_WARN(" assuming 2 TB with 512-byte sectors as READ CAPACITY didn't answer. "); + MSC_INST(dev)->numblocks = 0xffffffff; + MSC_INST(dev)->blocksize = 512; + } + else + { + MSC_INST(dev)->numblocks = ntohl(buf[0]) + 1; + MSC_INST(dev)->blocksize = ntohl(buf[1]); + } + + FUSB_INFO(" %d %d-byte sectors (%d MB) ", MSC_INST(dev)->numblocks, + MSC_INST(dev)->blocksize, + /* round down high block counts to avoid integer overflow */ + MSC_INST(dev)->numblocks > 1000000 + ? (MSC_INST(dev)->numblocks / 1000) * MSC_INST(dev)->blocksize / 1000 + : MSC_INST(dev)->numblocks * MSC_INST(dev)->blocksize / 1000 / 1000); + + return MSC_COMMAND_OK; +} + +static int FUsbMscWaitReady(FUsbDev *dev) +{ + int i; + /* SCSI/ATA specs say we have to wait up to 30s, but most devices + * are ready much sooner. Use a 5 sec timeout to better accommodate + * devices which fail to respond. */ + const tick_t timeout_tick = 1000000; + FError ret = FUSB_ERR_WAIT_TIMEOUT; + + FUSB_INFO(" Waiting for device to become ready..."); + + /* Initially mark the device ready. */ + MSC_INST(dev)->ready = FUSB_MSC_READY; + tick_t start_tick = FUsbMscStartTick(); + + do + { + switch (FUsbMscCheckIfReady(dev)) + { + case MSC_COMMAND_OK: + break; + case MSC_COMMAND_FAIL: + fsleep_millisec(100); + FUSB_INFO("."); + continue; + default: + /* Device detached, return immediately */ + return FUSB_MSC_DETACHED; + } + break; + } + while (!FUsbMscTimeout(start_tick, timeout_tick)); + + if (FUsbMscTimeout(start_tick, timeout_tick)) + { + FUSB_INFO("timeout. Device not ready. "); + MSC_INST(dev)->ready = FUSB_MSC_NOT_READY; + } + + /* Don't bother spinning up the storage device if the device is not + * ready. This can happen when empty card readers are present. + * Polling will pick it back up if readiness changes. */ + if (!MSC_INST(dev)->ready) + return MSC_INST(dev)->ready; + + for (i = 0; i < 30; i++) + { + FUSB_INFO("."); + switch (FUsbMscSpinUp(dev)) + { + case MSC_COMMAND_OK: + FUSB_INFO(" OK."); + break; + case MSC_COMMAND_FAIL: + fsleep_millisec(100); + continue; + default: + /* Device detached, return immediately */ + return FUSB_MSC_DETACHED; + } + break; + } + + if (FUsbMscReadCapcity(dev) == MSC_COMMAND_DETACHED) + return FUSB_MSC_DETACHED; + + return MSC_INST(dev)->ready; +} + +/** + * @name: FUsbMassStorageInit + * @msg: USB大容量存储设备的初始化函数,由应用程序注册到FUSB框架中 + * @return {*} + * @param {FUsbDev} *dev, USB大容量存储设备实例 + */ +void FUsbMassStorageInit(FUsbDev *dev) +{ + FASSERT(dev && dev->configuration); + FUsbConfigurationDescriptor *cd = + (FUsbConfigurationDescriptor *)dev->configuration; + FASSERT(FUSB_DESC_TYPE_CONFIG == cd->bDescriptorType); + FUsbInterfaceDescriptor *interface = + (FUsbInterfaceDescriptor *)(((char *)cd) + cd->bLength); + FASSERT(FUSB_DESC_TYPE_INTERFACE == interface->bDescriptorType); + + if (FUSB_MASS_STORAGE_DEVICE != interface->bInterfaceClass) + { + FUSB_ERROR("Class %d not supported. ", interface->bInterfaceClass); + return; + } + + FUSB_INFO(" command set: %s protocol: %s ", + FUsbMscSubClassString[interface->bInterfaceSubClass], + FUsbMscProtocolStrings[interface->bInterfaceProtocol]); + + if (interface->bInterfaceProtocol != FUSB_MSC_PROTOCOL_BULK_ONLY) + { + FUSB_ERROR(" Protocol not supported. "); + FUsbDetachDev(dev->controller, dev->address); + return; + } + + if ((interface->bInterfaceSubClass != FUSB_MSC_SUBCLASS_ATAPI_8020) && /* ATAPI 8020 */ + (interface->bInterfaceSubClass != FUSB_MSC_SUBCLASS_ATAPI_8070) && /* ATAPI 8070 */ + (interface->bInterfaceSubClass != FUSB_MSC_SUBCLASS_SCSI)) + { + /* SCSI */ + /* Other protocols, such as ATAPI don't seem to be very popular. looks like ATAPI would be really easy to add, if necessary. */ + FUSB_ERROR(" Interface SubClass not supported. "); + FUsbDetachDev(dev->controller, dev->address); + return; + } + + FUsbMassStorageForceInit(dev, 0); + return; +} + +/* Force a device to enumerate as MSC, without checking class/protocol types. + It must still have a bulk endpoint pair and respond to MSC commands. */ +static void FUsbMassStorageForceInit(FUsbDev *dev, u32 quirks) +{ + FASSERT(dev && dev->controller); + int i; + FUsb *instance = dev->controller->usb; + FASSERT(instance); + + /* init .data before setting .destroy */ + dev->data = NULL; + dev->destroy = FUsbMscDestory; + dev->poll = FUsbMscPoll; + + FASSERT(NULL == dev->data); + dev->data = FUSB_ALLOCATE(instance, sizeof(FUsbMassStorage), FUSB_DEFAULT_ALIGN); + if (NULL == dev->data) + { + FUSB_ERROR("Not enough memory for USB MSC device. "); + FASSERT(0); + } + + MSC_INST(dev)->bulk_in = NULL; + MSC_INST(dev)->bulk_out = NULL; + MSC_INST(dev)->usbdisk_created = 0; + MSC_INST(dev)->quirks = quirks; + + /* loop over all ep except ep0 to get bulk-in/bulk-out ep instance */ + for (i = 1; i <= dev->num_endp; i++) + { + if (dev->endpoints[i].endpoint == 0) + continue; + if (dev->endpoints[i].type != FUSB_BULK_EP) + continue; + if ((dev->endpoints[i].direction == FUSB_IN) && (MSC_INST(dev)->bulk_in == 0)) + MSC_INST(dev)->bulk_in = &dev->endpoints[i]; + if ((dev->endpoints[i].direction == FUSB_OUT) && (MSC_INST(dev)->bulk_out == 0)) + MSC_INST(dev)->bulk_out = &dev->endpoints[i]; + } + + /* check if non bulk-in ep */ + if (MSC_INST(dev)->bulk_in == NULL) + { + FUSB_ERROR("couldn't find bulk-in endpoint. "); + FUsbDetachDev(dev->controller, dev->address); + return; + } + + /* check if non bulk-out ep */ + if (MSC_INST(dev)->bulk_out == NULL) + { + FUSB_ERROR("couldn't find bulk-out endpoint. "); + FUsbDetachDev(dev->controller, dev->address); + return; + } + + FUSB_INFO("using endpoint %x as in, %x as out ", + MSC_INST(dev)->bulk_in->endpoint, + MSC_INST(dev)->bulk_out->endpoint); + + /* Some sticks need a little more time to get ready after SET_CONFIG. */ + fsleep_microsec(50); + + FUsbMscInitLuns(dev); + FUSB_INFO(" has %d luns ", MSC_INST(dev)->num_luns); + + /* Test if msc is ready (nothing to do if it isn't). */ + if (FUsbMscWaitReady(dev) != FUSB_MSC_READY) + return; + + /* Create the disk. */ + FUsbMscCreateDisk(dev); + + return; +} + +static void FUsbMscPoll(FUsbDev *dev) +{ + FUsbMassStorage *msc = MSC_INST(dev); + int prev_ready = msc->ready; + + if (FUsbMscWaitReady(dev) == FUSB_MSC_DETACHED) + return; + + if (!prev_ready && msc->ready) + { + FUSB_INFO("USB msc: not ready -> ready (lun %d) ", msc->lun); + FUsbMscCreateDisk(dev); + } + else if (prev_ready && !msc->ready) + { + FUSB_INFO("USB msc: ready -> not ready (lun %d) ", msc->lun); + FUsbMscRemoveDisk(dev); + } + else if (!prev_ready && !msc->ready) + { + u8 new_lun = (msc->lun + 1) % msc->num_luns; + FUSB_INFO("USB msc: not ready (lun %d) -> lun %d ", msc->lun, + new_lun); + msc->lun = new_lun; + } + + return; +} diff --git a/bsp/phytium/libraries/standalone/drivers/usb/fusb_msc.h b/bsp/phytium/libraries/standalone/drivers/usb/fusb_msc.h new file mode 100644 index 0000000000..bc0a1989b9 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/usb/fusb_msc.h @@ -0,0 +1,163 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fusb_msc.h + * Date: 2022-02-11 13:33:09 + * LastEditTime: 2022-02-17 17:50:46 + * Description:  This files is for definition of USB mass storage function + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/2/7 init commit + */ + +#ifndef DRIVERS_USB_MSC_H +#define DRIVERS_USB_MSC_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ + +#include "fusb.h" + +/************************** Constant Definitions *****************************/ +/* Possible values for quirks field. */ +enum +{ + /* Don't check for LUNs (force assumption that there's only one LUN). */ + FUSB_MSC_QUIRK_NO_LUNS = 1 << 0, + /* Never do a BULK_ONLY reset, just continue. This means that the device + cannot recover from phase errors and won't detach automatically for + unrecoverable errors. Do not use unless you have to. */ + FUSB_MSC_QUIRK_NO_RESET = 1 << 1, +}; + +/* Possible values for ready field. */ +enum +{ + FUSB_MSC_DETACHED = -1, /* Disk detached or out to lunch. */ + FUSB_MSC_NOT_READY = 0, /* Disk not ready yet -- empty card reader */ + FUSB_MSC_READY = 1, /* Disk ready to communicate. */ +}; + +enum +{ + FUSB_MSC_SUBCLASS_ATAPI_8020 = 0x2, + FUSB_MSC_SUBCLASS_ATAPI_8070 = 0x5, + FUSB_MSC_SUBCLASS_SCSI = 0x6 +}; + +/* Protocols of MSC */ +enum +{ + FUSB_MSC_PROTOCOL_BULK_ONLY = 0x50 /* Usb bulk-only transfer protocol */ +}; + +typedef enum +{ + FUSB_DIR_DATA_IN = 0x80, /* data from the device to the host */ + FUSB_DIR_DATA_OUT = 0 /* data from the host to the device */ +} FUsbMassStorageDirection; + +/**************************** Type Definitions *******************************/ +typedef struct +{ + unsigned int blocksize; + unsigned int numblocks; + FUsbEndpoint *bulk_in; + FUsbEndpoint *bulk_out; + u8 quirks : 7; + u8 usbdisk_created : 1; + s8 ready; + u8 lun; + u8 num_luns; + void *data; /* For use by consumers of libpayload. */ +} FUsbMassStorage; + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define MSC_INST(dev) ((FUsbMassStorage*)(dev)->data) + +/** + * @name: FUsbMscGetCapcityMB + * @msg: 获取USB大容量存储设备的容量,单位MB + * @return {fsize_t} 容量,MB + * @param {FUsbDev} *dev, USB大容量存储设备实例 + */ +static inline u32 FUsbMscGetCapcityMB(FUsbDev *dev) +{ + FASSERT(dev); + return (u32)(MSC_INST(dev)->numblocks > 1000000 + ? (MSC_INST(dev)->numblocks / 1024) * MSC_INST(dev)->blocksize / 1024 + : MSC_INST(dev)->numblocks * MSC_INST(dev)->blocksize / 1024 / 1024); +} + +/** + * @name: FUsbMscGetBlkSize + * @msg: 获取USB大容量存储设备的块大小 + * @return {*} 块大小,字节数 + * @param {FUsbDev} *dev, USB大容量存储设备实例 + */ +static inline u32 FUsbMscGetBlkSize(FUsbDev *dev) +{ + FASSERT(dev); + return (u32)MSC_INST(dev)->blocksize; +} + +/** + * @name: FUsbMscGetBlkNum + * @msg: 获取USB大容量存储设备块数目 + * @return {*} 块数目 + * @param {FUsbDev} *dev, USB大容量存储设备实例 + */ +static inline u32 FUsbMscGetBlkNum(FUsbDev *dev) +{ + FASSERT(dev); + return (u32)MSC_INST(dev)->numblocks; +} + +/************************** Function Prototypes ******************************/ +/* 读写USB大容量存储设备,以512字节为一块 */ +int FUsbMscRwBlk512(FUsbDev *dev, int start, int n, FUsbMassStorageDirection dir, u8 *buf); + +/* USB大容量存储设备的初始化函数,由应用程序注册到FUSB框架中 */ +void FUsbMassStorageInit(FUsbDev *dev); + +/** + * To be implemented by application. It's called by the USB stack + * when a new USB storage device is found, so the client has the chance + * to know about it. + * + * @param dev descriptor for the USB storage device + */ +void __attribute__((weak)) FUsbDiskCreate(FUsbDev *dev); + +/** + * To be implemented by application. It's called by the USB stack + * when it finds out that a USB storage device is removed. + * + * @param dev descriptor for the USB storage device + */ +void __attribute__((weak)) FUsbDiskRemove(FUsbDev *dev); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/bsp/phytium/libraries/standalone/drivers/usb/fusb_private.h b/bsp/phytium/libraries/standalone/drivers/usb/fusb_private.h new file mode 100644 index 0000000000..b1db521507 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/usb/fusb_private.h @@ -0,0 +1,104 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fusb_private.h + * Date: 2022-02-11 13:33:11 + * LastEditTime: 2022-02-18 09:21:22 + * Description:  This files is for definition of internal function interface + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/2/7 init commit + */ + +#pragma once + +/***************************** Include Files *********************************/ +#ifdef __aarch64__ + #include "faarch64.h" +#else + #include "fcp15.h" +#endif + +#include "fkernel.h" +#include "fio.h" +#include "fassert.h" +#include "fusb.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FUSB_REG32_GET_BITS(x, a, b) (u32)((((u32)(x)) & GENMASK(a, b)) >> b) +#define FUSB_REG32_SET_BITS(x, a, b) (u32)((((u32)(x)) << b) & GENMASK(a, b)) +#define FUSB_REG64_GET_BITS(x, a, b) (u64)((((u64)(x)) & GENMASK_ULL(a, b)) >> b) +#define FUSB_REG64_SET_BITS(x, a, b) (u64)((((u64)(x)) << b) & GENMASK_ULL(a, b)) + +/************************** Function Prototypes ******************************/ +/* 创建USB控制器实例,添加到USB实例的Hc链表中 */ +FUsbHc *FUsbAllocateHc(FUsb *instance); + +/* 删除USB控制器实例,从USB实例的Hc链表中删去 */ +void FUsbDetachHc(FUsbHc *controller); + +/* 初始化USB设备 */ +FUsbDev *FUsbInitDevEntry(FUsbHc *controller, int slot_id); + +/* 根据USB设备速度,选择最大包长度 */ +int FUsbDecodeMaxPacketSz0(FUsbSpeed speed, u8 bMaxPacketSize0); + +/* 据设备速度获取最大包长度 */ +int FUsbSpeedtoDefaultMaxPacketSz(FUsbSpeed speed); + +/* 配置USB配置描述符解析器 */ +FError FUsbSetupConfigParser(FUsbDev *dev, const void *buf, u32 buf_len); + +/* 去初始化USB配置描述符解析器 */ +void FUsbRevokeConfigParser(FUsbDev *dev); + +/* 初始化字符串描述符解析器 */ +void FUsbSetupStringParser(FUsbDev *dev); + +/* 去初始化字符串描述符解析器 */ +void FUsbRevokeStringParser(FUsbDev *dev); + +/* 检索字符串描述符,保存在FUsbStringParser结构中 */ +FError FUsbSearchStringDescriptor(FUsb *instance, FUsbDev *dev, u8 id); + +/* 获取刚刚检索到的字符串描述符内容 */ +const char *FUsbGetString(const FUsbDev *const dev); + +/* 从配置描述符解析器中获取指定类型的描述符(端点描述符/接口描述符) */ +const FUsbDescriptor *FUsbGetDescriptorFromParser(FUsbConfigParser *parser, FUsbDescriptorType type); + +/* 默认的USB设备初始化函数 */ +void FUsbNopDevInit(FUsbDev *dev); + +/* 默认的USB设备初始化函数 */ +void FUsbGenericDevInit(FUsbDev *dev); + +/* 打印设备描述符信息 */ +void FUsbDumpDeviceDescriptor(const FUsbDeviceDescriptor *descriptor); + +/* 打印配置描述符信息 */ +void FUsbDumpConfigDescriptor(const FUsbConfigurationDescriptor *descriptor); + +/* 打印接口描述符信息 */ +void FUsbDumpInterfaceDescriptor(const FUsbInterfaceDescriptor *descriptor); + +/* 打印端点描述符信息 */ +void FUsbDumpEndpointDescriptor(const FUsbEndpointDescriptor *descriptor); \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/usb/fusb_sinit.c b/bsp/phytium/libraries/standalone/drivers/usb/fusb_sinit.c new file mode 100644 index 0000000000..cf5af243e1 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/usb/fusb_sinit.c @@ -0,0 +1,60 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fusb_sinit.c + * Date: 2022-02-11 13:33:11 + * LastEditTime: 2022-02-18 09:21:45 + * Description:  This files is for static initialization of USB + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/2/8 init version + */ + +/***************************** Include Files *********************************/ +#include "fparameters.h" +#include "fusb_private.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Variable Definitions *****************************/ +extern const FUsbConfig FUSBHC_CONFIG_TBL[FUSB3_NUM]; + +/************************** Function Prototypes ******************************/ +/** + * @name: FUsbLookupConfig + * @msg: 获取USB的默认配置 + * @return {const FUsbConfig *} USB默认配置 + * @param {u32} instance_id USB实例号 + */ +const FUsbConfig *FUsbLookupConfig(u32 instance_id) +{ + const FUsbConfig *ptr = NULL; + u32 index; + + for (index = 0; index < (u32)FUSB3_NUM; index++) + { + if (FUSBHC_CONFIG_TBL[index].instance_id == instance_id) + { + ptr = &FUSBHC_CONFIG_TBL[index]; + break; + } + } + + return (const FUsbConfig *)ptr; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci.c b/bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci.c new file mode 100644 index 0000000000..17b5538d47 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci.c @@ -0,0 +1,1240 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fxhci.c + * Date: 2022-02-11 13:33:12 + * LastEditTime: 2022-02-18 09:17:20 + * Description:  This files is for implmentation of XHCI driver + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/2/7 init commit + */ + +#include +#include "fsleep.h" +#include "fcache.h" + +#include "fparameters.h" +#include "fdebug.h" + +#include "fxhci_private.h" + +#define FUSB_DEBUG_TAG "FXHCI" +#define FUSB_ERROR(format, ...) FT_DEBUG_PRINT_E(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_WARN(format, ...) FT_DEBUG_PRINT_W(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_INFO(format, ...) FT_DEBUG_PRINT_I(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_DEBUG(format, ...) FT_DEBUG_PRINT_D(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) + +typedef enum +{ + FXHCI_OP_REG, +} FXhciHandShakeType; + +static void FXhciStart(FUsbHc *controller); +static void FXhciStop(FUsbHc *controller); +static FXhciTransCode FXhciReset(FUsbHc *controller); +static void FXhciReinit(FUsbHc *controller); +static void FXhciShutdown(FUsbHc *controller); +static FXhciTransCode FXhciBulk(FUsbEndpoint *ep, int size, u8 *data, int finalize); +static FXhciTransCode FXhciControl(FUsbDev *dev, FUsbDirection dir, int drlen, void *devreq, + int dalen, u8 *data); +static void *FXhciCreateIntrQueue(FUsbEndpoint *ep, int reqsize, int reqcount, int reqtiming); +static void FXhciDestoryIntrQueue(FUsbEndpoint *ep, void *queue); +static u8 *FXhciPollIntrQueue(void *queue); + +/* + * Some structures must not cross page boundaries. To get this, + * we align them by their size (or the next greater power of 2). + */ +/** + * @name: FXhciAlign + * @msg: 分配一段对齐的内存 + * @return {*} + * @param {FXhci} *xhci, XHCI控制器实例 + * @param {size_t} min_align, 对齐方式 + * @param {size_t} size, 请求的字节数目 + */ +void *FXhciAlign(FXhci *const xhci, const size_t min_align, const size_t size) +{ + FASSERT(xhci && xhci->usb); + size_t align; + FUsb *instance = xhci->usb; + + if (!(size & (size - 1))) + align = size; /* It's a power of 2 */ + else + align = 1 << ((sizeof(unsigned) << 3) - __builtin_clz(size)); + + if (align < min_align) + align = min_align; + + return FUSB_ALLOCATE(instance, size, align); +} + +#ifdef FMEMP_TAG_DEBUG +void *FXhciAlignTag(FXhci *const xhci, const size_t min_align, const size_t size, const char *file, unsigned long line, const char *msg) +{ + FASSERT(xhci && xhci->usb); + size_t align; + FUsb *instance = xhci->usb; + + if (!(size & (size - 1))) + align = size; /* It's a power of 2 */ + else + align = 1 << ((sizeof(unsigned) << 3) - __builtin_clz(size)); + + if (align < min_align) + align = min_align; + + return FUsbMempAllocateTag(instance, size, align, file, line, msg); +} +#endif + +/** + * @name: FXhciClearTrb + * @msg: 清空TRB,反转TRB的Cycle state + * @return {*} + * @param {FXhciTrb} *trb, TRB实例 + * @param {int} pcs, TRB ring的Cycle state + */ +void FXhciClearTrb(FXhciTrb *const trb, const int pcs) +{ + FASSERT(trb); + + trb->ptr_low = 0; + trb->ptr_high = 0; + trb->status = 0; + trb->control = !pcs; + + return; +} + +/** + * @name: FXhciInitCycleRing + * @msg: 初始化TRB ring + * @return {*} + * @param {FXhciTransRing} *tr, TRB ring实例 + * @param {size_t} ring_size, TRB ring中的TRB数目 + */ +void FXhciInitCycleRing(FXhciTransRing *const tr, const size_t ring_size) +{ + FASSERT(tr && tr->ring); + + memset((void *)tr->ring, 0, ring_size * sizeof(*tr->ring)); + FXHCI_TRB_SET(TT, &tr->ring[ring_size - 1], FXHCI_TRB_LINK); /* TRB Type */ + FXHCI_TRB_SET(TC, &tr->ring[ring_size - 1], 1); /* Toggle Cycle */ + + /* only one segment that points to itself */ + tr->ring[ring_size - 1].ptr_low = (uintptr)(tr->ring); + + tr->pcs = 1; + tr->cur = tr->ring; + + return; +} + +/** + * @name: FXhciHandShake + * @msg: 等待XHCI状态,完成握手 + * @return {FError} 等待返回值 + * @param {FXhci} *xhci, xhci实例 + * @param {FXhciHandShakeType} type, 等待类型,e.g 等待Op寄存器 + * @param {uintptr} reg_off, 寄存器偏移量 + * @param {u32} mask, 寄存器掩码位 + * @param {u32} wait_for, 等待的状态,如果状态到达,成功退出 + * @param {s32} timeout, 等待的tick超时 + */ +static FError FXhciHandShake(FXhci *xhci, FXhciHandShakeType type, uintptr reg_off, u32 mask, u32 wait_for, s32 timeout) +{ + FASSERT(xhci); + FError ret = FUSB_SUCCESS; + + switch (type) + { + case FXHCI_OP_REG: + ret = FXhciWaitOper32(&xhci->mmio, reg_off, mask, wait_for, timeout); + break; + default: + FASSERT(0); + break; + } + + return ret; +} + +/** + * @name: FXhciWaitReady + * @msg: 等待XHCI控制器重置完成 + * @return {*} + * @param {FXhci} *xhci, xhci实例 + */ +static FError FXhciWaitReady(FXhci *const xhci) +{ + FASSERT(xhci); + FUSB_INFO("Waiting for controller to be ready... "); + FError ret = FXhciHandShake(xhci, FXHCI_OP_REG, FXHCI_REG_OP_USBSTS, FXHCI_REG_OP_USBSTS_CNR, 0, FXHCI_TIMEOUT); + + if (FUSB_SUCCESS == ret) + FUSB_INFO("ok"); + else + FUSB_ERROR("timeout."); + + return ret; +} + +/** + * @name: FXhciHcInit + * @msg: 创建XHCI USB 控制器实例,完成初始化 + * @return {FUsbHc *} XHCI控制器实例 + * @param {FUsb} *instance, USB实例 + * @param {uintptr} base_addr, XHCI控制器基地址 + */ +FUsbHc *FXhciHcInit(FUsb *instance, uintptr base_addr) +{ + FASSERT(instance); + int i; + FXhciMMIO *mmio = NULL; + u32 reg_val; + u16 hc_version; + uintptr xhci_base_addr = base_addr + FUSB3_XHCI_OFFSET; + + FUSB_DEBUG("xhci base addr: 0x%x", xhci_base_addr); + /* First, allocate and initialize static controller structures */ + FUsbHc *const controller = FUsbAllocateHc(instance); + if (NULL == controller) + { + FUSB_ERROR("Out of memory "); + return NULL; + } + + /* set USB Hc CB according to XHCI */ + controller->type = FUSB_HC_XHCI; + controller->start = FXhciStart; + controller->stop = FXhciStop; + controller->reset = FXhciReset; + controller->init = FXhciReinit; + controller->shutdown = FXhciShutdown; + controller->bulk = FXhciBulk; + controller->control = FXhciControl; + controller->set_address = FXhciSetAddress; + controller->finish_device_config = FXhciFinishDevConfig; + controller->destroy_device = FXhciDestoryDev; + controller->create_intr_queue = FXhciCreateIntrQueue; + controller->destroy_intr_queue = FXhciDestoryIntrQueue; + controller->poll_intr_queue = FXhciPollIntrQueue; + + /* allocate xhci instance */ + controller->reg_base = base_addr; + FASSERT(NULL == controller->instance); + controller->instance = FUSB_ALLOCATE(instance, sizeof(FXhci), FUSB_DEFAULT_ALIGN); + if (NULL == controller->instance) + { + FUSB_INFO("Out of memory "); + goto _free_controller; + } + + controller->usb = instance; + + FXhci *const xhci = (FXhci *)controller->instance; + xhci->usb = instance; + + /* init roothub at slot-0 */ + FUsbInitDevEntry(controller, 0); + xhci->roothub = controller->devices[0]; + + /* allocate command ring and event ring */ + FASSERT((NULL == xhci->cr.ring) && (NULL == xhci->er.ring) && + (NULL == xhci->ev_ring_table)); + xhci->cr.ring = FXHCI_ALIGN(xhci, 64, FXHCI_COMMAND_RING_SIZE * sizeof(FXhciTrb)); + xhci->er.ring = FXHCI_ALIGN(xhci, 64, FXHCI_EVENT_RING_SIZE * sizeof(FXhciTrb)); + xhci->ev_ring_table = FXHCI_ALIGN(xhci, 64, sizeof(FXhciErstEntry)); + if ((NULL == xhci->roothub) || (NULL == xhci->cr.ring) || + (NULL == xhci->er.ring) || (NULL == xhci->ev_ring_table)) + { + FUSB_INFO("Out of memory "); + goto _free_xhci; + } + + /* setup xhci mmio for register access */ + mmio = &xhci->mmio; + FXhciSetupMMIO(mmio, xhci_base_addr); + + /* check if XHCI version is supported */ + hc_version = FXhciReadHcVersion(mmio); + if (hc_version < FXHCI_HC_VERSION_MIN || hc_version > FXHCI_HC_VERSION_MAX) + { + FUSB_ERROR("xHCI version 0x%x not support", hc_version); + goto _free_xhci; + } + + const unsigned pagesize = FXhciReadOper32(mmio, FXHCI_REG_OP_PAGESIZE) << 12; + + FUSB_INFO("regbase: 0x%x", mmio->base); + FUSB_INFO("caplen: 0x%x", FXhciReadCaplen(mmio)); + FUSB_INFO("rtsoff: 0X%x", mmio->runtime_base - mmio->base); + FUSB_INFO("dboff: 0X%x", mmio->doorbell_base - mmio->base); + FUSB_INFO("hciversion: 0x%x", FXhciReadHcVersion(mmio)); + FUSB_INFO("context size: %dB ", FXhciGetCtxSize(mmio)); + FUSB_INFO("page size: %dB ", pagesize); + + /* + * We haven't touched the hardware yet. So we allocate all dynamic + * structures at first and can still chicken out easily if we run out + * of memory. + */ + reg_val = FXhciReadCap32(&xhci->mmio, FXHCI_REG_CAP_HCSPARAMS1); + xhci->max_slots_en = FXHCI_REG_CAP_HCSPARAMS1_MAX_SLOTS_GET(reg_val); /* record max slot num */ + + /* allocate device related memory, DCBAA and device info */ + FASSERT(NULL == xhci->dcbaa); + xhci->dcbaa = FXHCI_ALIGN(xhci, 64, (xhci->max_slots_en + 1) * sizeof(u64)); + FASSERT(NULL == xhci->dev); + xhci->dev = FUSB_ALLOCATE(instance, (xhci->max_slots_en + 1) * sizeof(*xhci->dev), FUSB_DEFAULT_ALIGN); + if ((NULL == xhci->dcbaa) || (NULL == xhci->dev)) + { + FUSB_INFO("Out of memory "); + goto _free_xhci; + } + + /* + * Let dcbaa[0] point to another array of pointers, sp_ptrs. + * The pointers therein point to scratchpad buffers (pages). + */ + reg_val = FXhciReadCap32(&xhci->mmio, FXHCI_REG_CAP_HCSPARAMS2); + const size_t max_sp_bufs = FXHCI_REG_CAP_HCSPARAMS2_MAX_SCRATCHPAD_BUFS_GET(reg_val); + FUSB_INFO("max scratchpad bufs: 0x%lx reg_val : 0x%x", max_sp_bufs, reg_val); + if (0 < max_sp_bufs) + { + FUSB_INFO("allocate sp_ptrs"); + const size_t sp_ptrs_size = max_sp_bufs * sizeof(u64); + /* allocate scratchpad bufs entry to preserve pointers of scratchpad buf */ + FASSERT(NULL == xhci->sp_ptrs); + xhci->sp_ptrs = FXHCI_ALIGN(xhci, 64, sp_ptrs_size); + if (NULL == xhci->sp_ptrs) + { + FUSB_INFO("Out of memory "); + goto _free_xhci_structs; + } + + for (i = 0; (size_t)i < max_sp_bufs; ++i) + { + /* allocate each scratchpad buf */ + void *const page = FUSB_ALLOCATE(instance, pagesize, pagesize); + if (NULL == page) + { + FUSB_INFO("Out of memory "); + goto _free_xhci_structs; + } + + xhci->sp_ptrs[i] = (uintptr)(page); /* assign pointer to scratchpad buf*/ + } + + xhci->dcbaa[0] = (uintptr)(xhci->sp_ptrs); /* assign pointer to scratchpad bufs entry */ + } + + /* Now start working on the hardware */ + if (FUSB_SUCCESS != FXhciWaitReady(xhci)) + goto _free_xhci_structs; + + /* TODO: Check if BIOS claims ownership (and hand over) */ + + if (FUSB_CC_SUCCESS != FXhciReset(controller)) + { + goto _free_xhci_structs; + } + + FXhciReinit(controller); + + /* init roothub device instance */ + xhci->roothub->controller = controller; + xhci->roothub->init = FXhciRootHubInit; + xhci->roothub->init(xhci->roothub); + + FUSB_INFO("init xHc@%p success", controller); + return controller; + +_free_xhci_structs: + if (xhci->sp_ptrs) + { + for (i = 0; (size_t)i < max_sp_bufs; ++i) + { + if (xhci->sp_ptrs[i]) + FUSB_FREE(instance, (void *)(uintptr)(xhci->sp_ptrs[i])); + } + } + FUSB_FREE(instance, xhci->sp_ptrs); + FUSB_FREE(instance, xhci->dcbaa); + +_free_xhci: + FUSB_FREE(instance, (void *)xhci->ev_ring_table); + FUSB_FREE(instance, (void *)xhci->er.ring); + FUSB_FREE(instance, (void *)xhci->cr.ring); + FUSB_FREE(instance, xhci->roothub); + FUSB_FREE(instance, xhci->dev); + FUSB_FREE(instance, xhci); + +_free_controller: + FUsbDetachHc(controller); + FUSB_FREE(instance, controller); + + return NULL; +} + +/** + * @name: FXhciReset + * @msg: 重置XHCI控制器 + * @return {*} + * @param {FUsbHc} *controller, USB Hc控制器 + */ +static FUsbTransCode FXhciReset(FUsbHc *const controller) +{ + FASSERT(controller); + FXhci *const xhci = FXHCI_INST_GET(controller); + FASSERT(xhci); + u32 reg_val; + + /* stop xhci if still run */ + FXhciStop(controller); + + reg_val = FXhciReadOper32(&xhci->mmio, FXHCI_REG_OP_USBCMD); + reg_val |= FXHCI_REG_OP_USBCMD_HCRST; + FXhciWriteOper32(&xhci->mmio, FXHCI_REG_OP_USBCMD, reg_val); + + FUSB_INFO("Resetting hc..."); + if (FUSB_SUCCESS != FXhciHandShake(xhci, FXHCI_OP_REG, FXHCI_REG_OP_USBCMD, FXHCI_REG_OP_USBCMD_HCRST, 0, FXHCI_TIMEOUT)) + { + FUSB_ERROR("Wait hc reset timeout !!!"); + return -1; + } + + return FUSB_CC_SUCCESS; +} + +/** + * @name: FXhciReinit + * @msg: 重新初始化XHCI控制器 + * @return {*} + * @param {FUsbHc} *controller, USB Hc控制器 + */ +static void FXhciReinit(FUsbHc *controller) +{ + FASSERT(controller); + FXhci *const xhci = FXHCI_INST_GET(controller); + FASSERT(xhci); + const FUsbConfig *const config = &controller->usb->config; + u32 reg_val; + u64 reg_val64; + + /* wait xhci ready */ + if (FUSB_SUCCESS != FXhciWaitReady(xhci)) + return; + + /* Enable all available slots */ + reg_val = FXhciReadOper32(&xhci->mmio, FXHCI_REG_OP_CONFIG); + reg_val &= ~FXHCI_REG_OP_CONFIG_MAX_SLOTS_EN_MASK; + reg_val |= FXHCI_REG_OP_CONFIG_MAX_SLOTS_EN_SET(xhci->max_slots_en); + FXhciWriteOper32(&xhci->mmio, FXHCI_REG_OP_CONFIG, reg_val); + + /* Set DCBAA */ + FXhciWriteOper64(&xhci->mmio, FXHCI_REG_OP_DCBAAP, (u64)(uintptr)xhci->dcbaa); + FUSB_INFO("dcba at 0x%lx", FXhciReadOper64(&xhci->mmio, FXHCI_REG_OP_DCBAAP)); + + /* Initialize command ring */ + FXhciInitCycleRing(&xhci->cr, FXHCI_COMMAND_RING_SIZE); + FUSB_INFO("command ring @%p", xhci->cr.ring); + reg_val64 = FXhciReadOper64(&xhci->mmio, FXHCI_REG_OP_CRCR); + + reg_val64 = FXHCI_REG_OP_CRCR_CR_PTR_MASK & (u64)(uintptr)xhci->cr.ring; + reg_val64 |= FXHCI_REG_OP_CRCR_RCS & xhci->cr.pcs; + FXhciWriteOper64(&xhci->mmio, FXHCI_REG_OP_CRCR, reg_val64); + + /* Make sure interrupts are disabled */ + reg_val = FXhciReadOper32(&xhci->mmio, FXHCI_REG_OP_USBCMD); + reg_val &= ~FXHCI_REG_OP_USBCMD_INTE; + FXhciWriteOper32(&xhci->mmio, FXHCI_REG_OP_USBCMD, reg_val); + + /* Initialize event ring */ + FXhciResetEvtRing(&xhci->er); + FUSB_INFO("event ring @%p (0x%08p) ", + xhci->er.ring, (uintptr)(xhci->er.ring)); + + reg_val = FXhciReadCap32(&xhci->mmio, FXHCI_REG_CAP_HCSPARAMS2); + FUSB_INFO("ERST Max: %d", + FXHCI_REG_CAP_HCSPARAMS2_ERST_MAX_GET(reg_val)); + memset((void *)xhci->ev_ring_table, 0x00, sizeof(FXhciErstEntry)); + xhci->ev_ring_table[0].seg_base_lo = (uintptr)(xhci->er.ring); + xhci->ev_ring_table[0].seg_base_hi = 0; + xhci->ev_ring_table[0].seg_size = FXHCI_EVENT_RING_SIZE; + + /* pass event ring table to hardware */ + WMB(); + + /* Initialize primary interrupter */ + FXhciWriteRt32(&xhci->mmio, 0, FXHCI_REG_RT_IR_ERSTSZ, FXHCI_REG_RT_IR_ERSTSZ_MASK & 1); /* Segment Table Size = 1 */ + FXhciUpdateEvtDQ(xhci); + + /* erstba has to be written at last */ + FXhciWriteRt64(&xhci->mmio, 0, FXHCI_REG_RT_IR_ERSTBA, FXHCI_REG_RT_IR_ERSTBA_MASK & ((u64)(uintptr)xhci->ev_ring_table)); + FUSB_INFO("ERST base: 0x%lx == %p", + FXhciReadRt64(&xhci->mmio, 0, FXHCI_REG_RT_IR_ERSTBA), + xhci->ev_ring_table); + + FXhciStart(controller); + + /* run Cmd Nop to test if command ring okay */ + for (int i = 0; i < 3; ++i) + { + FUSB_INFO("NOOP run #%d ", i); + if (FXHCI_CC_SUCCESS != FXhciCmdNop(xhci)) + { + FUSB_ERROR("noop command failed. "); + break; + } + } + + return; +} + +/** + * @name: FXhciShutdown + * @msg: 关闭XHCI控制器 + * @return {*} + * @param {FUsbHc} *controller, USB Hc控制器 + */ +static void FXhciShutdown(FUsbHc *const controller) +{ + int i; + u32 reg_val; + + if (controller == NULL) + return; + + /* detach the Hc instance */ + FUsbDetachHc(controller); + + FXhci *const xhci = FXHCI_INST_GET(controller); + FASSERT(xhci && xhci->usb); + FUsb *instance = xhci->usb; + FXhciStop(controller); /* stop xhci instance */ + + /* free scatchpad bufs */ + if (NULL != xhci->sp_ptrs) + { + reg_val = FXhciReadCap32(&xhci->mmio, FXHCI_REG_CAP_HCSPARAMS2); + const size_t max_sp_bufs = FXHCI_REG_CAP_HCSPARAMS2_MAX_SCRATCHPAD_BUFS_GET(reg_val); + for (i = 0; (size_t)i < max_sp_bufs; ++i) + { + if (NULL != (void *)(uintptr)xhci->sp_ptrs[i]) + FUSB_FREE(instance, (void *)(uintptr)(xhci->sp_ptrs[i])); + } + } + + + FUSB_FREE(instance, xhci->sp_ptrs); + FUSB_FREE(instance, xhci->dcbaa); + FUSB_FREE(instance, xhci->dev); + FUSB_FREE(instance, (void *)xhci->ev_ring_table); + FUSB_FREE(instance, (void *)xhci->er.ring); + FUSB_FREE(instance, (void *)xhci->cr.ring); + FUSB_FREE(instance, xhci); + + return; +} + +/** + * @name: FXhciStart + * @msg: 启动XHCI控制器 + * @return {*} + * @param {FUsbHc} *controller, USB Hc控制器 + */ +static void FXhciStart(FUsbHc *controller) +{ + FASSERT(controller); + FXhci *const xhci = FXHCI_INST_GET(controller); + FASSERT(xhci); + u32 reg_val; + + /* setting the USBCMD register Run/Stop (R/S) bit to ‘1’ */ + reg_val = FXhciReadOper32(&xhci->mmio, FXHCI_REG_OP_USBCMD); + reg_val |= FXHCI_REG_OP_USBCMD_RUN_STOP; + FXhciWriteOper32(&xhci->mmio, FXHCI_REG_OP_USBCMD, reg_val); + + if (FUSB_SUCCESS != FXhciHandShake(xhci, FXHCI_OP_REG, FXHCI_REG_OP_USBSTS, FXHCI_REG_OP_USBSTS_HCH, 0, FXHCI_TIMEOUT)) + { + FUSB_ERROR("Wait hc start timeout !!!"); + } + + return; +} + +/** + * @name: FXhciStop + * @msg: 停止XHCI控制器 + * @return {*} + * @param {FUsbHc} *controller, USB Hc控制器 + */ +static void FXhciStop(FUsbHc *controller) +{ + FASSERT(controller); + FXhci *const xhci = FXHCI_INST_GET(controller); + FASSERT(xhci); + u32 reg_val; + + /* setting the USBCMD register Run/Stop (R/S) bit to ‘0’ */ + reg_val = FXhciReadOper32(&xhci->mmio, FXHCI_REG_OP_USBCMD); + reg_val &= ~FXHCI_REG_OP_USBCMD_RUN_STOP; + FXhciWriteOper32(&xhci->mmio, FXHCI_REG_OP_USBCMD, reg_val); + + if (FUSB_SUCCESS != FXhciHandShake(xhci, FXHCI_OP_REG, FXHCI_REG_OP_USBSTS, FXHCI_REG_OP_USBSTS_HCH, FXHCI_REG_OP_USBSTS_HCH, FXHCI_TIMEOUT)) + { + FUSB_ERROR("Wait hc stop timeout !!!"); + } + + return; +} + +/** + * @name: FXhciResetEp + * @msg: 重置XHCI端点 + * @return {FError} 返回错误码 + * @param {FUsbDev} *dev, 端点所在的USB设备实例 + * @param {FUsbEndpoint} *ep, 端点实例 + */ +static FError FXhciResetEp(FUsbDev *const dev, FUsbEndpoint *const ep) +{ + FASSERT(dev && dev->controller); + FXhci *const xhci = FXHCI_INST_GET(dev->controller); + FASSERT(xhci); + const int slot_id = dev->address; + const int ep_id = (NULL != ep) ? FXhciEpId(ep) : FXHCI_EP0_ID; /* ep-0 or normal ep */ + FXhciEpCtx *const epctx = xhci->dev[slot_id].ctx.ep[ep_id]; + + FUSB_INFO("Resetting ID %d EP %d (ep state: %d) ", + slot_id, ep_id, FXHCI_EC_GET(STATE, epctx)); + + /* Run Reset Endpoint Command if the EP is in Halted state */ + if (FXHCI_EC_GET(STATE, epctx) == FXHCI_EC_STATE_HALTED) + { + const FXhciTransCode cc = FXhciCmdResetEp(xhci, slot_id, ep_id); + if (cc != FXHCI_CC_SUCCESS) + { + FUSB_INFO("Reset Endpoint Command failed: %d ", cc); + return FUSB_ERR_TRANS_FAIL; + } + } + + /* Clear TT buffer for bulk and control endpoints behind a TT */ + const int hub = dev->hub; + if (hub && dev->speed < FUSB_HIGH_SPEED && + dev->controller->devices[hub]->speed == FUSB_HIGH_SPEED) + { + /* TODO */; + } + + /* Reset transfer ring if the endpoint is in the right state */ + const unsigned ep_state = FXHCI_EC_GET(STATE, epctx); + if (ep_state == FXHCI_EC_STATE_STOPPED || ep_state == FXHCI_EC_STATE_ERROR) + { + FXhciTransRing *const tr = + xhci->dev[slot_id].transfer_rings[ep_id]; + const FXhciTransCode cc = FXhciCmdSetTrDq(xhci, slot_id, ep_id, + tr->ring, 1); + if (cc != FXHCI_CC_SUCCESS) + { + FUSB_INFO("Set TR Dequeue Command failed: %d ", cc); + return FUSB_ERR_TRANS_FAIL; + } + + FXhciInitCycleRing(tr, FXHCI_TRANSFER_RING_SIZE); + } + + FUSB_INFO("Finished resetting ID %d EP %d (ep state: %d) ", + slot_id, ep_id, FXHCI_EC_GET(STATE, epctx)); + + return FUSB_SUCCESS; +} + +/** + * @name: FXhciEnqueueTrb + * @msg: TRB入队,向TRB ring加入一条TRB + * @return {*} + * @param {FXhciTransRing} *tr, TRB ring实例 + */ +static void FXhciEnqueueTrb(FXhciTransRing *const tr) +{ + FASSERT(tr); + + const int chain = FXHCI_TRB_GET(CH, tr->cur); + FXHCI_TRB_SET(C, tr->cur, tr->pcs); /* Cycle Bit */ + ++tr->cur; + + while (FXHCI_TRB_GET(TT, tr->cur) == FXHCI_TRB_LINK) + { + FUSB_DEBUG("Handling LINK pointer "); + + const int tc = FXHCI_TRB_GET(TC, tr->cur); + FXHCI_TRB_SET(CH, tr->cur, chain); /* Chain Bit */ + + WMB(); + + FXHCI_TRB_SET(C, tr->cur, tr->pcs); /* Cycle Bit */ + tr->cur = (void *)(uintptr)(tr->cur->ptr_low); + + /* toggle cycle state */ + if (tc) + tr->pcs ^= 1; + } + + return; +} + +/** + * @name: FXhciRingDoorbell + * @msg: 提醒Hc处理刚加入的TRB + * @return {*} + * @param {FUsbEndpoint} *ep, 端点实例 + */ +static void FXhciRingDoorbell(FUsbEndpoint *const ep) +{ + FASSERT(ep); + FXhci *xhci = FXHCI_INST_GET(ep->dev->controller); + + /* Ensure all TRB changes are written to memory. */ + WMB(); + + FXhciWriteDb32(&xhci->mmio, ep->dev->address, FXhciEpId(ep)); + + return; +} + +/** + * @name: FXhciEnqueueTD + * @msg: 填充一条TRB + * @return {*} + * @param {FXhciTransRing} *tr, TRB ring实例 + * @param {int} ep, 端点实例 + * @param {size_t} mps, 最大包长度 + * @param {int} dalen, 数据长度 + * @param {void} *data, 数据缓冲区 + * @param {int} dir, 发送方向 + */ +static void FXhciEnqueueTD(FXhciTransRing *const tr, const int ep, const size_t mps, + const int dalen, void *const data, const int dir) +{ + FASSERT(tr); + FXhciTrb *trb = NULL; /* cur TRB */ + u8 *cur_start = data; /* cur data pointer */ + size_t length = dalen; /* remaining bytes */ + size_t packets = (length + mps - 1) / mps; /* remaining packets */ + size_t residue = 0; /* residue from last TRB */ + size_t trb_count = 0; /* TRBs added so far */ + + while ((length > 0) || (trb_count == 0) /* enqueue at least one */) + { + const size_t cur_end = ((size_t)cur_start + 0x10000) & ~0xffff; /* best guess, send at most 0x1000 bytes */ + size_t cur_length = cur_end - (size_t)cur_start; + + if (length < cur_length) + { + cur_length = length; + packets = 0; + length = 0; + } + else + { + packets -= (residue + cur_length) / mps; + residue = (residue + cur_length) % mps; + length -= cur_length; + } + + trb = tr->cur; + FXhciClearTrb(trb, tr->pcs); + trb->ptr_low = (uintptr)(cur_start); + FXHCI_TRB_SET(TL, trb, cur_length); /* Transfer Length */ + FXHCI_TRB_SET(TDS, trb, min((size_t)FXHCI_TRB_MAX_TD_SIZE, packets)); /* TD Size */ + FXHCI_TRB_SET(CH, trb, 1); /* associate this TRB with the next TRB on the Ring */ + + /* Check for first, data stage TRB, only Ep0 can handle */ + if ((trb_count == 0) && ep == FXHCI_EP0_ID) + { + FXHCI_TRB_SET(DIR, trb, dir); /* Direction */ + FXHCI_TRB_SET(TT, trb, FXHCI_TRB_DATA_STAGE); /* TRB Type */ + } + else + { + FXHCI_TRB_SET(TT, trb, FXHCI_TRB_NORMAL); /* TRB Type */ + } + + /* + * This is a workaround for Synopsys DWC3. If the ENT flag is + * not set for the Normal and Data Stage TRBs. We get Event TRB + * with length 0x20d from the controller when we enqueue a TRB + * for the IN endpoint with length 0x200. + */ + if (0 == length) + { + /* xHC shall fetch and evaluate the next TRB before saving the endpoint state */ + FXHCI_TRB_SET(ENT, trb, 1); /* Evaluate Next TRB */ + } + + FXhciEnqueueTrb(tr); + + cur_start += cur_length; + ++trb_count; + } + + trb = tr->cur; + FXhciClearTrb(trb, tr->pcs); + trb->ptr_low = (uintptr)(trb); /* for easier debugging only */ + FXHCI_TRB_SET(TT, trb, FXHCI_TRB_EVENT_DATA); /* set transfer type */ + FXHCI_TRB_SET(IOC, trb, 1); /* xHc shalle notify the system of the completion by placing an Transfer Event TRB on the Event ring */ + + FXhciEnqueueTrb(tr); + + return; +} + +/** + * @name: FXhciControl + * @msg: XHCI控制传输 + * @return {FXhciTransCode} 传输返回值 + * @param {FUsbDev} *dev, USB设备实例 + * @param {FUsbDirection} dir, 控制传输类型, IN, OUT, SETUP + * @param {int} drlen, USB请求长度 + * @param {void} *devreq, USB请求,参考FUsbDevReq + * @param {int} dalen, 控制传输数据长度 + * @param {unsigned char} *src, 控制传输数据 + */ +static FXhciTransCode FXhciControl(FUsbDev *const dev, const FUsbDirection dir, + const int drlen, void *const devreq, const int dalen, + unsigned char *const src) +{ + FASSERT(dev && dev->controller); + unsigned char *data = src; + FXhci *const xhci = FXHCI_INST_GET(dev->controller); + FASSERT(xhci); + FXhciEpCtx *const epctx = xhci->dev[dev->address].ctx.ep0; + FXhciTransRing *const tr = xhci->dev[dev->address].transfer_rings[1]; + FASSERT(epctx && tr); + + /* check the transfer data length, less than WORD size, u16 pointer can hold */ + const size_t off = (size_t)data & 0xffff; + if ((off + dalen) > ((FXHCI_TRANSFER_RING_SIZE - 4) << 16)) + { + FUSB_ERROR("Unsupported transfer size 0x%lx!!!", dalen); + return FXHCI_CC_GENERAL_ERROR; + } + + /* Reset endpoint if it's not running and not disabled */ + const unsigned ep_state = FXHCI_EC_GET(STATE, epctx); + if (ep_state > FXHCI_EC_STATE_RUNNING) + { + if (FUSB_SUCCESS != FXhciResetEp(dev, NULL)) + { + FUSB_ERROR("Reset endpoint failed !!!"); + return FXHCI_CC_GENERAL_ERROR; + } + } + + /* Fill and enqueue setup TRB */ + FXhciTrb *const setup = tr->cur; + FXhciClearTrb(setup, tr->pcs); + setup->ptr_low = ((u32 *)devreq)[0]; /* request data */ + setup->ptr_high = ((u32 *)devreq)[1]; + FXHCI_TRB_SET(TL, setup, drlen/*8*/); /* Transfer length */ + FXHCI_TRB_SET(TRT, setup, (dalen > 0) ? ((dir == FUSB_OUT) ? FXHCI_TRB_TRT_OUT_DATA : FXHCI_TRB_TRT_IN_DATA) : FXHCI_TRB_TRT_NO_DATA); + FXHCI_TRB_SET(TT, setup, FXHCI_TRB_SETUP_STAGE); /* TRB Type */ + FXHCI_TRB_SET(IDT, setup, 1); /* Immediate Data (IDT). shall be set to ‘1’ in a Setup Stage TRB */ + FXHCI_TRB_SET(IOC, setup, 1); /* Interrupt On Completion (IOC) */ + FXhciEnqueueTrb(tr); + + /* Fill and enqueue data TRBs (if any) */ + if (dalen > 0) + { + const unsigned mps = FXHCI_EC_GET(MPS, epctx); + const unsigned dt_dir = (dir == FUSB_OUT) ? FXHCI_TRB_DIR_OUT : FXHCI_TRB_DIR_IN; + FXhciEnqueueTD(tr, FXHCI_EP0_ID, mps, dalen, data, dt_dir); + } + + /* Fill status TRB */ + FXhciTrb *const status = tr->cur; + FXhciClearTrb(status, tr->pcs); + FXHCI_TRB_SET(DIR, status, (dir == FUSB_OUT) ? FXHCI_TRB_DIR_IN : FXHCI_TRB_DIR_OUT); /* Direction */ + FXHCI_TRB_SET(TT, status, FXHCI_TRB_STATUS_STAGE); /* TRB Type */ + FXHCI_TRB_SET(IOC, status, 1); /* Interrupt On Completion */ + FXhciEnqueueTrb(tr); + + /* Ring doorbell for EP0 */ + FXhciRingDoorbell(&dev->endpoints[0]); + + /* Wait for transfer events from IN DATA OUT stages */ + int i, transferred = 0; + const int n_stages = 2 + !!dalen; /* 2 stage without data */ + + /* flush cache of request data / transfer data before transfer */ + if (dalen > 0) + { + FCacheDCacheInvalidateRange((uintptr)data, dalen); + FCacheDCacheInvalidateRange((uintptr)devreq, drlen); + } + + for (i = 0; i < n_stages; ++i) + { + const FXhciTransCode ret = FXhciWaitForTransfer(xhci, dev->address, 1); + transferred += ret; /* record bytes transfered successfully */ + if (ret < FXHCI_CC_ZERO_BYTES) /* negative ret means transfer error */ + { + if (ret == FXHCI_CC_TIMEOUT) + { + FUSB_ERROR("Stopping ID %d EP 1 ", + dev->address); + FXhciCmdStopEp(xhci, dev->address, FXHCI_EP0_ID); + } + + FUSB_ERROR("Stage %d/%d failed: %d \r\n" + " trb ring: @%p \r\n" + " setup trb: @%p \r\n" + " status trb: @%p \r\n" + " ep state: %d -> %d \r\n" + " usbsts: 0x%08x ", + i, n_stages, ret, + tr->ring, setup, status, + ep_state, FXHCI_EC_GET(STATE, epctx), + FXhciReadOper32(&xhci->mmio, FXHCI_REG_OP_USBSTS)); + return ret; + } + } + + /* flush cache of request data / transfer data after transfer */ + if (dalen > 0) + { + FCacheDCacheInvalidateRange((uintptr)data, dalen); + FCacheDCacheInvalidateRange((uintptr)devreq, drlen); + } + + return transferred; +} + +/** + * @name: FXhciBulk + * @msg: XHCI块传输 + * @return {FXhciTransCode} + * @param {FUsbEndpoint} *ep, 端点实例 + * @param {int} size, + * @param {u8} *src + * @param {int} finalize + * @note: finalize == 1: if data is of packet aligned size, add a zero length packet + */ +static FXhciTransCode FXhciBulk(FUsbEndpoint *const ep, const int size, u8 *const src, + const int finalize) +{ + /* finalize: Hopefully the xHCI controller always does this. + We have no control over the packets. */ + FASSERT(ep); + u8 *data = src; + FXhci *const xhci = FXHCI_INST_GET(ep->dev->controller); + FASSERT(xhci); + const int slot_id = ep->dev->address; + const int ep_id = FXhciEpId(ep); /* must not for Ep0 */ + FXhciEpCtx *const epctx = xhci->dev[slot_id].ctx.ep[ep_id]; + FXhciTransRing *const tr = xhci->dev[slot_id].transfer_rings[ep_id]; + + /* check the transfer data length, less than WORD size, u16 pointer can hold */ + const size_t off = (size_t)data & 0xffff; + if ((off + size) > ((FXHCI_TRANSFER_RING_SIZE - 2) << 16)) + { + FUSB_INFO("Unsupported transfer size "); + return FXHCI_CC_GENERAL_ERROR; + } + + /* Reset endpoint if it's not running */ + const unsigned ep_state = FXHCI_EC_GET(STATE, epctx); + if (ep_state > FXHCI_EC_STATE_RUNNING) + { + if (FUSB_SUCCESS != FXhciResetEp(ep->dev, ep)) + return FXHCI_CC_GENERAL_ERROR; + } + + FCacheDCacheInvalidateRange((uintptr)data, size); + + /* Enqueue transfer and ring doorbell */ + const unsigned mps = FXHCI_EC_GET(MPS, epctx); + const unsigned dir = (ep->direction == FUSB_OUT) ? FXHCI_TRB_DIR_OUT : FXHCI_TRB_DIR_IN; + FXhciEnqueueTD(tr, ep_id, mps, size, data, dir); + FXhciRingDoorbell(ep); + + /* Wait for transfer event */ + const FXhciTransCode ret = FXhciWaitForTransfer(xhci, ep->dev->address, ep_id); + if (ret < FXHCI_CC_ZERO_BYTES) + { + if (ret == FXHCI_CC_TIMEOUT) + { + FUSB_INFO("Stopping ID %d EP %d ", + ep->dev->address, ep_id); + FXhciCmdStopEp(xhci, ep->dev->address, ep_id); + } + + FUSB_INFO("Bulk transfer failed: %d \r\n" + " ep state: %d -> %d \r\n" + " usbsts: 0x%08x ", + ret, ep_state, + FXHCI_EC_GET(STATE, epctx), + FXhciReadOper32(&xhci->mmio, FXHCI_REG_OP_USBSTS)); + + return ret; + } + + return ret; +} + +/** + * @name: FXhciNextTrb + * @msg: 获取下一个可用的TRB + * @return {FXhciTrb*} 可用的TRB + * @param {FXhciTrb} *cur, 当前TRB + * @param {int} *pcs, 传入的待反转的Cycle state + */ +static FXhciTrb *FXhciNextTrb(FXhciTrb *cur, int *const pcs) +{ + FASSERT(cur); + ++cur; + + while (FXHCI_TRB_GET(TT, cur) == FXHCI_TRB_LINK) + { + if (pcs && FXHCI_TRB_GET(TC, cur)) + *pcs ^= 1; + + cur = (void *)(uintptr)(cur->ptr_low); + } + + return cur; +} + +/** + * @name: FXhciCreateIntrQueue + * @msg: 创建XHCI的中断队列 + * @return {void *} 成功则返回指向中断队列的指针,失败返回NULL + * @param {FUsbEndpoint} *ep, 端点实例 + * @param {int} reqsize, 中断队列可接受的请求字节数 + * @param {int} reqcount, 中断队列可接受的最大请求数目 + * @param {int} reqtiming, 请求超时 + * @note create and hook-up an intr queue into device schedul + */ +static void *FXhciCreateIntrQueue(FUsbEndpoint *const ep, const int reqsize, const int reqcount, + const int reqtiming) +{ + /* reqtiming: We ignore it and use the interval from the + endpoint descriptor configured earlier. */ + FASSERT(ep); + FXhci *const xhci = FXHCI_INST_GET(ep->dev->controller); + FASSERT(xhci && xhci->usb); + FUsb *instance = xhci->usb; + const int slot_id = ep->dev->address; + const int ep_id = FXhciEpId(ep); + FXhciTransRing *const tr = xhci->dev[slot_id].transfer_rings[ep_id]; + + if (reqcount > (FXHCI_TRANSFER_RING_SIZE - 2)) + { + FUSB_INFO("reqcount is too high, at most %d supported ", + FXHCI_TRANSFER_RING_SIZE - 2); + return NULL; + } + + if (reqsize > 0x10000) + { + FUSB_INFO("reqsize is too large, at most 64KiB supported "); + return NULL; + } + + if (xhci->dev[slot_id].interrupt_queues[ep_id]) + { + FUSB_INFO("Only one interrupt queue per endpoint supported "); + return NULL; + } + + /* Allocate intrq structure and reqdata chunks */ + FXhciIntrQ *const intrq = FUSB_ALLOCATE(instance, sizeof(*intrq), FUSB_DEFAULT_ALIGN); + if (NULL == intrq) + { + FUSB_INFO("Out of memory "); + return NULL; + } + + int i; + int pcs = tr->pcs; + FXhciTrb *cur = tr->cur; + for (i = 0; i < reqcount; ++i) + { + if (FXHCI_TRB_GET(C, cur) == (unsigned int)pcs) + { + FUSB_INFO("Not enough empty TRBs "); + goto _free_return; + } + + /* allocate request buffer for each TRB */ + void *const reqdata = FXHCI_ALIGN(xhci, 1, reqsize); + if (NULL == reqdata) + { + FUSB_INFO("Out of memory "); + goto _free_return; + } + + FXhciClearTrb(cur, pcs); + cur->ptr_low = (uintptr)(reqdata); + cur->ptr_high = 0; + FXHCI_TRB_SET(TL, cur, reqsize); /* Transfer Length */ + FXHCI_TRB_SET(TT, cur, FXHCI_TRB_NORMAL); /* TRB Type */ + FXHCI_TRB_SET(ISP, cur, 1); /* Interrupt-on Short Packet */ + FXHCI_TRB_SET(IOC, cur, 1); /* Interrupt On Completion */ + + cur = FXhciNextTrb(cur, &pcs); + } + + intrq->size = reqsize; + intrq->count = reqcount; + intrq->next = tr->cur; + intrq->ready = NULL; + intrq->ep = ep; + xhci->dev[slot_id].interrupt_queues[ep_id] = intrq; + + /* Now enqueue all the prepared TRBs but the last + and ring the doorbell. */ + for (i = 0; i < (reqcount - 1); ++i) + FXhciEnqueueTrb(tr); + + FXhciRingDoorbell(ep); + return intrq; + +_free_return: + cur = tr->cur; + for (--i; i >= 0; --i) + { + FUSB_FREE(instance, (void *)(uintptr)(cur->ptr_low)); + cur = FXhciNextTrb(cur, NULL); + } + FUSB_FREE(instance, intrq); + return NULL; +} + +/** + * @name: FXhciDestoryIntrQueue + * @msg: 删除中断队列 + * @return {*} + * @param {FUsbEndpoint} *ep, 端点实例 + * @param {void} *q, 中断队列 + * @note remove queue from device schedule, dropping all data that came in + */ +static void FXhciDestoryIntrQueue(FUsbEndpoint *const ep, void *const q) +{ + FASSERT(ep && q); + FXhci *const xhci = FXHCI_INST_GET(ep->dev->controller); + FASSERT(xhci); + FUsb *instance = xhci->usb; + const int slot_id = ep->dev->address; + const int ep_id = FXhciEpId(ep); + FXhciTransRing *const tr = xhci->dev[slot_id].transfer_rings[ep_id]; + FXhciIntrQ *const intrq = (FXhciIntrQ *)q; + + /* Make sure the endpoint is stopped */ + if (FXHCI_EC_GET(STATE, xhci->dev[slot_id].ctx.ep[ep_id]) == FXHCI_EC_STATE_RUNNING) + { + const FXhciTransCode cc = FXhciCmdStopEp(xhci, slot_id, ep_id); + if (cc != FXHCI_CC_SUCCESS) + FUSB_INFO("Warning: Failed to stop endpoint "); + } + + /* Process all remaining transfer events */ + FXhciHandleEvts(xhci); + + /* Free all pending transfers and the interrupt queue structure */ + int i; + for (i = 0; (size_t)i < intrq->count; ++i) + { + FUSB_FREE(instance, (void *)(uintptr)(intrq->next->ptr_low)); + intrq->next = FXhciNextTrb(intrq->next, NULL); + } + + xhci->dev[slot_id].interrupt_queues[ep_id] = NULL; + FUSB_FREE(instance, (void *)intrq); + + /* Reset the controller's dequeue pointer and reinitialize the ring */ + FXhciCmdSetTrDq(xhci, slot_id, ep_id, tr->ring, 1); + FXhciInitCycleRing(tr, FXHCI_TRANSFER_RING_SIZE); + + return; +} + +/** + * @name: FXhciPollIntrQueue + * @msg: 轮询一次中断队列进行处理 + * @return {*} + * @param {void} *q, 中断队列 + * @note read one intr-packet from queue, if available. extend the queue for new input. + return NULL if nothing new available. + Recommended use: while (data=poll_intr_queue(q)) process(data); + */ +static u8 *FXhciPollIntrQueue(void *const q) +{ + if (NULL == q) + return NULL; + + FXhciIntrQ *const intrq = (FXhciIntrQ *)q; + FUsbEndpoint *const ep = intrq->ep; + FXhci *const xhci = FXHCI_INST_GET(ep->dev->controller); + FASSERT(xhci); + + /* TODO: Reset interrupt queue if it gets halted? */ + FXhciHandleEvts(xhci); + + u8 *reqdata = NULL; + while (!reqdata && intrq->ready) + { + const int ep_id = FXhciEpId(ep); + FXhciTransRing *const tr = + xhci->dev[ep->dev->address].transfer_rings[ep_id]; + + /* Fetch the request's buffer */ + reqdata = (void *)(uintptr)(intrq->next->ptr_low); + + /* Enqueue the last (spare) TRB and ring doorbell */ + FXhciEnqueueTrb(tr); + FXhciRingDoorbell(ep); + + /* Reuse the current buffer for the next spare TRB */ + FXhciClearTrb(tr->cur, tr->pcs); + tr->cur->ptr_low = (uintptr)(reqdata); + tr->cur->ptr_high = 0; + FXHCI_TRB_SET(TL, tr->cur, intrq->size); /* Transfer Length */ + FXHCI_TRB_SET(TT, tr->cur, FXHCI_TRB_NORMAL); /* TRB Type */ + FXHCI_TRB_SET(ISP, tr->cur, 1); /* Interrupt-on Short Packet */ + FXHCI_TRB_SET(IOC, tr->cur, 1); /* Interrupt On Completion */ + + /* Check if anything was transferred */ + const size_t read = FXHCI_TRB_GET(TL, intrq->next); + if (!read) + reqdata = NULL; + else if (read < intrq->size) + /* At least zero it, poll interface is rather limited */ + memset(reqdata + read, 0x00, intrq->size - read); + + /* Advance the interrupt queue */ + if (intrq->ready == intrq->next) + /* This was last TRB being ready */ + intrq->ready = NULL; + intrq->next = FXhciNextTrb(intrq->next, NULL); + } + + return reqdata; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci.h b/bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci.h new file mode 100644 index 0000000000..b23ebd05a1 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci.h @@ -0,0 +1,94 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fxhci.h + * Date: 2022-02-11 13:33:12 + * LastEditTime: 2022-02-18 09:17:44 + * Description:  This files is for definition of XHCI user function + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/2/7 init commit + */ + +#ifndef DRIVERS_USB_FXHCI_H +#define DRIVERS_USB_FXHCI_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ +#include "fusb.h" + +/************************** Constant Definitions *****************************/ +#define FXHCI_EVENT_RING_SIZE 64 +#define FXHCI_TRANSFER_RING_SIZE 32 /* Never raise this above 256 to prevent transfer event length overflow! */ +#define FXHCI_COMMAND_RING_SIZE 4 +#define FXHCI_NUM_EPS 32 + +#define FXHCI_HC_VERSION_MIN 0x96 /* supported XHCI version from v0.96 to v1.2 */ +#define FXHCI_HC_VERSION_MAX 0x120 + +/* completion code for xhci */ +enum +{ + /* Make these high enough to not collide with negative FUSB_HC_XHCI CCs */ + FXHCI_CC_TIMEOUT = -65, + FXHCI_CC_CONTROLLER_ERROR = -66, + FXHCI_CC_COMMUNICATION_ERROR = -67, + FXHCI_CC_OUT_OF_MEMORY = -68, + FXHCI_CC_DRIVER_ERROR = -69, + FXHCI_CC_GENERAL_ERROR = -1, + + FXHCI_CC_ZERO_BYTES = FUSB_CC_ZERO_BYTES, + + /* defined according to Table 130: TRB Completion Code Definitions in spec */ + FXHCI_CC_SUCCESS = FUSB_CC_SUCCESS, + + /* Use as -FXHCI_CC_TRB_ERROR when return as error */ + FXHCI_CC_TRB_ERROR = 5, + FXHCI_CC_STALL_ERROR = 6, + FXHCI_CC_RESOURCE_ERROR = 7, + FXHCI_CC_BANDWIDTH_ERROR = 8, + FXHCI_CC_NO_SLOTS_AVAILABLE = 9, + FXHCI_CC_SLOT_NOT_ENABLED = 11, + FXHCI_CC_SHORT_PACKET = 13, + FXHCI_CC_EVENT_RING_FULL_ERROR = 21, + FXHCI_CC_COMMAND_RING_STOPPED = 24, + FXHCI_CC_COMMAND_ABORTED = 25, + FXHCI_CC_STOPPED = 26, + FXHCI_CC_STOPPED_LENGTH_INVALID = 27 +}; + +typedef FUsbTransCode FXhciTransCode; +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ +/* 创建XHCI USB 控制器实例,完成初始化 */ +FUsbHc *FXhciHcInit(FUsb *instance, uintptr base_addr); + +#ifdef __cplusplus +} +#endif + + +#endif + + diff --git a/bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci_cmd.c b/bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci_cmd.c new file mode 100644 index 0000000000..dbfb2780da --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci_cmd.c @@ -0,0 +1,243 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fxhci_cmd.c + * Date: 2022-02-11 13:33:12 + * LastEditTime: 2022-02-18 09:11:23 + * Description:  This files is for implementation of XHCI command + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/2/7 init commit + */ + +#include "fdebug.h" +#include "fxhci_private.h" + +#define FUSB_DEBUG_TAG "FXHCI_CMD" +#define FUSB_ERROR(format, ...) FT_DEBUG_PRINT_E(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_WARN(format, ...) FT_DEBUG_PRINT_W(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_INFO(format, ...) FT_DEBUG_PRINT_I(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_DEBUG(format, ...) FT_DEBUG_PRINT_D(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) + +FXhciTrb *FXhciNextCmdTrb(FXhci *const xhci) +{ + FXhciClearTrb(xhci->cr.cur, xhci->cr.pcs); + return xhci->cr.cur; +} + +void FXhciPostCmd(FXhci *const xhci) +{ + FUSB_INFO("Command %d (@%p) ", FXHCI_TRB_GET(TT, xhci->cr.cur), xhci->cr.cur); + + FXHCI_TRB_SET(C, xhci->cr.cur, xhci->cr.pcs); /* Cycle Bit */ + ++xhci->cr.cur; + + /* pass command trb to hardware */ + WMB(); + + /* Ring the doorbell */ + FXhciWriteDb32(&xhci->mmio, FXHCI_REG_DB_HOST_CONTROLLER, FXHCI_REG_DB_TARGET_HC_COMMAND); + + while (FXHCI_TRB_GET(TT, xhci->cr.cur) == FXHCI_TRB_LINK) + { + FUSB_DEBUG("Handling LINK pointer (@%p) ", xhci->cr.cur); + const int tc = FXHCI_TRB_GET(TC, xhci->cr.cur); /* Completion Code */ + FXHCI_TRB_SET(C, xhci->cr.cur, xhci->cr.pcs); /* Cycle Bit */ + xhci->cr.cur = (void *)(uintptr)(xhci->cr.cur->ptr_low); + if (tc) + xhci->cr.pcs ^= 1; + } +} + +static FXhciTransCode FXhciWaitForCmd(FXhci *const xhci, + const FXhciTrb *const cmd_trb, + const int clear_event) +{ + FXhciTransCode cc; + u64 reg_val64; + + cc = FXhciWaitForCmdDone(xhci, cmd_trb, clear_event); + if (cc != FXHCI_CC_TIMEOUT) + return cc; + + /* Abort command on timeout */ + FUSB_ERROR("Aborting command (@%p), CRCR: 0x%x ", cmd_trb, FXhciReadOper64(&xhci->mmio, FXHCI_REG_OP_CRCR)); + + /* + * Ref. xHCI Specification Revision 1.2, May 2019. + * Section 5.4.5, Table 5-24. + * + * Abort the command and stop the ring. + */ + reg_val64 = FXhciReadOper64(&xhci->mmio, FXHCI_REG_OP_CRCR); + reg_val64 |= FXHCI_REG_OP_CRCR_CA; + FXhciWriteOper64(&xhci->mmio, FXHCI_REG_OP_CRCR, reg_val64); + + cc = FXhciWaitForCmdAborted(xhci, cmd_trb); + + if ((FXhciReadOper64(&xhci->mmio, FXHCI_REG_OP_CRCR) & FXHCI_REG_OP_CRCR_CRR)) + FUSB_ERROR("xhci_wait_for_command: Command ring still running"); + + return cc; +} + +FXhciTransCode FXhciCmdNop(FXhci *const xhci) +{ + FXhciTrb *const cmd = FXhciNextCmdTrb(xhci); + FXHCI_TRB_SET(TT, cmd, FXHCI_TRB_CMD_NOOP); /* TRB Type */ + + FXhciPostCmd(xhci); + + /* wait for result in event ring */ + FXhciTransCode cc = FXhciWaitForCmdDone(xhci, cmd, 1); + + FUSB_INFO("Command ring is %srunning: cc: %d", + (FXhciReadOper64(&xhci->mmio, FXHCI_REG_OP_CRCR) & FXHCI_REG_OP_CRCR_CRR) ? "" : "not ", /* check if cmd ring is running */ + cc); + + if (cc != FXHCI_CC_SUCCESS) + FUSB_ERROR("noop command failed. "); + + return cc; +} + +/* + * xhci_cmd_* return >= 0: xhci completion code (cc) + * < 0: driver error code + */ + +FXhciTransCode FXhciCmdEnableSlot(FXhci *const xhci, int *const slot_id) +{ + FXhciTrb *const cmd = FXhciNextCmdTrb(xhci); + FXHCI_TRB_SET(TT, cmd, FXHCI_TRB_CMD_ENABLE_SLOT); /* TRB Type */ + FXhciPostCmd(xhci); + + FXhciTransCode cc = FXhciWaitForCmd(xhci, cmd, 0); + if (cc >= 0) + { + if (cc == FXHCI_CC_SUCCESS) + { + *slot_id = FXHCI_TRB_GET(ID, xhci->er.cur); + if (*slot_id > xhci->max_slots_en) + cc = FXHCI_CC_CONTROLLER_ERROR; + } + + FXhciAdvanceEvtRing(xhci); + FXhciHandleEvts(xhci); + } + + return cc; +} + +FXhciTransCode FXhciCmdDisableSlot(FXhci *const xhci, const int slot_id) +{ + FXhciTrb *const cmd = FXhciNextCmdTrb(xhci); + + FXHCI_TRB_SET(TT, cmd, FXHCI_TRB_CMD_DISABLE_SLOT); /* TRB Type */ + FXHCI_TRB_SET(ID, cmd, slot_id); /* Slot ID */ + FXhciPostCmd(xhci); + + return FXhciWaitForCmd(xhci, cmd, 1); +} + +FXhciTransCode FXhciCmdAddressDevice(FXhci *const xhci, + const int slot_id, + FXhciInputCtx *const ic) +{ + FXhciTrb *const cmd = FXhciNextCmdTrb(xhci); + + FXHCI_TRB_SET(TT, cmd, FXHCI_TRB_CMD_ADDRESS_DEV); /* TRB Type */ + FXHCI_TRB_SET(ID, cmd, slot_id); /* Slot ID */ + cmd->ptr_low = (uintptr)(ic->raw); + FXhciPostCmd(xhci); + + return FXhciWaitForCmd(xhci, cmd, 1); +} + +FXhciTransCode FXhciCmdConfigureEp(FXhci *const xhci, + const int slot_id, + const int config_id, + FXhciInputCtx *const ic) +{ + FXhciTrb *const cmd = FXhciNextCmdTrb(xhci); + + FXHCI_TRB_SET(TT, cmd, FXHCI_TRB_CMD_CONFIGURE_EP); /* TRB Type */ + FXHCI_TRB_SET(ID, cmd, slot_id); /* Slot ID */ + cmd->ptr_low = (uintptr)(ic->raw); + + if (config_id == 0) + FXHCI_TRB_SET(DC, cmd, 1); /* Deconfigure */ + + FXhciPostCmd(xhci); + + return FXhciWaitForCmd(xhci, cmd, 1); +} + +FXhciTransCode FXhciCmdEvaluateCtx(FXhci *const xhci, + const int slot_id, + FXhciInputCtx *const ic) +{ + FXhciTrb *const cmd = FXhciNextCmdTrb(xhci); + + FXHCI_TRB_SET(TT, cmd, FXHCI_TRB_CMD_EVAL_CTX); /* TRB Type */ + FXHCI_TRB_SET(ID, cmd, slot_id); /* Slot ID */ + + cmd->ptr_low = (uintptr)(ic->raw); + FXhciPostCmd(xhci); + + return FXhciWaitForCmd(xhci, cmd, 1); +} + +FXhciTransCode FXhciCmdResetEp(FXhci *const xhci, const int slot_id, const int ep) +{ + FXhciTrb *const cmd = FXhciNextCmdTrb(xhci); + + FXHCI_TRB_SET(TT, cmd, FXHCI_TRB_CMD_RESET_EP); /* TRB Type */ + FXHCI_TRB_SET(ID, cmd, slot_id); /* Slot ID */ + FXHCI_TRB_SET(EP, cmd, ep); /* Endpoint ID */ + + FXhciPostCmd(xhci); + + return FXhciWaitForCmd(xhci, cmd, 1); +} + +FXhciTransCode FXhciCmdStopEp(FXhci *const xhci, const int slot_id, const int ep) +{ + FXhciTrb *const cmd = FXhciNextCmdTrb(xhci); + + FXHCI_TRB_SET(TT, cmd, FXHCI_TRB_CMD_STOP_EP); /* TRB Type */ + FXHCI_TRB_SET(ID, cmd, slot_id); /* Slot ID */ + FXHCI_TRB_SET(EP, cmd, ep); /* Endpoint ID */ + + FXhciPostCmd(xhci); + + return FXhciWaitForCmd(xhci, cmd, 1); +} + +FXhciTransCode FXhciCmdSetTrDq(FXhci *const xhci, const int slot_id, const int ep, + FXhciTrb *const dq_trb, const int dcs) +{ + FXhciTrb *const cmd = FXhciNextCmdTrb(xhci); + + FXHCI_TRB_SET(TT, cmd, FXHCI_TRB_CMD_SET_TR_DQ); /* TRB Type */ + FXHCI_TRB_SET(ID, cmd, slot_id); /* Slot ID */ + FXHCI_TRB_SET(EP, cmd, ep); /* Endpoint ID */ + + cmd->ptr_low = (uintptr)(dq_trb) | dcs; + + FXhciPostCmd(xhci); + + return FXhciWaitForCmd(xhci, cmd, 1); +} diff --git a/bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci_debug.c b/bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci_debug.c new file mode 100644 index 0000000000..027a7f4390 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci_debug.c @@ -0,0 +1,131 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fxhci_debug.c + * Date: 2022-02-11 13:33:12 + * LastEditTime: 2022-02-18 09:12:15 + * Description:  This files is for implementation of XHCI debug utilities + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/2/7 init commit + */ + +#include +#include "fdebug.h" +#include "fxhci_private.h" + +#define FUSB_DEBUG_TAG "FXHCI_DEBUG" +#define FUSB_ERROR(format, ...) FT_DEBUG_PRINT_E(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_WARN(format, ...) FT_DEBUG_PRINT_W(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_INFO(format, ...) FT_DEBUG_PRINT_I(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_DEBUG(format, ...) FT_DEBUG_PRINT_D(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) + +void FXhciDumpSlotCtx(const FXhciSlotCtx *const sc) +{ + FUSB_INFO("Slot Context (@%p): ", sc); + FUSB_INFO(" FIELD1\t0x%08x ", sc->f1); + FUSB_INFO(" FIELD2\t0x%08x ", sc->f2); + FUSB_INFO(" FIELD3\t0x%08x ", sc->f3); + FUSB_INFO(" FIELD4\t0x%08x ", sc->f4); + FXHCI_SC_DUMP(FUSB_INFO, ROUTE, sc); + FXHCI_SC_DUMP(FUSB_INFO, SPEED1, sc); + FXHCI_SC_DUMP(FUSB_INFO, MTT, sc); + FXHCI_SC_DUMP(FUSB_INFO, HUB, sc); + FXHCI_SC_DUMP(FUSB_INFO, CTXENT, sc); + FXHCI_SC_DUMP(FUSB_INFO, RHPORT, sc); + FXHCI_SC_DUMP(FUSB_INFO, NPORTS, sc); + FXHCI_SC_DUMP(FUSB_INFO, TTID, sc); + FXHCI_SC_DUMP(FUSB_INFO, TTPORT, sc); + FXHCI_SC_DUMP(FUSB_INFO, TTT, sc); + FXHCI_SC_DUMP(FUSB_INFO, UADDR, sc); + FXHCI_SC_DUMP(FUSB_INFO, STATE, sc); +} + +void FXhciDumpEpCtx(const FXhciEpCtx *const ec) +{ + FUSB_INFO("Endpoint Context (@%p): ", ec); + FUSB_INFO(" FIELD1\t0x%08x ", ec->f1); + FUSB_INFO(" FIELD2\t0x%08x ", ec->f2); + FUSB_INFO(" TRDQ_L\t0x%08x ", ec->tr_dq_low); + FUSB_INFO(" TRDQ_H\t0x%08x ", ec->tr_dq_high); + FUSB_INFO(" FIELD5\t0x%08x ", ec->f5); + FXHCI_EC_DUMP(FUSB_INFO, STATE, ec); + FXHCI_EC_DUMP(FUSB_INFO, INTVAL, ec); + FXHCI_EC_DUMP(FUSB_INFO, CERR, ec); + FXHCI_EC_DUMP(FUSB_INFO, TYPE, ec); + FXHCI_EC_DUMP(FUSB_INFO, MBS, ec); + FXHCI_EC_DUMP(FUSB_INFO, MPS, ec); + FXHCI_EC_DUMP(FUSB_INFO, DCS, ec); + FXHCI_EC_DUMP(FUSB_INFO, AVRTRB, ec); + FXHCI_EC_DUMP(FUSB_INFO, MXESIT, ec); +} + +void FXhciDumpDevCtx(const FXhciDevCtx *const dc, const u32 ctx_mask) +{ + unsigned int i; + + if (ctx_mask & 1) + FXhciDumpSlotCtx(dc->slot); + + for (i = 1; i <= FXHCI_SC_GET(CTXENT, dc->slot); ++i) + { + if (ctx_mask & (1 << i)) + FXhciDumpEpCtx(dc->ep[i]); + } +} + +void FXhciDumpInputCtx(const FXhciInputCtx *const ic) +{ + FUSB_INFO("Input Control add: 0x%08x ", *ic->add); + FUSB_INFO("Input Control drop: 0x%08x ", *ic->drop); + FXhciDumpDevCtx(&ic->dev, *ic->add); +} + +void FXhciDumpTransferTrb(const FXhciTrb *const cur) +{ + FUSB_INFO("Transfer TRB (@%p): ", cur); + FUSB_INFO(" PTR_L\t0x%08x ", cur->ptr_low); + FUSB_INFO(" PTR_H\t0x%08x ", cur->ptr_high); + FUSB_INFO(" STATUS\t0x%08x ", cur->status); + FUSB_INFO(" CNTRL\t0x%08x ", cur->control); + FXHCI_TRB_DUMP(FUSB_INFO, TL, cur); + FXHCI_TRB_DUMP(FUSB_INFO, TDS, cur); + FXHCI_TRB_DUMP(FUSB_INFO, C, cur); + FXHCI_TRB_DUMP(FUSB_INFO, ISP, cur); + FXHCI_TRB_DUMP(FUSB_INFO, CH, cur); + FXHCI_TRB_DUMP(FUSB_INFO, IOC, cur); + FXHCI_TRB_DUMP(FUSB_INFO, IDT, cur); + FXHCI_TRB_DUMP(FUSB_INFO, TT, cur); + FXHCI_TRB_DUMP(FUSB_INFO, DIR, cur); +} + +static const FXhciTrb *FXhciNextTrb(const FXhciTrb *const cur) +{ + if (FXHCI_TRB_GET(TT, cur) == FXHCI_TRB_LINK) + return (!cur->ptr_low) ? NULL : (void *)(uintptr)(cur->ptr_low); + else + return cur + 1; +} + +void FXhciDumpTransferTrbs(const FXhciTrb *const first, const FXhciTrb *const last) +{ + const FXhciTrb *cur; + for (cur = first; cur; cur = FXhciNextTrb(cur)) + { + FXhciDumpTransferTrb(cur); + if (cur == last) + break; + } +} diff --git a/bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci_dev.c b/bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci_dev.c new file mode 100644 index 0000000000..3d1b868c85 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci_dev.c @@ -0,0 +1,597 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fxhci_dev.c + * Date: 2022-02-11 13:33:12 + * LastEditTime: 2022-02-18 09:12:46 + * Description:  This files is for implementation of XHCI device + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/2/7 init commit + */ + +#include +#include "fsleep.h" +#include "fcache.h" +#include "fdebug.h" + +#include "fxhci_private.h" + +#define FUSB_DEBUG_TAG "FXHCI_DEV" +#define FUSB_ERROR(format, ...) FT_DEBUG_PRINT_E(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_WARN(format, ...) FT_DEBUG_PRINT_W(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_INFO(format, ...) FT_DEBUG_PRINT_I(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_DEBUG(format, ...) FT_DEBUG_PRINT_D(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) + +static u32 FXhciGenRounte(FXhci *const xhci, const int hubport, const int hubaddr) +{ + FASSERT(xhci); + if (!hubaddr) + return 0; + + u32 route_string = FXHCI_SC_GET(ROUTE, xhci->dev[hubaddr].ctx.slot); + int i; + + for (i = 0; i < 20; i += 4) + { + if (!(route_string & (0xf << i))) + { + route_string |= (hubport & 0xf) << i; + break; + } + } + + return route_string; +} + +static int FXhciGetRoothubPort(FXhci *const xhci, const int hubport, const int hubaddr) +{ + FASSERT(xhci); + if (!hubaddr) + return hubport; + + return FXHCI_SC_GET(RHPORT, xhci->dev[hubaddr].ctx.slot); +} + +static int FXhciGetTT(FXhci *const xhci, const FUsbSpeed speed, + const int hubport, const int hubaddr, + int *const tt, int *const tt_port) +{ + FASSERT(xhci); + if (!hubaddr) + return 0; + + const FXhciSlotCtx *const slot = xhci->dev[hubaddr].ctx.slot; + + if ((*tt = FXHCI_SC_GET(TTID, slot))) + { + *tt_port = FXHCI_SC_GET(TTPORT, slot); + } + else if (speed < FUSB_HIGH_SPEED && + FXHCI_SC_GET(SPEED1, slot) - 1 == FUSB_HIGH_SPEED) + { + *tt = hubaddr; + *tt_port = hubport; + } + + return *tt != 0; +} + +static void FXhciReapSlots(FXhci *const xhci, int skip_slot) +{ + FASSERT(xhci); + int i; + FUsb *instance = xhci->usb; + + FUSB_INFO("xHC resource shortage, trying to reap old slots... "); + for (i = 1; i <= xhci->max_slots_en; i++) + { + if (i == skip_slot) + continue; /* don't reap slot we were working on */ + if (xhci->dev[i].transfer_rings[1]) + continue; /* slot still in use */ + if (NULL == xhci->dev[i].ctx.raw) + continue; /* slot already disabled */ + + const FXhciTransCode cc = FXhciCmdDisableSlot(xhci, i); + if (cc != FXHCI_CC_SUCCESS) + FUSB_INFO("Failed to disable slot %d: %d ", i, cc); + else + FUSB_INFO("Successfully reaped slot %d ", i); + xhci->dcbaa[i] = 0; + + FUSB_FREE(instance, xhci->dev[i].ctx.raw); + xhci->dev[i].ctx.raw = NULL; + } +} + +static FXhciInputCtx *FXhciMakeInputCtx(FXhci *xhci, const size_t ctxsize) +{ + int i; + const size_t size = (1 + FXHCI_NUM_EPS) * ctxsize; + FUsb *instance = xhci->usb; + FXhciInputCtx *const ic = FUSB_ALLOCATE(instance, sizeof(*ic), FUSB_DEFAULT_ALIGN); + void *dma_buffer = FUSB_ALLOCATE(instance, size, 64); + + if ((NULL == ic) || (NULL == dma_buffer)) + { + FUSB_FREE(instance, ic); + FUSB_FREE(instance, dma_buffer); + return NULL; + } + + + memset(dma_buffer, 0, size); + ic->drop = dma_buffer + 0; + ic->add = dma_buffer + 4; + dma_buffer += ctxsize; + for (i = 0; i < FXHCI_NUM_EPS; i++, dma_buffer += ctxsize) + ic->dev.ep[i] = dma_buffer; + + return ic; +} + +/** + * @name: FXhciSetAddress + * @msg: 设备USB设备的地址 + * @return {FUsbDev *}, USB设备实例 + * @param {FUsbHc} *controller, USB控制器实例 + * @param {FUsbSpeed} speed, USB设备的速度类型 + * @param {int} hubport, USB设备连接的port号 + * @param {int} hubaddr, USB设备连接的hub地址 + */ +FUsbDev *FXhciSetAddress(FUsbHc *controller, FUsbSpeed speed, int hubport, int hubaddr) +{ + FXhci *const xhci = FXHCI_INST_GET(controller); + FASSERT(xhci); + const size_t ctxsize = FXhciGetCtxSize(&xhci->mmio); + FXhciDevInfo *di = NULL; + FUsbDev *dev = NULL; + FUsb *instance = controller->usb; + int i; + + FXhciInputCtx *const ic = FXhciMakeInputCtx(xhci, ctxsize); + FXhciTransRing *const tr = FUSB_ALLOCATE(instance, sizeof(*tr), FUSB_DEFAULT_ALIGN); + + if (NULL != tr) + { + FASSERT(NULL == tr->ring); + tr->ring = FXHCI_ALIGN(xhci, 16, FXHCI_TRANSFER_RING_SIZE * sizeof(FXhciTrb)); + } + + if ((NULL == ic) || (NULL == tr) || (NULL == tr->ring)) + { + FUSB_INFO("Out of memory "); + goto _free_return; + } + + + int slot_id; + FXhciTransCode cc = FXhciCmdEnableSlot(xhci, &slot_id); + if (cc == FXHCI_CC_NO_SLOTS_AVAILABLE) + { + FXhciReapSlots(xhci, 0); + cc = FXhciCmdEnableSlot(xhci, &slot_id); + } + + if (cc != FXHCI_CC_SUCCESS) + { + FUSB_INFO("Enable slot failed: %d ", cc); + goto _free_return; + } + else + { + FUSB_INFO("Enabled slot %d ", slot_id); + } + + di = &xhci->dev[slot_id]; + void *dma_buffer = FUSB_ALLOCATE(instance, FXHCI_NUM_EPS * ctxsize, 64); + if (NULL == dma_buffer) + goto _disable_return; + + memset(dma_buffer, 0, FXHCI_NUM_EPS * ctxsize); + for (i = 0; i < FXHCI_NUM_EPS; i++, dma_buffer += ctxsize) + di->ctx.ep[i] = dma_buffer; + + *ic->add = (1 << 0) /* Slot Context */ | (1 << 1) /* EP0 Context */; + + FXHCI_SC_SET(ROUTE, ic->dev.slot, FXhciGenRounte(xhci, hubport, hubaddr)); + FXHCI_SC_SET(SPEED1, ic->dev.slot, speed + 1); + FXHCI_SC_SET(CTXENT, ic->dev.slot, 1); /* the endpoint 0 context */ + FXHCI_SC_SET(RHPORT, ic->dev.slot, FXhciGetRoothubPort(xhci, hubport, hubaddr)); + + int tt, tt_port; + if (FXhciGetTT(xhci, speed, hubport, hubaddr, &tt, &tt_port)) + { + FUSB_INFO("TT for %d: %d[%d] ", slot_id, tt, tt_port); + FXHCI_SC_SET(MTT, ic->dev.slot, FXHCI_SC_GET(MTT, xhci->dev[tt].ctx.slot)); + FXHCI_SC_SET(TTID, ic->dev.slot, tt); + FXHCI_SC_SET(TTPORT, ic->dev.slot, tt_port); + } + + di->transfer_rings[1] = tr; + FXhciInitCycleRing(tr, FXHCI_TRANSFER_RING_SIZE); + + ic->dev.ep0->tr_dq_low = (uintptr)(tr->ring); + ic->dev.ep0->tr_dq_high = 0; + FXHCI_EC_SET(TYPE, ic->dev.ep0, FXHCI_EP_CONTROL); + FXHCI_EC_SET(AVRTRB, ic->dev.ep0, 8); + FXHCI_EC_SET(MPS, ic->dev.ep0, FUsbSpeedtoDefaultMaxPacketSz(speed)); + FXHCI_EC_SET(CERR, ic->dev.ep0, 3); + FXHCI_EC_SET(DCS, ic->dev.ep0, 1); + + xhci->dcbaa[slot_id] = (uintptr)(di->ctx.raw); + + FCacheDCacheInvalidateRange((uintptr)ic, sizeof(*ic)); /* flush cache of input address */ + + cc = FXhciCmdAddressDevice(xhci, slot_id, ic); + if (cc == FXHCI_CC_RESOURCE_ERROR) + { + FXhciReapSlots(xhci, slot_id); + cc = FXhciCmdAddressDevice(xhci, slot_id, ic); + } + + if (cc != FXHCI_CC_SUCCESS) + { + FUSB_INFO("Address device failed: %d ", cc); + goto _disable_return; + } + else + { + FUSB_INFO("Addressed device %d (USB: %d) ", + slot_id, FXHCI_SC_GET(UADDR, di->ctx.slot)); + } + + fsleep_millisec(FUSB_SET_ADDRESS_MDELAY); + + dev = FUsbInitDevEntry(controller, slot_id); + if (!dev) + goto _disable_return; + + dev->address = slot_id; + dev->hub = hubaddr; + dev->port = hubport; + dev->speed = speed; + dev->endpoints[0].dev = dev; + dev->endpoints[0].endpoint = 0; + dev->endpoints[0].toggle = 0; + dev->endpoints[0].direction = FUSB_SETUP; + dev->endpoints[0].type = FUSB_CONTROL_EP; + + u8 buf[8]; + if (FUsbGetDescriptor(dev, FUsbGenerateReqType(FUSB_REQ_DEVICE_TO_HOST, FUSB_REQ_TYPE_STANDARD, FUSB_REQ_RECP_DEV), FUSB_DESC_TYPE_DEVICE, 0, buf, sizeof(buf)) != sizeof(buf)) + { + FUSB_INFO("first FUsbGetDescriptor(FUSB_DESC_TYPE_DEVICE) failed "); + goto _disable_return; + } + + dev->endpoints[0].maxpacketsize = FUsbDecodeMaxPacketSz0(speed, buf[7]); + if (dev->endpoints[0].maxpacketsize != FUsbSpeedtoDefaultMaxPacketSz(speed)) + { + memset((void *)ic->dev.ep0, 0x00, ctxsize); + *ic->add = (1 << 1); /* EP0 Context */ + FXHCI_EC_SET(MPS, ic->dev.ep0, dev->endpoints[0].maxpacketsize); + + /* flush cache of input context before send command */ + FCacheDCacheInvalidateRange((uintptr)ic, sizeof(*ic)); + + cc = FXhciCmdEvaluateCtx(xhci, slot_id, ic); + if (cc == FXHCI_CC_RESOURCE_ERROR) + { + FXhciReapSlots(xhci, slot_id); + cc = FXhciCmdEvaluateCtx(xhci, slot_id, ic); + } + if (cc != FXHCI_CC_SUCCESS) + { + FUSB_INFO("Context evaluation failed: %d ", cc); + goto _disable_return; + } + } + + goto _free_ic_return; + +_disable_return: + FXhciCmdDisableSlot(xhci, slot_id); + xhci->dcbaa[slot_id] = 0; + FUsbDetachDev(controller, slot_id); + dev = NULL; +_free_return: + if (tr) + FUSB_FREE(instance, (void *)tr->ring); + FUSB_FREE(instance, tr); + if (di) + { + FUSB_FREE(instance, di->ctx.raw); + di->ctx.raw = 0; + } +_free_ic_return: + if (ic) + { + FUSB_FREE(instance, ic->raw); + FUSB_FREE(instance, ic); + } + return dev; +} + +static int FXhciFinishHubConfig(FUsbDev *const dev, FXhciInputCtx *const ic) +{ + int type = FUsbIsSuperSpeed(dev->speed) ? 0x2a : 0x29; /* similar enough */ + FUsbHubDescriptor desc; + + if (FUsbGetDescriptor(dev, FUsbGenerateReqType(FUSB_REQ_DEVICE_TO_HOST, FUSB_REQ_TYPE_CLASS, FUSB_REQ_RECP_DEV), type, 0, &desc, sizeof(desc)) != sizeof(desc)) + { + FUSB_INFO("Failed to fetch hub descriptor "); + return FXHCI_CC_COMMUNICATION_ERROR; + } + + FXHCI_SC_SET(HUB, ic->dev.slot, 1); + FXHCI_SC_SET(MTT, ic->dev.slot, 0); /* No support for Multi-TT */ + FXHCI_SC_SET(NPORTS, ic->dev.slot, desc.bNbrPorts); + + if (dev->speed == FUSB_HIGH_SPEED) + FXHCI_SC_SET(TTT, ic->dev.slot, + (desc.wHubCharacteristics >> 5) & 0x0003); + + return 0; +} + +static size_t FXhciBoundInterval(const FUsbEndpoint *const ep) +{ + if ((ep->dev->speed == FUSB_LOW_SPEED && + (ep->type == FUSB_ISOCHRONOUS_EP || + ep->type == FUSB_INTERRUPT_EP)) || + (ep->dev->speed == FUSB_FULL_SPEED && + ep->type == FUSB_INTERRUPT_EP)) + { + if (ep->interval < 3) + return 3; + else if (ep->interval > 11) + return 11; + else + return ep->interval; + } + else + { + if (ep->interval < 0) + return 0; + else if (ep->interval > 15) + return 15; + else + return ep->interval; + } +} + +static int FXhciFinishEpConfig(const FUsbEndpoint *const ep, FXhciInputCtx *const ic) +{ + FXhci *const xhci = FXHCI_INST_GET(ep->dev->controller); + FASSERT(xhci); + FUsb *instance = xhci->usb; + const int ep_id = FXhciEpId(ep); + FUSB_INFO("ep_id: %d ", ep_id); + + if (ep_id <= 1 || 32 <= ep_id) + return FXHCI_CC_DRIVER_ERROR; + + FXhciTransRing *const tr = FUSB_ALLOCATE(instance, sizeof(*tr), FUSB_DEFAULT_ALIGN); + if (NULL != tr) + { + FASSERT(NULL == tr->ring); + tr->ring = FXHCI_ALIGN(xhci, 16, FXHCI_TRANSFER_RING_SIZE * sizeof(FXhciTrb)); + } + + if ((NULL == tr) || (NULL == tr->ring)) + { + FUSB_FREE(instance, tr); + FUSB_ERROR("Out of memory "); + return FXHCI_CC_OUT_OF_MEMORY; + } + + xhci->dev[ep->dev->address].transfer_rings[ep_id] = tr; + FXhciInitCycleRing(tr, FXHCI_TRANSFER_RING_SIZE); + + *ic->add |= (1 << ep_id); + if ((int)FXHCI_SC_GET(CTXENT, ic->dev.slot) < ep_id) + FXHCI_SC_SET(CTXENT, ic->dev.slot, ep_id); + + FXhciEpCtx *const epctx = ic->dev.ep[ep_id]; + + FUSB_DEBUG("Filling epctx (@%p) ", epctx); + epctx->tr_dq_low = (uintptr)(tr->ring); + epctx->tr_dq_high = 0; + + FXHCI_EC_SET(INTVAL, epctx, FXhciBoundInterval(ep)); + FXHCI_EC_SET(CERR, epctx, 3); + FXHCI_EC_SET(TYPE, epctx, ep->type | ((ep->direction != FUSB_OUT) << 2)); + FXHCI_EC_SET(MPS, epctx, ep->maxpacketsize); + FXHCI_EC_SET(DCS, epctx, 1); + + size_t avrtrb; + switch (ep->type) + { + case FUSB_BULK_EP: + case FUSB_ISOCHRONOUS_EP: + avrtrb = 3 * 1024; + break; + case FUSB_INTERRUPT_EP: + avrtrb = 1024; + break; + default: + avrtrb = 8; + break; + } + FXHCI_EC_SET(AVRTRB, epctx, avrtrb); + FXHCI_EC_SET(MXESIT, epctx, FXHCI_EC_GET(MPS, epctx) * FXHCI_EC_GET(MBS, epctx)); + + return 0; +} + +/** + * @name: FXhciFinishDevConfig + * @msg: 完成USB设备配置 + * @return {FXhciTransCode} 传输返回码 + * @param {FUsbDev} *dev, USB设备实例 + */ +FXhciTransCode FXhciFinishDevConfig(FUsbDev *const dev) +{ + FXhci *const xhci = FXHCI_INST_GET(dev->controller); + FASSERT(xhci); + FUsb *instance = xhci->usb; + int slot_id = dev->address; + FXhciDevInfo *const di = &xhci->dev[slot_id]; + + int i; + FXhciTransCode ret = FXHCI_CC_ZERO_BYTES; + + FXhciInputCtx *const ic = FXhciMakeInputCtx(xhci, FXhciGetCtxSize(&xhci->mmio)); + if (!ic) + { + FUSB_INFO("Out of memory "); + return FXHCI_CC_OUT_OF_MEMORY; + } + + *ic->add = (1 << 0); /* Slot Context */ + + ic->dev.slot->f1 = di->ctx.slot->f1; + ic->dev.slot->f2 = di->ctx.slot->f2; + ic->dev.slot->f3 = di->ctx.slot->f3; + /* f4 *must* be 0 in the Input Context... yeah, it's weird, I know. */ + + FCacheDCacheInvalidateRange((uintptr)ic, sizeof(*ic)); + + if (dev->descriptor->bDeviceClass == FUSB_HUB_DEVICE) + { + ret = FXhciFinishHubConfig(dev, ic); + if (ret) + goto _free_return; + } + + for (i = 1; i < dev->num_endp; ++i) + { + ret = FXhciFinishEpConfig(&dev->endpoints[i], ic); + if (ret) + goto _free_ep_ctx_return; + } + + const int config_id = dev->configuration->bConfigurationValue; + FUSB_INFO("config_id: %d ", config_id); + FXhciTransCode cc = FXhciCmdConfigureEp(xhci, slot_id, config_id, ic); + + if (cc == FXHCI_CC_RESOURCE_ERROR || cc == FXHCI_CC_BANDWIDTH_ERROR) + { + FXhciReapSlots(xhci, slot_id); + cc = FXhciCmdConfigureEp(xhci, slot_id, config_id, ic); + } + + if (cc != FXHCI_CC_SUCCESS) + { + FUSB_INFO("Configure endpoint failed: %d ", cc); + ret = FXHCI_CC_CONTROLLER_ERROR; + goto _free_ep_ctx_return; + } + else + { + FUSB_INFO("Endpoints configured "); + } + + goto _free_return; + +_free_ep_ctx_return: + for (i = 2; i < 31; ++i) + { + if (di->transfer_rings[i]) + { + FUSB_FREE(instance, (void *)di->transfer_rings[i]->ring); + FUSB_FREE(instance, di->transfer_rings[i]); + } + di->transfer_rings[i] = NULL; + } +_free_return: + if (NULL != ic) + { + FUSB_FREE(instance, ic->raw); + FUSB_FREE(instance, ic); + } + return ret; +} + +/** + * @name: FXhciDestoryDev + * @msg: 删除指定USB设备实例 + * @return {*} + * @param {FUsbHc} *controller, USB控制器实例 + * @param {int} slot_id, USB设备所在的slot号 + */ +void FXhciDestoryDev(FUsbHc *const controller, const int slot_id) +{ + FXhci *const xhci = FXHCI_INST_GET(controller); + FASSERT(xhci); + FUsb *instance = xhci->usb; + + if (slot_id <= 0 || slot_id > xhci->max_slots_en) + return; + + FXhciInputCtx *const ic = FXhciMakeInputCtx(xhci, FXhciGetCtxSize(&xhci->mmio)); + if (NULL == ic) + { + FUSB_WARN("Out of memory, leaking resources! "); + return; + } + + const int num_eps = controller->devices[slot_id]->num_endp; + *ic->add = 0; /* Leave Slot/EP0 state as it is for now. */ + *ic->drop = (1 << num_eps) - 1; /* Drop all endpoints we can. */ + *ic->drop &= ~(1 << 1 | 1 << 0); /* Not allowed to drop EP0 or Slot. */ + + FCacheDCacheInvalidateRange((uintptr)ic, sizeof(*ic)); + + FXhciTransCode cc = FXhciCmdEvaluateCtx(xhci, slot_id, ic); + + if (NULL != ic) + { + FUSB_FREE(instance, ic->raw); + FUSB_FREE(instance, ic); + } + + if (cc != FXHCI_CC_SUCCESS) + FUSB_INFO("Failed to quiesce slot %d: %d ", slot_id, cc); + + cc = FXhciCmdStopEp(xhci, slot_id, FXHCI_EP0_ID); + if (cc != FXHCI_CC_SUCCESS) + FUSB_INFO("Failed to stop EP0 on slot %d: %d ", slot_id, cc); + + int i; + FXhciDevInfo *const di = &xhci->dev[slot_id]; + for (i = 1; i < /*num_eps*/FXHCI_NUM_EPS; ++i) + { + if (di->transfer_rings[i]) + { + FUSB_FREE(instance, (void *)di->transfer_rings[i]->ring); + FUSB_FREE(instance, (void *)di->transfer_rings[i]); + } + FUSB_FREE(instance, di->interrupt_queues[i]); + } + + /* free device context */ + if (NULL != di->ctx.raw) + { + FUSB_FREE(instance, di->ctx.raw); + di->ctx.raw = NULL; + } + + FUSB_INFO("Stopped slot %d, but not disabling it yet. ", slot_id); + di->transfer_rings[1] = NULL; + + return; +} diff --git a/bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci_evt.c b/bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci_evt.c new file mode 100644 index 0000000000..cfa984ed6a --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci_evt.c @@ -0,0 +1,371 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fxhci_evt.c + * Date: 2022-02-11 13:33:12 + * LastEditTime: 2022-02-18 09:13:09 + * Description:  This files is for implementation of XHCI event + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/2/7 init commit + */ + +#include "fsleep.h" +#include "fdebug.h" + +#include "fxhci_private.h" + +#define FUSB_DEBUG_TAG "FXHCI-EVT" +#define FUSB_ERROR(format, ...) FT_DEBUG_PRINT_E(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_WARN(format, ...) FT_DEBUG_PRINT_W(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_INFO(format, ...) FT_DEBUG_PRINT_I(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_DEBUG(format, ...) FT_DEBUG_PRINT_D(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) + +/** + * @name: FXhciResetEvtRing + * @msg: 重置Event TRB ring + * @return {*} + * @param {FXhciEvtRing} *er, Event TRB ring实例 + */ +void FXhciResetEvtRing(FXhciEvtRing *const er) +{ + int i; + for (i = 0; i < FXHCI_EVENT_RING_SIZE; ++i) + er->ring[i].control &= ~FXHCI_TRB_CYCLE; + er->cur = er->ring; + er->last = er->ring + FXHCI_EVENT_RING_SIZE; + er->ccs = 1; + er->adv = 1; +} + +static inline int FXhciEvtReady(const FXhciEvtRing *const er) +{ + return (er->cur->control & FXHCI_TRB_CYCLE) == er->ccs; +} + +void FXhciUpdateEvtDQ(FXhci *const xhci) +{ + if (xhci->er.adv) + { + FUSB_DEBUG("Updating dq ptr: @0x%lx -> %p", + FXhciReadRt64(&xhci->mmio, 0, FXHCI_REG_RT_IR_ERDP), + xhci->er.cur); + FXhciWriteRt64(&xhci->mmio, 0, FXHCI_REG_RT_IR_ERDP, FXHCI_REG_RT_IR_ERDP_MASK & ((u64)(uintptr)xhci->er.cur)); + xhci->er.adv = 0; + } +} + +void FXhciAdvanceEvtRing(FXhci *const xhci) +{ + xhci->er.cur++; + xhci->er.adv = 1; + if (xhci->er.cur == xhci->er.last) + { + FUSB_DEBUG("Roll over in event ring "); + xhci->er.cur = xhci->er.ring; + xhci->er.ccs ^= 1; + FXhciUpdateEvtDQ(xhci); + } +} + +static void FXhciHandleTransferEvt(FXhci *const xhci) +{ + const FXhciTrb *const ev = xhci->er.cur; + + const FXhciTransCode cc = FXHCI_TRB_GET(CC, ev); /* Completion Code */ + const int id = FXHCI_TRB_GET(ID, ev); + const int ep = FXHCI_TRB_GET(EP, ev); + + FXhciIntrQ *intrq; + + if (id && id <= xhci->max_slots_en && + (intrq = xhci->dev[id].interrupt_queues[ep])) + { + /* It's a running interrupt endpoint */ + intrq->ready = (void *)(uintptr)(ev->ptr_low); + if (cc == FXHCI_CC_SUCCESS || cc == FXHCI_CC_SHORT_PACKET) + { + FXHCI_TRB_SET(TL, intrq->ready, + intrq->size - FXHCI_TRB_GET(EVTL, ev)); /* Transfer Length */ + } + else + { + FUSB_INFO("Interrupt Transfer failed: %d ", cc); + FXHCI_TRB_SET(TL, intrq->ready, 0); /* Transfer Length */ + } + } + else if (cc == FXHCI_CC_STOPPED || cc == FXHCI_CC_STOPPED_LENGTH_INVALID) + { + /* Ignore 'Forced Stop Events' */ + } + else + { + FUSB_WARN("Warning: \r\n" + "Spurious transfer event for ID %d, EP %d: \r\n" + " Pointer: 0x%08x%08x \r\n" + " TL: 0x%06x \r\n" + " CC: %d ", + id, ep, + ev->ptr_high, ev->ptr_low, + FXHCI_TRB_GET(EVTL, ev), cc); + } + + FXhciAdvanceEvtRing(xhci); +} + +static void FXhciHandleCmdCompletionEvt(FXhci *const xhci) +{ + const FXhciTrb *const ev = xhci->er.cur; + + FUSB_INFO("Warning: Spurious command completion event: \r\n" + " Pointer: 0x%08x%08x \r\n" + " CC: %d \r\n" + " Slot ID: %d \r\n" + " Cycle: %d ", + ev->ptr_high, ev->ptr_low, + FXHCI_TRB_GET(CC, ev), FXHCI_TRB_GET(ID, ev), ev->control & FXHCI_TRB_CYCLE); + FXhciAdvanceEvtRing(xhci); +} + +static void FXhciHandleHostCtrlEvt(FXhci *const xhci) +{ + const FXhciTrb *const ev = xhci->er.cur; + + const FXhciTransCode cc = FXHCI_TRB_GET(CC, ev); + switch (cc) + { + case FXHCI_CC_EVENT_RING_FULL_ERROR: + FUSB_INFO("Event ring full! (@%p) ", xhci->er.cur); + /* + * If we get here, we have processed the whole queue: + * xHC pushes this event, when it sees the ring full, + * full of other events. + * IMO it's save and necessary to update the dequeue + * pointer here. + */ + FXhciAdvanceEvtRing(xhci); + FXhciUpdateEvtDQ(xhci); + break; + default: + FUSB_INFO("Warning: Spurious host controller event: %d ", cc); + FXhciAdvanceEvtRing(xhci); + break; + } +} + +/* handle standard types: + * - command completion event + * - port status change event + * - transfer event + * - host controller event + */ +static void FXhciHandleEvt(FXhci *const xhci) +{ + const FXhciTrb *const ev = xhci->er.cur; + + const int trb_type = FXHCI_TRB_GET(TT, ev); + switch (trb_type) + { + /* Either pass along the event or advance event ring */ + case FXHCI_TRB_EV_TRANSFER: + FXhciHandleTransferEvt(xhci); + break; + case FXHCI_TRB_EV_CMD_CMPL: + FXhciHandleCmdCompletionEvt(xhci); + break; + case FXHCI_TRB_EV_PORTSC: + FUSB_INFO("Port Status Change Event for %d: %d ", + FXHCI_TRB_GET(PORT, ev), FXHCI_TRB_GET(CC, ev)); + /* We ignore the event as we look for the PORTSC + registers instead, at a time when it suits _us_. */ + FXhciAdvanceEvtRing(xhci); + break; + case FXHCI_TRB_EV_HOST: + FXhciHandleHostCtrlEvt(xhci); + break; + default: + FUSB_INFO("Warning: Spurious event: %d, Completion Code: %d ", + trb_type, FXHCI_TRB_GET(CC, ev)); + FXhciAdvanceEvtRing(xhci); + break; + } +} + +void FXhciHandleEvts(FXhci *const xhci) +{ + while (FXhciEvtReady(&xhci->er)) + FXhciHandleEvt(xhci); + + FXhciUpdateEvtDQ(xhci); + return; +} + +static unsigned long FXhciWaitForEvt(const FXhciEvtRing *const er, + unsigned long *const timeout_us) +{ + while (!FXhciEvtReady(er) && *timeout_us) + { + --*timeout_us; + fsleep_microsec(1); + } + return *timeout_us; +} + +static unsigned long FXhciWaitForEvtType(FXhci *const xhci, + const int trb_type, + unsigned long *const timeout_us) +{ + while (FXhciWaitForEvt(&xhci->er, timeout_us)) + { + if (FXHCI_TRB_GET(TT, xhci->er.cur) == (unsigned int)trb_type) + break; + + FXhciHandleEvt(xhci); + } + + return *timeout_us; +} + +/* + * Ref. xHCI Specification Revision 1.2, May 2019. + * Section 4.6.1.2. + * + * Process events from xHCI Abort command. + * + * Returns FXHCI_CC_COMMAND_RING_STOPPED on success and FXHCI_CC_TIMEOUT on failure. + */ + +FXhciTransCode FXhciWaitForCmdAborted(FXhci *const xhci, const FXhciTrb *const address) +{ + /* + * Specification says that something might be seriously wrong, if + * we don't get a response after 5s. Still, let the caller decide, + * what to do then. + */ + unsigned long timeout_us = FUSB_USB_MAX_PROCESSING_TIME_US; /* 5s */ + FXhciTransCode cc = FXHCI_CC_TIMEOUT; + /* + * Expects two command completion events: + * The first with CC == COMMAND_ABORTED should point to address + * (not present if command was not running), + * the second with CC == COMMAND_RING_STOPPED should point to new dq. + */ + while (FXhciWaitForEvtType(xhci, FXHCI_TRB_EV_CMD_CMPL, &timeout_us)) + { + if ((xhci->er.cur->ptr_low == (uintptr)(address)) && + (xhci->er.cur->ptr_high == 0)) + { + cc = FXHCI_TRB_GET(CC, xhci->er.cur); + FXhciAdvanceEvtRing(xhci); + break; + } + + FXhciHandleCmdCompletionEvt(xhci); + } + if (timeout_us == 0) + { + FUSB_INFO("Warning: Timed out waiting for " + "COMMAND_ABORTED or COMMAND_RING_STOPPED. "); + goto update_and_return; + } + if (cc == FXHCI_CC_COMMAND_RING_STOPPED) + { + /* There may not have been a command to abort. */ + goto update_and_return; + } + + timeout_us = FUSB_USB_MAX_PROCESSING_TIME_US; /* 5s */ + while (FXhciWaitForEvtType(xhci, FXHCI_TRB_EV_CMD_CMPL, &timeout_us)) + { + if (FXHCI_TRB_GET(CC, xhci->er.cur) == FXHCI_CC_COMMAND_RING_STOPPED) + { + cc = FXHCI_CC_COMMAND_RING_STOPPED; + FXhciAdvanceEvtRing(xhci); + break; + } + + FXhciHandleCmdCompletionEvt(xhci); + } + if (timeout_us == 0) + FUSB_INFO("Warning: Timed out " + "waiting for COMMAND_RING_STOPPED. "); + +update_and_return: + FXhciUpdateEvtDQ(xhci); + return cc; +} + +/* + * returns cc of command in question (pointed to by `address`) + * caller should abort command if cc is FXHCI_CC_TIMEOUT + */ +FXhciTransCode FXhciWaitForCmdDone(FXhci *const xhci, + const FXhciTrb *const address, + const int clear_event) +{ + unsigned long timeout_us = FUSB_USB_MAX_PROCESSING_TIME_US; /* 5s */ + FXhciTransCode cc = FXHCI_CC_TIMEOUT; + + while (FXhciWaitForEvtType(xhci, FXHCI_TRB_EV_CMD_CMPL, &timeout_us)) + { + if ((xhci->er.cur->ptr_low == (uintptr)(address)) && + (xhci->er.cur->ptr_high == 0)) + { + cc = FXHCI_TRB_GET(CC, xhci->er.cur); + break; + } + + FXhciHandleCmdCompletionEvt(xhci); + } + + if (!timeout_us) + { + FUSB_INFO("Warning: Timed out waiting for FXHCI_TRB_EV_CMD_CMPL. "); + } + else if (clear_event) + { + FXhciAdvanceEvtRing(xhci); + } + + FXhciUpdateEvtDQ(xhci); + return cc; +} + +/* returns amount of bytes transferred on success, negative CC on error */ +FXhciTransCode FXhciWaitForTransfer(FXhci *const xhci, const int slot_id, const int ep_id) +{ + /* 5s for all types of transfers */ + unsigned long timeout_us = FUSB_USB_MAX_PROCESSING_TIME_US; + FXhciTransCode ret = FXHCI_CC_TIMEOUT; + while (FXhciWaitForEvtType(xhci, FXHCI_TRB_EV_TRANSFER, &timeout_us)) + { + if (FXHCI_TRB_GET(ID, xhci->er.cur) == (unsigned int)slot_id && + FXHCI_TRB_GET(EP, xhci->er.cur) == (unsigned int)ep_id) + { + ret = -FXHCI_TRB_GET(CC, xhci->er.cur); + if (ret == -FXHCI_CC_SUCCESS || ret == -FXHCI_CC_SHORT_PACKET) + ret = FXHCI_TRB_GET(EVTL, xhci->er.cur); + FXhciAdvanceEvtRing(xhci); + break; + } + + FXhciHandleTransferEvt(xhci); + } + if (!timeout_us) + FUSB_INFO("Warning: Timed out waiting for FXHCI_TRB_EV_TRANSFER. "); + + FXhciUpdateEvtDQ(xhci); + return ret; +} diff --git a/bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci_hw.c b/bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci_hw.c new file mode 100644 index 0000000000..3af4ad4968 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci_hw.c @@ -0,0 +1,217 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fxhci_hw.c + * Date: 2022-02-11 13:33:12 + * LastEditTime: 2022-02-18 09:13:30 + * Description:  This files is for implementation of XHCI register functions + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/2/7 init commit + */ + +/***************************** Include Files *********************************/ +#include "fdebug.h" +#include "fgeneric_timer.h" +#include "fsleep.h" + +#include "fxhci_private.h" + + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FUSB_DEBUG_TAG "FXHCI_HW" +#define FUSB_ERROR(format, ...) FT_DEBUG_PRINT_E(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_WARN(format, ...) FT_DEBUG_PRINT_W(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_INFO(format, ...) FT_DEBUG_PRINT_I(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_DEBUG(format, ...) FT_DEBUG_PRINT_D(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + +/*****************************************************************************/ +FError FXhciSetupMMIO(FXhciMMIO *mmio, uintptr base_addr) +{ + FASSERT(mmio); + u32 reg_val; + + mmio->base = base_addr; + + /* add to register base to find the beginning of the Operational Register Space */ + mmio->oper_base = mmio->base + FtIn8(mmio->base + FXHCI_REG_CAP_CAPLENGTH); + mmio->doorbell_base = mmio->base + FXHCI_REG_CAP_DBOFF_GET(FtIn32(mmio->base + FXHCI_REG_CAP_DBOFF)); + mmio->runtime_base = mmio->base + FXHCI_REG_CAP_RTSOFF_GET(FtIn32(mmio->base + FXHCI_REG_CAP_RTSOFF)); + mmio->port_base = mmio->oper_base + FXHCI_REG_OP_PORTS_BASE; + + /* cache static information of CAP_HCSPARAMS */ + mmio->hcx_params[0] = FtIn32(mmio->base + FXHCI_REG_CAP_HCSPARAMS1); + mmio->hcx_params[1] = FtIn32(mmio->base + FXHCI_REG_CAP_HCSPARAMS2); + mmio->hcx_params[2] = FtIn32(mmio->base + FXHCI_REG_CAP_HCSPARAMS3); + mmio->hcx_params[3] = FtIn32(mmio->base + FXHCI_REG_CAP_HCCPARAMS); + + reg_val = mmio->hcx_params[3]; + mmio->xecp_base = mmio->base + (FXHCI_REG_CAP_HCCPARAMS_XECP_GET(reg_val) << 2); + + FUSB_DEBUG(" mmio base: 0x%x", mmio->base); + FUSB_DEBUG(" oper base: 0x%x", mmio->oper_base); + FUSB_DEBUG(" doorbell base: 0x%x", mmio->doorbell_base); + FUSB_DEBUG(" runtime base: 0x%x", mmio->runtime_base); + FUSB_DEBUG(" port base: 0x%x", mmio->port_base); + + return FUSB_SUCCESS; +} + +static void FXhciParseExtCap(FXhciMMIO *mmio, const uintptr offset, const u32 cap_id) +{ + FASSERT(mmio); + FASSERT(mmio->base != 0); + u32 reg_val; + u32 major_ver, minor_ver; + u32 psic; /* Protocol Speed ID (PSI) */ + + switch (cap_id) + { + case FXHCI_EXT_CAP_ID_USB_LEGACY_SUPPORT: + reg_val = FXhciReadExtCap32(mmio, offset + FXHCI_REG_EXT_CAP_USBLEGSUP_OFFSET); + FUSB_INFO(" BIOS owned %d OS owned %d", + FXHCI_USBLEGSUP_BIOS_OWNED_SEMAPHORE & reg_val, + FXHCI_USBLEGSUP_OS_OWNED_SEMAPHORE & reg_val); + + reg_val = FXhciReadExtCap32(mmio, offset + FXHCI_REG_EXT_CAP_USBLEGCTLSTS_OFFSET); + FUSB_INFO(" SMI ctrl/status 0x%x", reg_val); + break; + case FXHCI_EXT_CAP_ID_SUPPORT_PROTOCOL: + reg_val = FXhciReadExtCap32(mmio, offset + FXHCI_REG_EXT_CAP_USBSPCFDEF_OFFSET); + FUSB_INFO(" Name: %c%c%c%c", + *((char *)®_val), *((char *)®_val + 1), + *((char *)®_val + 2), *((char *)®_val + 3)); + + reg_val = FXhciReadExtCap32(mmio, offset + FXHCI_REG_EXT_CAP_USBSPCF_OFFSET); + major_ver = FXHCI_USBSPCF_MAJOR_REVERSION_GET(reg_val); + minor_ver = FXHCI_USBSPCF_MINOR_REVERSION_GET(reg_val); + FUSB_INFO(" Version: %d.%d", major_ver, minor_ver); + + reg_val = FXhciReadExtCap32(mmio, offset + FXHCI_REG_EXT_CAP_USBSPCFDEF2_OFFSET); + FUSB_INFO(" Compatible ports: [%d-%d]", + FXHCI_USBSPCFDEF2_COMPATIBLE_PORT_OFF_GET(reg_val), + FXHCI_USBSPCFDEF2_COMPATIBLE_PORT_OFF_GET(reg_val) + FXHCI_USBSPCFDEF2_COMPATIBLE_PORT_CNT_GET(reg_val) - 1); + + if (FXHCI_MAJOR_REVERSION_USB2 == major_ver) + { + mmio->usb2_ports.port_beg = FXHCI_USBSPCFDEF2_COMPATIBLE_PORT_OFF_GET(reg_val); + mmio->usb2_ports.port_end = FXHCI_USBSPCFDEF2_COMPATIBLE_PORT_OFF_GET(reg_val) + FXHCI_USBSPCFDEF2_COMPATIBLE_PORT_CNT_GET(reg_val) - 1; + + FUSB_INFO(" High-speed only: %d, Integrated hub: %d, Hardware LMP: %d", + FXHCI_USBSPCFDEF2_USB2_HIGH_SPEED_ONLY & reg_val, + FXHCI_USBSPCFDEF2_USB2_INTERGRATED_HUB & reg_val, + FXHCI_USBSPCFDEF2_USB2_HW_LMP_CAP & reg_val); + } + else if (FXHCI_MAJOR_REVERSION_USB3 == major_ver) + { + mmio->usb3_ports.port_beg = FXHCI_USBSPCFDEF2_COMPATIBLE_PORT_OFF_GET(reg_val); + mmio->usb3_ports.port_end = FXHCI_USBSPCFDEF2_COMPATIBLE_PORT_OFF_GET(reg_val) + FXHCI_USBSPCFDEF2_COMPATIBLE_PORT_CNT_GET(reg_val) - 1; + } + + psic = FXHCI_USBSPCFDEF2_PROTOCOL_SPEED_ID_CNT_GET(reg_val); + FUSB_INFO(" PSIC: 0x%x", psic); + + if (0 != psic) + { + reg_val = FXhciReadExtCap32(mmio, offset + FXHCI_REG_PROTOCOL_SPEED_ID_OFFSET(psic)); + + FUSB_INFO(" Protocol speed-id: %d^%d", + FXHCI_PROTOCOL_SPEED_ID_VALUE_GET(reg_val), + FXHCI_PROTOCOL_SPEED_ID_EXPONENT_GET(reg_val)); + FUSB_INFO(" PSI type: %d, PSI full-duplex: %d, Mantissa: 0x%x", + FXHCI_PROTOCOL_SPEED_ID_PSI_TYPE_GET(reg_val), + (FXHCI_PROTOCOL_SPEED_ID_PSI_FULL_DUPLEX & reg_val == FXHCI_PROTOCOL_SPEED_ID_PSI_FULL_DUPLEX), + FXHCI_PROTOCOL_SPEED_ID_MANTISSA_GET(reg_val)); + } + else + { + if (FXHCI_MAJOR_REVERSION_USB3 == major_ver) + FUSB_INFO("For USB3, only the default SuperSpeed bit rate is supported !!!"); + else if (FXHCI_MAJOR_REVERSION_USB2 == major_ver) + FUSB_INFO("For USB2, default Full-speed, Low-speed and High-speed bit rate supported !!!"); + } + + break; + case FXHCI_EXT_CAP_ID_USB_DEBUG_CAPABILITY: + + break; + default: + FUSB_WARN("Unhandled extend capabilities %d", cap_id); + break; + } + + return; +} + +void FXhciListExtCap(FXhciMMIO *mmio) +{ + FASSERT(mmio); + FASSERT(mmio->base != 0); + + uintptr next_ext_cap_offset = 0; + uintptr ext_cap_offset = 0; + u32 cap_id = 0; + u32 reg_val; + + FUSB_INFO("Extended capabilities"); + do + { + reg_val = FXhciReadExtCap32(mmio, ext_cap_offset); + next_ext_cap_offset = (FXHCI_REG_EXT_CAP_NEXT_CAP_PTR_GET(reg_val) << 2); + cap_id = FXHCI_REG_EXT_CAP_CAP_ID_GET(reg_val); + FXhciParseExtCap(mmio, ext_cap_offset, cap_id); + + FUSB_INFO("==== Capability ID: %d, Next Capability Pointer: 0x%x", + cap_id, next_ext_cap_offset); + ext_cap_offset += next_ext_cap_offset; + } + while (0 != next_ext_cap_offset); + + return; +} + +FError FXhciWaitOper32(FXhciMMIO *mmio, u32 offset, u32 mask, u32 exp_val, u32 timeout_tick) +{ + FASSERT(mmio); + FASSERT(mmio->base != 0); + FError ret = FUSB_ERR_WAIT_TIMEOUT; + u32 tick = 0U; + + do + { + if ((FXhciReadOper32(mmio, offset) & mask) == exp_val) + { + ret = FUSB_SUCCESS; + break; + } + fsleep_millisec(10); + } + while (tick++ < timeout_tick); + + if (FUSB_SUCCESS != ret) + { + FUSB_ERROR("wait status 0x%x timeout, current 0x%x, tick: %ld", exp_val, mask, tick); + } + + return ret; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci_hw.h b/bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci_hw.h new file mode 100644 index 0000000000..6c780e6a96 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci_hw.h @@ -0,0 +1,589 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fxhci_hw.h + * Date: 2022-02-11 13:33:12 + * LastEditTime: 2022-02-18 09:13:47 + * Description:  This files is for definition of XHCI hardware register interface + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/2/7 init commit + */ + +#ifndef DRIVERS_USB_FXHCI_HW_H +#define DRIVERS_USB_FXHCI_HW_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ +#include "fio.h" +#include "fassert.h" +#include "fkernel.h" + + +/************************** Constant Definitions *****************************/ +/** @name Register Map + * + * Register offsets from the base address of an SD device. + * @{ + */ +/***************** eXtensible Host Controller Capability Registers ***********************/ +#define FXHCI_REG_CAP_CAPLENGTH 0x00 /* specify the limits, restrictions and capabilities */ +#define FXHCI_REG_CAP_HCIVERSION 0x02 /* Interface Version Number */ +#define FXHCI_REG_CAP_HCSPARAMS1 0x04 /* Host Controller Structural Parameters 1 */ +#define FXHCI_REG_CAP_HCSPARAMS2 0x08 /* Host Controller Structural Parameters 2 */ +#define FXHCI_REG_CAP_HCSPARAMS3 0x0C /* Host Controller Structural Parameters 3 */ +#define FXHCI_REG_CAP_HCCPARAMS 0x10 /* Capability Parameters 1 */ +#define FXHCI_REG_CAP_DBOFF 0x14 /* Doorbell Offset Register */ +#define FXHCI_REG_CAP_RTSOFF 0x18 /* Runtime Register Space Offset Register */ + +/***************** Host Controller Operational Registers ***********************/ +#define FXHCI_REG_OP_USBCMD 0x00 /* USB Command Register */ +#define FXHCI_REG_OP_USBSTS 0x04 /* USB Status Register */ +#define FXHCI_REG_OP_PAGESIZE 0x08 /* Page Size Register */ +#define FXHCI_REG_OP_DNCTRL 0x14 /* Device Notification Control Register */ +#define FXHCI_REG_OP_CRCR 0x18 /* Command Ring Control Register */ +#define FXHCI_REG_OP_DCBAAP 0x30 /* Device Context Base Address Array Pointer Register */ +#define FXHCI_REG_OP_CONFIG 0x38 /* Configure Register */ + +/* Port Status and Ctrl Register : OP Base + (400h + (10h * (n–1))) 'n' is port num */ +#define FXHCI_REG_OP_PORTS_BASE 0x400 /* Port Status and Control Register Base */ +#define FXHCI_REG_OP_PORTS_SIZE 0x10 /* Size of one Port SC Register */ +#define FXHCI_REG_OP_PORTS_PORTSC 0x00 /* Port Status and Control Register */ +#define FXHCI_REG_OP_PORTS_PORTPMSC 0x04 /* USB3 Port Power Management Status and Control Register */ +#define FXHCI_REG_OP_PORTS_PORTLI 0x08 /* Port Link Info Register */ + +/***************** Host Controller Runtime Registers ***********************/ +#define FXHCI_REG_RT_MFINDEX 0x00 /* Microframe Index */ +#define FXHCI_REG_RT_IR0 0x20 /* Interrupter Register Set 0 */ +#define FXHCI_REG_RT_IR1023 0x8000 /* Interrupter Register Set 1023 */ + +/* Interrupter Register Set : RT Base + 020h + (32 * Interrupter) */ +#define FXHCI_REG_RT_IR_SIZE 0x20 /* Size of one IR Register */ +#define FXHCI_REG_RT_IR_IMAN 0x00 /* Interrupter Management Register */ +#define FXHCI_REG_RT_IR_IMOD 0x04 /* Interrupter Moderation Register */ +#define FXHCI_REG_RT_IR_ERSTSZ 0x08 /* Event Ring Segment Table Size Register */ +#define FXHCI_REG_RT_IR_ERSTBA 0x10 /* Event Ring Segment Table Base Address Register */ +#define FXHCI_REG_RT_IR_ERDP 0x18 /* Event Ring Dequeue Pointer Register */ + +/***************** Doorbell Register ***********************/ +#define FXHCI_REG_DB_SIZE 4 /* Doorbell registers are 32 bits in length */ + +/***************** eXtensible Host Controller Capability Registers ***********************/ + +/** @name FXHCI_REG_CAP_HCSPARAMS1 Register + */ +#define FXHCI_REG_CAP_HCSPARAMS1_MAX_SLOTS_GET(x) FUSB_REG32_GET_BITS(x, 7, 0) /* Number of Device Slots (MaxSlots) */ +#define FXHCI_REG_CAP_HCSPARAMS1_MAX_INTRS_GET(x) FUSB_REG32_GET_BITS(x, 18, 8) /* Number of Interrupters (MaxIntrs) */ +#define FXHCI_REG_CAP_HCSPARAMS1_MAX_PORTS_GET(x) FUSB_REG32_GET_BITS(x, 31, 24) /* Number of Ports (MaxPorts) */ + +/** @name FXHCI_REG_CAP_HCSPARAMS2 Register + */ +#define FXHCI_REG_CAP_HCSPARAMS2_IST_GET(x) FUSB_REG32_GET_BITS(x, 3, 0) /* Isochronous Scheduling Threshold (IST) */ +#define FXHCI_REG_CAP_HCSPARAMS2_ERST_MAX_GET(x) FUSB_REG32_GET_BITS(x, 7, 4) /* Event Ring Segment Table Max (ERST Max) */ +#define FXHCI_REG_CAP_HCSPARAMS2_SPR (1 << 26) /* Scratchpad Restore (SPR) */ +#define FXHCI_REG_CAP_HCSPARAMS2_MAX_SCRATCHPAD_BUFS_GET(x) FUSB_REG32_GET_BITS(x, 31, 27) /* Max Scratchpad Buffers (Max Scratchpad Bufs) */ + +/** @name FXHCI_REG_CAP_HCSPARAMS3 Register + */ +#define FXHCI_REG_CAP_HCSPARAMS3_U1_DEV_EXIT_LATENCY_GET(x) FUSB_REG32_GET_BITS(x, 7, 0) /* U1 Device Exit Latency */ +#define FXHCI_REG_CAP_HCSPARAMS3_U2_DEV_EXIT_LATENCY_GET(x) FUSB_REG32_GET_BITS(x, 31, 16) /* U2 Device Exit Latency */ + +/** @name FXHCI_REG_CAP_HCCPARAMS Register + */ +#define FXHCI_REG_CAP_HCCPARAMS_AC64 (1 << 0) /* 64-bit Addressing Capabilitya 1: 64-bit */ +#define FXHCI_REG_CAP_HCCPARAMS_BNC (1 << 1) /* BW Negotiation Capability (BNC) 1: support */ +#define FXHCI_REG_CAP_HCCPARAMS_CSZ (1 << 2) /* Context Size (CSZ) 1: 64 byte context data */ +#define FXHCI_REG_CAP_HCCPARAMS_PPC (1 << 3) /* Port Power Control (PPC) 1: support */ +#define FXHCI_REG_CAP_HCCPARAMS_PIND (1 << 4) /* Port Indicators (PIND) 1: support */ +#define FXHCI_REG_CAP_HCCPARAMS_LHRC (1 << 5) /* Light HC Reset Capability (LHRC) 1: support */ +#define FXHCI_REG_CAP_HCCPARAMS_LTC (1 << 6) /* Latency Tolerance Messaging Capability (LTC) */ +#define FXHCI_REG_CAP_HCCPARAMS_NSS (1 << 7) /* No Secondary SID Support (NSS) */ +#define FXHCI_REG_CAP_HCCPARAMS_MAX_PSA_SIZE_GET(x) FUSB_REG32_GET_BITS(x, 15, 12) /* Maximum Primary Stream Array Size (MaxPSASize) */ +#define FXHCI_REG_CAP_HCCPARAMS_XECP_GET(x) FUSB_REG32_GET_BITS(x, 31, 16) /* xHCI Extended Capabilities Pointer (xECP) */ + +/** @name FXHCI_REG_CAP_DBOFF Register + */ +#define FXHCI_REG_CAP_DBOFF_GET(x) ((x) & GENMASK(31, 2)) /* 32-byte offset of the Doorbell Array base address from the Base */ + +/** @name FXHCI_REG_CAP_RTSOFF Register + */ +#define FXHCI_REG_CAP_RTSOFF_GET(x) ((x) & GENMASK(31, 5)) /* 32-byte offset of the xHCI Runtime Registers */ + +/***************** Host Controller Operational Registers ***********************/ + +/** @name FXHCI_REG_OP_USBCMD Register + */ +#define FXHCI_REG_OP_USBCMD_RUN_STOP (1 << 0) /* Run/Stop (R/S) 1: RUN, 0: STOP - RW */ +#define FXHCI_REG_OP_USBCMD_HCRST (1 << 1) /* Host Controller Reset (HCRST) 1: RESET - RW */ +#define FXHCI_REG_OP_USBCMD_INTE (1 << 2) /* Interrupter Enable (INTE) 1: enabled - RW */ +#define FXHCI_REG_OP_USBCMD_HSEE (1 << 3) /* Host System Error Enable (HSEE) - RW */ +#define FXHCI_REG_OP_USBCMD_LHCRST (1 << 7) /* Light Host Controller Reset (LHCRST) - RW */ +#define FXHCI_REG_OP_USBCMD_CSS (1 << 8) /* Controller Save State (CSS) - RW */ +#define FXHCI_REG_OP_USBCMD_CRS (1 << 9) /* Controller Restore State (CRS) - RW */ +#define FXHCI_REG_OP_USBCMD_EWE (1 << 10) /* Enable Wrap Event (EWE) - RW */ +#define FXHCI_REG_OP_USBCMD_EU3S (1 << 11) /* Enable U3 MFINDEX Stop (EU3S) - RW */ + +/** @name FXHCI_REG_OP_USBSTS Register + */ +#define FXHCI_REG_OP_USBSTS_HCH (1 << 0) /* 1: Stopped executing */ +#define FXHCI_REG_OP_USBSTS_HSE (1 << 2) /* 1: Serious error detected */ +#define FXHCI_REG_OP_USBSTS_EINT (1 << 3) /* 1: Interrupt Pending (IP) */ +#define FXHCI_REG_OP_USBSTS_PCD (1 << 4) /* 1: Port Change Detect */ +#define FXHCI_REG_OP_USBSTS_SSS (1 << 8) /* remain 1 while the xHC saves its internal state */ +#define FXHCI_REG_OP_USBSTS_RSS (1 << 9) /* remain 1 while the xHC restores its internal state */ +#define FXHCI_REG_OP_USBSTS_SRE (1 << 10) /* if error occurs during a Save or Restore operation this bit shall be set to ‘1’. */ +#define FXHCI_REG_OP_USBSTS_CNR (1 << 11) /* 1: Controller Not Ready */ +#define FXHCI_REG_OP_USBSTS_HCE (1 << 12) /* 1: Internal xHC error condition */ +#define FXHCI_REG_OP_USBSTS_PRSRV_MASK ((1 << 1) | 0xffffe000) /* Rsvd bits */ + +/** @name FXHCI_REG_OP_PAGESIZE Register + */ +/* This xHC supports a page size of 2^(n+12) if bit n is Set */ +#define FXHCI_REG_OP_PAGESIZE_4K (1 << 0) /* if bit 0 is Set, the xHC supports 4k byte page sizes */ + +/** @name FXHCI_REG_OP_CRCR Register + */ +#define FXHCI_REG_OP_CRCR_RCS (1 << 0) /* Ring Cycle State, value of the xHC Consumer Cycle State (CCS) flag */ +#define FXHCI_REG_OP_CRCR_CS (1 << 1) /* Command Stop, 1 */ +#define FXHCI_REG_OP_CRCR_CA (1 << 2) /* Command Abort, 1 */ +#define FXHCI_REG_OP_CRCR_CRR (1 << 3) /* Command Ring Running */ +#define FXHCI_REG_OP_CRCR_CR_PTR_MASK GENMASK_ULL(63, 6) /* Command Ring Pointer, Dequeue Ptr of Command Ring */ + +/** @name FXHCI_REG_OP_DCBAAP Register + */ +#define FXHCI_REG_OP_DCBAAP_MASK GENMASK_ULL(63, 6) /* bit[31:6] Ptr of DCBAA */ + +/** @name FXHCI_REG_OP_CONFIG Register + */ +#define FXHCI_REG_OP_CONFIG_MAX_SLOTS_EN_MASK GENMASK(7, 0) /* Max Device Slots Enabled (MaxSlotsEn) – RW */ +#define FXHCI_REG_OP_CONFIG_MAX_SLOTS_EN_SET(x) FUSB_REG32_SET_BITS(x, 7, 0) /* bit[7:0] Max Device Slots Enabled */ +#define FXHCI_REG_OP_CONFIG_MAX_SLOTS_EN_GET(x) FUSB_REG32_GET_BITS(x, 7, 0) + +/** @name FXHCI_REG_OP_PORTS_PORTSC Register + */ +#define FXHCI_REG_OP_PORTS_PORTSC_CCS (1 << 0) /* Current Connect Status (CCS) – ROS */ +#define FXHCI_REG_OP_PORTS_PORTSC_PED (1 << 1) /* Port Enabled/Disabled (PED) – RW1CS */ +#define FXHCI_REG_OP_PORTS_PORTSC_OCA (1 << 3) /* Over-current Active (OCA) – RO */ +#define FXHCI_REG_OP_PORTS_PORTSC_PR (1 << 4) /* Port Reset (PR) – RW1S */ +#define FXHCI_REG_OP_PORTS_PORTSC_PLS_GET(x) FUSB_REG32_GET_BITS(x, 8, 5) /* Port Link State (PLS) – RWS */ +#define FXHCI_REG_OP_PORTS_PORTSC_PLS_SET(x) FUSB_REG32_SET_BITS(x, 8, 5) +#define FXHCI_REG_OP_PORTS_PORTSC_PLS_MASK GENMASK(8, 5) +#define FXHCI_REG_OP_PORTS_PORTSC_PLS(x) (x << 5) +/* Read value of Port Link State (PLS) */ +/* refer to FXHCI doc page-408 for details (Port Link State) */ +enum +{ + FXHCI_LINK_STATE_U0 = 0, /* U0 State */ + FXHCI_LINK_STATE_U1 = 1, /* U1 State */ + FXHCI_LINK_STATE_U2 = 2, /* U2 State */ + FXHCI_LINK_STATE_U3 = 3, /* U3 State (Device Suspended) */ + FXHCI_LINK_STATE_DISABLED = 4, /* Disabled State */ + FXHCI_LINK_STATE_RX_DETECT = 5, /* RxDetect State (Disconnected) */ + FXHCI_LINK_STATE_INACTIVE = 6, /* Inactive State */ + FXHCI_LINK_STATE_POLLING = 7, /* Polling State */ + FXHCI_LINK_STATE_RECOVERY = 8, /* Recovery State */ + FXHCI_LINK_STATE_HOT_RESET = 9, /* Hot Reset State */ + FXHCI_LINK_STATE_COMPLIANCE_MODE = 10, /* Compliance Mode State */ + FXHCI_LINK_STATE_TEST_MODE = 11, /* Test Mode State */ + FXHCI_LINK_STATE_RESUME = 15, /* Resume State */ + + FXHCI_LINK_STATE_MAX +}; + +#define FXHCI_REG_OP_PORTS_PORTSC_PLS_SET(x) FUSB_REG32_SET_BITS(x, 8, 5) + +#define FXHCI_REG_OP_PORTS_PORTSC_PP (1 << 9) /* Port Power (PP) – RWS */ +#define FXHCI_REG_OP_PORTS_PORTSC_PORT_SPEED_GET(x) FUSB_REG32_GET_BITS(x, 13, 10) /* Port Speed (Port Speed) – ROS */ +/* Protocol Speed ID (PSI) 1~15 */ +enum +{ + FXHCI_PORT_SPEED_UNDEFINED = 0, + FXHCI_PORT_SPEED_1 = 1, + FXHCI_PORT_SPEED_15 = 15, +}; + + +#define FXHCI_REG_OP_PORTS_PORTSC_PIC_SET(x) FUSB_REG32_SET_BITS(x, 15, 14) +#define FXHCI_REG_OP_PORTS_PORTSC_PIC_MASK GENMASK(15, 14) +enum +{ + FXHCI_PORT_INDICATOR_OFF = 0, + FXHCI_PORT_INDICATOR_AMBER = 1, + FXHCI_PORT_INDICATOR_GREEN = 2, + FXHCI_PORT_INDICATOR_UNDEFINED = 3 +}; + +#define FXHCI_REG_OP_PORTS_PORTSC_LWS (1 << 16) /* Port Link State Write Strobe (LWS) */ +#define FXHCI_REG_OP_PORTS_PORTSC_CSC (1 << 17) /* Connect Status Change (CSC) */ +#define FXHCI_REG_OP_PORTS_PORTSC_PEC (1 << 18) /* Port Enabled/Disabled Change (PEC) 1: clear PED */ +#define FXHCI_REG_OP_PORTS_PORTSC_WRC (1 << 19) /* Warm Port Reset Change 1: Warm Reset complete */ +#define FXHCI_REG_OP_PORTS_PORTSC_OCC (1 << 20) /* Over-current Change 1: Over-current Active */ +#define FXHCI_REG_OP_PORTS_PORTSC_PRC (1 << 21) /* Port Reset Change 1: Transition of Port Reset */ +#define FXHCI_REG_OP_PORTS_PORTSC_PLC (1 << 22) /* Port Link State Change 1: PLS transition */ +#define FXHCI_REG_OP_PORTS_PORTSC_CEC (1 << 23) /* Port Config Error Change 1: Port Config Error detected */ +#define FXHCI_REG_OP_PORTS_PORTSC_CAS (1 << 24) /* Cold Attach Status 1: Far-end Receiver Terminations were detected */ +#define FXHCI_REG_OP_PORTS_PORTSC_WCE (1 << 25) /* Wake on Connect Enable 1: enable port to be sensitive to device connects */ +#define FXHCI_REG_OP_PORTS_PORTSC_WDE (1 << 26) /* Wake on Disconnect Enable 1: enable port to be sensitive to device disconnects */ +#define FXHCI_REG_OP_PORTS_PORTSC_WOE (1 << 27) /* Wake on Over-current Enable 1: enable port to be sensitive to over-current conditions */ +#define FXHCI_REG_OP_PORTS_PORTSC_DR (1 << 30) /* Device Removable, 0: Device is removable. 1: Device is non-removable */ +#define FXHCI_REG_OP_PORTS_PORTSC_WPR (1 << 31) /* Warm Port Reset 1: follow Warm Reset sequence */ +#define FXHCI_REG_OP_PORTS_PORTSC_RW_MASK (FXHCI_REG_OP_PORTS_PORTSC_PR | FXHCI_REG_OP_PORTS_PORTSC_PLS_MASK | FXHCI_REG_OP_PORTS_PORTSC_PP \ + | FXHCI_REG_OP_PORTS_PORTSC_PIC_MASK | FXHCI_REG_OP_PORTS_PORTSC_LWS | FXHCI_REG_OP_PORTS_PORTSC_WCE \ + | FXHCI_REG_OP_PORTS_PORTSC_WDE | FXHCI_REG_OP_PORTS_PORTSC_WOE) + +/***************** Host Controller Runtime Registers ***********************/ + +/** @name FXHCI_REG_RT_IR_IMAN Register + */ +#define FXHCI_REG_RT_IR_IMAN_IP (1 << 0) /* Interrupt Pending, 1: an interrupt is pending for this Interrupter */ +#define FXHCI_REG_RT_IR_IMAN_IE (1 << 1) /* Interrupt Enable, 1: capable of generating an interrupt. */ + +/** @name FXHCI_REG_RT_IR_IMOD Register + */ +#define FXHCI_REG_RT_IR_IMOD_IMODI_MASK GENMASK(15, 0) /* bit[15:0] Interrupt Moderation Interval default 4000 ==> 1ms */ +#define FXHCI_REG_RT_IR_IMOD_IMODC_MASK GENMASK(31, 16) /* bit[31:16] Interrupt Moderation Counter(Down counter) */ + +/** @name FXHCI_REG_RT_IR_ERSTSZ Register + */ +#define FXHCI_REG_RT_IR_ERSTSZ_MASK GENMASK(15, 0) /* bit[15:0] the number of valid Event Ring Segment Table entries */ + +/** @name FXHCI_REG_RT_IR_ERSTBA Register + */ +#define FXHCI_REG_RT_IR_ERSTBA_MASK GENMASK_ULL(63, 6) /* Event Ring Segment Table Base Address */ + +/** @name FXHCI_REG_RT_IR_ERDP Register + */ +#define FXHCI_REG_RT_IR_ERDP_DESI_MASK GENMASK_ULL(2, 0) /* bit[2:0] Dequeue ERST Segment Index */ +#define FXHCI_REG_RT_IR_ERDP_EHB (1 << 3) /* Event Handler Busy */ +#define FXHCI_REG_RT_IR_ERDP_MASK GENMASK_ULL(63, 4) /* Event Ring Dequeue Pointer */ + +/***************** Doorbell Register ***********************/ +#define FXHCI_REG_DB_TARGET_HC_COMMAND 0 /* Host Controller Doorbell (0) Command Doorbell */ +#define FXHCI_REG_DB_TARGET_EP0 1 /* Device Context Doorbells Control EP 0 Enqueue Pointer Update */ +#define FXHCI_REG_DB_TARGET_EP1_OUT 2 /* EP 1 OUT Enqueue Pointer Update */ +#define FXHCI_REG_DB_TARGET_EP1_IN 3 /* EP 1 IN Enqueue Pointer Update */ +#define FXHCI_REG_DB_TARGET_EP15_OUT 30 /* EP 15 OUT Enqueue Pointer Update */ +#define FXHCI_REG_DB_TARGET_EP15_IN 31 /* EP 15 IN Enqueue Pointer Update */ + +/***************** xHCI Extended Capabilities Registers ***********************/ +#define FXHCI_REG_EXT_CAP_CAP_ID_GET(x) FUSB_REG32_GET_BITS(x, 7, 0) +/* refer to 'Table 138: xHCI Extended Capability Codes' for more details */ +enum +{ + FXHCI_EXT_CAP_ID_USB_LEGACY_SUPPORT = 1, + FXHCI_EXT_CAP_ID_SUPPORT_PROTOCOL = 2, + FXHCI_EXT_CAP_ID_EXTEND_POWER_MANAGEMENT = 3, + FXHCI_EXT_CAP_ID_IO_VIRTUALIZATION = 4, + FXHCI_EXT_CAP_ID_MESSAGE_INTERRUPT = 5, + FXHCI_EXT_CAP_ID_LOCAL_MEMORY = 6, + FXHCI_EXT_CAP_ID_USB_DEBUG_CAPABILITY = 10, + FXHCI_EXT_CAP_ID_EXT_MESSAGE_INTERRUPT = 17, + + FXHCI_EXT_CAP_ID_VENDOR_DEFINED_MIN = 192, + FXHCI_EXT_CAP_ID_VENDOR_DEFINED_MAX = 255 +}; + +#define FXHCI_REG_EXT_CAP_NEXT_CAP_PTR_GET(x) FUSB_REG32_GET_BITS(x, 15, 8) +#define FXHCI_REG_EXT_CAP_CAP_SPEC_GET(x) FUSB_REG32_GET_BITS(x, 31, 16) +/* Ext capabilities specific definitions */ +/* USB Legacy Support Capability */ +#define FXHCI_REG_EXT_CAP_USBLEGSUP_OFFSET 0x0 /* used by pre-OS software (BIOS) and the operating system to coordinate ownership of the xHC. */ +#define FXHCI_USBLEGSUP_BIOS_OWNED_SEMAPHORE (1 << 16) /* RW, The BIOS sets this bit to establish ownership of the xHC */ +#define FXHCI_USBLEGSUP_OS_OWNED_SEMAPHORE (1 << 24) /* RW, System software sets this bit to request ownership of the xHC */ + +#define FXHCI_REG_EXT_CAP_USBLEGCTLSTS_OFFSET 0x4 /* uses this register to enable System Management Interrupts (SMIs) for every xHCI/USB event it needs to track */ +#define FXHCI_USBLEGCTLSTS_USB_SMI_EN (1 << 0) /* RW, enable interrupts to trach event */ +#define FXHCI_USBLEGCTLSTS_SMI_HC_ERR_EN (1 << 4) /* RW */ +#define FXHCI_USBLEGCTLSTS_SMI_OS_OWE_EN (1 << 13) /* RW */ +#define FXHCI_USBLEGCTLSTS_SMI_PCI_CMD_EN (1 << 14) /* RW */ +#define FXHCI_USBLEGCTLSTS_SMI_BAR_EN (1 << 15) /* RW */ +#define FXHCI_USBLEGCTLSTS_SMI_EVT_INTERRUPT (1 << 16) /* RO */ +#define FXHCI_USBLEGCTLSTS_SMI_HC_SYS_ERR (1 << 20) /* RO */ +#define FXHCI_USBLEGCTLSTS_SMI_OS_OWN_CHG (1 << 29) /* RW1C */ +#define FXHCI_USBLEGCTLSTS_SMI_PCI_CMD (1 << 30) /* RW1C */ +#define FXHCI_USBLEGCTLSTS_SMI_BAR (1 << 31) /* RW1C */ + +/* xHCI Supported Protocol Capability */ +#define FXHCI_REG_EXT_CAP_USBSPCF_OFFSET 0x0 +#define FXHCI_USBSPCF_MINOR_REVERSION_GET(x) FUSB_REG32_GET_BITS(x, 23, 16) +#define FXHCI_USBSPCF_MAJOR_REVERSION_GET(x) FUSB_REG32_GET_BITS(x, 31, 24) +enum +{ + FXHCI_MAJOR_REVERSION_USB2 = 2, + FXHCI_MAJOR_REVERSION_USB3 = 3 +}; + +#define FXHCI_REG_EXT_CAP_USBSPCFDEF_OFFSET 0x4 +#define FXHCI_USBSPCFDEF_NAME_STRING_GET(x) FUSB_REG32_GET_BITS(x, 31, 0) /* four ASCII characters may be defined */ +#define FXHCI_USBSPCFDEF_NAME_STRING_USB 0x20425355 /* ASCII = "USB" */ + +#define FXHCI_REG_EXT_CAP_USBSPCFDEF2_OFFSET 0x8 +#define FXHCI_USBSPCFDEF2_COMPATIBLE_PORT_OFF_GET(x) FUSB_REG32_GET_BITS(x, 7, 0) +#define FXHCI_USBSPCFDEF2_COMPATIBLE_PORT_CNT_GET(x) FUSB_REG32_GET_BITS(x, 15, 8) +#define FXHCI_USBSPCFDEF2_PROTOCOL_DEFINED_GET(x) FUSB_REG32_GET_BITS(x, 27, 16) +/* USB3 - No Protocol Defined fields */ +/* USB2 */ +#define FXHCI_USBSPCFDEF2_USB2_HIGH_SPEED_ONLY (1 << 17) /* High-speed Only (HSO) - RO */ +#define FXHCI_USBSPCFDEF2_USB2_INTERGRATED_HUB (1 << 18) /* Integrated Hub Implemented (IHI) - RO */ +#define FXHCI_USBSPCFDEF2_USB2_HW_LMP_CAP (1 << 19) /* Hardware LMP Capability (HLC) - RO */ + +#define FXHCI_USBSPCFDEF2_PROTOCOL_SPEED_ID_CNT_GET(x) FUSB_REG32_GET_BITS(x, 31, 28) + +/* Protocol Speed ID (PSI) */ +#define FXHCI_REG_PROTOCOL_SPEED_ID_OFFSET(psic) (0xc + ((psic) * sizeof(u32))) +#define FXHCI_PROTOCOL_SPEED_ID_VALUE_GET(x) FUSB_REG32_GET_BITS(x, 3, 0) /* Protocol Speed ID Value (PSIV) */ +#define FXHCI_PROTOCOL_SPEED_ID_EXPONENT_GET(x) FUSB_REG32_GET_BITS(x, 5, 4) /* Protocol Speed ID Exponent (PSIE) */ +#define FXHCI_PROTOCOL_SPEED_ID_PSI_TYPE_GET(x) FUSB_REG32_GET_BITS(x, 7, 6) /* PSI Type (PLT) */ +enum +{ + FXHCI_PROTOCOL_SPEED_ID_PSI_SYMMETRIC = 0, + FXHCI_PROTOCOL_SPEED_ID_PSI_ASYMMETRIC_RX = 2, + FXHCI_PROTOCOL_SPEED_ID_PSI_ASYMMETRIC_TX = 3 +}; +#define FXHCI_PROTOCOL_SPEED_ID_PSI_FULL_DUPLEX (1 << 8) +#define FXHCI_PROTOCOL_SPEED_ID_MANTISSA_GET(x) FUSB_REG32_GET_BITS(x, 31, 16) /* Protocol Speed ID Mantissa (PSIM) */ + +/**************************** Type Definitions *******************************/ + +/* Device Context Base Address Array */ +#define FXHCI_SCRATCHPAD_BUF_ARRAY_BASE_ADDR_MASK GENMASK_ULL(63, 6) /* Array Element 0 Field Bit */ +#define FXHCI_DEVICE_CONTEXT_BASE_ADDR_MASK GENMASK_ULL(63, 6) /* Array Element 1-n Field Bit */ + +/* Slot index */ +#define FXHCI_REG_DB_HOST_CONTROLLER 0 +#define FXHCI_REG_DB_DEVICE_CONTEXT1 1 +#define FXHCI_REG_DB_DEVICE_CONTEXT255 255 + +typedef struct +{ + u8 port_beg; + u8 port_end; +} FXhciPortRange; + +typedef enum +{ + FXHCI_USB2_COMPATIBLE_PORT, + FXHCI_USB3_COMPATIBLE_PORT, + + FXHCI_NONE_COMPATIBLE_PORT +} FXhciPortCompatible; + +typedef struct +{ + uintptr base; /* Capability registers offset */ + uintptr oper_base; /* Operational registers offset */ + uintptr doorbell_base; /* Doorbell registers offset */ + uintptr runtime_base; /* Runtime registers offset */ + uintptr port_base; /* Port register set offset */ + uintptr xecp_base; /* xHCI Extended Capabilities register offset */ + u32 hcx_params[4]; /* Capability cache */ + FXhciPortRange usb2_ports; + FXhciPortRange usb3_ports; +} FXhciMMIO; + + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +static inline FXhciPortCompatible FUsb3CheckPortCompatible(FXhciMMIO *mmio, u8 port_id) +{ + FASSERT(mmio); + if ((port_id >= mmio->usb3_ports.port_beg) && (port_id <= mmio->usb3_ports.port_end)) + { + return FXHCI_USB3_COMPATIBLE_PORT; + } + else if ((port_id >= mmio->usb2_ports.port_beg) && (port_id <= mmio->usb2_ports.port_end)) + { + return FXHCI_USB2_COMPATIBLE_PORT; + } + + FASSERT(0); /* must not reach there */ + return FXHCI_NONE_COMPATIBLE_PORT; +} + +static inline u32 FXhciReadCap(const FXhciMMIO *mmio) +{ + FASSERT(mmio); + FASSERT(mmio->base != 0); + + return FtIn32(mmio->base + FXHCI_REG_CAP_CAPLENGTH); +} + +static inline u8 FXhciReadCaplen(const FXhciMMIO *mmio) +{ + FASSERT(mmio); + FASSERT(mmio->base != 0); + u32 reg_val = FXhciReadCap(mmio); + + /* get lower 8-bits */ + return (u8)(reg_val & 0xff); +} + +static inline u16 FXhciReadHcVersion(const FXhciMMIO *mmio) +{ + FASSERT(mmio); + FASSERT(mmio->base != 0); + u32 reg_val = FXhciReadCap(mmio); + + /* get upper 16 bits */ + return (u16)((reg_val >> 16) & 0xffff); +} + +static inline u32 FXhciReadCap32(const FXhciMMIO *mmio, u32 offset) +{ + FASSERT(mmio); + FASSERT(mmio->base != 0); + + offset -= FXHCI_REG_CAP_HCSPARAMS1; + offset /= 4; + + FASSERT(offset < 4); + return mmio->hcx_params[offset]; /* read caps from cache */ +} + +static inline u32 FXhciReadOper32(const FXhciMMIO *mmio, u32 offset) +{ + FASSERT(mmio); + FASSERT(mmio->base != 0); + + return FtIn32(mmio->oper_base + offset); +} + +static inline u64 FXhciReadOper64(const FXhciMMIO *mmio, u32 offset) +{ + FASSERT(mmio); + FASSERT(mmio->base != 0); + + uintptr addr = mmio->oper_base + offset; + + u64 val = FtIn32(addr); + val |= ((u64)FtIn32(addr + 4)) << 32; + + return val; +} + +static inline void FXhciWriteOper32(const FXhciMMIO *mmio, u32 offset, u32 val) +{ + FASSERT(mmio); + FASSERT(mmio->base != 0); + + FtOut32(mmio->oper_base + offset, val); +} + +static inline void FXhciWriteOper64(const FXhciMMIO *mmio, u32 offset, u64 val) +{ + FASSERT(mmio); + FASSERT(mmio->base != 0); + + FXhciWriteOper32(mmio, offset, LOWER_32_BITS(val)); + FXhciWriteOper32(mmio, offset + 4, UPPER_32_BITS(val)); +} + +static inline u32 FXhciReadPort32(const FXhciMMIO *mmio, u32 port, u32 offset) +{ + FASSERT(mmio); + FASSERT(mmio->base != 0); + + /* Operational Base + (400h + (10h * (n–1))) */ + return FtIn32(mmio->port_base + port * FXHCI_REG_OP_PORTS_SIZE + offset); +} + +static inline void FXhciWritePort32(const FXhciMMIO *mmio, u32 port, u32 offset, u32 val) +{ + FASSERT(mmio); + FASSERT(mmio->base != 0); + + FtOut32(mmio->port_base + port * FXHCI_REG_OP_PORTS_SIZE + offset, val); +} + +static inline u32 FXhciReadRt32(const FXhciMMIO *mmio, u32 interrupt, u32 offset) +{ + FASSERT(mmio); + FASSERT(mmio->base != 0); + + /* Runtime Base + 020h + (32 * Interrupter) */ + return FtIn32(mmio->runtime_base + FXHCI_REG_RT_IR0 + interrupt * FXHCI_REG_RT_IR_SIZE + offset); +} + +static inline u64 FXhciReadRt64(const FXhciMMIO *mmio, u32 interrupt, u32 offset) +{ + FASSERT(mmio); + FASSERT(mmio->base != 0); + + uintptr addr = mmio->runtime_base + FXHCI_REG_RT_IR0 + interrupt * FXHCI_REG_RT_IR_SIZE + offset; + u64 reg_val = FtIn32(addr); + reg_val |= (u64)FtIn32(addr + 4) << 32; + + return reg_val; +} + +static inline void FXhciWriteRt32(const FXhciMMIO *mmio, u32 interrupt, u32 offset, u32 val) +{ + FASSERT(mmio); + FASSERT(mmio->base != 0); + + FtOut32(mmio->runtime_base + FXHCI_REG_RT_IR0 + interrupt * FXHCI_REG_RT_IR_SIZE + offset, val); +} + +static inline void FXhciWriteRt64(const FXhciMMIO *mmio, u32 interrupt, u32 offset, u64 val) +{ + FASSERT(mmio); + FASSERT(mmio->base != 0); + + FXhciWriteRt32(mmio, interrupt, offset, (u32)val); + FXhciWriteRt32(mmio, interrupt, offset + 4, (u32)(val >> 32)); +} + +static inline u32 FXhciReadExtCap32(const FXhciMMIO *mmio, u32 offset) +{ + FASSERT(mmio); + FASSERT(mmio->base != 0); + FASSERT(mmio->xecp_base != 0); + + return FtIn32(mmio->xecp_base + offset); +} + +static inline void FXhciWriteDb32(const FXhciMMIO *mmio, u32 slot, u32 val) +{ + FASSERT(mmio); + FASSERT(mmio->base != 0); + + FtOut32(mmio->doorbell_base + slot * FXHCI_REG_DB_SIZE, val); +} + +static inline int FXhciGetCtxSize(const FXhciMMIO *mmio) +{ + FASSERT(mmio); + FASSERT(mmio->base != 0); + + u32 reg_val = FXhciReadCap32(mmio, FXHCI_REG_CAP_HCCPARAMS); + return ((reg_val & FXHCI_REG_CAP_HCCPARAMS_CSZ) == FXHCI_REG_CAP_HCCPARAMS_CSZ) ? 64 : 32; +} + +/************************** Function Prototypes ******************************/ +FError FXhciSetupMMIO(FXhciMMIO *mmio, uintptr base_addr); +void FXhciListExtCap(FXhciMMIO *mmio); +FError FXhciWaitOper32(FXhciMMIO *mmio, u32 offset, u32 mask, u32 exp_val, u32 timeout_tick); + +#ifdef __cplusplus +} +#endif + + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci_private.h b/bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci_private.h new file mode 100644 index 0000000000..818b5f2520 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci_private.h @@ -0,0 +1,475 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fxhci_private.h + * Date: 2022-02-11 13:33:12 + * LastEditTime: 2022-02-18 09:16:44 + * Description:  This files is for definition of XHCI internal function + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/2/7 init commit + */ + +#ifndef DRIVERS_USB_FXHCI_PRIVATE_H +#define DRIVERS_USB_FXHCI_PRIVATE_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/***************************** Include Files *********************************/ +#include "fusb_private.h" +#include "fxhci_hw.h" +#include "fxhci.h" + +/************************** Constant Definitions *****************************/ +typedef enum +{ + FXHCI_TRB_NORMAL = 1, + FXHCI_TRB_SETUP_STAGE = 2, + FXHCI_TRB_DATA_STAGE = 3, + FXHCI_TRB_STATUS_STAGE = 4, + FXHCI_TRB_LINK = 6, + FXHCI_TRB_EVENT_DATA = 7, + FXHCI_TRB_CMD_ENABLE_SLOT = 9, + FXHCI_TRB_CMD_DISABLE_SLOT = 10, + FXHCI_TRB_CMD_ADDRESS_DEV = 11, + FXHCI_TRB_CMD_CONFIGURE_EP = 12, + FXHCI_TRB_CMD_EVAL_CTX = 13, + FXHCI_TRB_CMD_RESET_EP = 14, + FXHCI_TRB_CMD_STOP_EP = 15, + FXHCI_TRB_CMD_SET_TR_DQ = 16, + FXHCI_TRB_CMD_NOOP = 23, + FXHCI_TRB_EV_TRANSFER = 32, + FXHCI_TRB_EV_CMD_CMPL = 33, + FXHCI_TRB_EV_PORTSC = 34, + FXHCI_TRB_EV_HOST = 37, +} FXhciTrbType; + +enum +{ + FXHCI_TRB_TRT_NO_DATA = 0, + FXHCI_TRB_TRT_OUT_DATA = 2, + FXHCI_TRB_TRT_IN_DATA = 3 +}; + +enum +{ + FXHCI_TRB_DIR_OUT = 0, + FXHCI_TRB_DIR_IN = 1 +}; + +#define FXHCI_TRB_MAX_TD_SIZE 0x1F /* bits 21:17 of TD Size in TRB */ +#define FXHCI_DMA_SIZE (64 * 1024) +#define FXHCI_TIMEOUT 1000000 + +enum +{ + FXHCI_EP_ISOC_OUT = 1, + FXHCI_EP_BULK_OUT = 2, + FXHCI_EP_INTR_OUT = 3, + FXHCI_EP_CONTROL = 4, + FXHCI_EP_ISOC_IN = 5, + FXHCI_EP_BULK_IN = 6, + FXHCI_EP_INTR_IN = 7 +}; + +/**************************** Type Definitions *******************************/ +typedef volatile struct +{ + u32 ptr_low; + u32 ptr_high; + u32 status; + u32 control; +} FXhciTrb; + +typedef struct +{ + FXhciTrb *ring; + FXhciTrb *cur; + FXhciTrb *last; + u8 ccs; + u8 adv; +} FXhciEvtRing; + +typedef struct +{ + FXhciTrb *ring; + FXhciTrb *cur; + u8 pcs; +} __attribute__((packed)) FXhciTransRing; + +typedef FXhciTransRing FXhciCmdRing; + +typedef volatile struct +{ + u32 f1; + u32 f2; + u32 f3; + u32 f4; + u32 rsvd[4]; +} FXhciSlotCtx; + +typedef volatile struct +{ + u32 f1; + u32 f2; + u32 tr_dq_low; + u32 tr_dq_high; + u32 f5; + u32 rsvd[3]; +} FXhciEpCtx; + +typedef union +{ + /* set of pointers, so we can dynamically adjust Slot/EP context size */ + struct + { + union + { + FXhciSlotCtx *slot; + void *raw; /* Pointer to the whole dev context. */ + }; + FXhciEpCtx *ep0; + FXhciEpCtx *eps1_30[FXHCI_NUM_EPS - 2]; + }; + FXhciEpCtx *ep[FXHCI_NUM_EPS]; /* At index 0 it's actually the slotctx, + we have it like that so we can use + the ep_id directly as index. */ +} FXhciDevCtx; + +typedef struct +{ + union + { + /* The drop flags are located at the start of the */ + u32 *drop; /* structure, so a pointer to them is equivalent */ + void *raw; /* to a pointer to the whole (raw) input context. */ + }; + u32 *add; + FXhciDevCtx dev; +} FXhciInputCtx; + +typedef struct +{ + u32 seg_base_lo; + u32 seg_base_hi; + u32 seg_size; + u32 rsvd; +} FXhciErstEntry; + +typedef struct +{ + size_t size; /* Size of each transfer */ + size_t count; /* The number of TRBs to fill at once */ + FXhciTrb *next; /* The next TRB expected to be processed by the controller */ + FXhciTrb *ready; /* The last TRB in the transfer ring processed by the controller */ + FUsbEndpoint *ep; +} FXhciIntrQ; + +typedef struct +{ + FXhciDevCtx ctx; + FXhciTransRing *transfer_rings[FXHCI_NUM_EPS]; + FXhciIntrQ *interrupt_queues[FXHCI_NUM_EPS]; +} FXhciDevInfo; + +typedef enum +{ + FXHCI_DMA_SET_ADDR = 0, + FXHCI_DMA_INPUT_CTX, + FXHCI_DMA_TRANS_RING, + FXHCI_DMA_TRANS_RING_TRB, + + FXHCI_MAX_DMA_TYPE +} FXhciDMAType; + +typedef struct +{ + FXhciDMAType type; +#define FXHCI_MAX_DMA_ENTRY 10 + void *entries[FXHCI_MAX_DMA_ENTRY]; + uintptr count; +} FXhciDMABuffer; + +typedef struct +{ + /* R/W, volatile, Memory -> bitfields allowed */ + u64 *dcbaa; /* pointers to sp_ptrs and output (device) contexts */ + u64 *sp_ptrs; /* pointers to scratchpad buffers */ + + FXhciCmdRing cr; + FXhciEvtRing er; + volatile FXhciErstEntry *ev_ring_table; + + FUsbDev *roothub; + + u8 max_slots_en; + FXhciDevInfo *dev; /* array of devinfos by slot_id */ + FUsb *usb; + FXhciMMIO mmio; +} FXhci; +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ +#define FXHCI_MASK(startbit, lenbit) (((1<<(lenbit))-1)<<(startbit)) + +/* shorcut to access TRB */ +#define FXHCI_TRB_PORT_FIELD ptr_low /* Pointer field of Port TRB */ +#define FXHCI_TRB_PORT_START 24 +#define FXHCI_TRB_PORT_LEN 8 +#define FXHCI_TRB_TL_FIELD status /* TL - Transfer Length */ +#define FXHCI_TRB_TL_START 0 +#define FXHCI_TRB_TL_LEN 17 +#define FXHCI_TRB_EVTL_FIELD status /* EVTL - (Event TRB) Transfer Length */ +#define FXHCI_TRB_EVTL_START 0 +#define FXHCI_TRB_EVTL_LEN 24 +#define FXHCI_TRB_TDS_FIELD status /* TDS - TD Size */ +#define FXHCI_TRB_TDS_START 17 +#define FXHCI_TRB_TDS_LEN 5 +#define FXHCI_TRB_CC_FIELD status /* CC - Completion Code */ +#define FXHCI_TRB_CC_START 24 +#define FXHCI_TRB_CC_LEN 8 +#define FXHCI_TRB_C_FIELD control /* C - Cycle Bit */ +#define FXHCI_TRB_C_START 0 +#define FXHCI_TRB_C_LEN 1 +#define FXHCI_TRB_TC_FIELD control /* TC - Toggle Cycle */ +#define FXHCI_TRB_TC_START 1 +#define FXHCI_TRB_TC_LEN 1 +#define FXHCI_TRB_ENT_FIELD control /* ENT - Evaluate Next TRB */ +#define FXHCI_TRB_ENT_START 1 +#define FXHCI_TRB_ENT_LEN 1 +#define FXHCI_TRB_ISP_FIELD control /* ISP - Interrupt-on Short Packet */ +#define FXHCI_TRB_ISP_START 2 +#define FXHCI_TRB_ISP_LEN 1 +#define FXHCI_TRB_CH_FIELD control /* CH - Chain Bit */ +#define FXHCI_TRB_CH_START 4 +#define FXHCI_TRB_CH_LEN 1 +#define FXHCI_TRB_IOC_FIELD control /* IOC - Interrupt On Completion */ +#define FXHCI_TRB_IOC_START 5 +#define FXHCI_TRB_IOC_LEN 1 +#define FXHCI_TRB_IDT_FIELD control /* IDT - Immediate Data */ +#define FXHCI_TRB_IDT_START 6 +#define FXHCI_TRB_IDT_LEN 1 +#define FXHCI_TRB_DC_FIELD control /* DC - Deconfigure */ +#define FXHCI_TRB_DC_START 9 +#define FXHCI_TRB_DC_LEN 1 +#define FXHCI_TRB_TT_FIELD control /* TT - TRB Type */ +#define FXHCI_TRB_TT_START 10 +#define FXHCI_TRB_TT_LEN 6 +#define FXHCI_TRB_TRT_FIELD control /* TRT - Transfer Type */ +#define FXHCI_TRB_TRT_START 16 +#define FXHCI_TRB_TRT_LEN 2 +#define FXHCI_TRB_DIR_FIELD control /* DIR - Direction */ +#define FXHCI_TRB_DIR_START 16 +#define FXHCI_TRB_DIR_LEN 1 +#define FXHCI_TRB_EP_FIELD control /* EP - Endpoint ID */ +#define FXHCI_TRB_EP_START 16 +#define FXHCI_TRB_EP_LEN 5 +#define FXHCI_TRB_ID_FIELD control /* ID - Slot ID */ +#define FXHCI_TRB_ID_START 24 +#define FXHCI_TRB_ID_LEN 8 +#define FXHCI_TRB_MASK(tok) FXHCI_MASK(FXHCI_TRB_##tok##_START, FXHCI_TRB_##tok##_LEN) +#define FXHCI_TRB_GET(tok, trb) (((trb)->FXHCI_TRB_##tok##_FIELD & FXHCI_TRB_MASK(tok)) \ + >> FXHCI_TRB_##tok##_START) +#define FXHCI_TRB_SET(tok, trb, to) (trb)->FXHCI_TRB_##tok##_FIELD = \ + (((trb)->FXHCI_TRB_##tok##_FIELD & ~FXHCI_TRB_MASK(tok)) | \ + (((to) << FXHCI_TRB_##tok##_START) & FXHCI_TRB_MASK(tok))) +#define FXHCI_TRB_DUMP(dumper, tok, trb) dumper(" "#tok"\t0x%04x ", FXHCI_TRB_GET(tok, trb)) + +#define FXHCI_TRB_CYCLE (1 << 0) + +/* shortcut to access slot context */ +#define FXHCI_SC_ROUTE_FIELD f1 /* ROUTE - Route String */ +#define FXHCI_SC_ROUTE_START 0 +#define FXHCI_SC_ROUTE_LEN 20 +#define FXHCI_SC_SPEED1_FIELD f1 /* SPEED - Port speed plus one (compared to FUsbSpeed enum) */ +#define FXHCI_SC_SPEED1_START 20 +#define FXHCI_SC_SPEED1_LEN 4 +#define FXHCI_SC_MTT_FIELD f1 /* MTT - Multi Transaction Translator */ +#define FXHCI_SC_MTT_START 25 +#define FXHCI_SC_MTT_LEN 1 +#define FXHCI_SC_HUB_FIELD f1 /* HUB - Is this a hub? */ +#define FXHCI_SC_HUB_START 26 +#define FXHCI_SC_HUB_LEN 1 +#define FXHCI_SC_CTXENT_FIELD f1 /* CTXENT - Context Entries (number of following ep contexts) */ +#define FXHCI_SC_CTXENT_START 27 +#define FXHCI_SC_CTXENT_LEN 5 +#define FXHCI_SC_RHPORT_FIELD f2 /* RHPORT - Root Hub Port Number */ +#define FXHCI_SC_RHPORT_START 16 +#define FXHCI_SC_RHPORT_LEN 8 +#define FXHCI_SC_NPORTS_FIELD f2 /* NPORTS - Number of Ports */ +#define FXHCI_SC_NPORTS_START 24 +#define FXHCI_SC_NPORTS_LEN 8 +#define FXHCI_SC_TTID_FIELD f3 /* TTID - TT Hub Slot ID */ +#define FXHCI_SC_TTID_START 0 +#define FXHCI_SC_TTID_LEN 8 +#define FXHCI_SC_TTPORT_FIELD f3 /* TTPORT - TT Port Number */ +#define FXHCI_SC_TTPORT_START 8 +#define FXHCI_SC_TTPORT_LEN 8 +#define FXHCI_SC_TTT_FIELD f3 /* TTT - TT Think Time */ +#define FXHCI_SC_TTT_START 16 +#define FXHCI_SC_TTT_LEN 2 +#define FXHCI_SC_UADDR_FIELD f4 /* UADDR - USB Device Address */ +#define FXHCI_SC_UADDR_START 0 +#define FXHCI_SC_UADDR_LEN 8 +#define FXHCI_SC_STATE_FIELD f4 /* STATE - Slot State */ +#define FXHCI_SC_STATE_START 27 +#define FXHCI_SC_STATE_LEN 5 +#define FXHCI_SC_MASK(tok) FXHCI_MASK(FXHCI_SC_##tok##_START, FXHCI_SC_##tok##_LEN) +#define FXHCI_SC_GET(tok, sc) (((sc)->FXHCI_SC_##tok##_FIELD & FXHCI_SC_MASK(tok)) \ + >> FXHCI_SC_##tok##_START) +#define FXHCI_SC_SET(tok, sc, to) (sc)->FXHCI_SC_##tok##_FIELD = \ + (((sc)->FXHCI_SC_##tok##_FIELD & ~FXHCI_SC_MASK(tok)) | \ + (((to) << FXHCI_SC_##tok##_START) & FXHCI_SC_MASK(tok))) +#define FXHCI_SC_DUMP(dumper, tok, sc) dumper(" "#tok"\t0x%04x ", FXHCI_SC_GET(tok, sc)) + +/* shortcut to access endpoint context */ +#define FXHCI_EC_STATE_FIELD f1 /* STATE - Endpoint State */ +#define FXHCI_EC_STATE_START 0 +#define FXHCI_EC_STATE_LEN 3 +#define FXHCI_EC_INTVAL_FIELD f1 /* INTVAL - Interval */ +#define FXHCI_EC_INTVAL_START 16 +#define FXHCI_EC_INTVAL_LEN 8 +#define FXHCI_EC_CERR_FIELD f2 /* CERR - Error Count */ +#define FXHCI_EC_CERR_START 1 +#define FXHCI_EC_CERR_LEN 2 +#define FXHCI_EC_TYPE_FIELD f2 /* TYPE - EP Type */ +#define FXHCI_EC_TYPE_START 3 +#define FXHCI_EC_TYPE_LEN 3 +#define FXHCI_EC_MBS_FIELD f2 /* MBS - Max Burst Size */ +#define FXHCI_EC_MBS_START 8 +#define FXHCI_EC_MBS_LEN 8 +#define FXHCI_EC_MPS_FIELD f2 /* MPS - Max Packet Size */ +#define FXHCI_EC_MPS_START 16 +#define FXHCI_EC_MPS_LEN 16 +#define FXHCI_EC_DCS_FIELD tr_dq_low /* DCS - Dequeue Cycle State */ +#define FXHCI_EC_DCS_START 0 +#define FXHCI_EC_DCS_LEN 1 +#define FXHCI_EC_AVRTRB_FIELD f5 /* AVRTRB - Average TRB Length */ +#define FXHCI_EC_AVRTRB_START 0 +#define FXHCI_EC_AVRTRB_LEN 16 +#define FXHCI_EC_MXESIT_FIELD f5 /* MXESIT - Max ESIT Payload */ +#define FXHCI_EC_MXESIT_START 16 +#define FXHCI_EC_MXESIT_LEN 16 +#define FXHCI_EC_BPKTS_FIELD rsvd[0] /* BPKTS - packets tx in scheduled uframe */ +#define FXHCI_EC_BPKTS_START 0 +#define FXHCI_EC_BPKTS_LEN 6 +#define FXHCI_EC_BBM_FIELD rsvd[0] /* BBM - burst mode for scheduling */ +#define FXHCI_EC_BBM_START 11 +#define FXHCI_EC_BBM_LEN 1 + +#define FXHCI_EC_MASK(tok) FXHCI_MASK(FXHCI_EC_##tok##_START, FXHCI_EC_##tok##_LEN) +#define FXHCI_EC_GET(tok, ec) (((ec)->FXHCI_EC_##tok##_FIELD & FXHCI_EC_MASK(tok)) \ + >> FXHCI_EC_##tok##_START) +#define FXHCI_EC_SET(tok, ec, to) (ec)->FXHCI_EC_##tok##_FIELD = \ + (((ec)->FXHCI_EC_##tok##_FIELD & ~FXHCI_EC_MASK(tok)) | \ + (((to) << FXHCI_EC_##tok##_START) & FXHCI_EC_MASK(tok))) +#define FXHCI_EC_DUMP(dumper, tok, ec) dumper(" "#tok"\t0x%04x ", FXHCI_EC_GET(tok, ec)) + +/* the current operational state of the endpoint. */ +enum +{ + FXHCI_EC_STATE_DISABLED = 0, /* endpoint is not operational */ + FXHCI_EC_STATE_RUNNING = 1, /* endpoint is operational */ + FXHCI_EC_STATE_HALTED = 2, /* endpoint is halted due to a Halt condition detected on the USB */ + FXHCI_EC_STATE_STOPPED = 3, /* endpoint is not running due to a Stop Endpoint Command */ + FXHCI_EC_STATE_ERROR = 4 /* endpoint is not running due to a TRB Error */ +}; + +#define FXHCI_INST_GET(controller) ((FXhci*)((controller)->instance)) + +static inline int FXhciEpId(const FUsbEndpoint *const ep) +{ + /* calculate endpoint ID (Device Context Index (DCI)) The range of DCI values is 0 to 31. + * For Isoch, Interrupt, or Bulk type endpoints + * DCI = (Endpoint Number * 2) + Direction , Direction = ‘0’ for OUT ‘1’ for IN + * For Control type endpoints + * DCI = (Endpoint Number * 2) + 1. + */ + return ((ep->endpoint & 0x7f) * 2) + (ep->direction != FUSB_OUT); +} + +#define FXHCI_EP0_ID 1 + +/************************** Function Prototypes ******************************/ +/* 初始化Roothub */ +void FXhciRootHubInit(FUsbDev *dev); + +/* 分配一段对齐的内存 */ +void *FXhciAlign(FXhci *xhci, const size_t min_align, const size_t size); + +/* 初始化TRB ring */ +void FXhciInitCycleRing(FXhciTransRing *ring, const size_t ring_size); + +/* 设备USB设备的地址 */ +FUsbDev *FXhciSetAddress(FUsbHc *hc, FUsbSpeed speed, int hubport, int hubaddr); + +/* 完成USB设备配置 */ +FXhciTransCode FXhciFinishDevConfig(FUsbDev *hc); + +/* 删除指定USB设备实例 */ +void FXhciDestoryDev(FUsbHc *xhci, int slot_id); + +/* 重置Event TRB ring */ +void FXhciResetEvtRing(FXhciEvtRing *ring); + +void FXhciAdvanceEvtRing(FXhci *xhci); +void FXhciUpdateEvtDQ(FXhci *xhci); +void FXhciHandleEvts(FXhci *xhci); + +FXhciTransCode FXhciWaitForCmdAborted(FXhci *xhci, const FXhciTrb *trb); +FXhciTransCode FXhciWaitForCmdDone(FXhci *xhci, const FXhciTrb *trb, int clear_event); +FXhciTransCode FXhciWaitForTransfer(FXhci *xhci, const int slot_id, const int ep_id); + +void FXhciClearTrb(FXhciTrb *trb, int pcs); +FXhciTrb *FXhciNextCmdTrb(FXhci *xhci); +void FXhciPostCmd(FXhci *xhci); + +FXhciTransCode FXhciCmdNop(FXhci *const xhci); +FXhciTransCode FXhciCmdEnableSlot(FXhci *xhci, int *slot_id); +FXhciTransCode FXhciCmdDisableSlot(FXhci *xhci, int slot_id); +FXhciTransCode FXhciCmdAddressDevice(FXhci *xhci, int slot_id, FXhciInputCtx *ctx); +FXhciTransCode FXhciCmdConfigureEp(FXhci *xhci, int slot_id, int config_id, FXhciInputCtx *ctx); +FXhciTransCode FXhciCmdEvaluateCtx(FXhci *xhci, int slot_id, FXhciInputCtx *ctx); +FXhciTransCode FXhciCmdResetEp(FXhci *xhci, int slot_id, int ep); +FXhciTransCode FXhciCmdStopEp(FXhci *xhci, int slot_id, int ep); +FXhciTransCode FXhciCmdSetTrDq(FXhci *xhci, int slot_id, int ep, FXhciTrb *trb, int dcs); + +void FXhciDumpSlotCtx(const FXhciSlotCtx *ctx); +void FXhciDumpEpCtx(const FXhciEpCtx *ctx); +void FXhciDumpDevCtx(const FXhciDevCtx *ctx, const u32 ctx_mask); +void FXhciDumpInputCtx(const FXhciInputCtx *ctx); +void FXhciDumpTransferTrb(const FXhciTrb *trb); +void FXhciDumpTransferTrbs(const FXhciTrb *first, const FXhciTrb *last); + + +/* 支持带TAG的内存分配,用于跟踪动态内存使用 */ +#ifdef FMEMP_TAG_DEBUG +void *FXhciAlignTag(FXhci *const xhci, const size_t min_align, const size_t size, const char *file, unsigned long line, const char *msg); + +#define FXHCI_ALIGN(xhci, min_align, size) FXhciAlignTag((xhci), (min_align), (size), __FILE__, __LINE__, "") +#else + +#define FXHCI_ALIGN(xhci, min_align, size) FXhciAlign((xhci), (min_align), (size)) +#endif + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci_roothub.c b/bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci_roothub.c new file mode 100644 index 0000000000..56619be498 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/usb/fxhci/fxhci_roothub.c @@ -0,0 +1,193 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fxhci_roothub.c + * Date: 2022-02-11 13:33:12 + * LastEditTime: 2022-02-18 09:17:02 + * Description:  This files is for implementation of XHCI roothub function + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Zhugengyu 2022/2/7 init commit + */ + +#include "fdebug.h" + +#include "fusb.h" +#include "fusb_generic_hub.h" +#include "fxhci_private.h" + +#define FUSB_DEBUG_TAG "FXHCI_ROOTHUB" +#define FUSB_ERROR(format, ...) FT_DEBUG_PRINT_E(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_WARN(format, ...) FT_DEBUG_PRINT_W(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_INFO(format, ...) FT_DEBUG_PRINT_I(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) +#define FUSB_DEBUG(format, ...) FT_DEBUG_PRINT_D(FUSB_DEBUG_TAG, format, ##__VA_ARGS__) + +static int FXhciRootHubStatusChanged(FUsbDev *const dev) +{ + FXhci *const xhci = FXHCI_INST_GET(dev->controller); + FASSERT(xhci); + u32 reg_val = FXhciReadOper32(&xhci->mmio, FXHCI_REG_OP_USBSTS); + const int changed = !!(FXHCI_REG_OP_USBSTS_PCD & reg_val); + + /* clear port change bit */ + if (changed) + { + reg_val &= FXHCI_REG_OP_USBSTS_PRSRV_MASK; + reg_val |= FXHCI_REG_OP_USBSTS_PCD; + FXhciWriteOper32(&xhci->mmio, FXHCI_REG_OP_USBSTS, reg_val); + } + + return changed; +} + +static int FXhciRootHubPortStatusChanged(FUsbDev *const dev, const int port) +{ + FXhci *const xhci = FXHCI_INST_GET(dev->controller); + FASSERT(xhci); + u32 portsc = FXhciReadPort32(&xhci->mmio, port - 1, FXHCI_REG_OP_PORTS_PORTSC); + const int changed = !!(portsc & (FXHCI_REG_OP_PORTS_PORTSC_CSC | FXHCI_REG_OP_PORTS_PORTSC_PRC)); + + /* always clear all the status change bits */ + portsc &= FXHCI_REG_OP_PORTS_PORTSC_RW_MASK; + portsc |= 0x00fe0000; + FXhciWritePort32(&xhci->mmio, port - 1, FXHCI_REG_OP_PORTS_PORTSC, portsc); + + return changed; +} + +static int FXhciRootHubPortConnected(FUsbDev *const dev, const int port) +{ + FXhci *const xhci = FXHCI_INST_GET(dev->controller); + FASSERT(xhci); + u32 portsc = FXhciReadPort32(&xhci->mmio, port - 1, FXHCI_REG_OP_PORTS_PORTSC); + return portsc & FXHCI_REG_OP_PORTS_PORTSC_CCS; +} + +static int FXhciRootHubPortInReset(FUsbDev *const dev, const int port) +{ + FXhci *const xhci = FXHCI_INST_GET(dev->controller); + FASSERT(xhci); + u32 portsc = FXhciReadPort32(&xhci->mmio, port - 1, FXHCI_REG_OP_PORTS_PORTSC); + return !!(portsc & FXHCI_REG_OP_PORTS_PORTSC_PR); +} + +static int FXhciRootHubPortEnabled(FUsbDev *const dev, const int port) +{ + FXhci *const xhci = FXHCI_INST_GET(dev->controller); + FASSERT(xhci); + u32 portsc = FXhciReadPort32(&xhci->mmio, port - 1, FXHCI_REG_OP_PORTS_PORTSC); + return !!(portsc & FXHCI_REG_OP_PORTS_PORTSC_PED); +} + +static FUsbSpeed FXhciRootHubPortSpeed(FUsbDev *const dev, const int port) +{ + FXhci *const xhci = FXHCI_INST_GET(dev->controller); + FASSERT(xhci); + u32 portsc = FXhciReadPort32(&xhci->mmio, port - 1, FXHCI_REG_OP_PORTS_PORTSC); + + if (portsc & FXHCI_REG_OP_PORTS_PORTSC_PED) + { + return FXHCI_REG_OP_PORTS_PORTSC_PORT_SPEED_GET(portsc) - 1; + } + else + { + return FUSB_UNKNOWN_SPEED; + } +} + +static int FXhciRootHubResetPort(FUsbDev *const dev, const int port) +{ + FXhci *const xhci = FXHCI_INST_GET(dev->controller); + FASSERT(xhci); + u32 portsc = FXhciReadPort32(&xhci->mmio, port - 1, FXHCI_REG_OP_PORTS_PORTSC); + + /* Trigger port reset. */ + portsc &= FXHCI_REG_OP_PORTS_PORTSC_RW_MASK; + portsc |= FXHCI_REG_OP_PORTS_PORTSC_PR; + FXhciWritePort32(&xhci->mmio, port - 1, FXHCI_REG_OP_PORTS_PORTSC, portsc); + + /* Wait for port_in_reset == 0, up to 150 * 1000us = 150ms */ + if (FUsbGenericHubWaitForPort(dev, port, 0, FXhciRootHubPortInReset, + 150, 1000) == 0) + { + FUSB_INFO("xhci_rh: Reset timed out at port %d ", port); + } + else + { + portsc = FXhciReadPort32(&xhci->mmio, port - 1, FXHCI_REG_OP_PORTS_PORTSC); + portsc &= FXHCI_REG_OP_PORTS_PORTSC_RW_MASK; + portsc |= FXHCI_REG_OP_PORTS_PORTSC_PRC | FXHCI_REG_OP_PORTS_PORTSC_WRC; + FXhciWritePort32(&xhci->mmio, port - 1, FXHCI_REG_OP_PORTS_PORTSC, portsc); + } + + return 0; +} + +static int FXhciRootHubEnablePort(FUsbDev *const dev, int port) +{ + FXhci *const xhci = FXHCI_INST_GET(dev->controller); + FASSERT(xhci); + u32 portsc = FXhciReadPort32(&xhci->mmio, port - 1, FXHCI_REG_OP_PORTS_PORTSC); + + /* + * Before sending commands to a port, the Port Power in + * PORTSC register should be enabled. + */ + portsc &= FXHCI_REG_OP_PORTS_PORTSC_RW_MASK; + portsc |= FXHCI_REG_OP_PORTS_PORTSC_PP; + FXhciWritePort32(&xhci->mmio, port - 1, FXHCI_REG_OP_PORTS_PORTSC, portsc); + + return 0; +} + +static const FUsbGenericHubOps FXHCI_ROOTHUB_OPS = +{ + .hub_status_changed = FXhciRootHubStatusChanged, + .port_status_changed = FXhciRootHubPortStatusChanged, + .port_connected = FXhciRootHubPortConnected, + .port_in_reset = FXhciRootHubPortInReset, + .port_enabled = FXhciRootHubPortEnabled, + .port_speed = FXhciRootHubPortSpeed, + .enable_port = FXhciRootHubEnablePort, + .disable_port = NULL, + .start_port_reset = NULL, + .reset_port = FXhciRootHubResetPort, +}; + +/** + * @name: FXhciRootHubInit + * @msg: 初始化Roothub + * @return {*} + * @param {FUsbDev} *dev, Roothub实例 + */ +void FXhciRootHubInit(FUsbDev *dev) +{ + u32 reg_val; + FXhci *xhci = FXHCI_INST_GET(dev->controller); + FASSERT(xhci); + + /* we can set them here because a root hub _really_ shouldn't + appear elsewhere */ + dev->address = 0; + dev->hub = FUSB_NO_HUB; + dev->port = FUSB_NO_PORT; + + reg_val = FXhciReadCap32(&xhci->mmio, FXHCI_REG_CAP_HCSPARAMS1); + const int num_ports = FXHCI_REG_CAP_HCSPARAMS1_MAX_PORTS_GET(reg_val); /* TODO: maybe we need to read extended caps */ + + FUsbGenericHubInit(dev, num_ports, &FXHCI_ROOTHUB_OPS); + + FUSB_INFO("xHCI: root hub init done "); +} diff --git a/bsp/phytium/libraries/standalone/drivers/watchdog/Kconfig b/bsp/phytium/libraries/standalone/drivers/watchdog/Kconfig new file mode 100644 index 0000000000..5da173ab68 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/watchdog/Kconfig @@ -0,0 +1,10 @@ + +menu "FWDT Configuration" + config USE_FWDT + bool + prompt "Use FWDT" + default n + +endmenu + + diff --git a/bsp/phytium/libraries/standalone/drivers/watchdog/fwdt/fwdt.c b/bsp/phytium/libraries/standalone/drivers/watchdog/fwdt/fwdt.c new file mode 100644 index 0000000000..68b0db3f15 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/watchdog/fwdt/fwdt.c @@ -0,0 +1,255 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * FilePath: fwdt.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-07-15 17:05:09 + * Description:  This files is for wdt ctrl function implementation. + * Users can operate as a single stage watchdog or a two stages watchdog. + * In the single stage mode, when the timeout is reached, your system will + * be reset by WS1. The first signal (WS0) is ignored. + * In the two stages mode, when the timeout is reached, the first signal (WS0) + * will trigger panic. If the system is getting into trouble and cannot be reset + * by panic or restart properly by the kdump kernel(if supported), then the + * second stage (as long as the first stage) will be reached, system will be + * reset by WS1. This function can help administrator to backup the system + * context info by panic console output or kdump. + * + * GWDT: + * two stages mode: + * |--------WOR-------WS0--------WOR-------WS1 + * |----timeout-----(panic)----timeout-----reset + * + * single stage mode: + * |------WOR-----WS0(ignored)-----WOR------WS1 + * |--------------timeout-------------------reset + * + * Note: Since this watchdog timer has two stages, and each stage is determined + * by WOR, in the single stage mode, the timeout is (WOR * 2); in the two + * stages mode, the timeout is WOR. + * This driver use two stages mode, when WS0=1, it can Raise the timeout interrupt. + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Wangxiaodong 2021/8/25 init + * 1.1 Wangxiaodong 2021/11/5 restruct + * 1.2 Wangxiaodong 2022/7/20 add some functions + */ + +#include +#include "fgeneric_timer.h" +#include "fkernel.h" +#include "fparameters.h" +#include "ftypes.h" +#include "ferror_code.h" +#include "fdebug.h" +#include "fwdt.h" +#include "fwdt_hw.h" + +#define FWDT_DEBUG_TAG "WDT" +#define FWDT_ERROR(format, ...) FT_DEBUG_PRINT_E(FWDT_DEBUG_TAG, format, ##__VA_ARGS__) +#define FWDT_WARN(format, ...) FT_DEBUG_PRINT_W(FWDT_DEBUG_TAG, format, ##__VA_ARGS__) +#define FWDT_INFO(format, ...) FT_DEBUG_PRINT_I(FWDT_DEBUG_TAG, format, ##__VA_ARGS__) +#define FWDT_DEBUG(format, ...) FT_DEBUG_PRINT_D(FWDT_DEBUG_TAG, format, ##__VA_ARGS__) + +/** + * @name: FWdtCfgInitialize + * @msg: Initializes a specific instance such that it is ready to be used. + * @param {FWdtCtrl} *pctrl, instance of FWDT controller + * @param {FWdtConfig} *input_config_p, Configuration parameters of FWdt + * @return err code information, FWDT_SUCCESS indicates success,others indicates failed + */ +FError FWdtCfgInitialize(FWdtCtrl *pctrl, const FWdtConfig *input_config_p) +{ + FASSERT(pctrl && input_config_p); + + FError ret = FWDT_SUCCESS; + /* + * If the device is started, disallow the initialize and return a Status + * indicating it is started. This allows the user to de-initialize the device + * and reinitialize, but prevents a user from inadvertently + * initializing. + */ + if (FT_COMPONENT_IS_READY == pctrl->is_ready) + { + FWDT_WARN("device is already initialized!!!"); + } + + /*Set default values and configuration data */ + FWdtDeInitialize(pctrl); + + pctrl->config = *input_config_p; + + pctrl->is_ready = FT_COMPONENT_IS_READY; + + return ret; +} + +/** + * @name: FWdtDeInitialize + * @msg: DeInitialization function for the device instance + * @param {FWdtCtrl} *pctrl, instance of FWDT controller + * @return {*} + */ +void FWdtDeInitialize(FWdtCtrl *pctrl) +{ + FASSERT(pctrl); + + pctrl->is_ready = 0; + memset(pctrl, 0, sizeof(*pctrl)); + + return; +} + +/** + * @name: FWdtSetTimeout + * @msg: Set Timeout Value, the first time it will raise a signal, which is typically + * wired to an interrupt; If this watchdog remains un-refreshed, it will raise a + * second signal which can be used to interrupt higher-privileged software + * or cause a PE reset. + * @param {WdtCtrl} *pctrl, instance of FWDT controller. + * @param {u32} timeout, represent in seconds, this parameter must be a number between 1 and 89. + * @return {FError} err code information, FWDT_SUCCESS indicates success, others indicates failed. + */ +FError FWdtSetTimeout(FWdtCtrl *pctrl, u32 timeout) +{ + FASSERT(pctrl != NULL); + if (pctrl->is_ready != FT_COMPONENT_IS_READY) + { + FWDT_ERROR("device is not already!!!"); + return FWDT_NOT_READY; + } + if (timeout > FWDT_MAX_TIMEOUT) + { + FWDT_ERROR("timeout value is invalid"); + return FWDT_ERR_INVAL_PARM; + } + uintptr base_addr = pctrl->config.control_base_addr; + + FWDT_WRITE_REG32(base_addr, FWDT_GWDT_WOR, (u32)(FWDT_CLK * timeout)); + + return FWDT_SUCCESS; +} + +/** + * @name: WdtGetTimeleft + * @msg: Get Timeout countdown, in seconds + * @param {FWdtCtrl} *pctrl, pointer to a WdtCtrl structure that contains + * the configuration information for the specified wdt module. + * @return {u32} Timeout countdown, in seconds + */ +u32 FWdtGetTimeleft(FWdtCtrl *pctrl) +{ + FASSERT(pctrl != NULL); + u64 timeleft = 0; + uintptr base_addr = pctrl->config.control_base_addr; + + /* if the ws0 bit of register WCS is zero,indicates that there is one more timeout opportunity */ + if (!(FWdtReadWCS(base_addr) & FWDT_GWDT_WCS_WS0)) + timeleft += FWdtReadWOR(base_addr); + + u32 wcvh = (u32)FWdtReadWCVH(base_addr); + u32 wcvl = (u32)FWdtReadWCVL(base_addr); + u64 wcv = (((u64)wcvh << 32) | wcvl); + + timeleft += (wcv - GenericTimerRead()); + + // f_printk("------wcvh=%llx, wcvl=%llx, wcv=%llx, timeleft=%llx\n", wcvh, wcvl, wcv, timeleft); + + do_div(timeleft, FWDT_CLK); + + return (u32)timeleft; +} + +/** + * @name: FWdtRefresh + * @msg: Refresh watchdog + * @param {WdtCtrl} *pctrl, instance of FWDT controller. + * @return {FError} err code information, FWDT_SUCCESS indicates success, others indicates failed. + */ +FError FWdtRefresh(FWdtCtrl *pctrl) +{ + FASSERT(pctrl != NULL); + if (pctrl->is_ready != FT_COMPONENT_IS_READY) + { + FWDT_ERROR("device is not already!!!"); + return FWDT_NOT_READY; + } + uintptr base_addr = pctrl->config.refresh_base_addr; + FWDT_WRITE_REG32(base_addr, FWDT_GWDT_WRR, 0); + return FWDT_SUCCESS; +} + +/** + * @name: FWdtStart + * @msg: Start watchdog + * @param {WdtCtrl} *pctrl, instance of FWDT controller + * @return {FError} err code information, FWDT_SUCCESS indicates success, others indicates failed. + */ +FError FWdtStart(FWdtCtrl *pctrl) +{ + FASSERT(pctrl != NULL); + if (pctrl->is_ready != FT_COMPONENT_IS_READY) + { + FWDT_ERROR("device is not already!!!"); + return FWDT_NOT_READY; + } + + uintptr base_addr = pctrl->config.control_base_addr; + FWDT_WRITE_REG32(base_addr, FWDT_GWDT_WCS, FWDT_GWDT_WCS_WDT_EN); + + return FWDT_SUCCESS; +} + +/** + * @name: FWdtStop + * @msg: Stop watchdog + * @param {WdtCtrl} *pctrl, instance of FWDT controller + * @return {FError} err code information, FWDT_SUCCESS indicates success, others indicates failed. + */ +FError FWdtStop(FWdtCtrl *pctrl) +{ + FASSERT(pctrl != NULL); + uintptr base_addr = pctrl->config.control_base_addr; + FWDT_WRITE_REG32(base_addr, FWDT_GWDT_WCS, 0); + return FWDT_SUCCESS; +} + +/** + * @name: FWdtReadFWdtReadWIIDR + * @msg: Read wdt iidr register value. + * @param {FWdtCtrl} *pctrl, instance of FWDT controller + * @param {FWdtIdentifier} *wdt_identify, wdt identifier struct. + * @return {FError} err code information, FWDT_SUCCESS indicates success, others indicates failed. + */ +FError FWdtReadFWdtReadWIIDR(FWdtCtrl *pctrl, FWdtIdentifier *wdt_identify) +{ + FASSERT(pctrl != NULL); + FASSERT(wdt_identify != NULL); + + if (pctrl->is_ready != FT_COMPONENT_IS_READY) + { + FWDT_ERROR("device is not already!!!"); + return FWDT_NOT_READY; + } + + u32 reg_val = 0; + uintptr base_addr = pctrl->config.refresh_base_addr; + reg_val = FWDT_READ_REG32(base_addr, FWDT_GWDT_W_IIDR); + + wdt_identify->version = (u16)((reg_val & FWDT_VERSION_MASK) >> 16); + wdt_identify->continuation_code = (u8)((reg_val & FWDT_CONTINUATION_CODE_MASK) >> 8); + wdt_identify->identity_code = (u8)((reg_val & FWDT_IDENTIFY_CODE_MASK)); + + return FWDT_SUCCESS; +} \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/watchdog/fwdt/fwdt.h b/bsp/phytium/libraries/standalone/drivers/watchdog/fwdt/fwdt.h new file mode 100644 index 0000000000..e1fcc10390 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/watchdog/fwdt/fwdt.h @@ -0,0 +1,103 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fwdt.h + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:24:34 + * Description:  This files is for wdt ctrl function definition + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Wangxiaodong 2021/8/26 init + * 1.1 Wangxiaodong 2021/11/5 restruct + */ + +#ifndef BSP_DRIVERS_FWDT_H +#define BSP_DRIVERS_FWDT_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ftypes.h" +#include "fdebug.h" +#include "ferror_code.h" +#include "fkernel.h" +#include "fassert.h" + +#define FWDT_SUCCESS FT_SUCCESS +#define FWDT_ERR_INVAL_PARM FT_MAKE_ERRCODE(ErrModBsp, ErrBspWdt, 1) +#define FWDT_NOT_READY FT_MAKE_ERRCODE(ErrModBsp, ErrBspWdt, 2) +#define FWDT_NOT_SUPPORT FT_MAKE_ERRCODE(ErrModBsp, ErrBspWdt, 3) +#define FWDT_TIMEOUT FT_MAKE_ERRCODE(ErrModBsp, ErrBspWdt, 4) + +/* max timeout = 0xFFFFFFFF/ WDT_CLK = 89 */ +#define FWDT_MAX_TIMEOUT 89 + +typedef struct +{ + u16 version; /* wdt version */ + u8 continuation_code;/* JEP106 continuation code of the implementer */ + u8 identity_code; /* JEP106 identity code of the implementer */ +} FWdtIdentifier; /* wdt Identifier*/ + +typedef struct +{ + u32 instance_id;/* wdt id */ + uintptr refresh_base_addr;/* wdt refresh base addr */ + uintptr control_base_addr;/* wdt control base addr */ + u32 irq_num; /* wdt ir num */ + u32 irq_prority;/* wdt irq priority */ + const char *instance_name;/* instance name */ +} FWdtConfig; /* wdt config */ + +typedef struct +{ + FWdtConfig config; /* wdt config */ + u32 is_ready; /* wdt initialize the complete flag */ +} FWdtCtrl; + + +/* get wdt default configs */ +const FWdtConfig *FWdtLookupConfig(u32 instance_id); + +/* wdt config init */ +FError FWdtCfgInitialize(FWdtCtrl *pctrl, const FWdtConfig *input_config_p); + +/* wdt config deinit */ +void FWdtDeInitialize(FWdtCtrl *pctrl); + +/* set wdt timeout value*/ +FError FWdtSetTimeout(FWdtCtrl *pCtrl, u32 timeout); + +u32 FWdtGetTimeleft(FWdtCtrl *pctrl); + +/* fresh the wdt */ +FError FWdtRefresh(FWdtCtrl *pCtrl); + +/* start wdt*/ +FError FWdtStart(FWdtCtrl *pCtrl); + +/* stop wdt*/ +FError FWdtStop(FWdtCtrl *pCtrl); + +/* read wdt w_iidr register*/ +FError FWdtReadFWdtReadWIIDR(FWdtCtrl *pctrl, FWdtIdentifier *wdt_identify); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/watchdog/fwdt/fwdt_g.c b/bsp/phytium/libraries/standalone/drivers/watchdog/fwdt/fwdt_g.c new file mode 100644 index 0000000000..21bc05458b --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/watchdog/fwdt/fwdt_g.c @@ -0,0 +1,48 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fwdt_g.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:22:49 + * Description:  This files is for static config of wdt ctrl + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 Wangxiaodong 2021/8/25 init + */ + +#include "fparameters.h" +#include "fwdt.h" + +/* default configs of wdt ctrl */ +const FWdtConfig FWdtConfigTbl[FWDT_INSTANCE_NUM] = +{ + { + .instance_id = FWDT_INSTANCE_0, + .refresh_base_addr = FWDT0_REFRESH_BASE, + .control_base_addr = FWDT0_CONTROL_BASE, + .irq_num = FWDT0_INTR_IRQ, + .irq_prority = 0, + .instance_name = "WDT-0" + }, + + { + .instance_id = FWDT_INSTANCE_1, + .refresh_base_addr = FWDT1_REFRESH_BASE, + .control_base_addr = FWDT1_CONTROL_BASE, + .irq_num = FWDT1_INTR_IRQ, + .irq_prority = 0, + .instance_name = "WDT-1" + } +}; diff --git a/bsp/phytium/libraries/standalone/drivers/watchdog/fwdt/fwdt_hw.c b/bsp/phytium/libraries/standalone/drivers/watchdog/fwdt/fwdt_hw.c new file mode 100644 index 0000000000..d81b945332 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/watchdog/fwdt/fwdt_hw.c @@ -0,0 +1,37 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fwdt_hw.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-18 09:23:08 + * Description:  This files is for wdt register function + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +/***************************** Include Files *********************************/ + +#include "fwdt_hw.h" + + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ diff --git a/bsp/phytium/libraries/standalone/drivers/watchdog/fwdt/fwdt_hw.h b/bsp/phytium/libraries/standalone/drivers/watchdog/fwdt/fwdt_hw.h new file mode 100644 index 0000000000..ede7d81373 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/watchdog/fwdt/fwdt_hw.h @@ -0,0 +1,131 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fwdt_hw.h + * Date: 2021-08-25 10:27:42 + * LastEditTime: 2022-02-25 11:44:33 + * Description:  This files is for ctrl of watchdog timer functions + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.0 wangxiaodong 2021/8/25 init + */ + +#ifndef BSP_DRIVERS_FWDT_HW_H +#define BSP_DRIVERS_FWDT_HW_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "fkernel.h" +#include "fio.h" + +/* Watchdog register definitions */ + +/* refresh frame */ +#define FWDT_GWDT_WRR 0x000 + +/* control frame */ +#define FWDT_GWDT_WCS 0x000 /* WCS register */ +#define FWDT_GWDT_WOR 0x008 +#define FWDT_GWDT_WCVL 0x010 +#define FWDT_GWDT_WCVH 0x014 + +/* refresh/control frame */ +#define FWDT_GWDT_W_IIDR 0xfcc +#define FWDT_GWDT_IDR 0xfd0 + +/* Watchdog Control and Status Register */ +#define FWDT_GWDT_WCS_WDT_EN BIT(0) +#define FWDT_GWDT_WCS_WS0 BIT(1) +#define FWDT_GWDT_WCS_WS1 BIT(2) + +/***************** Macros (Inline Functions) Definitions *********************/ + +/** + * @name: WDT_READ_REG32 + * @msg: read WDT register + * @param {u32} addr, base address + * @param {u32} reg_offset, register offset + * @return {u32} register value + */ +#define FWDT_READ_REG32(addr, reg_offset) FtIn32((addr) + (u32)(reg_offset)) + +/** + * @name: FWDT_WRITE_REG32 + * @msg: write WDT register + * @param {u32} addr, base address + * @param {u32} reg_offset, register offset + * @param {u32} reg_value, value write to register + * @return {u32} register value + */ +#define FWDT_WRITE_REG32(addr, reg_offset, reg_value) FtOut32((addr) + (u32)(reg_offset), (u32)(reg_value)) + +#define FWDT_VERSION_MASK GENMASK(19, 16) +#define FWDT_CONTINUATION_CODE_MASK GENMASK(11, 8) +#define FWDT_IDENTIFY_CODE_MASK GENMASK(6, 0) +/** + * @name: FWdtReadWCVH + * @msg: Read wdt wcvh register value. wcvl and wclh register stores the comparison value of the watchdog count. + * timeout value = comparison value - sys_cnt. + * @param {uintptr} addr, pointer to a WdtCtrl base addr. + * @return {u32} register value + */ + +static inline u32 FWdtReadWCVH(uintptr addr) +{ + return FWDT_READ_REG32(addr, FWDT_GWDT_WCVH); +} + +/** + * @name: FWdtReadWCVL + * @msg: Read wdt wcvl register value. wcvl and wclh register stores the comparison value of the watchdog count. + * timeout value = comparison value - sys_cnt. + * @param {uintptr} addr, pointer to a WdtCtrl base addr. + * @return {u32} register value + */ +static inline u32 FWdtReadWCVL(uintptr addr) +{ + return FWDT_READ_REG32(addr, FWDT_GWDT_WCVL); +} + +/** + * @name: FWdtReadWOR + * @msg: Read wdt wor register value. used to set timeout value, wor + sys_cnt = wcv. + * @param {uintptr} addr, pointer to a WdtCtrl base addr. + * @return {u32} register value + */ +static inline u32 FWdtReadWOR(uintptr addr) +{ + return FWDT_READ_REG32(addr, FWDT_GWDT_WOR); +} + +/** + * @name: FWdtReadWCS + * @msg: Read wdt wcs register value. wcs is control and state register. bit0 enable(1) or disable(0) wdt. + * @param {uintptr} addr, pointer to a WdtCtrl base addr. + * @return {u32} register value + */ +static inline u32 FWdtReadWCS(uintptr addr) +{ + return FWDT_READ_REG32(addr, FWDT_GWDT_WCS); +} + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/drivers/watchdog/fwdt/fwdt_intr.c b/bsp/phytium/libraries/standalone/drivers/watchdog/fwdt/fwdt_intr.c new file mode 100644 index 0000000000..39972e27a0 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/watchdog/fwdt/fwdt_intr.c @@ -0,0 +1,22 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fwdt_intr.c + * Date: 2021-11-05 10:01:59 + * LastEditTime: 2022-02-25 11:44:02 + * Description:  This files is for intrrupt function of wdt ctrl + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ diff --git a/bsp/phytium/libraries/standalone/drivers/watchdog/fwdt/fwdt_sinit.c b/bsp/phytium/libraries/standalone/drivers/watchdog/fwdt/fwdt_sinit.c new file mode 100644 index 0000000000..69498558e4 --- /dev/null +++ b/bsp/phytium/libraries/standalone/drivers/watchdog/fwdt/fwdt_sinit.c @@ -0,0 +1,75 @@ +/* + * Copyright : (C) 2022 Phytium Information Technology, Inc. + * All Rights Reserved. + * + * This program is OPEN SOURCE software: you can redistribute it and/or modify it + * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, + * either version 1.0 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the Phytium Public License for more details. + * + * + * FilePath: fwdt_sinit.c + * Date: 2022-02-10 14:53:42 + * LastEditTime: 2022-02-25 11:45:05 + * Description:  This files is for + * + * Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +/***************************** Include Files *********************************/ + +#include "fwdt.h" +#include "fparameters.h" + +#define FWDT_DEBUG_TAG "WDT_SINIT" +#define FWDT_ERROR(format, ...) FT_DEBUG_PRINT_E(FWDT_DEBUG_TAG, format, ##__VA_ARGS__) +#define FWDT_WARN(format, ...) FT_DEBUG_PRINT_W(FWDT_DEBUG_TAG, format, ##__VA_ARGS__) +#define FWDT_INFO(format, ...) FT_DEBUG_PRINT_I(FWDT_DEBUG_TAG, format, ##__VA_ARGS__) +#define FWDT_DEBUG(format, ...) FT_DEBUG_PRINT_D(FWDT_DEBUG_TAG, format, ##__VA_ARGS__) + + +extern FWdtConfig FWdtConfigTbl[FWDT_INSTANCE_NUM]; +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/*****************************************************************************/ + +/** + * @name: FWdtLookupConfig + * @msg: get wdt configs by id + * @param {u32} instance_id, id of wdt ctrl + * @return {FWdtConfig *}, wdt config address + */ +const FWdtConfig *FWdtLookupConfig(u32 instance_id) +{ + const FWdtConfig *pconfig = NULL; + u32 index; + + if (instance_id >= FWDT_INSTANCE_NUM) + { + FWDT_ERROR("wdt id is not invalid."); + return NULL; + } + + for (index = 0; index < (u32)FWDT_INSTANCE_NUM; index++) + { + if (FWdtConfigTbl[index].instance_id == instance_id) + { + pconfig = &FWdtConfigTbl[index]; + break; + } + } + return (const FWdtConfig *)pconfig; +} + diff --git a/bsp/phytium/libraries/standalone/gitinfo b/bsp/phytium/libraries/standalone/gitinfo new file mode 100644 index 0000000000..4cb410e5a6 --- /dev/null +++ b/bsp/phytium/libraries/standalone/gitinfo @@ -0,0 +1,4 @@ +[commit-id]: +ccf728ed578713cae89a5472f7dfb6fbd01e7a70 +[branch]: +new_master

q#xex#U9%{S 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 C1)HcM9QO?hOw&(78nFN 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 o7R5s?Wpn4~-pve$cJ;L+Kd;;Qfy+T&`+(-+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bsp/phytium/libraries/standalone/doc/design/system_2.png b/bsp/phytium/libraries/standalone/doc/design/system_2.png new file mode 100644 index 0000000000000000000000000000000000000000..33d0803e348fe0dbb81022d33c8362a907ea3df8 GIT binary patch literal 178111 zcmeEv2OyR0`@dCGazsVhI~j3oj=i%-N%lBo9D8q(L$;(St3<;ng{+W0A|uLPk-c~L zKL@9mUhnt)_U0Y`@9*`z<9Iyh8Tb9UKi74AuIs*^07ZGp<44aPML|J1E-fXdjDms= zLP0?b!8#1=>6N4*1OA6|NMoW9&yk!x8Hf{?HjeVb4E79)`LJ1kHSL3CgOO_#15)r zrDuRR+!5$^VBa-6m^ttpEASBjn4t-9ZBa{eOPj67Vly%_-v z-`38?(hR!mFNU1@oE(U6nCt07%@r+eP3%l8Er7Eb0B42*rxkI4+Sr)@Jth0y0d|&% z`_VHuG2ZuZ;hjI|TiV%K!hj=@J^4#>?X)1`0?-{GZ|y=N9cl@K+Sxb*J%aoz60SQa zX9b4H7N{M6!3-zQ&c18A@q#>1ZwIHw-_8y6%1#dhA7D;f5ZE6MJ45Wx>^~kZLl!PB zps~K|KSK^Ew;?;Q|5yFT4c2Gd>hV9JdBGh2ZS%719QZTM%f0;sdzzQi92i+5MwY+; z1G5->!#St5Jz~%TkYZyuL~JzRUv{zBBH0bO4*(VNBk;+1`j4-8 zF1hy}fPLFfh}?~!>0fRN3=9s$E$$MSksPu*Hg`n%&&{N5F;bqV4b6TIpHr>8l?D&<-Yk z0faa^jR-f5uo%Mb^Kj9K0Qdp+h|q{|{tkY&Z)ESy{U3!DnCsWD;@;V{+aw$ato{O% z{?9TAY@EMtT@IvG-rc;c$f5jG!5i24p`o@NcxQK^FgmugG3je-+{g)rXEJ%bQ(X`8FMvm3J z0NcjKPmSOGT;_iuzJA;6igVI%i|@F0fL|xfc23dK#?F@hUwgbAF-v}nqmVHgC(^C^ z8}Np->HokO0?hK8F~r_L0TDy6(r^P-ZhN5oAC4H1-T5~J3Lle1P>3ee|uHUNf$?=_)19O6z zcbuKy@vGQ&4*MCu3VDtLV5e`|^ABN4R!#a}K(T!T)Hdvqi3jks$azyn&g2`39JWeJ7;;Dfj_$)BzdCKL$T_JtHIA-5%1vjE3lspJFt& ze=gj1l7N37+d=IRsW32$%C)To;Wx+pU)cRL?AZ5%-8Q}KA~sf}+O=DuU;~)#0E{X7 zAIlA}ZT$dV)wf*KA9c_qO$;0WR{lSYs-1+@L6}V>RCc09c5W`_A25{%0VrTlAjZWH z^3}lsN(E|Tq6ZW*{EMiAam2ny6dN})C)X}VY{x$B;;a3FNd#SebKMTc5koq2e;d*i zMWx06#Tg=n{wFXO8xIRJ%Rj>5XWvi5Kv3MSDg{Il7NQ_%uYRy6?EhXZ_|1BReJh*( z3k~2qDPfPyfT*bXP9OO1`wUThvQ_w|r;jYv+REGgW~I`1f)mF+f)fWjH#6%_G`8EV zK_;tzCPLdUK>M!0_V&|{=S}t55F60p-<2vEaUeEe|F5(l&aIz)$I3T={nw>nNQT`- zx@<^I=Ac`Xb-(7^Hm=(dz<*nsw6{RxfU8GD6)z#up*-R=+#*|fQ3)Cmz%qSv0)8no z`DNe#PG0oK5d_2(0KKyCu!4bHIwv+}l6uCGX=2@Alz- zQ;PWkX4|RPXJcV!2I}#_Ts&-?tn3^-JL1YeABf;TrV0L?CieS6B^sb~3E>L@1p$Dq z1!{`40AA+g`cb^NK>v%fonkdwRgFP;uaf#&!w8T%V}LROG}PgVzzJ3~%? z_}gruG#HqC{VzQo2tEDE5F&l9T}~BJ82>Y{gtXj0HtFx+jGc<2^uJg-AZlXc+8aE6 z^WXm>EbXWpe;Y4}t4blUq>pgb_snAcy{Vo*lVME>XJ`Kvx|msacI^V# zpK191n5ch%WF;mi_pj*#77sZP)5ih+?*O%P)SqGf_RHCCJ2w9*X?bvfl2nvdKvLGf z1ogAkx!;*Tpf+S%lHbKnf2J$7gVVpw{0WQw%S{KxP2aoae=(N!lb`_aYIiNs5BV$m zq(Kg}HU$|){MY&)D{?)A9aV;FpRi}&S`}pU{WD@R>weAq`?UY-%*9?0^MGqr5UB`& z!GyO~%HiIcz`wor2{73jk*)w1(E)FL;6^Nn0_+iH-^yeFtLd=Na0B}3w+Q`zyQ<~K zvG<)K$c1!lciD+=t7h%?I*{M{DeUd11^!PDZBC?KwX1pe9oh#`)W`bAvg3d8(B3LT z+e$qGs}_CBLH;{nw-y-u^>PW{2IG#g;N;-?FDH^7#E50vFY5em#2#c}@2|9kZ&eTb zSaC$b@9$3g{jMfrPrbxGg${pW$uB$7|3>!YJ}&y7#tr)gd*Air&pK6HP(4m4Vutqa zVi919x1k=C+i2glB{>baq56n<+rMu0Z@~L~uj((M)}3hT2jiPxQ|n*1_~5?P9l%0K zTdNQP%Mt!=?eBlAvioPQIrsypL9Q6O)3G~al9dBdvHQyp$BwtW3uF6fdU9`!8Oc4E!&v78G?0(VaU+Hkjn&|)Q%pO!a==%jb zfeAr-yxARS{tm7V$d~0r4urp}W=q%x=D^LQw~Jx^Bb)qR^JS6U^ZT;RP5=&O`KD*? zm_JUQ{g%oDvwb^mw+Tc#pFd*(IRS41;ZW@HF#bxq5*Ah#mE!!1W;p);H?Qo6F~tTf zz{2`nfzAH9`OZYf?fyS-TkL04w!c+WdWdtzqP0Y2M_C>WhHi0r(Hqg-?gR$=N~JG`*n8YAR()$Cb!GF{`G+UK1ldyPU-k5 zz_PRNbo`FdW#idl+Fh7LYG}KS?m-C|2bGjNNZ82>%LA{X{T40!i@R^{NZm90|EF-r zfh^Pen{anfdjCP=AE{-FnCDUn`#cW9%(ZzxTUuvG^(YeIKp-gW!jZynZazaDec$ zG67y=@h<@XTmZ7O?esI!9{*kZ2QbVp@0tgGfjLP6uXMjAXK4rp-smn4)Q1B*4Ndfb zN@(D9={vu}oVF@o*u)J^ORb}zkfTV839CA3j~8Oz*DSxyywNooN6b-kMqJ2C>a91m zo?_e?=|Npr*o9?F^XqNh3oh&38}S;BVJG)SqlW+2kBy*2M_yD2!-WVn0lOa$H#EtJmc_w7c?;2wXcY1SOjT-dCPw*ls<;FGR4(x8e4U_)Ffhd1;YhA$vVnxvQc3;WGYmnkY z9qRjhCapEA$_RrTqhNQR1_h5uiWv$4I=<0}bsu1RICmm+R(K`BO;LbXlw3k^d2@q% z>n>w~hEenVd}61+wjbKtf?R0!{qg56qnE@<1vbM5{Md72SWb1DVfe#wH1s`nJrf{` zWQt_UWHyjqC#Rv0=o?G%3B!pgQo?F;jd5^G)}&a9b-*sLNE^rJxaAPHHgMFxZawD9NI@pio(9=Dwe;ROply7JA zwiN>{3p&wK%{AvdjsFAQJ0vN~i`w>hk= zX+MJXd4pex2oAYYoEOYIJAIpeqQ=DJSZ|5~qYvSlP41iwJNDocK~sGyjNX@Op%q^@ zuLkM)tX&gP3F{hh%zlM2-As2!?;YKm{K`h&yR}Pix^ijdhy$kh+0mm%OziG7Yjra@ z53I_i_sFV}fk{e79t%DetheXy-AurreUD#}OXyP38%hC^%Z904iK1!LnGI>kWQ>+q zcnQmI%}k|Yreeh}W3-Own>-a<3>fk(1MazW-P zqM16|seW(Q49V(O0q5;M)dV{YeR9mdaA>0WbKboAjR$GS}m zYhP89hQPV>70wThyk}bSyoy|Ij11gj=}$SKyQ#bBFWE#-76}@~9Ido9cboQHhA+p# zn`KG0&0w%X6$l=s%4DYg@zwtR{#vG57ZRZ&;L*4Ww5QhltX{9EwmyM%;7z{5E&ixY z2TFN5ZsPW4Scqd|Jta^{tp|_w{lKY|(g^lOsX=f>5e0|@OOAjoSQ4w#US+W4$na<+ z#O?*-8wrHH+2$`)N?bDU^a%K-o_gyP9_kAkkB`afG$~XCf+vCE~|Q)I)h@6}=@FdJ<~5iarlR zc^7Kl=DoutwiJ046!O~Nr{?BbIx~yM`ol|~=fv9Mm(O5)^!(_Vmmx?m7)chQKpfw| zxmLE>NTx91%)++8W?WxKCgA2Y*Q_8&a*m!pw=8>N3gtB|G0vTW#db4O)Al0@F_!AB zMxm82S8p<;e-!lG_&RNZqrtmYRcgGMSgR-F;^+QIl+-A{s`PT*=A?NL=_ioja)bC$ zAF}lh;*&$rED)Lhhj$^tFD-5n4$8EsR7*-pcV2lX-$bO*Iry-uG$+|X(;(q(6lY(w zaZhR8J=({ePfgEG7h1}fx5i4UlCaO*eflW&w*3!J1) zOh&LfvTLDC*P-e<>IF_S@nXI=0|P77qiFb8k7MG_u-qRy)U1W0VM7$}EtKFq()7r_ zi&e_CUn@&@l}%^T=CZ>23JIgTnOVw|=S-{FbEk6J77jbj*_qOB5(o6Q0@UGbDS7YamxdHt5DMu_n5!RDeE_vU zSx3iH$HBoNv2E;B1i4D}g+o3i7nrp2US++0z3fn%sf{;o-m$SXJ3G4><{59)P$~lq ze|BOV&lh|!qmD((JB;d?xqK!r{axprzqFfB@Ir>|E9b~1W?29TJDy5(nkP%qU5`5I zmX~o>aMdH=rPj0H%-b4~FVd?^X-F;~jel&WB;i`#haavGv#Dj^m6LI&9dEG`I?>G$ zP{wDqS=iP;ytz(mcQ-r?6My66yULbIC(@)zP^T+Xa<`K;9~z5ZXKz&@ zIk_SyPRe0fC?zV*SOxeoMLVWYvWaqH4>*W+T0AzJmALPZzi?=QX4_s5yCldAmSj&@ zxZb#`7!kBEq%Lvh0wDy$rc&b!w5 z05lK=C)JuGHzN>9d@lk(J(aJ*otrrBg<*BX=Pv1H{3+Af>2l_=mwLu$cnD^Ik7&lZ;OLJ>gu=f zyKhK~d-Fru^=0rk4i>_w$H&N%B?bD6%v~U!*!b(Aj%-JiyGO}OQ!Sq}jy0gfWy3TW z3m_;8QMYgN4^F#wT33=BVq#*lskvjGb!(mnl&Ran^Vvn$av~=38ktfY+-!1ewgrB) z!Fg2fv$KEUkf-tC^gO%A58k&Ey*Li)T;qxCq#Pse&nJ23%4V5|RpXlQ_BxLKM#CKw z%VHlLD+k%pyKlx9(v&Wg-$}ppId5z@Jv9Go%Es)HKRjtL>WEd3j!Pf;*R_VI^KYl4 z86BWkDmm(u0zdo}qjxo1g1suJoL+L6Hp%Y$=2^#`lVNmsQpkqUP zPb`$>o|v>HHF_6gf1_>e_8b{+$DLO)+3uFOAUV>>3FY-cn$ONwRi$62WZTS0H)i7~ zj}CRO%eNFyzFt;;*SqP)({lZ@XLNv4X`m{9=Lp9Nu0`w2c~`AgWj7Q#NP1v_?kN5R zSi$N&x1@BPqN%y%o+8ag3psJ)m3+_8I~l4~ZUfa;U%<<5;k$iJ^6Ork1H-&pEc_jx z|( zKkaLxQj`?)a;d76Ki2Yt>6@ziSR@pnQ#k5`qjjA`vn(w$#A0_W&na71zjx?JkADoS z0C}vQJ&)5i>ZTtz^~n=^b3-R)YJFPANVnzrgT;>Q4xvv({fMR@$OwkC#RY!GdGh-5 zhvDA7tC6y?38gU}vu8&~HSFJ%20kAyW<~U6-!n%opE26t>%OO-a9|gm00qv)%CA45 zPJBD_^vi@cpKDJGw5d!lgrJ;4=|#u%+#AugY`!6%Wn8kQ7l+0{h)v1WXI99WJM=Tq z!E-a!qSDgUf~d zqq7-&>DlE&!AZGoie+cV1MahEDJ#z!p!6RJ*NUB7bb>s)pXsX#IYh1nq0r7)(i&sR94wa-~8-04aH_&)8ilKTiQWZ}*dRUEvLYPA~niRcNUC!hz1 z#otM6Dx7QV#;RT(zfHxmIm~b$VqD%8OmD;8*}#t4MEx+-_RBpdnakYW5nrZetis)1 zsST9g@)%y}y_~dmf6{i*cJibUKwXIKeX|QWv-4dZTO+@Q;k9F4d?@BG8O>_?%L5t8 zAKwG~m}x6+O_j@+TL(GGwKQJcOz97qowZRXJCS=)My4z7`cxzku29CQQAS;PU3yCs z{$XKh#Z3ey9gTb;FE=&STn6w8ZinO;`a{);(7c7Jn;yw97&XLoRSwKlE!9OWoJJv~ zXJA+>R1Xc09HaCuy9tradsa*Tp>ECcGW{&BEB|WN*vxE`T+B(?TwIXH$XxrenZVnM zY*RgVAGA_`?M&;YvDrQvpM+g84yesV%!RZmA|ym_wZ4g4&FbIbaWEKj=x- zXQha|y?(hj&rIT}O_?s2-)omgY?KzQg500q6^w{LmdhI?dqPzN2a2uQRJ2t-yRXbW z{mQlR=Ga3xfq3xBtw_(Ot1~Or9ihv(@wckE)-Zuhk}hQPR*E8&Wp{y|eqEO3Ji?iDHVTJ0UkD>OMJ>OUolYGH zVo#zwp$c(&tufVJHSWlEqC9N(FhqphgWa$yj7(sCzS-L%NoMuZg!Ai{fX~Z6VWIC~ zXV4rh$Hlqje;_miKsBIrDJ~i+4~c_2&kY^NI_%?##=;7?(pRGvb%wg7Ln0kgjB2guT_Ju)BkRS@U&{YkXv9M+`(tjPSzla2S=+ zYLOg(hH}cWJP08va&dy zyRF!$*L5XM+78zu$MAJRLc23u3GzfJrSHf$#ES>@p z-~rN+prW9%V8vXLm0R!f`?&!h&xz~HgA_`ocSq!dy%r&z5|$K&Hv*iFeX{%rY=kfo zBrTXBnj9JdSGl*wzvc%V+5R#b%=O*#$DEkXaKACw$8OL`u~uI#I{t+1h4f0n`V;oD z6n?aEvT?FWmB4dAjLC)80zz!D9j=j~lU0}q4G0}Yj&x`-CjUwEBXm3T=Hs$lET1D> zyuNL{)W9t`P;5zFCuYM&ud9>p@>3965gMTJQ5VawKcNJvz2m92kf9yE!gY44BmaXx z=8RQ--zTzHJdbu(sw6DdmnR9(h`Bxj!+kaC^6w2( ziNcpAx^9;FIKA#l-LZfS2nz_vex%rI^05Nb8J@v$aMhQXa->Gdc~_z&cL(1>7!e}H zMIQ9kqRLpbVOMU!&)BIn$vI!nrO(Z;SSidu+z~&~pcVDH77rMYUSCi*SE1CPBOf=V zX#&D9FH$@Td&nwj@U00^N4;N4fx;lv;^=rXsa2(?n(<`nRQEn-!X4ac%PYbSqEBA{ zG9E`iW9V)-CV>u@-1*CqlljqbT968A$6>}cQu_ir(s5AZ^r0H`tqx`BgS22GHsc@x z;8fBLqFk`eBppD)i%Oc)JG}RD%f&N^!xfmD6*(h7!@Is8?DJD4sGZ#v2Mb~N3n>Hb^+t4C^llyOb3p(x zP|pmzV_SdS1;l~;U!ZW91N?YK7RS_Zb8DO4mVE&V4kVDBfK$$|=U*O4GQ`_UcJEz*2Jw`d4KrQN8t!`;>&s+6WYIr0Q)=gb%sPPJO_@gt zur)d_AN~xCc|f+OPRIOu59s=LB=a$FnG9*?T*k275ck5O_VMzZtNd@07__YB^ph4U z0r`zz{E~@cEUwjnzS+m(kq_t_@65gced!7G<>+tpWd_YQt{G;nsxLvHzA49NUQxtx zitP|S!WXopsb-A$uFXQcJjWzlNb%qoUPSVmn>?U>I|QDj zMdUdE$K|uns`qNxYS;j3rD!|4@qPZ@9+BBFLUbw0+lvs*p`-m%XSndXZ1~5bsN%|0 zLfa!1f>GAc*8o0Q^#Z;JFjALTgD4(^JTWN-q8X!jm6mdE-l^~gaCswOr1@l2m6qR0 z+LuSC5En%xi(2F0GPs+|cK9UkKh4a#r9Y(e2d#yYq>w2JY>!7`GHwAYw=a(|T`b(@ zErA{?{;I$XaEa!9OW@^KTbbI+o%Q^y4+~zeB3IT1v;v*o`q8ND6&#FpqJj9c(Vc**v z32gvAdH_l^o#$|VO};;kOt?yuMu@O@mjRbml_8OpqS_pNFVQbV{1J2x^OwYwaXN5| zJ(9CwUhDCy5m?Sr=Lu+OclRba+7odgO_=5Mtm&jPp`kN1Af$D5z^lojVX%W9%p|byEzS*Pu${c)Y^WX{$dXy zo^5lnV>B25?*-uk=K6HQOga-H@6@6N{E`Pi7#}d!vLh+r1D!#^mzJ3g!!nf>-8~rI zcVGc&XOUy)W?3A1qh0ODLl*}d;~?#~W~F!08LlK~+3`}-T$t%b0zU{HH(Xi;b@EkZ z?BeTYZB=6W?dSF$cqoAw`qS7=@QqkWX;POPkf4e9QT<z#{vy*Q)MZ6Gc6d<%v9&)(LVyHl1GVG)py~+k$>ciI z-d1ESKxi0zl6G)yZ(k=6oB$Z~(DW&1LX|QW5i35u9YcyWjk2FPGlbCiD%hO5hoNAW3<$-+O>AXx>L&YOCI>%ySHz2ylzW z-0O^xqYa-!qXrFME1iA$UAHSx0lDX9f3V=*A#D{xDKu*p!pSHYxR=D|*?|-B2cMhU zLtuZ^1?WvbZfY&s^Z3UCt-5>6agX6Hd&jj-UrIKvjk~D87(R-jPHsXO@fMo>?~3IAz}JUN%UP1u(5fquy$* z5aHP{{mF!?(gHJ5G;_822E@-S*k(3`VT!)U;NcE|(4d#aC7Ekyi!$dm1k*#0}QS@+c%X_lZ5&+_)?R!of ze7fcwC(MrJGt4*bb`0VEe?xUI<9l@In1`!PgQ&Ijj4@+v%t;)0ybd7c%}`Gm)!pOD zuw)_pD$7Es`O8aDoDFT8X%-*xLnKAsN4YvYMGx3I*=Nc=!P=LzSdK#K_>KB@I zGDEWXy2M2yX;ceeHYy3`Xf?!Q#P%D;QlwN$@ptunU|Znj(dK;Lg6Wk4|6Ju4%#zSv z;x}<*?4yf}N1Nw|so6IB3q6*6QzIO_FD9IxHWu>?i>E=u=IeE;Yg?VJ*6Y}2Y`j$# zA6Ys+IpdL|djmNC64RxbIFk$ub@!2Xt9+MMCa3I22UacSV#Al-rY||)yaHO(aV5~jgf)gG+)Y7rN`7^;$9)h9iuG1!8hFYt(_BJgrIJqWO8w<;DI*R_H6 zhXCm?wB9V(TK@6l$3qtYIpeYC=0;__$LfezfyULVtC6sdOcJFmSVvCeojaa*8keP| zr3FzM&aIssgxt^Jsxfw0C>Kk#?`L6p1!MKgqiPmfNO0QES`h>2YLVI)d%1Ces z%#F_YZ{hNew;d~4yNm}kZ4!TPdNGKkU;4L(`D=}&Ao5txh&aS2tDcnv^8B?=D z(OwT;z6$2%`!Mo8T1!CFk{h4mSvh^rdUC-@G;>SY*=zdi8FyVT2`^1e=5#i^vQ9ia zR^?gmuECiZQxUHm67$l5VW1+Df7+KVvhRYQ7cELiY{pG&@tQD4duLL$e1Fuw-p{?s z$4}(eO*Q6?jSo~`Vr;T-IF}uet9CgP{Puz8JKJhesSxLMSvE{%XC1+|BJM%?&s2`} zw$Q5R+TN)!);V|i{E@RGcy$EgtC`i6*?AvH`Sgt0&ljAcVH1;`R-=B$dbVRB=JDX& zv+y(&h=WU{Z`s@PS(nz8+j#Y-qcg19na5$?bdkq9OnsonuNoShC6@%7>+|X_Uzv5N zzIDo%ZrrxpHYfkd-4@Z+024aV$a(jV%|{iu=cq?Gys=%A;wYzHtV`JWhl{3J2T?vR z{}Ox##F*84bu;b*WHHj_WEJ;yuPF?3hQTltio=v-+Be`_kx!-<{ewOVNai@*uB+m^ z>fjavsV*vgp0wV%0KO?>YI%rPz!IJVAt<=v&%NF`?`ltW+b1mNM4Z)SRc6X5vsm-h zgo5GY&|Dxd3u*kyj%qW=(B$~pm7UZ^d;xCa8ruBk3~uK{6=TE`N7Z`w61Sv7tMQrq zE~W+g0>i6}LnAA1XvRF+a)%R>k_K&2qR`G^ObY_lI2$T1b^siyfK2RDpY)IwAcHAc zTU$%UCa4e%F28f<&Jq!dK2Q8}Ml(}#+>gU^E8Q4YiLj2McQrMu4rXR!8S>(!fWD2{ zo?UpfSUlyaq;UhF@Vx^RJ~CN$_Xi{F&dXOHR7X!@OZHxAylD6e$A?bNx=;QvDo2?Z z_KoKsj%E-t8_`|{3G-&1vI$3TtBW>}h4a6gzG1t7k;@Q!>k5(lgeEDkCDe_lLO!yS zdr{)myc9|7iFRlZubtExYmOngnxeFLO4bgWGz&CC|V^Foi>BYYcjo4u`SjUpO3YiW$mgOMlJep<*}_ z$K02&V;Vu%Pdk-X{}ga>WFzySbhk~AlT43f@G)b~whxs-N+j9WuPqP@ z+PhvsZJBo`$t~%Vm&8v!F5LWD5+WZS6^y4acdJFYHi$KwZ;?Bkh=XVxH~w+KnTwBz z=mt%$bKU<;=$Pjo)RMh051wbIA=walIG{UE1bbgSN1O%Zd&zpcM zB4#%>LhPFa`mfr?+)0FVPy`V<5f3#8h;>k}5URd;1_$1?;2)RRAzS_k03afM}HH0eYU9(L^Mh09f=<5Isy*qC2!sHa_mm4VBh0 z>}y;d_#&c<@=i-yArIcT_s&Qd-|boerL;YKqkH`5y>uHXA3jKx?+qS3Y5hVcU)gIB zlohYQ%1{X<$f@kQ*Ec^C*bK49M%yYd;BvB+C+3RzQ$LxV;MRs}n#sO1~(-y#BDN9Ga9=ee0P``cCH_%#f+yx^Fq{&H)tvM@w3k*r zLnsKHErNv0=UW{8-Kv)2+B~TSUluBnSPMV(7+iGw;`)I!IzP;{pN%z6A!k^uRuUHP zsPvvw*i}=Xl)+PpB`8}6MLSedrT#hX&@2}txG#*Bk1-Byg+Db8E+XtyAiu7<(4`l~ z`L@q0e^_bgQO_~>ye+(KM8JTUNy36!^9W~q`*kS=1(in0M(I;@-cb*%!1` z>+Rz01{G#+ThXi#Mc)s1^Tm>p2)KcMw2wD@NU|@$jLOEH^UDAZ31xca!dH@vdT}>x zjZOISAV6b=V6Qe5!z)qtp_KzG+{8GWpRy-`cPjc!6j6SS<--@S*?r}Bd z0yvO3cGN);PqdO=d@>zv9Gr;V||HSq?wiNM&Qh0{u~+0mVzs#KFhqo;JvQswfWXh7(8Nk0n~) zK=tz$V)2>}vYW7CB`H_0NDPvp=#k$H@hx&bsn9aTx0%J6L(ag5?w!rj1g0LZ7%6We zpo)4j)^qVN49aMBe|{}dkx)B+Q8u@{${l}!eMCUN`<5y!4K>O5Cf$X0jYy{Pd9FEE zA?!}}BgS^!wpCBh>&%P!x`;1?WZnpdF>ltVN}hXpIo0sNtLizmst3v!MAwC4=}{5eczUTk?!j0xY}`&o@HI9He?Zdr0DFks3A^6!)umL zXZ#<3CDt;Xhv?vHR~YA6vx5qKin@&WIN;_a_!?u&0-r@@9$C>0;q zJ%_hErgZmFZLPFOLi*notf&1pAiG=@XTQx zi<%6B)2k#K4t@SyefEZiT%MnwAJcYu(pqf_cr|u7oCDX`^2o` z`(tVc9Bx?+S+fsW2)E7zPFGiYoLpc~MR)NdG}gx^D(zs^Z%KMYiG7x8J)YA^RYTl^ zYt!NRD@KftvM;v!)9)xOOoy7GX*?5O7IK4VnlU>W%AgWj{z+f$tKtX+&kuF^dY?0( zy?8jw8XW@-6-qAd|5)Ws9g3v?BT95$L17-kuunP@_E_-pL`I3{7ALDK38gFiqMm4o zpHCPYo={17n|am&TS!9&SIuoG^Yuhyl*dV!Iy|h+1+{g<{n+8-BPpzxDjpqE$)IJm z0QYic2W2t!oW#Po{UVJokRlrAm;+X>@NM>cVKCPc5_h;P-&_usH{QdPiH>g%6>cHilJxM`ju&k|$-->ZyfVrd4@(Rz{|7 z@kPtx;+fZ2YjT*K6>dP67B_5Xp8_k zZWNZpz#g}daJalge${mGuyvX@+gUQAg&SsJ&0x~C95vnLFNrmG11rX3N_rRtKNoWP z<-sMn(@q3;`(RBGg|-i55en*Qwp_Ja0P)Qcp0&-PdT0c3Fke4uz#KH0(fh#S{Ea-v zAQS#+KDOw3#$@bOT4E`Fu0%pGkqJ6X2Pl-2u2$b0B+@9VpDJ}g366HvB_R!M zAC(BowNmX|Oej#)na_SRE<`F8VjvRG>97t|0JAZiJSx4cflM=!g~%2)I0((ADC%I2P*c1|;R?kP#*7fk5yMHm}CJTJ23 zOTc^I8Z9{PB8DJIpkJOuP`;C0=Z1f(!L^!C-mjj%_?YD`C=p6l??IH)L)&)2M_m#Z z3sWg4ErU|)VWk!>!Eko!I#votaT^P+BtuBzRPkE}J-*0WgYAj=%ZD;S57Rh;lF@Hq zadr^$7-1O3%9;ygr<{^IbT6#g0oB10;#)#>SWKjZSSQ&`&!gZR{K^3TBA@G;v#Iq| zYOII@Q4Dyg#MTMh)GX#U@tF&@3SZt$E`r{88qEi?y&#E#Ez49se-zDTj5D|5wyCe) zYTc1dL#mtp^4#hqpd^TQ0cCKGlby$|m-LPSl{Tz{6vQ|UK)Yr(hmRMuSjeR!I`_hvbbd|mD+|?ybuc!*zpxkRfL+TlmNy;qBFNcJb zrsOZ`wY)xYVg-E6T+ZETs_Yt{O)Ckc-G%`dJEw1X2(>1MRXK?CPC?8@PedUoD&y2+ zrnfT>?Y}e^DP}MwxXs`6FstSF&!#;Vyk@HlZ(^|<#Le=fy4=v-eVLtLIlP${+m%yI zzxervsaCmjkzyg7U=*|QX7c>{^N86Sgx5s|Rli!y{?6aQFz@k6?{I!l#vU|^`R>AA)w{c5TObrCwwBx}p{UbS1+>`m)F}+XYBf#qg+waf(2Tp2jvn~qf^20(hG#$^`*_x$8Fy9mQIC+ z3R%9|ym`E#q6cO&R2RJ>5I>cHauMaR6^(6^tz$JHM!sOo-bfrM3=7VE##9>vaJjeQ zxk$i?ISS~GC(vb#^<-vuYI|k}fN_sr#{nVLo8X={DAQcRiZp8aA2eK!`BbAs<4HjqA(tVfMsKKzs3k z&tfhTzx8L0fw1Uj8&etiMBm18im|d2^}gETDU9vR65JfIPVXi_t{5Abyn1=?)Zj?0 zqthc&)LIEMheKEILAYO3!pG;)I6j?FubX48*(N}xr6iq#1d36`Xeym+*N%RMluOK zHLK_-=!~`PWH|6p9y8KByLwTeOwinzuT=+a8AJiX(;u918hn!vFDPtnZ+9`4knp>5 zEb}8!RSe6GxmJYNP})*E1!PyKyBd8iIZeJeG`i6(X;JBwXmkE^bq3Va6}_W%>dWlf zWartjd(&+?1^#b64!vI3&^NB-TbH1;=eeZSP%)gzAZ@#Gc@P8MS0zspmAJ?<{NW*% z)LmKI*Eqo&dZsc>Cmwb**h$Jpj^p2iT!BkG)VGX;%wL1vJ%gtH0c&;CYPwW23p)Hd z5L#ka`HbH4SW}_NHU1cQHZKv+p-=S>tghd+!ANCkx4}uwLVdi_5?C-U@P#YEQgaq} zmV+{~2sFa9Zf)htv^vl5KL4#XlUitcqUkY?1fF~}ZX48?vFn5oH(WO(c`N0mRS z^f5&UR6z{AH90k@bTue$NS6fg4^{I8aLB0WPCIHI8LxgKX}gGX1zy>o;6zGo9>4%{ zEfCE9Of2!<5lynqG?&b_@$qZ!oS|{=3tbYm;hL{F{K6RqDKZzPOs-VR<&G5;)jsLv z!EC%bCHyYs^sMb#qUEN5OETYKE}l}V`x@S_THkQLdP#ai{LrZEnZmWZ+JVV!Rboq| z!9_q>Yt}{psp*%Bj1TFOwi1UFdCtCDfW<({Oh6%YhG~WTA!(hsa0quCCeLu+u@EJC zIvKJo#<5Z>Zbp@^J9fZKjR#=Q4Y#p@P{Fv-R2fAxpg2D4zC`PjR}49<+-%cd=OeQId>r3;E{&`I zLl1MwB$F%4LI^z}s|8`4>o_!pIY+4&!=mjtD^u%fG^ApB>p*_c93A`?VV(W zDSJzbsRj7`Wi^o>ZdKFMjvlBe9aeR3786~`Af%_PL zLt6ikBLCIQC3JY@gWNZVd?c|iA8QU^Ge)gvcm1-UuyNY+UFm%uTff?XcTgx4FMUwJ z3Z+YHPFI4oO@9>USX$RZ6}2rRRfT4*?$5+V>Ih6yxg<}DN4ankrP2KJc3HN`O!C*J z%v3TIMLONu4@4bVH;4P8YNvQVd$~O&uzMUv0n&e73DN*h9dUj=6>lLauZ{VpHT~QX zg_{wWu^B6A$+Lu$F4ss8b(oUr1 zt|?_@$McrEetk4k8s!T~GWJu-=ff}Y3cmgovfQ*#0=L6@)Gqdl;#l%MqPN_%dNR4< z=SJJ;fXQ)JNy0P^S<0Y>Oqxh&Ycqpig6kdZ-77v6(Vx?c!i-FL^&;_*s2|a+RmG*- zr+6DqIIZ6z=)Z+o0&Ra~!lRv`CV%fu*}zOt4x`z1Schdtc2~~~#Lgt=Q5-D@sQMnC znp^QjsRBx^Q7o7l7&`j9Noq1vj|0=T41sA|r|JVfd7`pt$X6YnmO=k3k6YWy$@gMeF0h^u=b$ll8j=5M;*z-nf!e=w5Qb*iDJ~?kGqszOE zAT{J(8&i%#g}7R%%4ZIa>|S6pviN4A`wPbokAmqk5yQ_$eUtFv#3$)TE_`+AqaU+v z%Ngpw9w;f6Ge2g2c!Rnp!-%>eVmR}Wp2j5Upf5bZRNRb7H&25&^9dg-ji;Pjkg_7`7`eE z9Ld;lIF#Jw%n$T^iigf-IP4&jtC5ZCb>I)wZt64mVO!iBE%nEu?T`lCVkb!N)O9Ai7TwH+l`?{u78&6Tu=LSRPdIiaZ!G4 zSAH7W1<1h)Q+sD^EQwuv40$AP)R@KJg675Ihi?s~aqY7{reT%d&nO`$M%hx2>^0usZm6oQ1 z3khXi$v++=(`SKQZ{gcFqdb5MzQEpd5kXhRIeggs(|PQD?0ph;cJe;geC@EdXxZq@ z%FE60l<6NU@hIwZ?zpe`fz+7r%_vdI7_N0LRWDFia(qhWqfaAXx80uth-+6-QOuuc z5G5t$$8g%><4`RMj%Kpzr^p3)>QbtDrAgOxvz|ew7~*K;6EwdH>fiZb`ji^0U3!h( zDd^}hgf4VaD9S=CK%P!aOhnkOCAZ*d@_6*sF=)nhY?ckZWL`l;o( zER;$B<_P%tJ|$v10J=Zh_6Ke>8-~%Ts=S2>UW#PXAHoTt--*)$6>(xzMonfgl}MKP zVkSiVc>XLxK0H0{3{qp4^f}^|z5v4MF3sy}jYv_Y4jXcM~9IN4?a>_=~!HE1@cE#m%d68`G`#wgPK< z+k;Pel-%u``TSi?HD2%Mp~xs=i(O_WW@cpU-}_wx>VS;-)O3&MZ?l2JHP--omaB^S z89$3SVKwk{#WcfkL%W@VEuMvS|J>2$P+T~jZ0Ch%WScu~fUQsm_ysGK!-EV4>1^%t z?SL6>wt?l13C%-ytFeNs06fAf+iiVo`36+hW6<+ICl3b^Zt3NkRfWb7qBnP&nti?Z zAc%E6COd!oM2XN)wT=K1g zs|>8m{>+x-J3PcU)^lOQOz`+(Yx_lvkkU^oXYB68Rv#n%?jhIiMNoy9*I%YO9krQn z)$x&(k^Z;AAOyz}S3yk;L~N3Wq#{H_f~vADSf_~FI6oozW!oSwudF)17@AUR$+_ZqHfOOU)#f(>w;3(6A*f{ zJKfUQ3OMp@lw4FiUDjBRJj$U_4%+nj(fP)!!}mNFbi;D62pe6%c=ZdRzOvf7Zw3*j z0vUQL>86(cVd^i(X3q51-4HeNpf1Z>uI-xqP;Ht##K@P}B~`E0&f?sXkxvzr6zeaX z%zN}l?c~O(CfgfP$y^_GW>QoEd^VF2z`=QV71e3jV-U#OaQ8wNxIXywvc{~h_=!x} z!+YT7jrR9ibzHY(zjeRtv>sP4f#5t=Vp4qnjP`GAb*aX|ddQCit|NN58~3iYJUUB z0~~&BEq^t{K?+;js;lEGoz?|x#Ua+(OF)b3(l`-B|1?5j{f_)N#tq&;h)idJOrX+V z^|yh4)@t3>4Q_PDyYwl=d~pR1oH2Ko_!2mcN}g}lO+P=vVfx)5(tM0(paJ?9)~bgW zx%A2bcaDpGlWQ4CuMxGUGIuWSz!zWnjnFyk+4y?E{r8aVk$2-39T1?3NF(YM)(NFQ zWii}9On_s7v+SGtD89s84wlE$7{{Xi#*hRFNsyJ;fMn-c#vm@InZiC-@B8hvZX`fY z{Z6*!;Ngtb^42u={jD$I0PPi8{)9<*B7;Etr-eP`rDqLp0hQ=zse7`X-+}J>%dyV> zp=sIU#x2cO`{6~@$jC&`QsZ}`Ko2Iz+d5Pm#L{;okfhSIA`Y_piP$@)2e-nXIf)So zfb3Mm;f08@59+rZ&)5|8p!}|2IvZLwefry2H%K*Oek+s)Qfg{~G|k;w=irPqc93p@ z&`mbi0@vR2=U1P!te?-v2k`&~7~JG+_V58xXxPmENs&EE^lj}fm|r!0WeM^+?BpWl z5};RlAA`OkrKiTY1EvBk1w8x?#y>Z|W87WlL>Ggp7+(9OC*I24U0=$tQmzye*xfdN z`SvU~0(qY!sJfmNS? z+n4b=&c98aw1g=31e`V+lRN#Ph!DSV3{AKqkD))4Pe)Xp7!RN**C*`ysTyj^RPfRE z@$nJS)7vEel4(Qg&j7Y_INcZ~#kcO%yShNcVOn2{x{?B`k^;DXbo?2Awz$16H?B;f zyCqpx#PL84)MZZP+&8Qh0UoxFh+tp7KY&6(9f2A4-K4_6fxGJ=$hQnCKy^9veEgMv z7VILs@$rbgu;ixBb@Z70l7h2Z3X|VH&#lqQa*)F%sYx@uhlP+@G8Le#t&nJ-C+%g? zGVYzk)bI2&GpaRT|HgBBRZ-OqbycksqxmS;>%S4Ym`f+Fq-{-tK1b?fm39jfEc&M2 zv88k$r6gy4$ellW?ER9eFJ9luar?>FBXz{uJBNT_sxuiZeC1&57f zGvqP3N->X`jQ;TP1(;QES)j35x|$0fxsm*y#`(POn(?`Prlws0#aAm3UR|$n%IkM$ACJV&sFmpPHpv3eUR3|@n1&pO z-0o@Rv`q4)FI@}s``uZ1>8KV3Mw1EHy<5m9@n|wjKj$5KjGUT%k!!l0^VYtZA+@~1 z#jsnekSpE2v?1#i4-ZqAY{1|twfExov@$!M3B9Qh1ke%>W_cV~Om~8aiefC?JP`8r zO#}@b5=mElq6$#{7$^rfl_j{@ZLLT5vY1+}{*5dxaX|$+} zWZFYbBX!X33EK2@IL@ZHxU1*)I-2Q7XT;<^>S(358TWMOHPP|*^Lm`7JHi8;!_wD7 zJ{FPSvo8FbbE|j^)ZHU@+o<8ui<=V^{zh8~w2_KmM7IsBc|w+G`fQs7pn zqiFbG;gl~cW}n!oQ>spf3-bSwkmb26D|y0uq_W*CF)2%l0eu@$hv{PW9_*7XZVkRj z(3h*a$LqN9gJ8c>*o=|_5VILX;@pA2#gML!l%#~B%jW7eEYh#F#7z4;eHBCHx9J2K zs;LXqS9ZN2Zd?`Wrp0o;1T2;M?Os2!2Ip$ zSiGlOh0hv4P(kA!@w4>ZXW8WZZ=3#xjJDmYU_$7L8!r^o&?5PPjO^Q4bw{Wdaauv7 zvD>kc1WYs zffdTMB6jSEHNF1b&~02UPj^shO-D#r>^*%Zi6=wj3+ef$%xxfmoZpN!l@4Mh#(+Gw zEEgwdo>dACw1?R7o;%R!-i4+UrK>|LHxFVM0JY${p{ZFV6~`oJ8lLxO)nH8YKr>7S z-Od`s6*IAzpeUcbm%P?MEx9kr6o>ZM9c%(e+Htg_MEQZv>ADdqHCB)(JQTf1|132w zsZ1(S&G$lOw($sEv^Vg=dzU>L{#(i`g4*TySP0F&(nH^Qw|#txW`ow)((8>b!Tp=Y z(=b*leRg%;>Rr;YyIcM}T)=baHV-e*W%!iEyMcd!ZT5BjDLW+(0_GRfgZ%CR$qaOd znyojb%RXrlHW*JQl?rgjE9X2Uhs681dRHSE7ka7l>9gb&O$CTy{!~7#Io0_5i4Ar8 zd3UkAS5AwentY~a8~N(GvuLd95e@?BgokH^CK7yzx@3WE$JVWDt#-D)5BLmKtR2gU z=P$t9p0L!ejXsLs@*w7f&f^;%5s9gdqb{7=i@VBkvIT*Tb$kEywfxrZKDu&A9dMj@ zIdvZXQ#GDY^P6(IGq{3Ue&8^sX>Uk#ndYk?%Pg87Z?_SW;sDd*qr$eA`0l5$1|MLv zNFeioHd!S3+I;(JKdDd&Tsya>hNw+yFqLh^&$X}YaakFEw$b*k7q^&Wf+*p$)ph;K z$Y9Wu$tVmDbrV$KBG#v0R-H`;q`z0XNxp8dg*n98h~_T-4x(cO&r7dAUK5%km7KoF zK3_x4sMH|4w1g$!x!+X3VEY&+glq2mwpD(h3vZjV1z-rESW}dXbg?}kW@*# z6ES}@vgiVb9;Ch`os3CxrNoAs6Kk&+7KD{GZ~cN-j~e_;=T(8gJr9L_!*D79J&}CH zs7&_8bCgk~#&r`JZUJ*LG#KsYal^Y;)5Y*u%GMwbaaIJm+{)}9{fsIkH4&g)stfWv zxPtq>;{qm|9_h3DBm6B}bf|sLHWYQ@JCgk(_rlVGVT+gbNxcIIr0Tcr zl(OiY+;~+URUHp<9wxa8a|;H^cF9 *#-XRSJ)V%>u~dfayYw|FeI>TE(aFNN6O`Sfleeh%rs`3e#tNG+c29 z+ZLI-ZZ@>?oZ(%yVIl6$~t!=9Ovie#!NjgGv{zVVT z`Hi~KIi;~mru+G413{do0)OfKrnHPV#9ss~F(Q)ha#lG#yt_XophtW~Z5==*Z?g<@ z$-xUUZT0-zeNK5nk^G9O8u2`uc}*ewVS%-!t=-u(l!yj*+3W?O;ArhZa@*+n+3G)R*EvuLu% z4wXRm>lFgAuwP|Y&JACJXb1$5h`&?BZwA4(^>-u>-sDR2S~4?Q@9MTmd0g@PjVEb5 zZemO3;Ig{@Ci`}Gxojvy#jp_Tto)H(^A_DmDB7|O2a*`he{kmuiNQ~oy<=lUh*f+yI9_#`!@4e zquH}Q0ugdJ>$O-_GMcKUCj>y^#cJunJ8_w_ctjCZycA+;^Jcm`@;|8gmMZ{a9tDh4K1cSJGsJ6cnG z%-;}|^o=+5S)A6x`v;2DSdWuNcDgs*+6)%UOk0YPlt6N`z$_*$;;u`zXO)8INF zYFJy3;)0F5#AiSIT^PsTJxz7C&aJYJCz z)9ZV{>U)bWEtt}J#Jy;DbAFZicu*_cFnaMvxM|a0!ait7$k|hMA^Ml!km8}gFFBU- z%GJ@0b>A3Oy5b|5M9!V@NRe2^{po4+ zI!VR(Ncd)5@~tn!Pc|;J+$U;ed|J#iMDxyI2`00v8=dOz9HUN;vMnjM;l&3qDdwuj zneFqcy6$*}jJp@43-$F&$L))$6_v|eRLa?g$Z7iZ*j2I-m?%LNcVrGnt!%4gicW&j#-w7LZzhKQBf)*Ith7`WmkAY0+4BrC=z-Ts36- zDAAv+!acd?`4tL2^m%I*QtWQnO|O?im{*I09Z zY=OEd`|{mMlreSbjkNBNREj4tVKoL8u=fQDAZX3|-i(3BJ zL{d!xiO#8PHZTZs=Q8qq>`+ z@Zsq-Z{+!@#Udu#@5RC_I}$30o~~Mtpm*!VkWsn6axcaGbije_XLs>MS8CV}bHr+L zJ}M|$>9XV6)Vc=RwY_QSoCGnpjwi5`;UFuzmuduyd|UoB_7fSjYMRpBIt~2hegfaw zxO4F{`&0YS_VQ-F`{FnyqSeb<$b}-l*hRH}a6~)ZU-rOvIonTH2E-N}u#!3#5*kUw0}_4hoD+<4 zTG5wmwATL_ImCs?E5Th>!-wQuOJoZ>#Mj6vbZ zo9U$MvU~ElU&COpNI91dlj~U$6~O<@G#$u8`@QXZ9KUAbqnTSy)ovcPCrosnqcSg> z>PmcUX!OMvS2G-P+IKXoT7`>8GG+nT)@Kj+$p#0QYZLa|bdWopgyB4R1NWOR_btL5 z?{Qj3hcm~aP4Ht66k!#209rwaqgf-4IloBSu!6B)IL6vtY<6E3K#2hOeIk_thKk5xaD_RIlE~KXZi}yCRbRoFITBsVOPJoblXFZj2ld$!d z&#Qpz#?#!1=fA4aT@RN1-o%aItXkgl2J9Hn=bZupgg*fsxC!773D%PH6^eu6IrsN1rzH9V7!04koh$E5Z%q*Vgpi$=;W>~67b%1M#7!z zCi3{rVc_|74ooO3mc0N}#rxmB>jAZFT z?qKo=kBN;{?A+^|LhqS8pSE-^VgaYVdp(5Y65d?t@q1Vfe1t4$d?*K{)yLg7h^VjC zwh6@CKup+uDd|Hjy=uljQN=O3kkDR$yza{nK-s9J5g=~lIg53@W+Pm2f+HVpc>rHQ z>w0(L&}!GHc{S&|dXaWq8*X2J0nc=${zds-fTjukqvjN5_c$9ytVit9{hknVrzWaD zs(t&y57Y7+&aTEHJQC5tYC!tl^)p*X7S`7D=Hc?O$1%^%m6qJ@X94BE%oF;Hnz6Jm zxz1|XV0uX!^MO&&S7?*xF{|@U$SNiET8Qv(YIJ0zGg7PIXR_dqJVF!{GC>p$TDTkw zVV;cO_j$=YSU8a-)V5i8)QyNdjkvjyAYe6`P;fC5JYTFPAs((XgJZL80X54C};eU?t0+#eV3Izo8_oQWHAS$SE7%BettREO5{r) zDM0}fA$*L4NkON##tGvHgH6!mfCAx)Vo`^5UBY_nyq}joF_g#`!m2IAt!)v>@0?#2 zCCam)=bd?Ka_)UUB8l`rx=d;L0DNKyZRs&&fRrhNl{ubL40bk9&C9uj>jl*LmM_#- zm`4dMt#s?6vf8AHG0>)UMz@zcRDJXgl&x?__M`7>ZcXu~noTD3 zusIj?6f%jplyR6&C=kCR#a1`cJaWn}vrlx-$xwSBNxV!vS?BqP$}7B1wN*E;w!WIP zg!_}Tt{4(5ep|fNE_5xQGLSquN1ex+>u()#{dZ+8?77gpdk`Ty)5V}U7#>5SmMvIVjb$t0 z^|u@|LOe>AE09f>E@>cbfK?O+&0QnH$-M2;VwQ|u;LyPrmq^){=rN;RpL5%=PWVIi zfc%qND`OvW6gKbXJ(=FBfBlapdU#ufNL=bM)WPNhkAulhkV05!G{C6{i@0pwYoar^ zN9A}&6g)$;5fS|;Ed3US20u8L(6ZNT`m<2QI zwq@ghZ;kc^m0n*r*Xx2Gb5)krO(%|i6AAs0BH}Yt()eF40Ppyw;QrGAx9c&t+8^B$ z=8E1YT3>~;f>xY67tuELo%33| zg(WqA$PH5?V~=zlJqEkEXO33TKRl`ET*jY}v&qW8UG>A8l!+{K(V)WEZv)~|4ih?_ zma^56+*4^4SpGUfxwo7^IPNE(eF5 zt^PUm2OiU{hB;j&lV3o}sB^}o$tRl@lr=za46YeGa^xQs21;@Vl&6n%AQah9m)i{H zOW|OS6ggZ}Yy{L@Mj-+5wX77^R1)Htv6+Nj6d9+Ix_$w520N!qk zJxPX5F{ITjc|tYodK4zF(c$I$A>fiF_9FXUKhSAjWav={8j)V!I#PfK>d-^4MLGWA zM7-jlx1kfA06dS^57sx93Zm=$@aKoh{5wgzziV^jM_%iP77{968{~b?xPd2KDZgWp z4hZ<|`e)#8fo^F%lhGRB8$(djtj5{9h$-ln~ z(-c;XIe)ZDr`($*QlAxgKKmuuq}XsR55LWGWY=?Cfls%DpId*ppJg>GG`E-`xzmIv z=C_r?Toipo?JBG9UH2AfG@6NRSJWwSiQaA&=}RAZ{~`beTnzB&P7goI znvXGW5@%Ei3dJ%#aiWHJQ23B$1-bXs6ZOD{d3(E7Q~L#aslxdqYoKh8Ju~YWM0J%* zz}@64P0Kmy&H9qA_dD*ex*Hzv2BOlXAdA#&JGxR~ezY<9BVIG>ATkxTo^FOnJeL7s z$OWCBfBw7D49e>@tZXrnlDkF?^Mq4xCf{ttE{Vw$(Isy+Mkj&_ZY8?qO38_m%1Px9 zEP!vImLqF=#Wcz~`+2&e$)LMvv>&9YRgFZGkzMCDzPUwk&7z!ZP9D#-}{oidCl-)^1hEUY&W2UUK}0QVuG=254WO^7mLwSFBy( zRX3(q5%~*CY~>aGlajVdmKu?W@&4)fH(rMtWyrN1V)Q(B_G6C zGI7PY(Zwm~)<@W?GP}mZQSkHbp1F^#cr0_Ed)YP$_n%&zOF+J@6bEQNnEDpb=GP+E zpnhbs^FEo-R~L~3GUWP{WapBlSdffxF{J$a5@y0rJi?u7m;bdLMJXfsjV(=fP#U?M`)9wKd)G~0o|vwD{OeA}w^M&q1}LMv zpUc6&Pr`WKevm|*Uxa^*YJc!LBGa|A6W53dB3gJmFY7v64#JP zkzkoB7)m(^l~k~ewm6(pgbG3ntVDDqs0Dl;ubpI(d{uhf%ZAdxg_yL*F9 zA_d1s!STFXQOYHnzEV@FA6>mgtUrI{rv2JGiFBObCcu&^Xzs>^{sv#EQDWOMnJ%Db zf$O9Dc5HDbtf>keZiEh^$g$hUNB`HVM`l}s^2D*x@1%COP20nMQ zZZL1J&OT0(;vPaTy30bt+m@Ks+))PSp9p-hr!2>c z#E6-FJ?A+TPhPKDFx#qo1>&yp(R`Rl$;^V*G@3PDbj?Rd$BM1=COv7yWs<>gpV;;i z85F0-w8wtuX}9bVMYpDZ1foFlCH-kXEs;i0@^|-RKXo6h&ORV3LH&J$5;@ZamNnF za{El>O`26G({PSBSyL!b!bwPBHf)uJG%5u>1d%m9LYxwbgXNwcn8_R}Qw;B4B;>EZ zhVWCGdYD)6&D>zhT&A9|3|BOSID@L2h&~Gb-zvGEYSM9R!K^WIQ|Kg#i+yuWzWTo| zQ8}|)u5g5i(Q^#7e!DCF80YUPe0o+-b3eXsJ1^$#Ia7azsn@0o=O#zE1am5=`vYTu z8U^A2-O@;EI~X)IQ17Y%sqxXNQ<-+}3%sNh3!&DXp&H&!6Qq10>OLy>^wW*yIq2N( z)JYc0a}VQmA@x3_%j9-P7-t$~2a4;ZI&|h7dl}3Pf9T1H5`P6!HCs(sFhK&;tKSA9 zlPwW4^f5mlpb>HwI*P@g<|JrA-@s$zs*0+GVZX=+b$pb<)I1mG$n*t$g*(JB|CvGP zT9P98q+5Q}niVJQ{X0;hax^n)Jg2ITG`@1Wj{fcxE+%EotcYjIOU}`lg87`+$R2SoUhz~dp`a~)69!e>RMC?O~ zaPht5!-|k5J;-<^7svRkgi5l!NoF{VT@45uiGipu%u;Zq z5aWKFqsPMIf1nKGs2;)#iL8D_Xn>}YdA?P@KAQ4SFeI4D{`fwQ&|6R&7Ei(mBXUM_Wy1 zcJ^7F-n^q+>^?lXifGJYuA7J^$kZ_?pDwcAc9%{@Qw1+l8ra;JhxG98D3NSVL7O;s zrNd44ZQA=l3iD-c1+) z!DLP4p}qZW@=y|D(tqOpoC{l1!pDMrQ2ZP@XoKzWt&(fDXIAPQ#4nM~ix~Ldov&j3 z*q-$ecYZuMjQc6&ZaWVWcPJ~dPuC~7nRP&oRd>~6V@;emvNL0e1{5%eQ{3Pvcp{b+ zV13Q;X8RM`zyB`myVZ86y6i0DMzg4}i9`n_v%@5&>o!QGw;@Op1A`1jY5R)j@DvpH zD+E5&-4acDESxG+esngmsYJB=oP8bs_9nxT80~~%puXqNk-8ltcoR#=J`~Coosg0* z(JNda))Dl^CqIA?P0qPmaZ?OTaG-(q$#bU=aOLr35G>`tjv^ysppj~zxk4N zLjcD9{CJq2yv(6Vc|n5@6okrZAYY#uIMi9{q4y1lqG=5=oiS(Vv;=tzGUz@AY5y=V zRoF24wKFg8uUspZBFWCn7ES{{r{#CgXVS+~I3$nYQT|bRzsw-C3O;Lp9_bX&=`#2Z z1H_Jso0xuEKFrwk`vTM0$zWqK(%ls{`S2zGUyOx{U0@Rgpf+Da!7uuCG%j(ZK<5Jv zH0r%eL2D?McOUjEdt2F*27j%~gf0r>MLy4Tk*qsY&|&!`!`Qd%{g&IL7}l$y;a*l- zAmA09=J>U`SzRCCG^alABHNRL_ge8Jug^T>wUrYC)(a-w%PQ#PSQ0B5LDrR3aVxS}bBds%O4rpH*rmvCDzw$p z*1ysjDm*w|tRr=csyvC5(ev{M8znQ{BaWCF;|wac(#!N?O`sfZZv=I3_qS)1B#&tY z12x*3i$DQ_@%~CBZiyWP(9$j;Upp4LD?(?nb>UP(cG^C#H%kk+z~m66~v7(Ml0hwj9@T6d-qoB5_b8y-sVBc$Gwdikuh zUNOUfCIHt8$7-oyB;85PsFfl+f0K0J4Gn-Omq7p&bvMD!{Mqod;IbKNRj|r%)afNcmV9MXsO#B z5x`&$8XC#RkvPZTKd1Pdnh(oafhMsNk&Hy(uDHb;W^#f zypTb8qRAP386|+F<2wlfM-YVH;n$+BdA8|x|G3MGGGiaCupQ5>I1Rn^ z{E=11D^fSM3t@Zr*i~T(Zbi;liGUc~C3FG{d`NQnk`w@+!}5uQXDP{)&aNtseE(^p z{FG6Q=dr7bjAR`9N265xK~H88;rjb{a92IMda8T%4t~l5Q1+`;rP22^8G+jycr>7| za1@H+ciZ}a0+NSnxE*HSdYu41wq_2m`{H0yq#t2SbDjEUGAI8QW^9q*%=A0xc0hL^ zd-vX%KR@SS;G%fW8Jopg4cDvVU-LhrYx|=1`%=XJE>$3gqiw7#R>=I&+LP>vD^zDH zlNzM|)+%`?I)#`maI5#Gu11t4TTd64OpW*`!h6*PHkx1U*S^EMc zw)?`Md(&v5o`ZaK-Ss39*8E!%4F`*F`w9eklRYrW?^1q4jX&E$(klIaj6tB96;GF> z*uD-Foh8nr=y2vnO=mz~DndOXFwmK$hA&mw@~$bob#j9?K+j4E$oYl;Izfrbp@Cwb z%jpC(y^)|e?6cz@5)6a*eW19n4bJc_!$)5laH32NJ4&|c#uZR)wD5;yMlcEUTA_8f z8xTkg$Y4IIg$P9N7^P`KTKCRVT%vwq2(XW1k^Le6#ZE)o&k6}s7ZPv2B;^9mpft!d zCmK?P?FZJXa_$cSbgf1DwH#Iy>Egv9;hGY{n0Kuxfx!0{3EA-*_;0}3vWp!*xVPEP>D4F$FO3ty+jp7ky5`z1|2s zzX)z;LlE+d5A)!PsgBWg!lw^FiT^^6Ae+1s))Jw87r1F>3K!7Xm=`gmllsr9dyi`% z58|UH9tXYs>SHJ#k|22w|LmqfU%xGO%NR#Dab4srfHT=C1hM@(nm>rX0uPZuPrR&@ z=V@CcsR?rfj2-&zH*g)%rZlt|_)^0^>cwYM=#r=s1a5^>i+$T&5t>RQcozfk<9cjj z`dkbA#Z~4hdXylIvAN9QXepGVdmQ(8H1!(9t!z3K%z*l4$Yg`3=~9pY2O|%%E||V1 zzwN9n7@fH%D{Pctyh2J5L8{KK+|0Z@%#L#9Yu;q(C|f)JupvGTmtiy_4ajNfuZ6Tt zgfKEpALs+5GBFDV^W0JwRrVfvAuCYleO*5!G=rQS4r5{lQMaw*DLE--gp(;^ zIM&g0y;)GqyREs+ z`hf8YUR~87-7?anNBAI*8XFwfN!pf+@nIr>U3Zpv=>ulw6t-gbL2r`@A3B30KMyk$ z-ePB}L}eZh%gppJSvMdcz=Kjy!$A3FJZXA3xu8+vuaU}2!*luC`$AjM+U_A}Lylb4 z5mIlNjUqIu$Z6+KNkGhxc4Q(X;)xOugU=N=E-;d%)AxRer}VFiCLBT#eoWt)PRLw5 zDxyi?o5<@~TcITtaswjQMVzCum0a*rPIkg|9N`Z#^r)KH;kpR-A1zF*xIfYAl3rpQ zY2xu*l8Tjl=$px0<>Rnn&_fc<(lSU@WGDt^x1^279_cm^(q(-wPIY0suE)X*(kNy-*4aurRD*f>BOk1wLXCsJv3i) zCP%*B&QZ}VjQY@cjWATR%{=5EG+cRZpC4$B|v=W z3zRi&RP*b-ka%*V*xz7bDefY3!$*eR5tdoT57S`fnu=9a0!!2Pw6KUngdfD|BJVmk zIUWv#pW0-UStl6r-m4J@^w}2N?{P5Pmaus&nj{pC*c9;(Tpxfgd1|5{vVC&u+v0hq zz8!;TD~thkGN^eG65y7!&|Qq)HZ9o^ygbHp<{^{-_FW=sf+WTx2l2B9H*|F+(PgVz zWVD&~ce8-gVkD6-BK{FcknI2)_T*4u0RM#t>bB7G#woAt-8zom?Qz|alV|A-Ze`yP~$GQ(OIo1s-!TLrL~9` zEM6B6(pI+Oh;mS|I8G~AL`nvh^6BDd@ved7X2PvP= zS2ltgekMNK*^QLsa!^`cF~XB$M2T{K%BgXLZT#USVHhe57Yuj3QX|szv_|%b(S~Cz z(pNucQ}Gic-I}zg_E38x!NDByN6WP?5!IMF8I$w*O9Wn%N+)RS|) z^J>k$XfaHBz03nHNtXjUBc7?*WdimTZBsTv*B@?D;bFs>-aM(=jxze@cFn zV>@qVL`VzBq}xf#HC_I&pD|CRJ8E*5?3*`)QRAgG>)P^le2H<$!6-T_TK3hP_qw?aDX zNOIrx^S4HmGOo>f&AwxKTdh?}LVdp_rC$f*{28 zWRZ>~Yt&ZtNCTszsapMP6lSo_L0$P}<1Q4xlOHD3ZIuvC4ID|AP`7IgW z!}W-C#!^$Vgiy2q{aVVi9wz<4N%CAM_jj=FT0nv;8mTRJf!YrDuwW7d(h-gtx#8?A zX{nWQF0#2yoGUzI7mFAxL>O{!)Fa8_=C6LQX@gP9NsprvEFyp1&A<@_@+shn=vg_{ zCD#{y(l@%mn3@hLdwSAa+C1%hb*-5e%he#bW5x{9nh<;5?HlD{E@ClDHRvryhSIHO z70LHU0#5y_0ueze0Z%~E-tl|AMpqT|V3F9J?;BzL=5gJRB`~Xav&r1#6Zno5R-EmX z)%dmcs0YQ^W8{erdpm>yfCQXy5mF*qaDdF)RBIqQ8JPPP!6KX-x(&k7PvAM?pG}9c z%WJg9-H=>^=iazKJ~kuw@SJ(RN4`-+n)*#D!?i1*-D z{0V{xnnwR(%3;njh-?>%xCkD=fPttze{>}@+Y5}N$%Y6=mn5thnEh&A<8G=OMrKzG&Gew+Yo9qPHI`=fh%nFDx^Vk$Gg!B}461~XNz?0xJyGc@t3*lU z?E1RbqWvRe8XE1tu+ZIz+?`twD98Hz%fdRqIv2&q7G8x*MY5U3R%R4xFUP57@^v&* zVDL@Q8S}!c-$Gy`xG1H8M2}&E2(cD5o%cUw2$KfFIbX}T1`vU-4FQyyQ<8QwlX2yY z$dxHfF&kbV+BQ?y0Y7Romx;~RUs6Ote#DZAKP0J*CoVp3p`(*9pU~Pu7ObEtpk#jL zaC)uDpce-ZWV?Xy|ChGLZ!nkS%p1aA#CP3C>r5QEvmfSUq1y^4R&t=VQMJvwW_9kYPPHnC<^8m*en8I}nn*J<-d=!E7tan>6B#Z;E>giDAoyekWTfhuO}G>O}l`%0;585E8onO)X|k^gh^Zrc0k zD)WN=>Tte{7ZCLG!I>Hx!<{D2ovfG1<~|#XK)96ROGyd7h;9K*HG?<}`V^0E>!yC} zztLK|XIT7+C9wV+GX4I}O0*&p5|Z)gshD?uVxImX*r==8X!<{G&=7A{{lhC**sX7$ z>);-29jH>*C5`892a@lOblm}8Ohm>3Yye2tK8r0@j-WcQwC7+GXq$c)iCK(Jn!o3$xV4QBM>V% zeM6If!%}`m?=)31cgoQxMve>8^mj7%tg(>lOmnMey`uZht#p_6hSJs!o@ppV5i8o) z7r;hYtwjrxe%WzWBj?%9%Wo4EznS9am(@?Jcgr)WUZvu@Yhi`hmo{r(xf2K2|I@GS z63g7>B-ry)4Eq7&JBixy&B6G~bjDlgCfBy1T~9w)Sl#-R9u+zt*<1nChE($Sn4gy> z7eDHybb<@GibQVH@^Z4dqI30SUq2^u_g|OdKP04UAm-{_>{(sDR1m%Aj`ISGducf>`7}HE z!c>ET{t`4S3Q9-9c0pK5r6u+-cgFaCYiEOW%oRV2cGEwXvq_~t0p4;$|Cq5SpIRoD!^?BdV-ZgUftt9pOH zbEND+M3j*})N_OW&jlqnuV`taJuTY}GpxM>LsH_W;}t3lx=5+E4r4U*8DBOh@4S;`&Ym9%KK9%@tl-(!{~xa2GOEp}X&c2IfvLrh~kN$J3~$xkm1=qS6L zA8>}e0UvEW$eYx33{oP43v~ZT6_pSj-1rqen81}CeQKi!uD**4>6tJ6$b~+;3eI^g z^nFb_3#r?eAb+u_k;c4l7Axjkf1kbZyX?juM2MdCO+kBBOAk=-9Oo9$QNOK0(r7!s z`^ltQ!D~pWCoQDN#bp%%9c<(_T;SSm>^{|v+TN{^@mvC;K*a%>FhgT0=8z+A;5q~~ zHZI5_rf8L6o!7`&IvFV%>Ze%8GR5k?i@-h2Rn2b+RNwRclC3?wKppD0+qdzK{js-> zvTE+dCDT+rrhaGFrJMcZ00(IyU|(+T+Ba5-HY|QM2&y9}FJxlV6bTR*x-Qf;BxRuU z>g~~p8;^ojHnzxi!HeSSZ{4(rz+K7NInUa^5u^-B^`bwB0fU=SA`zWU z#DG5fad}c+iPyUk*wVh_}%W$UnDrnb;dMlvkar$Wggj`b2 zgk|Jyw96q_ctYmDqul%L(dc=2PZ#i_-d5_Nu!uX`)YJ~(M=r{6-w3Q1{kxr7@eYuW zG36~3k>5u!$FFiia;}llN?%e`P5HVh(M}?`<|}Lz@b%{6JM(7G z?TY!@6PTZwBbr;tN8@v6;WYl{tiGlx5?vuZ%Ua_-MYHJG@B;ze`4p}qTlZ!UIL21t^Z=k54Q#**Lf-Kqn@ekoe2X$9Se9)E* zGfVVVQ|X4>Y(2&C)2vj<#oqHnVVS{)Z$u8hLNT6;9Q~sbo4*1+Y#N5g5s5vu)K7I? zuQ{R7dk`q*U*PP55EomZcrhiBt}aLTRSDq(tdx5Z60M`VkGg-Z2d7rgR~;@h5dq=y zcsvvRBP8}%1$LR=OO)UG?Zxgogfug4%!Q0hdkWRnQ0ZLjd<_eeZ`I8BQ#~8#b6w}{ zvJ;r8xoVu4k9i`#I&{kYclZ7JhmdvW*Mpb`H#6QGA&a>i;YPq#(k-8~OA{}cn>gAv zB(eqkAW{v6Dh3zW!#r7cRc8;Auk$2)@rJxhGz|^t3W>3izfJVa<4Q8)WUZ3@+Sh!K zl4Q~S-d}UBb?g+TNH+|2uyi5Lg;mBEG`GFaspL2;y-KBC`Ss<^de8G!N9%ovy*^RU zCo}Xvza&4pyudi`uDU3vYa7veKvz^iY%~CYQEMkzJID)*`7DB$cxV^~BUi`E zugFKb`0iu+{8g}-e%&B`%PY^^m1@ny5G5*Ur}Tk)_O|>-=%&hT?P||GL8I||&RYh| zc1-MPv)XN_bu$CTsJh5xYJ)=i`7>pt>kce)DyRzktSg9I&}kz~;KX;ejw6TqNAAeu zWsUdth(^`6ulCnxtEtK&%6@gE$Yw_TsIXdF-~O!eMd@nL?jz%_R(`i�D8t7l3NR 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 z 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ℑB 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