From 197ee19bef60a8853bb12549030fd4a7978b4e88 Mon Sep 17 00:00:00 2001 From: Josh Gao Date: Sat, 2 Nov 2024 00:57:31 +0800 Subject: [PATCH] add cx9261a_demo vitis classic project, some projects need further revision due to bsp version upgrade --- .../vitis_classic_autobuild/.gitignore | 35 + .../vitis_classic_autobuild/Makefile | 8 + .../vitis_classic_autobuild/config.json | 84 + .../source_CX9261A_DEMO/cx9261a_076_config.c | 342 + .../source_CX9261A_DEMO/cx9261a_151_config.c | 708 ++ .../source_CX9261A_DEMO/cx9261a_710_config.c | 835 ++ .../source_CX9261A_DEMO/download_bin.c | 45 + .../source_CX9261A_DEMO/echo.c | 461 + .../source_CX9261A_DEMO/frqhop_config.c | 58 + .../source_CX9261A_DEMO/iic_phyreset.c | 115 + .../source_CX9261A_DEMO/main.c | 300 + .../source_CX9261A_DEMO/platform_config.h | 10 + .../source_CX9261A_DEMO/rx_wideband_qec.c | 61 + .../source_CX9261A_DEMO/sd.c | 178 + .../source_CX9261A_DEMO/sd.h | 9 + .../source_CX9261A_DEMO/tx_bisection.c | 74 + .../source_CX9261A_DEMO/xilinx_io.c | 861 ++ .../source_CX9261A_DEMO/xilinx_io.h | 149 + .../source_CX9261A_HOP/cx9261a_076_config.c | 342 + .../source_CX9261A_HOP/cx9261a_151_config.c | 708 ++ .../source_CX9261A_HOP/cx9261a_710_config.c | 835 ++ .../source_CX9261A_HOP/download_bin.c | 80 + .../source_CX9261A_HOP/echo.c | 461 + .../source_CX9261A_HOP/frqhop_config.c | 52 + .../source_CX9261A_HOP/iic_phyreset.c | 115 + .../source_CX9261A_HOP/main.c | 300 + .../source_CX9261A_HOP/platform_config.h | 10 + .../source_CX9261A_HOP/rx_wideband_qec.c | 61 + .../source_CX9261A_HOP/sd.c | 178 + .../source_CX9261A_HOP/sd.h | 9 + .../source_CX9261A_HOP/tx_bisection.c | 74 + .../source_CX9261A_HOP/xilinx_io.c | 905 ++ .../source_CX9261A_HOP/xilinx_io.h | 153 + .../cx9261adriver.cpp | 2188 +++++ .../source_cx9261a_api_1_9_0/cx9261adriver.h | 546 ++ .../source_cx9261a_api_1_9_0/defs.h | 424 + .../source_cx9261a_api_1_9_0/main.cpp | 138 + .../source_cx9261a_api_1_9_0/utils.h | 99 + .../config/app_config.h | 331 + .../dev_profile/ad9361_profile.c | 340 + .../dev_profile/ad9361_reg_profile.c | 1746 ++++ .../dev_profile/ad9528_profile.c | 134 + .../dev_profile/ad9653_profile.c | 46 + .../dev_profile/ad9694_profile.c | 72 + .../dev_profile/cx9261a_profile.c | 228 + .../dev_profile/dev_profile.h | 62 + .../dev_profile/hmc7044_profile.c | 132 + .../dev_profile/intc_profile.c | 14 + .../dev_profile/lmk04828_profile.c | 182 + .../dev_profile/lmx2594_profile.c | 181 + .../dev_profile/timer_profile.c | 21 + .../drivers/adc/ad9653/ad9653.c | 129 + .../drivers/adc/ad9653/ad9653.h | 233 + .../drivers/adc/ad9694/ad9694.c | 128 + .../drivers/adc/ad9694/ad9694.h | 231 + .../drivers/api/gpio.c | 241 + .../drivers/api/i2c.c | 135 + .../drivers/api/irq.c | 229 + .../drivers/api/spi.c | 161 + .../drivers/api/timer.c | 211 + .../drivers/api/trng.c | 111 + .../drivers/axi_core/axi_dmac/axi_dmac.c | 547 ++ .../drivers/axi_core/axi_dmac/axi_dmac.h | 156 + .../drivers/axi_core/spi_engine/spi_engine.c | 996 +++ .../drivers/axi_core/spi_engine/spi_engine.h | 229 + .../axi_core/spi_engine/spi_engine_private.h | 154 + .../drivers/frequency/ad9528/ad9528.c | 783 ++ .../drivers/frequency/ad9528/ad9528.h | 245 + .../drivers/frequency/hmc7044/hmc7044.c | 927 ++ .../drivers/frequency/hmc7044/hmc7044.h | 276 + .../drivers/frequency/lmk04828/lmk04828.c | 128 + .../drivers/frequency/lmk04828/lmk04828.h | 90 + .../drivers/frequency/lmx2594/lmx2594.c | 129 + .../drivers/frequency/lmx2594/lmx2594.h | 90 + .../drivers/platform/xilinx_axi_io.c | 79 + .../drivers/platform/xilinx_delay.c | 100 + .../drivers/platform/xilinx_gpio.c | 464 + .../drivers/platform/xilinx_gpio.h | 95 + .../drivers/platform/xilinx_gpio_irq.c | 360 + .../drivers/platform/xilinx_gpio_irq.h | 92 + .../drivers/platform/xilinx_i2c.c | 514 ++ .../drivers/platform/xilinx_i2c.h | 115 + .../drivers/platform/xilinx_irq.c | 433 + .../drivers/platform/xilinx_irq.h | 91 + .../drivers/platform/xilinx_spi.c | 462 + .../drivers/platform/xilinx_spi.h | 115 + .../drivers/platform/xilinx_spi_pl.c | 533 ++ .../drivers/platform/xilinx_timer.c | 456 + .../drivers/platform/xilinx_timer.h | 101 + .../drivers/rf-transceiver/ad9361/ad9361.c | 7475 +++++++++++++++++ .../drivers/rf-transceiver/ad9361/ad9361.h | 3568 ++++++++ .../rf-transceiver/ad9361/ad9361_api.c | 2157 +++++ .../rf-transceiver/ad9361/ad9361_api.h | 552 ++ .../rf-transceiver/ad9361/ad9361_conv.c | 86 + .../rf-transceiver/ad9361/ad9361_util.c | 335 + .../rf-transceiver/ad9361/ad9361_util.h | 120 + .../drivers/rf-transceiver/ad9361/common.h | 66 + .../rf-transceiver/ad9361_reg/ad9361_reg.c | 151 + .../rf-transceiver/ad9361_reg/ad9361_reg.h | 104 + .../drivers/rf-transceiver/cx9261a/cx9261a.c | 147 + .../drivers/rf-transceiver/cx9261a/cx9261a.h | 85 + .../rf-transceiver/cx9261a/cx9261adriver_c.h | 982 +++ .../rf-transceiver/cx9261a/lib/.project | 26 + .../rf-transceiver/cx9261a/lib/libcx9261a_c.a | Bin 0 -> 149108 bytes .../cx9261a/lib/libcx9261a_c_dbg.a | Bin 0 -> 2528404 bytes .../source_cx9261a_api_lib_c/hwconfig.c | 927 ++ .../source_cx9261a_api_lib_c/hwconfig.h | 32 + .../source_cx9261a_api_lib_c/include/axi_io.h | 59 + .../include/circ_queue.h | 100 + .../source_cx9261a_api_lib_c/include/clist.h | 138 + .../source_cx9261a_api_lib_c/include/common.h | 93 + .../source_cx9261a_api_lib_c/include/delay.h | 70 + .../source_cx9261a_api_lib_c/include/error.h | 56 + .../include/global_timer.h | 95 + .../source_cx9261a_api_lib_c/include/gpio.h | 188 + .../source_cx9261a_api_lib_c/include/i2c.h | 134 + .../source_cx9261a_api_lib_c/include/irq.h | 235 + .../source_cx9261a_api_lib_c/include/list.h | 327 + .../source_cx9261a_api_lib_c/include/spi.h | 209 + .../source_cx9261a_api_lib_c/include/timer.h | 159 + .../source_cx9261a_api_lib_c/include/trng.h | 115 + .../source_cx9261a_api_lib_c/include/util.h | 177 + .../source_cx9261a_api_lib_c/main.c | 524 ++ .../source_cx9261a_api_lib_c/system_monitor.c | 402 + .../source_cx9261a_api_lib_c/system_monitor.h | 241 + .../util/circ_queue.c | 134 + .../source_cx9261a_api_lib_c/util/clist.c | 353 + .../source_cx9261a_api_lib_c/util/common.c | 51 + .../source_cx9261a_api_lib_c/util/crc.c | 444 + .../source_cx9261a_api_lib_c/util/list.c | 886 ++ .../source_cx9261a_api_lib_c/util/tables.txt | 231 + .../source_cx9261a_api_lib_c/util/util.c | 403 + .../source_cx9261a_api_lib_c/xilinx_io.c | 160 + .../source_cx9261a_api_lib_c/xilinx_io.h | 92 + 134 files changed, 48958 insertions(+) create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/.gitignore create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/Makefile create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/config.json create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/cx9261a_076_config.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/cx9261a_151_config.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/cx9261a_710_config.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/download_bin.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/echo.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/frqhop_config.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/iic_phyreset.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/main.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/platform_config.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/rx_wideband_qec.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/sd.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/sd.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/tx_bisection.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/xilinx_io.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/xilinx_io.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/cx9261a_076_config.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/cx9261a_151_config.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/cx9261a_710_config.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/download_bin.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/echo.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/frqhop_config.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/iic_phyreset.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/main.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/platform_config.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/rx_wideband_qec.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/sd.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/sd.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/tx_bisection.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/xilinx_io.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/xilinx_io.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_1_9_0/cx9261adriver.cpp create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_1_9_0/cx9261adriver.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_1_9_0/defs.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_1_9_0/main.cpp create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_1_9_0/utils.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/config/app_config.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/ad9361_profile.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/ad9361_reg_profile.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/ad9528_profile.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/ad9653_profile.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/ad9694_profile.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/cx9261a_profile.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/dev_profile.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/hmc7044_profile.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/intc_profile.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/lmk04828_profile.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/lmx2594_profile.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/timer_profile.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/adc/ad9653/ad9653.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/adc/ad9653/ad9653.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/adc/ad9694/ad9694.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/adc/ad9694/ad9694.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/api/gpio.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/api/i2c.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/api/irq.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/api/spi.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/api/timer.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/api/trng.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/axi_core/axi_dmac/axi_dmac.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/axi_core/axi_dmac/axi_dmac.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/axi_core/spi_engine/spi_engine.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/axi_core/spi_engine/spi_engine.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/axi_core/spi_engine/spi_engine_private.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/frequency/ad9528/ad9528.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/frequency/ad9528/ad9528.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/frequency/hmc7044/hmc7044.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/frequency/hmc7044/hmc7044.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/frequency/lmk04828/lmk04828.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/frequency/lmk04828/lmk04828.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/frequency/lmx2594/lmx2594.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/frequency/lmx2594/lmx2594.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_axi_io.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_delay.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_gpio.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_gpio.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_gpio_irq.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_gpio_irq.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_i2c.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_i2c.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_irq.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_irq.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_spi.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_spi.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_spi_pl.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_timer.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_timer.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361/ad9361.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361/ad9361.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361/ad9361_api.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361/ad9361_api.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361/ad9361_conv.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361/ad9361_util.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361/ad9361_util.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361/common.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361_reg/ad9361_reg.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361_reg/ad9361_reg.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/cx9261a/cx9261a.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/cx9261a/cx9261a.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/cx9261a/cx9261adriver_c.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/cx9261a/lib/.project create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/cx9261a/lib/libcx9261a_c.a create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/cx9261a/lib/libcx9261a_c_dbg.a create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/hwconfig.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/hwconfig.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/axi_io.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/circ_queue.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/clist.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/common.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/delay.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/error.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/global_timer.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/gpio.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/i2c.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/irq.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/list.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/spi.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/timer.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/trng.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/util.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/main.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/system_monitor.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/system_monitor.h create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/util/circ_queue.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/util/clist.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/util/common.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/util/crc.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/util/list.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/util/tables.txt create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/util/util.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/xilinx_io.c create mode 100644 cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/xilinx_io.h diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/.gitignore b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/.gitignore new file mode 100644 index 0000000..3ce08f3 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/.gitignore @@ -0,0 +1,35 @@ +# keep compiled libs +!*.a + +# Buid folder +build/ + +# Xilinx tools generated files +.metadata/ +.settings/ +.wsdata/ +.Xil/ +logs/ +Packages/ +*.log* + +# System generated files +# -- macOS +.DS_Store +.AppleDouble +.LSOverride +._* + +# -- Windows +Thumbs.db +ehthumbs.db +Desktop.ini + +# -- Linux +*~ + +# IDEs and editors' files +*.idea +*.vscode +*.sublime-workspace +*.sublime-project diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/Makefile b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/Makefile new file mode 100644 index 0000000..e7b8a91 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/Makefile @@ -0,0 +1,8 @@ +include ../../scripts/generic_variables.mk + +PLATFORM := xilinx +CONFIG_FILE := $(PROJECT)/config.json +XILINX_VITIS_CLASSIC = y +SCRIPT_DIR = ../../scripts + +include ../../scripts/generic.mk \ No newline at end of file diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/config.json b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/config.json new file mode 100644 index 0000000..487b93c --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/config.json @@ -0,0 +1,84 @@ +{ + "project_name": "cx9261a_demo_cmos_20240513", + "version": "20240513", + "hw_design": "../CX9261A_ZC706.xsa", + "hw_version": "20241031", + "apps": { + "cx9261a_api_app": { + "sources": ["source_cx9261a_api_1_9_0/"], + "target_cpu": "ps7_cortexa9_0", + "os": "standalone", + "language": "C++", + "mem_layout" : { + "heap_size": "0x80000", + "stack_size": "0x4000" + } + }, + "cx9261a_api_lib_app": { + "sources": ["source_cx9261a_api_lib_c/"], + "target_cpu": "ps7_cortexa9_0", + "os": "standalone", + "language": "C", + "mem_layout" : { + "heap_size": "0x80000", + "stack_size": "0x4000" + }, + "build_config": { + "debug": { + "include-path": ["source_cx9261a_api_lib_c/include"], + "libraries": ["cx9261a_c_dbg", "stdc++", "m"], + "library-search-path": ["source_cx9261a_api_lib_c/drivers/rf-transceiver/cx9261a/lib"] + }, + "release": { + "include-path": ["source_cx9261a_api_lib_c/include"], + "libraries": ["cx9261a_c", "stdc++", "m"], + "library-search-path": ["source_cx9261a_api_lib_c/drivers/rf-transceiver/cx9261a/lib"] + } + } + }, + "CX9261A_DEMO": { + "sources": ["source_CX9261A_DEMO/"], + "target_cpu": "ps7_cortexa9_0", + "os": "freertos10_xilinx", + "language": "C", + "mem_layout" : { + "heap_size": "0x80000", + "stack_size": "0x4000" + }, + "bsp_lib": { + "lwip220": { + "version": "1.0", + "config": { + "api_mode": "SOCKET_API", + "pbuf_pool_size": 2048 + } + }, + "xilffs": { + "version": "5.2" + } + } + }, + "CX9261A_HOP": { + "sources": ["source_CX9261A_HOP/"], + "target_cpu": "ps7_cortexa9_0", + "os": "freertos10_xilinx", + "language": "C", + "mem_layout" : { + "heap_size": "0x80000", + "stack_size": "0x4000" + }, + "bsp_lib": { + "lwip220": { + "version": "1.0", + "config": { + "api_mode": "SOCKET_API", + "pbuf_pool_size": 2048 + } + }, + "xilffs": { + "version": "5.2" + } + } + } + } +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/cx9261a_076_config.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/cx9261a_076_config.c new file mode 100644 index 0000000..64c1e10 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/cx9261a_076_config.c @@ -0,0 +1,342 @@ +/* + * cx9261a_076_config.c + * + * Created on: 2023Äê11ÔÂ30ÈÕ + * Author: cxlab + */ + + +#include "xilinx_io.h" +#include "sleep.h" +void cx9261a_076_config(void){ +u8 rx_data; +int temp; + +//setPin(pin,value) +GpioSetPinDir(0,0x01); +GpioWritePin(0,1); +GpioSetPinDir(32,0x01); +GpioWritePin(32,1); +GpioSetPinDir(33,0x01); +GpioWritePin(33,1); +GpioSetPinDir(34,0x01); +GpioWritePin(34,0); +GpioSetPinDir(35,0x01); //tx:0-rx:1 +GpioWritePin(35,1); //tx:0-rx:1 +GpioSetPinDir(36,0x01); //fdd:0-tdd:1 +GpioWritePin(36,1); //fdd:0-tdd:1 +GpioSetPinDir(37,0x01); //1r1t:0-2r2t:1 +GpioWritePin(37,0); //1r1t:0-2r2t:1 +GpioSetPinDir(38,0x01); //dualport:0-singleport:1 +GpioWritePin(38,1); //dualport:0-singleport:1 +GpioSetPinDir(39,0x01); +GpioWritePin(39,1); +GpioSetPinDir(40,0x01); +GpioWritePin(40,1); +//Èý¸öͨµÀ¹²ͬ¿ØÖÆ +SpiPs_write_read1(0x000,0x02,&rx_data); +//µçԴ¹ÜÀí×Զ¨Òå +//¶ϵçģʽ +SpiPs_write_read1(0x031,0x00,&rx_data); +SpiPs_write_read1(0x032,0x7F,&rx_data); +SpiPs_write_read1(0x033,0xFF,&rx_data); +SpiPs_write_read1(0x034,0xFF,&rx_data); +//ÐÝÃßģʽ +SpiPs_write_read1(0x035,0x00,&rx_data); +SpiPs_write_read1(0x036,0x3D,&rx_data); +SpiPs_write_read1(0x037,0xFF,&rx_data); +SpiPs_write_read1(0x038,0xEB,&rx_data); +//¾¯½äģʽ +SpiPs_write_read1(0x039,0x00,&rx_data); +SpiPs_write_read1(0x03A,0x3D,&rx_data); +SpiPs_write_read1(0x03B,0xFF,&rx_data); +SpiPs_write_read1(0x03C,0xE0,&rx_data); +//У׼ģʽ +SpiPs_write_read1(0x03D,0x1B,&rx_data); +SpiPs_write_read1(0x03E,0x39,&rx_data); +SpiPs_write_read1(0x03F,0x27,&rx_data); +SpiPs_write_read1(0x040,0xE0,&rx_data); +//½ÓÊÕģʽ +SpiPs_write_read1(0x041,0x03,&rx_data); +SpiPs_write_read1(0x042,0x3D,&rx_data); +SpiPs_write_read1(0x043,0xE7,&rx_data); +SpiPs_write_read1(0x044,0xF8,&rx_data); +//·¢Éäģʽ +SpiPs_write_read1(0x045,0x1B,&rx_data); +SpiPs_write_read1(0x046,0x3D,&rx_data); +SpiPs_write_read1(0x047,0x26,&rx_data); +SpiPs_write_read1(0x048,0x7E,&rx_data); +//FDDģʽ +SpiPs_write_read1(0x049,0x1B,&rx_data); +SpiPs_write_read1(0x04A,0x3D,&rx_data); +SpiPs_write_read1(0x04B,0x27,&rx_data); +SpiPs_write_read1(0x04C,0xE0,&rx_data); +//¹۲âģʽ +SpiPs_write_read1(0x04D,0x04,&rx_data); +SpiPs_write_read1(0x04E,0x3D,&rx_data); +SpiPs_write_read1(0x04F,0xD9,&rx_data); +SpiPs_write_read1(0x050,0xFE,&rx_data); +//´ø϶»ù׼ +SpiPs_write_read1(0x773,0x53,&rx_data); +SpiPs_write_read1(0x774,0x86,&rx_data); +//²ο¼ʱÖÓÉèÖà +SpiPs_write_read1(0x789,0x00,&rx_data); +SpiPs_write_read1(0x785,0x49,&rx_data); +SpiPs_write_read1(0x788,0x24,&rx_data); +usleep(10000); +SpiPs_write_read1(0x788,0xA4,&rx_data); +//BBPLL +SpiPs_write_read1(0x0C0,0x50,&rx_data); +SpiPs_write_read1(0x0C1,0x50,&rx_data); +SpiPs_write_read1(0x0C2,0x50,&rx_data); +SpiPs_write_read1(0x601,0x0F,&rx_data); +SpiPs_write_read1(0x602,0x21,&rx_data); +SpiPs_write_read1(0x60D,0x0C,&rx_data); +SpiPs_write_read1(0x60E,0x66,&rx_data); +SpiPs_write_read1(0x60F,0x66,&rx_data); +SpiPs_write_read1(0x610,0x66,&rx_data); +SpiPs_write_read1(0x611,0x13,&rx_data); +SpiPs_write_read1(0x612,0x00,&rx_data); +//ADC +SpiPs_write_read1(0x70B,0x0E,&rx_data); +SpiPs_write_read1(0x727,0x0E,&rx_data); +SpiPs_write_read1(0x743,0x0E,&rx_data); +SpiPs_write_read1(0x70D,0x3F,&rx_data); +SpiPs_write_read1(0x729,0x3F,&rx_data); +SpiPs_write_read1(0x745,0x3F,&rx_data); +SpiPs_write_read1(0x70E,0x48,&rx_data); +SpiPs_write_read1(0x72A,0x48,&rx_data); +SpiPs_write_read1(0x746,0x48,&rx_data); +SpiPs_write_read1(0x70F,0xA0,&rx_data); +SpiPs_write_read1(0x72B,0xA0,&rx_data); +SpiPs_write_read1(0x747,0xA0,&rx_data); +//ͨµÀʱÖÓÀ´Դ +SpiPs_write_read1(0x77D,0xC0,&rx_data); +SpiPs_write_read1(0x775,0x24,&rx_data); +SpiPs_write_read1(0x6C3,0x00,&rx_data); +//PLL1 +SpiPs_write_read1(0x620,0x81,&rx_data); +SpiPs_write_read1(0x61D,0x00,&rx_data); +SpiPs_write_read1(0xB06,0x00,&rx_data); +SpiPs_write_read1(0xB07,0x64,&rx_data); +SpiPs_write_read1(0xB08,0x00,&rx_data); +SpiPs_write_read1(0xB09,0x00,&rx_data); +SpiPs_write_read1(0xB0A,0x00,&rx_data); +SpiPs_write_read1(0x61A,0x0B,&rx_data); +SpiPs_write_read1(0x61B,0x20,&rx_data); +SpiPs_write_read1(0x61C,0x13,&rx_data); +SpiPs_write_read1(0x624,0x11,&rx_data); +SpiPs_write_read1(0x625,0x11,&rx_data); +SpiPs_write_read1(0x62A,0x00,&rx_data); +SpiPs_write_read1(0x61E,0x30,&rx_data); +SpiPs_write_read1(0x61F,0x08,&rx_data); +SpiPs_write_read1(0x621,0x4D,&rx_data); +SpiPs_write_read1(0x622,0x19,&rx_data); +SpiPs_write_read1(0x627,0x10,&rx_data); +//RxͨµÀÉèÖà +SpiPs_write_read1(0x700,0x56,&rx_data); +SpiPs_write_read1(0x71C,0x56,&rx_data); +SpiPs_write_read1(0x738,0x56,&rx_data); +SpiPs_write_read1(0x701,0x02,&rx_data); +SpiPs_write_read1(0x702,0x35,&rx_data); +SpiPs_write_read1(0x71D,0x02,&rx_data); +SpiPs_write_read1(0x71E,0x35,&rx_data); +SpiPs_write_read1(0x739,0x02,&rx_data); +SpiPs_write_read1(0x73A,0x35,&rx_data); +SpiPs_write_read1(0x703,0xF6,&rx_data); +SpiPs_write_read1(0x71F,0xF6,&rx_data); +SpiPs_write_read1(0x73B,0xF6,&rx_data); +SpiPs_write_read1(0x704,0xC8,&rx_data); +SpiPs_write_read1(0x720,0xC8,&rx_data); +SpiPs_write_read1(0x73C,0xC8,&rx_data); +SpiPs_write_read1(0x907,0x1F,&rx_data); +SpiPs_write_read1(0x705,0xFC,&rx_data); +SpiPs_write_read1(0x721,0xFC,&rx_data); +SpiPs_write_read1(0x73D,0xFC,&rx_data); +SpiPs_write_read1(0x706,0x65,&rx_data); +SpiPs_write_read1(0x722,0x65,&rx_data); +SpiPs_write_read1(0x73E,0x65,&rx_data); +SpiPs_write_read1(0x707,0x91,&rx_data); +SpiPs_write_read1(0x723,0x91,&rx_data); +SpiPs_write_read1(0x73F,0x91,&rx_data); +SpiPs_write_read1(0x708,0x04,&rx_data); +SpiPs_write_read1(0x724,0x04,&rx_data); +SpiPs_write_read1(0x740,0x04,&rx_data); +SpiPs_write_read1(0x709,0x94,&rx_data); +SpiPs_write_read1(0x725,0x94,&rx_data); +SpiPs_write_read1(0x741,0x94,&rx_data); +SpiPs_write_read1(0x70A,0xFC,&rx_data); +SpiPs_write_read1(0x726,0xFC,&rx_data); +SpiPs_write_read1(0x742,0xFC,&rx_data); +SpiPs_write_read1(0x75A,0x83,&rx_data); +SpiPs_write_read1(0x75B,0xDA,&rx_data); +usleep(10000); +SpiPs_write_read1(0x75B,0x9A,&rx_data); +SpiPs_write_read1(0x75C,0x81,&rx_data); +SpiPs_write_read1(0x75D,0xF8,&rx_data); +SpiPs_write_read1(0x75E,0xA7,&rx_data); +SpiPs_write_read1(0x75F,0x51,&rx_data); +SpiPs_write_read1(0x760,0xFF,&rx_data); +SpiPs_write_read1(0x761,0x04,&rx_data); +SpiPs_write_read1(0x762,0x70,&rx_data); +SpiPs_write_read1(0x763,0x04,&rx_data); +SpiPs_write_read1(0x764,0x83,&rx_data); +SpiPs_write_read1(0x765,0xDA,&rx_data); +usleep(100000); +SpiPs_write_read1(0x765,0x9A,&rx_data); +SpiPs_write_read1(0x766,0x81,&rx_data); +SpiPs_write_read1(0x767,0xF8,&rx_data); +SpiPs_write_read1(0x768,0xA7,&rx_data); +SpiPs_write_read1(0x769,0x51,&rx_data); +SpiPs_write_read1(0x76A,0xFF,&rx_data); +SpiPs_write_read1(0x76B,0x04,&rx_data); +SpiPs_write_read1(0x76C,0x70,&rx_data); +SpiPs_write_read1(0x76D,0x04,&rx_data); +//RxÂ˲¨Æ÷ +SpiPs_write_read1(0x106,0x02,&rx_data); +//TxÂ˲¨Æ÷ +SpiPs_write_read1(0x107,0x00,&rx_data); +SpiPs_write_read1(0x108,0xD0,&rx_data); +//Rx NCO +SpiPs_write_read1(0x11C,0x00,&rx_data); +SpiPs_write_read1(0xA07,0x00,&rx_data); +SpiPs_write_read1(0xA08,0x00,&rx_data); +SpiPs_write_read1(0xA16,0x00,&rx_data); +SpiPs_write_read1(0xA17,0x00,&rx_data); +SpiPs_write_read1(0xA18,0x00,&rx_data); +SpiPs_write_read1(0xA19,0x00,&rx_data); +SpiPs_write_read1(0xA1A,0x00,&rx_data); +SpiPs_write_read1(0xA1B,0x00,&rx_data); +//Tx NCO +SpiPs_write_read1(0x128,0x10,&rx_data); +SpiPs_write_read1(0x11B,0x00,&rx_data); +SpiPs_write_read1(0x11E,0x00,&rx_data); +SpiPs_write_read1(0x161,0x00,&rx_data); +SpiPs_write_read1(0x162,0x00,&rx_data); +SpiPs_write_read1(0x164,0x00,&rx_data); +SpiPs_write_read1(0x165,0x00,&rx_data); +SpiPs_write_read1(0x166,0x00,&rx_data); +SpiPs_write_read1(0x167,0x00,&rx_data); +SpiPs_write_read1(0x168,0x00,&rx_data); +SpiPs_write_read1(0x169,0x00,&rx_data); +//AGCÅäÖà +SpiPs_write_read1(0x900,0x00,&rx_data); +SpiPs_write_read1(0x901,0x00,&rx_data); +SpiPs_write_read1(0x903,0x11,&rx_data); +SpiPs_write_read1(0x908,0x77,&rx_data); +SpiPs_write_read1(0x909,0x03,&rx_data); +SpiPs_write_read1(0x90A,0x32,&rx_data); +SpiPs_write_read1(0x90D,0x00,&rx_data); +SpiPs_write_read1(0x90E,0x0F,&rx_data); +SpiPs_write_read1(0x90F,0x65,&rx_data); +SpiPs_write_read1(0x910,0x32,&rx_data); +SpiPs_write_read1(0x911,0x00,&rx_data); +SpiPs_write_read1(0x912,0x0F,&rx_data); +SpiPs_write_read1(0x913,0x3A,&rx_data); +SpiPs_write_read1(0x914,0x37,&rx_data); +SpiPs_write_read1(0x915,0x34,&rx_data); +SpiPs_write_read1(0x916,0x31,&rx_data); +//½øÈëÐÝÃßģʽ +SpiPs_write_read1(0x02E,0x01,&rx_data); +usleep(1000000); +//½øÈëУ׼ģʽ +SpiPs_write_read1(0x02E,0x03,&rx_data); +//BBPLL AFC +SpiPs_write_read1(0x600,0x00,&rx_data); +usleep(100000); +SpiPs_write_read1(0x600,0x11,&rx_data); +//PLL1 AFC +SpiPs_write_read1(0x617,0x00,&rx_data); +usleep(100000); +SpiPs_write_read1(0x617,0x03,&rx_data); +//½ӿÚÅäÖà +SpiPs_write_read1(0x02E,0x06,&rx_data); +SpiPs_write_read1(0x003,0x33,&rx_data); +SpiPs_write_read1(0x200,0x00,&rx_data); +SpiPs_write_read1(0x201,0x00,&rx_data); +SpiPs_write_read1(0x202,0x03,&rx_data); +SpiPs_write_read1(0x209,0x00,&rx_data); +SpiPs_write_read1(0x780,0x11,&rx_data); +SpiPs_write_read1(0x781,0x6F,&rx_data); +SpiPs_write_read1(0x782,0x01,&rx_data); +SpiPs_write_read1(0x783,0x01,&rx_data); +SpiPs_write_read1(0x784,0x01,&rx_data); +//ADCУ׼ +SpiPs_write_read1(0x70B,0x1E,&rx_data); +SpiPs_write_read1(0x727,0x1E,&rx_data); +SpiPs_write_read1(0x743,0x1E,&rx_data); +SpiPs_write_read1(0x70F,0xA0,&rx_data); +SpiPs_write_read1(0x710,0x83,&rx_data); +SpiPs_write_read1(0x711,0xF0,&rx_data); +SpiPs_write_read1(0x72B,0xA0,&rx_data); +SpiPs_write_read1(0x72C,0x83,&rx_data); +SpiPs_write_read1(0x72D,0xF0,&rx_data); +SpiPs_write_read1(0x747,0xA0,&rx_data); +SpiPs_write_read1(0x748,0x83,&rx_data); +SpiPs_write_read1(0x749,0xF0,&rx_data); +SpiPs_write_read1(0x713,0x80,&rx_data); +SpiPs_write_read1(0x72F,0x80,&rx_data); +SpiPs_write_read1(0x74B,0x80,&rx_data); +SpiPs_write_read1(0x70B,0x9E,&rx_data); +SpiPs_write_read1(0x727,0x9E,&rx_data); +SpiPs_write_read1(0x743,0x9E,&rx_data); +SpiPs_write_read1(0x70B,0x9A,&rx_data); +SpiPs_write_read1(0x727,0x9A,&rx_data); +SpiPs_write_read1(0x743,0x9A,&rx_data); +SpiPs_write_read1(0x70B,0x9E,&rx_data); +SpiPs_write_read1(0x727,0x9E,&rx_data); +SpiPs_write_read1(0x743,0x9E,&rx_data); +usleep(100000); +SpiPs_write_read1(0x70B,0x1E,&rx_data); +SpiPs_write_read1(0x727,0x1E,&rx_data); +SpiPs_write_read1(0x743,0x1E,&rx_data); +SpiPs_write_read1(0x713,0xC0,&rx_data); +SpiPs_write_read1(0x72F,0xC0,&rx_data); +SpiPs_write_read1(0x74B,0xC0,&rx_data); +//FlashУ׼ +SpiPs_write_read1(0x70B,0x1E,&rx_data); +SpiPs_write_read1(0x727,0x1E,&rx_data); +SpiPs_write_read1(0x743,0x1E,&rx_data); +SpiPs_write_read1(0x715,0x80,&rx_data); +SpiPs_write_read1(0x731,0x80,&rx_data); +SpiPs_write_read1(0x74D,0x80,&rx_data); +SpiPs_write_read1(0x715,0xC0,&rx_data); +SpiPs_write_read1(0x731,0xC0,&rx_data); +SpiPs_write_read1(0x74D,0xC0,&rx_data); +SpiPs_write_read1(0x716,0x40,&rx_data); +SpiPs_write_read1(0x732,0x40,&rx_data); +SpiPs_write_read1(0x74E,0x40,&rx_data); +SpiPs_write_read1(0x70B,0x16,&rx_data); +SpiPs_write_read1(0x727,0x16,&rx_data); +SpiPs_write_read1(0x743,0x16,&rx_data); +usleep(100000); +SpiPs_write_read1(0x70B,0x1E,&rx_data); +SpiPs_write_read1(0x727,0x1E,&rx_data); +SpiPs_write_read1(0x743,0x1E,&rx_data); +SpiPs_write_read1(0x715,0x80,&rx_data); +SpiPs_write_read1(0x731,0x80,&rx_data); +SpiPs_write_read1(0x74D,0x80,&rx_data); +SpiPs_write_read1(0x716,0x00,&rx_data); +SpiPs_write_read1(0x732,0x00,&rx_data); +SpiPs_write_read1(0x74E,0x00,&rx_data); +SpiPs_write_read1(0x109,0x11,&rx_data); +SpiPs_write_read1(0x10A,0x70,&rx_data); +SpiPs_write_read1(0x11F,0x33,&rx_data); +usleep(1000); +SpiPs_write_read2(0x615,0x00,&rx_data); +temp=rx_data; +SpiPs_write_read2(0x635,0x00,&rx_data); +temp=rx_data; +////ÅäÖÃTx NCO BIST ON +//SpiPs_write_read1(0x11B,0x01,&rx_data); +//SpiPs_write_read1(0x11E,0x01,&rx_data); +//SpiPs_write_read1(0x161,0x01,&rx_data); +//SpiPs_write_read1(0x162,0x01,&rx_data); +////ÅäÖÃTx NCOƵÂÊ 1MHz +//SpiPs_write_read1(0x164,0x05,&rx_data); +//SpiPs_write_read1(0x165,0x55,&rx_data); +//SpiPs_write_read1(0x166,0x55,&rx_data); +//SpiPs_write_read1(0x167,0x55,&rx_data); +//SpiPs_write_read1(0x168,0x55,&rx_data); +//SpiPs_write_read1(0x169,0x55,&rx_data); +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/cx9261a_151_config.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/cx9261a_151_config.c new file mode 100644 index 0000000..c1e78f7 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/cx9261a_151_config.c @@ -0,0 +1,708 @@ +/* + * cx9261_config.c + * + * Created on: 2023Äê10ÔÂ16ÈÕ + * Author: cxlab + */ + +#include "xilinx_io.h" +#include "sleep.h" +void cx9261a_151_config(void){ +u8 rx_data; +int temp; + +//setPin(pin,value) +GpioSetPinDir(0,0x01); +GpioWritePin(0,1); +GpioSetPinDir(32,0x01); +GpioWritePin(32,1); +GpioSetPinDir(33,0x01); +GpioWritePin(33,1); +GpioSetPinDir(34,0x01); +GpioWritePin(34,0); +GpioSetPinDir(35,0x01); //tx:0-rx:1 +GpioWritePin(35,1); //tx:0-rx:1 +GpioSetPinDir(36,0x01); //fdd:0-tdd:1 +GpioWritePin(36,1); //fdd:0-tdd:1 +GpioSetPinDir(37,0x01); //1r1t:0-2r2t:1 +GpioWritePin(37,0); //1r1t:0-2r2t:1 +GpioSetPinDir(38,0x01); //dualport:0-singleport:1 +GpioWritePin(38,1); //dualport:0-singleport:1 +GpioSetPinDir(39,0x01); +GpioWritePin(39,1); +GpioSetPinDir(40,0x01); +GpioWritePin(40,1); +//Èý¸öͨµÀ¹²ͬ¿ØÖÆ +SpiPs_write_read1(0x000,0x02,&rx_data); +//µçԴ¹ÜÀí×Զ¨Òå +//¶ϵçģʽ +SpiPs_write_read1(0x031,0x00,&rx_data); +SpiPs_write_read1(0x032,0x7F,&rx_data); +SpiPs_write_read1(0x033,0xFF,&rx_data); +SpiPs_write_read1(0x034,0xFF,&rx_data); +//ÐÝÃßģʽ +SpiPs_write_read1(0x035,0x00,&rx_data); +SpiPs_write_read1(0x036,0x3D,&rx_data); +SpiPs_write_read1(0x037,0xFF,&rx_data); +SpiPs_write_read1(0x038,0xEB,&rx_data); +//¾¯½äģʽ +SpiPs_write_read1(0x039,0x00,&rx_data); +SpiPs_write_read1(0x03A,0x3D,&rx_data); +SpiPs_write_read1(0x03B,0xFF,&rx_data); +SpiPs_write_read1(0x03C,0xE0,&rx_data); +//У׼ģʽ +SpiPs_write_read1(0x03D,0x1B,&rx_data); +SpiPs_write_read1(0x03E,0x39,&rx_data); +SpiPs_write_read1(0x03F,0x27,&rx_data); +SpiPs_write_read1(0x040,0xE0,&rx_data); +//½ÓÊÕģʽ +SpiPs_write_read1(0x041,0x03,&rx_data); +SpiPs_write_read1(0x042,0x3D,&rx_data); +SpiPs_write_read1(0x043,0xE7,&rx_data); +SpiPs_write_read1(0x044,0xF8,&rx_data); +//·¢Éäģʽ +SpiPs_write_read1(0x045,0x18,&rx_data); +SpiPs_write_read1(0x046,0x3D,&rx_data); +SpiPs_write_read1(0x047,0x3F,&rx_data); +SpiPs_write_read1(0x048,0xF8,&rx_data); +//FDDģʽ +SpiPs_write_read1(0x049,0x1B,&rx_data); +SpiPs_write_read1(0x04A,0x3D,&rx_data); +SpiPs_write_read1(0x04B,0x27,&rx_data); +SpiPs_write_read1(0x04C,0xE0,&rx_data); +//¹۲âģʽ +SpiPs_write_read1(0x04D,0x04,&rx_data); +SpiPs_write_read1(0x04E,0x3D,&rx_data); +SpiPs_write_read1(0x04F,0xD9,&rx_data); +SpiPs_write_read1(0x050,0xFE,&rx_data); +//´ø϶»ù׼ +SpiPs_write_read1(0x773,0x53,&rx_data); +SpiPs_write_read1(0x774,0x86,&rx_data); +//²ο¼ʱÖÓÉèÖà +SpiPs_write_read1(0x789,0x00,&rx_data); +SpiPs_write_read1(0x785,0x49,&rx_data); +SpiPs_write_read1(0x788,0x24,&rx_data); +usleep(10000); +SpiPs_write_read1(0x788,0xA4,&rx_data); +//BBPLL +SpiPs_write_read1(0x0C0,0x50,&rx_data); +SpiPs_write_read1(0x0C1,0x50,&rx_data); +SpiPs_write_read1(0x0C2,0x50,&rx_data); +SpiPs_write_read1(0x601,0x0F,&rx_data); +SpiPs_write_read1(0x602,0x21,&rx_data); +SpiPs_write_read1(0x60D,0x0C,&rx_data); +SpiPs_write_read1(0x60E,0x66,&rx_data); +SpiPs_write_read1(0x60F,0x66,&rx_data); +SpiPs_write_read1(0x610,0x66,&rx_data); +SpiPs_write_read1(0x611,0x13,&rx_data); +SpiPs_write_read1(0x612,0x00,&rx_data); +//ADC +SpiPs_write_read1(0x70B,0x0E,&rx_data); +SpiPs_write_read1(0x727,0x0E,&rx_data); +SpiPs_write_read1(0x743,0x0E,&rx_data); +SpiPs_write_read1(0x70D,0x3F,&rx_data); +SpiPs_write_read1(0x729,0x3F,&rx_data); +SpiPs_write_read1(0x745,0x3F,&rx_data); +SpiPs_write_read1(0x70E,0x48,&rx_data); +SpiPs_write_read1(0x72A,0x48,&rx_data); +SpiPs_write_read1(0x746,0x48,&rx_data); +SpiPs_write_read1(0x70F,0xA0,&rx_data); +SpiPs_write_read1(0x72B,0xA0,&rx_data); +SpiPs_write_read1(0x747,0xA0,&rx_data); +//ͨµÀʱÖÓÀ´Դ +SpiPs_write_read1(0x77D,0xCF,&rx_data); +SpiPs_write_read1(0x775,0x24,&rx_data); +SpiPs_write_read1(0x6C3,0x00,&rx_data); +//PLL1 +SpiPs_write_read1(0x620,0x81,&rx_data); +SpiPs_write_read1(0x61D,0x07,&rx_data); +SpiPs_write_read1(0xB06,0x00,&rx_data); +SpiPs_write_read1(0xB07,0x64,&rx_data); +SpiPs_write_read1(0xB08,0x00,&rx_data); +SpiPs_write_read1(0xB09,0x00,&rx_data); +SpiPs_write_read1(0xB0A,0x00,&rx_data); +SpiPs_write_read1(0x61A,0x0B,&rx_data); +SpiPs_write_read1(0x61B,0x20,&rx_data); +SpiPs_write_read1(0x61C,0x13,&rx_data); +SpiPs_write_read1(0x624,0x11,&rx_data); +SpiPs_write_read1(0x625,0x11,&rx_data); +SpiPs_write_read1(0x62A,0x00,&rx_data); +SpiPs_write_read1(0x61E,0x30,&rx_data); +SpiPs_write_read1(0x61F,0x08,&rx_data); +SpiPs_write_read1(0x621,0x4D,&rx_data); +SpiPs_write_read1(0x622,0x19,&rx_data); +SpiPs_write_read1(0x627,0x10,&rx_data); +//PLL2 +SpiPs_write_read1(0x642,0x91,&rx_data); +SpiPs_write_read1(0x63F,0x36,&rx_data); +SpiPs_write_read1(0xB14,0x00,&rx_data); +SpiPs_write_read1(0xB15,0x70,&rx_data); +SpiPs_write_read1(0xB16,0x00,&rx_data); +SpiPs_write_read1(0xB17,0x00,&rx_data); +SpiPs_write_read1(0xB18,0x00,&rx_data); +SpiPs_write_read1(0x63C,0x0C,&rx_data); +SpiPs_write_read1(0x63D,0x20,&rx_data); +SpiPs_write_read1(0x63E,0x0C,&rx_data); +SpiPs_write_read1(0x646,0x11,&rx_data); +SpiPs_write_read1(0x647,0x11,&rx_data); +SpiPs_write_read1(0x64C,0x00,&rx_data); +SpiPs_write_read1(0x640,0x30,&rx_data); +SpiPs_write_read1(0x641,0x08,&rx_data); +SpiPs_write_read1(0x643,0x4D,&rx_data); +SpiPs_write_read1(0x644,0x19,&rx_data); +SpiPs_write_read1(0x649,0x10,&rx_data); +//PLL3 +SpiPs_write_read1(0x664,0x91,&rx_data); +SpiPs_write_read1(0x661,0x36,&rx_data); +SpiPs_write_read1(0xB22,0x00,&rx_data); +SpiPs_write_read1(0xB23,0x80,&rx_data); +SpiPs_write_read1(0xB24,0x00,&rx_data); +SpiPs_write_read1(0xB25,0x00,&rx_data); +SpiPs_write_read1(0xB26,0x00,&rx_data); +SpiPs_write_read1(0x65E,0x0D,&rx_data); +SpiPs_write_read1(0x65F,0x20,&rx_data); +SpiPs_write_read1(0x660,0x0F,&rx_data); +SpiPs_write_read1(0x668,0x11,&rx_data); +SpiPs_write_read1(0x669,0x11,&rx_data); +SpiPs_write_read1(0x66E,0x00,&rx_data); +SpiPs_write_read1(0x662,0x30,&rx_data); +SpiPs_write_read1(0x663,0x08,&rx_data); +SpiPs_write_read1(0x665,0x4D,&rx_data); +SpiPs_write_read1(0x666,0x19,&rx_data); +SpiPs_write_read1(0x66B,0x10,&rx_data); +//PLL4 +SpiPs_write_read1(0x686,0x81,&rx_data); +SpiPs_write_read1(0x683,0x37,&rx_data); +SpiPs_write_read1(0xB30,0x00,&rx_data); +SpiPs_write_read1(0xB31,0x48,&rx_data); +SpiPs_write_read1(0xB32,0x00,&rx_data); +SpiPs_write_read1(0xB33,0x00,&rx_data); +SpiPs_write_read1(0xB34,0x00,&rx_data); +SpiPs_write_read1(0x680,0x0C,&rx_data); +SpiPs_write_read1(0x681,0x20,&rx_data); +SpiPs_write_read1(0x682,0x11,&rx_data); +SpiPs_write_read1(0x68A,0x11,&rx_data); +SpiPs_write_read1(0x68B,0x11,&rx_data); +SpiPs_write_read1(0x690,0x00,&rx_data); +SpiPs_write_read1(0x684,0x30,&rx_data); +SpiPs_write_read1(0x685,0x08,&rx_data); +SpiPs_write_read1(0x687,0x4D,&rx_data); +SpiPs_write_read1(0x688,0x19,&rx_data); +SpiPs_write_read1(0x68D,0x10,&rx_data); +//PLL5 +SpiPs_write_read1(0x6A8,0x81,&rx_data); +SpiPs_write_read1(0x6A5,0x37,&rx_data); +SpiPs_write_read1(0xB3E,0x00,&rx_data); +SpiPs_write_read1(0xB3F,0x50,&rx_data); +SpiPs_write_read1(0xB40,0x00,&rx_data); +SpiPs_write_read1(0xB41,0x00,&rx_data); +SpiPs_write_read1(0xB42,0x00,&rx_data); +SpiPs_write_read1(0x6A2,0x0C,&rx_data); +SpiPs_write_read1(0x6A3,0x20,&rx_data); +SpiPs_write_read1(0x6A4,0x11,&rx_data); +SpiPs_write_read1(0x6AC,0x11,&rx_data); +SpiPs_write_read1(0x6AD,0x11,&rx_data); +SpiPs_write_read1(0x6B2,0x00,&rx_data); +SpiPs_write_read1(0x6A6,0x30,&rx_data); +SpiPs_write_read1(0x6A7,0x08,&rx_data); +SpiPs_write_read1(0x6A9,0x4D,&rx_data); +SpiPs_write_read1(0x6AA,0x19,&rx_data); +SpiPs_write_read1(0x6AF,0x10,&rx_data); +//RxͨµÀÉèÖà +SpiPs_write_read1(0x700,0x56,&rx_data); +SpiPs_write_read1(0x71C,0x56,&rx_data); +SpiPs_write_read1(0x738,0x56,&rx_data); +SpiPs_write_read1(0x701,0x02,&rx_data); +SpiPs_write_read1(0x702,0x35,&rx_data); +SpiPs_write_read1(0x71D,0x02,&rx_data); +SpiPs_write_read1(0x71E,0x35,&rx_data); +SpiPs_write_read1(0x739,0x02,&rx_data); +SpiPs_write_read1(0x73A,0x35,&rx_data); +SpiPs_write_read1(0x703,0xF6,&rx_data); +SpiPs_write_read1(0x71F,0xF6,&rx_data); +SpiPs_write_read1(0x73B,0xF6,&rx_data); +SpiPs_write_read1(0x704,0xC8,&rx_data); +SpiPs_write_read1(0x720,0xC8,&rx_data); +SpiPs_write_read1(0x73C,0xC8,&rx_data); +SpiPs_write_read1(0x907,0x1F,&rx_data); +SpiPs_write_read1(0x705,0xFC,&rx_data); +SpiPs_write_read1(0x721,0xFC,&rx_data); +SpiPs_write_read1(0x73D,0xFC,&rx_data); +SpiPs_write_read1(0x706,0x65,&rx_data); +SpiPs_write_read1(0x722,0x65,&rx_data); +SpiPs_write_read1(0x73E,0x65,&rx_data); +SpiPs_write_read1(0x707,0x91,&rx_data); +SpiPs_write_read1(0x723,0x91,&rx_data); +SpiPs_write_read1(0x73F,0x91,&rx_data); +SpiPs_write_read1(0x708,0x04,&rx_data); +SpiPs_write_read1(0x724,0x04,&rx_data); +SpiPs_write_read1(0x740,0x04,&rx_data); +SpiPs_write_read1(0x709,0x94,&rx_data); +SpiPs_write_read1(0x725,0x94,&rx_data); +SpiPs_write_read1(0x741,0x94,&rx_data); +SpiPs_write_read1(0x70A,0xFC,&rx_data); +SpiPs_write_read1(0x726,0xFC,&rx_data); +SpiPs_write_read1(0x742,0xFC,&rx_data); +SpiPs_write_read1(0x75A,0x83,&rx_data); +SpiPs_write_read1(0x75B,0xDA,&rx_data); +usleep(100000); +SpiPs_write_read1(0x75B,0x9A,&rx_data); +SpiPs_write_read1(0x75C,0x81,&rx_data); +SpiPs_write_read1(0x75D,0xF8,&rx_data); +SpiPs_write_read1(0x75E,0xA7,&rx_data); +SpiPs_write_read1(0x75F,0x50,&rx_data); +SpiPs_write_read1(0x760,0xFF,&rx_data); +SpiPs_write_read1(0x761,0x07,&rx_data); +SpiPs_write_read1(0x762,0x6C,&rx_data); +SpiPs_write_read1(0x763,0x04,&rx_data); +SpiPs_write_read1(0x764,0x83,&rx_data); +SpiPs_write_read1(0x765,0xDA,&rx_data); +usleep(100000); +SpiPs_write_read1(0x765,0x9A,&rx_data); +SpiPs_write_read1(0x766,0x81,&rx_data); +SpiPs_write_read1(0x767,0xF8,&rx_data); +SpiPs_write_read1(0x768,0xA7,&rx_data); +SpiPs_write_read1(0x769,0x50,&rx_data); +SpiPs_write_read1(0x76A,0xFF,&rx_data); +SpiPs_write_read1(0x76B,0x07,&rx_data); +SpiPs_write_read1(0x76C,0x6C,&rx_data); +SpiPs_write_read1(0x76D,0x04,&rx_data); +//RxÂ˲¨Æ÷ +SpiPs_write_read1(0x106,0x02,&rx_data); +//TxÂ˲¨Æ÷ +SpiPs_write_read1(0x107,0x00,&rx_data); +SpiPs_write_read1(0x108,0xD0,&rx_data); +//Rx NCO +SpiPs_write_read1(0x11C,0x00,&rx_data); +SpiPs_write_read1(0xA07,0x00,&rx_data); +SpiPs_write_read1(0xA08,0x00,&rx_data); +SpiPs_write_read1(0xA16,0x00,&rx_data); +SpiPs_write_read1(0xA17,0x00,&rx_data); +SpiPs_write_read1(0xA18,0x00,&rx_data); +SpiPs_write_read1(0xA19,0x00,&rx_data); +SpiPs_write_read1(0xA1A,0x00,&rx_data); +SpiPs_write_read1(0xA1B,0x00,&rx_data); +//Tx NCO +SpiPs_write_read1(0x128,0x10,&rx_data); +SpiPs_write_read1(0x11B,0x01,&rx_data); +SpiPs_write_read1(0x11E,0x01,&rx_data); +SpiPs_write_read1(0x161,0x01,&rx_data); +SpiPs_write_read1(0x162,0x01,&rx_data); +SpiPs_write_read1(0x164,0x05,&rx_data); +SpiPs_write_read1(0x165,0x55,&rx_data); +SpiPs_write_read1(0x166,0x55,&rx_data); +SpiPs_write_read1(0x167,0x55,&rx_data); +SpiPs_write_read1(0x168,0x55,&rx_data); +SpiPs_write_read1(0x169,0x55,&rx_data); +//AGCÅäÖà +SpiPs_write_read1(0x900,0x00,&rx_data); +SpiPs_write_read1(0x901,0x00,&rx_data); +SpiPs_write_read1(0x903,0x11,&rx_data); +SpiPs_write_read1(0x908,0x77,&rx_data); +SpiPs_write_read1(0x909,0x03,&rx_data); +SpiPs_write_read1(0x90A,0x32,&rx_data); +SpiPs_write_read1(0x90D,0x00,&rx_data); +SpiPs_write_read1(0x90E,0x0F,&rx_data); +SpiPs_write_read1(0x90F,0x65,&rx_data); +SpiPs_write_read1(0x910,0x32,&rx_data); +SpiPs_write_read1(0x911,0x00,&rx_data); +SpiPs_write_read1(0x912,0x0F,&rx_data); +SpiPs_write_read1(0x913,0x3A,&rx_data); +SpiPs_write_read1(0x914,0x37,&rx_data); +SpiPs_write_read1(0x915,0x34,&rx_data); +SpiPs_write_read1(0x916,0x31,&rx_data); +//½øÈëÐÝÃßģʽ +SpiPs_write_read1(0x02E,0x01,&rx_data); +usleep(1000000); +//½øÈëУ׼ģʽ +SpiPs_write_read1(0x02E,0x03,&rx_data); +usleep(100000); +//BBPLL AFC +SpiPs_write_read1(0x600,0x00,&rx_data); +SpiPs_write_read1(0x600,0x11,&rx_data); +usleep(50); +//PLL1 AFC +while(temp != 48) +{ +SpiPs_write_read1(0x617,0x00,&rx_data); +SpiPs_write_read1(0x617,0x03,&rx_data); +usleep(100); +SpiPs_write_read2(0x635,0x00,&rx_data); +temp=rx_data; +} +//½ӿÚÅäÖà +SpiPs_write_read1(0x02E,0x06,&rx_data); +SpiPs_write_read1(0x003,0x33,&rx_data); +SpiPs_write_read1(0x200,0x00,&rx_data); +SpiPs_write_read1(0x201,0x00,&rx_data); +SpiPs_write_read1(0x202,0x03,&rx_data); +SpiPs_write_read1(0x208,0x00,&rx_data); +SpiPs_write_read1(0x209,0x00,&rx_data); +SpiPs_write_read1(0x780,0x11,&rx_data); +SpiPs_write_read1(0x781,0x6F,&rx_data); +SpiPs_write_read1(0x782,0x01,&rx_data); +SpiPs_write_read1(0x783,0x01,&rx_data); +SpiPs_write_read1(0x784,0x01,&rx_data); +//ADCУ׼ +SpiPs_write_read1(0x70B,0x1E,&rx_data); +SpiPs_write_read1(0x727,0x1E,&rx_data); +SpiPs_write_read1(0x743,0x1E,&rx_data); +SpiPs_write_read1(0x70F,0xA0,&rx_data); +SpiPs_write_read1(0x710,0x83,&rx_data); +SpiPs_write_read1(0x711,0xF0,&rx_data); +SpiPs_write_read1(0x72B,0xA0,&rx_data); +SpiPs_write_read1(0x72C,0x83,&rx_data); +SpiPs_write_read1(0x72D,0xF0,&rx_data); +SpiPs_write_read1(0x747,0xA0,&rx_data); +SpiPs_write_read1(0x748,0x83,&rx_data); +SpiPs_write_read1(0x749,0xF0,&rx_data); +SpiPs_write_read1(0x713,0x80,&rx_data); +SpiPs_write_read1(0x72F,0x80,&rx_data); +SpiPs_write_read1(0x74B,0x80,&rx_data); +SpiPs_write_read1(0x70B,0x9E,&rx_data); +SpiPs_write_read1(0x727,0x9E,&rx_data); +SpiPs_write_read1(0x743,0x9E,&rx_data); +SpiPs_write_read1(0x70B,0x9A,&rx_data); +SpiPs_write_read1(0x727,0x9A,&rx_data); +SpiPs_write_read1(0x743,0x9A,&rx_data); +SpiPs_write_read1(0x70B,0x9E,&rx_data); +SpiPs_write_read1(0x727,0x9E,&rx_data); +SpiPs_write_read1(0x743,0x9E,&rx_data); +usleep(100000); +SpiPs_write_read1(0x70B,0x1E,&rx_data); +SpiPs_write_read1(0x727,0x1E,&rx_data); +SpiPs_write_read1(0x743,0x1E,&rx_data); +SpiPs_write_read1(0x713,0xC0,&rx_data); +SpiPs_write_read1(0x72F,0xC0,&rx_data); +SpiPs_write_read1(0x74B,0xC0,&rx_data); +//FlashУ׼ +SpiPs_write_read1(0x70B,0x1E,&rx_data); +SpiPs_write_read1(0x727,0x1E,&rx_data); +SpiPs_write_read1(0x743,0x1E,&rx_data); +SpiPs_write_read1(0x715,0x80,&rx_data); +SpiPs_write_read1(0x731,0x80,&rx_data); +SpiPs_write_read1(0x74D,0x80,&rx_data); +SpiPs_write_read1(0x715,0xC0,&rx_data); +SpiPs_write_read1(0x731,0xC0,&rx_data); +SpiPs_write_read1(0x74D,0xC0,&rx_data); +SpiPs_write_read1(0x716,0x40,&rx_data); +SpiPs_write_read1(0x732,0x40,&rx_data); +SpiPs_write_read1(0x74E,0x40,&rx_data); +SpiPs_write_read1(0x70B,0x16,&rx_data); +SpiPs_write_read1(0x727,0x16,&rx_data); +SpiPs_write_read1(0x743,0x16,&rx_data); +usleep(100000); +SpiPs_write_read1(0x70B,0x1E,&rx_data); +SpiPs_write_read1(0x727,0x1E,&rx_data); +SpiPs_write_read1(0x743,0x1E,&rx_data); +SpiPs_write_read1(0x715,0x80,&rx_data); +SpiPs_write_read1(0x731,0x80,&rx_data); +SpiPs_write_read1(0x74D,0x80,&rx_data); +SpiPs_write_read1(0x716,0x00,&rx_data); +SpiPs_write_read1(0x732,0x00,&rx_data); +SpiPs_write_read1(0x74E,0x00,&rx_data); +SpiPs_write_read1(0x109,0x11,&rx_data); +SpiPs_write_read1(0x10A,0x70,&rx_data); +SpiPs_write_read1(0x11F,0x33,&rx_data); +//QECУ׼ +//Rx TST Calibration +SpiPs_write_read1(0x000,0x12,&rx_data); +SpiPs_write_read1(0x109,0x00,&rx_data); +SpiPs_write_read1(0x77E,0x50,&rx_data); +SpiPs_write_read1(0x14E,0x00,&rx_data); +// Ð޸ÄУ׼ģʽµçԴ¹ÜÀí +SpiPs_write_read1(0x03D,0x09,&rx_data); +SpiPs_write_read1(0x03E,0x39,&rx_data); +SpiPs_write_read1(0x03F,0xB6,&rx_data); +SpiPs_write_read1(0x040,0x78,&rx_data); +//Ð޸ĵçԴģʽ +SpiPs_write_read1(0x02E,0x03,&rx_data); +//Calibration PLL Source +// Ð޸ÄPllÀ´Դ +SpiPs_write_read1(0x77D,0x90,&rx_data); +SpiPs_write_read1(0x6C3,0x05,&rx_data); +SpiPs_write_read1(0x775,0x24,&rx_data); +//Ð޸ÄPll4ƵÂÊ +//PLL4 +SpiPs_write_read1(0x686,0x81,&rx_data); +SpiPs_write_read1(0x683,0x07,&rx_data); +SpiPs_write_read1(0xB30,0x00,&rx_data); +SpiPs_write_read1(0xB31,0x63,&rx_data); +SpiPs_write_read1(0xB32,0xEB,&rx_data); +SpiPs_write_read1(0xB33,0x85,&rx_data); +SpiPs_write_read1(0xB34,0x1E,&rx_data); +SpiPs_write_read1(0x680,0x0B,&rx_data); +SpiPs_write_read1(0x681,0x20,&rx_data); +SpiPs_write_read1(0x682,0x13,&rx_data); +SpiPs_write_read1(0x68A,0x11,&rx_data); +SpiPs_write_read1(0x68B,0x11,&rx_data); +SpiPs_write_read1(0x690,0x0B,&rx_data); +SpiPs_write_read1(0x684,0x30,&rx_data); +SpiPs_write_read1(0x685,0x08,&rx_data); +SpiPs_write_read1(0x687,0x4D,&rx_data); +SpiPs_write_read1(0x688,0x19,&rx_data); +SpiPs_write_read1(0x68D,0x10,&rx_data); +//PLL4 AFC +while(temp != 48) +{ +SpiPs_write_read1(0x67D,0x00,&rx_data); +SpiPs_write_read1(0x67D,0x03,&rx_data); +usleep(100); +SpiPs_write_read2(0x69B,0x00,&rx_data); +temp=rx_data; +} +SpiPs_write_read1(0x02A,0x00,&rx_data); +SpiPs_write_read1(0x121,0x00,&rx_data); +SpiPs_write_read1(0x11F,0x73,&rx_data); +SpiPs_write_read1(0x124,0xFF,&rx_data); +SpiPs_write_read1(0x120,0x00,&rx_data); +SpiPs_write_read1(0x122,0x03,&rx_data); +SpiPs_write_read1(0x126,0x01,&rx_data); +SpiPs_write_read1(0x02A,0x12,&rx_data); + +SpiPs_write_read2(0x14D,0x00,&rx_data); +temp=rx_data & 0x10; +while(temp != 16){ +SpiPs_write_read2(0x14D,0x00,&rx_data); +temp=rx_data & 0x10; +usleep(100000); +} + +SpiPs_write_read1(0x77E,0x00,&rx_data); +SpiPs_write_read1(0x775,0x24,&rx_data); +SpiPs_write_read1(0x77D,0xCF,&rx_data); +SpiPs_write_read1(0x6C3,0x00,&rx_data); +//Rx TST Calibration +SpiPs_write_read1(0x000,0x22,&rx_data); +SpiPs_write_read1(0x109,0x00,&rx_data); +SpiPs_write_read1(0x77E,0x0A,&rx_data); +SpiPs_write_read1(0x14E,0x00,&rx_data); +// Ð޸ÄУ׼ģʽµçԴ¹ÜÀí +SpiPs_write_read1(0x03D,0x12,&rx_data); +SpiPs_write_read1(0x03E,0x39,&rx_data); +SpiPs_write_read1(0x03F,0x6E,&rx_data); +SpiPs_write_read1(0x040,0x78,&rx_data); +//Ð޸ĵçԴģʽ +SpiPs_write_read1(0x02E,0x03,&rx_data); +//Calibration PLL Source +// Ð޸ÄPllÀ´Դ +SpiPs_write_read1(0x77D,0x60,&rx_data); +SpiPs_write_read1(0x6C3,0x0A,&rx_data); +SpiPs_write_read1(0x775,0x24,&rx_data); +//Ð޸ÄPll4ƵÂÊ +//PLL4 +SpiPs_write_read1(0x686,0x81,&rx_data); +SpiPs_write_read1(0x683,0x07,&rx_data); +SpiPs_write_read1(0xB30,0x00,&rx_data); +SpiPs_write_read1(0xB31,0x63,&rx_data); +SpiPs_write_read1(0xB32,0xEB,&rx_data); +SpiPs_write_read1(0xB33,0x85,&rx_data); +SpiPs_write_read1(0xB34,0x1E,&rx_data); +SpiPs_write_read1(0x680,0x0B,&rx_data); +SpiPs_write_read1(0x681,0x20,&rx_data); +SpiPs_write_read1(0x682,0x13,&rx_data); +SpiPs_write_read1(0x68A,0x11,&rx_data); +SpiPs_write_read1(0x68B,0x11,&rx_data); +SpiPs_write_read1(0x690,0x0B,&rx_data); +SpiPs_write_read1(0x684,0x30,&rx_data); +SpiPs_write_read1(0x685,0x08,&rx_data); +SpiPs_write_read1(0x687,0x4D,&rx_data); +SpiPs_write_read1(0x688,0x19,&rx_data); +SpiPs_write_read1(0x68D,0x10,&rx_data); +//PLL4 AFC +while(temp != 48) +{ +SpiPs_write_read1(0x67D,0x00,&rx_data); +SpiPs_write_read1(0x67D,0x03,&rx_data); +usleep(100); +SpiPs_write_read2(0x69B,0x00,&rx_data); +temp=rx_data; +} +SpiPs_write_read1(0x02B,0x00,&rx_data); +SpiPs_write_read1(0x121,0x00,&rx_data); +SpiPs_write_read1(0x11F,0x73,&rx_data); +SpiPs_write_read1(0x124,0xFF,&rx_data); +SpiPs_write_read1(0x120,0x00,&rx_data); +SpiPs_write_read1(0x122,0x03,&rx_data); +SpiPs_write_read1(0x126,0x01,&rx_data); +SpiPs_write_read1(0x02B,0x12,&rx_data); + +SpiPs_write_read2(0x14D,0x00,&rx_data); +temp=rx_data & 0x10; +while(temp != 16){ +SpiPs_write_read2(0x14D,0x00,&rx_data); +temp=rx_data & 0x10; +usleep(100000); +} + +SpiPs_write_read1(0x77E,0x00,&rx_data); +SpiPs_write_read1(0x775,0x24,&rx_data); +SpiPs_write_read1(0x77D,0xCF,&rx_data); +SpiPs_write_read1(0x6C3,0x00,&rx_data); +SpiPs_write_read1(0x02C,0x00,&rx_data); +SpiPs_write_read1(0x02B,0x00,&rx_data); +SpiPs_write_read1(0x02A,0x00,&rx_data); +//»ָ´У׼ǰÉèÖà +SpiPs_write_read1(0x000,0x02,&rx_data); +SpiPs_write_read1(0x02E,0x06,&rx_data); +SpiPs_write_read1(0x03D,0x1B,&rx_data); +SpiPs_write_read1(0x03E,0x39,&rx_data); +SpiPs_write_read1(0x03F,0x27,&rx_data); +SpiPs_write_read1(0x040,0xF8,&rx_data); +SpiPs_write_read1(0x109,0x11,&rx_data); +SpiPs_write_read1(0x10A,0x70,&rx_data); +SpiPs_write_read1(0x11B,0x01,&rx_data); +SpiPs_write_read1(0x11E,0x01,&rx_data); +SpiPs_write_read1(0x11F,0x33,&rx_data); +SpiPs_write_read1(0x128,0x10,&rx_data); +SpiPs_write_read1(0x161,0x01,&rx_data); +SpiPs_write_read1(0x162,0x01,&rx_data); +SpiPs_write_read1(0x164,0x05,&rx_data); +SpiPs_write_read1(0x165,0x55,&rx_data); +SpiPs_write_read1(0x166,0x55,&rx_data); +SpiPs_write_read1(0x167,0x55,&rx_data); +SpiPs_write_read1(0x168,0x55,&rx_data); +SpiPs_write_read1(0x169,0x55,&rx_data); +SpiPs_write_read1(0xA07,0x00,&rx_data); +SpiPs_write_read1(0xA08,0x00,&rx_data); +SpiPs_write_read1(0xA16,0x00,&rx_data); +SpiPs_write_read1(0xA17,0x00,&rx_data); +SpiPs_write_read1(0xA18,0x00,&rx_data); +SpiPs_write_read1(0xA19,0x00,&rx_data); +SpiPs_write_read1(0xA1A,0x00,&rx_data); +SpiPs_write_read1(0xA1B,0x00,&rx_data); +SpiPs_write_read1(0xA1C,0x00,&rx_data); +SpiPs_write_read1(0xA2D,0x00,&rx_data); +// TX¶þ·ַ¨У׼ +// Ð޸ÄУ׼ģʽµçԴ¹ÜÀí +SpiPs_write_read1(0x03D,0x1B,&rx_data); +SpiPs_write_read1(0x03E,0x39,&rx_data); +SpiPs_write_read1(0x03F,0x27,&rx_data); +SpiPs_write_read1(0x040,0xF8,&rx_data); +//Ð޸ĵçԴģʽ +SpiPs_write_read1(0x02E,0x03,&rx_data); +SpiPs_write_read1(0x128,0x11,&rx_data); +SpiPs_write_read1(0x11E,0x01,&rx_data); +SpiPs_write_read1(0x77E,0x24,&rx_data); +SpiPs_write_read1(0x11F,0x01,&rx_data); +SpiPs_write_read1(0x121,0x02,&rx_data); +SpiPs_write_read1(0x11B,0x01,&rx_data); +SpiPs_write_read1(0x161,0x01,&rx_data); +SpiPs_write_read1(0x162,0x01,&rx_data); +SpiPs_write_read1(0x163,0x00,&rx_data); +SpiPs_write_read1(0x164,0x05,&rx_data); +SpiPs_write_read1(0x165,0x55,&rx_data); +SpiPs_write_read1(0x166,0x55,&rx_data); +SpiPs_write_read1(0x167,0x55,&rx_data); +SpiPs_write_read1(0x168,0x55,&rx_data); +SpiPs_write_read1(0x169,0x55,&rx_data); +SpiPs_write_read1(0xA2D,0x02,&rx_data); +SpiPs_write_read1(0xA07,0x00,&rx_data); +SpiPs_write_read1(0xA08,0x0F,&rx_data); +SpiPs_write_read1(0xA1C,0x01,&rx_data); +SpiPs_write_read1(0xA16,0x2A,&rx_data); +SpiPs_write_read1(0xA17,0xAA,&rx_data); +SpiPs_write_read1(0xA18,0xAA,&rx_data); +SpiPs_write_read1(0xA19,0xAA,&rx_data); +SpiPs_write_read1(0xA1A,0xAA,&rx_data); +SpiPs_write_read1(0xA1B,0xAA,&rx_data); +SpiPs_write_read1(0x186,0x01,&rx_data); +SpiPs_write_read1(0x763,0x34,&rx_data); +SpiPs_write_read1(0x76D,0x34,&rx_data); +SpiPs_write_read1(0x101,0xEF,&rx_data); +SpiPs_write_read1(0x101,0xFF,&rx_data); +SpiPs_write_read1(0x800,0x00,&rx_data); +SpiPs_write_read1(0x81A,0xF7,&rx_data); +SpiPs_write_read1(0x81B,0x34,&rx_data); +SpiPs_write_read1(0x81C,0x33,&rx_data); +SpiPs_write_read1(0x81D,0x12,&rx_data); +SpiPs_write_read1(0x81E,0x00,&rx_data); +SpiPs_write_read1(0x81F,0xFF,&rx_data); +SpiPs_write_read1(0x820,0x00,&rx_data); +SpiPs_write_read1(0x821,0xFF,&rx_data); +SpiPs_write_read1(0x800,0x80,&rx_data); +SpiPs_write_read1(0x800,0x81,&rx_data); +//×îÉÙ0.1s +usleep(100000); +SpiPs_write_read1(0x77E,0x00,&rx_data); +SpiPs_write_read1(0x77E,0x24,&rx_data); +SpiPs_write_read1(0x11F,0x03,&rx_data); +SpiPs_write_read1(0x121,0x02,&rx_data); +SpiPs_write_read1(0x11B,0x01,&rx_data); +SpiPs_write_read1(0x11E,0x01,&rx_data); +SpiPs_write_read1(0x161,0x01,&rx_data); +SpiPs_write_read1(0x162,0x01,&rx_data); +SpiPs_write_read1(0x164,0x05,&rx_data); +SpiPs_write_read1(0x165,0x55,&rx_data); +SpiPs_write_read1(0x166,0x55,&rx_data); +SpiPs_write_read1(0x167,0x55,&rx_data); +SpiPs_write_read1(0x168,0x55,&rx_data); +SpiPs_write_read1(0x169,0x55,&rx_data); +SpiPs_write_read1(0xA2D,0x02,&rx_data); +SpiPs_write_read1(0xA07,0x00,&rx_data); +SpiPs_write_read1(0xA08,0x0F,&rx_data); +SpiPs_write_read1(0xA1C,0x01,&rx_data); +SpiPs_write_read1(0xA16,0x15,&rx_data); +SpiPs_write_read1(0xA17,0x55,&rx_data); +SpiPs_write_read1(0xA18,0x55,&rx_data); +SpiPs_write_read1(0xA19,0x55,&rx_data); +SpiPs_write_read1(0xA1A,0x55,&rx_data); +SpiPs_write_read1(0xA1B,0x55,&rx_data); +SpiPs_write_read1(0x81A,0xF7,&rx_data); +SpiPs_write_read1(0x81B,0x44,&rx_data); +SpiPs_write_read1(0x81C,0x33,&rx_data); +SpiPs_write_read1(0x81D,0x14,&rx_data); +SpiPs_write_read1(0x800,0x80,&rx_data); +SpiPs_write_read1(0x800,0x82,&rx_data); +//×îÉÙ0.1s +usleep(500000); +SpiPs_write_read1(0x77E,0x00,&rx_data); +//»ָ´У׼ǰÉèÖà +SpiPs_write_read1(0x000,0x02,&rx_data); +SpiPs_write_read1(0x02E,0x06,&rx_data); +SpiPs_write_read1(0x03D,0x1B,&rx_data); +SpiPs_write_read1(0x03E,0x39,&rx_data); +SpiPs_write_read1(0x03F,0x27,&rx_data); +SpiPs_write_read1(0x040,0xF8,&rx_data); +SpiPs_write_read1(0x109,0x11,&rx_data); +SpiPs_write_read1(0x10A,0x70,&rx_data); +SpiPs_write_read1(0x11B,0x01,&rx_data); +SpiPs_write_read1(0x11E,0x01,&rx_data); +SpiPs_write_read1(0x11F,0x33,&rx_data); +SpiPs_write_read1(0x128,0x10,&rx_data); +SpiPs_write_read1(0x161,0x01,&rx_data); +SpiPs_write_read1(0x162,0x01,&rx_data); +SpiPs_write_read1(0x164,0x05,&rx_data); +SpiPs_write_read1(0x165,0x55,&rx_data); +SpiPs_write_read1(0x166,0x55,&rx_data); +SpiPs_write_read1(0x167,0x55,&rx_data); +SpiPs_write_read1(0x168,0x55,&rx_data); +SpiPs_write_read1(0x169,0x55,&rx_data); +SpiPs_write_read1(0xA07,0x00,&rx_data); +SpiPs_write_read1(0xA08,0x00,&rx_data); +SpiPs_write_read1(0xA16,0x00,&rx_data); +SpiPs_write_read1(0xA17,0x00,&rx_data); +SpiPs_write_read1(0xA18,0x00,&rx_data); +SpiPs_write_read1(0xA19,0x00,&rx_data); +SpiPs_write_read1(0xA1A,0x00,&rx_data); +SpiPs_write_read1(0xA1B,0x00,&rx_data); +SpiPs_write_read1(0xA1C,0x00,&rx_data); +SpiPs_write_read1(0xA2D,0x00,&rx_data); +usleep(1000); +SpiPs_write_read2(0x615,0x00,&rx_data); +temp=rx_data; +SpiPs_write_read2(0x635,0x00,&rx_data); +temp=rx_data; +////ÅäÖÃTx NCO BIST ON +//SpiPs_write_read1(0x11B,0x01,&rx_data); +//SpiPs_write_read1(0x11E,0x01,&rx_data); +//SpiPs_write_read1(0x161,0x01,&rx_data); +//SpiPs_write_read1(0x162,0x01,&rx_data); +////ÅäÖÃTx NCOƵÂÊ 1MHz +//SpiPs_write_read1(0x164,0x05,&rx_data); +//SpiPs_write_read1(0x165,0x55,&rx_data); +//SpiPs_write_read1(0x166,0x55,&rx_data); +//SpiPs_write_read1(0x167,0x55,&rx_data); +//SpiPs_write_read1(0x168,0x55,&rx_data); +//SpiPs_write_read1(0x169,0x55,&rx_data); +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/cx9261a_710_config.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/cx9261a_710_config.c new file mode 100644 index 0000000..c8bb612 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/cx9261a_710_config.c @@ -0,0 +1,835 @@ +/* + * cx9261a_710_config.c + * + * Created on: 2024Äê3ÔÂ21ÈÕ + * Author: cxlab + */ + + +/* + * cx9261a_076_config.c + * + * Created on: 2023Äê11ÔÂ30ÈÕ + * Author: cxlab + */ + + +#include "xilinx_io.h" +#include "sleep.h" +void cx9261a_710_config(void){ +u8 rx_data; +u8 temp,r13D,r13E; + +//setPin(pin,value) +GpioSetPinDir(0,0x01); +GpioWritePin(0,1); +GpioSetPinDir(32,0x01); +GpioWritePin(32,1); +GpioSetPinDir(33,0x01); +GpioWritePin(33,1); +GpioSetPinDir(34,0x01); +GpioWritePin(34,0); +GpioSetPinDir(35,0x01); //tx:0-rx:1 +GpioWritePin(35,1); //tx:0-rx:1 +GpioSetPinDir(36,0x01); //fdd:0-tdd:1 +GpioWritePin(36,1); //fdd:0-tdd:1 +GpioSetPinDir(37,0x01); //1r1t:0-2r2t:1 +GpioWritePin(37,1); //1r1t:0-2r2t:1 +GpioSetPinDir(38,0x01); //dualport:0-singleport:1 +GpioWritePin(38,1); //dualport:0-singleport:1 +GpioSetPinDir(39,0x01); +GpioWritePin(39,1); +GpioSetPinDir(40,0x01); +GpioWritePin(40,1); +//ÅäÖÃоƬ +//³õʼ»¯оƬ +SpiPs_write_read1(0x000,0x02,&rx_data); +SpiPs_write_read1(0x773,0x53,&rx_data); +SpiPs_write_read1(0x774,0x86,&rx_data); +SpiPs_write_read1(0x781,0x6F,&rx_data); +SpiPs_write_read1(0x70B,0x0E,&rx_data); +SpiPs_write_read1(0x727,0x0E,&rx_data); +SpiPs_write_read1(0x743,0x0E,&rx_data); +SpiPs_write_read1(0x624,0x11,&rx_data); +SpiPs_write_read1(0x625,0x11,&rx_data); +SpiPs_write_read1(0x61E,0x30,&rx_data); +SpiPs_write_read1(0x61F,0x08,&rx_data); +SpiPs_write_read1(0x621,0x4D,&rx_data); +SpiPs_write_read1(0x622,0x19,&rx_data); +SpiPs_write_read1(0x627,0x10,&rx_data); +SpiPs_write_read1(0x646,0x11,&rx_data); +SpiPs_write_read1(0x647,0x11,&rx_data); +SpiPs_write_read1(0x640,0x30,&rx_data); +SpiPs_write_read1(0x641,0x08,&rx_data); +SpiPs_write_read1(0x643,0x4D,&rx_data); +SpiPs_write_read1(0x644,0x19,&rx_data); +SpiPs_write_read1(0x649,0x10,&rx_data); +SpiPs_write_read1(0x668,0x11,&rx_data); +SpiPs_write_read1(0x669,0x11,&rx_data); +SpiPs_write_read1(0x662,0x30,&rx_data); +SpiPs_write_read1(0x663,0x08,&rx_data); +SpiPs_write_read1(0x665,0x4D,&rx_data); +SpiPs_write_read1(0x666,0x19,&rx_data); +SpiPs_write_read1(0x66B,0x10,&rx_data); +SpiPs_write_read1(0x68A,0x11,&rx_data); +SpiPs_write_read1(0x68B,0x11,&rx_data); +SpiPs_write_read1(0x684,0x30,&rx_data); +SpiPs_write_read1(0x685,0x08,&rx_data); +SpiPs_write_read1(0x687,0x4D,&rx_data); +SpiPs_write_read1(0x688,0x19,&rx_data); +SpiPs_write_read1(0x68D,0x10,&rx_data); +SpiPs_write_read1(0x6AC,0x11,&rx_data); +SpiPs_write_read1(0x6AD,0x11,&rx_data); +SpiPs_write_read1(0x6A6,0x30,&rx_data); +SpiPs_write_read1(0x6A7,0x08,&rx_data); +SpiPs_write_read1(0x6A9,0x4D,&rx_data); +SpiPs_write_read1(0x6AA,0x19,&rx_data); +SpiPs_write_read1(0x6AF,0x10,&rx_data); +SpiPs_write_read1(0x705,0xFC,&rx_data); +SpiPs_write_read1(0x706,0x6D,&rx_data); +SpiPs_write_read1(0x707,0x91,&rx_data); +SpiPs_write_read1(0x708,0x04,&rx_data); +SpiPs_write_read1(0x709,0x94,&rx_data); +SpiPs_write_read1(0x70A,0xFC,&rx_data); +SpiPs_write_read1(0x70C,0xC0,&rx_data); +SpiPs_write_read1(0x721,0xFC,&rx_data); +SpiPs_write_read1(0x722,0x6D,&rx_data); +SpiPs_write_read1(0x723,0x91,&rx_data); +SpiPs_write_read1(0x724,0x04,&rx_data); +SpiPs_write_read1(0x725,0x94,&rx_data); +SpiPs_write_read1(0x726,0xFC,&rx_data); +SpiPs_write_read1(0x728,0xC0,&rx_data); +SpiPs_write_read1(0x73D,0xFC,&rx_data); +SpiPs_write_read1(0x73E,0x6D,&rx_data); +SpiPs_write_read1(0x73F,0x91,&rx_data); +SpiPs_write_read1(0x740,0x04,&rx_data); +SpiPs_write_read1(0x741,0x94,&rx_data); +SpiPs_write_read1(0x742,0xFC,&rx_data); +SpiPs_write_read1(0x744,0xC0,&rx_data); +SpiPs_write_read1(0x75A,0x83,&rx_data); +SpiPs_write_read1(0x75B,0xDA,&rx_data); +usleep(100000); +SpiPs_write_read1(0x75B,0x9A,&rx_data); +SpiPs_write_read1(0x75C,0x81,&rx_data); +SpiPs_write_read1(0x763,0x04,&rx_data); +SpiPs_write_read1(0x764,0x83,&rx_data); +SpiPs_write_read1(0x765,0xDA,&rx_data); +usleep(100000); +SpiPs_write_read1(0x765,0x9A,&rx_data); +SpiPs_write_read1(0x766,0x81,&rx_data); +SpiPs_write_read1(0x76D,0x04,&rx_data); +//³õʼ»¯µçԴ +SpiPs_write_read1(0x031,0x00,&rx_data); +SpiPs_write_read1(0x032,0x7F,&rx_data); +SpiPs_write_read1(0x033,0xFF,&rx_data); +SpiPs_write_read1(0x034,0xFF,&rx_data); +SpiPs_write_read1(0x035,0x00,&rx_data); +SpiPs_write_read1(0x036,0x3D,&rx_data); +SpiPs_write_read1(0x037,0xFF,&rx_data); +SpiPs_write_read1(0x038,0xFB,&rx_data); +SpiPs_write_read1(0x039,0x00,&rx_data); +SpiPs_write_read1(0x03A,0x3D,&rx_data); +SpiPs_write_read1(0x03B,0xFF,&rx_data); +SpiPs_write_read1(0x03C,0xF8,&rx_data); +SpiPs_write_read1(0x03D,0x1B,&rx_data); +SpiPs_write_read1(0x03E,0x39,&rx_data); +SpiPs_write_read1(0x03F,0x26,&rx_data); +SpiPs_write_read1(0x040,0x78,&rx_data); +SpiPs_write_read1(0x041,0x03,&rx_data); +SpiPs_write_read1(0x042,0x3D,&rx_data); +SpiPs_write_read1(0x043,0xE7,&rx_data); +SpiPs_write_read1(0x044,0xF8,&rx_data); +SpiPs_write_read1(0x045,0x18,&rx_data); +SpiPs_write_read1(0x046,0x3D,&rx_data); +SpiPs_write_read1(0x047,0x3F,&rx_data); +SpiPs_write_read1(0x048,0xF8,&rx_data); +SpiPs_write_read1(0x049,0x1F,&rx_data); +SpiPs_write_read1(0x04A,0x3D,&rx_data); +SpiPs_write_read1(0x04B,0x07,&rx_data); +SpiPs_write_read1(0x04C,0xF8,&rx_data); +SpiPs_write_read1(0x04D,0x04,&rx_data); +SpiPs_write_read1(0x04E,0x3D,&rx_data); +SpiPs_write_read1(0x04F,0xD9,&rx_data); +SpiPs_write_read1(0x050,0xFE,&rx_data); +//×Զ¨Òå½ÓÊÕģʽ +SpiPs_write_read1(0x041,0x03,&rx_data); +SpiPs_write_read1(0x042,0x3D,&rx_data); +SpiPs_write_read1(0x043,0xE7,&rx_data); +SpiPs_write_read1(0x044,0xF8,&rx_data); +//×Զ¨Ò巢Éäģʽ +SpiPs_write_read1(0x045,0x18,&rx_data); +SpiPs_write_read1(0x046,0x3D,&rx_data); +SpiPs_write_read1(0x047,0x3F,&rx_data); +SpiPs_write_read1(0x048,0xF8,&rx_data); +//×Զ¨ÒåFDDģʽ +SpiPs_write_read1(0x049,0x1B,&rx_data); +SpiPs_write_read1(0x04A,0x3D,&rx_data); +SpiPs_write_read1(0x04B,0x27,&rx_data); +SpiPs_write_read1(0x04C,0xF8,&rx_data); +//ÅäÖÃBBPLL +SpiPs_write_read1(0x789,0x10,&rx_data); +SpiPs_write_read1(0x785,0x49,&rx_data); +SpiPs_write_read1(0x788,0x24,&rx_data); +usleep(100000); +SpiPs_write_read1(0x788,0xA4,&rx_data); +SpiPs_write_read1(0x0C0,0x50,&rx_data); +SpiPs_write_read1(0x0C1,0x50,&rx_data); +SpiPs_write_read1(0x0C2,0x50,&rx_data); +SpiPs_write_read1(0x601,0x0F,&rx_data); +SpiPs_write_read1(0x602,0x21,&rx_data); +SpiPs_write_read1(0x60D,0x0D,&rx_data); +SpiPs_write_read1(0x60E,0xFF,&rx_data); +SpiPs_write_read1(0x60F,0xFF,&rx_data); +SpiPs_write_read1(0x610,0xFF,&rx_data); +SpiPs_write_read1(0x611,0x2F,&rx_data); +SpiPs_write_read1(0x612,0x00,&rx_data); +SpiPs_write_read1(0x70E,0x6B,&rx_data); +SpiPs_write_read1(0x72A,0x6B,&rx_data); +SpiPs_write_read1(0x746,0x6B,&rx_data); +SpiPs_write_read1(0x70F,0xE0,&rx_data); +SpiPs_write_read1(0x72B,0xE0,&rx_data); +SpiPs_write_read1(0x747,0xE0,&rx_data); +SpiPs_write_read1(0x70D,0x43,&rx_data); +SpiPs_write_read1(0x729,0x43,&rx_data); +SpiPs_write_read1(0x745,0x43,&rx_data); +SpiPs_write_read1(0x782,0x76,&rx_data); +SpiPs_write_read1(0x783,0x76,&rx_data); +SpiPs_write_read1(0x784,0x06,&rx_data); +//ÅäÖÃPLLÀ´Դ +SpiPs_write_read1(0x77D,0xCF,&rx_data); +SpiPs_write_read1(0x775,0x24,&rx_data); +SpiPs_write_read1(0x6C3,0x00,&rx_data); +//ÅäÖÃPLL1 +SpiPs_write_read1(0x620,0x91,&rx_data); +SpiPs_write_read1(0x61D,0x37,&rx_data); +SpiPs_write_read1(0xB06,0x00,&rx_data); +SpiPs_write_read1(0xB07,0xCB,&rx_data); +SpiPs_write_read1(0xB08,0x20,&rx_data); +SpiPs_write_read1(0xB09,0x00,&rx_data); +SpiPs_write_read1(0xB0A,0x00,&rx_data); +SpiPs_write_read1(0x61A,0x0C,&rx_data); +SpiPs_write_read1(0x61B,0x30,&rx_data); +SpiPs_write_read1(0x61C,0x08,&rx_data); +SpiPs_write_read1(0x62A,0x0B,&rx_data); +//ÅäÖÃPLL2 +SpiPs_write_read1(0x642,0x91,&rx_data); +SpiPs_write_read1(0x63F,0x35,&rx_data); +SpiPs_write_read1(0xB14,0x00,&rx_data); +SpiPs_write_read1(0xB15,0xFA,&rx_data); +SpiPs_write_read1(0xB16,0x00,&rx_data); +SpiPs_write_read1(0xB17,0x00,&rx_data); +SpiPs_write_read1(0xB18,0x00,&rx_data); +SpiPs_write_read1(0x63C,0x0D,&rx_data); +SpiPs_write_read1(0x63D,0x30,&rx_data); +SpiPs_write_read1(0x63E,0x0B,&rx_data); +SpiPs_write_read1(0x64C,0x00,&rx_data); +//ÅäÖÃPLL3 +SpiPs_write_read1(0x664,0x81,&rx_data); +SpiPs_write_read1(0x661,0x36,&rx_data); +SpiPs_write_read1(0xB22,0x00,&rx_data); +SpiPs_write_read1(0xB23,0xBB,&rx_data); +SpiPs_write_read1(0xB24,0x80,&rx_data); +SpiPs_write_read1(0xB25,0x00,&rx_data); +SpiPs_write_read1(0xB26,0x00,&rx_data); +SpiPs_write_read1(0x65E,0x0B,&rx_data); +SpiPs_write_read1(0x65F,0x30,&rx_data); +SpiPs_write_read1(0x660,0x0E,&rx_data); +SpiPs_write_read1(0x66E,0x0B,&rx_data); +//ÅäÖÃPLL4 +SpiPs_write_read1(0x686,0x91,&rx_data); +SpiPs_write_read1(0x683,0x36,&rx_data); +SpiPs_write_read1(0xB30,0x00,&rx_data); +SpiPs_write_read1(0xB31,0xDA,&rx_data); +SpiPs_write_read1(0xB32,0xC0,&rx_data); +SpiPs_write_read1(0xB33,0x00,&rx_data); +SpiPs_write_read1(0xB34,0x00,&rx_data); +SpiPs_write_read1(0x680,0x0C,&rx_data); +SpiPs_write_read1(0x681,0x30,&rx_data); +SpiPs_write_read1(0x682,0x08,&rx_data); +SpiPs_write_read1(0x690,0x0B,&rx_data); +//ÅäÖÃPLL5 +SpiPs_write_read1(0x6A8,0x81,&rx_data); +SpiPs_write_read1(0x6A5,0x36,&rx_data); +SpiPs_write_read1(0xB3E,0x00,&rx_data); +SpiPs_write_read1(0xB3F,0x9C,&rx_data); +SpiPs_write_read1(0xB40,0x40,&rx_data); +SpiPs_write_read1(0xB41,0x00,&rx_data); +SpiPs_write_read1(0xB42,0x00,&rx_data); +SpiPs_write_read1(0x6A2,0x0C,&rx_data); +SpiPs_write_read1(0x6A3,0x30,&rx_data); +SpiPs_write_read1(0x6A4,0x0C,&rx_data); +SpiPs_write_read1(0x6B2,0x0B,&rx_data); +//ÅäÖÃTx1ͨµÀƵÂÊÏà¹ؼĴæÆ÷ +SpiPs_write_read1(0x75F,0x55,&rx_data); +SpiPs_write_read1(0x761,0x04,&rx_data); +SpiPs_write_read1(0x762,0x70,&rx_data); +//ÅäÖÃTx1ͨµÀƵÂÊÏà¹ؼĴæÆ÷ +SpiPs_write_read1(0x769,0x55,&rx_data); +SpiPs_write_read1(0x76B,0x04,&rx_data); +SpiPs_write_read1(0x76C,0x70,&rx_data); +//ÅäÖÃRx1ͨµÀ +SpiPs_write_read1(0x700,0x56,&rx_data); +SpiPs_write_read1(0x701,0x00,&rx_data); +SpiPs_write_read1(0x702,0xDB,&rx_data); +SpiPs_write_read1(0x703,0x45,&rx_data); +SpiPs_write_read1(0x704,0xF8,&rx_data); +SpiPs_write_read1(0x907,0x1F,&rx_data); +//ÅäÖÃRx2ͨµÀ +SpiPs_write_read1(0x71C,0x56,&rx_data); +SpiPs_write_read1(0x71D,0x00,&rx_data); +SpiPs_write_read1(0x71E,0xDB,&rx_data); +SpiPs_write_read1(0x71F,0x45,&rx_data); +SpiPs_write_read1(0x720,0xF8,&rx_data); +SpiPs_write_read1(0x907,0x1F,&rx_data); +//ÅäÖÃRx3ͨµÀ +SpiPs_write_read1(0x738,0x56,&rx_data); +SpiPs_write_read1(0x739,0x00,&rx_data); +SpiPs_write_read1(0x73A,0xDB,&rx_data); +SpiPs_write_read1(0x73B,0x45,&rx_data); +SpiPs_write_read1(0x73C,0xF8,&rx_data); +SpiPs_write_read1(0x907,0x1F,&rx_data); +//ÅäÖÃTx1ͨµÀ +SpiPs_write_read1(0x75E,0xFF,&rx_data); +SpiPs_write_read1(0x75D,0x4A,&rx_data); +SpiPs_write_read1(0x75F,0x7D,&rx_data); +SpiPs_write_read1(0x760,0xFF,&rx_data); +//ÅäÖÃTx2ͨµÀ +SpiPs_write_read1(0x768,0xFF,&rx_data); +SpiPs_write_read1(0x767,0x4A,&rx_data); +SpiPs_write_read1(0x769,0x7D,&rx_data); +SpiPs_write_read1(0x76A,0xFF,&rx_data); +//ÅäÖÃRxÂ˲¨Æ÷ +SpiPs_write_read1(0x106,0x02,&rx_data); +//ÅäÖÃTxÂ˲¨Æ÷ +SpiPs_write_read1(0x107,0x00,&rx_data); +SpiPs_write_read1(0x108,0xD0,&rx_data); +//ÅäÖÃRx»ìƵÆ÷ +SpiPs_write_read1(0x11C,0x00,&rx_data); +SpiPs_write_read1(0xA07,0x00,&rx_data); +SpiPs_write_read1(0xA08,0x00,&rx_data); +SpiPs_write_read1(0xA09,0x00,&rx_data); +SpiPs_write_read1(0xA16,0x00,&rx_data); +SpiPs_write_read1(0xA17,0x00,&rx_data); +SpiPs_write_read1(0xA18,0x00,&rx_data); +SpiPs_write_read1(0xA19,0x00,&rx_data); +SpiPs_write_read1(0xA1A,0x00,&rx_data); +SpiPs_write_read1(0xA1B,0x00,&rx_data); +//ÅäÖÃTx»ìƵÆ÷ +SpiPs_write_read1(0x128,0x10,&rx_data); +SpiPs_write_read1(0x11B,0x00,&rx_data); +SpiPs_write_read1(0x11E,0x00,&rx_data); +SpiPs_write_read1(0x161,0x00,&rx_data); +SpiPs_write_read1(0x162,0x00,&rx_data); +SpiPs_write_read1(0x164,0x00,&rx_data); +SpiPs_write_read1(0x165,0x00,&rx_data); +SpiPs_write_read1(0x166,0x00,&rx_data); +SpiPs_write_read1(0x167,0x00,&rx_data); +SpiPs_write_read1(0x168,0x00,&rx_data); +SpiPs_write_read1(0x169,0x00,&rx_data); +//ÅäÖÃAGC +SpiPs_write_read1(0x903,0x11,&rx_data); +SpiPs_write_read1(0x900,0x00,&rx_data); +SpiPs_write_read1(0x901,0x00,&rx_data); +SpiPs_write_read1(0x908,0x77,&rx_data); +SpiPs_write_read1(0x909,0x03,&rx_data); +SpiPs_write_read1(0x90A,0x32,&rx_data); +SpiPs_write_read1(0x90D,0x00,&rx_data); +SpiPs_write_read1(0x90E,0x0F,&rx_data); +SpiPs_write_read1(0x90F,0x65,&rx_data); +SpiPs_write_read1(0x910,0x32,&rx_data); +SpiPs_write_read1(0x911,0x00,&rx_data); +SpiPs_write_read1(0x912,0x0F,&rx_data); +SpiPs_write_read1(0x913,0x3A,&rx_data); +SpiPs_write_read1(0x914,0x37,&rx_data); +SpiPs_write_read1(0x915,0x34,&rx_data); +SpiPs_write_read1(0x916,0x31,&rx_data); +//Çл»µ½ÐÝÃßģʽ +SpiPs_write_read1(0x02E,0x01,&rx_data); +usleep(1000000); +//Çл»µ½У׼ģʽ +SpiPs_write_read1(0x02E,0x03,&rx_data); +usleep(100000); +//BBPllËø¶¨ +SpiPs_write_read1(0x600,0x00,&rx_data); +SpiPs_write_read1(0x600,0x11,&rx_data); +usleep(100000); +//BBPllËø¶¨ ³ɹ¦ +//ADCУ׼ +SpiPs_write_read1(0x70B,0x1E,&rx_data); +SpiPs_write_read1(0x727,0x1E,&rx_data); +SpiPs_write_read1(0x743,0x1E,&rx_data); +SpiPs_write_read1(0x70F,0xE0,&rx_data); +SpiPs_write_read1(0x710,0x83,&rx_data); +SpiPs_write_read1(0x711,0xF0,&rx_data); +SpiPs_write_read1(0x72B,0xE0,&rx_data); +SpiPs_write_read1(0x72C,0x83,&rx_data); +SpiPs_write_read1(0x72D,0xF0,&rx_data); +SpiPs_write_read1(0x747,0xE0,&rx_data); +SpiPs_write_read1(0x748,0x83,&rx_data); +SpiPs_write_read1(0x749,0xF0,&rx_data); +SpiPs_write_read1(0x713,0x80,&rx_data); +SpiPs_write_read1(0x72F,0x80,&rx_data); +SpiPs_write_read1(0x74B,0x80,&rx_data); +SpiPs_write_read1(0x70B,0x9E,&rx_data); +SpiPs_write_read1(0x727,0x9E,&rx_data); +SpiPs_write_read1(0x743,0x9E,&rx_data); +SpiPs_write_read1(0x70B,0x9A,&rx_data); +SpiPs_write_read1(0x727,0x9A,&rx_data); +SpiPs_write_read1(0x743,0x9A,&rx_data); +SpiPs_write_read1(0x70B,0x9E,&rx_data); +SpiPs_write_read1(0x727,0x9E,&rx_data); +SpiPs_write_read1(0x743,0x9E,&rx_data); +usleep(100000); +SpiPs_write_read1(0x70B,0x1E,&rx_data); +SpiPs_write_read1(0x727,0x1E,&rx_data); +SpiPs_write_read1(0x743,0x1E,&rx_data); +SpiPs_write_read1(0x713,0xC0,&rx_data); +SpiPs_write_read1(0x72F,0xC0,&rx_data); +SpiPs_write_read1(0x74B,0xC0,&rx_data); +//flashУ׼ +SpiPs_write_read1(0x70B,0x1E,&rx_data); +SpiPs_write_read1(0x727,0x1E,&rx_data); +SpiPs_write_read1(0x743,0x1E,&rx_data); +SpiPs_write_read1(0x715,0x80,&rx_data); +SpiPs_write_read1(0x731,0x80,&rx_data); +SpiPs_write_read1(0x74D,0x80,&rx_data); +SpiPs_write_read1(0x715,0xC0,&rx_data); +SpiPs_write_read1(0x731,0xC0,&rx_data); +SpiPs_write_read1(0x74D,0xC0,&rx_data); +SpiPs_write_read1(0x716,0x40,&rx_data); +SpiPs_write_read1(0x732,0x40,&rx_data); +SpiPs_write_read1(0x74E,0x40,&rx_data); +SpiPs_write_read1(0x70B,0x16,&rx_data); +SpiPs_write_read1(0x727,0x16,&rx_data); +SpiPs_write_read1(0x743,0x16,&rx_data); +usleep(100000); +SpiPs_write_read1(0x70B,0x1E,&rx_data); +SpiPs_write_read1(0x727,0x1E,&rx_data); +SpiPs_write_read1(0x743,0x1E,&rx_data); +SpiPs_write_read1(0x715,0x80,&rx_data); +SpiPs_write_read1(0x731,0x80,&rx_data); +SpiPs_write_read1(0x74D,0x80,&rx_data); +SpiPs_write_read1(0x716,0x00,&rx_data); +SpiPs_write_read1(0x732,0x00,&rx_data); +SpiPs_write_read1(0x74E,0x00,&rx_data); +//DC Tracking +SpiPs_write_read1(0x109,0x11,&rx_data); +SpiPs_write_read1(0x10A,0x70,&rx_data); +SpiPs_write_read1(0x11F,0x33,&rx_data); +//Rx1 QECУ׼ +SpiPs_write_read1(0x02E,0x03,&rx_data); +SpiPs_write_read1(0x03F,0x26,&rx_data); +SpiPs_write_read1(0x040,0x78,&rx_data); +SpiPs_write_read1(0x77D,0x90,&rx_data); +SpiPs_write_read1(0x775,0x24,&rx_data); +SpiPs_write_read1(0x6C3,0x05,&rx_data); +SpiPs_write_read1(0x61D,0x27,&rx_data); +SpiPs_write_read1(0x617,0x00,&rx_data); +SpiPs_write_read1(0x617,0x03,&rx_data); +usleep(100000); +SpiPs_write_read1(0x61D,0x37,&rx_data); +SpiPs_write_read1(0x686,0x81,&rx_data); +SpiPs_write_read1(0x683,0x36,&rx_data); +SpiPs_write_read1(0xB30,0x00,&rx_data); +SpiPs_write_read1(0xB31,0x87,&rx_data); +SpiPs_write_read1(0xB32,0x04,&rx_data); +SpiPs_write_read1(0xB33,0x44,&rx_data); +SpiPs_write_read1(0xB34,0x44,&rx_data); +SpiPs_write_read1(0x680,0x0C,&rx_data); +SpiPs_write_read1(0x681,0x30,&rx_data); +SpiPs_write_read1(0x682,0x0C,&rx_data); +SpiPs_write_read1(0x690,0x0B,&rx_data); +SpiPs_write_read1(0x683,0x26,&rx_data); +SpiPs_write_read1(0x67D,0x00,&rx_data); +SpiPs_write_read1(0x67D,0x03,&rx_data); +usleep(100000); +SpiPs_write_read1(0x683,0x36,&rx_data); +SpiPs_write_read1(0x000,0x12,&rx_data); +SpiPs_write_read1(0x77E,0x50,&rx_data); +SpiPs_write_read1(0x11B,0x01,&rx_data); +SpiPs_write_read1(0x11E,0x01,&rx_data); +SpiPs_write_read1(0x164,0x00,&rx_data); +SpiPs_write_read1(0x165,0x00,&rx_data); +SpiPs_write_read1(0x166,0x00,&rx_data); +SpiPs_write_read1(0x167,0x00,&rx_data); +SpiPs_write_read1(0x168,0x00,&rx_data); +SpiPs_write_read1(0x169,0x00,&rx_data); +SpiPs_write_read1(0x161,0x00,&rx_data); +SpiPs_write_read1(0x162,0x00,&rx_data); +SpiPs_write_read1(0x162,0x01,&rx_data); +SpiPs_write_read1(0x161,0x01,&rx_data); +SpiPs_write_read1(0x706,0x65,&rx_data); +SpiPs_write_read1(0x6C3,0x00,&rx_data); +SpiPs_write_read1(0x75F,0x7D,&rx_data); +SpiPs_write_read1(0x11F,0x73,&rx_data); +SpiPs_write_read1(0x153,0x00,&rx_data); +SpiPs_write_read1(0x708,0x24,&rx_data); +SpiPs_write_read1(0x196,0x01,&rx_data); +SpiPs_write_read1(0x02A,0x12,&rx_data); + +do{ +usleep(100); +SpiPs_write_read2(0x14D,0x00,&rx_data); +temp=rx_data&0x10; +}while (temp != 16); + +SpiPs_write_read1(0x02A,0x00,&rx_data); +SpiPs_write_read1(0x196,0x11,&rx_data); +SpiPs_write_read2(0x13D,0x00,&rx_data); +r13D = rx_data; +SpiPs_write_read2(0x13E,0x00,&rx_data); +r13E = rx_data; + +unsigned long B = r13D * 256 + r13E; +signed long B_s = B - (B >= 32768) * 65536; +unsigned long B_abs = abs(B_s); +unsigned short B_b = (unsigned short)B_abs; +unsigned short B_delay = (B_b>>6) & 0x1ff; + +if(B_s > 0) +{ + SpiPs_write_read1(0x199, ((B_delay>>8)&0x1),&rx_data); + SpiPs_write_read1(0x19A, B_delay&0xff,&rx_data); + SpiPs_write_read1(0x19B, 0,&rx_data); + SpiPs_write_read1(0x19C, 0,&rx_data); +} +else +{ + SpiPs_write_read1(0x199, 0,&rx_data); + SpiPs_write_read1(0x19A, 0,&rx_data); + SpiPs_write_read1(0x19B, ((B_delay>>8)&0x1),&rx_data); + SpiPs_write_read1(0x19C, B_delay&0xff,&rx_data); +} + +SpiPs_write_read1(0x196,0x10,&rx_data); +SpiPs_write_read1(0x153,0x00,&rx_data); +SpiPs_write_read1(0x02A,0x12,&rx_data); + +do{ +usleep(100); +SpiPs_write_read2(0x14D,0x00,&rx_data); +temp=rx_data&0x10; +}while (temp != 16); + +SpiPs_write_read1(0x02A,0x00,&rx_data); +SpiPs_write_read1(0x77E,0x00,&rx_data); +SpiPs_write_read1(0x000,0x02,&rx_data); +//Rx2 QECУ׼ +SpiPs_write_read1(0x02E,0x03,&rx_data); +SpiPs_write_read1(0x03F,0x26,&rx_data); +SpiPs_write_read1(0x040,0x78,&rx_data); +SpiPs_write_read1(0x77D,0x60,&rx_data); +SpiPs_write_read1(0x775,0x24,&rx_data); +SpiPs_write_read1(0x6C3,0x0A,&rx_data); +SpiPs_write_read1(0x61D,0x27,&rx_data); +SpiPs_write_read1(0x617,0x00,&rx_data); +SpiPs_write_read1(0x617,0x03,&rx_data); +usleep(100000); +SpiPs_write_read1(0x61D,0x37,&rx_data); +SpiPs_write_read1(0x686,0x81,&rx_data); +SpiPs_write_read1(0x683,0x36,&rx_data); +SpiPs_write_read1(0xB30,0x00,&rx_data); +SpiPs_write_read1(0xB31,0x87,&rx_data); +SpiPs_write_read1(0xB32,0x04,&rx_data); +SpiPs_write_read1(0xB33,0x44,&rx_data); +SpiPs_write_read1(0xB34,0x44,&rx_data); +SpiPs_write_read1(0x680,0x0C,&rx_data); +SpiPs_write_read1(0x681,0x30,&rx_data); +SpiPs_write_read1(0x682,0x0C,&rx_data); +SpiPs_write_read1(0x690,0x0B,&rx_data); +SpiPs_write_read1(0x683,0x26,&rx_data); +SpiPs_write_read1(0x67D,0x00,&rx_data); +SpiPs_write_read1(0x67D,0x03,&rx_data); +usleep(100000); +SpiPs_write_read1(0x683,0x36,&rx_data); +SpiPs_write_read1(0x000,0x22,&rx_data); +SpiPs_write_read1(0x77E,0x0A,&rx_data); +SpiPs_write_read1(0x11B,0x01,&rx_data); +SpiPs_write_read1(0x11E,0x01,&rx_data); +SpiPs_write_read1(0x164,0x00,&rx_data); +SpiPs_write_read1(0x165,0x00,&rx_data); +SpiPs_write_read1(0x166,0x00,&rx_data); +SpiPs_write_read1(0x167,0x00,&rx_data); +SpiPs_write_read1(0x168,0x00,&rx_data); +SpiPs_write_read1(0x169,0x00,&rx_data); +SpiPs_write_read1(0x161,0x00,&rx_data); +SpiPs_write_read1(0x162,0x00,&rx_data); +SpiPs_write_read1(0x162,0x01,&rx_data); +SpiPs_write_read1(0x161,0x01,&rx_data); +SpiPs_write_read1(0x706,0x65,&rx_data); +SpiPs_write_read1(0x6C3,0x00,&rx_data); +SpiPs_write_read1(0x75F,0x7D,&rx_data); +SpiPs_write_read1(0x11F,0x73,&rx_data); +SpiPs_write_read1(0x153,0x00,&rx_data); +SpiPs_write_read1(0x724,0x24,&rx_data); +SpiPs_write_read1(0x196,0x01,&rx_data); +SpiPs_write_read1(0x02B,0x12,&rx_data); + +do{ +usleep(100); +SpiPs_write_read2(0x14D,0x00,&rx_data); +temp=rx_data&0x10; +}while (temp != 16); + +SpiPs_write_read1(0x02B,0x00,&rx_data); +SpiPs_write_read1(0x196,0x11,&rx_data); +SpiPs_write_read2(0x13D,0x00,&rx_data); +r13D = rx_data; +SpiPs_write_read2(0x13E,0x00,&rx_data); +r13E = rx_data; + +B = r13D * 256 + r13E; +B_s = B - (B >= 32768) * 65536; +B_abs = abs(B_s); +B_b = (unsigned short)B_abs; +B_delay = (B_b>>6) & 0x1ff; + +if(B_s > 0) +{ + SpiPs_write_read1(0x199, ((B_delay>>8)&0x1),&rx_data); + SpiPs_write_read1(0x19A, B_delay&0xff,&rx_data); + SpiPs_write_read1(0x19B, 0,&rx_data); + SpiPs_write_read1(0x19C, 0,&rx_data); +} +else +{ + SpiPs_write_read1(0x199, 0,&rx_data); + SpiPs_write_read1(0x19A, 0,&rx_data); + SpiPs_write_read1(0x19B, ((B_delay>>8)&0x1),&rx_data); + SpiPs_write_read1(0x19C, B_delay&0xff,&rx_data); +} + +SpiPs_write_read1(0x196,0x10,&rx_data); +SpiPs_write_read1(0x153,0x00,&rx_data); +SpiPs_write_read1(0x02B,0x12,&rx_data); + +do{ +usleep(100); +SpiPs_write_read2(0x14D,0x00,&rx_data); +temp=rx_data&0x10; +}while (temp != 16); + +SpiPs_write_read1(0x02B,0x00,&rx_data); +SpiPs_write_read1(0x77E,0x00,&rx_data); +SpiPs_write_read1(0x000,0x02,&rx_data); +//»ָ´У׼Öб»¸ıäµÄÅäÖà +SpiPs_write_read1(0x000,0x02,&rx_data); +SpiPs_write_read1(0x02E,0x03,&rx_data); +SpiPs_write_read1(0x109,0x11,&rx_data); +SpiPs_write_read1(0x10A,0x70,&rx_data); +SpiPs_write_read1(0x11B,0x00,&rx_data); +SpiPs_write_read1(0x11E,0x00,&rx_data); +SpiPs_write_read1(0x11F,0x33,&rx_data); +SpiPs_write_read1(0x128,0x10,&rx_data); +SpiPs_write_read1(0x161,0x00,&rx_data); +SpiPs_write_read1(0x162,0x00,&rx_data); +SpiPs_write_read1(0x164,0x00,&rx_data); +SpiPs_write_read1(0x165,0x00,&rx_data); +SpiPs_write_read1(0x166,0x00,&rx_data); +SpiPs_write_read1(0x167,0x00,&rx_data); +SpiPs_write_read1(0x168,0x00,&rx_data); +SpiPs_write_read1(0x169,0x00,&rx_data); +SpiPs_write_read1(0x6C3,0x00,&rx_data); +SpiPs_write_read1(0x775,0x24,&rx_data); +SpiPs_write_read1(0x77D,0xCF,&rx_data); +SpiPs_write_read1(0xA07,0x00,&rx_data); +SpiPs_write_read1(0xA08,0x00,&rx_data); +SpiPs_write_read1(0xA16,0x00,&rx_data); +SpiPs_write_read1(0xA17,0x00,&rx_data); +SpiPs_write_read1(0xA18,0x00,&rx_data); +SpiPs_write_read1(0xA19,0x00,&rx_data); +SpiPs_write_read1(0xA1A,0x00,&rx_data); +SpiPs_write_read1(0xA1B,0x00,&rx_data); +SpiPs_write_read1(0x686,0x91,&rx_data); +SpiPs_write_read1(0x683,0x36,&rx_data); +SpiPs_write_read1(0xB30,0x00,&rx_data); +SpiPs_write_read1(0xB31,0xDA,&rx_data); +SpiPs_write_read1(0xB32,0xC0,&rx_data); +SpiPs_write_read1(0xB33,0x00,&rx_data); +SpiPs_write_read1(0xB34,0x00,&rx_data); +SpiPs_write_read1(0x680,0x0C,&rx_data); +SpiPs_write_read1(0x681,0x30,&rx_data); +SpiPs_write_read1(0x682,0x08,&rx_data); +SpiPs_write_read1(0x690,0x0B,&rx_data); +//TXУ׼ +SpiPs_write_read1(0x03F,0x27,&rx_data); +SpiPs_write_read1(0x040,0xF8,&rx_data); +SpiPs_write_read1(0x61D,0x27,&rx_data); +SpiPs_write_read1(0x617,0x00,&rx_data); +SpiPs_write_read1(0x617,0x03,&rx_data); +usleep(100000); +SpiPs_write_read1(0x61D,0x37,&rx_data); +SpiPs_write_read1(0x02E,0x03,&rx_data); +SpiPs_write_read1(0x128,0x11,&rx_data); +SpiPs_write_read1(0x11E,0x01,&rx_data); +SpiPs_write_read1(0x77E,0x24,&rx_data); +SpiPs_write_read1(0x11F,0x01,&rx_data); +SpiPs_write_read1(0x121,0x02,&rx_data); +SpiPs_write_read1(0x11B,0x01,&rx_data); +SpiPs_write_read1(0x161,0x01,&rx_data); +SpiPs_write_read1(0x162,0x01,&rx_data); +SpiPs_write_read1(0x163,0x00,&rx_data); +SpiPs_write_read1(0x164,0x04,&rx_data); +SpiPs_write_read1(0x165,0x00,&rx_data); +SpiPs_write_read1(0x166,0x00,&rx_data); +SpiPs_write_read1(0x167,0x00,&rx_data); +SpiPs_write_read1(0x168,0x00,&rx_data); +SpiPs_write_read1(0x169,0x00,&rx_data); +SpiPs_write_read1(0xA2D,0x02,&rx_data); +SpiPs_write_read1(0xA07,0x00,&rx_data); +SpiPs_write_read1(0xA08,0x0F,&rx_data); +SpiPs_write_read1(0xA1C,0x01,&rx_data); +SpiPs_write_read1(0xA16,0x20,&rx_data); +SpiPs_write_read1(0xA17,0x00,&rx_data); +SpiPs_write_read1(0xA18,0x00,&rx_data); +SpiPs_write_read1(0xA19,0x00,&rx_data); +SpiPs_write_read1(0xA1A,0x00,&rx_data); +SpiPs_write_read1(0xA1B,0x00,&rx_data); +SpiPs_write_read1(0x102,0x0F,&rx_data); +SpiPs_write_read1(0x102,0xFF,&rx_data); +SpiPs_write_read1(0x186,0x00,&rx_data); +SpiPs_write_read1(0x186,0x01,&rx_data); +SpiPs_write_read1(0x763,0x34,&rx_data); +SpiPs_write_read1(0x76D,0x34,&rx_data); +SpiPs_write_read1(0x101,0xEF,&rx_data); +SpiPs_write_read1(0x101,0xFF,&rx_data); +SpiPs_write_read1(0x800,0x00,&rx_data); +SpiPs_write_read1(0x81A,0xF7,&rx_data); +SpiPs_write_read1(0x81B,0x34,&rx_data); +SpiPs_write_read1(0x81C,0x33,&rx_data); +SpiPs_write_read1(0x81D,0x12,&rx_data); +SpiPs_write_read1(0x81E,0x00,&rx_data); +SpiPs_write_read1(0x81F,0xFF,&rx_data); +SpiPs_write_read1(0x820,0x00,&rx_data); +SpiPs_write_read1(0x821,0xFF,&rx_data); +SpiPs_write_read1(0x800,0x80,&rx_data); +usleep(100000); +SpiPs_write_read1(0x800,0x81,&rx_data); +usleep(100000); +SpiPs_write_read1(0x77E,0x00,&rx_data); +SpiPs_write_read1(0x77E,0x24,&rx_data); +SpiPs_write_read1(0x11F,0x03,&rx_data); +SpiPs_write_read1(0x121,0x02,&rx_data); +SpiPs_write_read1(0x11B,0x01,&rx_data); +SpiPs_write_read1(0x11E,0x01,&rx_data); +SpiPs_write_read1(0x161,0x01,&rx_data); +SpiPs_write_read1(0x162,0x01,&rx_data); +SpiPs_write_read1(0x164,0x04,&rx_data); +SpiPs_write_read1(0x165,0x00,&rx_data); +SpiPs_write_read1(0x166,0x00,&rx_data); +SpiPs_write_read1(0x167,0x00,&rx_data); +SpiPs_write_read1(0x168,0x00,&rx_data); +SpiPs_write_read1(0x169,0x00,&rx_data); +SpiPs_write_read1(0xA2D,0x02,&rx_data); +SpiPs_write_read1(0xA07,0x00,&rx_data); +SpiPs_write_read1(0xA08,0x0F,&rx_data); +SpiPs_write_read1(0xA1C,0x01,&rx_data); +SpiPs_write_read1(0xA16,0x10,&rx_data); +SpiPs_write_read1(0xA17,0x00,&rx_data); +SpiPs_write_read1(0xA18,0x00,&rx_data); +SpiPs_write_read1(0xA19,0x00,&rx_data); +SpiPs_write_read1(0xA1A,0x00,&rx_data); +SpiPs_write_read1(0xA1B,0x00,&rx_data); +SpiPs_write_read1(0x81A,0xF7,&rx_data); +SpiPs_write_read1(0x81B,0x44,&rx_data); +SpiPs_write_read1(0x81C,0x33,&rx_data); +SpiPs_write_read1(0x81D,0x14,&rx_data); +SpiPs_write_read1(0x800,0x80,&rx_data); +SpiPs_write_read1(0x800,0x82,&rx_data); +usleep(100000); +SpiPs_write_read1(0x77E,0x00,&rx_data); +//»ָ´У׼Öб»¸ıäµÄÅäÖà +SpiPs_write_read1(0x000,0x02,&rx_data); +SpiPs_write_read1(0x02E,0x03,&rx_data); +SpiPs_write_read1(0x109,0x11,&rx_data); +SpiPs_write_read1(0x10A,0x70,&rx_data); +SpiPs_write_read1(0x11B,0x00,&rx_data); +SpiPs_write_read1(0x11E,0x00,&rx_data); +SpiPs_write_read1(0x11F,0x33,&rx_data); +SpiPs_write_read1(0x128,0x10,&rx_data); +SpiPs_write_read1(0x161,0x00,&rx_data); +SpiPs_write_read1(0x162,0x00,&rx_data); +SpiPs_write_read1(0x164,0x00,&rx_data); +SpiPs_write_read1(0x165,0x00,&rx_data); +SpiPs_write_read1(0x166,0x00,&rx_data); +SpiPs_write_read1(0x167,0x00,&rx_data); +SpiPs_write_read1(0x168,0x00,&rx_data); +SpiPs_write_read1(0x169,0x00,&rx_data); +SpiPs_write_read1(0x6C3,0x00,&rx_data); +SpiPs_write_read1(0x775,0x24,&rx_data); +SpiPs_write_read1(0x77D,0xCF,&rx_data); +SpiPs_write_read1(0xA07,0x00,&rx_data); +SpiPs_write_read1(0xA08,0x00,&rx_data); +SpiPs_write_read1(0xA16,0x00,&rx_data); +SpiPs_write_read1(0xA17,0x00,&rx_data); +SpiPs_write_read1(0xA18,0x00,&rx_data); +SpiPs_write_read1(0xA19,0x00,&rx_data); +SpiPs_write_read1(0xA1A,0x00,&rx_data); +SpiPs_write_read1(0xA1B,0x00,&rx_data); +//Pll1Ëø¶¨ +SpiPs_write_read1(0x61D,0x27,&rx_data); +SpiPs_write_read1(0x617,0x00,&rx_data); +SpiPs_write_read1(0x617,0x03,&rx_data); +usleep(100000); +SpiPs_write_read1(0x61D,0x37,&rx_data); +//Pll1Ëø¶¨ ³ɹ¦ +//Pll2Ëø¶¨ +SpiPs_write_read1(0x63F,0x25,&rx_data); +SpiPs_write_read1(0x639,0x00,&rx_data); +SpiPs_write_read1(0x639,0x03,&rx_data); +usleep(100000); +SpiPs_write_read1(0x63F,0x35,&rx_data); +//Pll2Ëø¶¨ ³ɹ¦ +//Pll3Ëø¶¨ +SpiPs_write_read1(0x661,0x26,&rx_data); +SpiPs_write_read1(0x65B,0x00,&rx_data); +SpiPs_write_read1(0x65B,0x03,&rx_data); +usleep(100000); +SpiPs_write_read1(0x661,0x36,&rx_data); +//Pll3Ëø¶¨ ³ɹ¦ +//Pll4Ëø¶¨ +SpiPs_write_read1(0x683,0x26,&rx_data); +SpiPs_write_read1(0x67D,0x00,&rx_data); +SpiPs_write_read1(0x67D,0x03,&rx_data); +usleep(100000); +SpiPs_write_read1(0x683,0x36,&rx_data); +//Pll4Ëø¶¨ ³ɹ¦ +//Pll5Ëø¶¨ +SpiPs_write_read1(0x6A5,0x26,&rx_data); +SpiPs_write_read1(0x69F,0x00,&rx_data); +SpiPs_write_read1(0x69F,0x03,&rx_data); +usleep(100000); +SpiPs_write_read1(0x6A5,0x36,&rx_data); +//Pll5Ëø¶¨ ³ɹ¦ +//ÅäÖýӿÚ +SpiPs_write_read1(0x02E,0x05,&rx_data); +SpiPs_write_read1(0x003,0x33,&rx_data); +SpiPs_write_read1(0x200,0x01,&rx_data); +SpiPs_write_read1(0x201,0x3F,&rx_data); +SpiPs_write_read1(0x202,0x03,&rx_data); +SpiPs_write_read1(0x206,0x00,&rx_data); +SpiPs_write_read1(0x208,0x00,&rx_data); +SpiPs_write_read1(0x209,0x00,&rx_data); +SpiPs_write_read1(0x77F,0x00,&rx_data); +SpiPs_write_read1(0x780,0x33,&rx_data); +SpiPs_write_read1(0x781,0x6F,&rx_data); +//ÅäÖýáÊø + +SpiPs_write_read1(0x02E,0x04,&rx_data); +SpiPs_write_read1(0x780,0x00,&rx_data); + +SpiPs_write_read1(0x11F,0x00,&rx_data); +SpiPs_write_read1(0x11F,0x33,&rx_data); +////ÅäÖÃTx NCO BIST ON +//SpiPs_write_read1(0x11B,0x01,&rx_data); +//SpiPs_write_read1(0x11E,0x01,&rx_data); +//SpiPs_write_read1(0x161,0x01,&rx_data); +//SpiPs_write_read1(0x162,0x01,&rx_data); +////ÅäÖÃTx NCOƵÂÊ 1MHz +//SpiPs_write_read1(0x164,0x05,&rx_data); +//SpiPs_write_read1(0x165,0x55,&rx_data); +//SpiPs_write_read1(0x166,0x55,&rx_data); +//SpiPs_write_read1(0x167,0x55,&rx_data); +//SpiPs_write_read1(0x168,0x55,&rx_data); +//SpiPs_write_read1(0x169,0x55,&rx_data); +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/download_bin.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/download_bin.c new file mode 100644 index 0000000..53d5e8f --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/download_bin.c @@ -0,0 +1,45 @@ +/* + * download_bin.c + * + * Created on: 2023Äê11ÔÂ23ÈÕ + * Author: cxlab + */ + +#include "xilinx_io.h" +#include "sleep.h" +void download_bin(void){ + +// int temp; + u8 rx_data; + u8 data[4]; + u32 buf; + u8 addr[2]; + int on = 1; + if (on) { + SpiPs_write_read1(0x071,0x26,&rx_data); + SpiPs_write_read1(0x236,0x0F,&rx_data); + SpiPs_write_read1(0x20A,0x01,&rx_data); + for(int i=0;i<15448;i++){ + addr[0]=(i>>8)&0xff; + addr[1]= i&0xff; + buf=rom_read((u8 *)&addr); + data[0]=buf>>24; + data[1]=(buf>>16)&0xff; + data[2]=(buf>>8)&0xff; + data[3]=buf&0xff; + for(int j=0;j<4;j++){ + SpiPs_write_read1(0x070,data[j],&rx_data); +// SpiPs_write_read2(0x070,0x00,&rx_data); +// temp=rx_data; + } + if(i==15447){ + xil_printf("bin download completely"); + } + + } + } + else { + SpiPs_write_read1(0x071,0x00,&rx_data); + } + +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/echo.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/echo.c new file mode 100644 index 0000000..9a5462d --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/echo.c @@ -0,0 +1,461 @@ +/* + * Copyright (C) 2016 - 2018 Xilinx, Inc. + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + */ + +#include +#include + +#include "lwip/sockets.h" +#include "netif/xadapter.h" +#include "lwipopts.h" +#include "xil_printf.h" +#include "FreeRTOS.h" +#include "task.h" +#include "xilinx_io.h" + +#define THREAD_STACKSIZE 1024 +#define ACK 0x01 +u16_t echo_port = 7; +//#define RECV_BUF_SIZE 65536*16*8 +#define RECV_BUF_SIZE 1024 +//char recv_buf[RECV_BUF_SIZE]; +char recv_buf[1024]; +char recv_buf_temp[3]; +char trans_buf[8192*5]; +struct spi_stack spistack; + + +u16 hop_cnt = 0; + +void print_echo_app_header() +{ + xil_printf("%20s %6d %s\r\n", "echo server", + echo_port, + "$ telnet 7"); + +} + + +enum { + CMD_VER = 0, + CMD_RST = 1, + CMD_SPI_WRI = 2 , + CMD_SPI_READ = 3, + CMD_DATA_CAPTURE0 = 4, + CMD_WAVE_DOWNLOAD = 5, + CMD_PIN_SET = 6, + CMD_DATA_CAPTURE1 = 8, + CMD_DATA_CAPTURE2 = 9, + CMD_STACK_OUT = 10, + CMD_hopping = 11, + CMD_qecbin = 12, + CMD_cx9261a_config = 13, + CMD_cx3e04_config = 14, + CMD_cx3e04_SPI_WRI = 15 , + CMD_cx3e04_SPI_READ = 16, + CMD_IMAGE_UPDATE = 22, +}; +/* thread spawned for each connection */ +void process_echo_request(void *p) +{ + int sd = (int)p; + int status; + int n, nwrote,send_n,total_send; + u8 rx_data; + u32 capture_length; + u8 pin; + u8 pin_value; + char FPGA_SDK_IP[]="FPGA:20240315-SDK:20231122"; + spistack.spi_write_num = 0; + while (1) { + /* read a max of RECV_BUF_SIZE bytes from socket */ + if ((n = read(sd, recv_buf, 5)) < 0) { + xil_printf("%s: error reading from socket %d, closing socket\r\n", __FUNCTION__, sd); + break; + } + + /* break if the recved message = "quit" */ + if (!strncmp(recv_buf, "quit", 4)) + break; + + /* break if client closed connection */ + if (n <= 0) + break; + + else{/* excute cmd if recved message = "cmd"*/ + n=0; + if (!strncmp(recv_buf, "cmd ", 4)){ +// xil_printf("%d %d %d %d.\r\n", recv_buf[0],recv_buf[1],recv_buf[2],recv_buf[4]); + dbg_printf("Rece %d bytes.\r\n",n); + switch(recv_buf[4]){ + case CMD_IMAGE_UPDATE: + { + + u32 offset; + u32 image_size; + recv_buf[0] = ACK; + nwrote = write(sd, recv_buf, 1); + n = read(sd, recv_buf, RECV_BUF_SIZE); + + offset = *(u32 *)recv_buf; + image_size = *(u32 *)(recv_buf + 4); + xil_printf("offset is 0x%x image_size is 0x%x.\n", offset, image_size); + + void *image_ptr = (void *)0x30000000; + u32 LengthBytes; + write(sd, recv_buf, 1); // ask for image data + //// receive flash image to ddr + LengthBytes = 0; + while (LengthBytes < image_size) + { + n = read(sd, image_ptr + LengthBytes, RECV_BUF_SIZE); + // xil_printf("read %d bytes.\n",n); + LengthBytes = LengthBytes + n; + } + + // image data sumcheck + + // program image data into flash + //QspiStore( (u32) image_ptr, offset, image_size); + //xil_printf("flash program done.\n", n); + status = FfsSdWrite(image_ptr, image_size); + write(sd, recv_buf, 1); // send done + xil_printf("sd program done.\n", n); + n = 0; + break; + } + + case CMD_VER: + dbg_printf("Version:FPGA_20240315;SDK_20231122;IP_192.168.1.99.\r\n"); + n=26; + for(int i=0;i<26;i++){ + recv_buf[i] = FPGA_SDK_IP[i]; + } + break; + case CMD_RST: + dbg_printf("Rece cmd excute rst.\r\n"); + GpioSetPinDir(NRST,GPIO_DIR_OUTPUT); + DeviceRst(); + break; + + case CMD_STACK_OUT: + dbg_printf("Rece cmd STACK OUT.\r\n"); + spi_stack_out(&spistack); + n=1; + recv_buf[0] = 0x55; + break; + case CMD_SPI_WRI: + read(sd, &recv_buf[5], 3); + dbg_printf("Rece cmd excute spi write.\r\n"); + dbg_printf("ADDR 0x%x DATA 0x%x.\r\n",recv_buf[5]*256+recv_buf[6]-32768,recv_buf[7]); +// SpiPs_write_read((u8 *)&recv_buf[5],&rx_data); + status = spi_stack_in((u8 *)&recv_buf[5],&spistack); + if(status){//when memory is near full,start spi write + spi_stack_out(&spistack); + } +// n=1; +// recv_buf[0] = rx_data; + break; + case CMD_SPI_READ: + read(sd, &recv_buf[5], 3); + dbg_printf("Rece cmd excute spi read.\r\n"); + dbg_printf("ADDR 0x%x DATA 0x%x.\r\n",recv_buf[5]*256+recv_buf[6],recv_buf[7]); + SpiPs_write_read((u8 *)&recv_buf[5],&rx_data); + n=1; + recv_buf[0] = rx_data; + break; + case CMD_DATA_CAPTURE0: + read(sd, &recv_buf[5], 4); + capture_length = *((u32 *) &recv_buf[5]);//num of sample + dbg_printf("Rece cmd excute %d sample data capture.\r\n",capture_length); + XAxiDmaCapture(xAxiDma0Get(),capture_length); + n = capture_length*8; + total_send = 0; + /* handle request */ + while(n>0){ + if(n>1024) + send_n = 1024; + else + send_n = n; + n= n-send_n; + memcpy(recv_buf,TX_BUFFER_BASE + total_send,send_n); + total_send = total_send + send_n; + if ((nwrote = write(sd, &recv_buf, send_n)) < 0) { + xil_printf("%s: ERROR responding to client echo request. received = %d, written = %d\r\n",__FUNCTION__, n, nwrote); + xil_printf("Closing socket %d\r\n", sd); + break; + } + else{ + if(send_n!=nwrote){ + xil_printf("%d Bytes Send %d.\r\n", send_n,nwrote); + } + } + } + dbg_printf("Send %d bytes.\r\n",n); + break; + case CMD_DATA_CAPTURE1: + read(sd, &recv_buf[5], 4); + capture_length = *((u32 *) &recv_buf[5]);//num of sample + dbg_printf("Rece cmd excute %d sample data capture.\r\n",capture_length); + XAxiDmaCapture(xAxiDma1Get(),capture_length); + n = capture_length*8; + total_send = 0; + /* handle request */ + while(n>0){ + if(n>1024) + send_n = 1024; + else + send_n = n; + n= n-send_n; + + memcpy(recv_buf,TX_BUFFER_BASE + total_send,send_n); + total_send = total_send + send_n; + if ((nwrote = write(sd, &recv_buf, send_n)) < 0) { + xil_printf("%s: ERROR responding to client echo request. received = %d, written = %d\r\n",__FUNCTION__, n, nwrote); + xil_printf("Closing socket %d\r\n", sd); + break; + } + else{ + if(send_n!=nwrote){ + xil_printf("%d Bytes Send %d.\r\n", send_n,nwrote); + } + } + } + dbg_printf("Send %d bytes.\r\n",n); + break; + case CMD_DATA_CAPTURE2: + read(sd, &recv_buf[5], 4); + capture_length = *((u32 *) &recv_buf[5]);//num of sample + dbg_printf("Rece cmd excute %d sample data capture.\r\n",capture_length); + XAxiDmaCapture(xAxiDma2Get(),capture_length); + n = capture_length*8; + total_send = 0; + /* handle request */ + while(n>0){ + if(n>1024) + send_n = 1024; + else + send_n = n; + n= n-send_n; + memcpy(recv_buf,TX_BUFFER_BASE + total_send,send_n); + total_send = total_send + send_n; + if ((nwrote = write(sd, &recv_buf, send_n)) < 0) { + xil_printf("%s: ERROR responding to client echo request. received = %d, written = %d\r\n",__FUNCTION__, n, nwrote); + xil_printf("Closing socket %d\r\n", sd); + break; + } + else{ + if(send_n!=nwrote){ + xil_printf("%d Bytes Send %d.\r\n", send_n,nwrote); + } + } + } + dbg_printf("Send %d bytes.\r\n",n); + break; + case CMD_WAVE_DOWNLOAD: + dbg_printf("Rece cmd excute wave download.\r\n"); + while(n0){ + if(n>1024) + send_n = 1024; + else + send_n = n; + n= n-send_n; + total_send = total_send + send_n; + memcpy(recv_buf,TX_BUFFER_BASE + total_send,send_n); + if ((nwrote = write(sd, &recv_buf, send_n)) < 0) { + xil_printf("%s: ERROR responding to client echo request. received = %d, written = %d\r\n", + __FUNCTION__, n, nwrote); + xil_printf("Closing socket %d\r\n", sd); + break; + } + else{ + if(send_n!=nwrote){ + xil_printf("%d Bytes Send %d.\r\n", send_n,nwrote); + } + } + } + dbg_printf("Send %d bytes.\r\n",n); + }*/ + + } + } + /* handle request */ + if ((nwrote = write(sd, recv_buf, n)) < 0) { + xil_printf("%s: ERROR responding to client echo request. received = %d, written = %d\r\n", + __FUNCTION__, n, nwrote); + xil_printf("Closing socket %d\r\n", sd); + break; + } + else{ + if(n!=nwrote){ + xil_printf("%d Bytes Send %d.\r\n", n,nwrote); + } + } + } + + /* close connection */ + close(sd); + vTaskDelete(NULL); +} + +void echo_application_thread() +{ + int sock, new_sd; + int size; +#if LWIP_IPV6==0 + struct sockaddr_in address, remote; + + memset(&address, 0, sizeof(address)); + + if ((sock = lwip_socket(AF_INET, SOCK_STREAM, 0)) < 0) + return; + + address.sin_family = AF_INET; + address.sin_port = htons(echo_port); + address.sin_addr.s_addr = INADDR_ANY; +#else + struct sockaddr_in6 address, remote; + + memset(&address, 0, sizeof(address)); + + address.sin6_len = sizeof(address); + address.sin6_family = AF_INET6; + address.sin6_port = htons(echo_port); + + memset(&(address.sin6_addr), 0, sizeof(address.sin6_addr)); + + if ((sock = lwip_socket(AF_INET6, SOCK_STREAM, 0)) < 0) + return; +#endif + + if (lwip_bind(sock, (struct sockaddr *)&address, sizeof (address)) < 0) + return; + + lwip_listen(sock, 0); + + size = sizeof(remote); + + while (1) { + if ((new_sd = lwip_accept(sock, (struct sockaddr *)&remote, (socklen_t *)&size)) > 0) { + sys_thread_new("echos", process_echo_request, + (void*)new_sd, + THREAD_STACKSIZE, + DEFAULT_THREAD_PRIO); + } + } +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/frqhop_config.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/frqhop_config.c new file mode 100644 index 0000000..2f18e03 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/frqhop_config.c @@ -0,0 +1,58 @@ +/* + * frqhop_config.c + * + * Created on: 2023Äê11ÔÂ24ÈÕ + * Author: cxlab + */ + +#include "xilinx_io.h" +#include "sleep.h" +#include "time.h" +#include "xtime_l.h" +#include "math.h" +#include + +void frqhop_config(int hop_flag){ + XTime tCur; + XTime_GetTime(&tCur); + srand(tCur); + double hop_freq = (rand()%(800-30))+30+(rand()%(101))/(double)100;//Ëæ»ú²úÉú[30,800)doubleÀàÐ͵ÄÊý +// double hop_freq = 400.52; + double hop_freq1 = 800.25; + static int pll_index=1; + static int unlock_pll1_cnt=0,unlock_pll2_cnt=0; + if(hop_freq == hop_freq1){ + if(pll_index == 2){ + pll_index = 4; + } + else if(pll_index == 1){ + pll_index = 3; + } + } + if(hop_flag == 1){ + if(pll_index == 1 || pll_index == 3){ + //Çл»ÖÁpll2À´Դ + GpioSetPinDir(hop,GPIO_DIR_OUTPUT); + GpioWritePin(hop,0x01); + trx_ch_pll_config(2); + GpioWritePin(hop,0x00); + //ÅäÖÃpll1²ÎÊý + unlock_pll1_cnt=PLL_config(1,hop_freq)-1; + pll_index = 2; + } + else if(pll_index == 2 || pll_index == 4){ + //Çл»ÖÁpll1À´Դ + GpioSetPinDir(hop,GPIO_DIR_OUTPUT); + GpioWritePin(hop,0x01); + trx_ch_pll_config(1); + GpioWritePin(hop,0x00); + //ÅäÖÃpll2²ÎÊý + unlock_pll2_cnt=PLL_config(2,hop_freq)-1; + pll_index = 1; + } + } + hop_freq1 = hop_freq; + if(unlock_pll2_cnt!=0 || unlock_pll1_cnt !=0){ + xil_printf("pll1 unlock num: %d; pll2 unlock num: %d;.\n", unlock_pll1_cnt, unlock_pll2_cnt); + } +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/iic_phyreset.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/iic_phyreset.c new file mode 100644 index 0000000..9d13181 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/iic_phyreset.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2016 - 2018 Xilinx, Inc. + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + */ + +#include + +#include "xparameters.h" +#if defined (__arm__) || defined(__aarch64__) +#include "xil_printf.h" +#endif + +#ifdef XPS_BOARD_ZCU102 +#ifdef XPAR_XIICPS_0_DEVICE_ID +#include "xiicps.h" + +#define BUF_LEN 10U + +#define IOEXPANDER1_ADDR 0x20U + +#define IIC_SCLK_RATE_IOEXP 400000 + +#define CMD_CFG_0_REG 0x06U +#define CMD_OUTPUT_0_REG 0x02U +#define DATA_OUTPUT 0x0U + +#define DATA_COMMON_CFG 0xE0U +#define DATA_GT_0000_CFG 0x00U + +XIicPs I2c0InstancePtr; + +int IicPhyReset(void) +{ + + u8 WriteBuffer[BUF_LEN] = {0}; + XIicPs_Config *I2c0CfgPtr; + int Status = XST_SUCCESS; + + /* Initialize the IIC0 driver so that it is ready to use */ + I2c0CfgPtr = XIicPs_LookupConfig(XPAR_XIICPS_0_DEVICE_ID); + if (I2c0CfgPtr == NULL) { + Status = XST_FAILURE; + return Status; + } + + Status = XIicPs_CfgInitialize(&I2c0InstancePtr, I2c0CfgPtr, + I2c0CfgPtr->BaseAddress); + if (Status != XST_SUCCESS) { + Status = XST_FAILURE; + return Status; + } + + /* Set the IIC serial clock rate */ + XIicPs_SetSClk(&I2c0InstancePtr, IIC_SCLK_RATE_IOEXP); + + /* Configure I/O pins as Output */ + WriteBuffer[0] = CMD_CFG_0_REG; + WriteBuffer[1] = DATA_OUTPUT; + Status = XIicPs_MasterSendPolled(&I2c0InstancePtr, + WriteBuffer, 2, IOEXPANDER1_ADDR); + if (Status != XST_SUCCESS) { + Status = XST_FAILURE; + return Status; + } + + /* Wait until bus is idle to start another transfer */ + while (XIicPs_BusIsBusy(&I2c0InstancePtr)); + + /* + * Deasserting I2C_MUX_RESETB + * And GEM3 Resetb + * Selecting lanes based on configuration + */ + WriteBuffer[0] = CMD_OUTPUT_0_REG; + /* gt0000 or no GT configuration */ + WriteBuffer[1] = DATA_COMMON_CFG | DATA_GT_0000_CFG; + + /* Send the Data */ + Status = XIicPs_MasterSendPolled(&I2c0InstancePtr, + WriteBuffer, 2, IOEXPANDER1_ADDR); + if (Status != XST_SUCCESS) { + Status = XST_FAILURE; + return Status; + } + + /* Wait until bus is idle */ + while (XIicPs_BusIsBusy(&I2c0InstancePtr)); + + xil_printf("IIC PHY reset on ZCU102 successful \n\r"); +} +#endif +#endif diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/main.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/main.c new file mode 100644 index 0000000..4b61222 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/main.c @@ -0,0 +1,300 @@ +/* + * Copyright (C) 2016 - 2018 Xilinx, Inc. + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + */ + +#include +#include "xparameters.h" +#include "netif/xadapter.h" +#include "platform_config.h" +#include "xil_printf.h" +#include "xilinx_io.h" +#include "FreeRTOS.h" +#include "queue.h" +#include "semphr.h" +#include "math.h" + +#if LWIP_IPV6==1 +#include "lwip/ip.h" +#else +#if LWIP_DHCP==1 +#include "lwip/dhcp.h" +#endif +#endif + +#ifdef XPS_BOARD_ZCU102 +#ifdef XPAR_XIICPS_0_DEVICE_ID +int IicPhyReset(void); +#endif +#endif + +int main_thread(); +void print_echo_app_header(); +void echo_application_thread(void *); + +void lwip_init(); + +#if LWIP_IPV6==0 +#if LWIP_DHCP==1 +extern volatile int dhcp_timoutcntr; +err_t dhcp_start(struct netif *netif); +#endif +#endif + +#define THREAD_STACKSIZE 1024 + +static struct netif server_netif; +struct netif *echo_netif; + +#if LWIP_IPV6==1 +void print_ip6(char *msg, ip_addr_t *ip) +{ + print(msg); + xil_printf(" %x:%x:%x:%x:%x:%x:%x:%x\n\r", + IP6_ADDR_BLOCK1(&ip->u_addr.ip6), + IP6_ADDR_BLOCK2(&ip->u_addr.ip6), + IP6_ADDR_BLOCK3(&ip->u_addr.ip6), + IP6_ADDR_BLOCK4(&ip->u_addr.ip6), + IP6_ADDR_BLOCK5(&ip->u_addr.ip6), + IP6_ADDR_BLOCK6(&ip->u_addr.ip6), + IP6_ADDR_BLOCK7(&ip->u_addr.ip6), + IP6_ADDR_BLOCK8(&ip->u_addr.ip6)); +} + +#else +void +print_ip(char *msg, ip_addr_t *ip) +{ + xil_printf(msg); + xil_printf("%d.%d.%d.%d\n\r", ip4_addr1(ip), ip4_addr2(ip), + ip4_addr3(ip), ip4_addr4(ip)); +} + +void +print_ip_settings(ip_addr_t *ip, ip_addr_t *mask, ip_addr_t *gw) +{ + + print_ip("Board IP: ", ip); + print_ip("Netmask : ", mask); + print_ip("Gateway : ", gw); +} + +#endif + +int main() +{ + xil_printf("start"); + + sys_thread_new("main_thrd", (void(*)(void*))main_thread, 0, + THREAD_STACKSIZE, + DEFAULT_THREAD_PRIO); + vTaskStartScheduler(); + + while(1); + + return 0; +} + +void network_thread(void *p) +{ + struct netif *netif; + /* the mac address of the board. this should be unique per board */ + unsigned char mac_ethernet_address[] = { 0x00, 0x0a, 0x35, 0x00, 0x01, 0x02 }; +#if LWIP_IPV6==0 + ip_addr_t ipaddr, netmask, gw; +#if LWIP_DHCP==1 + int mscnt = 0; +#endif +#endif + + netif = &server_netif; + + xil_printf("\r\n\r\n"); + xil_printf("-----lwIP Socket Mode CX9261 TEST server Application Version 2022.05.31------\r\n"); + +#if LWIP_IPV6==0 +#if LWIP_DHCP==0 + /* initliaze IP addresses to be used */ + IP4_ADDR(&ipaddr, 192, 168, 1, 99); + IP4_ADDR(&netmask, 255, 255, 255, 0); + IP4_ADDR(&gw, 192, 168, 1, 1); +#endif + + /* print out IP settings of the board */ + +#if LWIP_DHCP==0 + print_ip_settings(&ipaddr, &netmask, &gw); + /* print all application headers */ +#endif + +#if LWIP_DHCP==1 + ipaddr.addr = 0; + gw.addr = 0; + netmask.addr = 0; +#endif +#endif + +#if LWIP_IPV6==0 + /* Add network interface to the netif_list, and set it as default */ + if (!xemac_add(netif, &ipaddr, &netmask, &gw, mac_ethernet_address, PLATFORM_EMAC_BASEADDR)) { + xil_printf("Error adding N/W interface\r\n"); + return; + } +#else + /* Add network interface to the netif_list, and set it as default */ + if (!xemac_add(netif, NULL, NULL, NULL, mac_ethernet_address, PLATFORM_EMAC_BASEADDR)) { + xil_printf("Error adding N/W interface\r\n"); + return; + } + + netif->ip6_autoconfig_enabled = 1; + + netif_create_ip6_linklocal_address(netif, 1); + netif_ip6_addr_set_state(netif, 0, IP6_ADDR_VALID); + + print_ip6("\n\rBoard IPv6 address ", &netif->ip6_addr[0].u_addr.ip6); +#endif + + netif_set_default(netif); + + /* specify that the network if is up */ + netif_set_up(netif); + + /* start packet receive thread - required for lwIP operation */ + sys_thread_new("xemacif_input_thread", (void(*)(void*))xemacif_input_thread, netif, + THREAD_STACKSIZE, + DEFAULT_THREAD_PRIO); + +#if LWIP_IPV6==0 +#if LWIP_DHCP==1 + dhcp_start(netif); + while (1) { + vTaskDelay(DHCP_FINE_TIMER_MSECS / portTICK_RATE_MS); + dhcp_fine_tmr(); + mscnt += DHCP_FINE_TIMER_MSECS; + if (mscnt >= DHCP_COARSE_TIMER_SECS*1000) { + dhcp_coarse_tmr(); + mscnt = 0; + } + } +#else + xil_printf("\r\n"); + xil_printf("%20s %6s %s\r\n", "Server", "Port", "Connect With.."); + xil_printf("%20s %6s %s\r\n", "--------------------", "------", "--------------------"); + + print_echo_app_header(); + xil_printf("\r\n"); + sys_thread_new("echod", echo_application_thread, 0, + THREAD_STACKSIZE, + DEFAULT_THREAD_PRIO); + vTaskDelete(NULL); +#endif +#else + print_echo_app_header(); + xil_printf("\r\n"); + sys_thread_new("echod",echo_application_thread, 0, + THREAD_STACKSIZE, + DEFAULT_THREAD_PRIO); + vTaskDelete(NULL); +#endif + return; +} + +int main_thread() +{ +#if LWIP_DHCP==1 + int mscnt = 0; +#endif + +#ifdef XPS_BOARD_ZCU102 + IicPhyReset(); +#endif + + /* initialize lwIP before calling sys_thread_new */ + lwip_init(); + SpiPsInit(SPI_DEVICE_ID); + GpioInit(GPIO_DEVICE_ID); + AXIGpioInit(); + XScuGicInit(); + ConfigAXIGPIOIntr(); + ExceptionInit(); + + XAxiDma_Init(xAxiDma0Get(),AXI_DMA_DEVCIE_ID0); + XAxiDma_Init(xAxiDma1Get(),AXI_DMA_DEVCIE_ID1); + XAxiDma_Init(xAxiDma2Get(),AXI_DMA_DEVCIE_ID2); + PackerRst(); + + /* cx9261a configuration */ + peripheral_test(); + cx3e04_config(); +// cx9261a_710_config(); +// frqhop_config(1); + int temp = cx3e04_read(0x03A); + /* any thread using lwIP should be created using sys_thread_new */ + sys_thread_new("NW_THRD", network_thread, NULL, + THREAD_STACKSIZE, + DEFAULT_THREAD_PRIO); + +#if LWIP_IPV6==0 +#if LWIP_DHCP==1 + while (1) { + vTaskDelay(DHCP_FINE_TIMER_MSECS / portTICK_RATE_MS); + if (server_netif.ip_addr.addr) { + xil_printf("DHCP request success\r\n"); + print_ip_settings(&(server_netif.ip_addr), &(server_netif.netmask), &(server_netif.gw)); + print_echo_app_header(); + xil_printf("\r\n"); + sys_thread_new("echod", echo_application_thread, 0, + THREAD_STACKSIZE, + DEFAULT_THREAD_PRIO); + break; + } + mscnt += DHCP_FINE_TIMER_MSECS; + if (mscnt >= 10000) { + xil_printf("ERROR: DHCP request timed out\r\n"); + xil_printf("Configuring default IP of 192.168.1.100\r\n"); + IP4_ADDR(&(server_netif.ip_addr), 192, 168, 1, 100); + IP4_ADDR(&(server_netif.netmask), 255, 255, 255, 0); + IP4_ADDR(&(server_netif.gw), 192, 168, 1, 1); + print_ip_settings(&(server_netif.ip_addr), &(server_netif.netmask), &(server_netif.gw)); + /* print all application headers */ + xil_printf("\r\n"); + xil_printf("%20s %6s %s\r\n", "Server", "Port", "Connect With.."); + xil_printf("%20s %6s %s\r\n", "--------------------", "------", "--------------------"); + + print_echo_app_header(); + xil_printf("\r\n"); + sys_thread_new("echod", echo_application_thread, 0, + THREAD_STACKSIZE, + DEFAULT_THREAD_PRIO); + break; + } + } +#endif +#endif + vTaskDelete(NULL); + return 0; +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/platform_config.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/platform_config.h new file mode 100644 index 0000000..b84bf38 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/platform_config.h @@ -0,0 +1,10 @@ +#ifndef __PLATFORM_CONFIG_H_ +#define __PLATFORM_CONFIG_H_ + + +#define PLATFORM_EMAC_BASEADDR XPAR_XEMACPS_0_BASEADDR + +#define PLATFORM_ZYNQ + + +#endif diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/rx_wideband_qec.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/rx_wideband_qec.c new file mode 100644 index 0000000..ec8b2f5 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/rx_wideband_qec.c @@ -0,0 +1,61 @@ +/* + * wideband_qec.c + * + * Created on: 2023Äê11ÔÂ30ÈÕ + * Author: cxlab + */ + +#include "xilinx_io.h" +#include "sleep.h" +#include "math.h" +void rxFFTCalib(int lo, int fs,int ref, int firLength, int chan) { + u8 rx_data; + SpiPs_write_read1(0x000, chan == 1 ? 0x12 : 0x22,&rx_data); + SpiPs_write_read1(0x128, 0x00,&rx_data); + SpiPs_write_read1(0x11B, 0x01,&rx_data); + SpiPs_write_read1(0x11E, 0x01,&rx_data); + configTxNcoFreq(0, fs); + SpiPs_write_read1(0x161, 0x00,&rx_data); + SpiPs_write_read1(0x162, 0x00,&rx_data); + SpiPs_write_read1(0x162, 0x01,&rx_data); + SpiPs_write_read1(0x161, 0x01,&rx_data); + SpiPs_write_read1(0x000, chan == 1 ? 0x12 : chan == 2 ? 0x22 : 0x32,&rx_data); + SpiPs_write_read1(0x102, 0x03,&rx_data); + SpiPs_write_read1(0x102, 0xFF,&rx_data); + SpiPs_write_read1(0x15C, 0x00,&rx_data); + SpiPs_write_read1(0x160, 0x01,&rx_data); + SpiPs_write_read1(0x77E, chan == 1 ? 0x50 : chan == 2 ? 0x0A : 0x03,&rx_data); + SpiPs_write_read1(0x09B, 0x01,&rx_data); // TXPLL4 + SpiPs_write_read1(0x09D, firLength,&rx_data); + SpiPs_write_read1(0x096, ((u32)round(lo) >> 8) & 0xFF,&rx_data); + SpiPs_write_read1(0x097, (u32)round(lo) & 0xFF,&rx_data); + SpiPs_write_read1(0x098, ((u32)round(ref * 10) >> 8) & 0xFF,&rx_data); + SpiPs_write_read1(0x099, (u32)round(ref * 10) & 0xFF,&rx_data); + SpiPs_write_read1(0x09E, ((u32)round(fs * 100) >> 8) & 0xFF,&rx_data); + SpiPs_write_read1(0x09F, (u32)round(fs * 100) & 0xFF,&rx_data); + SpiPs_write_read1(0x09C, chan,&rx_data); + while (1) { + if (SpiPs_write_read2(0x0B3,0x00,&rx_data) == 5) + break; + usleep(10000); + } + SpiPs_write_read1(0x09C, 0x00,&rx_data); + SpiPs_write_read1(0x15C, 0x01,&rx_data); + SpiPs_write_read1(0x77E, 0x00,&rx_data); + + //ͨµÀʱÖÓÀ´Դ + u8 r77D=0xC0; + u8 r775=0x24; + u8 r6C3=0x00; + SpiPs_write_read1(0x77D, r77D,&rx_data); + SpiPs_write_read1(0x775, r775,&rx_data); + SpiPs_write_read1(0x6C3, r6C3,&rx_data); +} + + +void configDcCalib(int dcCalibEnable,int dcCalibLength) { + u8 rx_data; + SpiPs_write_read1(0x109, dcCalibEnable ? 0x11 : 0x00,&rx_data); + SpiPs_write_read1(0x10A, (dcCalibLength) << 4,&rx_data); + SpiPs_write_read1(0x11F, dcCalibEnable ? 0x33 : 0x73,&rx_data); +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/sd.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/sd.c new file mode 100644 index 0000000..111123b --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/sd.c @@ -0,0 +1,178 @@ +#include "sd.h" +/************************** Variable Definitions *****************************/ +static FIL fil; /* File object */ +static FATFS fatfs; +/* + * To test logical drive 0, FileName should be "0:/" or + * "". For logical drive 1, FileName should be "1:/" + */ +static char FileName[32] = "BOOT.bin"; +static char *SD_File; + + +//#define BUFF_SIZE sizeof(image_data) +#define BUFF_SIZE 0x2000000 +#ifdef __ICCARM__ +#pragma data_alignment = 32 +u8 DestinationAddress[10*1024]; +#pragma data_alignment = 32 +u8 SourceAddress[10*1024]; +#else +//u8 DestinationAddress[10*1024] __attribute__ ((aligned(32))); +//u8 SourceAddress[10*1024] __attribute__ ((aligned(32))); + +u8 DestinationAddress[BUFF_SIZE] __attribute__ ((aligned(32))); +u8 SourceAddress[BUFF_SIZE] __attribute__ ((aligned(32))); +#endif + +#define TEST 7 + + + +/*****************************************************************************/ +/** +* +* File system example using SD driver to write to and read from an SD card +* in polled mode. This example creates a new file on an +* SD card (which is previously formatted with FATFS), write data to the file +* and reads the same data back to verify. +* +* @param None +* +* @return XST_SUCCESS if successful, otherwise XST_FAILURE. +* +* @note None +* +******************************************************************************/ +int FfsSdWrite(u8*image_data, u32 FileSize) +{ + int error_flag = 0; + FRESULT Res; + UINT NumBytesRead; + UINT NumBytesWritten; + u32 BuffCnt; + BYTE work[FF_MAX_SS]; +// u32 FileSize = (8*1024); +// u32 FileSize = sizeof(image_data); + /* + * To test logical drive 0, Path should be "0:/" + * For logical drive 1, Path should be "1:/" + */ + TCHAR *Path = "0:/"; + u8 checksum = 0; + + for(BuffCnt = 0; BuffCnt < FileSize; BuffCnt++){ +// SourceAddress[BuffCnt] = TEST + BuffCnt; + SourceAddress[BuffCnt] = image_data[BuffCnt]; + checksum = checksum + image_data[BuffCnt]; + } + xil_printf("checksum is 0x%x.",checksum); + + + /* + * Register volume work area, initialize device + */ + Res = f_mount(&fatfs, Path, 0); + + if (Res != FR_OK) { + return XST_FAILURE; + } + + /* + * Path - Path to logical driver, 0 - FDISK format. + * 0 - Cluster size is automatically determined based on Vol size. + */ + Res = f_mkfs(Path, FM_FAT32, 0, work, sizeof work); + if (Res != FR_OK) { + return XST_FAILURE; + } + + /* + * Open file with required permissions. + * Here - Creating new file with read/write permissions. . + * To open file with write permissions, file system should not + * be in Read Only mode. + */ + SD_File = (char *)FileName; + + Res = f_open(&fil, SD_File, FA_CREATE_ALWAYS | FA_WRITE | FA_READ); + if (Res) { + return XST_FAILURE; + } + + /* + * Pointer to beginning of file . + */ + Res = f_lseek(&fil, 0); + if (Res) { + return XST_FAILURE; + } + + /* + * Write data to file. + */ + Res = f_write(&fil, (const void*)SourceAddress, FileSize, + &NumBytesWritten); + if (Res) { + return XST_FAILURE; + } + /* + * Close file. + */ + Res = f_close(&fil); + if (Res) { + return XST_FAILURE; + } + Res = f_open(&fil, SD_File, FA_READ); + if (Res) { + return XST_FAILURE; + } + /* + * Pointer to beginning of file . + */ + Res = f_lseek(&fil, 0); + if (Res) { + return XST_FAILURE; + } + + /* + * Read data from file. + */ + Res = f_read(&fil, (void*)DestinationAddress, FileSize, + &NumBytesRead); + if (Res) { + return XST_FAILURE; + } + + /* + * Data verification + */ + for(BuffCnt = 0; BuffCnt < FileSize; BuffCnt++){ + if(SourceAddress[BuffCnt] != DestinationAddress[BuffCnt]){ +// return XST_FAILURE; + error_flag = 1; + xil_printf('Data check error.'); + } + } + + /* + * Close file. + */ + Res = f_close(&fil); + if (Res) { + return XST_FAILURE; + } + + if(error_flag){ + return XST_FAILURE; + } + +// Res = f_unmount(Path); +// if (Res) { +// return XST_FAILURE; +// } +// +// sleep(5); + + return XST_SUCCESS; +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/sd.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/sd.h new file mode 100644 index 0000000..91e744c --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/sd.h @@ -0,0 +1,9 @@ +/***************************** Include Files *********************************/ +#include "xparameters.h" /* SDK generated parameters */ +#include "xsdps.h" /* SD device driver */ +#include "xil_printf.h" +#include "ff.h" +#include "xil_cache.h" +#include "xplatform_info.h" +/************************** Function Prototypes ******************************/ +int FfsSdWrite(u8*image_data, u32 FileSize); diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/tx_bisection.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/tx_bisection.c new file mode 100644 index 0000000..fd031b9 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/tx_bisection.c @@ -0,0 +1,74 @@ +/* + * tx_bisection.c + * + * Created on: 2023Äê12ÔÂ1ÈÕ + * Author: cxlab + */ + + +#include "xilinx_io.h" +#include "sleep.h" +#include "math.h" + +void txBisectionQecCalib(int f0, int fs) { + u8 rx_data; + SpiPs_write_read1(0x02E, 0x03,&rx_data); + SpiPs_write_read1(0x128, 0x11,&rx_data); + SpiPs_write_read1(0x11E, 0x01,&rx_data); + SpiPs_write_read1(0x77E, 0x24,&rx_data); + SpiPs_write_read1(0x11F, 0x01,&rx_data); + SpiPs_write_read1(0x121, 0x02,&rx_data); + SpiPs_write_read1(0x11B, 0x01,&rx_data); + SpiPs_write_read1(0x161, 0x01,&rx_data); + SpiPs_write_read1(0x162, 0x01,&rx_data); + SpiPs_write_read1(0x163, 0x00,&rx_data); + configTxNcoFreq(f0, fs * 4); + SpiPs_write_read1(0xA2D, 0x02,&rx_data); + SpiPs_write_read1(0xA07, 0x00,&rx_data); + SpiPs_write_read1(0xA08, 0x0F,&rx_data); + SpiPs_write_read1(0xA1C, 0x01,&rx_data); + configRxNcoFreq(f0 * 2, fs); + SpiPs_write_read1(0x186, 0x01,&rx_data); + SpiPs_write_read1(0x763, 0x34,&rx_data); + SpiPs_write_read1(0x76D, 0x34,&rx_data); + SpiPs_write_read1(0x101, 0xEF,&rx_data); + SpiPs_write_read1(0x101, 0xFF,&rx_data); + SpiPs_write_read1(0x800, 0x00,&rx_data); + SpiPs_write_read1(0x81A, 0xF7,&rx_data); + SpiPs_write_read1(0x81B, 0x34,&rx_data); + SpiPs_write_read1(0x81C, 0x33,&rx_data); + SpiPs_write_read1(0x81D, 0x12,&rx_data); + SpiPs_write_read1(0x81E, 0x00,&rx_data); + SpiPs_write_read1(0x81F, 0xFF,&rx_data); + SpiPs_write_read1(0x820, 0x00,&rx_data); + SpiPs_write_read1(0x821, 0xFF,&rx_data); + SpiPs_write_read1(0x800, 0x80,&rx_data); + SpiPs_write_read1(0x800, 0x81,&rx_data); + usleep(100000); + SpiPs_write_read1(0x77E, 0x00,&rx_data); +} + +void txBisectionDcCalib(int f0, int fs) { + u8 rx_data; + SpiPs_write_read1(0x77E, 0x24,&rx_data); + SpiPs_write_read1(0x11F, 0x03,&rx_data); + SpiPs_write_read1(0x121, 0x02,&rx_data); + SpiPs_write_read1(0x11B, 0x01,&rx_data); + SpiPs_write_read1(0x11E, 0x01,&rx_data); + SpiPs_write_read1(0x161, 0x01,&rx_data); + SpiPs_write_read1(0x162, 0x01,&rx_data); + configTxNcoFreq(f0, fs * 4); + SpiPs_write_read1(0xA2D, 0x02,&rx_data); + SpiPs_write_read1(0xA07, 0x00,&rx_data); + SpiPs_write_read1(0xA08, 0x0F,&rx_data); + SpiPs_write_read1(0xA1C, 0x01,&rx_data); + configRxNcoFreq(f0, fs); + SpiPs_write_read1(0x81A, 0xF7,&rx_data); + SpiPs_write_read1(0x81B, 0x44,&rx_data); + SpiPs_write_read1(0x81C, 0x33,&rx_data); + SpiPs_write_read1(0x81D, 0x14,&rx_data); + SpiPs_write_read1(0x800, 0x80,&rx_data); + SpiPs_write_read1(0x800, 0x82,&rx_data); + usleep(500000); + SpiPs_write_read1(0x77E, 0x00,&rx_data); +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/xilinx_io.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/xilinx_io.c new file mode 100644 index 0000000..ef4637b --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/xilinx_io.c @@ -0,0 +1,861 @@ +/* + * xilinx_io.c + * + * Created on: 2021Äê9ÔÂ6ÈÕ + * Author: cxlab + */ + + +#include "sleep.h" +#include "math.h" +#include "xilinx_io.h" /* SPI device driver */ + +static XSpiPs SpiInstance; +XGpioPs Gpio; /* The driver instance for GPIO Device. */ +XGpio axi_gpio_inst; //PL ¶Ë AXI GPIO Çý¶¯ʵÀý +XScuGic scugic_inst; //ÖжϿØÖÆÆ÷ Çý¶¯ʵÀý +XScuGic_Config * scugic_cfg_ptr; //ÖжϿØÖÆÆ÷ ÅäÖÃÐÅϢ +int hop_flag,hop_intr_flag; + +XAxiDma AxiDma0,AxiDma1,AxiDma2; + +XAxiDma* xAxiDma0Get(void){ + return &AxiDma0; +} + +XAxiDma* xAxiDma1Get(void){ + return &AxiDma1; +} + +XAxiDma* xAxiDma2Get(void){ + return &AxiDma2; +} +void AXIGpioInit(void){ + XGpio_Initialize(&axi_gpio_inst, AXI_GPIO_ID); + //ÅäÖà PL AXI GPIO + XGpio_SetDataDirection(&axi_gpio_inst, 1, 1); //ÉèÖà AXI GPIO ͨµÀ 1 ΪÊäÈë + XGpio_InterruptEnable(&axi_gpio_inst, KEY_MASK); //ʹÄÜͨµÀ 1 ÖжÏ + XGpio_InterruptGlobalEnable(&axi_gpio_inst); //ʹÄÜ AXI GPIO ȫ¾ÖÖжÏ +} + +void ConfigAXIGPIOIntr(void){ + //ÉèÖÃÖжÏÓÅÏȼ¶ºʹ¥·¢ÀàÐÍ(¸ߵçƽ´¥·¢) + XScuGic_SetPriorityTriggerType(&scugic_inst, GPIO_INT_ID, 0xA0, 0x1); + //¹ØÁªÖжÏ ID ºÍÖжϴ¦Àíº¯Êý + XScuGic_Connect(&scugic_inst, GPIO_INT_ID, axi_gpio_handler, &axi_gpio_inst); + //ʹÄÜ AXI GPIO ÖжÏ + XScuGic_Enable(&scugic_inst, GPIO_INT_ID); +} +void axi_gpio_handler(void *CallbackRef){ + XGpio *GpioPtr = (XGpio *)CallbackRef; + print("Interrupt Detected!\n"); + XGpio_InterruptDisable(GpioPtr, KEY_MASK); //¹رÕ AXI GPIO ÖжÏʹÄÜ + frqhop_config(hop_flag); + XGpio_InterruptClear(GpioPtr, KEY_MASK); //Çå³ýÖжÏ + XGpio_InterruptEnable(GpioPtr, KEY_MASK); //ʹÄÜ AXI GPIO ÖжÏ +} +void frqhop_flag(int frqhop_flag){ + u8 rx_data; + hop_flag = frqhop_flag; + GpioSetPinDir(hop,GPIO_DIR_OUTPUT); + GpioWritePin(hop,0x01); + SpiPs_write_read2(0x615,0x00,&rx_data); +} + +void ExceptionInit(void){ + //ÉèÖò¢´ò¿ªÖжÏÒ쳣´¦Àí¹¦ÄÜ + Xil_ExceptionInit(); + Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, &scugic_inst); + Xil_ExceptionEnable(); +} +void XScuGicInit(void){ + scugic_cfg_ptr = XScuGic_LookupConfig(SCUGIC_ID); + XScuGic_CfgInitialize(&scugic_inst, scugic_cfg_ptr, scugic_cfg_ptr->CpuBaseAddress); +} + +int SpiPsInit(u16 SpiDeviceId){ + int Status; + XSpiPs_Config *SpiConfig; + /* + * Initialize the SPI driver so that it's ready to use + */ + SpiConfig = XSpiPs_LookupConfig(SpiDeviceId); + if (NULL == SpiConfig) { + return XST_FAILURE; + } + Status = XSpiPs_CfgInitialize((&SpiInstance), SpiConfig, + SpiConfig->BaseAddress); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + /* + * Perform a self-test to check hardware build + */ + Status = XSpiPs_SelfTest((&SpiInstance)); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + /* + * The SPI device is a slave by default and the clock phase + * have to be set according to its master. In this example, CPOL is set + * to quiescent high and CPHA is set to 1. + */ + + XSpiPs_SetOptions((&SpiInstance), (XSPIPS_CR_CPHA_MASK) | + XSPIPS_MASTER_OPTION | + XSPIPS_FORCE_SSELECT_OPTION); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + XSpiPs_SetClkPrescaler((&SpiInstance), XSPIPS_CLK_PRESCALE_8); + /* + * Set the Rx FIFO Threshold to the Max Data + */ +// XSpiPs_SetRXWatermark((&SpiInstance),MAX_DATA); + /* + * Enable the device. + */ + XSpiPs_Enable((&SpiInstance)); + +return XST_SUCCESS; +} + +int cx3e04_write(int addr , int value) +{ + Xil_Out32(AXI4_LITE_SPI_BASEADDR+addr*4,value); + return 1; +} + +int cx3e04_read(int addr) +{ + return Xil_In32(AXI4_LITE_SPI_BASEADDR+addr*4); +} + +void cx3e04_config(void) +{ + //³Çо¿Ƽ¼CX3E04 + //Common + cx3e04_write(0x000,0x12); + cx3e04_write(0x002,0x38); + cx3e04_write(0x038,0x01); + cx3e04_write(0x003,0x30); + cx3e04_write(0x039,0x00); + //CLK + //clk lvds en + cx3e04_write(0x011,0x7F); + cx3e04_write(0x012,0x34); + cx3e04_write(0x036,0x7F); + cx3e04_write(0x01A,0x4B); + cx3e04_write(0x013,0x04); + cx3e04_write(0x014,0x04); + cx3e04_write(0x004,0x00); + cx3e04_write(0x008,0x00); + cx3e04_write(0x009,0x00); + cx3e04_write(0x015,0x04); + cx3e04_write(0x016,0x04); + cx3e04_write(0x005,0x00); + cx3e04_write(0x00A,0x00); + cx3e04_write(0x00B,0x00); + cx3e04_write(0x017,0x04); + cx3e04_write(0x018,0x04); + cx3e04_write(0x006,0x00); + cx3e04_write(0x00C,0x00); + cx3e04_write(0x00D,0x00); + cx3e04_write(0x019,0x04); + cx3e04_write(0x007,0x00); + cx3e04_write(0x00E,0x00); + cx3e04_write(0x00F,0x00); + cx3e04_write(0x010,0x00); + cx3e04_write(0x029,0x00); + cx3e04_write(0x02A,0x00); + //SYNC + //sync lvds en + cx3e04_write(0x033,0x7F); + cx3e04_write(0x034,0x34); + cx3e04_write(0x035,0x7F); + cx3e04_write(0x032,0x03); + //sync inner div + cx3e04_write(0x03A,0x00); + cx3e04_write(0x03B,0x00); + cx3e04_write(0x03A,0x01); + cx3e04_write(0x03B,0xFF);//512·ÖƵ + cx3e04_write(0x03C,0x21); + cx3e04_write(0x03D,0x80); + cx3e04_write(0x02B,0x04); + cx3e04_write(0x02C,0x04); + cx3e04_write(0x01B,0x00); + cx3e04_write(0x01C,0x00); + cx3e04_write(0x022,0x00); + cx3e04_write(0x023,0x00); + cx3e04_write(0x02D,0x04); + cx3e04_write(0x02E,0x04); + cx3e04_write(0x01D,0x00); + cx3e04_write(0x01E,0x00); + cx3e04_write(0x024,0x00); + cx3e04_write(0x025,0x00); + cx3e04_write(0x02F,0x04); + cx3e04_write(0x030,0x04); + cx3e04_write(0x01F,0x00); + cx3e04_write(0x020,0x00); + cx3e04_write(0x026,0x00); + cx3e04_write(0x027,0x00); + cx3e04_write(0x031,0x04); + cx3e04_write(0x021,0x00); + cx3e04_write(0x028,0x00); + //ƫÖõçÁ÷ + cx3e04_write(0x040,0xFF); + cx3e04_write(0x041,0xFF); + cx3e04_write(0x042,0xFF); + cx3e04_write(0x043,0xF0); + cx3e04_write(0x044,0xFF); + cx3e04_write(0x045,0xFF); + cx3e04_write(0x046,0xFF); + cx3e04_write(0x047,0xF0); + //Others + cx3e04_write(0x037,0x07); +} + +//int SpiPs_write_read(u8 cmd, u16 addr,u8 data,u8 * rx_data){ +int SpiPs_write_read(u8 * data,u8 * rx_data){ + int Status; + + u8 rx_buffer[3]; + + XSpiPs_SetSlaveSelect((&SpiInstance),0x00); + + + Status = XSpiPs_PolledTransfer((&SpiInstance),data,rx_buffer,3); + + *rx_data = rx_buffer[2]; +return 0; +} +int SpiPs_write_read1(u16 addr,u8 data,u8 * rx_data){ + u8 rx_data1; + u16 temp; + u8 rx_buffer[3]; + + temp= addr&0xff00; + rx_buffer[0]= temp/256+128; + rx_buffer[1]= addr&0x00ff; + rx_buffer[2]= data; + + SpiPs_write_read((u8 *)&rx_buffer,&rx_data1); + + *rx_data = rx_data1; +return 0; +} +int SpiPs_write_read2(u16 addr,u8 data,u8 * rx_data){ + u8 rx_data1; + u16 temp; + u8 rx_buffer[3]; + + temp= addr&0xff00; + rx_buffer[0]= temp/256; + rx_buffer[1]= addr&0x00ff; + rx_buffer[2]= data; + + SpiPs_write_read((u8 *)&rx_buffer,&rx_data1); + + *rx_data = rx_data1; +return 0; +} + +int GpioInit(u16 DeviceId) +{ + int Status; + XGpioPs_Config *ConfigPtr; + /* Initialize the GPIO driver. */ + ConfigPtr = XGpioPs_LookupConfig(DeviceId); + Status = XGpioPs_CfgInitialize(&Gpio, ConfigPtr, + ConfigPtr->BaseAddr); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + return XST_SUCCESS; +} + +int GpioSetPinDir(u32 Pin,u32 Direction){ + XGpioPs_SetDirectionPin((&Gpio),GPIO_EMIO_OFFSET + Pin,Direction); + return 0; +} +int GpioWritePin(u32 Pin,u32 data){ + XGpioPs_SetOutputEnablePin(&Gpio, GPIO_EMIO_OFFSET + Pin, 1); + XGpioPs_WritePin((&Gpio),GPIO_EMIO_OFFSET + Pin,data); + return 0; +} + +int GpioReadPin(u32 Pin){ + return XGpioPs_ReadPin((&Gpio),GPIO_EMIO_OFFSET + Pin); +} + +void DeviceRst(void){ + GpioWritePin(NRST,0); + GpioWritePin(NRST,1); +} + +int XAxiDma_Init(XAxiDma* AxiDma,u16 DeviceId){ + + XAxiDma_Config *CfgPtr; + int Status; + /* Initialize the XAxiDma device. + */ + CfgPtr = XAxiDma_LookupConfig(DeviceId); + if (!CfgPtr) { + xil_printf("No config found for %d\r\n", DeviceId); + return XST_FAILURE; + } + + Status = XAxiDma_CfgInitialize(AxiDma, CfgPtr); + if (Status != XST_SUCCESS) { + xil_printf("Initialization failed %d\r\n", Status); + return XST_FAILURE; + } + + if(XAxiDma_HasSg(AxiDma)){ + xil_printf("Device configured as SG mode \r\n"); + return XST_FAILURE; + } + + /* Disable interrupts, we use polling mode + */ + XAxiDma_IntrDisable(AxiDma, XAXIDMA_IRQ_ALL_MASK, + XAXIDMA_DEVICE_TO_DMA); + XAxiDma_IntrDisable(AxiDma, XAXIDMA_IRQ_ALL_MASK, + XAXIDMA_DMA_TO_DEVICE); +return 0; +} + +int XAxiDmaCapture(XAxiDma* AxiDma,u32 data_length){ + int Status; + u8 *TxBufferPtr; + u32 MAX_PKT_LEN; + MAX_PKT_LEN = data_length; + TxBufferPtr = (u8 *)TX_BUFFER_BASE; + Xil_DCacheFlushRange((UINTPTR)TxBufferPtr, MAX_PKT_LEN); + Status = XAxiDma_Busy(AxiDma,XAXIDMA_DEVICE_TO_DMA); + Status = XAxiDma_Busy(AxiDma,XAXIDMA_DMA_TO_DEVICE); + XAxiDma_Reset(AxiDma); + Status = XAxiDma_ResetIsDone(AxiDma); + Status = XAxiDma_Busy(AxiDma,XAXIDMA_DEVICE_TO_DMA); + Status = XAxiDma_Busy(AxiDma,XAXIDMA_DMA_TO_DEVICE); + + Status = XAxiDma_SimpleTransfer(AxiDma,(UINTPTR) TxBufferPtr,MAX_PKT_LEN*8, XAXIDMA_DEVICE_TO_DMA); + SetPacketSize(AxiDma,MAX_PKT_LEN); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + u32 start_TickCount,current_TickCount=0; + start_TickCount = sys_now(); + u32 offset= 0; + //never come out ,reason is stream does't have tlast.Need a module to pack excatly the same length defined in para. + while ((offset< 500)&&(XAxiDma_Busy(AxiDma,XAXIDMA_DEVICE_TO_DMA))) { + Status = XAxiDma_Busy(AxiDma,XAXIDMA_DEVICE_TO_DMA); + + current_TickCount =sys_now(); + offset = current_TickCount-start_TickCount; +// xil_printf("offset: %d\r\n", offset); + } + /* + u64 offset= 0; + //never come out ,reason is stream does't have tlast.Need a module to pack excatly the same length defined in para. + while ((offset> 100000)||(XAxiDma_Busy(AxiDma,XAXIDMA_DEVICE_TO_DMA))) { + Status = XAxiDma_Busy(AxiDma,XAXIDMA_DEVICE_TO_DMA); + }*/ + return 0; +} + + + +int SetPacketSize(XAxiDma* AxiDma,u32 length){ + + UINTPTR Addr; + if(AxiDma == xAxiDma0Get()) + Addr = PACKER_BASEADDR0; + else if(AxiDma == xAxiDma1Get()) + Addr = PACKER_BASEADDR1; + else + Addr = PACKER_BASEADDR2; + xil_printf("addr %x .\n",Addr); + Xil_Out32(Addr,0); + xil_printf("PACKER_BASEADDR DATA %d .\n",Xil_In32(Addr)); + Xil_Out32(Addr+4,length); + xil_printf("PACKER_BASEADDR +4 DATA %d .\n",Xil_In32(Addr + 4)); + Xil_Out32(Addr,1); + xil_printf("PACKER_BASEADDR DATA %d .\n",Xil_In32(Addr)); + return 0; +} +int DownloadWave(u8 * data){ + u32 * buffer; + u64 addr; + int i; + buffer = (u32 *) data; + addr = BRAM_BASEADDR; + for(i=0;irecv_buf[spistack->spi_write_num * 3 + 0] = *(data + 0); + spistack->recv_buf[spistack->spi_write_num * 3 + 1] = *(data + 1); + spistack->recv_buf[spistack->spi_write_num * 3 + 2] = *(data + 2); + spistack->spi_write_num = spistack->spi_write_num + 1; + if(spistack->spi_write_num >= SPI_STACK_LENGTH-1){ + return 1; + } + else + return 0; +} + +void spi_stack_out(struct spi_stack * spistack){ + + int i; + u8 rx_data; + for(i=0;ispi_write_num;i++ ){ + SpiPs_write_read((u8 *)&(spistack->recv_buf[i * 3]),&rx_data); + } + spistack->spi_write_num = 0; +} + +int rom_read(u8 * data) +{ + int addr ; + addr = *(data + 0)*256+ *(data + 1); + return Xil_In32(BROM_BASEADDR+addr*4); +} + +void trx_ch_pll_config(u8 pll_index) +{ + u8 rx_data; + u8 recv_buf[3]={0}; + u32 trx_ch_pll_data; + + if(pll_index==1){ + trx_ch_pll_data = 0x00077DCF; + }else if(pll_index==2){ + trx_ch_pll_data = 0x00077D0C; + } + + recv_buf[0] =(trx_ch_pll_data>>16 & 0xff)|0x80 ; + recv_buf[1] = trx_ch_pll_data>>8 & 0xff; + recv_buf[2] = trx_ch_pll_data & 0xff; + SpiPs_write_read(recv_buf,&rx_data); +} + +u16 pll1_config_addr[] ={0x0620,0x061D,0x0B06,0x0B07,0x0B08,0x0B09,0x0B0A,0x061A,0x061B,0x061C,0x062A}; +u16 pll2_config_addr[] ={0x0642,0x063F,0x0B14,0x0B15,0x0B16,0x0B17,0x0B18,0x063C,0x063D,0x063E,0x064C}; +u16 pll3_config_addr[] ={0x0664,0x0661,0x0B22,0x0B23,0x0B24,0x0B25,0x0B26,0x065E,0x065F,0x0660,0x066E}; +u16 pll4_config_addr[] ={0x0686,0x0683,0x0B30,0x0B31,0x0B32,0x0B33,0x0B34,0x0680,0x0681,0x0682,0x0690}; + +int PLL_config(u8 pll_index,double pll_frq) +{ + + int i = 0,afc_cnt=0; + u16 pll_config_addr[11]; + u16 afc_addr,lock_addr; + double ref=200; + u8 pllreg_buf[11]; + u8 recv_buf[3]; + u8 rx_data,pll_div; + u16 div_addr; + + switch(pll_index){ + case 1:for (i = 0; i < 11; i++){pll_config_addr[i] = pll1_config_addr[i];} afc_addr=0x0617;lock_addr=0x0635;break; + case 2:for (i = 0; i < 11; i++){pll_config_addr[i] = pll2_config_addr[i];} afc_addr=0x0639;lock_addr=0x0657;break; + case 3:for (i = 0; i < 11; i++){pll_config_addr[i] = pll3_config_addr[i];} afc_addr=0x065B;lock_addr=0x0679;break; + case 4:for (i = 0; i < 11; i++){pll_config_addr[i] = pll4_config_addr[i];} afc_addr=0x067D;lock_addr=0x069B;break; + default :; + } + //caculate pll frequence word + pll_frq_caculation_new(ref,pll_frq,(u8 *)&pllreg_buf); + for (i = 0; i < 11; i++) + { + recv_buf[0] = (pll_config_addr[i]>>8 & 0xff)|0x80 ; + recv_buf[1] = pll_config_addr[i] & 0xff; + recv_buf[2] = pllreg_buf[i] & 0xff; + SpiPs_write_read((u8 *)&recv_buf[0],&rx_data); + } + //afc pll + pll_div = pllreg_buf[1]; + div_addr = pll_config_addr[1]; + recv_buf[0] = lock_addr>>8 & 0xff ; + recv_buf[1] = lock_addr & 0xff; + recv_buf[2] = 0x00; + while(rx_data!=48){ + PLL_afc(afc_addr,div_addr,pll_div); + SpiPs_write_read((u8 *)&recv_buf[0],&rx_data); + afc_cnt++; + } + return afc_cnt; + //xil_printf("afc lock count: %d.\r\n",afc_cnt); + +} + +void PLL_afc(u16 afc_addr,u16 div_addr,u8 pll_div){ + + u8 recv_buf[3]; + u8 rx_data; + if(pll_div>=48){ + recv_buf[0] = (div_addr>>8 & 0xff)|0x80 ; + recv_buf[1] = div_addr & 0xff; + recv_buf[2] = pll_div & 0x2f; + SpiPs_write_read((u8 *)&recv_buf[0],&rx_data); + } + recv_buf[0] = (afc_addr>>8 & 0xff)|0x80 ; + recv_buf[1] = afc_addr & 0xff; + recv_buf[2] = 0x00; + SpiPs_write_read((u8 *)&recv_buf[0],&rx_data); + recv_buf[2] = 0x03; + SpiPs_write_read((u8 *)&recv_buf[0],&rx_data); + usleep(30); + if(pll_div>=48){ + recv_buf[0] = (div_addr>>8 & 0xff)|0x80 ; + recv_buf[1] = div_addr & 0xff; + recv_buf[2] = pll_div & 0xff; + SpiPs_write_read((u8 *)&recv_buf[0],&rx_data); + } + +} + +int pll_frq_caculation(double ref,double pll_frq,u8 *reg_data){ + + u8 row,column; + int vco = pll_frq; + int div = 1; + while(vco < 7001){ + vco = vco*2; + div = div*2; + } + char coreTable[7][4][3] = {{{0x0C,0x30,0x08},{0x0B,0x30,0x09},{0x0C,0x30,0x05},{0x0D,0x30,0x08}}, + {{0x0C,0x30,0x0C},{0x0B,0x30,0x0E},{0x0C,0x30,0x08},{0x0D,0x30,0x0B}}, + {{0x0C,0x20,0x10},{0x0B,0x20,0x10},{0x0C,0x20,0x0A},{0x0D,0x20,0x0D}}, + {{0x0C,0x20,0x11},{0x0B,0x20,0x13},{0x0C,0x20,0x0C},{0x0D,0x20,0x0F}}, + {{0x0D,0x30,0x14},{0x0D,0x22,0x14},{0x0D,0x20,0x0D},{0x0D,0x15,0x0C}}, + {{0x0E,0x30,0x18},{0x0E,0x25,0x16},{0x0E,0x25,0x0F},{0x0E,0x18,0x0F}}, + {{0x0F,0x38,0x1A},{0x0F,0x32,0x1C},{0x0F,0x18,0x12},{0x0F,0x18,0x12}}}; + + if (ref >= 28 && ref < 40){ + row = 0; + }else if (ref >= 40 && ref < 60){ + row = 1; + }else if (ref >= 60 && ref < 90){ + row = 2; + }else if (ref >= 90 && ref < 110){ + row = 3; + }else if (ref >= 110 && ref < 140){ + row = 4; + }else if (ref >= 140 && ref < 180){ + row = 5; + }else if (ref >= 180 && ref <= 240){ + row = 6; + } + + if (vco >= 7001 && vco < 8400){ + column = 0; + }else if (vco >= 8400 && vco < 10500){ + column = 1; + }else if (vco >= 10500 && vco < 12000){ + column = 2; + }else if (vco >= 12000 && vco <= 14000){ + column = 3; + } + + char cp = coreTable[row][column][0]; + char c1r3 = coreTable[row][column][1]; + char r1 = coreTable[row][column][2]; + + char dither,nHigh,nLow,fHigh,fMiddle,fLow,outputDiv,core = 0x81; + if(vco > 10500){ + core = core + 0x10; + } + if (div == 2){ + outputDiv = 0+7; + }else if (div == 4){ + outputDiv = 32+7; + }else{ + outputDiv = 48 + divBin(div); + } + + double nf = 1.0*vco / ref; + int n = nf; + double f = nf - n; + if(f > 1e-10){ + dither = 11; + }else{ + dither = 0; + } + if(n >= 256){ + nHigh = 1; + }else{ + nHigh = 0; + } + nLow = n & 0xff; + int f1 = f * pow(2,24); + fHigh = f1>>16 & 0xff; + fMiddle = f1>>8 & 0xff; + fLow = f1 & 0xff; + reg_data[0]=core; + reg_data[1]=outputDiv; + reg_data[2]=nHigh; + reg_data[3]=nLow; + reg_data[4]=fHigh; + reg_data[5]=fMiddle; + reg_data[6]=fLow; + reg_data[7]=cp; + reg_data[8]=c1r3; + reg_data[9]=r1; + reg_data[10]=dither; + return 0; +} +int pll_frq_caculation_new(double ref,double pll_frq,u8 *reg_data){ + + u8 row,column; + double vco = pll_frq; + int div = 1; + while(vco < 6701){ + vco = vco*2; + div = div*2; + } + char coreTable[7][4][3] = {{{0x0C,0x30,0x08},{0x0B,0x30,0x09},{0x0C,0x30,0x05},{0x0D,0x30,0x08}}, + {{0x0C,0x30,0x0C},{0x0B,0x30,0x0E},{0x0C,0x30,0x08},{0x0D,0x30,0x0B}}, + {{0x0C,0x20,0x10},{0x0B,0x20,0x10},{0x0C,0x20,0x0A},{0x0D,0x20,0x0D}}, + {{0x0C,0x20,0x11},{0x0B,0x20,0x13},{0x0C,0x20,0x0C},{0x0D,0x20,0x0F}}, + {{0x0D,0x30,0x14},{0x0D,0x22,0x14},{0x0D,0x20,0x0D},{0x0D,0x15,0x0C}}, + {{0x0E,0x30,0x18},{0x0E,0x25,0x16},{0x0E,0x25,0x0F},{0x0E,0x18,0x0F}}, + {{0x0F,0x38,0x1A},{0x0F,0x32,0x1C},{0x0F,0x18,0x12},{0x0F,0x18,0x12}}}; + + if (ref >= 28 && ref < 40){ + row = 0; + }else if (ref >= 40 && ref < 60){ + row = 1; + }else if (ref >= 60 && ref < 90){ + row = 2; + }else if (ref >= 90 && ref < 110){ + row = 3; + }else if (ref >= 110 && ref < 140){ + row = 4; + }else if (ref >= 140 && ref < 180){ + row = 5; + }else if (ref >= 180 && ref <= 240){ + row = 6; + } + + if (vco < 8400){ + column = 0; + }else if (vco >= 8400 && vco < 10150){ + column = 1; + }else if (vco >= 10150 && vco < 12000){ + column = 2; + }else if (vco >= 12000 ){ + column = 3; + } + + char cp = coreTable[row][column][0]; + char c1r3 = coreTable[row][column][1]; + char r1 = coreTable[row][column][2]; + + char dither,nHigh,nLow,fHigh,fMiddle,fLow,outputDiv,core = 0x81; + if(vco > 10150){ + core = core + 0x10; + } + if (div == 2){ + outputDiv = 0+7; + }else if (div == 4){ + outputDiv = 32+7; + }else{ + outputDiv = 48 + divBin(div); + } + + double nf = 1.0*vco / ref; + int n = nf; + double f = nf - n; + if(f > 1e-10){ + dither = 11; + }else{ + dither = 0; + } + if(n >= 256){ + nHigh = 1; + }else{ + nHigh = 0; + } + nLow = n & 0xff; + int f1 = f * pow(2,24); + fHigh = f1>>16 & 0xff; + fMiddle = f1>>8 & 0xff; + fLow = f1 & 0xff; + reg_data[0]=core; + reg_data[1]=outputDiv; + reg_data[2]=nHigh; + reg_data[3]=nLow; + reg_data[4]=fHigh; + reg_data[5]=fMiddle; + reg_data[6]=fLow; + reg_data[7]=cp; + reg_data[8]=c1r3; + reg_data[9]=r1; + reg_data[10]=dither; + return 0; +} +char divBin(u16 div){ + + switch(div){ + case 8:return 7;break; + case 16:return 6;break; + case 32:return 5;break; + case 64:return 4;break; + case 128:return 3;break; + case 256:return 2;break; + default :return 2; + } + +} + +void configTxNcoFreq(int freq, int fs) { + u8 rx_data; + double ratio = (double) freq < 0 ? (freq + fs) / fs : freq / fs; + double bin = ratio * pow(2,48); + int bin_low = (u32) bin & 0xFFFFFF; + int bin_high = (u32)(bin / pow(2,24)) & 0xFFFFFF; + SpiPs_write_read1(0x164, (bin_high >> 16) & 0xFF,&rx_data); + SpiPs_write_read1(0x165, (bin_high >> 8) & 0xFF,&rx_data); + SpiPs_write_read1(0x166, bin_high & 0xFF,&rx_data); + SpiPs_write_read1(0x167, (bin_low >> 16) & 0xFF,&rx_data); + SpiPs_write_read1(0x168, (bin_low >> 8) & 0xFF,&rx_data); + SpiPs_write_read1(0x169, bin_low & 0xFF,&rx_data); +} +void configRxNcoFreq(int freq, int fs) { + u8 rx_data; + double ratio = (double) freq < 0 ? (freq + fs) / fs : freq / fs; + double bin = ratio * pow(2,48); + int bin_low = (u32) bin & 0xFFFFFF; + int bin_high = (u32)(bin / pow(2,24)) & 0xFFFFFF; + SpiPs_write_read1(0xA16, (bin_high >> 16) & 0xFF,&rx_data); + SpiPs_write_read1(0xA17, (bin_high >> 8) & 0xFF,&rx_data); + SpiPs_write_read1(0xA18, bin_high & 0xFF,&rx_data); + SpiPs_write_read1(0xA19, (bin_low >> 16) & 0xFF,&rx_data); + SpiPs_write_read1(0xA1A, (bin_low >> 8) & 0xFF,&rx_data); + SpiPs_write_read1(0xA1B, bin_low & 0xFF,&rx_data); +} + diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/xilinx_io.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/xilinx_io.h new file mode 100644 index 0000000..2a5b601 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_DEMO/xilinx_io.h @@ -0,0 +1,149 @@ +/* + * xilinx_io.h + * + * Created on: 2021Äê9ÔÂ6ÈÕ + * Author: cxlab + */ + +#ifndef SRC_XILINX_IO_H_ +#define SRC_XILINX_IO_H_ + + + +#endif /* SRC_XILINX_IO_H_ */ +#include "xspips.h" /* SPI device driver */ +#include "xgpiops.h" +#include "xgpio.h" +#include "xaxidma.h" +#include "xparameters.h" +#include "xscugic.h" +#include "xil_exception.h" + +//#define DBGLOG +#define MAX_DATA 100 +#define SPI_WRITE 0x80 +#define SPI_READ 0x00 +#define GPIO_DIR_INPUT 0x00 +#define GPIO_DIR_OUTPUT 0x01 +#define GPIO_EMIO_OFFSET 54 + +#define TX_EN 0x08 +#define RX_EN 0x07 +#define GPIO_1 0x06 +#define GPIO_0 0x05 + +#define NRST 0 +#define ENABLE_TRX1 1 +#define ENABLE_TRX2 2 +#define ENABLE_TRX3 3 +#define TXNRX_TRX1 4 +#define TXNRX_TRX2 5 +#define TXNRX_RX3 6 + +#define aresetn_0 33 +#define rst 34 +#define tr_sel 35 +#define tdd_fdd_sel 36 +#define r1t1_r2t2_sel 37 +#define sp_dp_sel 38 +#define aresetn_1 39 +#define aresetn_2 40 +#define hop 41 +#define cx3e04_rstn 42 +#define debug_mode 63 + +#define INTER_SIG3 0x03//dma_axis_rx_rstn +#define INTER_SIG2 0x02//frame_pattern +#define INTER_SIG1 0x01//mode_sel_async +#define INTER_SIG0 0x00//rst_async + +#define SPI_DEVICE_ID XPAR_XSPIPS_0_DEVICE_ID +#define GPIO_DEVICE_ID XPAR_PS7_GPIO_0_DEVICE_ID + +#define AXI_DMA_DEVCIE_ID0 XPAR_AXI_DMA_0_DEVICE_ID +#define AXI_DMA_DEVCIE_ID1 XPAR_AXI_DMA_1_DEVICE_ID +#define AXI_DMA_DEVCIE_ID2 XPAR_AXI_DMA_2_DEVICE_ID + +#define BROM_BASEADDR XPAR_M08_AXI_0_BASEADDR + +#define AXI4_LITE_SPI_BASEADDR XPAR_AXI4_LITE_SPI_0_BASEADDR +#define BRAM_BASEADDR XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR +#define BRAM_BASEADDR_1 XPAR_AXI_BRAM_CTRL_1_S_AXI_BASEADDR +#define PACKER_BASEADDR0 XPAR_AXIS_ADC_DATA_PACKER_0_S00_AXI_BASEADDR +#define PACKER_BASEADDR1 XPAR_AXIS_ADC_DATA_PACKER_1_S00_AXI_BASEADDR +#define PACKER_BASEADDR2 XPAR_AXIS_ADC_DATA_PACKER_2_S00_AXI_BASEADDR +#define WAVELENGTH 8192 +#define TX_BUFFER_BASE XPAR_MIG_7SERIES_0_BASEADDR +0x2000 +#define WAVE_LENGTH 8192*4 + +#define GPIO_INT_ID XPAR_FABRIC_GPIO_0_VEC_ID //PL ¶Ë AXI GPIO ÖжÏ ID +#define AXI_GPIO_ID XPAR_AXI_GPIO_0_DEVICE_ID //PL ¶Ë AXI GPIO Æ÷¼þ ID +#define SCUGIC_ID XPAR_SCUGIC_0_DEVICE_ID //ÖжϿØÖÆÆ÷ ID + +#define KEY_MASK XGPIO_IR_CH1_MASK //ͨµÀ 1 µÄ붨Òå + +int cx3e04_write(int addr , int value); +int cx3e04_read(int addr); +void cx3e04_config(void); +void cx9261a_710_config(void); + +void AXIGpioInit(void); +void ConfigAXIGPIOIntr(void); +void axi_gpio_handler(void *CallbackRef); +void XScuGicInit(void); +void ExceptionInit(void); + +int SpiPsInit(u16 SpiDeviceId); +//int SpiPs_write_read(u8 cmd, u16 addr,u8 data,u8 * rx_data); +int SpiPs_write_read(u8 * data,u8 * rx_data); +int SpiPs_write_read1(u16 addr,u8 data,u8 * rx_data); +int SpiPs_write_read2(u16 addr,u8 data,u8 * rx_data); +int GpioInit(u16 DeviceId); +int GpioSetPinDir(u32 Pin,u32 Direction); +int GpioWritePin(u32 Pin,u32 data); +int GpioReadPin(u32 Pin); +void DeviceRst(void); + +//У׼ +void cx9261a_151_config(void); +void cx9261a_076_config(void); +void rxFFTCalib(int lo, int fs,int ref, int firLength,int chan); +void configTxNcoFreq(int freq, int fs); +void configRxNcoFreq(int freq, int fs); +void configDcCalib(int dcCalibEnable,int dcCalibLength); +void txBisectionQecCalib(int f0, int fs); +void txBisectionDcCalib(int f0, int fs); + +extern XAxiDma* xAxiDma0Get(void); +extern XAxiDma* xAxiDma1Get(void); +extern XAxiDma* xAxiDma2Get(void); +int XAxiDma_Init(XAxiDma* AxiDma,u16 DeviceId); +int XAxiDmaCapture(XAxiDma* AxiDma,u32 data_length); +int XAxiDmaCapture2(XAxiDma* AxiDma,u32 data_length); + +int DownloadWave(u8 * data); +int SetPacketSize(XAxiDma* AxiDma,u32 length); +void dbg_printf( const char8 *ctrl1, ...); +void PackerRst(void); +int peripheral_test(void); + +#define SPI_STACK_LENGTH 65536 +struct spi_stack { + int spi_write_num; + u8 recv_buf[SPI_STACK_LENGTH*3]; +}; +int spi_stack_in(u8 * data,struct spi_stack * spistack); + +void spi_stack_out(struct spi_stack * spistack); + +int rom_read(u8 * data); +void download_bin(void); +//ÌøƵ +void frqhop_config(int hop_flag); +void frqhop_flag(int frqhop_flag); +int PLL_config(u8 pll_index,double pll_frq); +void trx_ch_pll_config(u8 pll_index); +int pll_frq_caculation(double ref,double pll_frq,u8 *reg_data); +int pll_frq_caculation_new(double ref,double pll_frq,u8 *reg_data); +void PLL_afc(u16 afc_addr,u16 div_addr,u8 pll_div); +char divBin(u16 div); diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/cx9261a_076_config.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/cx9261a_076_config.c new file mode 100644 index 0000000..64c1e10 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/cx9261a_076_config.c @@ -0,0 +1,342 @@ +/* + * cx9261a_076_config.c + * + * Created on: 2023Äê11ÔÂ30ÈÕ + * Author: cxlab + */ + + +#include "xilinx_io.h" +#include "sleep.h" +void cx9261a_076_config(void){ +u8 rx_data; +int temp; + +//setPin(pin,value) +GpioSetPinDir(0,0x01); +GpioWritePin(0,1); +GpioSetPinDir(32,0x01); +GpioWritePin(32,1); +GpioSetPinDir(33,0x01); +GpioWritePin(33,1); +GpioSetPinDir(34,0x01); +GpioWritePin(34,0); +GpioSetPinDir(35,0x01); //tx:0-rx:1 +GpioWritePin(35,1); //tx:0-rx:1 +GpioSetPinDir(36,0x01); //fdd:0-tdd:1 +GpioWritePin(36,1); //fdd:0-tdd:1 +GpioSetPinDir(37,0x01); //1r1t:0-2r2t:1 +GpioWritePin(37,0); //1r1t:0-2r2t:1 +GpioSetPinDir(38,0x01); //dualport:0-singleport:1 +GpioWritePin(38,1); //dualport:0-singleport:1 +GpioSetPinDir(39,0x01); +GpioWritePin(39,1); +GpioSetPinDir(40,0x01); +GpioWritePin(40,1); +//Èý¸öͨµÀ¹²ͬ¿ØÖÆ +SpiPs_write_read1(0x000,0x02,&rx_data); +//µçԴ¹ÜÀí×Զ¨Òå +//¶ϵçģʽ +SpiPs_write_read1(0x031,0x00,&rx_data); +SpiPs_write_read1(0x032,0x7F,&rx_data); +SpiPs_write_read1(0x033,0xFF,&rx_data); +SpiPs_write_read1(0x034,0xFF,&rx_data); +//ÐÝÃßģʽ +SpiPs_write_read1(0x035,0x00,&rx_data); +SpiPs_write_read1(0x036,0x3D,&rx_data); +SpiPs_write_read1(0x037,0xFF,&rx_data); +SpiPs_write_read1(0x038,0xEB,&rx_data); +//¾¯½äģʽ +SpiPs_write_read1(0x039,0x00,&rx_data); +SpiPs_write_read1(0x03A,0x3D,&rx_data); +SpiPs_write_read1(0x03B,0xFF,&rx_data); +SpiPs_write_read1(0x03C,0xE0,&rx_data); +//У׼ģʽ +SpiPs_write_read1(0x03D,0x1B,&rx_data); +SpiPs_write_read1(0x03E,0x39,&rx_data); +SpiPs_write_read1(0x03F,0x27,&rx_data); +SpiPs_write_read1(0x040,0xE0,&rx_data); +//½ÓÊÕģʽ +SpiPs_write_read1(0x041,0x03,&rx_data); +SpiPs_write_read1(0x042,0x3D,&rx_data); +SpiPs_write_read1(0x043,0xE7,&rx_data); +SpiPs_write_read1(0x044,0xF8,&rx_data); +//·¢Éäģʽ +SpiPs_write_read1(0x045,0x1B,&rx_data); +SpiPs_write_read1(0x046,0x3D,&rx_data); +SpiPs_write_read1(0x047,0x26,&rx_data); +SpiPs_write_read1(0x048,0x7E,&rx_data); +//FDDģʽ +SpiPs_write_read1(0x049,0x1B,&rx_data); +SpiPs_write_read1(0x04A,0x3D,&rx_data); +SpiPs_write_read1(0x04B,0x27,&rx_data); +SpiPs_write_read1(0x04C,0xE0,&rx_data); +//¹۲âģʽ +SpiPs_write_read1(0x04D,0x04,&rx_data); +SpiPs_write_read1(0x04E,0x3D,&rx_data); +SpiPs_write_read1(0x04F,0xD9,&rx_data); +SpiPs_write_read1(0x050,0xFE,&rx_data); +//´ø϶»ù׼ +SpiPs_write_read1(0x773,0x53,&rx_data); +SpiPs_write_read1(0x774,0x86,&rx_data); +//²ο¼ʱÖÓÉèÖà +SpiPs_write_read1(0x789,0x00,&rx_data); +SpiPs_write_read1(0x785,0x49,&rx_data); +SpiPs_write_read1(0x788,0x24,&rx_data); +usleep(10000); +SpiPs_write_read1(0x788,0xA4,&rx_data); +//BBPLL +SpiPs_write_read1(0x0C0,0x50,&rx_data); +SpiPs_write_read1(0x0C1,0x50,&rx_data); +SpiPs_write_read1(0x0C2,0x50,&rx_data); +SpiPs_write_read1(0x601,0x0F,&rx_data); +SpiPs_write_read1(0x602,0x21,&rx_data); +SpiPs_write_read1(0x60D,0x0C,&rx_data); +SpiPs_write_read1(0x60E,0x66,&rx_data); +SpiPs_write_read1(0x60F,0x66,&rx_data); +SpiPs_write_read1(0x610,0x66,&rx_data); +SpiPs_write_read1(0x611,0x13,&rx_data); +SpiPs_write_read1(0x612,0x00,&rx_data); +//ADC +SpiPs_write_read1(0x70B,0x0E,&rx_data); +SpiPs_write_read1(0x727,0x0E,&rx_data); +SpiPs_write_read1(0x743,0x0E,&rx_data); +SpiPs_write_read1(0x70D,0x3F,&rx_data); +SpiPs_write_read1(0x729,0x3F,&rx_data); +SpiPs_write_read1(0x745,0x3F,&rx_data); +SpiPs_write_read1(0x70E,0x48,&rx_data); +SpiPs_write_read1(0x72A,0x48,&rx_data); +SpiPs_write_read1(0x746,0x48,&rx_data); +SpiPs_write_read1(0x70F,0xA0,&rx_data); +SpiPs_write_read1(0x72B,0xA0,&rx_data); +SpiPs_write_read1(0x747,0xA0,&rx_data); +//ͨµÀʱÖÓÀ´Դ +SpiPs_write_read1(0x77D,0xC0,&rx_data); +SpiPs_write_read1(0x775,0x24,&rx_data); +SpiPs_write_read1(0x6C3,0x00,&rx_data); +//PLL1 +SpiPs_write_read1(0x620,0x81,&rx_data); +SpiPs_write_read1(0x61D,0x00,&rx_data); +SpiPs_write_read1(0xB06,0x00,&rx_data); +SpiPs_write_read1(0xB07,0x64,&rx_data); +SpiPs_write_read1(0xB08,0x00,&rx_data); +SpiPs_write_read1(0xB09,0x00,&rx_data); +SpiPs_write_read1(0xB0A,0x00,&rx_data); +SpiPs_write_read1(0x61A,0x0B,&rx_data); +SpiPs_write_read1(0x61B,0x20,&rx_data); +SpiPs_write_read1(0x61C,0x13,&rx_data); +SpiPs_write_read1(0x624,0x11,&rx_data); +SpiPs_write_read1(0x625,0x11,&rx_data); +SpiPs_write_read1(0x62A,0x00,&rx_data); +SpiPs_write_read1(0x61E,0x30,&rx_data); +SpiPs_write_read1(0x61F,0x08,&rx_data); +SpiPs_write_read1(0x621,0x4D,&rx_data); +SpiPs_write_read1(0x622,0x19,&rx_data); +SpiPs_write_read1(0x627,0x10,&rx_data); +//RxͨµÀÉèÖà +SpiPs_write_read1(0x700,0x56,&rx_data); +SpiPs_write_read1(0x71C,0x56,&rx_data); +SpiPs_write_read1(0x738,0x56,&rx_data); +SpiPs_write_read1(0x701,0x02,&rx_data); +SpiPs_write_read1(0x702,0x35,&rx_data); +SpiPs_write_read1(0x71D,0x02,&rx_data); +SpiPs_write_read1(0x71E,0x35,&rx_data); +SpiPs_write_read1(0x739,0x02,&rx_data); +SpiPs_write_read1(0x73A,0x35,&rx_data); +SpiPs_write_read1(0x703,0xF6,&rx_data); +SpiPs_write_read1(0x71F,0xF6,&rx_data); +SpiPs_write_read1(0x73B,0xF6,&rx_data); +SpiPs_write_read1(0x704,0xC8,&rx_data); +SpiPs_write_read1(0x720,0xC8,&rx_data); +SpiPs_write_read1(0x73C,0xC8,&rx_data); +SpiPs_write_read1(0x907,0x1F,&rx_data); +SpiPs_write_read1(0x705,0xFC,&rx_data); +SpiPs_write_read1(0x721,0xFC,&rx_data); +SpiPs_write_read1(0x73D,0xFC,&rx_data); +SpiPs_write_read1(0x706,0x65,&rx_data); +SpiPs_write_read1(0x722,0x65,&rx_data); +SpiPs_write_read1(0x73E,0x65,&rx_data); +SpiPs_write_read1(0x707,0x91,&rx_data); +SpiPs_write_read1(0x723,0x91,&rx_data); +SpiPs_write_read1(0x73F,0x91,&rx_data); +SpiPs_write_read1(0x708,0x04,&rx_data); +SpiPs_write_read1(0x724,0x04,&rx_data); +SpiPs_write_read1(0x740,0x04,&rx_data); +SpiPs_write_read1(0x709,0x94,&rx_data); +SpiPs_write_read1(0x725,0x94,&rx_data); +SpiPs_write_read1(0x741,0x94,&rx_data); +SpiPs_write_read1(0x70A,0xFC,&rx_data); +SpiPs_write_read1(0x726,0xFC,&rx_data); +SpiPs_write_read1(0x742,0xFC,&rx_data); +SpiPs_write_read1(0x75A,0x83,&rx_data); +SpiPs_write_read1(0x75B,0xDA,&rx_data); +usleep(10000); +SpiPs_write_read1(0x75B,0x9A,&rx_data); +SpiPs_write_read1(0x75C,0x81,&rx_data); +SpiPs_write_read1(0x75D,0xF8,&rx_data); +SpiPs_write_read1(0x75E,0xA7,&rx_data); +SpiPs_write_read1(0x75F,0x51,&rx_data); +SpiPs_write_read1(0x760,0xFF,&rx_data); +SpiPs_write_read1(0x761,0x04,&rx_data); +SpiPs_write_read1(0x762,0x70,&rx_data); +SpiPs_write_read1(0x763,0x04,&rx_data); +SpiPs_write_read1(0x764,0x83,&rx_data); +SpiPs_write_read1(0x765,0xDA,&rx_data); +usleep(100000); +SpiPs_write_read1(0x765,0x9A,&rx_data); +SpiPs_write_read1(0x766,0x81,&rx_data); +SpiPs_write_read1(0x767,0xF8,&rx_data); +SpiPs_write_read1(0x768,0xA7,&rx_data); +SpiPs_write_read1(0x769,0x51,&rx_data); +SpiPs_write_read1(0x76A,0xFF,&rx_data); +SpiPs_write_read1(0x76B,0x04,&rx_data); +SpiPs_write_read1(0x76C,0x70,&rx_data); +SpiPs_write_read1(0x76D,0x04,&rx_data); +//RxÂ˲¨Æ÷ +SpiPs_write_read1(0x106,0x02,&rx_data); +//TxÂ˲¨Æ÷ +SpiPs_write_read1(0x107,0x00,&rx_data); +SpiPs_write_read1(0x108,0xD0,&rx_data); +//Rx NCO +SpiPs_write_read1(0x11C,0x00,&rx_data); +SpiPs_write_read1(0xA07,0x00,&rx_data); +SpiPs_write_read1(0xA08,0x00,&rx_data); +SpiPs_write_read1(0xA16,0x00,&rx_data); +SpiPs_write_read1(0xA17,0x00,&rx_data); +SpiPs_write_read1(0xA18,0x00,&rx_data); +SpiPs_write_read1(0xA19,0x00,&rx_data); +SpiPs_write_read1(0xA1A,0x00,&rx_data); +SpiPs_write_read1(0xA1B,0x00,&rx_data); +//Tx NCO +SpiPs_write_read1(0x128,0x10,&rx_data); +SpiPs_write_read1(0x11B,0x00,&rx_data); +SpiPs_write_read1(0x11E,0x00,&rx_data); +SpiPs_write_read1(0x161,0x00,&rx_data); +SpiPs_write_read1(0x162,0x00,&rx_data); +SpiPs_write_read1(0x164,0x00,&rx_data); +SpiPs_write_read1(0x165,0x00,&rx_data); +SpiPs_write_read1(0x166,0x00,&rx_data); +SpiPs_write_read1(0x167,0x00,&rx_data); +SpiPs_write_read1(0x168,0x00,&rx_data); +SpiPs_write_read1(0x169,0x00,&rx_data); +//AGCÅäÖà +SpiPs_write_read1(0x900,0x00,&rx_data); +SpiPs_write_read1(0x901,0x00,&rx_data); +SpiPs_write_read1(0x903,0x11,&rx_data); +SpiPs_write_read1(0x908,0x77,&rx_data); +SpiPs_write_read1(0x909,0x03,&rx_data); +SpiPs_write_read1(0x90A,0x32,&rx_data); +SpiPs_write_read1(0x90D,0x00,&rx_data); +SpiPs_write_read1(0x90E,0x0F,&rx_data); +SpiPs_write_read1(0x90F,0x65,&rx_data); +SpiPs_write_read1(0x910,0x32,&rx_data); +SpiPs_write_read1(0x911,0x00,&rx_data); +SpiPs_write_read1(0x912,0x0F,&rx_data); +SpiPs_write_read1(0x913,0x3A,&rx_data); +SpiPs_write_read1(0x914,0x37,&rx_data); +SpiPs_write_read1(0x915,0x34,&rx_data); +SpiPs_write_read1(0x916,0x31,&rx_data); +//½øÈëÐÝÃßģʽ +SpiPs_write_read1(0x02E,0x01,&rx_data); +usleep(1000000); +//½øÈëУ׼ģʽ +SpiPs_write_read1(0x02E,0x03,&rx_data); +//BBPLL AFC +SpiPs_write_read1(0x600,0x00,&rx_data); +usleep(100000); +SpiPs_write_read1(0x600,0x11,&rx_data); +//PLL1 AFC +SpiPs_write_read1(0x617,0x00,&rx_data); +usleep(100000); +SpiPs_write_read1(0x617,0x03,&rx_data); +//½ӿÚÅäÖà +SpiPs_write_read1(0x02E,0x06,&rx_data); +SpiPs_write_read1(0x003,0x33,&rx_data); +SpiPs_write_read1(0x200,0x00,&rx_data); +SpiPs_write_read1(0x201,0x00,&rx_data); +SpiPs_write_read1(0x202,0x03,&rx_data); +SpiPs_write_read1(0x209,0x00,&rx_data); +SpiPs_write_read1(0x780,0x11,&rx_data); +SpiPs_write_read1(0x781,0x6F,&rx_data); +SpiPs_write_read1(0x782,0x01,&rx_data); +SpiPs_write_read1(0x783,0x01,&rx_data); +SpiPs_write_read1(0x784,0x01,&rx_data); +//ADCУ׼ +SpiPs_write_read1(0x70B,0x1E,&rx_data); +SpiPs_write_read1(0x727,0x1E,&rx_data); +SpiPs_write_read1(0x743,0x1E,&rx_data); +SpiPs_write_read1(0x70F,0xA0,&rx_data); +SpiPs_write_read1(0x710,0x83,&rx_data); +SpiPs_write_read1(0x711,0xF0,&rx_data); +SpiPs_write_read1(0x72B,0xA0,&rx_data); +SpiPs_write_read1(0x72C,0x83,&rx_data); +SpiPs_write_read1(0x72D,0xF0,&rx_data); +SpiPs_write_read1(0x747,0xA0,&rx_data); +SpiPs_write_read1(0x748,0x83,&rx_data); +SpiPs_write_read1(0x749,0xF0,&rx_data); +SpiPs_write_read1(0x713,0x80,&rx_data); +SpiPs_write_read1(0x72F,0x80,&rx_data); +SpiPs_write_read1(0x74B,0x80,&rx_data); +SpiPs_write_read1(0x70B,0x9E,&rx_data); +SpiPs_write_read1(0x727,0x9E,&rx_data); +SpiPs_write_read1(0x743,0x9E,&rx_data); +SpiPs_write_read1(0x70B,0x9A,&rx_data); +SpiPs_write_read1(0x727,0x9A,&rx_data); +SpiPs_write_read1(0x743,0x9A,&rx_data); +SpiPs_write_read1(0x70B,0x9E,&rx_data); +SpiPs_write_read1(0x727,0x9E,&rx_data); +SpiPs_write_read1(0x743,0x9E,&rx_data); +usleep(100000); +SpiPs_write_read1(0x70B,0x1E,&rx_data); +SpiPs_write_read1(0x727,0x1E,&rx_data); +SpiPs_write_read1(0x743,0x1E,&rx_data); +SpiPs_write_read1(0x713,0xC0,&rx_data); +SpiPs_write_read1(0x72F,0xC0,&rx_data); +SpiPs_write_read1(0x74B,0xC0,&rx_data); +//FlashУ׼ +SpiPs_write_read1(0x70B,0x1E,&rx_data); +SpiPs_write_read1(0x727,0x1E,&rx_data); +SpiPs_write_read1(0x743,0x1E,&rx_data); +SpiPs_write_read1(0x715,0x80,&rx_data); +SpiPs_write_read1(0x731,0x80,&rx_data); +SpiPs_write_read1(0x74D,0x80,&rx_data); +SpiPs_write_read1(0x715,0xC0,&rx_data); +SpiPs_write_read1(0x731,0xC0,&rx_data); +SpiPs_write_read1(0x74D,0xC0,&rx_data); +SpiPs_write_read1(0x716,0x40,&rx_data); +SpiPs_write_read1(0x732,0x40,&rx_data); +SpiPs_write_read1(0x74E,0x40,&rx_data); +SpiPs_write_read1(0x70B,0x16,&rx_data); +SpiPs_write_read1(0x727,0x16,&rx_data); +SpiPs_write_read1(0x743,0x16,&rx_data); +usleep(100000); +SpiPs_write_read1(0x70B,0x1E,&rx_data); +SpiPs_write_read1(0x727,0x1E,&rx_data); +SpiPs_write_read1(0x743,0x1E,&rx_data); +SpiPs_write_read1(0x715,0x80,&rx_data); +SpiPs_write_read1(0x731,0x80,&rx_data); +SpiPs_write_read1(0x74D,0x80,&rx_data); +SpiPs_write_read1(0x716,0x00,&rx_data); +SpiPs_write_read1(0x732,0x00,&rx_data); +SpiPs_write_read1(0x74E,0x00,&rx_data); +SpiPs_write_read1(0x109,0x11,&rx_data); +SpiPs_write_read1(0x10A,0x70,&rx_data); +SpiPs_write_read1(0x11F,0x33,&rx_data); +usleep(1000); +SpiPs_write_read2(0x615,0x00,&rx_data); +temp=rx_data; +SpiPs_write_read2(0x635,0x00,&rx_data); +temp=rx_data; +////ÅäÖÃTx NCO BIST ON +//SpiPs_write_read1(0x11B,0x01,&rx_data); +//SpiPs_write_read1(0x11E,0x01,&rx_data); +//SpiPs_write_read1(0x161,0x01,&rx_data); +//SpiPs_write_read1(0x162,0x01,&rx_data); +////ÅäÖÃTx NCOƵÂÊ 1MHz +//SpiPs_write_read1(0x164,0x05,&rx_data); +//SpiPs_write_read1(0x165,0x55,&rx_data); +//SpiPs_write_read1(0x166,0x55,&rx_data); +//SpiPs_write_read1(0x167,0x55,&rx_data); +//SpiPs_write_read1(0x168,0x55,&rx_data); +//SpiPs_write_read1(0x169,0x55,&rx_data); +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/cx9261a_151_config.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/cx9261a_151_config.c new file mode 100644 index 0000000..c1e78f7 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/cx9261a_151_config.c @@ -0,0 +1,708 @@ +/* + * cx9261_config.c + * + * Created on: 2023Äê10ÔÂ16ÈÕ + * Author: cxlab + */ + +#include "xilinx_io.h" +#include "sleep.h" +void cx9261a_151_config(void){ +u8 rx_data; +int temp; + +//setPin(pin,value) +GpioSetPinDir(0,0x01); +GpioWritePin(0,1); +GpioSetPinDir(32,0x01); +GpioWritePin(32,1); +GpioSetPinDir(33,0x01); +GpioWritePin(33,1); +GpioSetPinDir(34,0x01); +GpioWritePin(34,0); +GpioSetPinDir(35,0x01); //tx:0-rx:1 +GpioWritePin(35,1); //tx:0-rx:1 +GpioSetPinDir(36,0x01); //fdd:0-tdd:1 +GpioWritePin(36,1); //fdd:0-tdd:1 +GpioSetPinDir(37,0x01); //1r1t:0-2r2t:1 +GpioWritePin(37,0); //1r1t:0-2r2t:1 +GpioSetPinDir(38,0x01); //dualport:0-singleport:1 +GpioWritePin(38,1); //dualport:0-singleport:1 +GpioSetPinDir(39,0x01); +GpioWritePin(39,1); +GpioSetPinDir(40,0x01); +GpioWritePin(40,1); +//Èý¸öͨµÀ¹²ͬ¿ØÖÆ +SpiPs_write_read1(0x000,0x02,&rx_data); +//µçԴ¹ÜÀí×Զ¨Òå +//¶ϵçģʽ +SpiPs_write_read1(0x031,0x00,&rx_data); +SpiPs_write_read1(0x032,0x7F,&rx_data); +SpiPs_write_read1(0x033,0xFF,&rx_data); +SpiPs_write_read1(0x034,0xFF,&rx_data); +//ÐÝÃßģʽ +SpiPs_write_read1(0x035,0x00,&rx_data); +SpiPs_write_read1(0x036,0x3D,&rx_data); +SpiPs_write_read1(0x037,0xFF,&rx_data); +SpiPs_write_read1(0x038,0xEB,&rx_data); +//¾¯½äģʽ +SpiPs_write_read1(0x039,0x00,&rx_data); +SpiPs_write_read1(0x03A,0x3D,&rx_data); +SpiPs_write_read1(0x03B,0xFF,&rx_data); +SpiPs_write_read1(0x03C,0xE0,&rx_data); +//У׼ģʽ +SpiPs_write_read1(0x03D,0x1B,&rx_data); +SpiPs_write_read1(0x03E,0x39,&rx_data); +SpiPs_write_read1(0x03F,0x27,&rx_data); +SpiPs_write_read1(0x040,0xE0,&rx_data); +//½ÓÊÕģʽ +SpiPs_write_read1(0x041,0x03,&rx_data); +SpiPs_write_read1(0x042,0x3D,&rx_data); +SpiPs_write_read1(0x043,0xE7,&rx_data); +SpiPs_write_read1(0x044,0xF8,&rx_data); +//·¢Éäģʽ +SpiPs_write_read1(0x045,0x18,&rx_data); +SpiPs_write_read1(0x046,0x3D,&rx_data); +SpiPs_write_read1(0x047,0x3F,&rx_data); +SpiPs_write_read1(0x048,0xF8,&rx_data); +//FDDģʽ +SpiPs_write_read1(0x049,0x1B,&rx_data); +SpiPs_write_read1(0x04A,0x3D,&rx_data); +SpiPs_write_read1(0x04B,0x27,&rx_data); +SpiPs_write_read1(0x04C,0xE0,&rx_data); +//¹۲âģʽ +SpiPs_write_read1(0x04D,0x04,&rx_data); +SpiPs_write_read1(0x04E,0x3D,&rx_data); +SpiPs_write_read1(0x04F,0xD9,&rx_data); +SpiPs_write_read1(0x050,0xFE,&rx_data); +//´ø϶»ù׼ +SpiPs_write_read1(0x773,0x53,&rx_data); +SpiPs_write_read1(0x774,0x86,&rx_data); +//²ο¼ʱÖÓÉèÖà +SpiPs_write_read1(0x789,0x00,&rx_data); +SpiPs_write_read1(0x785,0x49,&rx_data); +SpiPs_write_read1(0x788,0x24,&rx_data); +usleep(10000); +SpiPs_write_read1(0x788,0xA4,&rx_data); +//BBPLL +SpiPs_write_read1(0x0C0,0x50,&rx_data); +SpiPs_write_read1(0x0C1,0x50,&rx_data); +SpiPs_write_read1(0x0C2,0x50,&rx_data); +SpiPs_write_read1(0x601,0x0F,&rx_data); +SpiPs_write_read1(0x602,0x21,&rx_data); +SpiPs_write_read1(0x60D,0x0C,&rx_data); +SpiPs_write_read1(0x60E,0x66,&rx_data); +SpiPs_write_read1(0x60F,0x66,&rx_data); +SpiPs_write_read1(0x610,0x66,&rx_data); +SpiPs_write_read1(0x611,0x13,&rx_data); +SpiPs_write_read1(0x612,0x00,&rx_data); +//ADC +SpiPs_write_read1(0x70B,0x0E,&rx_data); +SpiPs_write_read1(0x727,0x0E,&rx_data); +SpiPs_write_read1(0x743,0x0E,&rx_data); +SpiPs_write_read1(0x70D,0x3F,&rx_data); +SpiPs_write_read1(0x729,0x3F,&rx_data); +SpiPs_write_read1(0x745,0x3F,&rx_data); +SpiPs_write_read1(0x70E,0x48,&rx_data); +SpiPs_write_read1(0x72A,0x48,&rx_data); +SpiPs_write_read1(0x746,0x48,&rx_data); +SpiPs_write_read1(0x70F,0xA0,&rx_data); +SpiPs_write_read1(0x72B,0xA0,&rx_data); +SpiPs_write_read1(0x747,0xA0,&rx_data); +//ͨµÀʱÖÓÀ´Դ +SpiPs_write_read1(0x77D,0xCF,&rx_data); +SpiPs_write_read1(0x775,0x24,&rx_data); +SpiPs_write_read1(0x6C3,0x00,&rx_data); +//PLL1 +SpiPs_write_read1(0x620,0x81,&rx_data); +SpiPs_write_read1(0x61D,0x07,&rx_data); +SpiPs_write_read1(0xB06,0x00,&rx_data); +SpiPs_write_read1(0xB07,0x64,&rx_data); +SpiPs_write_read1(0xB08,0x00,&rx_data); +SpiPs_write_read1(0xB09,0x00,&rx_data); +SpiPs_write_read1(0xB0A,0x00,&rx_data); +SpiPs_write_read1(0x61A,0x0B,&rx_data); +SpiPs_write_read1(0x61B,0x20,&rx_data); +SpiPs_write_read1(0x61C,0x13,&rx_data); +SpiPs_write_read1(0x624,0x11,&rx_data); +SpiPs_write_read1(0x625,0x11,&rx_data); +SpiPs_write_read1(0x62A,0x00,&rx_data); +SpiPs_write_read1(0x61E,0x30,&rx_data); +SpiPs_write_read1(0x61F,0x08,&rx_data); +SpiPs_write_read1(0x621,0x4D,&rx_data); +SpiPs_write_read1(0x622,0x19,&rx_data); +SpiPs_write_read1(0x627,0x10,&rx_data); +//PLL2 +SpiPs_write_read1(0x642,0x91,&rx_data); +SpiPs_write_read1(0x63F,0x36,&rx_data); +SpiPs_write_read1(0xB14,0x00,&rx_data); +SpiPs_write_read1(0xB15,0x70,&rx_data); +SpiPs_write_read1(0xB16,0x00,&rx_data); +SpiPs_write_read1(0xB17,0x00,&rx_data); +SpiPs_write_read1(0xB18,0x00,&rx_data); +SpiPs_write_read1(0x63C,0x0C,&rx_data); +SpiPs_write_read1(0x63D,0x20,&rx_data); +SpiPs_write_read1(0x63E,0x0C,&rx_data); +SpiPs_write_read1(0x646,0x11,&rx_data); +SpiPs_write_read1(0x647,0x11,&rx_data); +SpiPs_write_read1(0x64C,0x00,&rx_data); +SpiPs_write_read1(0x640,0x30,&rx_data); +SpiPs_write_read1(0x641,0x08,&rx_data); +SpiPs_write_read1(0x643,0x4D,&rx_data); +SpiPs_write_read1(0x644,0x19,&rx_data); +SpiPs_write_read1(0x649,0x10,&rx_data); +//PLL3 +SpiPs_write_read1(0x664,0x91,&rx_data); +SpiPs_write_read1(0x661,0x36,&rx_data); +SpiPs_write_read1(0xB22,0x00,&rx_data); +SpiPs_write_read1(0xB23,0x80,&rx_data); +SpiPs_write_read1(0xB24,0x00,&rx_data); +SpiPs_write_read1(0xB25,0x00,&rx_data); +SpiPs_write_read1(0xB26,0x00,&rx_data); +SpiPs_write_read1(0x65E,0x0D,&rx_data); +SpiPs_write_read1(0x65F,0x20,&rx_data); +SpiPs_write_read1(0x660,0x0F,&rx_data); +SpiPs_write_read1(0x668,0x11,&rx_data); +SpiPs_write_read1(0x669,0x11,&rx_data); +SpiPs_write_read1(0x66E,0x00,&rx_data); +SpiPs_write_read1(0x662,0x30,&rx_data); +SpiPs_write_read1(0x663,0x08,&rx_data); +SpiPs_write_read1(0x665,0x4D,&rx_data); +SpiPs_write_read1(0x666,0x19,&rx_data); +SpiPs_write_read1(0x66B,0x10,&rx_data); +//PLL4 +SpiPs_write_read1(0x686,0x81,&rx_data); +SpiPs_write_read1(0x683,0x37,&rx_data); +SpiPs_write_read1(0xB30,0x00,&rx_data); +SpiPs_write_read1(0xB31,0x48,&rx_data); +SpiPs_write_read1(0xB32,0x00,&rx_data); +SpiPs_write_read1(0xB33,0x00,&rx_data); +SpiPs_write_read1(0xB34,0x00,&rx_data); +SpiPs_write_read1(0x680,0x0C,&rx_data); +SpiPs_write_read1(0x681,0x20,&rx_data); +SpiPs_write_read1(0x682,0x11,&rx_data); +SpiPs_write_read1(0x68A,0x11,&rx_data); +SpiPs_write_read1(0x68B,0x11,&rx_data); +SpiPs_write_read1(0x690,0x00,&rx_data); +SpiPs_write_read1(0x684,0x30,&rx_data); +SpiPs_write_read1(0x685,0x08,&rx_data); +SpiPs_write_read1(0x687,0x4D,&rx_data); +SpiPs_write_read1(0x688,0x19,&rx_data); +SpiPs_write_read1(0x68D,0x10,&rx_data); +//PLL5 +SpiPs_write_read1(0x6A8,0x81,&rx_data); +SpiPs_write_read1(0x6A5,0x37,&rx_data); +SpiPs_write_read1(0xB3E,0x00,&rx_data); +SpiPs_write_read1(0xB3F,0x50,&rx_data); +SpiPs_write_read1(0xB40,0x00,&rx_data); +SpiPs_write_read1(0xB41,0x00,&rx_data); +SpiPs_write_read1(0xB42,0x00,&rx_data); +SpiPs_write_read1(0x6A2,0x0C,&rx_data); +SpiPs_write_read1(0x6A3,0x20,&rx_data); +SpiPs_write_read1(0x6A4,0x11,&rx_data); +SpiPs_write_read1(0x6AC,0x11,&rx_data); +SpiPs_write_read1(0x6AD,0x11,&rx_data); +SpiPs_write_read1(0x6B2,0x00,&rx_data); +SpiPs_write_read1(0x6A6,0x30,&rx_data); +SpiPs_write_read1(0x6A7,0x08,&rx_data); +SpiPs_write_read1(0x6A9,0x4D,&rx_data); +SpiPs_write_read1(0x6AA,0x19,&rx_data); +SpiPs_write_read1(0x6AF,0x10,&rx_data); +//RxͨµÀÉèÖà +SpiPs_write_read1(0x700,0x56,&rx_data); +SpiPs_write_read1(0x71C,0x56,&rx_data); +SpiPs_write_read1(0x738,0x56,&rx_data); +SpiPs_write_read1(0x701,0x02,&rx_data); +SpiPs_write_read1(0x702,0x35,&rx_data); +SpiPs_write_read1(0x71D,0x02,&rx_data); +SpiPs_write_read1(0x71E,0x35,&rx_data); +SpiPs_write_read1(0x739,0x02,&rx_data); +SpiPs_write_read1(0x73A,0x35,&rx_data); +SpiPs_write_read1(0x703,0xF6,&rx_data); +SpiPs_write_read1(0x71F,0xF6,&rx_data); +SpiPs_write_read1(0x73B,0xF6,&rx_data); +SpiPs_write_read1(0x704,0xC8,&rx_data); +SpiPs_write_read1(0x720,0xC8,&rx_data); +SpiPs_write_read1(0x73C,0xC8,&rx_data); +SpiPs_write_read1(0x907,0x1F,&rx_data); +SpiPs_write_read1(0x705,0xFC,&rx_data); +SpiPs_write_read1(0x721,0xFC,&rx_data); +SpiPs_write_read1(0x73D,0xFC,&rx_data); +SpiPs_write_read1(0x706,0x65,&rx_data); +SpiPs_write_read1(0x722,0x65,&rx_data); +SpiPs_write_read1(0x73E,0x65,&rx_data); +SpiPs_write_read1(0x707,0x91,&rx_data); +SpiPs_write_read1(0x723,0x91,&rx_data); +SpiPs_write_read1(0x73F,0x91,&rx_data); +SpiPs_write_read1(0x708,0x04,&rx_data); +SpiPs_write_read1(0x724,0x04,&rx_data); +SpiPs_write_read1(0x740,0x04,&rx_data); +SpiPs_write_read1(0x709,0x94,&rx_data); +SpiPs_write_read1(0x725,0x94,&rx_data); +SpiPs_write_read1(0x741,0x94,&rx_data); +SpiPs_write_read1(0x70A,0xFC,&rx_data); +SpiPs_write_read1(0x726,0xFC,&rx_data); +SpiPs_write_read1(0x742,0xFC,&rx_data); +SpiPs_write_read1(0x75A,0x83,&rx_data); +SpiPs_write_read1(0x75B,0xDA,&rx_data); +usleep(100000); +SpiPs_write_read1(0x75B,0x9A,&rx_data); +SpiPs_write_read1(0x75C,0x81,&rx_data); +SpiPs_write_read1(0x75D,0xF8,&rx_data); +SpiPs_write_read1(0x75E,0xA7,&rx_data); +SpiPs_write_read1(0x75F,0x50,&rx_data); +SpiPs_write_read1(0x760,0xFF,&rx_data); +SpiPs_write_read1(0x761,0x07,&rx_data); +SpiPs_write_read1(0x762,0x6C,&rx_data); +SpiPs_write_read1(0x763,0x04,&rx_data); +SpiPs_write_read1(0x764,0x83,&rx_data); +SpiPs_write_read1(0x765,0xDA,&rx_data); +usleep(100000); +SpiPs_write_read1(0x765,0x9A,&rx_data); +SpiPs_write_read1(0x766,0x81,&rx_data); +SpiPs_write_read1(0x767,0xF8,&rx_data); +SpiPs_write_read1(0x768,0xA7,&rx_data); +SpiPs_write_read1(0x769,0x50,&rx_data); +SpiPs_write_read1(0x76A,0xFF,&rx_data); +SpiPs_write_read1(0x76B,0x07,&rx_data); +SpiPs_write_read1(0x76C,0x6C,&rx_data); +SpiPs_write_read1(0x76D,0x04,&rx_data); +//RxÂ˲¨Æ÷ +SpiPs_write_read1(0x106,0x02,&rx_data); +//TxÂ˲¨Æ÷ +SpiPs_write_read1(0x107,0x00,&rx_data); +SpiPs_write_read1(0x108,0xD0,&rx_data); +//Rx NCO +SpiPs_write_read1(0x11C,0x00,&rx_data); +SpiPs_write_read1(0xA07,0x00,&rx_data); +SpiPs_write_read1(0xA08,0x00,&rx_data); +SpiPs_write_read1(0xA16,0x00,&rx_data); +SpiPs_write_read1(0xA17,0x00,&rx_data); +SpiPs_write_read1(0xA18,0x00,&rx_data); +SpiPs_write_read1(0xA19,0x00,&rx_data); +SpiPs_write_read1(0xA1A,0x00,&rx_data); +SpiPs_write_read1(0xA1B,0x00,&rx_data); +//Tx NCO +SpiPs_write_read1(0x128,0x10,&rx_data); +SpiPs_write_read1(0x11B,0x01,&rx_data); +SpiPs_write_read1(0x11E,0x01,&rx_data); +SpiPs_write_read1(0x161,0x01,&rx_data); +SpiPs_write_read1(0x162,0x01,&rx_data); +SpiPs_write_read1(0x164,0x05,&rx_data); +SpiPs_write_read1(0x165,0x55,&rx_data); +SpiPs_write_read1(0x166,0x55,&rx_data); +SpiPs_write_read1(0x167,0x55,&rx_data); +SpiPs_write_read1(0x168,0x55,&rx_data); +SpiPs_write_read1(0x169,0x55,&rx_data); +//AGCÅäÖà +SpiPs_write_read1(0x900,0x00,&rx_data); +SpiPs_write_read1(0x901,0x00,&rx_data); +SpiPs_write_read1(0x903,0x11,&rx_data); +SpiPs_write_read1(0x908,0x77,&rx_data); +SpiPs_write_read1(0x909,0x03,&rx_data); +SpiPs_write_read1(0x90A,0x32,&rx_data); +SpiPs_write_read1(0x90D,0x00,&rx_data); +SpiPs_write_read1(0x90E,0x0F,&rx_data); +SpiPs_write_read1(0x90F,0x65,&rx_data); +SpiPs_write_read1(0x910,0x32,&rx_data); +SpiPs_write_read1(0x911,0x00,&rx_data); +SpiPs_write_read1(0x912,0x0F,&rx_data); +SpiPs_write_read1(0x913,0x3A,&rx_data); +SpiPs_write_read1(0x914,0x37,&rx_data); +SpiPs_write_read1(0x915,0x34,&rx_data); +SpiPs_write_read1(0x916,0x31,&rx_data); +//½øÈëÐÝÃßģʽ +SpiPs_write_read1(0x02E,0x01,&rx_data); +usleep(1000000); +//½øÈëУ׼ģʽ +SpiPs_write_read1(0x02E,0x03,&rx_data); +usleep(100000); +//BBPLL AFC +SpiPs_write_read1(0x600,0x00,&rx_data); +SpiPs_write_read1(0x600,0x11,&rx_data); +usleep(50); +//PLL1 AFC +while(temp != 48) +{ +SpiPs_write_read1(0x617,0x00,&rx_data); +SpiPs_write_read1(0x617,0x03,&rx_data); +usleep(100); +SpiPs_write_read2(0x635,0x00,&rx_data); +temp=rx_data; +} +//½ӿÚÅäÖà +SpiPs_write_read1(0x02E,0x06,&rx_data); +SpiPs_write_read1(0x003,0x33,&rx_data); +SpiPs_write_read1(0x200,0x00,&rx_data); +SpiPs_write_read1(0x201,0x00,&rx_data); +SpiPs_write_read1(0x202,0x03,&rx_data); +SpiPs_write_read1(0x208,0x00,&rx_data); +SpiPs_write_read1(0x209,0x00,&rx_data); +SpiPs_write_read1(0x780,0x11,&rx_data); +SpiPs_write_read1(0x781,0x6F,&rx_data); +SpiPs_write_read1(0x782,0x01,&rx_data); +SpiPs_write_read1(0x783,0x01,&rx_data); +SpiPs_write_read1(0x784,0x01,&rx_data); +//ADCУ׼ +SpiPs_write_read1(0x70B,0x1E,&rx_data); +SpiPs_write_read1(0x727,0x1E,&rx_data); +SpiPs_write_read1(0x743,0x1E,&rx_data); +SpiPs_write_read1(0x70F,0xA0,&rx_data); +SpiPs_write_read1(0x710,0x83,&rx_data); +SpiPs_write_read1(0x711,0xF0,&rx_data); +SpiPs_write_read1(0x72B,0xA0,&rx_data); +SpiPs_write_read1(0x72C,0x83,&rx_data); +SpiPs_write_read1(0x72D,0xF0,&rx_data); +SpiPs_write_read1(0x747,0xA0,&rx_data); +SpiPs_write_read1(0x748,0x83,&rx_data); +SpiPs_write_read1(0x749,0xF0,&rx_data); +SpiPs_write_read1(0x713,0x80,&rx_data); +SpiPs_write_read1(0x72F,0x80,&rx_data); +SpiPs_write_read1(0x74B,0x80,&rx_data); +SpiPs_write_read1(0x70B,0x9E,&rx_data); +SpiPs_write_read1(0x727,0x9E,&rx_data); +SpiPs_write_read1(0x743,0x9E,&rx_data); +SpiPs_write_read1(0x70B,0x9A,&rx_data); +SpiPs_write_read1(0x727,0x9A,&rx_data); +SpiPs_write_read1(0x743,0x9A,&rx_data); +SpiPs_write_read1(0x70B,0x9E,&rx_data); +SpiPs_write_read1(0x727,0x9E,&rx_data); +SpiPs_write_read1(0x743,0x9E,&rx_data); +usleep(100000); +SpiPs_write_read1(0x70B,0x1E,&rx_data); +SpiPs_write_read1(0x727,0x1E,&rx_data); +SpiPs_write_read1(0x743,0x1E,&rx_data); +SpiPs_write_read1(0x713,0xC0,&rx_data); +SpiPs_write_read1(0x72F,0xC0,&rx_data); +SpiPs_write_read1(0x74B,0xC0,&rx_data); +//FlashУ׼ +SpiPs_write_read1(0x70B,0x1E,&rx_data); +SpiPs_write_read1(0x727,0x1E,&rx_data); +SpiPs_write_read1(0x743,0x1E,&rx_data); +SpiPs_write_read1(0x715,0x80,&rx_data); +SpiPs_write_read1(0x731,0x80,&rx_data); +SpiPs_write_read1(0x74D,0x80,&rx_data); +SpiPs_write_read1(0x715,0xC0,&rx_data); +SpiPs_write_read1(0x731,0xC0,&rx_data); +SpiPs_write_read1(0x74D,0xC0,&rx_data); +SpiPs_write_read1(0x716,0x40,&rx_data); +SpiPs_write_read1(0x732,0x40,&rx_data); +SpiPs_write_read1(0x74E,0x40,&rx_data); +SpiPs_write_read1(0x70B,0x16,&rx_data); +SpiPs_write_read1(0x727,0x16,&rx_data); +SpiPs_write_read1(0x743,0x16,&rx_data); +usleep(100000); +SpiPs_write_read1(0x70B,0x1E,&rx_data); +SpiPs_write_read1(0x727,0x1E,&rx_data); +SpiPs_write_read1(0x743,0x1E,&rx_data); +SpiPs_write_read1(0x715,0x80,&rx_data); +SpiPs_write_read1(0x731,0x80,&rx_data); +SpiPs_write_read1(0x74D,0x80,&rx_data); +SpiPs_write_read1(0x716,0x00,&rx_data); +SpiPs_write_read1(0x732,0x00,&rx_data); +SpiPs_write_read1(0x74E,0x00,&rx_data); +SpiPs_write_read1(0x109,0x11,&rx_data); +SpiPs_write_read1(0x10A,0x70,&rx_data); +SpiPs_write_read1(0x11F,0x33,&rx_data); +//QECУ׼ +//Rx TST Calibration +SpiPs_write_read1(0x000,0x12,&rx_data); +SpiPs_write_read1(0x109,0x00,&rx_data); +SpiPs_write_read1(0x77E,0x50,&rx_data); +SpiPs_write_read1(0x14E,0x00,&rx_data); +// Ð޸ÄУ׼ģʽµçԴ¹ÜÀí +SpiPs_write_read1(0x03D,0x09,&rx_data); +SpiPs_write_read1(0x03E,0x39,&rx_data); +SpiPs_write_read1(0x03F,0xB6,&rx_data); +SpiPs_write_read1(0x040,0x78,&rx_data); +//Ð޸ĵçԴģʽ +SpiPs_write_read1(0x02E,0x03,&rx_data); +//Calibration PLL Source +// Ð޸ÄPllÀ´Դ +SpiPs_write_read1(0x77D,0x90,&rx_data); +SpiPs_write_read1(0x6C3,0x05,&rx_data); +SpiPs_write_read1(0x775,0x24,&rx_data); +//Ð޸ÄPll4ƵÂÊ +//PLL4 +SpiPs_write_read1(0x686,0x81,&rx_data); +SpiPs_write_read1(0x683,0x07,&rx_data); +SpiPs_write_read1(0xB30,0x00,&rx_data); +SpiPs_write_read1(0xB31,0x63,&rx_data); +SpiPs_write_read1(0xB32,0xEB,&rx_data); +SpiPs_write_read1(0xB33,0x85,&rx_data); +SpiPs_write_read1(0xB34,0x1E,&rx_data); +SpiPs_write_read1(0x680,0x0B,&rx_data); +SpiPs_write_read1(0x681,0x20,&rx_data); +SpiPs_write_read1(0x682,0x13,&rx_data); +SpiPs_write_read1(0x68A,0x11,&rx_data); +SpiPs_write_read1(0x68B,0x11,&rx_data); +SpiPs_write_read1(0x690,0x0B,&rx_data); +SpiPs_write_read1(0x684,0x30,&rx_data); +SpiPs_write_read1(0x685,0x08,&rx_data); +SpiPs_write_read1(0x687,0x4D,&rx_data); +SpiPs_write_read1(0x688,0x19,&rx_data); +SpiPs_write_read1(0x68D,0x10,&rx_data); +//PLL4 AFC +while(temp != 48) +{ +SpiPs_write_read1(0x67D,0x00,&rx_data); +SpiPs_write_read1(0x67D,0x03,&rx_data); +usleep(100); +SpiPs_write_read2(0x69B,0x00,&rx_data); +temp=rx_data; +} +SpiPs_write_read1(0x02A,0x00,&rx_data); +SpiPs_write_read1(0x121,0x00,&rx_data); +SpiPs_write_read1(0x11F,0x73,&rx_data); +SpiPs_write_read1(0x124,0xFF,&rx_data); +SpiPs_write_read1(0x120,0x00,&rx_data); +SpiPs_write_read1(0x122,0x03,&rx_data); +SpiPs_write_read1(0x126,0x01,&rx_data); +SpiPs_write_read1(0x02A,0x12,&rx_data); + +SpiPs_write_read2(0x14D,0x00,&rx_data); +temp=rx_data & 0x10; +while(temp != 16){ +SpiPs_write_read2(0x14D,0x00,&rx_data); +temp=rx_data & 0x10; +usleep(100000); +} + +SpiPs_write_read1(0x77E,0x00,&rx_data); +SpiPs_write_read1(0x775,0x24,&rx_data); +SpiPs_write_read1(0x77D,0xCF,&rx_data); +SpiPs_write_read1(0x6C3,0x00,&rx_data); +//Rx TST Calibration +SpiPs_write_read1(0x000,0x22,&rx_data); +SpiPs_write_read1(0x109,0x00,&rx_data); +SpiPs_write_read1(0x77E,0x0A,&rx_data); +SpiPs_write_read1(0x14E,0x00,&rx_data); +// Ð޸ÄУ׼ģʽµçԴ¹ÜÀí +SpiPs_write_read1(0x03D,0x12,&rx_data); +SpiPs_write_read1(0x03E,0x39,&rx_data); +SpiPs_write_read1(0x03F,0x6E,&rx_data); +SpiPs_write_read1(0x040,0x78,&rx_data); +//Ð޸ĵçԴģʽ +SpiPs_write_read1(0x02E,0x03,&rx_data); +//Calibration PLL Source +// Ð޸ÄPllÀ´Դ +SpiPs_write_read1(0x77D,0x60,&rx_data); +SpiPs_write_read1(0x6C3,0x0A,&rx_data); +SpiPs_write_read1(0x775,0x24,&rx_data); +//Ð޸ÄPll4ƵÂÊ +//PLL4 +SpiPs_write_read1(0x686,0x81,&rx_data); +SpiPs_write_read1(0x683,0x07,&rx_data); +SpiPs_write_read1(0xB30,0x00,&rx_data); +SpiPs_write_read1(0xB31,0x63,&rx_data); +SpiPs_write_read1(0xB32,0xEB,&rx_data); +SpiPs_write_read1(0xB33,0x85,&rx_data); +SpiPs_write_read1(0xB34,0x1E,&rx_data); +SpiPs_write_read1(0x680,0x0B,&rx_data); +SpiPs_write_read1(0x681,0x20,&rx_data); +SpiPs_write_read1(0x682,0x13,&rx_data); +SpiPs_write_read1(0x68A,0x11,&rx_data); +SpiPs_write_read1(0x68B,0x11,&rx_data); +SpiPs_write_read1(0x690,0x0B,&rx_data); +SpiPs_write_read1(0x684,0x30,&rx_data); +SpiPs_write_read1(0x685,0x08,&rx_data); +SpiPs_write_read1(0x687,0x4D,&rx_data); +SpiPs_write_read1(0x688,0x19,&rx_data); +SpiPs_write_read1(0x68D,0x10,&rx_data); +//PLL4 AFC +while(temp != 48) +{ +SpiPs_write_read1(0x67D,0x00,&rx_data); +SpiPs_write_read1(0x67D,0x03,&rx_data); +usleep(100); +SpiPs_write_read2(0x69B,0x00,&rx_data); +temp=rx_data; +} +SpiPs_write_read1(0x02B,0x00,&rx_data); +SpiPs_write_read1(0x121,0x00,&rx_data); +SpiPs_write_read1(0x11F,0x73,&rx_data); +SpiPs_write_read1(0x124,0xFF,&rx_data); +SpiPs_write_read1(0x120,0x00,&rx_data); +SpiPs_write_read1(0x122,0x03,&rx_data); +SpiPs_write_read1(0x126,0x01,&rx_data); +SpiPs_write_read1(0x02B,0x12,&rx_data); + +SpiPs_write_read2(0x14D,0x00,&rx_data); +temp=rx_data & 0x10; +while(temp != 16){ +SpiPs_write_read2(0x14D,0x00,&rx_data); +temp=rx_data & 0x10; +usleep(100000); +} + +SpiPs_write_read1(0x77E,0x00,&rx_data); +SpiPs_write_read1(0x775,0x24,&rx_data); +SpiPs_write_read1(0x77D,0xCF,&rx_data); +SpiPs_write_read1(0x6C3,0x00,&rx_data); +SpiPs_write_read1(0x02C,0x00,&rx_data); +SpiPs_write_read1(0x02B,0x00,&rx_data); +SpiPs_write_read1(0x02A,0x00,&rx_data); +//»ָ´У׼ǰÉèÖà +SpiPs_write_read1(0x000,0x02,&rx_data); +SpiPs_write_read1(0x02E,0x06,&rx_data); +SpiPs_write_read1(0x03D,0x1B,&rx_data); +SpiPs_write_read1(0x03E,0x39,&rx_data); +SpiPs_write_read1(0x03F,0x27,&rx_data); +SpiPs_write_read1(0x040,0xF8,&rx_data); +SpiPs_write_read1(0x109,0x11,&rx_data); +SpiPs_write_read1(0x10A,0x70,&rx_data); +SpiPs_write_read1(0x11B,0x01,&rx_data); +SpiPs_write_read1(0x11E,0x01,&rx_data); +SpiPs_write_read1(0x11F,0x33,&rx_data); +SpiPs_write_read1(0x128,0x10,&rx_data); +SpiPs_write_read1(0x161,0x01,&rx_data); +SpiPs_write_read1(0x162,0x01,&rx_data); +SpiPs_write_read1(0x164,0x05,&rx_data); +SpiPs_write_read1(0x165,0x55,&rx_data); +SpiPs_write_read1(0x166,0x55,&rx_data); +SpiPs_write_read1(0x167,0x55,&rx_data); +SpiPs_write_read1(0x168,0x55,&rx_data); +SpiPs_write_read1(0x169,0x55,&rx_data); +SpiPs_write_read1(0xA07,0x00,&rx_data); +SpiPs_write_read1(0xA08,0x00,&rx_data); +SpiPs_write_read1(0xA16,0x00,&rx_data); +SpiPs_write_read1(0xA17,0x00,&rx_data); +SpiPs_write_read1(0xA18,0x00,&rx_data); +SpiPs_write_read1(0xA19,0x00,&rx_data); +SpiPs_write_read1(0xA1A,0x00,&rx_data); +SpiPs_write_read1(0xA1B,0x00,&rx_data); +SpiPs_write_read1(0xA1C,0x00,&rx_data); +SpiPs_write_read1(0xA2D,0x00,&rx_data); +// TX¶þ·ַ¨У׼ +// Ð޸ÄУ׼ģʽµçԴ¹ÜÀí +SpiPs_write_read1(0x03D,0x1B,&rx_data); +SpiPs_write_read1(0x03E,0x39,&rx_data); +SpiPs_write_read1(0x03F,0x27,&rx_data); +SpiPs_write_read1(0x040,0xF8,&rx_data); +//Ð޸ĵçԴģʽ +SpiPs_write_read1(0x02E,0x03,&rx_data); +SpiPs_write_read1(0x128,0x11,&rx_data); +SpiPs_write_read1(0x11E,0x01,&rx_data); +SpiPs_write_read1(0x77E,0x24,&rx_data); +SpiPs_write_read1(0x11F,0x01,&rx_data); +SpiPs_write_read1(0x121,0x02,&rx_data); +SpiPs_write_read1(0x11B,0x01,&rx_data); +SpiPs_write_read1(0x161,0x01,&rx_data); +SpiPs_write_read1(0x162,0x01,&rx_data); +SpiPs_write_read1(0x163,0x00,&rx_data); +SpiPs_write_read1(0x164,0x05,&rx_data); +SpiPs_write_read1(0x165,0x55,&rx_data); +SpiPs_write_read1(0x166,0x55,&rx_data); +SpiPs_write_read1(0x167,0x55,&rx_data); +SpiPs_write_read1(0x168,0x55,&rx_data); +SpiPs_write_read1(0x169,0x55,&rx_data); +SpiPs_write_read1(0xA2D,0x02,&rx_data); +SpiPs_write_read1(0xA07,0x00,&rx_data); +SpiPs_write_read1(0xA08,0x0F,&rx_data); +SpiPs_write_read1(0xA1C,0x01,&rx_data); +SpiPs_write_read1(0xA16,0x2A,&rx_data); +SpiPs_write_read1(0xA17,0xAA,&rx_data); +SpiPs_write_read1(0xA18,0xAA,&rx_data); +SpiPs_write_read1(0xA19,0xAA,&rx_data); +SpiPs_write_read1(0xA1A,0xAA,&rx_data); +SpiPs_write_read1(0xA1B,0xAA,&rx_data); +SpiPs_write_read1(0x186,0x01,&rx_data); +SpiPs_write_read1(0x763,0x34,&rx_data); +SpiPs_write_read1(0x76D,0x34,&rx_data); +SpiPs_write_read1(0x101,0xEF,&rx_data); +SpiPs_write_read1(0x101,0xFF,&rx_data); +SpiPs_write_read1(0x800,0x00,&rx_data); +SpiPs_write_read1(0x81A,0xF7,&rx_data); +SpiPs_write_read1(0x81B,0x34,&rx_data); +SpiPs_write_read1(0x81C,0x33,&rx_data); +SpiPs_write_read1(0x81D,0x12,&rx_data); +SpiPs_write_read1(0x81E,0x00,&rx_data); +SpiPs_write_read1(0x81F,0xFF,&rx_data); +SpiPs_write_read1(0x820,0x00,&rx_data); +SpiPs_write_read1(0x821,0xFF,&rx_data); +SpiPs_write_read1(0x800,0x80,&rx_data); +SpiPs_write_read1(0x800,0x81,&rx_data); +//×îÉÙ0.1s +usleep(100000); +SpiPs_write_read1(0x77E,0x00,&rx_data); +SpiPs_write_read1(0x77E,0x24,&rx_data); +SpiPs_write_read1(0x11F,0x03,&rx_data); +SpiPs_write_read1(0x121,0x02,&rx_data); +SpiPs_write_read1(0x11B,0x01,&rx_data); +SpiPs_write_read1(0x11E,0x01,&rx_data); +SpiPs_write_read1(0x161,0x01,&rx_data); +SpiPs_write_read1(0x162,0x01,&rx_data); +SpiPs_write_read1(0x164,0x05,&rx_data); +SpiPs_write_read1(0x165,0x55,&rx_data); +SpiPs_write_read1(0x166,0x55,&rx_data); +SpiPs_write_read1(0x167,0x55,&rx_data); +SpiPs_write_read1(0x168,0x55,&rx_data); +SpiPs_write_read1(0x169,0x55,&rx_data); +SpiPs_write_read1(0xA2D,0x02,&rx_data); +SpiPs_write_read1(0xA07,0x00,&rx_data); +SpiPs_write_read1(0xA08,0x0F,&rx_data); +SpiPs_write_read1(0xA1C,0x01,&rx_data); +SpiPs_write_read1(0xA16,0x15,&rx_data); +SpiPs_write_read1(0xA17,0x55,&rx_data); +SpiPs_write_read1(0xA18,0x55,&rx_data); +SpiPs_write_read1(0xA19,0x55,&rx_data); +SpiPs_write_read1(0xA1A,0x55,&rx_data); +SpiPs_write_read1(0xA1B,0x55,&rx_data); +SpiPs_write_read1(0x81A,0xF7,&rx_data); +SpiPs_write_read1(0x81B,0x44,&rx_data); +SpiPs_write_read1(0x81C,0x33,&rx_data); +SpiPs_write_read1(0x81D,0x14,&rx_data); +SpiPs_write_read1(0x800,0x80,&rx_data); +SpiPs_write_read1(0x800,0x82,&rx_data); +//×îÉÙ0.1s +usleep(500000); +SpiPs_write_read1(0x77E,0x00,&rx_data); +//»ָ´У׼ǰÉèÖà +SpiPs_write_read1(0x000,0x02,&rx_data); +SpiPs_write_read1(0x02E,0x06,&rx_data); +SpiPs_write_read1(0x03D,0x1B,&rx_data); +SpiPs_write_read1(0x03E,0x39,&rx_data); +SpiPs_write_read1(0x03F,0x27,&rx_data); +SpiPs_write_read1(0x040,0xF8,&rx_data); +SpiPs_write_read1(0x109,0x11,&rx_data); +SpiPs_write_read1(0x10A,0x70,&rx_data); +SpiPs_write_read1(0x11B,0x01,&rx_data); +SpiPs_write_read1(0x11E,0x01,&rx_data); +SpiPs_write_read1(0x11F,0x33,&rx_data); +SpiPs_write_read1(0x128,0x10,&rx_data); +SpiPs_write_read1(0x161,0x01,&rx_data); +SpiPs_write_read1(0x162,0x01,&rx_data); +SpiPs_write_read1(0x164,0x05,&rx_data); +SpiPs_write_read1(0x165,0x55,&rx_data); +SpiPs_write_read1(0x166,0x55,&rx_data); +SpiPs_write_read1(0x167,0x55,&rx_data); +SpiPs_write_read1(0x168,0x55,&rx_data); +SpiPs_write_read1(0x169,0x55,&rx_data); +SpiPs_write_read1(0xA07,0x00,&rx_data); +SpiPs_write_read1(0xA08,0x00,&rx_data); +SpiPs_write_read1(0xA16,0x00,&rx_data); +SpiPs_write_read1(0xA17,0x00,&rx_data); +SpiPs_write_read1(0xA18,0x00,&rx_data); +SpiPs_write_read1(0xA19,0x00,&rx_data); +SpiPs_write_read1(0xA1A,0x00,&rx_data); +SpiPs_write_read1(0xA1B,0x00,&rx_data); +SpiPs_write_read1(0xA1C,0x00,&rx_data); +SpiPs_write_read1(0xA2D,0x00,&rx_data); +usleep(1000); +SpiPs_write_read2(0x615,0x00,&rx_data); +temp=rx_data; +SpiPs_write_read2(0x635,0x00,&rx_data); +temp=rx_data; +////ÅäÖÃTx NCO BIST ON +//SpiPs_write_read1(0x11B,0x01,&rx_data); +//SpiPs_write_read1(0x11E,0x01,&rx_data); +//SpiPs_write_read1(0x161,0x01,&rx_data); +//SpiPs_write_read1(0x162,0x01,&rx_data); +////ÅäÖÃTx NCOƵÂÊ 1MHz +//SpiPs_write_read1(0x164,0x05,&rx_data); +//SpiPs_write_read1(0x165,0x55,&rx_data); +//SpiPs_write_read1(0x166,0x55,&rx_data); +//SpiPs_write_read1(0x167,0x55,&rx_data); +//SpiPs_write_read1(0x168,0x55,&rx_data); +//SpiPs_write_read1(0x169,0x55,&rx_data); +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/cx9261a_710_config.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/cx9261a_710_config.c new file mode 100644 index 0000000..c8bb612 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/cx9261a_710_config.c @@ -0,0 +1,835 @@ +/* + * cx9261a_710_config.c + * + * Created on: 2024Äê3ÔÂ21ÈÕ + * Author: cxlab + */ + + +/* + * cx9261a_076_config.c + * + * Created on: 2023Äê11ÔÂ30ÈÕ + * Author: cxlab + */ + + +#include "xilinx_io.h" +#include "sleep.h" +void cx9261a_710_config(void){ +u8 rx_data; +u8 temp,r13D,r13E; + +//setPin(pin,value) +GpioSetPinDir(0,0x01); +GpioWritePin(0,1); +GpioSetPinDir(32,0x01); +GpioWritePin(32,1); +GpioSetPinDir(33,0x01); +GpioWritePin(33,1); +GpioSetPinDir(34,0x01); +GpioWritePin(34,0); +GpioSetPinDir(35,0x01); //tx:0-rx:1 +GpioWritePin(35,1); //tx:0-rx:1 +GpioSetPinDir(36,0x01); //fdd:0-tdd:1 +GpioWritePin(36,1); //fdd:0-tdd:1 +GpioSetPinDir(37,0x01); //1r1t:0-2r2t:1 +GpioWritePin(37,1); //1r1t:0-2r2t:1 +GpioSetPinDir(38,0x01); //dualport:0-singleport:1 +GpioWritePin(38,1); //dualport:0-singleport:1 +GpioSetPinDir(39,0x01); +GpioWritePin(39,1); +GpioSetPinDir(40,0x01); +GpioWritePin(40,1); +//ÅäÖÃоƬ +//³õʼ»¯оƬ +SpiPs_write_read1(0x000,0x02,&rx_data); +SpiPs_write_read1(0x773,0x53,&rx_data); +SpiPs_write_read1(0x774,0x86,&rx_data); +SpiPs_write_read1(0x781,0x6F,&rx_data); +SpiPs_write_read1(0x70B,0x0E,&rx_data); +SpiPs_write_read1(0x727,0x0E,&rx_data); +SpiPs_write_read1(0x743,0x0E,&rx_data); +SpiPs_write_read1(0x624,0x11,&rx_data); +SpiPs_write_read1(0x625,0x11,&rx_data); +SpiPs_write_read1(0x61E,0x30,&rx_data); +SpiPs_write_read1(0x61F,0x08,&rx_data); +SpiPs_write_read1(0x621,0x4D,&rx_data); +SpiPs_write_read1(0x622,0x19,&rx_data); +SpiPs_write_read1(0x627,0x10,&rx_data); +SpiPs_write_read1(0x646,0x11,&rx_data); +SpiPs_write_read1(0x647,0x11,&rx_data); +SpiPs_write_read1(0x640,0x30,&rx_data); +SpiPs_write_read1(0x641,0x08,&rx_data); +SpiPs_write_read1(0x643,0x4D,&rx_data); +SpiPs_write_read1(0x644,0x19,&rx_data); +SpiPs_write_read1(0x649,0x10,&rx_data); +SpiPs_write_read1(0x668,0x11,&rx_data); +SpiPs_write_read1(0x669,0x11,&rx_data); +SpiPs_write_read1(0x662,0x30,&rx_data); +SpiPs_write_read1(0x663,0x08,&rx_data); +SpiPs_write_read1(0x665,0x4D,&rx_data); +SpiPs_write_read1(0x666,0x19,&rx_data); +SpiPs_write_read1(0x66B,0x10,&rx_data); +SpiPs_write_read1(0x68A,0x11,&rx_data); +SpiPs_write_read1(0x68B,0x11,&rx_data); +SpiPs_write_read1(0x684,0x30,&rx_data); +SpiPs_write_read1(0x685,0x08,&rx_data); +SpiPs_write_read1(0x687,0x4D,&rx_data); +SpiPs_write_read1(0x688,0x19,&rx_data); +SpiPs_write_read1(0x68D,0x10,&rx_data); +SpiPs_write_read1(0x6AC,0x11,&rx_data); +SpiPs_write_read1(0x6AD,0x11,&rx_data); +SpiPs_write_read1(0x6A6,0x30,&rx_data); +SpiPs_write_read1(0x6A7,0x08,&rx_data); +SpiPs_write_read1(0x6A9,0x4D,&rx_data); +SpiPs_write_read1(0x6AA,0x19,&rx_data); +SpiPs_write_read1(0x6AF,0x10,&rx_data); +SpiPs_write_read1(0x705,0xFC,&rx_data); +SpiPs_write_read1(0x706,0x6D,&rx_data); +SpiPs_write_read1(0x707,0x91,&rx_data); +SpiPs_write_read1(0x708,0x04,&rx_data); +SpiPs_write_read1(0x709,0x94,&rx_data); +SpiPs_write_read1(0x70A,0xFC,&rx_data); +SpiPs_write_read1(0x70C,0xC0,&rx_data); +SpiPs_write_read1(0x721,0xFC,&rx_data); +SpiPs_write_read1(0x722,0x6D,&rx_data); +SpiPs_write_read1(0x723,0x91,&rx_data); +SpiPs_write_read1(0x724,0x04,&rx_data); +SpiPs_write_read1(0x725,0x94,&rx_data); +SpiPs_write_read1(0x726,0xFC,&rx_data); +SpiPs_write_read1(0x728,0xC0,&rx_data); +SpiPs_write_read1(0x73D,0xFC,&rx_data); +SpiPs_write_read1(0x73E,0x6D,&rx_data); +SpiPs_write_read1(0x73F,0x91,&rx_data); +SpiPs_write_read1(0x740,0x04,&rx_data); +SpiPs_write_read1(0x741,0x94,&rx_data); +SpiPs_write_read1(0x742,0xFC,&rx_data); +SpiPs_write_read1(0x744,0xC0,&rx_data); +SpiPs_write_read1(0x75A,0x83,&rx_data); +SpiPs_write_read1(0x75B,0xDA,&rx_data); +usleep(100000); +SpiPs_write_read1(0x75B,0x9A,&rx_data); +SpiPs_write_read1(0x75C,0x81,&rx_data); +SpiPs_write_read1(0x763,0x04,&rx_data); +SpiPs_write_read1(0x764,0x83,&rx_data); +SpiPs_write_read1(0x765,0xDA,&rx_data); +usleep(100000); +SpiPs_write_read1(0x765,0x9A,&rx_data); +SpiPs_write_read1(0x766,0x81,&rx_data); +SpiPs_write_read1(0x76D,0x04,&rx_data); +//³õʼ»¯µçԴ +SpiPs_write_read1(0x031,0x00,&rx_data); +SpiPs_write_read1(0x032,0x7F,&rx_data); +SpiPs_write_read1(0x033,0xFF,&rx_data); +SpiPs_write_read1(0x034,0xFF,&rx_data); +SpiPs_write_read1(0x035,0x00,&rx_data); +SpiPs_write_read1(0x036,0x3D,&rx_data); +SpiPs_write_read1(0x037,0xFF,&rx_data); +SpiPs_write_read1(0x038,0xFB,&rx_data); +SpiPs_write_read1(0x039,0x00,&rx_data); +SpiPs_write_read1(0x03A,0x3D,&rx_data); +SpiPs_write_read1(0x03B,0xFF,&rx_data); +SpiPs_write_read1(0x03C,0xF8,&rx_data); +SpiPs_write_read1(0x03D,0x1B,&rx_data); +SpiPs_write_read1(0x03E,0x39,&rx_data); +SpiPs_write_read1(0x03F,0x26,&rx_data); +SpiPs_write_read1(0x040,0x78,&rx_data); +SpiPs_write_read1(0x041,0x03,&rx_data); +SpiPs_write_read1(0x042,0x3D,&rx_data); +SpiPs_write_read1(0x043,0xE7,&rx_data); +SpiPs_write_read1(0x044,0xF8,&rx_data); +SpiPs_write_read1(0x045,0x18,&rx_data); +SpiPs_write_read1(0x046,0x3D,&rx_data); +SpiPs_write_read1(0x047,0x3F,&rx_data); +SpiPs_write_read1(0x048,0xF8,&rx_data); +SpiPs_write_read1(0x049,0x1F,&rx_data); +SpiPs_write_read1(0x04A,0x3D,&rx_data); +SpiPs_write_read1(0x04B,0x07,&rx_data); +SpiPs_write_read1(0x04C,0xF8,&rx_data); +SpiPs_write_read1(0x04D,0x04,&rx_data); +SpiPs_write_read1(0x04E,0x3D,&rx_data); +SpiPs_write_read1(0x04F,0xD9,&rx_data); +SpiPs_write_read1(0x050,0xFE,&rx_data); +//×Զ¨Òå½ÓÊÕģʽ +SpiPs_write_read1(0x041,0x03,&rx_data); +SpiPs_write_read1(0x042,0x3D,&rx_data); +SpiPs_write_read1(0x043,0xE7,&rx_data); +SpiPs_write_read1(0x044,0xF8,&rx_data); +//×Զ¨Ò巢Éäģʽ +SpiPs_write_read1(0x045,0x18,&rx_data); +SpiPs_write_read1(0x046,0x3D,&rx_data); +SpiPs_write_read1(0x047,0x3F,&rx_data); +SpiPs_write_read1(0x048,0xF8,&rx_data); +//×Զ¨ÒåFDDģʽ +SpiPs_write_read1(0x049,0x1B,&rx_data); +SpiPs_write_read1(0x04A,0x3D,&rx_data); +SpiPs_write_read1(0x04B,0x27,&rx_data); +SpiPs_write_read1(0x04C,0xF8,&rx_data); +//ÅäÖÃBBPLL +SpiPs_write_read1(0x789,0x10,&rx_data); +SpiPs_write_read1(0x785,0x49,&rx_data); +SpiPs_write_read1(0x788,0x24,&rx_data); +usleep(100000); +SpiPs_write_read1(0x788,0xA4,&rx_data); +SpiPs_write_read1(0x0C0,0x50,&rx_data); +SpiPs_write_read1(0x0C1,0x50,&rx_data); +SpiPs_write_read1(0x0C2,0x50,&rx_data); +SpiPs_write_read1(0x601,0x0F,&rx_data); +SpiPs_write_read1(0x602,0x21,&rx_data); +SpiPs_write_read1(0x60D,0x0D,&rx_data); +SpiPs_write_read1(0x60E,0xFF,&rx_data); +SpiPs_write_read1(0x60F,0xFF,&rx_data); +SpiPs_write_read1(0x610,0xFF,&rx_data); +SpiPs_write_read1(0x611,0x2F,&rx_data); +SpiPs_write_read1(0x612,0x00,&rx_data); +SpiPs_write_read1(0x70E,0x6B,&rx_data); +SpiPs_write_read1(0x72A,0x6B,&rx_data); +SpiPs_write_read1(0x746,0x6B,&rx_data); +SpiPs_write_read1(0x70F,0xE0,&rx_data); +SpiPs_write_read1(0x72B,0xE0,&rx_data); +SpiPs_write_read1(0x747,0xE0,&rx_data); +SpiPs_write_read1(0x70D,0x43,&rx_data); +SpiPs_write_read1(0x729,0x43,&rx_data); +SpiPs_write_read1(0x745,0x43,&rx_data); +SpiPs_write_read1(0x782,0x76,&rx_data); +SpiPs_write_read1(0x783,0x76,&rx_data); +SpiPs_write_read1(0x784,0x06,&rx_data); +//ÅäÖÃPLLÀ´Դ +SpiPs_write_read1(0x77D,0xCF,&rx_data); +SpiPs_write_read1(0x775,0x24,&rx_data); +SpiPs_write_read1(0x6C3,0x00,&rx_data); +//ÅäÖÃPLL1 +SpiPs_write_read1(0x620,0x91,&rx_data); +SpiPs_write_read1(0x61D,0x37,&rx_data); +SpiPs_write_read1(0xB06,0x00,&rx_data); +SpiPs_write_read1(0xB07,0xCB,&rx_data); +SpiPs_write_read1(0xB08,0x20,&rx_data); +SpiPs_write_read1(0xB09,0x00,&rx_data); +SpiPs_write_read1(0xB0A,0x00,&rx_data); +SpiPs_write_read1(0x61A,0x0C,&rx_data); +SpiPs_write_read1(0x61B,0x30,&rx_data); +SpiPs_write_read1(0x61C,0x08,&rx_data); +SpiPs_write_read1(0x62A,0x0B,&rx_data); +//ÅäÖÃPLL2 +SpiPs_write_read1(0x642,0x91,&rx_data); +SpiPs_write_read1(0x63F,0x35,&rx_data); +SpiPs_write_read1(0xB14,0x00,&rx_data); +SpiPs_write_read1(0xB15,0xFA,&rx_data); +SpiPs_write_read1(0xB16,0x00,&rx_data); +SpiPs_write_read1(0xB17,0x00,&rx_data); +SpiPs_write_read1(0xB18,0x00,&rx_data); +SpiPs_write_read1(0x63C,0x0D,&rx_data); +SpiPs_write_read1(0x63D,0x30,&rx_data); +SpiPs_write_read1(0x63E,0x0B,&rx_data); +SpiPs_write_read1(0x64C,0x00,&rx_data); +//ÅäÖÃPLL3 +SpiPs_write_read1(0x664,0x81,&rx_data); +SpiPs_write_read1(0x661,0x36,&rx_data); +SpiPs_write_read1(0xB22,0x00,&rx_data); +SpiPs_write_read1(0xB23,0xBB,&rx_data); +SpiPs_write_read1(0xB24,0x80,&rx_data); +SpiPs_write_read1(0xB25,0x00,&rx_data); +SpiPs_write_read1(0xB26,0x00,&rx_data); +SpiPs_write_read1(0x65E,0x0B,&rx_data); +SpiPs_write_read1(0x65F,0x30,&rx_data); +SpiPs_write_read1(0x660,0x0E,&rx_data); +SpiPs_write_read1(0x66E,0x0B,&rx_data); +//ÅäÖÃPLL4 +SpiPs_write_read1(0x686,0x91,&rx_data); +SpiPs_write_read1(0x683,0x36,&rx_data); +SpiPs_write_read1(0xB30,0x00,&rx_data); +SpiPs_write_read1(0xB31,0xDA,&rx_data); +SpiPs_write_read1(0xB32,0xC0,&rx_data); +SpiPs_write_read1(0xB33,0x00,&rx_data); +SpiPs_write_read1(0xB34,0x00,&rx_data); +SpiPs_write_read1(0x680,0x0C,&rx_data); +SpiPs_write_read1(0x681,0x30,&rx_data); +SpiPs_write_read1(0x682,0x08,&rx_data); +SpiPs_write_read1(0x690,0x0B,&rx_data); +//ÅäÖÃPLL5 +SpiPs_write_read1(0x6A8,0x81,&rx_data); +SpiPs_write_read1(0x6A5,0x36,&rx_data); +SpiPs_write_read1(0xB3E,0x00,&rx_data); +SpiPs_write_read1(0xB3F,0x9C,&rx_data); +SpiPs_write_read1(0xB40,0x40,&rx_data); +SpiPs_write_read1(0xB41,0x00,&rx_data); +SpiPs_write_read1(0xB42,0x00,&rx_data); +SpiPs_write_read1(0x6A2,0x0C,&rx_data); +SpiPs_write_read1(0x6A3,0x30,&rx_data); +SpiPs_write_read1(0x6A4,0x0C,&rx_data); +SpiPs_write_read1(0x6B2,0x0B,&rx_data); +//ÅäÖÃTx1ͨµÀƵÂÊÏà¹ؼĴæÆ÷ +SpiPs_write_read1(0x75F,0x55,&rx_data); +SpiPs_write_read1(0x761,0x04,&rx_data); +SpiPs_write_read1(0x762,0x70,&rx_data); +//ÅäÖÃTx1ͨµÀƵÂÊÏà¹ؼĴæÆ÷ +SpiPs_write_read1(0x769,0x55,&rx_data); +SpiPs_write_read1(0x76B,0x04,&rx_data); +SpiPs_write_read1(0x76C,0x70,&rx_data); +//ÅäÖÃRx1ͨµÀ +SpiPs_write_read1(0x700,0x56,&rx_data); +SpiPs_write_read1(0x701,0x00,&rx_data); +SpiPs_write_read1(0x702,0xDB,&rx_data); +SpiPs_write_read1(0x703,0x45,&rx_data); +SpiPs_write_read1(0x704,0xF8,&rx_data); +SpiPs_write_read1(0x907,0x1F,&rx_data); +//ÅäÖÃRx2ͨµÀ +SpiPs_write_read1(0x71C,0x56,&rx_data); +SpiPs_write_read1(0x71D,0x00,&rx_data); +SpiPs_write_read1(0x71E,0xDB,&rx_data); +SpiPs_write_read1(0x71F,0x45,&rx_data); +SpiPs_write_read1(0x720,0xF8,&rx_data); +SpiPs_write_read1(0x907,0x1F,&rx_data); +//ÅäÖÃRx3ͨµÀ +SpiPs_write_read1(0x738,0x56,&rx_data); +SpiPs_write_read1(0x739,0x00,&rx_data); +SpiPs_write_read1(0x73A,0xDB,&rx_data); +SpiPs_write_read1(0x73B,0x45,&rx_data); +SpiPs_write_read1(0x73C,0xF8,&rx_data); +SpiPs_write_read1(0x907,0x1F,&rx_data); +//ÅäÖÃTx1ͨµÀ +SpiPs_write_read1(0x75E,0xFF,&rx_data); +SpiPs_write_read1(0x75D,0x4A,&rx_data); +SpiPs_write_read1(0x75F,0x7D,&rx_data); +SpiPs_write_read1(0x760,0xFF,&rx_data); +//ÅäÖÃTx2ͨµÀ +SpiPs_write_read1(0x768,0xFF,&rx_data); +SpiPs_write_read1(0x767,0x4A,&rx_data); +SpiPs_write_read1(0x769,0x7D,&rx_data); +SpiPs_write_read1(0x76A,0xFF,&rx_data); +//ÅäÖÃRxÂ˲¨Æ÷ +SpiPs_write_read1(0x106,0x02,&rx_data); +//ÅäÖÃTxÂ˲¨Æ÷ +SpiPs_write_read1(0x107,0x00,&rx_data); +SpiPs_write_read1(0x108,0xD0,&rx_data); +//ÅäÖÃRx»ìƵÆ÷ +SpiPs_write_read1(0x11C,0x00,&rx_data); +SpiPs_write_read1(0xA07,0x00,&rx_data); +SpiPs_write_read1(0xA08,0x00,&rx_data); +SpiPs_write_read1(0xA09,0x00,&rx_data); +SpiPs_write_read1(0xA16,0x00,&rx_data); +SpiPs_write_read1(0xA17,0x00,&rx_data); +SpiPs_write_read1(0xA18,0x00,&rx_data); +SpiPs_write_read1(0xA19,0x00,&rx_data); +SpiPs_write_read1(0xA1A,0x00,&rx_data); +SpiPs_write_read1(0xA1B,0x00,&rx_data); +//ÅäÖÃTx»ìƵÆ÷ +SpiPs_write_read1(0x128,0x10,&rx_data); +SpiPs_write_read1(0x11B,0x00,&rx_data); +SpiPs_write_read1(0x11E,0x00,&rx_data); +SpiPs_write_read1(0x161,0x00,&rx_data); +SpiPs_write_read1(0x162,0x00,&rx_data); +SpiPs_write_read1(0x164,0x00,&rx_data); +SpiPs_write_read1(0x165,0x00,&rx_data); +SpiPs_write_read1(0x166,0x00,&rx_data); +SpiPs_write_read1(0x167,0x00,&rx_data); +SpiPs_write_read1(0x168,0x00,&rx_data); +SpiPs_write_read1(0x169,0x00,&rx_data); +//ÅäÖÃAGC +SpiPs_write_read1(0x903,0x11,&rx_data); +SpiPs_write_read1(0x900,0x00,&rx_data); +SpiPs_write_read1(0x901,0x00,&rx_data); +SpiPs_write_read1(0x908,0x77,&rx_data); +SpiPs_write_read1(0x909,0x03,&rx_data); +SpiPs_write_read1(0x90A,0x32,&rx_data); +SpiPs_write_read1(0x90D,0x00,&rx_data); +SpiPs_write_read1(0x90E,0x0F,&rx_data); +SpiPs_write_read1(0x90F,0x65,&rx_data); +SpiPs_write_read1(0x910,0x32,&rx_data); +SpiPs_write_read1(0x911,0x00,&rx_data); +SpiPs_write_read1(0x912,0x0F,&rx_data); +SpiPs_write_read1(0x913,0x3A,&rx_data); +SpiPs_write_read1(0x914,0x37,&rx_data); +SpiPs_write_read1(0x915,0x34,&rx_data); +SpiPs_write_read1(0x916,0x31,&rx_data); +//Çл»µ½ÐÝÃßģʽ +SpiPs_write_read1(0x02E,0x01,&rx_data); +usleep(1000000); +//Çл»µ½У׼ģʽ +SpiPs_write_read1(0x02E,0x03,&rx_data); +usleep(100000); +//BBPllËø¶¨ +SpiPs_write_read1(0x600,0x00,&rx_data); +SpiPs_write_read1(0x600,0x11,&rx_data); +usleep(100000); +//BBPllËø¶¨ ³ɹ¦ +//ADCУ׼ +SpiPs_write_read1(0x70B,0x1E,&rx_data); +SpiPs_write_read1(0x727,0x1E,&rx_data); +SpiPs_write_read1(0x743,0x1E,&rx_data); +SpiPs_write_read1(0x70F,0xE0,&rx_data); +SpiPs_write_read1(0x710,0x83,&rx_data); +SpiPs_write_read1(0x711,0xF0,&rx_data); +SpiPs_write_read1(0x72B,0xE0,&rx_data); +SpiPs_write_read1(0x72C,0x83,&rx_data); +SpiPs_write_read1(0x72D,0xF0,&rx_data); +SpiPs_write_read1(0x747,0xE0,&rx_data); +SpiPs_write_read1(0x748,0x83,&rx_data); +SpiPs_write_read1(0x749,0xF0,&rx_data); +SpiPs_write_read1(0x713,0x80,&rx_data); +SpiPs_write_read1(0x72F,0x80,&rx_data); +SpiPs_write_read1(0x74B,0x80,&rx_data); +SpiPs_write_read1(0x70B,0x9E,&rx_data); +SpiPs_write_read1(0x727,0x9E,&rx_data); +SpiPs_write_read1(0x743,0x9E,&rx_data); +SpiPs_write_read1(0x70B,0x9A,&rx_data); +SpiPs_write_read1(0x727,0x9A,&rx_data); +SpiPs_write_read1(0x743,0x9A,&rx_data); +SpiPs_write_read1(0x70B,0x9E,&rx_data); +SpiPs_write_read1(0x727,0x9E,&rx_data); +SpiPs_write_read1(0x743,0x9E,&rx_data); +usleep(100000); +SpiPs_write_read1(0x70B,0x1E,&rx_data); +SpiPs_write_read1(0x727,0x1E,&rx_data); +SpiPs_write_read1(0x743,0x1E,&rx_data); +SpiPs_write_read1(0x713,0xC0,&rx_data); +SpiPs_write_read1(0x72F,0xC0,&rx_data); +SpiPs_write_read1(0x74B,0xC0,&rx_data); +//flashУ׼ +SpiPs_write_read1(0x70B,0x1E,&rx_data); +SpiPs_write_read1(0x727,0x1E,&rx_data); +SpiPs_write_read1(0x743,0x1E,&rx_data); +SpiPs_write_read1(0x715,0x80,&rx_data); +SpiPs_write_read1(0x731,0x80,&rx_data); +SpiPs_write_read1(0x74D,0x80,&rx_data); +SpiPs_write_read1(0x715,0xC0,&rx_data); +SpiPs_write_read1(0x731,0xC0,&rx_data); +SpiPs_write_read1(0x74D,0xC0,&rx_data); +SpiPs_write_read1(0x716,0x40,&rx_data); +SpiPs_write_read1(0x732,0x40,&rx_data); +SpiPs_write_read1(0x74E,0x40,&rx_data); +SpiPs_write_read1(0x70B,0x16,&rx_data); +SpiPs_write_read1(0x727,0x16,&rx_data); +SpiPs_write_read1(0x743,0x16,&rx_data); +usleep(100000); +SpiPs_write_read1(0x70B,0x1E,&rx_data); +SpiPs_write_read1(0x727,0x1E,&rx_data); +SpiPs_write_read1(0x743,0x1E,&rx_data); +SpiPs_write_read1(0x715,0x80,&rx_data); +SpiPs_write_read1(0x731,0x80,&rx_data); +SpiPs_write_read1(0x74D,0x80,&rx_data); +SpiPs_write_read1(0x716,0x00,&rx_data); +SpiPs_write_read1(0x732,0x00,&rx_data); +SpiPs_write_read1(0x74E,0x00,&rx_data); +//DC Tracking +SpiPs_write_read1(0x109,0x11,&rx_data); +SpiPs_write_read1(0x10A,0x70,&rx_data); +SpiPs_write_read1(0x11F,0x33,&rx_data); +//Rx1 QECУ׼ +SpiPs_write_read1(0x02E,0x03,&rx_data); +SpiPs_write_read1(0x03F,0x26,&rx_data); +SpiPs_write_read1(0x040,0x78,&rx_data); +SpiPs_write_read1(0x77D,0x90,&rx_data); +SpiPs_write_read1(0x775,0x24,&rx_data); +SpiPs_write_read1(0x6C3,0x05,&rx_data); +SpiPs_write_read1(0x61D,0x27,&rx_data); +SpiPs_write_read1(0x617,0x00,&rx_data); +SpiPs_write_read1(0x617,0x03,&rx_data); +usleep(100000); +SpiPs_write_read1(0x61D,0x37,&rx_data); +SpiPs_write_read1(0x686,0x81,&rx_data); +SpiPs_write_read1(0x683,0x36,&rx_data); +SpiPs_write_read1(0xB30,0x00,&rx_data); +SpiPs_write_read1(0xB31,0x87,&rx_data); +SpiPs_write_read1(0xB32,0x04,&rx_data); +SpiPs_write_read1(0xB33,0x44,&rx_data); +SpiPs_write_read1(0xB34,0x44,&rx_data); +SpiPs_write_read1(0x680,0x0C,&rx_data); +SpiPs_write_read1(0x681,0x30,&rx_data); +SpiPs_write_read1(0x682,0x0C,&rx_data); +SpiPs_write_read1(0x690,0x0B,&rx_data); +SpiPs_write_read1(0x683,0x26,&rx_data); +SpiPs_write_read1(0x67D,0x00,&rx_data); +SpiPs_write_read1(0x67D,0x03,&rx_data); +usleep(100000); +SpiPs_write_read1(0x683,0x36,&rx_data); +SpiPs_write_read1(0x000,0x12,&rx_data); +SpiPs_write_read1(0x77E,0x50,&rx_data); +SpiPs_write_read1(0x11B,0x01,&rx_data); +SpiPs_write_read1(0x11E,0x01,&rx_data); +SpiPs_write_read1(0x164,0x00,&rx_data); +SpiPs_write_read1(0x165,0x00,&rx_data); +SpiPs_write_read1(0x166,0x00,&rx_data); +SpiPs_write_read1(0x167,0x00,&rx_data); +SpiPs_write_read1(0x168,0x00,&rx_data); +SpiPs_write_read1(0x169,0x00,&rx_data); +SpiPs_write_read1(0x161,0x00,&rx_data); +SpiPs_write_read1(0x162,0x00,&rx_data); +SpiPs_write_read1(0x162,0x01,&rx_data); +SpiPs_write_read1(0x161,0x01,&rx_data); +SpiPs_write_read1(0x706,0x65,&rx_data); +SpiPs_write_read1(0x6C3,0x00,&rx_data); +SpiPs_write_read1(0x75F,0x7D,&rx_data); +SpiPs_write_read1(0x11F,0x73,&rx_data); +SpiPs_write_read1(0x153,0x00,&rx_data); +SpiPs_write_read1(0x708,0x24,&rx_data); +SpiPs_write_read1(0x196,0x01,&rx_data); +SpiPs_write_read1(0x02A,0x12,&rx_data); + +do{ +usleep(100); +SpiPs_write_read2(0x14D,0x00,&rx_data); +temp=rx_data&0x10; +}while (temp != 16); + +SpiPs_write_read1(0x02A,0x00,&rx_data); +SpiPs_write_read1(0x196,0x11,&rx_data); +SpiPs_write_read2(0x13D,0x00,&rx_data); +r13D = rx_data; +SpiPs_write_read2(0x13E,0x00,&rx_data); +r13E = rx_data; + +unsigned long B = r13D * 256 + r13E; +signed long B_s = B - (B >= 32768) * 65536; +unsigned long B_abs = abs(B_s); +unsigned short B_b = (unsigned short)B_abs; +unsigned short B_delay = (B_b>>6) & 0x1ff; + +if(B_s > 0) +{ + SpiPs_write_read1(0x199, ((B_delay>>8)&0x1),&rx_data); + SpiPs_write_read1(0x19A, B_delay&0xff,&rx_data); + SpiPs_write_read1(0x19B, 0,&rx_data); + SpiPs_write_read1(0x19C, 0,&rx_data); +} +else +{ + SpiPs_write_read1(0x199, 0,&rx_data); + SpiPs_write_read1(0x19A, 0,&rx_data); + SpiPs_write_read1(0x19B, ((B_delay>>8)&0x1),&rx_data); + SpiPs_write_read1(0x19C, B_delay&0xff,&rx_data); +} + +SpiPs_write_read1(0x196,0x10,&rx_data); +SpiPs_write_read1(0x153,0x00,&rx_data); +SpiPs_write_read1(0x02A,0x12,&rx_data); + +do{ +usleep(100); +SpiPs_write_read2(0x14D,0x00,&rx_data); +temp=rx_data&0x10; +}while (temp != 16); + +SpiPs_write_read1(0x02A,0x00,&rx_data); +SpiPs_write_read1(0x77E,0x00,&rx_data); +SpiPs_write_read1(0x000,0x02,&rx_data); +//Rx2 QECУ׼ +SpiPs_write_read1(0x02E,0x03,&rx_data); +SpiPs_write_read1(0x03F,0x26,&rx_data); +SpiPs_write_read1(0x040,0x78,&rx_data); +SpiPs_write_read1(0x77D,0x60,&rx_data); +SpiPs_write_read1(0x775,0x24,&rx_data); +SpiPs_write_read1(0x6C3,0x0A,&rx_data); +SpiPs_write_read1(0x61D,0x27,&rx_data); +SpiPs_write_read1(0x617,0x00,&rx_data); +SpiPs_write_read1(0x617,0x03,&rx_data); +usleep(100000); +SpiPs_write_read1(0x61D,0x37,&rx_data); +SpiPs_write_read1(0x686,0x81,&rx_data); +SpiPs_write_read1(0x683,0x36,&rx_data); +SpiPs_write_read1(0xB30,0x00,&rx_data); +SpiPs_write_read1(0xB31,0x87,&rx_data); +SpiPs_write_read1(0xB32,0x04,&rx_data); +SpiPs_write_read1(0xB33,0x44,&rx_data); +SpiPs_write_read1(0xB34,0x44,&rx_data); +SpiPs_write_read1(0x680,0x0C,&rx_data); +SpiPs_write_read1(0x681,0x30,&rx_data); +SpiPs_write_read1(0x682,0x0C,&rx_data); +SpiPs_write_read1(0x690,0x0B,&rx_data); +SpiPs_write_read1(0x683,0x26,&rx_data); +SpiPs_write_read1(0x67D,0x00,&rx_data); +SpiPs_write_read1(0x67D,0x03,&rx_data); +usleep(100000); +SpiPs_write_read1(0x683,0x36,&rx_data); +SpiPs_write_read1(0x000,0x22,&rx_data); +SpiPs_write_read1(0x77E,0x0A,&rx_data); +SpiPs_write_read1(0x11B,0x01,&rx_data); +SpiPs_write_read1(0x11E,0x01,&rx_data); +SpiPs_write_read1(0x164,0x00,&rx_data); +SpiPs_write_read1(0x165,0x00,&rx_data); +SpiPs_write_read1(0x166,0x00,&rx_data); +SpiPs_write_read1(0x167,0x00,&rx_data); +SpiPs_write_read1(0x168,0x00,&rx_data); +SpiPs_write_read1(0x169,0x00,&rx_data); +SpiPs_write_read1(0x161,0x00,&rx_data); +SpiPs_write_read1(0x162,0x00,&rx_data); +SpiPs_write_read1(0x162,0x01,&rx_data); +SpiPs_write_read1(0x161,0x01,&rx_data); +SpiPs_write_read1(0x706,0x65,&rx_data); +SpiPs_write_read1(0x6C3,0x00,&rx_data); +SpiPs_write_read1(0x75F,0x7D,&rx_data); +SpiPs_write_read1(0x11F,0x73,&rx_data); +SpiPs_write_read1(0x153,0x00,&rx_data); +SpiPs_write_read1(0x724,0x24,&rx_data); +SpiPs_write_read1(0x196,0x01,&rx_data); +SpiPs_write_read1(0x02B,0x12,&rx_data); + +do{ +usleep(100); +SpiPs_write_read2(0x14D,0x00,&rx_data); +temp=rx_data&0x10; +}while (temp != 16); + +SpiPs_write_read1(0x02B,0x00,&rx_data); +SpiPs_write_read1(0x196,0x11,&rx_data); +SpiPs_write_read2(0x13D,0x00,&rx_data); +r13D = rx_data; +SpiPs_write_read2(0x13E,0x00,&rx_data); +r13E = rx_data; + +B = r13D * 256 + r13E; +B_s = B - (B >= 32768) * 65536; +B_abs = abs(B_s); +B_b = (unsigned short)B_abs; +B_delay = (B_b>>6) & 0x1ff; + +if(B_s > 0) +{ + SpiPs_write_read1(0x199, ((B_delay>>8)&0x1),&rx_data); + SpiPs_write_read1(0x19A, B_delay&0xff,&rx_data); + SpiPs_write_read1(0x19B, 0,&rx_data); + SpiPs_write_read1(0x19C, 0,&rx_data); +} +else +{ + SpiPs_write_read1(0x199, 0,&rx_data); + SpiPs_write_read1(0x19A, 0,&rx_data); + SpiPs_write_read1(0x19B, ((B_delay>>8)&0x1),&rx_data); + SpiPs_write_read1(0x19C, B_delay&0xff,&rx_data); +} + +SpiPs_write_read1(0x196,0x10,&rx_data); +SpiPs_write_read1(0x153,0x00,&rx_data); +SpiPs_write_read1(0x02B,0x12,&rx_data); + +do{ +usleep(100); +SpiPs_write_read2(0x14D,0x00,&rx_data); +temp=rx_data&0x10; +}while (temp != 16); + +SpiPs_write_read1(0x02B,0x00,&rx_data); +SpiPs_write_read1(0x77E,0x00,&rx_data); +SpiPs_write_read1(0x000,0x02,&rx_data); +//»ָ´У׼Öб»¸ıäµÄÅäÖà +SpiPs_write_read1(0x000,0x02,&rx_data); +SpiPs_write_read1(0x02E,0x03,&rx_data); +SpiPs_write_read1(0x109,0x11,&rx_data); +SpiPs_write_read1(0x10A,0x70,&rx_data); +SpiPs_write_read1(0x11B,0x00,&rx_data); +SpiPs_write_read1(0x11E,0x00,&rx_data); +SpiPs_write_read1(0x11F,0x33,&rx_data); +SpiPs_write_read1(0x128,0x10,&rx_data); +SpiPs_write_read1(0x161,0x00,&rx_data); +SpiPs_write_read1(0x162,0x00,&rx_data); +SpiPs_write_read1(0x164,0x00,&rx_data); +SpiPs_write_read1(0x165,0x00,&rx_data); +SpiPs_write_read1(0x166,0x00,&rx_data); +SpiPs_write_read1(0x167,0x00,&rx_data); +SpiPs_write_read1(0x168,0x00,&rx_data); +SpiPs_write_read1(0x169,0x00,&rx_data); +SpiPs_write_read1(0x6C3,0x00,&rx_data); +SpiPs_write_read1(0x775,0x24,&rx_data); +SpiPs_write_read1(0x77D,0xCF,&rx_data); +SpiPs_write_read1(0xA07,0x00,&rx_data); +SpiPs_write_read1(0xA08,0x00,&rx_data); +SpiPs_write_read1(0xA16,0x00,&rx_data); +SpiPs_write_read1(0xA17,0x00,&rx_data); +SpiPs_write_read1(0xA18,0x00,&rx_data); +SpiPs_write_read1(0xA19,0x00,&rx_data); +SpiPs_write_read1(0xA1A,0x00,&rx_data); +SpiPs_write_read1(0xA1B,0x00,&rx_data); +SpiPs_write_read1(0x686,0x91,&rx_data); +SpiPs_write_read1(0x683,0x36,&rx_data); +SpiPs_write_read1(0xB30,0x00,&rx_data); +SpiPs_write_read1(0xB31,0xDA,&rx_data); +SpiPs_write_read1(0xB32,0xC0,&rx_data); +SpiPs_write_read1(0xB33,0x00,&rx_data); +SpiPs_write_read1(0xB34,0x00,&rx_data); +SpiPs_write_read1(0x680,0x0C,&rx_data); +SpiPs_write_read1(0x681,0x30,&rx_data); +SpiPs_write_read1(0x682,0x08,&rx_data); +SpiPs_write_read1(0x690,0x0B,&rx_data); +//TXУ׼ +SpiPs_write_read1(0x03F,0x27,&rx_data); +SpiPs_write_read1(0x040,0xF8,&rx_data); +SpiPs_write_read1(0x61D,0x27,&rx_data); +SpiPs_write_read1(0x617,0x00,&rx_data); +SpiPs_write_read1(0x617,0x03,&rx_data); +usleep(100000); +SpiPs_write_read1(0x61D,0x37,&rx_data); +SpiPs_write_read1(0x02E,0x03,&rx_data); +SpiPs_write_read1(0x128,0x11,&rx_data); +SpiPs_write_read1(0x11E,0x01,&rx_data); +SpiPs_write_read1(0x77E,0x24,&rx_data); +SpiPs_write_read1(0x11F,0x01,&rx_data); +SpiPs_write_read1(0x121,0x02,&rx_data); +SpiPs_write_read1(0x11B,0x01,&rx_data); +SpiPs_write_read1(0x161,0x01,&rx_data); +SpiPs_write_read1(0x162,0x01,&rx_data); +SpiPs_write_read1(0x163,0x00,&rx_data); +SpiPs_write_read1(0x164,0x04,&rx_data); +SpiPs_write_read1(0x165,0x00,&rx_data); +SpiPs_write_read1(0x166,0x00,&rx_data); +SpiPs_write_read1(0x167,0x00,&rx_data); +SpiPs_write_read1(0x168,0x00,&rx_data); +SpiPs_write_read1(0x169,0x00,&rx_data); +SpiPs_write_read1(0xA2D,0x02,&rx_data); +SpiPs_write_read1(0xA07,0x00,&rx_data); +SpiPs_write_read1(0xA08,0x0F,&rx_data); +SpiPs_write_read1(0xA1C,0x01,&rx_data); +SpiPs_write_read1(0xA16,0x20,&rx_data); +SpiPs_write_read1(0xA17,0x00,&rx_data); +SpiPs_write_read1(0xA18,0x00,&rx_data); +SpiPs_write_read1(0xA19,0x00,&rx_data); +SpiPs_write_read1(0xA1A,0x00,&rx_data); +SpiPs_write_read1(0xA1B,0x00,&rx_data); +SpiPs_write_read1(0x102,0x0F,&rx_data); +SpiPs_write_read1(0x102,0xFF,&rx_data); +SpiPs_write_read1(0x186,0x00,&rx_data); +SpiPs_write_read1(0x186,0x01,&rx_data); +SpiPs_write_read1(0x763,0x34,&rx_data); +SpiPs_write_read1(0x76D,0x34,&rx_data); +SpiPs_write_read1(0x101,0xEF,&rx_data); +SpiPs_write_read1(0x101,0xFF,&rx_data); +SpiPs_write_read1(0x800,0x00,&rx_data); +SpiPs_write_read1(0x81A,0xF7,&rx_data); +SpiPs_write_read1(0x81B,0x34,&rx_data); +SpiPs_write_read1(0x81C,0x33,&rx_data); +SpiPs_write_read1(0x81D,0x12,&rx_data); +SpiPs_write_read1(0x81E,0x00,&rx_data); +SpiPs_write_read1(0x81F,0xFF,&rx_data); +SpiPs_write_read1(0x820,0x00,&rx_data); +SpiPs_write_read1(0x821,0xFF,&rx_data); +SpiPs_write_read1(0x800,0x80,&rx_data); +usleep(100000); +SpiPs_write_read1(0x800,0x81,&rx_data); +usleep(100000); +SpiPs_write_read1(0x77E,0x00,&rx_data); +SpiPs_write_read1(0x77E,0x24,&rx_data); +SpiPs_write_read1(0x11F,0x03,&rx_data); +SpiPs_write_read1(0x121,0x02,&rx_data); +SpiPs_write_read1(0x11B,0x01,&rx_data); +SpiPs_write_read1(0x11E,0x01,&rx_data); +SpiPs_write_read1(0x161,0x01,&rx_data); +SpiPs_write_read1(0x162,0x01,&rx_data); +SpiPs_write_read1(0x164,0x04,&rx_data); +SpiPs_write_read1(0x165,0x00,&rx_data); +SpiPs_write_read1(0x166,0x00,&rx_data); +SpiPs_write_read1(0x167,0x00,&rx_data); +SpiPs_write_read1(0x168,0x00,&rx_data); +SpiPs_write_read1(0x169,0x00,&rx_data); +SpiPs_write_read1(0xA2D,0x02,&rx_data); +SpiPs_write_read1(0xA07,0x00,&rx_data); +SpiPs_write_read1(0xA08,0x0F,&rx_data); +SpiPs_write_read1(0xA1C,0x01,&rx_data); +SpiPs_write_read1(0xA16,0x10,&rx_data); +SpiPs_write_read1(0xA17,0x00,&rx_data); +SpiPs_write_read1(0xA18,0x00,&rx_data); +SpiPs_write_read1(0xA19,0x00,&rx_data); +SpiPs_write_read1(0xA1A,0x00,&rx_data); +SpiPs_write_read1(0xA1B,0x00,&rx_data); +SpiPs_write_read1(0x81A,0xF7,&rx_data); +SpiPs_write_read1(0x81B,0x44,&rx_data); +SpiPs_write_read1(0x81C,0x33,&rx_data); +SpiPs_write_read1(0x81D,0x14,&rx_data); +SpiPs_write_read1(0x800,0x80,&rx_data); +SpiPs_write_read1(0x800,0x82,&rx_data); +usleep(100000); +SpiPs_write_read1(0x77E,0x00,&rx_data); +//»ָ´У׼Öб»¸ıäµÄÅäÖà +SpiPs_write_read1(0x000,0x02,&rx_data); +SpiPs_write_read1(0x02E,0x03,&rx_data); +SpiPs_write_read1(0x109,0x11,&rx_data); +SpiPs_write_read1(0x10A,0x70,&rx_data); +SpiPs_write_read1(0x11B,0x00,&rx_data); +SpiPs_write_read1(0x11E,0x00,&rx_data); +SpiPs_write_read1(0x11F,0x33,&rx_data); +SpiPs_write_read1(0x128,0x10,&rx_data); +SpiPs_write_read1(0x161,0x00,&rx_data); +SpiPs_write_read1(0x162,0x00,&rx_data); +SpiPs_write_read1(0x164,0x00,&rx_data); +SpiPs_write_read1(0x165,0x00,&rx_data); +SpiPs_write_read1(0x166,0x00,&rx_data); +SpiPs_write_read1(0x167,0x00,&rx_data); +SpiPs_write_read1(0x168,0x00,&rx_data); +SpiPs_write_read1(0x169,0x00,&rx_data); +SpiPs_write_read1(0x6C3,0x00,&rx_data); +SpiPs_write_read1(0x775,0x24,&rx_data); +SpiPs_write_read1(0x77D,0xCF,&rx_data); +SpiPs_write_read1(0xA07,0x00,&rx_data); +SpiPs_write_read1(0xA08,0x00,&rx_data); +SpiPs_write_read1(0xA16,0x00,&rx_data); +SpiPs_write_read1(0xA17,0x00,&rx_data); +SpiPs_write_read1(0xA18,0x00,&rx_data); +SpiPs_write_read1(0xA19,0x00,&rx_data); +SpiPs_write_read1(0xA1A,0x00,&rx_data); +SpiPs_write_read1(0xA1B,0x00,&rx_data); +//Pll1Ëø¶¨ +SpiPs_write_read1(0x61D,0x27,&rx_data); +SpiPs_write_read1(0x617,0x00,&rx_data); +SpiPs_write_read1(0x617,0x03,&rx_data); +usleep(100000); +SpiPs_write_read1(0x61D,0x37,&rx_data); +//Pll1Ëø¶¨ ³ɹ¦ +//Pll2Ëø¶¨ +SpiPs_write_read1(0x63F,0x25,&rx_data); +SpiPs_write_read1(0x639,0x00,&rx_data); +SpiPs_write_read1(0x639,0x03,&rx_data); +usleep(100000); +SpiPs_write_read1(0x63F,0x35,&rx_data); +//Pll2Ëø¶¨ ³ɹ¦ +//Pll3Ëø¶¨ +SpiPs_write_read1(0x661,0x26,&rx_data); +SpiPs_write_read1(0x65B,0x00,&rx_data); +SpiPs_write_read1(0x65B,0x03,&rx_data); +usleep(100000); +SpiPs_write_read1(0x661,0x36,&rx_data); +//Pll3Ëø¶¨ ³ɹ¦ +//Pll4Ëø¶¨ +SpiPs_write_read1(0x683,0x26,&rx_data); +SpiPs_write_read1(0x67D,0x00,&rx_data); +SpiPs_write_read1(0x67D,0x03,&rx_data); +usleep(100000); +SpiPs_write_read1(0x683,0x36,&rx_data); +//Pll4Ëø¶¨ ³ɹ¦ +//Pll5Ëø¶¨ +SpiPs_write_read1(0x6A5,0x26,&rx_data); +SpiPs_write_read1(0x69F,0x00,&rx_data); +SpiPs_write_read1(0x69F,0x03,&rx_data); +usleep(100000); +SpiPs_write_read1(0x6A5,0x36,&rx_data); +//Pll5Ëø¶¨ ³ɹ¦ +//ÅäÖýӿÚ +SpiPs_write_read1(0x02E,0x05,&rx_data); +SpiPs_write_read1(0x003,0x33,&rx_data); +SpiPs_write_read1(0x200,0x01,&rx_data); +SpiPs_write_read1(0x201,0x3F,&rx_data); +SpiPs_write_read1(0x202,0x03,&rx_data); +SpiPs_write_read1(0x206,0x00,&rx_data); +SpiPs_write_read1(0x208,0x00,&rx_data); +SpiPs_write_read1(0x209,0x00,&rx_data); +SpiPs_write_read1(0x77F,0x00,&rx_data); +SpiPs_write_read1(0x780,0x33,&rx_data); +SpiPs_write_read1(0x781,0x6F,&rx_data); +//ÅäÖýáÊø + +SpiPs_write_read1(0x02E,0x04,&rx_data); +SpiPs_write_read1(0x780,0x00,&rx_data); + +SpiPs_write_read1(0x11F,0x00,&rx_data); +SpiPs_write_read1(0x11F,0x33,&rx_data); +////ÅäÖÃTx NCO BIST ON +//SpiPs_write_read1(0x11B,0x01,&rx_data); +//SpiPs_write_read1(0x11E,0x01,&rx_data); +//SpiPs_write_read1(0x161,0x01,&rx_data); +//SpiPs_write_read1(0x162,0x01,&rx_data); +////ÅäÖÃTx NCOƵÂÊ 1MHz +//SpiPs_write_read1(0x164,0x05,&rx_data); +//SpiPs_write_read1(0x165,0x55,&rx_data); +//SpiPs_write_read1(0x166,0x55,&rx_data); +//SpiPs_write_read1(0x167,0x55,&rx_data); +//SpiPs_write_read1(0x168,0x55,&rx_data); +//SpiPs_write_read1(0x169,0x55,&rx_data); +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/download_bin.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/download_bin.c new file mode 100644 index 0000000..97b90e0 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/download_bin.c @@ -0,0 +1,80 @@ +/* + * download_bin.c + * + * Created on: 2023Äê11ÔÂ23ÈÕ + * Author: cxlab + */ + +#include "xilinx_io.h" +#include "sleep.h" +void download_bin(void){ + + int temp; + u8 rx_data; + u8 data[4]; + u32 buf; + u8 addr[2]; + int on = 1; + if (on) { + SpiPs_write_read1(0x079,0x00,&rx_data);//¹رÕCRCУÑé + SpiPs_write_read1(0x071,0x00,&rx_data); + SpiPs_write_read1(0x071,0x09,&rx_data); + // SpiPs_write_read1(0x236,0x0F,&rx_data); + // SpiPs_write_read1(0x20A,0x01,&rx_data); + for(int i=0;i<5507;i++){ + addr[0]=(i>>8)&0xff; + addr[1]= i&0xff; + buf=rom_read((u8 *)&addr); + data[0]=buf>>24; + data[1]=(buf>>16)&0xff; + data[2]=(buf>>8)&0xff; + data[3]=buf&0xff; + for(int j=0;j<4;j++){ + SpiPs_write_read1(0x070,data[j],&rx_data); + } + + } + + SpiPs_write_read1(0x071,0x1A,&rx_data);//¿ªÆôCRCУÑé + SpiPs_write_read1(0x079,0x08,&rx_data);//¿ªÆôCRCУÑé + usleep(1000); + SpiPs_write_read2(0x078,0x00,&rx_data); + temp=rx_data; + + if (temp == 0x01) { + xil_printf("CRC successful"); + } + else { + xil_printf("CRC unsuccessful"); + } + SpiPs_write_read1(0x071,0x06,&rx_data);//´¦ÀíÆ÷ÃſØʱÖÓʹÄÜ + usleep(10000); + SpiPs_write_read2(0x0A7,0x00,&rx_data);//´¦ÀíÆ÷ÔËÐгɹû±ê־ + temp=rx_data; + + if (temp == 0x11) { + xil_printf("core run successful"); + } + else { + xil_printf("core run unsuccessful"); + } +////////////////////dpdϵÊý¼ÓÔØ//////////////////// + SpiPs_write_read1(0x302,0xDF,&rx_data); + SpiPs_write_read1(0x0A3,0x52,&rx_data); + usleep(200000); + SpiPs_write_read2(0x0A7,0x00,&rx_data);//dpdϵÊý¼ÓÔØÍê³ɱê־ + temp=rx_data; + + if (temp == 0x61) { + xil_printf("dpd index load successful"); + } + else { + xil_printf("dpd index load unsuccessful"); + } + } + else { + SpiPs_write_read1(0x071,0x00,&rx_data); + } + + } + diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/echo.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/echo.c new file mode 100644 index 0000000..705e9e2 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/echo.c @@ -0,0 +1,461 @@ +/* + * Copyright (C) 2016 - 2018 Xilinx, Inc. + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + */ + +#include +#include + +#include "lwip/sockets.h" +#include "netif/xadapter.h" +#include "lwipopts.h" +#include "xil_printf.h" +#include "FreeRTOS.h" +#include "task.h" +#include "xilinx_io.h" + +#define THREAD_STACKSIZE 1024 +#define ACK 0x01 +u16_t echo_port = 7; +//#define RECV_BUF_SIZE 65536*16*8 +#define RECV_BUF_SIZE 1024 +//char recv_buf[RECV_BUF_SIZE]; +char recv_buf[1024]; +char recv_buf_temp[3]; +char trans_buf[8192*5]; +struct spi_stack spistack; + + +u16 hop_cnt = 0; + +void print_echo_app_header() +{ + xil_printf("%20s %6d %s\r\n", "echo server", + echo_port, + "$ telnet 7"); + +} + + +enum { + CMD_VER = 0, + CMD_RST = 1, + CMD_SPI_WRI = 2 , + CMD_SPI_READ = 3, + CMD_DATA_CAPTURE0 = 4, + CMD_WAVE_DOWNLOAD = 5, + CMD_PIN_SET = 6, + CMD_DATA_CAPTURE1 = 8, + CMD_DATA_CAPTURE2 = 9, + CMD_STACK_OUT = 10, + CMD_hopping = 11, + CMD_qecbin = 12, + CMD_cx9261a_config = 13, + CMD_cx3e04_config = 14, + CMD_cx3e04_SPI_WRI = 15 , + CMD_cx3e04_SPI_READ = 16, + CMD_IMAGE_UPDATE = 22, +}; +/* thread spawned for each connection */ +void process_echo_request(void *p) +{ + int sd = (int)p; + int status; + int n, nwrote,send_n,total_send; + u8 rx_data; + u32 capture_length; + u8 pin; + u8 pin_value; + char FPGA_SDK_IP[]="FPGA:20240513-SDK:20240514"; + spistack.spi_write_num = 0; + while (1) { + /* read a max of RECV_BUF_SIZE bytes from socket */ + if ((n = read(sd, recv_buf, 5)) < 0) { + xil_printf("%s: error reading from socket %d, closing socket\r\n", __FUNCTION__, sd); + break; + } + + /* break if the recved message = "quit" */ + if (!strncmp(recv_buf, "quit", 4)) + break; + + /* break if client closed connection */ + if (n <= 0) + break; + + else{/* excute cmd if recved message = "cmd"*/ + n=0; + if (!strncmp(recv_buf, "cmd ", 4)){ +// xil_printf("%d %d %d %d.\r\n", recv_buf[0],recv_buf[1],recv_buf[2],recv_buf[4]); + dbg_printf("Rece %d bytes.\r\n",n); + switch(recv_buf[4]){ + case CMD_IMAGE_UPDATE: + { + + u32 offset; + u32 image_size; + recv_buf[0] = ACK; + nwrote = write(sd, recv_buf, 1); + n = read(sd, recv_buf, RECV_BUF_SIZE); + + offset = *(u32 *)recv_buf; + image_size = *(u32 *)(recv_buf + 4); + xil_printf("offset is 0x%x image_size is 0x%x.\n", offset, image_size); + + void *image_ptr = (void *)0x30000000; + u32 LengthBytes; + write(sd, recv_buf, 1); // ask for image data + //// receive flash image to ddr + LengthBytes = 0; + while (LengthBytes < image_size) + { + n = read(sd, image_ptr + LengthBytes, RECV_BUF_SIZE); + // xil_printf("read %d bytes.\n",n); + LengthBytes = LengthBytes + n; + } + + // image data sumcheck + + // program image data into flash + //QspiStore( (u32) image_ptr, offset, image_size); + //xil_printf("flash program done.\n", n); + status = FfsSdWrite(image_ptr, image_size); + write(sd, recv_buf, 1); // send done + xil_printf("sd program done.\n", n); + n = 0; + break; + } + + case CMD_VER: + dbg_printf("Version:FPGA_20240513;SDK_20240514;IP_192.168.1.99.\r\n"); + n=26; + for(int i=0;i<26;i++){ + recv_buf[i] = FPGA_SDK_IP[i]; + } + break; + case CMD_RST: + dbg_printf("Rece cmd excute rst.\r\n"); + GpioSetPinDir(NRST,GPIO_DIR_OUTPUT); + DeviceRst(); + break; + + case CMD_STACK_OUT: + dbg_printf("Rece cmd STACK OUT.\r\n"); + spi_stack_out(&spistack); + n=1; + recv_buf[0] = 0x55; + break; + case CMD_SPI_WRI: + read(sd, &recv_buf[5], 3); + dbg_printf("Rece cmd excute spi write.\r\n"); + dbg_printf("ADDR 0x%x DATA 0x%x.\r\n",recv_buf[5]*256+recv_buf[6]-32768,recv_buf[7]); +// SpiPs_write_read((u8 *)&recv_buf[5],&rx_data); + status = spi_stack_in((u8 *)&recv_buf[5],&spistack); + if(status){//when memory is near full,start spi write + spi_stack_out(&spistack); + } +// n=1; +// recv_buf[0] = rx_data; + break; + case CMD_SPI_READ: + read(sd, &recv_buf[5], 3); + dbg_printf("Rece cmd excute spi read.\r\n"); + dbg_printf("ADDR 0x%x DATA 0x%x.\r\n",recv_buf[5]*256+recv_buf[6],recv_buf[7]); + SpiPs_write_read((u8 *)&recv_buf[5],&rx_data); + n=1; + recv_buf[0] = rx_data; + break; + case CMD_DATA_CAPTURE0: + read(sd, &recv_buf[5], 4); + capture_length = *((u32 *) &recv_buf[5]);//num of sample + dbg_printf("Rece cmd excute %d sample data capture.\r\n",capture_length); + XAxiDmaCapture(xAxiDma0Get(),capture_length); + n = capture_length*8; + total_send = 0; + /* handle request */ + while(n>0){ + if(n>1024) + send_n = 1024; + else + send_n = n; + n= n-send_n; + memcpy(recv_buf,TX_BUFFER_BASE + total_send,send_n); + total_send = total_send + send_n; + if ((nwrote = write(sd, &recv_buf, send_n)) < 0) { + xil_printf("%s: ERROR responding to client echo request. received = %d, written = %d\r\n",__FUNCTION__, n, nwrote); + xil_printf("Closing socket %d\r\n", sd); + break; + } + else{ + if(send_n!=nwrote){ + xil_printf("%d Bytes Send %d.\r\n", send_n,nwrote); + } + } + } + dbg_printf("Send %d bytes.\r\n",n); + break; + case CMD_DATA_CAPTURE1: + read(sd, &recv_buf[5], 4); + capture_length = *((u32 *) &recv_buf[5]);//num of sample + dbg_printf("Rece cmd excute %d sample data capture.\r\n",capture_length); + XAxiDmaCapture(xAxiDma1Get(),capture_length); + n = capture_length*8; + total_send = 0; + /* handle request */ + while(n>0){ + if(n>1024) + send_n = 1024; + else + send_n = n; + n= n-send_n; + + memcpy(recv_buf,TX_BUFFER_BASE + total_send,send_n); + total_send = total_send + send_n; + if ((nwrote = write(sd, &recv_buf, send_n)) < 0) { + xil_printf("%s: ERROR responding to client echo request. received = %d, written = %d\r\n",__FUNCTION__, n, nwrote); + xil_printf("Closing socket %d\r\n", sd); + break; + } + else{ + if(send_n!=nwrote){ + xil_printf("%d Bytes Send %d.\r\n", send_n,nwrote); + } + } + } + dbg_printf("Send %d bytes.\r\n",n); + break; + case CMD_DATA_CAPTURE2: + read(sd, &recv_buf[5], 4); + capture_length = *((u32 *) &recv_buf[5]);//num of sample + dbg_printf("Rece cmd excute %d sample data capture.\r\n",capture_length); + XAxiDmaCapture(xAxiDma2Get(),capture_length); + n = capture_length*8; + total_send = 0; + /* handle request */ + while(n>0){ + if(n>1024) + send_n = 1024; + else + send_n = n; + n= n-send_n; + memcpy(recv_buf,TX_BUFFER_BASE + total_send,send_n); + total_send = total_send + send_n; + if ((nwrote = write(sd, &recv_buf, send_n)) < 0) { + xil_printf("%s: ERROR responding to client echo request. received = %d, written = %d\r\n",__FUNCTION__, n, nwrote); + xil_printf("Closing socket %d\r\n", sd); + break; + } + else{ + if(send_n!=nwrote){ + xil_printf("%d Bytes Send %d.\r\n", send_n,nwrote); + } + } + } + dbg_printf("Send %d bytes.\r\n",n); + break; + case CMD_WAVE_DOWNLOAD: + dbg_printf("Rece cmd excute wave download.\r\n"); + while(n0){ + if(n>1024) + send_n = 1024; + else + send_n = n; + n= n-send_n; + total_send = total_send + send_n; + memcpy(recv_buf,TX_BUFFER_BASE + total_send,send_n); + if ((nwrote = write(sd, &recv_buf, send_n)) < 0) { + xil_printf("%s: ERROR responding to client echo request. received = %d, written = %d\r\n", + __FUNCTION__, n, nwrote); + xil_printf("Closing socket %d\r\n", sd); + break; + } + else{ + if(send_n!=nwrote){ + xil_printf("%d Bytes Send %d.\r\n", send_n,nwrote); + } + } + } + dbg_printf("Send %d bytes.\r\n",n); + }*/ + + } + } + /* handle request */ + if ((nwrote = write(sd, recv_buf, n)) < 0) { + xil_printf("%s: ERROR responding to client echo request. received = %d, written = %d\r\n", + __FUNCTION__, n, nwrote); + xil_printf("Closing socket %d\r\n", sd); + break; + } + else{ + if(n!=nwrote){ + xil_printf("%d Bytes Send %d.\r\n", n,nwrote); + } + } + } + + /* close connection */ + close(sd); + vTaskDelete(NULL); +} + +void echo_application_thread() +{ + int sock, new_sd; + int size; +#if LWIP_IPV6==0 + struct sockaddr_in address, remote; + + memset(&address, 0, sizeof(address)); + + if ((sock = lwip_socket(AF_INET, SOCK_STREAM, 0)) < 0) + return; + + address.sin_family = AF_INET; + address.sin_port = htons(echo_port); + address.sin_addr.s_addr = INADDR_ANY; +#else + struct sockaddr_in6 address, remote; + + memset(&address, 0, sizeof(address)); + + address.sin6_len = sizeof(address); + address.sin6_family = AF_INET6; + address.sin6_port = htons(echo_port); + + memset(&(address.sin6_addr), 0, sizeof(address.sin6_addr)); + + if ((sock = lwip_socket(AF_INET6, SOCK_STREAM, 0)) < 0) + return; +#endif + + if (lwip_bind(sock, (struct sockaddr *)&address, sizeof (address)) < 0) + return; + + lwip_listen(sock, 0); + + size = sizeof(remote); + + while (1) { + if ((new_sd = lwip_accept(sock, (struct sockaddr *)&remote, (socklen_t *)&size)) > 0) { + sys_thread_new("echos", process_echo_request, + (void*)new_sd, + THREAD_STACKSIZE, + DEFAULT_THREAD_PRIO); + } + } +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/frqhop_config.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/frqhop_config.c new file mode 100644 index 0000000..033cd78 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/frqhop_config.c @@ -0,0 +1,52 @@ +/* + * frqhop_config.c + * + * Created on: 2023Äê11ÔÂ24ÈÕ + * Author: cxlab + */ + +#include "xilinx_io.h" +#include "sleep.h" +#include "time.h" +#include "xtime_l.h" +#include "math.h" +#include + +void frqhop_config(int hop_flag){ + XTime tCur; + XTime_GetTime(&tCur); + srand(tCur); + double hop_freq = (rand()%(800-30))+30+(rand()%(101))/(double)100;//Ëæ»ú²úÉú[30,800)doubleÀàÐ͵ÄÊý +// double hop_freq = 400.52; + double hop_freq1 = 800.25; + static int pll_index=1; + static int unlock_pll1_cnt=0,unlock_pll2_cnt=0; + if(hop_freq == hop_freq1){ + if(pll_index == 2){ + pll_index = 4; + } + else if(pll_index == 1){ + pll_index = 3; + } + } + if(hop_flag == 1){ + if(pll_index == 1 || pll_index == 3){ + //Çл»ÖÁpll2À´Դ + trx_ch_pll_config(2); + //ÅäÖÃpll1²ÎÊý + unlock_pll1_cnt=PLL_config(1,hop_freq)-1; + pll_index = 2; + } + else if(pll_index == 2 || pll_index == 4){ + //Çл»ÖÁpll1À´Դ + trx_ch_pll_config(1); + //ÅäÖÃpll2²ÎÊý + unlock_pll2_cnt=PLL_config(2,hop_freq)-1; + pll_index = 1; + } + } + hop_freq1 = hop_freq; + if(unlock_pll2_cnt!=0 || unlock_pll1_cnt !=0){ + xil_printf("pll1 unlock num: %d; pll2 unlock num: %d;.\n", unlock_pll1_cnt, unlock_pll2_cnt); + } +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/iic_phyreset.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/iic_phyreset.c new file mode 100644 index 0000000..9d13181 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/iic_phyreset.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2016 - 2018 Xilinx, Inc. + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + */ + +#include + +#include "xparameters.h" +#if defined (__arm__) || defined(__aarch64__) +#include "xil_printf.h" +#endif + +#ifdef XPS_BOARD_ZCU102 +#ifdef XPAR_XIICPS_0_DEVICE_ID +#include "xiicps.h" + +#define BUF_LEN 10U + +#define IOEXPANDER1_ADDR 0x20U + +#define IIC_SCLK_RATE_IOEXP 400000 + +#define CMD_CFG_0_REG 0x06U +#define CMD_OUTPUT_0_REG 0x02U +#define DATA_OUTPUT 0x0U + +#define DATA_COMMON_CFG 0xE0U +#define DATA_GT_0000_CFG 0x00U + +XIicPs I2c0InstancePtr; + +int IicPhyReset(void) +{ + + u8 WriteBuffer[BUF_LEN] = {0}; + XIicPs_Config *I2c0CfgPtr; + int Status = XST_SUCCESS; + + /* Initialize the IIC0 driver so that it is ready to use */ + I2c0CfgPtr = XIicPs_LookupConfig(XPAR_XIICPS_0_DEVICE_ID); + if (I2c0CfgPtr == NULL) { + Status = XST_FAILURE; + return Status; + } + + Status = XIicPs_CfgInitialize(&I2c0InstancePtr, I2c0CfgPtr, + I2c0CfgPtr->BaseAddress); + if (Status != XST_SUCCESS) { + Status = XST_FAILURE; + return Status; + } + + /* Set the IIC serial clock rate */ + XIicPs_SetSClk(&I2c0InstancePtr, IIC_SCLK_RATE_IOEXP); + + /* Configure I/O pins as Output */ + WriteBuffer[0] = CMD_CFG_0_REG; + WriteBuffer[1] = DATA_OUTPUT; + Status = XIicPs_MasterSendPolled(&I2c0InstancePtr, + WriteBuffer, 2, IOEXPANDER1_ADDR); + if (Status != XST_SUCCESS) { + Status = XST_FAILURE; + return Status; + } + + /* Wait until bus is idle to start another transfer */ + while (XIicPs_BusIsBusy(&I2c0InstancePtr)); + + /* + * Deasserting I2C_MUX_RESETB + * And GEM3 Resetb + * Selecting lanes based on configuration + */ + WriteBuffer[0] = CMD_OUTPUT_0_REG; + /* gt0000 or no GT configuration */ + WriteBuffer[1] = DATA_COMMON_CFG | DATA_GT_0000_CFG; + + /* Send the Data */ + Status = XIicPs_MasterSendPolled(&I2c0InstancePtr, + WriteBuffer, 2, IOEXPANDER1_ADDR); + if (Status != XST_SUCCESS) { + Status = XST_FAILURE; + return Status; + } + + /* Wait until bus is idle */ + while (XIicPs_BusIsBusy(&I2c0InstancePtr)); + + xil_printf("IIC PHY reset on ZCU102 successful \n\r"); +} +#endif +#endif diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/main.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/main.c new file mode 100644 index 0000000..4b61222 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/main.c @@ -0,0 +1,300 @@ +/* + * Copyright (C) 2016 - 2018 Xilinx, Inc. + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + */ + +#include +#include "xparameters.h" +#include "netif/xadapter.h" +#include "platform_config.h" +#include "xil_printf.h" +#include "xilinx_io.h" +#include "FreeRTOS.h" +#include "queue.h" +#include "semphr.h" +#include "math.h" + +#if LWIP_IPV6==1 +#include "lwip/ip.h" +#else +#if LWIP_DHCP==1 +#include "lwip/dhcp.h" +#endif +#endif + +#ifdef XPS_BOARD_ZCU102 +#ifdef XPAR_XIICPS_0_DEVICE_ID +int IicPhyReset(void); +#endif +#endif + +int main_thread(); +void print_echo_app_header(); +void echo_application_thread(void *); + +void lwip_init(); + +#if LWIP_IPV6==0 +#if LWIP_DHCP==1 +extern volatile int dhcp_timoutcntr; +err_t dhcp_start(struct netif *netif); +#endif +#endif + +#define THREAD_STACKSIZE 1024 + +static struct netif server_netif; +struct netif *echo_netif; + +#if LWIP_IPV6==1 +void print_ip6(char *msg, ip_addr_t *ip) +{ + print(msg); + xil_printf(" %x:%x:%x:%x:%x:%x:%x:%x\n\r", + IP6_ADDR_BLOCK1(&ip->u_addr.ip6), + IP6_ADDR_BLOCK2(&ip->u_addr.ip6), + IP6_ADDR_BLOCK3(&ip->u_addr.ip6), + IP6_ADDR_BLOCK4(&ip->u_addr.ip6), + IP6_ADDR_BLOCK5(&ip->u_addr.ip6), + IP6_ADDR_BLOCK6(&ip->u_addr.ip6), + IP6_ADDR_BLOCK7(&ip->u_addr.ip6), + IP6_ADDR_BLOCK8(&ip->u_addr.ip6)); +} + +#else +void +print_ip(char *msg, ip_addr_t *ip) +{ + xil_printf(msg); + xil_printf("%d.%d.%d.%d\n\r", ip4_addr1(ip), ip4_addr2(ip), + ip4_addr3(ip), ip4_addr4(ip)); +} + +void +print_ip_settings(ip_addr_t *ip, ip_addr_t *mask, ip_addr_t *gw) +{ + + print_ip("Board IP: ", ip); + print_ip("Netmask : ", mask); + print_ip("Gateway : ", gw); +} + +#endif + +int main() +{ + xil_printf("start"); + + sys_thread_new("main_thrd", (void(*)(void*))main_thread, 0, + THREAD_STACKSIZE, + DEFAULT_THREAD_PRIO); + vTaskStartScheduler(); + + while(1); + + return 0; +} + +void network_thread(void *p) +{ + struct netif *netif; + /* the mac address of the board. this should be unique per board */ + unsigned char mac_ethernet_address[] = { 0x00, 0x0a, 0x35, 0x00, 0x01, 0x02 }; +#if LWIP_IPV6==0 + ip_addr_t ipaddr, netmask, gw; +#if LWIP_DHCP==1 + int mscnt = 0; +#endif +#endif + + netif = &server_netif; + + xil_printf("\r\n\r\n"); + xil_printf("-----lwIP Socket Mode CX9261 TEST server Application Version 2022.05.31------\r\n"); + +#if LWIP_IPV6==0 +#if LWIP_DHCP==0 + /* initliaze IP addresses to be used */ + IP4_ADDR(&ipaddr, 192, 168, 1, 99); + IP4_ADDR(&netmask, 255, 255, 255, 0); + IP4_ADDR(&gw, 192, 168, 1, 1); +#endif + + /* print out IP settings of the board */ + +#if LWIP_DHCP==0 + print_ip_settings(&ipaddr, &netmask, &gw); + /* print all application headers */ +#endif + +#if LWIP_DHCP==1 + ipaddr.addr = 0; + gw.addr = 0; + netmask.addr = 0; +#endif +#endif + +#if LWIP_IPV6==0 + /* Add network interface to the netif_list, and set it as default */ + if (!xemac_add(netif, &ipaddr, &netmask, &gw, mac_ethernet_address, PLATFORM_EMAC_BASEADDR)) { + xil_printf("Error adding N/W interface\r\n"); + return; + } +#else + /* Add network interface to the netif_list, and set it as default */ + if (!xemac_add(netif, NULL, NULL, NULL, mac_ethernet_address, PLATFORM_EMAC_BASEADDR)) { + xil_printf("Error adding N/W interface\r\n"); + return; + } + + netif->ip6_autoconfig_enabled = 1; + + netif_create_ip6_linklocal_address(netif, 1); + netif_ip6_addr_set_state(netif, 0, IP6_ADDR_VALID); + + print_ip6("\n\rBoard IPv6 address ", &netif->ip6_addr[0].u_addr.ip6); +#endif + + netif_set_default(netif); + + /* specify that the network if is up */ + netif_set_up(netif); + + /* start packet receive thread - required for lwIP operation */ + sys_thread_new("xemacif_input_thread", (void(*)(void*))xemacif_input_thread, netif, + THREAD_STACKSIZE, + DEFAULT_THREAD_PRIO); + +#if LWIP_IPV6==0 +#if LWIP_DHCP==1 + dhcp_start(netif); + while (1) { + vTaskDelay(DHCP_FINE_TIMER_MSECS / portTICK_RATE_MS); + dhcp_fine_tmr(); + mscnt += DHCP_FINE_TIMER_MSECS; + if (mscnt >= DHCP_COARSE_TIMER_SECS*1000) { + dhcp_coarse_tmr(); + mscnt = 0; + } + } +#else + xil_printf("\r\n"); + xil_printf("%20s %6s %s\r\n", "Server", "Port", "Connect With.."); + xil_printf("%20s %6s %s\r\n", "--------------------", "------", "--------------------"); + + print_echo_app_header(); + xil_printf("\r\n"); + sys_thread_new("echod", echo_application_thread, 0, + THREAD_STACKSIZE, + DEFAULT_THREAD_PRIO); + vTaskDelete(NULL); +#endif +#else + print_echo_app_header(); + xil_printf("\r\n"); + sys_thread_new("echod",echo_application_thread, 0, + THREAD_STACKSIZE, + DEFAULT_THREAD_PRIO); + vTaskDelete(NULL); +#endif + return; +} + +int main_thread() +{ +#if LWIP_DHCP==1 + int mscnt = 0; +#endif + +#ifdef XPS_BOARD_ZCU102 + IicPhyReset(); +#endif + + /* initialize lwIP before calling sys_thread_new */ + lwip_init(); + SpiPsInit(SPI_DEVICE_ID); + GpioInit(GPIO_DEVICE_ID); + AXIGpioInit(); + XScuGicInit(); + ConfigAXIGPIOIntr(); + ExceptionInit(); + + XAxiDma_Init(xAxiDma0Get(),AXI_DMA_DEVCIE_ID0); + XAxiDma_Init(xAxiDma1Get(),AXI_DMA_DEVCIE_ID1); + XAxiDma_Init(xAxiDma2Get(),AXI_DMA_DEVCIE_ID2); + PackerRst(); + + /* cx9261a configuration */ + peripheral_test(); + cx3e04_config(); +// cx9261a_710_config(); +// frqhop_config(1); + int temp = cx3e04_read(0x03A); + /* any thread using lwIP should be created using sys_thread_new */ + sys_thread_new("NW_THRD", network_thread, NULL, + THREAD_STACKSIZE, + DEFAULT_THREAD_PRIO); + +#if LWIP_IPV6==0 +#if LWIP_DHCP==1 + while (1) { + vTaskDelay(DHCP_FINE_TIMER_MSECS / portTICK_RATE_MS); + if (server_netif.ip_addr.addr) { + xil_printf("DHCP request success\r\n"); + print_ip_settings(&(server_netif.ip_addr), &(server_netif.netmask), &(server_netif.gw)); + print_echo_app_header(); + xil_printf("\r\n"); + sys_thread_new("echod", echo_application_thread, 0, + THREAD_STACKSIZE, + DEFAULT_THREAD_PRIO); + break; + } + mscnt += DHCP_FINE_TIMER_MSECS; + if (mscnt >= 10000) { + xil_printf("ERROR: DHCP request timed out\r\n"); + xil_printf("Configuring default IP of 192.168.1.100\r\n"); + IP4_ADDR(&(server_netif.ip_addr), 192, 168, 1, 100); + IP4_ADDR(&(server_netif.netmask), 255, 255, 255, 0); + IP4_ADDR(&(server_netif.gw), 192, 168, 1, 1); + print_ip_settings(&(server_netif.ip_addr), &(server_netif.netmask), &(server_netif.gw)); + /* print all application headers */ + xil_printf("\r\n"); + xil_printf("%20s %6s %s\r\n", "Server", "Port", "Connect With.."); + xil_printf("%20s %6s %s\r\n", "--------------------", "------", "--------------------"); + + print_echo_app_header(); + xil_printf("\r\n"); + sys_thread_new("echod", echo_application_thread, 0, + THREAD_STACKSIZE, + DEFAULT_THREAD_PRIO); + break; + } + } +#endif +#endif + vTaskDelete(NULL); + return 0; +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/platform_config.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/platform_config.h new file mode 100644 index 0000000..b84bf38 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/platform_config.h @@ -0,0 +1,10 @@ +#ifndef __PLATFORM_CONFIG_H_ +#define __PLATFORM_CONFIG_H_ + + +#define PLATFORM_EMAC_BASEADDR XPAR_XEMACPS_0_BASEADDR + +#define PLATFORM_ZYNQ + + +#endif diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/rx_wideband_qec.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/rx_wideband_qec.c new file mode 100644 index 0000000..ec8b2f5 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/rx_wideband_qec.c @@ -0,0 +1,61 @@ +/* + * wideband_qec.c + * + * Created on: 2023Äê11ÔÂ30ÈÕ + * Author: cxlab + */ + +#include "xilinx_io.h" +#include "sleep.h" +#include "math.h" +void rxFFTCalib(int lo, int fs,int ref, int firLength, int chan) { + u8 rx_data; + SpiPs_write_read1(0x000, chan == 1 ? 0x12 : 0x22,&rx_data); + SpiPs_write_read1(0x128, 0x00,&rx_data); + SpiPs_write_read1(0x11B, 0x01,&rx_data); + SpiPs_write_read1(0x11E, 0x01,&rx_data); + configTxNcoFreq(0, fs); + SpiPs_write_read1(0x161, 0x00,&rx_data); + SpiPs_write_read1(0x162, 0x00,&rx_data); + SpiPs_write_read1(0x162, 0x01,&rx_data); + SpiPs_write_read1(0x161, 0x01,&rx_data); + SpiPs_write_read1(0x000, chan == 1 ? 0x12 : chan == 2 ? 0x22 : 0x32,&rx_data); + SpiPs_write_read1(0x102, 0x03,&rx_data); + SpiPs_write_read1(0x102, 0xFF,&rx_data); + SpiPs_write_read1(0x15C, 0x00,&rx_data); + SpiPs_write_read1(0x160, 0x01,&rx_data); + SpiPs_write_read1(0x77E, chan == 1 ? 0x50 : chan == 2 ? 0x0A : 0x03,&rx_data); + SpiPs_write_read1(0x09B, 0x01,&rx_data); // TXPLL4 + SpiPs_write_read1(0x09D, firLength,&rx_data); + SpiPs_write_read1(0x096, ((u32)round(lo) >> 8) & 0xFF,&rx_data); + SpiPs_write_read1(0x097, (u32)round(lo) & 0xFF,&rx_data); + SpiPs_write_read1(0x098, ((u32)round(ref * 10) >> 8) & 0xFF,&rx_data); + SpiPs_write_read1(0x099, (u32)round(ref * 10) & 0xFF,&rx_data); + SpiPs_write_read1(0x09E, ((u32)round(fs * 100) >> 8) & 0xFF,&rx_data); + SpiPs_write_read1(0x09F, (u32)round(fs * 100) & 0xFF,&rx_data); + SpiPs_write_read1(0x09C, chan,&rx_data); + while (1) { + if (SpiPs_write_read2(0x0B3,0x00,&rx_data) == 5) + break; + usleep(10000); + } + SpiPs_write_read1(0x09C, 0x00,&rx_data); + SpiPs_write_read1(0x15C, 0x01,&rx_data); + SpiPs_write_read1(0x77E, 0x00,&rx_data); + + //ͨµÀʱÖÓÀ´Դ + u8 r77D=0xC0; + u8 r775=0x24; + u8 r6C3=0x00; + SpiPs_write_read1(0x77D, r77D,&rx_data); + SpiPs_write_read1(0x775, r775,&rx_data); + SpiPs_write_read1(0x6C3, r6C3,&rx_data); +} + + +void configDcCalib(int dcCalibEnable,int dcCalibLength) { + u8 rx_data; + SpiPs_write_read1(0x109, dcCalibEnable ? 0x11 : 0x00,&rx_data); + SpiPs_write_read1(0x10A, (dcCalibLength) << 4,&rx_data); + SpiPs_write_read1(0x11F, dcCalibEnable ? 0x33 : 0x73,&rx_data); +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/sd.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/sd.c new file mode 100644 index 0000000..111123b --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/sd.c @@ -0,0 +1,178 @@ +#include "sd.h" +/************************** Variable Definitions *****************************/ +static FIL fil; /* File object */ +static FATFS fatfs; +/* + * To test logical drive 0, FileName should be "0:/" or + * "". For logical drive 1, FileName should be "1:/" + */ +static char FileName[32] = "BOOT.bin"; +static char *SD_File; + + +//#define BUFF_SIZE sizeof(image_data) +#define BUFF_SIZE 0x2000000 +#ifdef __ICCARM__ +#pragma data_alignment = 32 +u8 DestinationAddress[10*1024]; +#pragma data_alignment = 32 +u8 SourceAddress[10*1024]; +#else +//u8 DestinationAddress[10*1024] __attribute__ ((aligned(32))); +//u8 SourceAddress[10*1024] __attribute__ ((aligned(32))); + +u8 DestinationAddress[BUFF_SIZE] __attribute__ ((aligned(32))); +u8 SourceAddress[BUFF_SIZE] __attribute__ ((aligned(32))); +#endif + +#define TEST 7 + + + +/*****************************************************************************/ +/** +* +* File system example using SD driver to write to and read from an SD card +* in polled mode. This example creates a new file on an +* SD card (which is previously formatted with FATFS), write data to the file +* and reads the same data back to verify. +* +* @param None +* +* @return XST_SUCCESS if successful, otherwise XST_FAILURE. +* +* @note None +* +******************************************************************************/ +int FfsSdWrite(u8*image_data, u32 FileSize) +{ + int error_flag = 0; + FRESULT Res; + UINT NumBytesRead; + UINT NumBytesWritten; + u32 BuffCnt; + BYTE work[FF_MAX_SS]; +// u32 FileSize = (8*1024); +// u32 FileSize = sizeof(image_data); + /* + * To test logical drive 0, Path should be "0:/" + * For logical drive 1, Path should be "1:/" + */ + TCHAR *Path = "0:/"; + u8 checksum = 0; + + for(BuffCnt = 0; BuffCnt < FileSize; BuffCnt++){ +// SourceAddress[BuffCnt] = TEST + BuffCnt; + SourceAddress[BuffCnt] = image_data[BuffCnt]; + checksum = checksum + image_data[BuffCnt]; + } + xil_printf("checksum is 0x%x.",checksum); + + + /* + * Register volume work area, initialize device + */ + Res = f_mount(&fatfs, Path, 0); + + if (Res != FR_OK) { + return XST_FAILURE; + } + + /* + * Path - Path to logical driver, 0 - FDISK format. + * 0 - Cluster size is automatically determined based on Vol size. + */ + Res = f_mkfs(Path, FM_FAT32, 0, work, sizeof work); + if (Res != FR_OK) { + return XST_FAILURE; + } + + /* + * Open file with required permissions. + * Here - Creating new file with read/write permissions. . + * To open file with write permissions, file system should not + * be in Read Only mode. + */ + SD_File = (char *)FileName; + + Res = f_open(&fil, SD_File, FA_CREATE_ALWAYS | FA_WRITE | FA_READ); + if (Res) { + return XST_FAILURE; + } + + /* + * Pointer to beginning of file . + */ + Res = f_lseek(&fil, 0); + if (Res) { + return XST_FAILURE; + } + + /* + * Write data to file. + */ + Res = f_write(&fil, (const void*)SourceAddress, FileSize, + &NumBytesWritten); + if (Res) { + return XST_FAILURE; + } + /* + * Close file. + */ + Res = f_close(&fil); + if (Res) { + return XST_FAILURE; + } + Res = f_open(&fil, SD_File, FA_READ); + if (Res) { + return XST_FAILURE; + } + /* + * Pointer to beginning of file . + */ + Res = f_lseek(&fil, 0); + if (Res) { + return XST_FAILURE; + } + + /* + * Read data from file. + */ + Res = f_read(&fil, (void*)DestinationAddress, FileSize, + &NumBytesRead); + if (Res) { + return XST_FAILURE; + } + + /* + * Data verification + */ + for(BuffCnt = 0; BuffCnt < FileSize; BuffCnt++){ + if(SourceAddress[BuffCnt] != DestinationAddress[BuffCnt]){ +// return XST_FAILURE; + error_flag = 1; + xil_printf('Data check error.'); + } + } + + /* + * Close file. + */ + Res = f_close(&fil); + if (Res) { + return XST_FAILURE; + } + + if(error_flag){ + return XST_FAILURE; + } + +// Res = f_unmount(Path); +// if (Res) { +// return XST_FAILURE; +// } +// +// sleep(5); + + return XST_SUCCESS; +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/sd.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/sd.h new file mode 100644 index 0000000..91e744c --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/sd.h @@ -0,0 +1,9 @@ +/***************************** Include Files *********************************/ +#include "xparameters.h" /* SDK generated parameters */ +#include "xsdps.h" /* SD device driver */ +#include "xil_printf.h" +#include "ff.h" +#include "xil_cache.h" +#include "xplatform_info.h" +/************************** Function Prototypes ******************************/ +int FfsSdWrite(u8*image_data, u32 FileSize); diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/tx_bisection.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/tx_bisection.c new file mode 100644 index 0000000..fd031b9 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/tx_bisection.c @@ -0,0 +1,74 @@ +/* + * tx_bisection.c + * + * Created on: 2023Äê12ÔÂ1ÈÕ + * Author: cxlab + */ + + +#include "xilinx_io.h" +#include "sleep.h" +#include "math.h" + +void txBisectionQecCalib(int f0, int fs) { + u8 rx_data; + SpiPs_write_read1(0x02E, 0x03,&rx_data); + SpiPs_write_read1(0x128, 0x11,&rx_data); + SpiPs_write_read1(0x11E, 0x01,&rx_data); + SpiPs_write_read1(0x77E, 0x24,&rx_data); + SpiPs_write_read1(0x11F, 0x01,&rx_data); + SpiPs_write_read1(0x121, 0x02,&rx_data); + SpiPs_write_read1(0x11B, 0x01,&rx_data); + SpiPs_write_read1(0x161, 0x01,&rx_data); + SpiPs_write_read1(0x162, 0x01,&rx_data); + SpiPs_write_read1(0x163, 0x00,&rx_data); + configTxNcoFreq(f0, fs * 4); + SpiPs_write_read1(0xA2D, 0x02,&rx_data); + SpiPs_write_read1(0xA07, 0x00,&rx_data); + SpiPs_write_read1(0xA08, 0x0F,&rx_data); + SpiPs_write_read1(0xA1C, 0x01,&rx_data); + configRxNcoFreq(f0 * 2, fs); + SpiPs_write_read1(0x186, 0x01,&rx_data); + SpiPs_write_read1(0x763, 0x34,&rx_data); + SpiPs_write_read1(0x76D, 0x34,&rx_data); + SpiPs_write_read1(0x101, 0xEF,&rx_data); + SpiPs_write_read1(0x101, 0xFF,&rx_data); + SpiPs_write_read1(0x800, 0x00,&rx_data); + SpiPs_write_read1(0x81A, 0xF7,&rx_data); + SpiPs_write_read1(0x81B, 0x34,&rx_data); + SpiPs_write_read1(0x81C, 0x33,&rx_data); + SpiPs_write_read1(0x81D, 0x12,&rx_data); + SpiPs_write_read1(0x81E, 0x00,&rx_data); + SpiPs_write_read1(0x81F, 0xFF,&rx_data); + SpiPs_write_read1(0x820, 0x00,&rx_data); + SpiPs_write_read1(0x821, 0xFF,&rx_data); + SpiPs_write_read1(0x800, 0x80,&rx_data); + SpiPs_write_read1(0x800, 0x81,&rx_data); + usleep(100000); + SpiPs_write_read1(0x77E, 0x00,&rx_data); +} + +void txBisectionDcCalib(int f0, int fs) { + u8 rx_data; + SpiPs_write_read1(0x77E, 0x24,&rx_data); + SpiPs_write_read1(0x11F, 0x03,&rx_data); + SpiPs_write_read1(0x121, 0x02,&rx_data); + SpiPs_write_read1(0x11B, 0x01,&rx_data); + SpiPs_write_read1(0x11E, 0x01,&rx_data); + SpiPs_write_read1(0x161, 0x01,&rx_data); + SpiPs_write_read1(0x162, 0x01,&rx_data); + configTxNcoFreq(f0, fs * 4); + SpiPs_write_read1(0xA2D, 0x02,&rx_data); + SpiPs_write_read1(0xA07, 0x00,&rx_data); + SpiPs_write_read1(0xA08, 0x0F,&rx_data); + SpiPs_write_read1(0xA1C, 0x01,&rx_data); + configRxNcoFreq(f0, fs); + SpiPs_write_read1(0x81A, 0xF7,&rx_data); + SpiPs_write_read1(0x81B, 0x44,&rx_data); + SpiPs_write_read1(0x81C, 0x33,&rx_data); + SpiPs_write_read1(0x81D, 0x14,&rx_data); + SpiPs_write_read1(0x800, 0x80,&rx_data); + SpiPs_write_read1(0x800, 0x82,&rx_data); + usleep(500000); + SpiPs_write_read1(0x77E, 0x00,&rx_data); +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/xilinx_io.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/xilinx_io.c new file mode 100644 index 0000000..69b4d8a --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/xilinx_io.c @@ -0,0 +1,905 @@ +/* + * xilinx_io.c + * + * Created on: 2021Äê9ÔÂ6ÈÕ + * Author: cxlab + */ + + +#include "sleep.h" +#include "math.h" +#include "xilinx_io.h" /* SPI device driver */ + +static XSpiPs SpiInstance; +XGpioPs Gpio; /* The driver instance for GPIO Device. */ +XGpio axi_gpio_inst; //PL ¶Ë AXI GPIO Çý¶¯ʵÀý +XScuGic scugic_inst; //ÖжϿØÖÆÆ÷ Çý¶¯ʵÀý +XScuGic_Config * scugic_cfg_ptr; //ÖжϿØÖÆÆ÷ ÅäÖÃÐÅϢ +int hop_flag,hop_intr_flag; +static int spi_select_value=0; + +XAxiDma AxiDma0,AxiDma1,AxiDma2; + +XAxiDma* xAxiDma0Get(void){ + return &AxiDma0; +} + +XAxiDma* xAxiDma1Get(void){ + return &AxiDma1; +} + +XAxiDma* xAxiDma2Get(void){ + return &AxiDma2; +} +void AXIGpioInit(void){ + XGpio_Initialize(&axi_gpio_inst, AXI_GPIO_ID); + //ÅäÖà PL AXI GPIO + XGpio_SetDataDirection(&axi_gpio_inst, 1, 1); //ÉèÖà AXI GPIO ͨµÀ 1 ΪÊäÈë + XGpio_InterruptEnable(&axi_gpio_inst, KEY_MASK); //ʹÄÜͨµÀ 1 ÖжÏ + XGpio_InterruptGlobalEnable(&axi_gpio_inst); //ʹÄÜ AXI GPIO ȫ¾ÖÖжÏ +} + +void ConfigAXIGPIOIntr(void){ + //ÉèÖÃÖжÏÓÅÏȼ¶ºʹ¥·¢ÀàÐÍ(¸ߵçƽ´¥·¢) + XScuGic_SetPriorityTriggerType(&scugic_inst, GPIO_INT_ID, 0xA0, 0x3); + //¹ØÁªÖжÏ ID ºÍÖжϴ¦Àíº¯Êý + XScuGic_Connect(&scugic_inst, GPIO_INT_ID, axi_gpio_handler, &axi_gpio_inst); + //ʹÄÜ AXI GPIO ÖжÏ + XScuGic_Enable(&scugic_inst, GPIO_INT_ID); +} +void axi_gpio_handler(void *CallbackRef){ + XGpio *GpioPtr = (XGpio *)CallbackRef; +// print("Interrupt Detected!\n"); + GpioWritePin(hop,0x01); + XGpio_InterruptDisable(GpioPtr, KEY_MASK); //¹رÕ AXI GPIO ÖжÏʹÄÜ +// u32 r=XGpio_DiscreteRead(GpioPtr, 1); +// if(r == 1){ + frqhop_config(hop_flag); +// }else{ +// print("Interrupt unvalid!\n"); +// } + XGpio_InterruptClear(GpioPtr, KEY_MASK); //Çå³ýÖжÏ + XGpio_InterruptEnable(GpioPtr, KEY_MASK); //ʹÄÜ AXI GPIO ÖжÏ + GpioWritePin(hop,0x00); +} +void frqhop_flag(int frqhop_flag){ + u8 rx_data; + hop_flag = frqhop_flag; + GpioSetPinDir(hop,GPIO_DIR_OUTPUT); + GpioWritePin(hop,0x01); + SpiPs_write_read2(0x615,0x00,&rx_data); + GpioWritePin(hop,0x00); +} + +void ExceptionInit(void){ + //ÉèÖò¢´ò¿ªÖжÏÒ쳣´¦Àí¹¦ÄÜ + Xil_ExceptionInit(); + Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, &scugic_inst); + Xil_ExceptionEnable(); +} +void XScuGicInit(void){ + scugic_cfg_ptr = XScuGic_LookupConfig(SCUGIC_ID); + XScuGic_CfgInitialize(&scugic_inst, scugic_cfg_ptr, scugic_cfg_ptr->CpuBaseAddress); +} + +int SpiPsInit(u16 SpiDeviceId){ + int Status; + XSpiPs_Config *SpiConfig; + /* + * Initialize the SPI driver so that it's ready to use + */ + SpiConfig = XSpiPs_LookupConfig(SpiDeviceId); + if (NULL == SpiConfig) { + return XST_FAILURE; + } + Status = XSpiPs_CfgInitialize((&SpiInstance), SpiConfig, + SpiConfig->BaseAddress); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + /* + * Perform a self-test to check hardware build + */ + Status = XSpiPs_SelfTest((&SpiInstance)); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + /* + * The SPI device is a slave by default and the clock phase + * have to be set according to its master. In this example, CPOL is set + * to quiescent high and CPHA is set to 1. + */ + + XSpiPs_SetOptions((&SpiInstance), (XSPIPS_CR_CPHA_MASK) | + XSPIPS_MASTER_OPTION | + XSPIPS_FORCE_SSELECT_OPTION); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + XSpiPs_SetClkPrescaler((&SpiInstance), XSPIPS_CLK_PRESCALE_8); + /* + * Set the Rx FIFO Threshold to the Max Data + */ +// XSpiPs_SetRXWatermark((&SpiInstance),MAX_DATA); + /* + * Enable the device. + */ + XSpiPs_Enable((&SpiInstance)); + +return XST_SUCCESS; +} + +int cx3e04_write(int addr , int value) +{ + Xil_Out32(AXI4_LITE0_SPI_BASEADDR+addr*4,value); + return 1; +} + +int cx3e04_read(int addr) +{ + return Xil_In32(AXI4_LITE0_SPI_BASEADDR+addr*4); +} + +int cx9261a_write(int addr , int value) +{ + Xil_Out32(AXI4_LITE1_SPI_BASEADDR+addr*4,value); + return 1; +} + +int cx9261a_read(int addr) +{ + return Xil_In32(AXI4_LITE1_SPI_BASEADDR+addr*4); +} + +void cx3e04_config(void) +{ + //³Çо¿Ƽ¼CX3E04 + //Common + cx3e04_write(0x000,0x12); + cx3e04_write(0x002,0x38); + cx3e04_write(0x038,0x01); + cx3e04_write(0x003,0x30); + cx3e04_write(0x039,0x00); + //CLK + //clk lvds en + cx3e04_write(0x011,0x7F); + cx3e04_write(0x012,0x34); + cx3e04_write(0x036,0x7F); + cx3e04_write(0x01A,0x4B); + cx3e04_write(0x013,0x04); + cx3e04_write(0x014,0x04); + cx3e04_write(0x004,0x00); + cx3e04_write(0x008,0x00); + cx3e04_write(0x009,0x00); + cx3e04_write(0x015,0x04); + cx3e04_write(0x016,0x04); + cx3e04_write(0x005,0x00); + cx3e04_write(0x00A,0x00); + cx3e04_write(0x00B,0x00); + cx3e04_write(0x017,0x04); + cx3e04_write(0x018,0x04); + cx3e04_write(0x006,0x00); + cx3e04_write(0x00C,0x00); + cx3e04_write(0x00D,0x00); + cx3e04_write(0x019,0x04); + cx3e04_write(0x007,0x00); + cx3e04_write(0x00E,0x00); + cx3e04_write(0x00F,0x00); + cx3e04_write(0x010,0x00); + cx3e04_write(0x029,0x00); + cx3e04_write(0x02A,0x00); + //SYNC + //sync lvds en + cx3e04_write(0x033,0x7F); + cx3e04_write(0x034,0x34); + cx3e04_write(0x035,0x7F); + cx3e04_write(0x032,0x03); + //sync inner div + cx3e04_write(0x03A,0x00); + cx3e04_write(0x03B,0x00); + cx3e04_write(0x03A,0x01); + cx3e04_write(0x03B,0xFF);//512·ÖƵ + cx3e04_write(0x03C,0x21); + cx3e04_write(0x03D,0x80); + cx3e04_write(0x02B,0x04); + cx3e04_write(0x02C,0x04); + cx3e04_write(0x01B,0x00); + cx3e04_write(0x01C,0x00); + cx3e04_write(0x022,0x00); + cx3e04_write(0x023,0x00); + cx3e04_write(0x02D,0x04); + cx3e04_write(0x02E,0x04); + cx3e04_write(0x01D,0x00); + cx3e04_write(0x01E,0x00); + cx3e04_write(0x024,0x00); + cx3e04_write(0x025,0x00); + cx3e04_write(0x02F,0x04); + cx3e04_write(0x030,0x04); + cx3e04_write(0x01F,0x00); + cx3e04_write(0x020,0x00); + cx3e04_write(0x026,0x00); + cx3e04_write(0x027,0x00); + cx3e04_write(0x031,0x04); + cx3e04_write(0x021,0x00); + cx3e04_write(0x028,0x00); + //ƫÖõçÁ÷ + cx3e04_write(0x040,0xFF); + cx3e04_write(0x041,0xFF); + cx3e04_write(0x042,0xFF); + cx3e04_write(0x043,0xF0); + cx3e04_write(0x044,0xFF); + cx3e04_write(0x045,0xFF); + cx3e04_write(0x046,0xFF); + cx3e04_write(0x047,0xF0); + //Others + cx3e04_write(0x037,0x07); +} + +//int SpiPs_write_read(u8 cmd, u16 addr,u8 data,u8 * rx_data){ +int SpiPs_write_read(u8 * data,u8 * rx_data){ + int Status; + u8 rx_buffer[3]; + + if(spi_select_value == 0){ + rx_buffer[0]= *data; + rx_buffer[1]= *(data+1); + rx_buffer[2]= *(data+2); + if(rx_buffer[0] >= 128){ + cx9261a_write((rx_buffer[0]-128)*256+rx_buffer[1],rx_buffer[2]); + }else{ + *rx_data=cx9261a_read(rx_buffer[0]*256+rx_buffer[1]); + } + }else{ + + XSpiPs_SetSlaveSelect((&SpiInstance),0x00); + + Status = XSpiPs_PolledTransfer((&SpiInstance),data,rx_buffer,3); + + *rx_data = rx_buffer[2]; + + } + +return 0; +} +int SpiPs_write_read1(u16 addr,u8 data,u8 * rx_data){ + u8 rx_data1; + u16 temp; + u8 rx_buffer[3]; + + temp= addr&0x00ff; + rx_buffer[0]= temp/256+128; + rx_buffer[1]= addr&0x00ff; + rx_buffer[2]= data; + + if(spi_select_value == 0){ + cx9261a_write(addr,data); + }else{ + + SpiPs_write_read((u8 *)&rx_buffer,&rx_data1); + *rx_data = rx_data1; + } + +return 0; +} +int SpiPs_write_read2(u16 addr,u8 data,u8 * rx_data){ + u8 rx_data1; + u16 temp; + u8 rx_buffer[3]; + + temp= addr&0x00ff; + rx_buffer[0]= temp/256; + rx_buffer[1]= addr&0x00ff; + rx_buffer[2]= data; + + if(spi_select_value == 0){ + *rx_data=cx9261a_read(addr); + }else{ + SpiPs_write_read((u8 *)&rx_buffer,&rx_data1); + *rx_data = rx_data1; + } + +return 0; +} + +int GpioInit(u16 DeviceId) +{ + int Status; + XGpioPs_Config *ConfigPtr; + /* Initialize the GPIO driver. */ + ConfigPtr = XGpioPs_LookupConfig(DeviceId); + Status = XGpioPs_CfgInitialize(&Gpio, ConfigPtr, + ConfigPtr->BaseAddr); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + return XST_SUCCESS; +} + +int GpioSetPinDir(u32 Pin,u32 Direction){ + XGpioPs_SetDirectionPin((&Gpio),GPIO_EMIO_OFFSET + Pin,Direction); + return 0; +} +int GpioWritePin(u32 Pin,u32 data){ + XGpioPs_SetOutputEnablePin(&Gpio, GPIO_EMIO_OFFSET + Pin, 1); + XGpioPs_WritePin((&Gpio),GPIO_EMIO_OFFSET + Pin,data); + return 0; +} + +int GpioReadPin(u32 Pin){ + return XGpioPs_ReadPin((&Gpio),GPIO_EMIO_OFFSET + Pin); +} + +void DeviceRst(void){ + GpioWritePin(NRST,0); + GpioWritePin(NRST,1); +} + +int XAxiDma_Init(XAxiDma* AxiDma,u16 DeviceId){ + + XAxiDma_Config *CfgPtr; + int Status; + /* Initialize the XAxiDma device. + */ + CfgPtr = XAxiDma_LookupConfig(DeviceId); + if (!CfgPtr) { + xil_printf("No config found for %d\r\n", DeviceId); + return XST_FAILURE; + } + + Status = XAxiDma_CfgInitialize(AxiDma, CfgPtr); + if (Status != XST_SUCCESS) { + xil_printf("Initialization failed %d\r\n", Status); + return XST_FAILURE; + } + + if(XAxiDma_HasSg(AxiDma)){ + xil_printf("Device configured as SG mode \r\n"); + return XST_FAILURE; + } + + /* Disable interrupts, we use polling mode + */ + XAxiDma_IntrDisable(AxiDma, XAXIDMA_IRQ_ALL_MASK, + XAXIDMA_DEVICE_TO_DMA); + XAxiDma_IntrDisable(AxiDma, XAXIDMA_IRQ_ALL_MASK, + XAXIDMA_DMA_TO_DEVICE); +return 0; +} + +int XAxiDmaCapture(XAxiDma* AxiDma,u32 data_length){ + int Status; + u8 *TxBufferPtr; + u32 MAX_PKT_LEN; + MAX_PKT_LEN = data_length; + TxBufferPtr = (u8 *)TX_BUFFER_BASE; + Xil_DCacheFlushRange((UINTPTR)TxBufferPtr, MAX_PKT_LEN); + Status = XAxiDma_Busy(AxiDma,XAXIDMA_DEVICE_TO_DMA); + Status = XAxiDma_Busy(AxiDma,XAXIDMA_DMA_TO_DEVICE); + XAxiDma_Reset(AxiDma); + Status = XAxiDma_ResetIsDone(AxiDma); + Status = XAxiDma_Busy(AxiDma,XAXIDMA_DEVICE_TO_DMA); + Status = XAxiDma_Busy(AxiDma,XAXIDMA_DMA_TO_DEVICE); + + Status = XAxiDma_SimpleTransfer(AxiDma,(UINTPTR) TxBufferPtr,MAX_PKT_LEN*8, XAXIDMA_DEVICE_TO_DMA); + SetPacketSize(AxiDma,MAX_PKT_LEN); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + u32 start_TickCount,current_TickCount=0; + start_TickCount = sys_now(); + u32 offset= 0; + //never come out ,reason is stream does't have tlast.Need a module to pack excatly the same length defined in para. + while ((offset< 500)&&(XAxiDma_Busy(AxiDma,XAXIDMA_DEVICE_TO_DMA))) { + Status = XAxiDma_Busy(AxiDma,XAXIDMA_DEVICE_TO_DMA); + + current_TickCount =sys_now(); + offset = current_TickCount-start_TickCount; +// xil_printf("offset: %d\r\n", offset); + } + /* + u64 offset= 0; + //never come out ,reason is stream does't have tlast.Need a module to pack excatly the same length defined in para. + while ((offset> 100000)||(XAxiDma_Busy(AxiDma,XAXIDMA_DEVICE_TO_DMA))) { + Status = XAxiDma_Busy(AxiDma,XAXIDMA_DEVICE_TO_DMA); + }*/ + return 0; +} + + + +int SetPacketSize(XAxiDma* AxiDma,u32 length){ + + UINTPTR Addr; + if(AxiDma == xAxiDma0Get()) + Addr = PACKER_BASEADDR0; + else if(AxiDma == xAxiDma1Get()) + Addr = PACKER_BASEADDR1; + else + Addr = PACKER_BASEADDR2; + xil_printf("addr %x .\n",Addr); + Xil_Out32(Addr,0); + xil_printf("PACKER_BASEADDR DATA %d .\n",Xil_In32(Addr)); + Xil_Out32(Addr+4,length); + xil_printf("PACKER_BASEADDR +4 DATA %d .\n",Xil_In32(Addr + 4)); + Xil_Out32(Addr,1); + xil_printf("PACKER_BASEADDR DATA %d .\n",Xil_In32(Addr)); + return 0; +} +int DownloadWave(u8 * data){ + u32 * buffer; + u64 addr; + int i; + buffer = (u32 *) data; + addr = BRAM_BASEADDR; + for(i=0;irecv_buf[spistack->spi_write_num * 3 + 0] = *(data + 0); + spistack->recv_buf[spistack->spi_write_num * 3 + 1] = *(data + 1); + spistack->recv_buf[spistack->spi_write_num * 3 + 2] = *(data + 2); + spistack->spi_write_num = spistack->spi_write_num + 1; + if(spistack->spi_write_num >= SPI_STACK_LENGTH-1){ + return 1; + } + else + return 0; +} + +void spi_stack_out(struct spi_stack * spistack){ + + int i; + u8 rx_data; + for(i=0;ispi_write_num;i++ ){ + SpiPs_write_read((u8 *)&(spistack->recv_buf[i * 3]),&rx_data); + } + spistack->spi_write_num = 0; +} + +int rom_read(u8 * data) +{ + int addr ; + addr = *(data + 0)*256+ *(data + 1); + return Xil_In32(BROM_BASEADDR+addr*4); +} + +void trx_ch_pll_config(u8 pll_index) +{ + u8 rx_data; + u8 recv_buf[3]={0}; + u32 trx_ch_pll_data; + + if(pll_index==1){ + trx_ch_pll_data = 0x00077DCF; + }else if(pll_index==2){ + trx_ch_pll_data = 0x00077D0C; + } + + recv_buf[0] =(trx_ch_pll_data>>16 & 0xff)|0x80 ; + recv_buf[1] = trx_ch_pll_data>>8 & 0xff; + recv_buf[2] = trx_ch_pll_data & 0xff; + SpiPs_write_read(recv_buf,&rx_data); +} + +u16 pll1_config_addr[] ={0x0620,0x061D,0x0B06,0x0B07,0x0B08,0x0B09,0x0B0A,0x061A,0x061B,0x061C,0x062A}; +u16 pll2_config_addr[] ={0x0642,0x063F,0x0B14,0x0B15,0x0B16,0x0B17,0x0B18,0x063C,0x063D,0x063E,0x064C}; +u16 pll3_config_addr[] ={0x0664,0x0661,0x0B22,0x0B23,0x0B24,0x0B25,0x0B26,0x065E,0x065F,0x0660,0x066E}; +u16 pll4_config_addr[] ={0x0686,0x0683,0x0B30,0x0B31,0x0B32,0x0B33,0x0B34,0x0680,0x0681,0x0682,0x0690}; + +int PLL_config(u8 pll_index,double pll_frq) +{ + + int i = 0,afc_cnt=0; + u16 pll_config_addr[11]; + u16 afc_addr,lock_addr; + double ref=200; + u8 pllreg_buf[11]; + u8 recv_buf[3]; + u8 rx_data,pll_div; + u16 div_addr; + + switch(pll_index){ + case 1:for (i = 0; i < 11; i++){pll_config_addr[i] = pll1_config_addr[i];} afc_addr=0x0617;lock_addr=0x0635;break; + case 2:for (i = 0; i < 11; i++){pll_config_addr[i] = pll2_config_addr[i];} afc_addr=0x0639;lock_addr=0x0657;break; + case 3:for (i = 0; i < 11; i++){pll_config_addr[i] = pll3_config_addr[i];} afc_addr=0x065B;lock_addr=0x0679;break; + case 4:for (i = 0; i < 11; i++){pll_config_addr[i] = pll4_config_addr[i];} afc_addr=0x067D;lock_addr=0x069B;break; + default :; + } + //caculate pll frequence word + pll_frq_caculation_new(ref,pll_frq,(u8 *)&pllreg_buf); + for (i = 0; i < 11; i++) + { + recv_buf[0] = (pll_config_addr[i]>>8 & 0xff)|0x80 ; + recv_buf[1] = pll_config_addr[i] & 0xff; + recv_buf[2] = pllreg_buf[i] & 0xff; + SpiPs_write_read((u8 *)&recv_buf[0],&rx_data); + } + //afc pll + pll_div = pllreg_buf[1]; + div_addr = pll_config_addr[1]; + recv_buf[0] = lock_addr>>8 & 0xff ; + recv_buf[1] = lock_addr & 0xff; + recv_buf[2] = 0x00; + rx_data = 0; + while(rx_data!=48){ + PLL_afc(afc_addr,div_addr,pll_div); + SpiPs_write_read((u8 *)&recv_buf[0],&rx_data); + afc_cnt++; + } + return afc_cnt; + //xil_printf("afc lock count: %d.\r\n",afc_cnt); + +} + +void PLL_afc(u16 afc_addr,u16 div_addr,u8 pll_div){ + + u8 recv_buf[3]; + u8 rx_data; + if(pll_div>=48){ + recv_buf[0] = (div_addr>>8 & 0xff)|0x80 ; + recv_buf[1] = div_addr & 0xff; + recv_buf[2] = pll_div & 0x2f; + SpiPs_write_read((u8 *)&recv_buf[0],&rx_data); + } + recv_buf[0] = (afc_addr>>8 & 0xff)|0x80 ; + recv_buf[1] = afc_addr & 0xff; + recv_buf[2] = 0x00; + SpiPs_write_read((u8 *)&recv_buf[0],&rx_data); + recv_buf[2] = 0x03; + SpiPs_write_read((u8 *)&recv_buf[0],&rx_data); + usleep(30); + if(pll_div>=48){ + recv_buf[0] = (div_addr>>8 & 0xff)|0x80 ; + recv_buf[1] = div_addr & 0xff; + recv_buf[2] = pll_div & 0xff; + SpiPs_write_read((u8 *)&recv_buf[0],&rx_data); + } + +} + +int pll_frq_caculation(double ref,double pll_frq,u8 *reg_data){ + + u8 row,column; + int vco = pll_frq; + int div = 1; + while(vco < 7001){ + vco = vco*2; + div = div*2; + } + char coreTable[7][4][3] = {{{0x0C,0x30,0x08},{0x0B,0x30,0x09},{0x0C,0x30,0x05},{0x0D,0x30,0x08}}, + {{0x0C,0x30,0x0C},{0x0B,0x30,0x0E},{0x0C,0x30,0x08},{0x0D,0x30,0x0B}}, + {{0x0C,0x20,0x10},{0x0B,0x20,0x10},{0x0C,0x20,0x0A},{0x0D,0x20,0x0D}}, + {{0x0C,0x20,0x11},{0x0B,0x20,0x13},{0x0C,0x20,0x0C},{0x0D,0x20,0x0F}}, + {{0x0D,0x30,0x14},{0x0D,0x22,0x14},{0x0D,0x20,0x0D},{0x0D,0x15,0x0C}}, + {{0x0E,0x30,0x18},{0x0E,0x25,0x16},{0x0E,0x25,0x0F},{0x0E,0x18,0x0F}}, + {{0x0F,0x38,0x1A},{0x0F,0x32,0x1C},{0x0F,0x18,0x12},{0x0F,0x18,0x12}}}; + + if (ref >= 28 && ref < 40){ + row = 0; + }else if (ref >= 40 && ref < 60){ + row = 1; + }else if (ref >= 60 && ref < 90){ + row = 2; + }else if (ref >= 90 && ref < 110){ + row = 3; + }else if (ref >= 110 && ref < 140){ + row = 4; + }else if (ref >= 140 && ref < 180){ + row = 5; + }else if (ref >= 180 && ref <= 240){ + row = 6; + } + + if (vco >= 7001 && vco < 8400){ + column = 0; + }else if (vco >= 8400 && vco < 10500){ + column = 1; + }else if (vco >= 10500 && vco < 12000){ + column = 2; + }else if (vco >= 12000 && vco <= 14000){ + column = 3; + } + + char cp = coreTable[row][column][0]; + char c1r3 = coreTable[row][column][1]; + char r1 = coreTable[row][column][2]; + + char dither,nHigh,nLow,fHigh,fMiddle,fLow,outputDiv,core = 0x81; + if(vco > 10500){ + core = core + 0x10; + } + if (div == 2){ + outputDiv = 0+7; + }else if (div == 4){ + outputDiv = 32+7; + }else{ + outputDiv = 48 + divBin(div); + } + + double nf = 1.0*vco / ref; + int n = nf; + double f = nf - n; + if(f > 1e-10){ + dither = 11; + }else{ + dither = 0; + } + if(n >= 256){ + nHigh = 1; + }else{ + nHigh = 0; + } + nLow = n & 0xff; + int f1 = f * pow(2,24); + fHigh = f1>>16 & 0xff; + fMiddle = f1>>8 & 0xff; + fLow = f1 & 0xff; + reg_data[0]=core; + reg_data[1]=outputDiv; + reg_data[2]=nHigh; + reg_data[3]=nLow; + reg_data[4]=fHigh; + reg_data[5]=fMiddle; + reg_data[6]=fLow; + reg_data[7]=cp; + reg_data[8]=c1r3; + reg_data[9]=r1; + reg_data[10]=dither; + return 0; +} +int pll_frq_caculation_new(double ref,double pll_frq,u8 *reg_data){ + + u8 row,column; + double vco = pll_frq; + int div = 1; + while(vco < 6701){ + vco = vco*2; + div = div*2; + } + char coreTable[7][4][3] = {{{0x0C,0x30,0x08},{0x0B,0x30,0x09},{0x0C,0x30,0x05},{0x0D,0x30,0x08}}, + {{0x0C,0x30,0x0C},{0x0B,0x30,0x0E},{0x0C,0x30,0x08},{0x0D,0x30,0x0B}}, + {{0x0C,0x20,0x10},{0x0B,0x20,0x10},{0x0C,0x20,0x0A},{0x0D,0x20,0x0D}}, + {{0x0C,0x20,0x11},{0x0B,0x20,0x13},{0x0C,0x20,0x0C},{0x0D,0x20,0x0F}}, + {{0x0D,0x30,0x14},{0x0D,0x22,0x14},{0x0D,0x20,0x0D},{0x0D,0x15,0x0C}}, + {{0x0E,0x30,0x18},{0x0E,0x25,0x16},{0x0E,0x25,0x0F},{0x0E,0x18,0x0F}}, + {{0x0F,0x38,0x1A},{0x0F,0x32,0x1C},{0x0F,0x18,0x12},{0x0F,0x18,0x12}}}; + + if (ref >= 28 && ref < 40){ + row = 0; + }else if (ref >= 40 && ref < 60){ + row = 1; + }else if (ref >= 60 && ref < 90){ + row = 2; + }else if (ref >= 90 && ref < 110){ + row = 3; + }else if (ref >= 110 && ref < 140){ + row = 4; + }else if (ref >= 140 && ref < 180){ + row = 5; + }else if (ref >= 180 && ref <= 240){ + row = 6; + } + + if (vco < 8400){ + column = 0; + }else if (vco >= 8400 && vco < 10150){ + column = 1; + }else if (vco >= 10150 && vco < 12000){ + column = 2; + }else if (vco >= 12000 ){ + column = 3; + } + + char cp = coreTable[row][column][0]; + char c1r3 = coreTable[row][column][1]; + char r1 = coreTable[row][column][2]; + + char dither,nHigh,nLow,fHigh,fMiddle,fLow,outputDiv,core = 0x81; + if(vco > 10150){ + core = core + 0x10; + } + if (div == 2){ + outputDiv = 0+7; + }else if (div == 4){ + outputDiv = 32+7; + }else{ + outputDiv = 48 + divBin(div); + } + + double nf = 1.0*vco / ref; + int n = nf; + double f = nf - n; + if(f > 1e-10){ + dither = 11; + }else{ + dither = 0; + } + if(n >= 256){ + nHigh = 1; + }else{ + nHigh = 0; + } + nLow = n & 0xff; + int f1 = f * pow(2,24); + fHigh = f1>>16 & 0xff; + fMiddle = f1>>8 & 0xff; + fLow = f1 & 0xff; + reg_data[0]=core; + reg_data[1]=outputDiv; + reg_data[2]=nHigh; + reg_data[3]=nLow; + reg_data[4]=fHigh; + reg_data[5]=fMiddle; + reg_data[6]=fLow; + reg_data[7]=cp; + reg_data[8]=c1r3; + reg_data[9]=r1; + reg_data[10]=dither; + return 0; +} +char divBin(u16 div){ + + switch(div){ + case 8:return 7;break; + case 16:return 6;break; + case 32:return 5;break; + case 64:return 4;break; + case 128:return 3;break; + case 256:return 2;break; + default :return 2; + } + +} + +void configTxNcoFreq(int freq, int fs) { + u8 rx_data; + double ratio = (double) freq < 0 ? (freq + fs) / fs : freq / fs; + double bin = ratio * pow(2,48); + int bin_low = (u32) bin & 0xFFFFFF; + int bin_high = (u32)(bin / pow(2,24)) & 0xFFFFFF; + SpiPs_write_read1(0x164, (bin_high >> 16) & 0xFF,&rx_data); + SpiPs_write_read1(0x165, (bin_high >> 8) & 0xFF,&rx_data); + SpiPs_write_read1(0x166, bin_high & 0xFF,&rx_data); + SpiPs_write_read1(0x167, (bin_low >> 16) & 0xFF,&rx_data); + SpiPs_write_read1(0x168, (bin_low >> 8) & 0xFF,&rx_data); + SpiPs_write_read1(0x169, bin_low & 0xFF,&rx_data); +} +void configRxNcoFreq(int freq, int fs) { + u8 rx_data; + double ratio = (double) freq < 0 ? (freq + fs) / fs : freq / fs; + double bin = ratio * pow(2,48); + int bin_low = (u32) bin & 0xFFFFFF; + int bin_high = (u32)(bin / pow(2,24)) & 0xFFFFFF; + SpiPs_write_read1(0xA16, (bin_high >> 16) & 0xFF,&rx_data); + SpiPs_write_read1(0xA17, (bin_high >> 8) & 0xFF,&rx_data); + SpiPs_write_read1(0xA18, bin_high & 0xFF,&rx_data); + SpiPs_write_read1(0xA19, (bin_low >> 16) & 0xFF,&rx_data); + SpiPs_write_read1(0xA1A, (bin_low >> 8) & 0xFF,&rx_data); + SpiPs_write_read1(0xA1B, bin_low & 0xFF,&rx_data); +} + diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/xilinx_io.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/xilinx_io.h new file mode 100644 index 0000000..7fbd767 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_CX9261A_HOP/xilinx_io.h @@ -0,0 +1,153 @@ +/* + * xilinx_io.h + * + * Created on: 2021Äê9ÔÂ6ÈÕ + * Author: cxlab + */ + +#ifndef SRC_XILINX_IO_H_ +#define SRC_XILINX_IO_H_ + + + +#endif /* SRC_XILINX_IO_H_ */ +#include "xspips.h" /* SPI device driver */ +#include "xgpiops.h" +#include "xgpio.h" +#include "xaxidma.h" +#include "xparameters.h" +#include "xscugic.h" +#include "xil_exception.h" + +//#define DBGLOG +#define MAX_DATA 100 +#define SPI_WRITE 0x80 +#define SPI_READ 0x00 +#define GPIO_DIR_INPUT 0x00 +#define GPIO_DIR_OUTPUT 0x01 +#define GPIO_EMIO_OFFSET 54 + +#define TX_EN 0x08 +#define RX_EN 0x07 +#define GPIO_1 0x06 +#define GPIO_0 0x05 + +#define NRST 0 +#define ENABLE_TRX1 1 +#define ENABLE_TRX2 2 +#define ENABLE_TRX3 3 +#define TXNRX_TRX1 4 +#define TXNRX_TRX2 5 +#define TXNRX_RX3 6 + +#define aresetn_0 33 +#define rst 34 +#define tr_sel 35 +#define tdd_fdd_sel 36 +#define r1t1_r2t2_sel 37 +#define sp_dp_sel 38 +#define aresetn_1 39 +#define aresetn_2 40 +#define hop 41 +#define cx3e04_rstn 42 +#define spi_select 46 +#define debug_mode 63 + +#define INTER_SIG3 0x03//dma_axis_rx_rstn +#define INTER_SIG2 0x02//frame_pattern +#define INTER_SIG1 0x01//mode_sel_async +#define INTER_SIG0 0x00//rst_async + +#define SPI_DEVICE_ID XPAR_XSPIPS_0_DEVICE_ID +#define GPIO_DEVICE_ID XPAR_PS7_GPIO_0_DEVICE_ID + +#define AXI_DMA_DEVCIE_ID0 XPAR_AXI_DMA_0_DEVICE_ID +#define AXI_DMA_DEVCIE_ID1 XPAR_AXI_DMA_1_DEVICE_ID +#define AXI_DMA_DEVCIE_ID2 XPAR_AXI_DMA_2_DEVICE_ID + +#define BROM_BASEADDR XPAR_M08_AXI_0_BASEADDR + +#define AXI4_LITE0_SPI_BASEADDR XPAR_AXI4_LITE_SPI_0_BASEADDR +#define AXI4_LITE1_SPI_BASEADDR XPAR_AXI4_LITE_SPI_1_BASEADDR +#define BRAM_BASEADDR XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR +#define BRAM_BASEADDR_1 XPAR_AXI_BRAM_CTRL_1_S_AXI_BASEADDR +#define PACKER_BASEADDR0 XPAR_AXIS_ADC_DATA_PACKER_0_S00_AXI_BASEADDR +#define PACKER_BASEADDR1 XPAR_AXIS_ADC_DATA_PACKER_1_S00_AXI_BASEADDR +#define PACKER_BASEADDR2 XPAR_AXIS_ADC_DATA_PACKER_2_S00_AXI_BASEADDR +#define WAVELENGTH 8192 +#define TX_BUFFER_BASE XPAR_MIG_7SERIES_0_BASEADDR +0x2000 +#define WAVE_LENGTH 8192*4 + +#define GPIO_INT_ID XPAR_FABRIC_GPIO_0_VEC_ID //PL ¶Ë AXI GPIO ÖжÏ ID +#define AXI_GPIO_ID XPAR_AXI_GPIO_0_DEVICE_ID //PL ¶Ë AXI GPIO Æ÷¼þ ID +#define SCUGIC_ID XPAR_SCUGIC_0_DEVICE_ID //ÖжϿØÖÆÆ÷ ID + +#define KEY_MASK XGPIO_IR_CH1_MASK //ͨµÀ 1 µÄ붨Òå + +int cx3e04_write(int addr , int value); +int cx3e04_read(int addr); +int cx9261a_write(int addr , int value); +int cx9261a_read(int addr); +void cx3e04_config(void); +void cx9261a_710_config(void); + +void AXIGpioInit(void); +void ConfigAXIGPIOIntr(void); +void axi_gpio_handler(void *CallbackRef); +void XScuGicInit(void); +void ExceptionInit(void); + +int SpiPsInit(u16 SpiDeviceId); +//int SpiPs_write_read(u8 cmd, u16 addr,u8 data,u8 * rx_data); +int SpiPs_write_read(u8 * data,u8 * rx_data); +int SpiPs_write_read1(u16 addr,u8 data,u8 * rx_data); +int SpiPs_write_read2(u16 addr,u8 data,u8 * rx_data); +int GpioInit(u16 DeviceId); +int GpioSetPinDir(u32 Pin,u32 Direction); +int GpioWritePin(u32 Pin,u32 data); +int GpioReadPin(u32 Pin); +void DeviceRst(void); + +//У׼ +void cx9261a_151_config(void); +void cx9261a_076_config(void); +void rxFFTCalib(int lo, int fs,int ref, int firLength,int chan); +void configTxNcoFreq(int freq, int fs); +void configRxNcoFreq(int freq, int fs); +void configDcCalib(int dcCalibEnable,int dcCalibLength); +void txBisectionQecCalib(int f0, int fs); +void txBisectionDcCalib(int f0, int fs); + +extern XAxiDma* xAxiDma0Get(void); +extern XAxiDma* xAxiDma1Get(void); +extern XAxiDma* xAxiDma2Get(void); +int XAxiDma_Init(XAxiDma* AxiDma,u16 DeviceId); +int XAxiDmaCapture(XAxiDma* AxiDma,u32 data_length); +int XAxiDmaCapture2(XAxiDma* AxiDma,u32 data_length); + +int DownloadWave(u8 * data); +int SetPacketSize(XAxiDma* AxiDma,u32 length); +void dbg_printf( const char8 *ctrl1, ...); +void PackerRst(void); +int peripheral_test(void); + +#define SPI_STACK_LENGTH 65536 +struct spi_stack { + int spi_write_num; + u8 recv_buf[SPI_STACK_LENGTH*3]; +}; +int spi_stack_in(u8 * data,struct spi_stack * spistack); + +void spi_stack_out(struct spi_stack * spistack); + +int rom_read(u8 * data); +void download_bin(void); +//ÌøƵ +void frqhop_config(int hop_flag); +void frqhop_flag(int frqhop_flag); +int PLL_config(u8 pll_index,double pll_frq); +void trx_ch_pll_config(u8 pll_index); +int pll_frq_caculation(double ref,double pll_frq,u8 *reg_data); +int pll_frq_caculation_new(double ref,double pll_frq,u8 *reg_data); +void PLL_afc(u16 afc_addr,u16 div_addr,u8 pll_div); +char divBin(u16 div); diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_1_9_0/cx9261adriver.cpp b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_1_9_0/cx9261adriver.cpp new file mode 100644 index 0000000..c6b7dac --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_1_9_0/cx9261adriver.cpp @@ -0,0 +1,2188 @@ +// ReSharper disable CppCompileTimeConstantCanBeReplacedWithBooleanConstant +// ReSharper disable CppUseRangeAlgorithm +// ReSharper disable CppUseStructuredBinding +// ReSharper disable CppTooWideScopeInitStatement +#include "cx9261adriver.h" +#include "utils.h" + + +namespace CX9261A { + +void logging(const std::string& msg){ + return; +} + +void writeSpi(int address, int value){ + return; +} + +int readSpi(int address){ + return 0; +} + +void pause(double ms){ + return; +} + +void setPin(int pin, int value){ + return; +} + +void initialChip() +{ + LOGGING("初始化寄存器配置"); + writeSpi(0x000, 0x02); + writeSpi(0x11F, 0x33); + writeSpi(0x773, 0x53); + writeSpi(0x774, 0x86); + writeSpi(0x781, 0x6F); + writeSpi(0x6C3, 0x00); + initialBBPLL(); + initialPLL1(); + initialPLL2(); + initialPLL3(); + initialPLL4(); + initialPLL5(); + initialRxChannel(RxChannel::Rx1); + initialRxChannel(RxChannel::Rx2); + initialRxChannel(RxChannel::Rx3); + initialTxChannel(TxChannel::Tx1); + initialTxChannel(TxChannel::Tx2); +} + +void initialPowerConfig(const CX9261AConfiguration& config) +{ + // 断电模式 + LOGGING("断电模式"); + writeSpi(0x031, 0x00); + writeSpi(0x032, 0x7F); + writeSpi(0x033, 0xFF); + writeSpi(0x034, 0xFF); + // 休眠模式 + LOGGING("休眠模式"); + writeSpi(0x035, 0x00); + writeSpi(0x036, config.pllSource.rxUseExt || config.pllSource.txUseExt ? 0x3C : 0x3D); + const bool usePll4 = !config.pllSource.txUseExt + && (config.pllSource.tx1Source == LoSource::Pll4 || config.pllSource.tx2Source == LoSource::Pll4); + const bool usePll5 = config.pllSource.rx3Source == LoSource::Pll5; + Register reg037(0xFF); + reg037[2] = !usePll5; + reg037[0] = !usePll4; + writeSpi(0x037, reg037); + const bool usePll3 = !config.pllSource.txUseExt + && (config.pllSource.tx1Source == LoSource::Pll3 || config.pllSource.tx2Source == LoSource::Pll3); + const bool usePll2 = !config.pllSource.rxUseExt + && (config.pllSource.rx1Source == LoSource::Pll2 || config.pllSource.rx2Source == LoSource::Pll2); + const bool usePll1 = !config.pllSource.rxUseExt + && (config.pllSource.rx1Source == LoSource::Pll1 || config.pllSource.rx2Source == LoSource::Pll1); + Register reg038(0xFF); + reg038[6] = !usePll3; + reg038[4] = !usePll2; + reg038[2] = !usePll1; + writeSpi(0x038, reg038); + // 警戒模式 + LOGGING("警戒模式"); + writeSpi(0x039, 0x00); + writeSpi(0x03A, config.pllSource.rxUseExt || config.pllSource.txUseExt ? 0x3C : 0x3D); + Register reg03B(0xFF); + reg03B[2] = !usePll5; + reg03B[1] = !usePll5; + reg03B[0] = !usePll4; + writeSpi(0x03B, reg03B); + Register reg03C(0xFE); + reg03C[7] = !usePll4; + reg03C[6] = !usePll3; + reg03C[5] = !usePll3; + reg03C[4] = !usePll2; + reg03C[3] = !usePll2; + reg03C[2] = !usePll1; + reg03C[1] = !usePll1; + writeSpi(0x03C, reg03C); + // 校准模式 + LOGGING("校准模式"); + Register reg03D(0x00); + reg03D[4] = config.calib.tx2CalibPll || config.calib.rx3CalibPll || config.calib.rx2CalibPll; + reg03D[3] = config.calib.tx1CalibPll || config.calib.rx1CalibPll; + reg03D[2] = config.calib.rx3CalibPll; + reg03D[1] = config.calib.tx2CalibPll || config.calib.rx2CalibPll; + reg03D[0] = config.calib.tx1CalibPll || config.calib.rx1CalibPll; + writeSpi(0x03D, reg03D); + writeSpi(0x03E, config.pllSource.rxUseExt || config.pllSource.txUseExt ? 0x38 : 0x39); + Register reg03F(0xFF); + reg03F[7] = !(config.calib.tx2CalibPll || config.calib.rx3CalibPll || config.calib.rx2CalibPll); + reg03F[6] = !(config.calib.tx1CalibPll || config.calib.rx1CalibPll); + reg03F[5] = !(config.calib.rx3CalibPll); + reg03F[4] = !(config.calib.tx2CalibPll || config.calib.rx2CalibPll); + reg03F[3] = !(config.calib.tx1CalibPll || config.calib.rx1CalibPll); + reg03F[2] = !usePll5; + reg03F[1] = !usePll5; + reg03F[0] = !(usePll4 || config.calib.rx3CalibPll || config.calib.rx2CalibPll || config.calib.rx1CalibPll); + writeSpi(0x03F, reg03F); + Register reg040(0xFE); + reg040[7] = !(usePll4 || config.calib.rx3CalibPll || config.calib.rx2CalibPll || config.calib.rx1CalibPll); + reg040[6] = !usePll3; + reg040[5] = !usePll3; + reg040[4] = !usePll2; + reg040[3] = !usePll2; + reg040[2] = !usePll1; + reg040[1] = !usePll1; + writeSpi(0x040, reg040); + // 接收模式 + LOGGING("接收模式"); + Register reg041(0x00); + reg041[2] = config.pllSource.rx3Source != LoSource::Off; + reg041[1] = config.pllSource.rxUseExt || config.pllSource.rx2Source != LoSource::Off; + reg041[0] = config.pllSource.rxUseExt || config.pllSource.rx1Source != LoSource::Off; + writeSpi(0x041, reg041); + writeSpi(0x042, config.pllSource.rxUseExt ? 0x3C : 0x3D); + Register reg043(0xFF); + reg043[5] = !reg041[2]; + reg043[4] = !reg041[1]; + reg043[3] = !reg041[0]; + reg043[2] = config.pllSource.rx3Source == LoSource::Off; + reg043[1] = config.pllSource.rx3Source == LoSource::Off; + writeSpi(0x043, reg043); + Register reg044(0xFE); + reg044[4] = !usePll2; + reg044[3] = !usePll2; + reg044[2] = !usePll1; + reg044[1] = !usePll1; + writeSpi(0x044, reg044); + // 发射模式 + LOGGING("发射模式"); + Register reg045(0x00); + reg045[4] = config.pllSource.txUseExt || config.pllSource.tx2Source != LoSource::Off; + reg045[3] = config.pllSource.txUseExt || config.pllSource.tx1Source != LoSource::Off; + writeSpi(0x045, reg045); + writeSpi(0x046, config.pllSource.txUseExt ? 0x3C : 0x3D); + Register reg047(0xFF); + reg047[7] = !reg045[4]; + reg047[6] = !reg045[3]; + reg047[0] = !usePll4; + writeSpi(0x047, reg047); + Register reg048(0xFE); + reg048[7] = !usePll4; + reg048[6] = !usePll3; + reg048[5] = !usePll3; + reg048[4] = !usePll2; + reg048[3] = !usePll2; + reg048[2] = !usePll1; + reg048[1] = !usePll1; + writeSpi(0x048, reg048); + // FDD模式 + LOGGING("FDD模式"); + Register reg049(0x00); + reg049[4] = config.pllSource.txUseExt || config.pllSource.tx2Source != LoSource::Off; + reg049[3] = config.pllSource.txUseExt || config.pllSource.tx1Source != LoSource::Off; + reg049[2] = config.pllSource.rx3Source == LoSource::Off; + reg049[1] = config.pllSource.rxUseExt || config.pllSource.rx2Source != LoSource::Off; + reg049[0] = config.pllSource.rxUseExt || config.pllSource.rx1Source != LoSource::Off; + writeSpi(0x049, reg049); + writeSpi(0x04A, config.pllSource.txUseExt || config.pllSource.rxUseExt ? 0x3C : 0x3D); + Register reg04B(0xFF); + reg04B[7] = !reg049[4]; + reg04B[6] = !reg049[3]; + reg04B[5] = !reg049[2]; + reg04B[4] = !reg049[1]; + reg04B[3] = !reg049[0]; + reg04B[2] = !usePll5; + reg04B[1] = !usePll5; + reg04B[0] = !usePll4; + writeSpi(0x04B, reg04B); + Register reg04C(0xFE); + reg04C[7] = !usePll4; + reg04C[6] = !usePll3; + reg04C[5] = !usePll3; + reg04C[4] = !usePll2; + reg04C[3] = !usePll2; + reg04C[2] = !usePll1; + reg04C[1] = !usePll1; + writeSpi(0x04C, reg04C); + // 观测模式 + LOGGING("观测模式"); + writeSpi(0x04D, 0x04); + writeSpi(0x04E, 0x3D); + writeSpi(0x04F, 0xD9); + writeSpi(0x050, 0xFE); +} + +void initialBBPLL() +{ + LOGGING("初始化BBPLL"); + writeSpi(0x70B, 0x0E); + writeSpi(0x727, 0x0E); + writeSpi(0x743, 0x0E); +} + +void initialPLL1() +{ + LOGGING("初始化PLL1"); + writeSpi(0x624, 0x00); + writeSpi(0x625, 0x11); + writeSpi(0x626, 0x02); + writeSpi(0x61E, 0x30); + writeSpi(0x61F, 0x08); + writeSpi(0x627, 0x10); +} + +void initialPLL2() +{ + LOGGING("初始化PLL2"); + writeSpi(0x646, 0x00); + writeSpi(0x647, 0x11); + writeSpi(0x648, 0x02); + writeSpi(0x640, 0x30); + writeSpi(0x641, 0x08); + writeSpi(0x649, 0x10); +} + +void initialPLL3() +{ + LOGGING("初始化PLL3"); + writeSpi(0x668, 0x00); + writeSpi(0x669, 0x11); + writeSpi(0x66A, 0x02); + writeSpi(0x662, 0x30); + writeSpi(0x663, 0x08); + writeSpi(0x66B, 0x10); +} + +void initialPLL4() +{ + LOGGING("初始化PLL4"); + writeSpi(0x68A, 0x00); + writeSpi(0x68B, 0x11); + writeSpi(0x68C, 0x02); + writeSpi(0x684, 0x30); + writeSpi(0x685, 0x08); + writeSpi(0x68D, 0x10); +} + +void initialPLL5() +{ + LOGGING("初始化PLL5"); + writeSpi(0x6AC, 0x00); + writeSpi(0x6AD, 0x11); + writeSpi(0x6AE, 0x02); + writeSpi(0x6A6, 0x30); + writeSpi(0x6A7, 0x08); + writeSpi(0x6AF, 0x10); +} + +void configRef(const double ref) +{ + LOGGING("初始化参考时钟"); + // PLL1 + writeSpi(0x621, ref <= 100 ? 0x41 : 0x49); + writeSpi(0x622, ref <= 100 ? 0x00 : ref > 150 ? 0x3F : 0x15); + // PLL2 + writeSpi(0x643, ref <= 100 ? 0x41 : 0x49); + writeSpi(0x644, ref <= 100 ? 0x00 : ref > 150 ? 0x3F : 0x15); + // PLL3 + writeSpi(0x665, ref <= 100 ? 0x41 : 0x49); + writeSpi(0x666, ref <= 100 ? 0x00 : ref > 150 ? 0x3F : 0x15); + // PLL4 + writeSpi(0x687, ref <= 100 ? 0x41 : 0x49); + writeSpi(0x688, ref <= 100 ? 0x00 : ref > 150 ? 0x3F : 0x15); + // PLL5 + writeSpi(0x6A9, ref <= 100 ? 0x41 : 0x49); + writeSpi(0x6AA, ref <= 100 ? 0x00 : ref > 150 ? 0x3F : 0x15); +} + +void configBBPLL(const CX9261AConfiguration& config) +{ + LOGGING("配置BBPLL"); + const auto& clock = config.clock; + configRefDoubler(clock.refDoubler, clock.ref); + // fclk div + const int fclkDiv = clock.adc > 64 ? 4 : 5; + Register r0C0{ 0x50 }; + r0C0[1] = fclkDiv != 5; + writeSpi(0x0C0, r0C0); + writeSpi(0x0C1, r0C0); + writeSpi(0x0C2, r0C0); + // clock + double bblo = clock.adc * fclkDiv * 4; + int bbloDiv = 1; + while (bblo <= 1350 && bbloDiv < 8) { + bblo *= 2; + bbloDiv *= 2; + } + Register r602; + r602.replace(1, 0, static_cast(std::log2(bbloDiv))); + r602[5] = fclkDiv == 5; + // ref div + Register r601{ 0x0F }; + r601.replace(1, 0, clock.refDiv == 2 ? 0b00 : clock.refDiv == 4 ? 0b01 : clock.refDiv == 8 ? 0b10 : 0b11); + writeSpi(0x601, r601); + // nf + const auto nf = bblo * clock.refDiv / (clock.ref * (clock.refDoubler ? 2 : 1)); + int n = floor(nf); + const auto f = nf - n; + r602[6] = clock.bbUseIntDiv; + writeSpi(0x602, r602); + // 整数模式 + if (clock.bbUseIntDiv) + n = round(2.0 * n / bbloDiv + 1); + const int fBin = round(std::pow(2, 25) * f); + Register r60D{ 0x0C }; + r60D[0] = f >= 0.5; + r60D[4] = clock.bbUseIntDiv; + const Register r60E = fBin >> 16 & 0xFF; + const Register r60F = fBin >> 8 & 0xFF; + const Register r610 = fBin & 0xFF; + writeSpi(0x60D, r60D); + writeSpi(0x60E, r60E); + writeSpi(0x60F, r60F); + writeSpi(0x610, r610); + writeSpi(0x611, n); + // Dither + Register r612; + r612[4] = clock.bbUseIntDiv; + writeSpi(0x612, r612); + // ADC + const auto adcClock = clock.adc * fclkDiv * 4; + Register r70E{ 0x6B }; + Register r70F{ 0xE0 }; + double adjAdc; + if (adcClock < 450) { + r70E.replace(6, 5, 0b00); + r70E.replace(4, 0, 0b00010); + r70F.replace(7, 6, 0b00); + adjAdc = 300; + } else if (adcClock < 750) { + r70E.replace(6, 5, 0b01); + r70E.replace(4, 0, 0b00101); + r70F.replace(7, 6, 0b01); + adjAdc = 600; + } else if (adcClock < 1050) { + r70E.replace(6, 5, 0b10); + r70E.replace(4, 0, 0b01000); + r70F.replace(7, 6, 0b10); + adjAdc = 900; + } else { + r70E.replace(6, 5, 0b11); + r70E.replace(4, 0, 0b01011); + r70F.replace(7, 6, 0b11); + adjAdc = 1200; + } + writeSpi(0x70E, r70E); + writeSpi(0x72A, r70E); + writeSpi(0x746, r70E); + writeSpi(0x70F, r70F); + writeSpi(0x72B, r70F); + writeSpi(0x747, r70F); + auto c = 8600 * adjAdc / adcClock; + c = (c - 3000) / 80; + const Register r70D = round(c); + writeSpi(0x70D, r70D); + writeSpi(0x729, r70D); + writeSpi(0x745, r70D); + // 时序配置 + if (config.interface.ioMode != IOMode::CMOS) { + writeSpi(0x782, 0x00); + writeSpi(0x783, 0x00); + } else if (clock.adc <= 60) { + writeSpi(0x782, 0x01); + writeSpi(0x783, 0x01); + } else { + writeSpi(0x782, 0x76); + writeSpi(0x783, 0x76); + } + writeSpi(0x784, clock.adc <= 51 ? 0x01 : 0x06); +} + +bool afcBBPll() +{ + LOGGING("BBPLL AFC"); + writeSpi(0x600, 0x00); + writeSpi(0x600, 0x11); + pause(delay); + return readSpi(0x615) & 0b00100000; +} + +std::vector configPllImpl(const double lo, const double ref) +{ + auto vco = lo; + auto div = 1; + while (vco <= 6700 && div < 256) { + vco *= 2; + div *= 2; + } + int row{}; + if (ref >= 28 && ref < 40) + row = 0; + if (ref >= 40 && ref < 60) + row = 1; + if (ref >= 60 && ref < 90) + row = 2; + if (ref >= 90 && ref < 110) + row = 3; + if (ref >= 110 && ref < 140) + row = 4; + if (ref >= 140 && ref < 180) + row = 5; + if (ref >= 180 && ref <= 240) + row = 6; + int column{}; + if (vco < 8400) + column = 0; + if (vco >= 8400 && vco < 10150) + column = 1; + if (vco >= 10150 && vco < 12000) + column = 2; + if (vco >= 12000) + column = 3; + + std::vector ret(11); + const Register core = vco > 10150 ? 0x91 : 0x81; + ret[0] = core; + const Register cp{ coreTable[row][column][0] }; + ret[7] = cp; + const Register c1r3{ coreTable[row][column][1] }; + ret[8] = c1r3; + const Register r1{ coreTable[row][column][2] }; + ret[9] = r1; + const Register outputDiv = [div]() { + switch (div) { + case 2: + return 0x07; + case 4: + return 0x27; + case 8: + return 0x37; + case 16: + return 0x36; + case 32: + return 0x35; + case 64: + return 0x34; + case 128: + return 0x33; + case 256: + return 0x32; + default: + return 0; + } + }(); + ret[1] = outputDiv; + const auto nf = static_cast(std::round(vco / ref * 1e9)) / 1.0e9; + const auto n = floor(nf); + const auto f = nf - n; + const Register nHigh = n >= 256; + ret[2] = nHigh; + const Register nLow = n & 0xFF; + ret[3] = nLow; + const int fint = round(std::pow(2, 24) * f); + ret[4] = fint >> 16 & 0xFF; + ret[5] = fint >> 8 & 0xFF; + ret[6] = fint & 0xFF; + const Register dither = f < 1e-10 ? 0x00 : 0x0B; + ret[10] = dither; + return ret; +} + +void configPll1(const double lo, const Clock& clock) +{ + LOGGING("配置PLL1"); + const auto pllMap = configPllImpl(lo, clock.ref * (clock.refDoubler ? 2 : 1)); + writeSpi(0x620, pllMap[0]); + writeSpi(0x61D, pllMap[1]); + writeSpi(0xB06, pllMap[2]); + writeSpi(0xB07, pllMap[3]); + writeSpi(0xB08, pllMap[4]); + writeSpi(0xB09, pllMap[5]); + writeSpi(0xB0A, pllMap[6]); + writeSpi(0x61A, pllMap[7]); + writeSpi(0x61B, pllMap[8]); + writeSpi(0x61C, pllMap[9]); + writeSpi(0x62A, pllMap[10]); +} + +bool afcPll1() +{ + LOGGING("PLL1 AFC"); + Register r61D = readSpi(0x61D); + const auto saved = r61D; + r61D.replace(5, 4, 0b10); + writeSpi(0x61D, r61D); + writeSpi(0x617, 0x00); + writeSpi(0x617, 0x13); + pause(delay); + writeSpi(0x61D, saved); + const auto lock = readSpi(0x635); + return 0x10 & lock; +} + +void configPll2(const double lo, const Clock& clock) +{ + LOGGING("配置PLL2"); + const auto pllMap = configPllImpl(lo, clock.ref * (clock.refDoubler ? 2 : 1)); + writeSpi(0x642, pllMap[0]); + writeSpi(0x63F, pllMap[1]); + writeSpi(0xB14, pllMap[2]); + writeSpi(0xB15, pllMap[3]); + writeSpi(0xB16, pllMap[4]); + writeSpi(0xB17, pllMap[5]); + writeSpi(0xB18, pllMap[6]); + writeSpi(0x63C, pllMap[7]); + writeSpi(0x63D, pllMap[8]); + writeSpi(0x63E, pllMap[9]); + writeSpi(0x64C, pllMap[10]); +} + +bool afcPll2() +{ + LOGGING("PLL2 AFC"); + Register r63F = readSpi(0x63F); + const auto saved = r63F; + r63F.replace(5, 4, 0b10); + writeSpi(0x63F, r63F); + writeSpi(0x639, 0x00); + writeSpi(0x639, 0x13); + pause(delay); + writeSpi(0x63F, saved); + const auto lock = readSpi(0x657); + return 0x10 & lock; +} + +void configPll3(const double lo, const Clock& clock) +{ + LOGGING("配置PLL3"); + const auto pllMap = configPllImpl(lo, clock.ref * (clock.refDoubler ? 2 : 1)); + writeSpi(0x664, pllMap[0]); + writeSpi(0x661, pllMap[1]); + writeSpi(0xB22, pllMap[2]); + writeSpi(0xB23, pllMap[3]); + writeSpi(0xB24, pllMap[4]); + writeSpi(0xB25, pllMap[5]); + writeSpi(0xB26, pllMap[6]); + writeSpi(0x65E, pllMap[7]); + writeSpi(0x65F, pllMap[8]); + writeSpi(0x660, pllMap[9]); + writeSpi(0x66E, pllMap[10]); +} + +bool afcPll3() +{ + LOGGING("PLL3 AFC"); + Register r661 = readSpi(0x661); + const auto saved = r661; + r661.replace(5, 4, 0b10); + writeSpi(0x661, r661); + writeSpi(0x65B, 0x00); + writeSpi(0x65B, 0x13); + pause(delay); + writeSpi(0x661, saved); + const auto lock = readSpi(0x679); + return 0x10 & lock; +} + +void configPll4(const double lo, const Clock& clock) +{ + LOGGING("配置PLL4"); + const auto pllMap = configPllImpl(lo, clock.ref * (clock.refDoubler ? 2 : 1)); + writeSpi(0x686, pllMap[0]); + writeSpi(0x683, pllMap[1]); + writeSpi(0xB30, pllMap[2]); + writeSpi(0xB31, pllMap[3]); + writeSpi(0xB32, pllMap[4]); + writeSpi(0xB33, pllMap[5]); + writeSpi(0xB34, pllMap[6]); + writeSpi(0x680, pllMap[7]); + writeSpi(0x681, pllMap[8]); + writeSpi(0x682, pllMap[9]); + writeSpi(0x690, pllMap[10]); +} + +bool afcPll4() +{ + LOGGING("PLL4 AFC"); + Register r683 = readSpi(0x683); + const auto saved = r683; + r683.replace(5, 4, 0b10); + writeSpi(0x683, r683); + writeSpi(0x67D, 0x00); + writeSpi(0x67D, 0x13); + pause(delay); + writeSpi(0x683, saved); + const auto lock = readSpi(0x69B); + return 0x10 & lock; +} + +void configPll5(const double lo, const Clock& clock) +{ + LOGGING("配置PLL5"); + const auto pllMap = configPllImpl(lo, clock.ref * (clock.refDoubler ? 2 : 1)); + writeSpi(0x6A8, pllMap[0]); + writeSpi(0x6A5, pllMap[1]); + writeSpi(0xB3E, pllMap[2]); + writeSpi(0xB3F, pllMap[3]); + writeSpi(0xB40, pllMap[4]); + writeSpi(0xB41, pllMap[5]); + writeSpi(0xB42, pllMap[6]); + writeSpi(0x6A2, pllMap[7]); + writeSpi(0x6A3, pllMap[8]); + writeSpi(0x6A4, pllMap[9]); + writeSpi(0x6B2, pllMap[10]); +} + +bool afcPll5() +{ + LOGGING("PLL5 AFC"); + Register r6A5 = readSpi(0x6A5); + const auto saved = r6A5; + r6A5.replace(5, 4, 0b10); + writeSpi(0x6A5, r6A5); + writeSpi(0x69F, 0x00); + writeSpi(0x69F, 0x13); + pause(delay); + writeSpi(0x6A5, saved); + const auto lock = readSpi(0x6BD); + return 0x10 & lock; +} + +void configAGCMode(const int mode) +{ + LOGGING("配置AGC模式"); + writeSpi(0x900, mode == 0 ? 0x00 : 0x11); + writeSpi(0x901, mode == 3 || mode == 4 ? mode + 0x80 : mode); +} + +void configAGCPDT(const AGCPDT& pdt) +{ + LOGGING("配置AGC PDT"); + writeSpi(0x908, (pdt.pdtN << 4) + pdt.pdtM); + writeSpi(0x909, pdt.pdtN < 3 && pdt.pdtM < 4 ? 0x00 : 0x03); + writeSpi(0x90A, pdt.pdtN < 3 && pdt.pdtM < 4 ? 0x02 : 0x32); +} + +void configAGCLMT(const AGCLMT& lmt) +{ + LOGGING("配置AGC LMT"); + writeSpi(0x90D, lmt.count >> 8); + writeSpi(0x90E, lmt.count & 0xFF); + writeSpi(0x90F, (lmt.threshold1 << 4) + lmt.threshold2); + writeSpi(0x910, (lmt.threshold3 << 4) + lmt.threshold4); +} + +void configAGCDIG(const AGCDIG& dig) +{ + LOGGING("配置AGC DIG"); + writeSpi(0x911, dig.count >> 8); + writeSpi(0x912, dig.count & 0xFF); + writeSpi(0x913, dig.threshold1 + 60); + writeSpi(0x914, dig.threshold2 + 60); + writeSpi(0x915, dig.threshold3 + 60); + writeSpi(0x916, dig.threshold4 + 60); +} + +void configAGC(const AGC& agc) +{ + LOGGING("配置AGC"); + writeSpi(0x903, 0x11); + configAGCMode(agc.mode); + configAGCPDT(agc.pdt); + configAGCLMT(agc.lmt); + configAGCDIG(agc.dig); +} + +void configRISCV(const bool on) +{ + LOGGING("配置处理器"); + if (on) { + writeSpi(0x071, 0x00); + writeSpi(0x071, 0x29); + writeSpi(0x236, 0x0F); + writeSpi(0x20A, 0x01); + writeSpi(0x071, 0x26); + } else { + writeSpi(0x071, 0x00); + } +} + +void configTemperatureCompensation(const TemperatureComp mode) +{ + LOGGING("配置温补"); + // BBPLL + writeSpi(0x613, 0x9F); + writeSpi(0x614, mode == TemperatureComp::Band ? 0xB4 : 0xA4); + Register r6C3 = readSpi(0x6C3); + r6C3[4] = true; + writeSpi(0x6C3, r6C3); + // PLL1 + writeSpi(0x632, 0x9F); + writeSpi(0x633, mode == TemperatureComp::Band ? 0x1C : 0x14); + // PLL2 + writeSpi(0x654, 0x9F); + writeSpi(0x655, mode == TemperatureComp::Band ? 0x1C : 0x14); + // PLL3 + writeSpi(0x676, 0x9F); + writeSpi(0x677, mode == TemperatureComp::Band ? 0x1C : 0x14); + // PLL4 + writeSpi(0x698, 0x9F); + writeSpi(0x699, mode == TemperatureComp::Band ? 0x1C : 0x14); + // PLL5 + writeSpi(0x6BA, 0x9F); + writeSpi(0x6BB, mode == TemperatureComp::Band ? 0x1C : 0x14); +} + +void configADCCalib(const double adc) +{ + LOGGING("配置ADC校准"); + writeSpi(0x70B, 0x1E); + writeSpi(0x727, 0x1E); + writeSpi(0x743, 0x1E); + const auto adcClk = adc <= 64 ? adc * 20 : adc * 16; + if (adcClk < 450) { + writeSpi(0x70F, 0x08); + writeSpi(0x710, 0x23); + writeSpi(0x711, 0x10); + + writeSpi(0x72B, 0x08); + writeSpi(0x72C, 0x23); + writeSpi(0x72D, 0x10); + + writeSpi(0x747, 0x08); + writeSpi(0x748, 0x23); + writeSpi(0x749, 0x10); + } else if (adcClk < 750) { + writeSpi(0x70F, 0x50); + writeSpi(0x710, 0x43); + writeSpi(0x711, 0x30); + + writeSpi(0x72B, 0x50); + writeSpi(0x72C, 0x43); + writeSpi(0x72D, 0x30); + + writeSpi(0x747, 0x50); + writeSpi(0x748, 0x43); + writeSpi(0x749, 0x30); + } else if (adcClk < 1050) { + writeSpi(0x70F, 0xA0); + writeSpi(0x710, 0x83); + writeSpi(0x711, 0xF0); + + writeSpi(0x72B, 0xA0); + writeSpi(0x72C, 0x83); + writeSpi(0x72D, 0xF0); + + writeSpi(0x747, 0xA0); + writeSpi(0x748, 0x83); + writeSpi(0x749, 0xF0); + } else { + writeSpi(0x70F, 0xE0); + writeSpi(0x710, 0x83); + writeSpi(0x711, 0xF0); + + writeSpi(0x72B, 0xE0); + writeSpi(0x72C, 0x83); + writeSpi(0x72D, 0xF0); + + writeSpi(0x747, 0xE0); + writeSpi(0x748, 0x83); + writeSpi(0x749, 0xF0); + } + writeSpi(0x713, 0x80); + writeSpi(0x72F, 0x80); + writeSpi(0x74B, 0x80); + writeSpi(0x70B, 0x9E); + writeSpi(0x727, 0x9E); + writeSpi(0x743, 0x9E); + writeSpi(0x70B, 0x9A); + writeSpi(0x727, 0x9A); + writeSpi(0x743, 0x9A); + writeSpi(0x70B, 0x9E); + writeSpi(0x727, 0x9E); + writeSpi(0x743, 0x9E); + pause(delay); + writeSpi(0x70B, 0x1E); + writeSpi(0x727, 0x1E); + writeSpi(0x743, 0x1E); + writeSpi(0x713, 0xC0); + writeSpi(0x72F, 0xC0); + writeSpi(0x74B, 0xC0); +} + +void configFlashCalib() +{ + LOGGING("配置Flash校准"); + writeSpi(0x70B, 0x1E); + writeSpi(0x727, 0x1E); + writeSpi(0x743, 0x1E); + writeSpi(0x715, 0x80); + writeSpi(0x731, 0x80); + writeSpi(0x74D, 0x80); + writeSpi(0x715, 0xC0); + writeSpi(0x731, 0xC0); + writeSpi(0x74D, 0xC0); + writeSpi(0x716, 0x40); + writeSpi(0x732, 0x40); + writeSpi(0x74E, 0x40); + writeSpi(0x70B, 0x16); + writeSpi(0x727, 0x16); + writeSpi(0x743, 0x16); + pause(delay); + writeSpi(0x70B, 0x1E); + writeSpi(0x727, 0x1E); + writeSpi(0x743, 0x1E); + writeSpi(0x715, 0x80); + writeSpi(0x731, 0x80); + writeSpi(0x74D, 0x80); + writeSpi(0x716, 0x00); + writeSpi(0x732, 0x00); + writeSpi(0x74E, 0x00); +} + +std::map beforeQecState() +{ + std::map state; + // SPI模式 + state[0x000] = 0x02; + // 电源模式 + state[0x02E] = readSpi(0x02E); + // DC Tracking + state[0x109] = readSpi(0x109); + state[0x10A] = readSpi(0x10A); + state[0x11F] = readSpi(0x11F); + // RX NCO + state[0xA07] = readSpi(0xA07); + state[0xA08] = readSpi(0xA08); + state[0xA16] = readSpi(0xA16); + state[0xA17] = readSpi(0xA17); + state[0xA18] = readSpi(0xA18); + state[0xA19] = readSpi(0xA19); + state[0xA1A] = readSpi(0xA1A); + state[0xA1B] = readSpi(0xA1B); + // TX NCO + state[0x11B] = readSpi(0x11B); + state[0x11E] = readSpi(0x11E); + state[0x128] = readSpi(0x128); + state[0x161] = readSpi(0x161); + state[0x162] = readSpi(0x162); + state[0x164] = readSpi(0x164); + state[0x165] = readSpi(0x165); + state[0x166] = readSpi(0x166); + state[0x167] = readSpi(0x167); + state[0x168] = readSpi(0x168); + state[0x169] = readSpi(0x169); + // PLL Source + state[0x775] = readSpi(0x775); + state[0x77D] = readSpi(0x77D); + state[0x6C3] = readSpi(0x6C3); + return state; +} + +void restoreQecState(const std::map& state) +{ + LOGGING("恢复校准前寄存器配置"); + for (const auto i : state) { + writeSpi(i.first, i.second); + } +} + +void preparePowerForRxCalib(const PllSource& pllSource) +{ + LOGGING("为Rx校准配置电源管理"); + writeSpi(0x02E, 0x03); + Register r03F = readSpi(0x03F); + r03F[2] = pllSource.rx3Source != LoSource::Pll5; + r03F[1] = pllSource.rx3Source != LoSource::Pll5; + r03F[0] = 0; + Register r040 = readSpi(0x040); + r040[7] = 0; + r040[6] = 1; + r040[5] = 1; + r040[4] = !(pllSource.rx1Source == LoSource::Pll2 || pllSource.rx2Source == LoSource::Pll2); + r040[3] = !(pllSource.rx1Source == LoSource::Pll2 || pllSource.rx2Source == LoSource::Pll2); + r040[2] = !(pllSource.rx1Source == LoSource::Pll1 || pllSource.rx2Source == LoSource::Pll1); + r040[1] = !(pllSource.rx1Source == LoSource::Pll1 || pllSource.rx2Source == LoSource::Pll1); + writeSpi(0x03F, r03F); + writeSpi(0x040, r040); +} + +void preparePllForRxCalib(const CX9261AConfiguration& config, const RxChannel chan) +{ + LOGGING("为" + toString(chan) + "校准配置PLL"); + auto pllSource = config.pllSource; + if (chan == RxChannel::Rx1) { + pllSource.rx2Source = LoSource::Off; + pllSource.tx1Source = LoSource::Pll4; + pllSource.tx2Source = LoSource::Off; + pllSource.txUseExt = false; + configPllSource(pllSource); + const auto rx1Freq = config.pllSource.rxUseExt ? config.pllFreq.rxExtFreq + : config.pllSource.rx1Source == LoSource::Pll1 ? config.pllFreq.pll1Freq + : config.pllFreq.pll2Freq; + if (config.pllSource.rx1Source == LoSource::Pll1) + afcPll1(); + if (config.pllSource.rx1Source == LoSource::Pll2) + afcPll2(); + configPll4(rx1Freq >= 3500 ? (rx1Freq + config.calib.f0) / 3 : (rx1Freq + config.calib.f0), config.clock); + } + if (chan == RxChannel::Rx2) { + pllSource.rx1Source = LoSource::Off; + pllSource.tx1Source = LoSource::Off; + pllSource.tx2Source = LoSource::Pll4; + pllSource.txUseExt = false; + configPllSource(pllSource); + const auto rx2Freq = config.pllSource.rxUseExt ? config.pllFreq.rxExtFreq + : config.pllSource.rx2Source == LoSource::Pll1 ? config.pllFreq.pll1Freq + : config.pllFreq.pll2Freq; + if (config.pllSource.rx2Source == LoSource::Pll1) + afcPll1(); + if (config.pllSource.rx2Source == LoSource::Pll2) + afcPll2(); + configPll4(rx2Freq >= 3500 ? (rx2Freq + config.calib.f0) / 3 : (rx2Freq + config.calib.f0), config.clock); + } + if (chan == RxChannel::Rx3) { + pllSource.tx1Source = LoSource::Off; + pllSource.tx2Source = LoSource::Pll4; + pllSource.txUseExt = false; + configPllSource(pllSource); + afcPll5(); + configPll4(config.pllFreq.pll5Freq >= 3500 ? (config.pllFreq.pll5Freq + config.calib.f0) / 3 + : (config.pllFreq.pll5Freq + config.calib.f0), + config.clock); + } + afcPll4(); +} + +bool waitRxTSTFinished() +{ + LOGGING("等待Rx时域校准结束"); + int cnt = 0; + pause(qecDelay); + while (cnt <= 10) { + if ((readSpi(0x14D) & 16) == 16) + return true; + cnt++; + pause(100); + } + return false; +} + +void rxTSTCalib(const RxChannel chan) +{ + LOGGING(toString(chan) + "时域校准"); + writeSpi(0x000, chan == RxChannel::Rx1 ? 0x12 : chan == RxChannel::Rx2 ? 0x22 : 0x32); + writeSpi(0x109, 0x00); + writeSpi(0x77E, chan == RxChannel::Rx1 ? 0x50 : chan == RxChannel::Rx2 ? 0x0A : 0x03); + writeSpi(0x153, 0x01); + writeSpi(0x14E, 0x00); + writeSpi(0x121, 0x00); + writeSpi(0x11F, 0x73); + writeSpi(0x124, 0xFF); + writeSpi(0x120, 0x00); + writeSpi(0x122, 0x03); + writeSpi(0x126, 0x01); + const auto qecAddr = chan == RxChannel::Rx1 ? 0x02A : chan == RxChannel::Rx2 ? 0x02B : 0x02C; + writeSpi(qecAddr, 0x00); + writeSpi(qecAddr, chan == RxChannel::Rx3 ? 0X11 : 0X12); + waitRxTSTFinished(); + if (chan == RxChannel::Rx3) + writeSpi(qecAddr, 0x00); + writeSpi(0x77E, 0x00); +} + +void rxDelayCalib(const double fs, const RxChannel chan) +{ + LOGGING(toString(chan) + "延时线校准"); + writeSpi(0x000, chan == RxChannel::Rx1 ? 0x12 : chan == RxChannel::Rx2 ? 0x22 : 0x32); + writeSpi(0x77E, chan == RxChannel::Rx1 ? 0x50 : chan == RxChannel::Rx2 ? 0x0A : 0x03); + writeSpi(0x11B, 0x01); + writeSpi(0x11E, 0x01); + configTxNcoFreq(0, fs); + writeSpi(0x161, 0x00); + writeSpi(0x162, 0x00); + writeSpi(0x162, 0x01); + writeSpi(0x161, 0x01); + writeSpi(chan == RxChannel::Rx1 ? 0x706 : chan == RxChannel::Rx2 ? 0x722 : 0x73E, 0x6D); + writeSpi(0x6C3, 0x00); + writeSpi(0x11F, 0x73); + writeSpi(0x153, 0x00); + writeSpi(chan == RxChannel::Rx2 ? 0x724 : 0x708, 0x24); + writeSpi(0x196, 0x01); + const auto qecAddr = chan == RxChannel::Rx1 ? 0x02A : chan == RxChannel::Rx2 ? 0x02B : 0x02C; + writeSpi(qecAddr, chan == RxChannel::Rx3 ? 0x11 : 0x12); + waitRxTSTFinished(); + writeSpi(qecAddr, 0x00); + writeSpi(0x196, 0x11); + const auto r13D = readSpi(0x13D); + const auto r13E = readSpi(0x13E); + const auto B = r13D * std::pow(2, 8) + r13E; + const auto B_s = B >= 32768 ? B - 65536 : B; + const auto B_abs = static_cast(std::abs(B_s)); + const auto B_delay = (B_abs & 0b0111111111000000) >> 6; + if (B_s > 0) { + writeSpi(0x199, B_delay >> 8); + writeSpi(0x19A, B_delay & 0xFF); + writeSpi(0x19B, 0); + writeSpi(0x19C, 0); + } else { + writeSpi(0x199, 0); + writeSpi(0x19A, 0); + writeSpi(0x19B, B_delay >> 8); + writeSpi(0x19C, B_delay & 0xFF); + } + writeSpi(0x196, 0x10); + writeSpi(0x153, 0x00); + writeSpi(qecAddr, chan == RxChannel::Rx3 ? 0x11 : 0x12); + waitRxTSTFinished(); + writeSpi(qecAddr, 0x00); + writeSpi(0x77E, 0x00); +} + +void configDcCalib(const Calib& config) +{ + LOGGING("配置DC校准"); + configDcCalibMethod(config.dcCalibMethod); + configDcTrackingLength(config.dcTrackingLength); + configDcHighPassFactor(config.dcHighPassFactor); +} + +void configDcCalibMethod(const DCCalibMethod method) +{ + LOGGING("配置校准方法") + writeSpi(0x109, method == DCCalibMethod::Tracking ? 0x11 : 0x00); + Register r11F = readSpi(0x11F); + r11F[6] = method != DCCalibMethod::Tracking; + writeSpi(0x11F, r11F); + writeSpi(0x155, method == DCCalibMethod::HighPass ? 0x01 : 0x00); +} + +void configDcTrackingLength(const int length) +{ + LOGGING("配置Tracking统计长度") + writeSpi(0x10A, static_cast(std::log2(std::max(1, length / 128))) << 4); +} + +void configDcHighPassFactor(const int factor) +{ + LOGGING("配置高通滤波器系数") + writeSpi(0x157, (factor & 0xFF00) >> 8); + writeSpi(0x156, factor & 0xFF); +} + +void rxCalib(const CX9261AConfiguration& config, const RxChannel chan) +{ + LOGGING(toString(chan) + "校准"); + if (config.calib.rxMethod == CalibMethod::TST) { + if (chan == RxChannel::Rx1 && config.pllSource.rx1Source == LoSource::Off) { + LOGGING(toString(chan) + " OFF") + return; + } + if (chan == RxChannel::Rx2 && config.pllSource.rx2Source == LoSource::Off) { + LOGGING(toString(chan) + " OFF") + return; + } + if (chan == RxChannel::Rx3 && config.pllSource.rx3Source == LoSource::Off) { + LOGGING(toString(chan) + " OFF") + return; + } + preparePowerForRxCalib(config.pllSource); + preparePllForRxCalib(config, chan); + rxTSTCalib(chan); + } + if (config.calib.rxMethod == CalibMethod::MultiPoints) + configRxMultiPointsQec(config, config.calib.rxMultiPoints, chan); + if (config.calib.rxMethod == CalibMethod::Tracking) { + preparePowerForRxCalib(config.pllSource); + preparePllForRxCalib(config, chan); + } +} + +void configCalibMethod(const Calib& calib) +{ + LOGGING("切换校准方法") + Register r11F = readSpi(0x11F); + r11F.replace(5, 4, calib.rxMethod == CalibMethod::Tracking ? 0b00 : 0b11); + writeSpi(0x11F, r11F); + writeSpi(0x15C, 0x00); + if (calib.rxMethod == CalibMethod::Tracking) { + writeSpi(0x102, 0xF1); + writeSpi(0xA07, 0x00); + writeSpi(0xA08, 0x00); + writeSpi(0x096, 0x00); + // TODO 此处默认开启RX1和RX2的Tracking,后续需要对通道进一步处理 + writeSpi(0x07A, 0x00); + writeSpi(0x080, 0x00); + writeSpi(0x09C, 0x03); + + writeSpi(0x102, 0xFF); + writeSpi(0x160, 0x01); + writeSpi(0x09D, 0x08); + writeSpi(0x09E, 0x03); + writeSpi(0x09F, 0xE8); + writeSpi(0x0A4, 0x00); + writeSpi(0x0A5, 0x01); + writeSpi(0x094, 0x02); + pause(100); + writeSpi(0x15C, 0x11); + } else { + writeSpi(0x094, + calib.rxMethod == CalibMethod::MultiPoints || calib.txMethod == CalibMethod::MultiPoints ? 0x01 + : 0x00); + } +} + +CalibPredis readRxCalibPredis(const RxChannel chan) +{ + LOGGING(std::string("读取") + toString(chan) + "校准值"); + writeSpi(0x000, chan == RxChannel::Rx1 ? 0x12 : chan == RxChannel::Rx2 ? 0x22 : 0x32); + writeSpi(0x121, 0x00); + const CalibPredis predis{ .bHigh = readSpi(0x13D), + .bLow = readSpi(0x13E), + .cHigh = readSpi(0x13F), + .cLow = readSpi(0x140), + .dciHigh = readSpi(0x141), + .dciLow = readSpi(0x142), + .dcqHigh = readSpi(0x143), + .dcqLow = readSpi(0x144) }; + writeSpi(0x000, 02); + return predis; +} + +void saveRxCalibPredis(const unsigned char index, const RxChannel chan, const LoSource pll) +{ + LOGGING(std::string("保存") + toString(chan) + " " + toString(pll) + " index: " + std::to_string(index) + + " 校准值"); + writeSpi(0x0A0, 0x00); + writeSpi(0x0A1, pll == LoSource::Pll1 ? 0x00 : pll == LoSource::Pll2 ? 0x01 : 0x04); + writeSpi(0x0A2, chan == RxChannel::Rx1 ? 0x00 : chan == RxChannel::Rx2 ? 0x01 : 0x02); + writeSpi(0x0A3, index); + writeSpi(0x0A0, 0x01); +} + +void loadRxCalibPredis(const unsigned char index, const RxChannel chan, const LoSource pll) +{ + LOGGING("加载: " + toString(chan) + " " + toString(pll) + " " + std::to_string(index) + " 校准值") + writeSpi(0x0A0, 0x00); + writeSpi(0x0A1, pll == LoSource::Pll1 ? 0x00 : pll == LoSource::Pll2 ? 0x01 : 0x04); + writeSpi(0x0A2, chan == RxChannel::Rx1 ? 0x00 : chan == RxChannel::Rx2 ? 0x01 : 0x02); + writeSpi(0x0A3, index); + writeSpi(0x0A0, 0x02); +} + +void configRxMultiPointsQecImpl(const CX9261AConfiguration& config, const std::vector& points, + const RxChannel chan) +{ + auto mutConfig = config; + for (auto i = 0; i < points.size(); ++i) { + LOGGING("频点: " + std::to_string(points[i])); + const auto source = chan == RxChannel::Rx1 ? config.pllSource.rx1Source + : chan == RxChannel::Rx2 ? config.pllSource.rx2Source + : config.pllSource.rx3Source; + if (source == LoSource::Pll1) { + mutConfig.pllFreq.pll1Freq = points[i]; + configPll1(points[i], config.clock); + } + if (source == LoSource::Pll2) { + mutConfig.pllFreq.pll2Freq = points[i]; + configPll2(points[i], config.clock); + } + if (source == LoSource::Pll5) { + mutConfig.pllFreq.pll5Freq = points[i]; + configPll5(points[i], config.clock); + } + preparePllForRxCalib(mutConfig, chan); + rxTSTCalib(chan); + saveRxCalibPredis(i, chan, source); + } +} + +void configRxMultiPointsQec(const CX9261AConfiguration& config, const std::vector& points, const RxChannel chan) +{ + LOGGING(toString(chan) + "多点校准"); + auto mutConfig = config; + for (int i = chan == RxChannel::Rx3 ? 4 : 0; i < 5; ++i) { + auto& source = chan == RxChannel::Rx1 ? mutConfig.pllSource.rx1Source + : chan == RxChannel::Rx2 ? mutConfig.pllSource.rx2Source + : mutConfig.pllSource.rx3Source; + const auto& calibPll = chan == RxChannel::Rx1 ? mutConfig.calib.rx1CalibPll + : chan == RxChannel::Rx2 ? mutConfig.calib.rx2CalibPll + : mutConfig.calib.rx3CalibPll; + if (calibPll & (0b1 << i)) { + source = static_cast(calibPll & (0b1 << i)); + preparePowerForRxCalib(mutConfig.pllSource); + configRxMultiPointsQecImpl(mutConfig, points, chan); + } + } +} + +void preparePowerForTxCalib(const PllSource& pllSource) +{ + LOGGING("为Tx校准配置电源管理"); + writeSpi(0x02E, 0x03); + Register r03F = readSpi(0x03F); + r03F[2] = 1; + r03F[1] = 1; + r03F[0] = !(pllSource.tx1Source == LoSource::Pll4 || pllSource.tx2Source == LoSource::Pll4); + Register r040 = readSpi(0x040); + r040[7] = !(pllSource.tx1Source == LoSource::Pll4 || pllSource.tx2Source == LoSource::Pll4); + r040[6] = !(pllSource.tx1Source == LoSource::Pll3 || pllSource.tx2Source == LoSource::Pll3); + r040[5] = !(pllSource.tx1Source == LoSource::Pll3 || pllSource.tx2Source == LoSource::Pll3); + r040[4] = !(pllSource.tx1Source == LoSource::Pll2 || pllSource.tx2Source == LoSource::Pll2); + r040[3] = !(pllSource.tx1Source == LoSource::Pll2 || pllSource.tx2Source == LoSource::Pll2); + r040[2] = !(pllSource.tx1Source == LoSource::Pll1 || pllSource.tx2Source == LoSource::Pll1); + r040[1] = !(pllSource.tx1Source == LoSource::Pll1 || pllSource.tx2Source == LoSource::Pll1); + writeSpi(0x03F, r03F); + writeSpi(0x040, r040); +} + +void preparePllForTxCalib(const CX9261AConfiguration& config) +{ + LOGGING("为Tx校准配置PLL"); + configPllSource(config.pllSource); + if (config.pllSource.tx1Source == LoSource::Pll4 || config.pllSource.tx2Source == LoSource::Pll4) + afcPll4(); + if (config.pllSource.tx1Source == LoSource::Pll3 || config.pllSource.tx2Source == LoSource::Pll3) + afcPll3(); + if (config.pllSource.tx1Source == LoSource::Pll2 || config.pllSource.tx2Source == LoSource::Pll2) + afcPll2(); + if (config.pllSource.tx1Source == LoSource::Pll1 || config.pllSource.tx2Source == LoSource::Pll1) + afcPll1(); +} + +void txBisectionQecCalib(const double f0, const double fs) +{ + LOGGING("Tx二分法校准"); + writeSpi(0x02E, 0x03); + writeSpi(0x128, 0x11); + writeSpi(0x11E, 0x01); + writeSpi(0x77E, 0x24); + writeSpi(0x11F, 0x01); + writeSpi(0x121, 0x02); + writeSpi(0x11B, 0x01); + writeSpi(0x161, 0x01); + writeSpi(0x162, 0x01); + writeSpi(0x163, 0x00); + configTxNcoFreq(f0, fs * 4); + writeSpi(0xA2D, 0x02); + writeSpi(0xA07, 0x00); + writeSpi(0xA08, 0x0F); + writeSpi(0xA1C, 0x01); + configRxNcoFreq(f0 * 2, fs); + writeSpi(0x102, 0x0F); + writeSpi(0x102, 0xFF); + writeSpi(0x186, 0x00); + writeSpi(0x186, 0x01); + writeSpi(0x763, 0x34); + writeSpi(0x76D, 0x34); + writeSpi(0x101, 0xEF); + writeSpi(0x101, 0xFF); + writeSpi(0x800, 0x00); + writeSpi(0x81A, 0xF7); + writeSpi(0x81B, 0x34); + writeSpi(0x81C, 0x33); + writeSpi(0x81D, 0x12); + writeSpi(0x81E, 0x00); + writeSpi(0x81F, 0xFF); + writeSpi(0x820, 0x00); + writeSpi(0x821, 0xFF); + writeSpi(0x800, 0x80); + pause(100); + writeSpi(0x800, 0x81); + pause(100); + writeSpi(0x77E, 0x00); +} + +void txTSTCalib(const CX9261AConfiguration& config, const TxChannel chan) +{ + LOGGING(toString(chan) + "时域校准"); + // writeSpi(chan == TxChannel::Tx1 ? 0x763 : 0x76D, 0x04); + writeSpi(0x000, chan == TxChannel::Tx1 ? 0x12 : 0x22); + writeSpi(0x109, 0x00); + writeSpi(0x101, 0xFF); + writeSpi(0x120, 0x10); + writeSpi(0x122, 0x03); + writeSpi(0x124, 0xFF); + writeSpi(0x11B, 0x01); + writeSpi(0x128, 0x11); + configTxNcoFreq(config.calib.f0, config.clock.adc * 4); + writeSpi(0x161, 0x00); + writeSpi(0x162, 0x00); + writeSpi(0x162, 0x01); + writeSpi(0x161, 0x01); + writeSpi(0x11F, 0x73); + writeSpi(0x121, 0x00); + writeSpi(0x14E, 0x00); + writeSpi(0x126, 0x03); + writeSpi(0x77E, chan == TxChannel::Tx1 ? 0x40 : 0x08); + writeSpi(chan == TxChannel::Tx1 ? 0x02A : 0x02B, 0x11); + pause(qecDelay); + while (true) { + if ((readSpi(0x14D) & 0x30) == 0x30) + break; + pause(100); + } + writeSpi(chan == TxChannel::Tx1 ? 0x02A : 0x02B, 0x00); + writeSpi(0x77E, 0x00); + writeSpi(0x128, 0x00); + writeSpi(0x11B, 0x00); +} + +void txBisectionDcCalib(const double f0, const double fs) +{ + LOGGING("Tx二分法DC校准"); + writeSpi(0x77E, 0x24); + writeSpi(0x11F, 0x03); + writeSpi(0x121, 0x02); + writeSpi(0x11B, 0x01); + writeSpi(0x11E, 0x01); + writeSpi(0x161, 0x01); + writeSpi(0x162, 0x01); + configTxNcoFreq(f0, fs * 4); + writeSpi(0xA2D, 0x02); + writeSpi(0xA07, 0x00); + writeSpi(0xA08, 0x0F); + writeSpi(0xA1C, 0x01); + configRxNcoFreq(f0, fs); + writeSpi(0x81A, 0xF7); + writeSpi(0x81B, 0x44); + writeSpi(0x81C, 0x33); + writeSpi(0x81D, 0x14); + writeSpi(0x800, 0x80); + writeSpi(0x800, 0x82); + pause(100); + writeSpi(0x77E, 0x00); +} + +void configTxMultiPointsQecImpl(const CX9261AConfiguration& config, const std::vector& points) +{ + LOGGING("Tx多点校准"); + for (auto i = 0; i < points.size(); ++i) { + LOGGING("频点: " + std::to_string(points[i])); + configTxChannelFrequency(points[i], TxChannel::Tx1); + configTxChannelFrequency(points[i], TxChannel::Tx2); + if (config.pllSource.tx1Source == LoSource::Pll1 || config.pllSource.tx2Source == LoSource::Pll1) { + configPll1(points[i], config.clock); + afcPll1(); + } + if (config.pllSource.tx1Source == LoSource::Pll2 || config.pllSource.tx2Source == LoSource::Pll2) { + configPll2(points[i], config.clock); + afcPll2(); + } + if (config.pllSource.tx1Source == LoSource::Pll3 || config.pllSource.tx2Source == LoSource::Pll3) { + configPll3(points[i], config.clock); + afcPll3(); + } + if (config.pllSource.tx1Source == LoSource::Pll4 || config.pllSource.tx2Source == LoSource::Pll4) { + configPll4(points[i], config.clock); + afcPll4(); + } + writeSpi(0x101, 0xEF); + writeSpi(0x101, 0xFF); + txBisectionQecCalib(config.calib.f0, config.clock.adc); + txBisectionDcCalib(config.calib.f0, config.clock.adc); + writeSpi(0x000, 0x12); + saveTxCalibPredis(i, TxChannel::Tx1, config.pllSource.tx1Source); + writeSpi(0x000, 0x22); + saveTxCalibPredis(i, TxChannel::Tx2, config.pllSource.tx2Source); + writeSpi(0x000, 0x02); + } +} + +void configTxMultiPointsQec(const CX9261AConfiguration& config, const std::vector& points) +{ + auto mutConfig = config; + for (int i = 0; i < 5; ++i) { + const auto& calibPll = mutConfig.calib.tx1CalibPll; + if (calibPll & (0b1 << i)) { + mutConfig.pllSource.tx1Source = static_cast(calibPll & (0b1 << i)); + mutConfig.pllSource.tx2Source = static_cast(calibPll & (0b1 << i)); + preparePowerForTxCalib(mutConfig.pllSource); + preparePllForTxCalib(mutConfig); + configTxMultiPointsQecImpl(mutConfig, points); + } + } +} + +void txCalib(const CX9261AConfiguration& config) +{ + LOGGING("Tx校准"); + preparePowerForTxCalib(config.pllSource); + preparePllForTxCalib(config); + if (config.calib.txMethod == CalibMethod::Bisec) { + txBisectionQecCalib(config.calib.f0, config.clock.adc); + txBisectionDcCalib(config.calib.f0, config.clock.adc); + } + if (config.calib.txMethod == CalibMethod::TST) { + txTSTCalib(config, TxChannel::Tx1); + txTSTCalib(config, TxChannel::Tx2); + } + if (config.calib.txMethod == CalibMethod::MultiPoints) { + configTxMultiPointsQec(config, config.calib.txMultiPoints); + } +} + +void saveTxCalibPredis(const unsigned char index, const TxChannel chan, const LoSource pll) +{ + LOGGING(std::string("保存") + toString(chan) + " " + "pll: " + toString(pll) + " " + + " index: " + std::to_string(index) + " 校准值"); + writeSpi(0x0A0, 0x00); + writeSpi(0x0A1, pll == LoSource::Pll1 ? 0x00 : pll == LoSource::Pll2 ? 0x01 : pll == LoSource::Pll3 ? 0x02 : 0x03); + writeSpi(0x0A2, chan == TxChannel::Tx1 ? 0x03 : 0x04); + writeSpi(0x0A3, index); + writeSpi(0x0A0, 0x01); +} + +void loadTxCalibPredis(const unsigned char index, const TxChannel chan, const LoSource pll) +{ + LOGGING("加载: " + toString(chan) + toString(pll) + " " + " " + std::to_string(index) + " 校准值") + writeSpi(0x0A0, 0x00); + writeSpi(0x0A1, pll == LoSource::Pll1 ? 0x00 : pll == LoSource::Pll2 ? 0x01 : pll == LoSource::Pll3 ? 0x02 : 0x03); + writeSpi(0x0A2, chan == TxChannel::Tx1 ? 0x03 : 0x04); + writeSpi(0x0A3, index); + writeSpi(0x0A0, 0x02); +} + +void configQecCalib(const CX9261AConfiguration& config) +{ + LOGGING("配置QEC校准"); + const auto regsState = beforeQecState(); + if (config.calib.rxMethod == CalibMethod::MultiPoints || config.calib.rxMethod == CalibMethod::Tracking + || config.calib.txMethod == CalibMethod::MultiPoints) { + configRISCV(true); + } + // Rx使用跟踪校准时,Tx无法使用多点校准,且此时配置校准模式应置于校准结束之后 + if (config.calib.rxMethod != CalibMethod::Tracking) + configCalibMethod(config.calib); + // Rx校准只在有任意Rx通道不是Off时发生 + const bool calibRx = config.pllSource.rx1Source != LoSource::Off || config.pllSource.rx2Source != LoSource::Off + || config.pllSource.rx3Source != LoSource::Off; + if (calibRx) { + rxCalib(config, RxChannel::Rx1); + rxCalib(config, RxChannel::Rx2); + rxCalib(config, RxChannel::Rx3); + // 恢复校准前配置 + restoreQecState(regsState); + configPll4(config.pllFreq.pll4Freq, config.clock); + if ((config.pllSource.tx1Source == LoSource::Pll4 || config.pllSource.tx2Source == LoSource::Pll4) + && config.interface.workMode != WorkMode::Rx) + afcPll4(); + } + // 如果Rx使用了多点校准模式,则需要在校准结束后切换回初始化频点 + if (calibRx && config.calib.rxMethod == CalibMethod::MultiPoints) { + LOGGING("Rx通道切换到初始化频点"); + if (config.pllSource.rx1Source == LoSource::Pll1 || config.pllSource.rx2Source == LoSource::Pll1) { + configPll1(config.pllFreq.pll1Freq, config.clock); + afcPll1(); + } + if (config.pllSource.rx1Source == LoSource::Pll2 || config.pllSource.rx2Source == LoSource::Pll2) { + configPll2(config.pllFreq.pll2Freq, config.clock); + afcPll2(); + } + if (config.pllSource.rx3Source == LoSource::Pll5) { + configPll5(config.pllFreq.pll5Freq, config.clock); + afcPll5(); + } + auto loadRxDefault = [&config](const RxChannel chan, const LoSource source, const double freq) { + auto points = config.calib.rxMultiPoints; + std::sort(points.begin(), points.end()); + const auto location = std::lower_bound(points.begin(), points.end(), freq); + if (location != points.end()) { + const auto fundFreq = *location; + const auto it = + std::find(config.calib.rxMultiPoints.begin(), config.calib.rxMultiPoints.end(), fundFreq); + if (it != config.calib.rxMultiPoints.end()) { + loadRxCalibPredis(it - config.calib.rxMultiPoints.begin(), chan, source); + } + } + }; + if (config.pllSource.rx1Source != LoSource::Off) { + LOGGING("Rx1加载初始化系数"); + const auto rx1Freq = config.pllSource.rxUseExt ? config.pllFreq.rxExtFreq + : config.pllSource.rx1Source == LoSource::Pll1 ? config.pllFreq.pll1Freq + : config.pllFreq.pll2Freq; + loadRxDefault(RxChannel::Rx1, config.pllSource.rx1Source, rx1Freq); + } + if (config.pllSource.rx2Source != LoSource::Off) { + LOGGING("Rx2加载初始化系数"); + const auto rx2Freq = config.pllSource.rxUseExt ? config.pllFreq.rxExtFreq + : config.pllSource.rx2Source == LoSource::Pll1 ? config.pllFreq.pll1Freq + : config.pllFreq.pll2Freq; + loadRxDefault(RxChannel::Rx2, config.pllSource.rx2Source, rx2Freq); + } + if (config.pllSource.rx3Source != LoSource::Off) { + LOGGING("Rx3加载初始化系数"); + const auto rx3Freq = config.pllSource.rxUseExt ? config.pllFreq.rxExtFreq : config.pllFreq.pll5Freq; + loadRxDefault(RxChannel::Rx3, config.pllSource.rx3Source, rx3Freq); + } + } + // Tx校准只在任意Tx通道不是Off时发生 + const bool calibTx = config.pllSource.tx1Source != LoSource::Off || config.pllSource.tx2Source != LoSource::Off; + if (calibTx) { + txCalib(config); + restoreQecState(regsState); + } + // 如果Tx使用了多点校准模式,则需要在校准结束后切换回初始化频点 + if (calibTx && config.calib.txMethod == CalibMethod::MultiPoints) { + LOGGING("Tx通道切换到初始化频点"); + if (config.pllSource.tx1Source == LoSource::Pll1 || config.pllSource.tx2Source == LoSource::Pll1) { + configPll1(config.pllFreq.pll1Freq, config.clock); + afcPll1(); + } + if (config.pllSource.tx1Source == LoSource::Pll2 || config.pllSource.tx2Source == LoSource::Pll2) { + configPll2(config.pllFreq.pll2Freq, config.clock); + afcPll2(); + } + if (config.pllSource.tx1Source == LoSource::Pll3 || config.pllSource.tx2Source == LoSource::Pll3) { + configPll3(config.pllFreq.pll3Freq, config.clock); + afcPll3(); + } + if (config.pllSource.tx1Source == LoSource::Pll4 || config.pllSource.tx2Source == LoSource::Pll4) { + configPll4(config.pllFreq.pll4Freq, config.clock); + afcPll4(); + } + auto loadTxDefault = [&config](const TxChannel chan, const LoSource source, const double freq) { + auto points = config.calib.txMultiPoints; + std::sort(points.begin(), points.end()); + const auto location = std::lower_bound(points.begin(), points.end(), freq); + if (location != points.end()) { + const auto fundFreq = *location; + const auto it = + std::find(config.calib.txMultiPoints.begin(), config.calib.txMultiPoints.end(), fundFreq); + if (it != config.calib.txMultiPoints.end()) { + loadTxCalibPredis(it - config.calib.txMultiPoints.begin(), chan, source); + } + } + }; + if (config.pllSource.tx1Source != LoSource::Off) { + const auto tx1Freq = config.pllSource.txUseExt ? config.pllFreq.txExtFreq + : config.pllSource.tx1Source == LoSource::Pll1 ? config.pllFreq.pll1Freq + : config.pllSource.tx1Source == LoSource::Pll2 ? config.pllFreq.pll2Freq + : config.pllSource.tx1Source == LoSource::Pll3 ? config.pllFreq.pll3Freq + : config.pllFreq.pll4Freq; + loadTxDefault(TxChannel::Tx1, config.pllSource.tx1Source, tx1Freq); + } + if (config.pllSource.tx2Source != LoSource::Off) { + const auto tx2Freq = config.pllSource.txUseExt ? config.pllFreq.txExtFreq + : config.pllSource.tx2Source == LoSource::Pll1 ? config.pllFreq.pll1Freq + : config.pllSource.tx2Source == LoSource::Pll2 ? config.pllFreq.pll2Freq + : config.pllSource.tx2Source == LoSource::Pll3 ? config.pllFreq.pll3Freq + : config.pllFreq.pll4Freq; + loadTxDefault(TxChannel::Tx2, config.pllSource.tx2Source, tx2Freq); + } + } + // 仅在rx使用Tracking校准时在此处配置 + if (config.calib.rxMethod == CalibMethod::Tracking) + configCalibMethod(config.calib); + + Register r121 = 0x00; + r121[0] = config.calib.rxMethod == CalibMethod::Bisec; + r121[1] = config.calib.txMethod == CalibMethod::Bisec; + r121.replace(5, 4, config.calib.rxMethod == CalibMethod::MultiPoints ? 0b10 : 0b00); + r121.replace(7, 6, config.calib.txMethod == CalibMethod::MultiPoints ? 0b10 : 0b00); + writeSpi(0x121, r121); +} + +void configTxNco(const TxNCO& nco, const double fs) +{ + LOGGING("配置Tx NCO"); + configTxBIST(nco.txUseBist); + configTxNcoFreq(nco.txNCOFreq, fs); +} + +void configTxBIST(const bool bist) +{ + LOGGING("配置Tx BIST"); + writeSpi(0x128, 0x10); + writeSpi(0x11B, bist); + writeSpi(0x11E, bist); + writeSpi(0x161, bist); + writeSpi(0x162, bist); +} + +void configTxNcoFreq(const double freq, const double fs) +{ + LOGGING("配置Tx NCO频率"); + const auto ratio = freq < 0 ? (freq + fs) / fs : freq / fs; + const auto bin = round64(ratio * std::pow(2, 48)); + writeSpi(0x164, bin >> 40 & 0xFF); + writeSpi(0x165, bin >> 32 & 0xFF); + writeSpi(0x166, bin >> 24 & 0xFF); + writeSpi(0x167, bin >> 16 & 0xFF); + writeSpi(0x168, bin >> 8 & 0xFF); + writeSpi(0x169, bin & 0xFF); +} + +void configRxNco(const RxNCO& nco, const double fs) +{ + LOGGING("配置Rx NCO"); + configRxNcoMode(nco.rxUseBist ? NCOMode::BIST + : nco.rxNCOBypass ? NCOMode::Bypass + : nco.rxNCOUpward ? NCOMode::Up + : NCOMode::Down); + configRxNcoMS(nco.rxNCOMaster ? NCOMode::Master : NCOMode::Slaver); + configRxNcoFreq(nco.rxNCOFreq, fs); +} + +void configRxNcoMode(const NCOMode mode) +{ + LOGGING("配置Rx NCO模式"); + writeSpi(0x11C, mode == NCOMode::BIST ? 0x3F : 0x00); + writeSpi(0xA07, mode == NCOMode::BIST ? 0x01 : 0x00); + writeSpi(0xA08, mode == NCOMode::Bypass ? 0x00 : mode == NCOMode::Up ? 0X07 : 0X87); +} + +void configRxNcoMS(const NCOMode mode) +{ + LOGGING("配置Rx NCO主从模式"); + writeSpi(0xA09, mode != NCOMode::Master); +} + +void configRxNcoFreq(const double freq, const double fs) +{ + LOGGING("配置Rx NCO频率"); + const auto ratio = freq < 0 ? (freq + fs) / fs : freq / fs; + const auto bin = round64(ratio * std::pow(2, 48)); + writeSpi(0xA16, bin >> 40 & 0xFF); + writeSpi(0xA17, bin >> 32 & 0xFF); + writeSpi(0xA18, bin >> 24 & 0xFF); + writeSpi(0xA19, bin >> 16 & 0xFF); + writeSpi(0xA1A, bin >> 8 & 0xFF); + writeSpi(0xA1B, bin & 0xFF); +} + +void configInterface(const Interface& interface) +{ + LOGGING("配置接口"); + writeSpi(0x02E, interface.workMode == WorkMode::Rx ? 0x04 : interface.workMode == WorkMode::Tx ? 0x05 : 0x06); + writeSpi(0x003, 0x33); + Register r200{ 0x00 }; + // 0x200 + if (interface.ioMode != IOMode::CMOS) + r200 = 0x0F; + else if (interface.trMode == TrMode::R1T1 && interface.portMode == PortMode::Single) + r200 = 0x00; + else + r200 = 0x01; + writeSpi(0x200, r200); + // 0x201 + if (interface.workMode == WorkMode::FDD) + writeSpi(0x201, 0x00); + else if (interface.portMode == PortMode::Single && interface.trMode == TrMode::R2T2) + writeSpi(0x201, 0x3F); + else + writeSpi(0x201, 0x15); + // 0x202 + writeSpi(0x202, interface.portMode == PortMode::Single ? 0x03 : 0x00); + // 0x206 + Register r206{ 0x00 }; + if (interface.ioMode == IOMode::LVDS_S) + r206 = 0x1F; + if (interface.ioMode == IOMode::LVDS_E) + r206 = 0x0F; + writeSpi(0x206, r206); + // 0x208 + if (interface.trMode == TrMode::R1T1 + && ((interface.workMode == WorkMode::FDD && interface.portMode == PortMode::Dual) + || (interface.workMode != WorkMode::FDD && interface.portMode == PortMode::Single))) + writeSpi(0x208, 0x0F); + else + writeSpi(0x208, 0x00); + // 0x209 + if (interface.portMode == PortMode::Dual && interface.trMode == TrMode::R1T1) + writeSpi(0x209, 0x80); + else + writeSpi(0x209, 0x00); + // 0x77F + if (interface.ioMode != IOMode::CMOS) + writeSpi(0x77F, 0x0F); + else + writeSpi(0x77F, 0x00); + // 0x780 + if (interface.portMode == PortMode::Dual && interface.workMode == WorkMode::FDD) + writeSpi(0x780, 0x30); + else + writeSpi(0x780, interface.workMode == WorkMode::FDD ? 0x11 : interface.workMode == WorkMode::Rx ? 0x00 : 0x33); + // 0x781 + writeSpi(0x781, interface.ioMode == IOMode::CMOS ? 0x6F : 0x01); +} + +void configPllSource(const PllSource& pllSource) +{ + LOGGING("配置PLL来源"); + // PLL Source + Register r77D; + r77D[7] = rxSrcMap.at({ pllSource.rx1Source, pllSource.rx2Source })[6]; + r77D[6] = rxSrcMap.at({ pllSource.rx1Source, pllSource.rx2Source })[5]; + r77D[5] = txSrcMap.at({ pllSource.tx1Source, pllSource.tx2Source })[6]; + r77D[4] = txSrcMap.at({ pllSource.tx1Source, pllSource.tx2Source })[5]; + r77D[3] = !pllSource.txUseExt && txSrcMap.at({ pllSource.tx1Source, pllSource.tx2Source })[10]; + r77D[2] = !pllSource.txUseExt && txSrcMap.at({ pllSource.tx1Source, pllSource.tx2Source })[9]; + r77D[1] = txSrcMap.at({ pllSource.tx1Source, pllSource.tx2Source })[8]; + r77D[0] = txSrcMap.at({ pllSource.tx1Source, pllSource.tx2Source })[7]; + + // MUX + Register r6C3 = readSpi(0x6C3); + r6C3[3] = !pllSource.rxUseExt && pllSource.rx1Source == LoSource::Off; + r6C3[2] = !pllSource.rxUseExt && pllSource.rx2Source == LoSource::Off; + r6C3[1] = !pllSource.txUseExt && pllSource.tx1Source == LoSource::Off; + r6C3[0] = !pllSource.txUseExt && pllSource.tx2Source == LoSource::Off; + + // External + Register r775{ 0x24 }; + r775[7] = pllSource.rxUseExt; + r775[6] = pllSource.txUseExt; + + writeSpi(0x77D, r77D); + writeSpi(0x775, r775); + writeSpi(0x6C3, r6C3); +} + +void configPowerMode(const PowerMode pm) +{ + LOGGING("配置电源模式"); + writeSpi(0x02E, static_cast(pm)); +} + +void configPowerPins(const Power& power, const PowerMode pm) +{ + Register reg1 = 0x00; + reg1[0] = power.dig_rx1; + reg1[1] = power.dig_rx2; + reg1[2] = power.dig_rx3; + reg1[3] = power.dig_tx1; + reg1[4] = power.dig_tx2; + Register reg2 = 0x00; + reg2[0] = !power.exlo; + reg2[1] = !power.bgr; + reg2[2] = !power.temp_sensor; + reg2[3] = !power.auxdac; + reg2[4] = !power.auxadc1; + reg2[5] = !power.auxadc2; + reg2[6] = !power.fref1_rx; + Register reg3 = 0x00; + reg3[0] = !power.pll4_ldo; + reg3[1] = !power.pll5; + reg3[2] = !power.pll5_ldo; + reg3[3] = !power.rx1; + reg3[4] = !power.rx2; + reg3[5] = !power.rx3; + reg3[6] = !power.tx1; + reg3[7] = !power.tx2; + Register reg4 = 0x00; + reg4[0] = !power.bbpll; + reg4[1] = !power.pll1; + reg4[2] = !power.pll1_ldo; + reg4[3] = !power.pll2; + reg4[4] = !power.pll2_ldo; + reg4[5] = !power.pll3; + reg4[6] = !power.pll3_ldo; + reg4[7] = !power.pll4; + const int initAddr = 0x031 + static_cast(pm) * 4; + writeSpi(initAddr, reg1); + writeSpi(initAddr + 1, reg2); + writeSpi(initAddr + 2, reg3); + writeSpi(initAddr + 3, reg4); +} + +void configRefDoubler(const bool doubler, const double ref) +{ + LOGGING("配置参考倍频"); + writeSpi(0x789, ref <= 75 ? 0x10 : 0x00); + writeSpi(0x785, doubler ? 0xC9 : 0x49); + writeSpi(0x788, 0x24); + pause(1); + writeSpi(0x788, 0xA4); +} + +void initialRxChannel(const RxChannel chan) +{ + LOGGING("初始化通道" + toString(chan)); + writeSpi(chan == RxChannel::Rx1 ? 0x705 : chan == RxChannel::Rx2 ? 0x721 : 0x73D, 0xFC); + writeSpi(chan == RxChannel::Rx1 ? 0x706 : chan == RxChannel::Rx2 ? 0x722 : 0x73E, 0x6D); + writeSpi(chan == RxChannel::Rx1 ? 0x707 : chan == RxChannel::Rx2 ? 0x723 : 0x73F, 0x91); + writeSpi(chan == RxChannel::Rx1 ? 0x708 : chan == RxChannel::Rx2 ? 0x724 : 0x740, 0x04); + writeSpi(chan == RxChannel::Rx1 ? 0x709 : chan == RxChannel::Rx2 ? 0x725 : 0x741, 0x94); + writeSpi(chan == RxChannel::Rx1 ? 0x70A : chan == RxChannel::Rx2 ? 0x726 : 0x742, 0xFC); + writeSpi(chan == RxChannel::Rx1 ? 0x70C : chan == RxChannel::Rx2 ? 0x728 : 0x744, 0xC0); +} + +std::pair configBandwidthImpl(const double bandwidth) +{ + const auto x = bandwidth / 172.8; + auto c = 255; + auto r = 0; + for (; c > 0; --c) { + r = floor(c * x + 128 * x - 1); + if (r <= 31 && r >= 0) + break; + } + r = clamp(r, 0, 31); + c = round((r + 1) / x) - 128; + return { r, c }; +} + +void configRxBandwidth(const double bandwidth, const RxChannel chan) +{ + LOGGING("配置带宽" + toString(chan)); + const auto rc = configBandwidthImpl(bandwidth); + const auto r = rc.first; + const auto c = rc.second; + Register rReg = 0xF8; + rReg.replace(7, 3, r); + writeSpi(chan == RxChannel::Rx1 ? 0x703 : chan == RxChannel::Rx2 ? 0x71F : 0x73B, c); + writeSpi(chan == RxChannel::Rx1 ? 0x704 : chan == RxChannel::Rx2 ? 0x720 : 0x73C, rReg); +} + +void configRxTIA(const int tia, const RxChannel chan) +{ + LOGGING("配置TIA" + toString(chan)); + Register tiaReg = 0x50; + tiaReg.replace(2, 0, tia); + writeSpi(chan == RxChannel::Rx1 ? 0x700 : chan == RxChannel::Rx2 ? 0x71C : 0x738, tiaReg); +} + +void configRxMixer(const int mixer) +{ + LOGGING("配置Mixer"); + writeSpi(0x907, mixer); +} + +void configRxChannel(const RxConfig& config, const RxChannel chan) +{ + LOGGING("配置通道" + toString(chan)); + configRxTIA(config.tia, chan); + int tiaCSel = round(1036.4364 * (config.tia + 1) / config.bandwidth - 40); + tiaCSel = clamp(tiaCSel, 0, 8191); + const Register c1 = tiaCSel >> 8; + const Register c2 = tiaCSel & 0xFF; + writeSpi(chan == RxChannel::Rx1 ? 0x701 : chan == RxChannel::Rx2 ? 0x71D : 0x739, c1); + writeSpi(chan == RxChannel::Rx1 ? 0x702 : chan == RxChannel::Rx2 ? 0x71E : 0x73A, c2); + configRxBandwidth(config.bandwidth, chan); + configRxMixer(config.mixer); +} + +void configRxFIR(const int fir) +{ + LOGGING("配置Rx FIR"); + Register firReg = readSpi(0x106); + firReg.replace(7, 5, fir == 8 ? 5 : fir); + writeSpi(0x106, firReg); +} + +void configRxHBF1(const int hbf) +{ + LOGGING("配置Rx HBF1"); + Register hbfReg = readSpi(0x106); + hbfReg[4] = hbf == 2; + writeSpi(0x106, hbfReg); +} + +void configRxHBF2(const int hbf) +{ + LOGGING("配置Rx HBF2"); + Register hbfReg = readSpi(0x106); + hbfReg[3] = hbf == 2; + writeSpi(0x106, hbfReg); +} + +void configRxHBF3(const int hbf) +{ + LOGGING("配置Rx HBF3"); + Register hbfReg = readSpi(0x106); + hbfReg.replace(2, 1, hbf == 0 ? 1 : hbf); + writeSpi(0x106, hbfReg); +} + +void configRxCIC(const int cic) +{ + LOGGING("配置Rx CIC"); + Register cicReg = readSpi(0x106); + cicReg[0] = cic == 20; + writeSpi(0x106, cicReg); +} + +void configRxFilters(const Filters& filters) +{ + LOGGING("配置Rx 滤波器"); + Register filtersReg; + filtersReg.replace(7, 5, filters.fir == 8 ? 5 : filters.fir); + filtersReg[4] = filters.hbf1 == 2; + filtersReg[3] = filters.hbf2 == 2; + filtersReg.replace(2, 1, filters.hbf3 == 0 ? 1 : filters.hbf3); + filtersReg[0] = filters.cic == 20; + writeSpi(0x106, filtersReg); +} + +void configRxFIRCoe(const std::vector& coe) +{ + LOGGING("配置Rx FIR系数"); + for (auto i = 0; i < coe.size(); ++i) { + const auto value = round(coe[i] * std::pow(2, 15)) + (coe[i] < 0 ? 65536 : 0); + writeSpi(0x401, i); + writeSpi(0x402, value >> 8 & 0xFF); + writeSpi(0x403, value & 0xFF); + writeSpi(0x401, 0x80 + i); + pause(1); + } + writeSpi(0x400, ceil(coe.size() / 16) - 1); + writeSpi(0x404, 00); + writeSpi(0x404, 01); +} + +std::pair configTxBandwidthImpl(const double bandwidth) +{ + const auto x = bandwidth / 144; + auto c = 127; + auto r = 0; + for (; c > 0; --c) { + r = floor(c * x + 128 * x - 1); + if (r <= 31 && r >= 0) + break; + } + r = clamp(r, 0, 31); + c = round((r + 1) / x) - 128; + return { r, c }; +} + +void configTxBandwidth(const double bandwidth, const TxChannel chan) +{ + LOGGING("配置通道" + toString(chan)); + const auto rc = configTxBandwidthImpl(bandwidth); + const auto r = rc.first; + const auto c = rc.second; + Register rReg = 0xAF; + rReg.replace(7, 3, r); + writeSpi(chan == TxChannel::Tx1 ? 0x75E : 0x768, rReg); + const Register cReg = c << 1; + writeSpi(chan == TxChannel::Tx1 ? 0x75D : 0x767, cReg); + Register lpfReg = readSpi(chan == TxChannel::Tx1 ? 0x75F : 0x769); + lpfReg.replace(7, 2, r); + writeSpi(chan == TxChannel::Tx1 ? 0x75F : 0x769, lpfReg); +} + +void configTxMixer(const int mixer, const TxChannel chan) +{ + LOGGING("配置Mixer" + toString(chan)); + Register mixerReg = 0xFF; + mixerReg.replace(7, 2, mixer); + writeSpi(chan == TxChannel::Tx1 ? 0x760 : 0x76A, mixerReg); +} + +void initialTxChannel(const TxChannel chan) +{ + LOGGING("初始化通道" + toString(chan)); + writeSpi(chan == TxChannel::Tx1 ? 0x75A : 0x764, 0x83); + writeSpi(chan == TxChannel::Tx1 ? 0x75B : 0x765, 0xDA); + pause(delay); + writeSpi(chan == TxChannel::Tx1 ? 0x75B : 0x765, 0x9A); + writeSpi(chan == TxChannel::Tx1 ? 0x75C : 0x766, 0x81); + writeSpi(chan == TxChannel::Tx1 ? 0x763 : 0x76D, 0x04); +} + +void configTxChannelFrequency(const double lo, const TxChannel chan) +{ + LOGGING("根据本振频率配置发射通道: " + toString(chan)); + Register r75F = readSpi(chan == TxChannel::Tx1 ? 0x75F : 0x769); + r75F.replace(1, 0, lo <= 3500 ? 0x01 : 0x00); + writeSpi(chan == TxChannel::Tx1 ? 0x75F : 0x769, r75F); + writeSpi(chan == TxChannel::Tx1 ? 0x761 : 0x76B, lo <= 3500 ? 0x04 : 0x07); + writeSpi(chan == TxChannel::Tx1 ? 0x762 : 0x76C, lo <= 3500 ? 0x70 : 0x6C); +} + +void configTxChannel(const TxConfig& config, const TxChannel chan) +{ + LOGGING("配置通道" + toString(chan)); + configTxBandwidth(config.bandwidth, chan); + configTxMixer(config.mixer, chan); +} + +void configTxFIR(const int fir) +{ + LOGGING("配置Tx FIR"); + const Register firReg = fir == 8 ? 5 : fir; + writeSpi(0x107, firReg); +} + +void configTxHBF1(const int hbf) +{ + LOGGING("配置Tx HBF1"); + Register hbfReg = readSpi(0x108); + hbfReg[7] = hbf == 2; + writeSpi(0x108, hbfReg); +} + +void configTxHBF2(const int hbf) +{ + LOGGING("配置Tx HBF2"); + Register hbfReg = readSpi(0x108); + hbfReg[6] = hbf == 2; + writeSpi(0x108, hbfReg); +} + +void configTxHBF3(const int hbf) +{ + LOGGING("配置Tx HBF3"); + Register hbfReg = readSpi(0x108); + hbfReg.replace(5, 4, hbf == 0 ? 1 : hbf); + writeSpi(0x108, hbfReg); +} + +void configTxCIC(const int cic) +{ + LOGGING("配置Tx CIC"); + Register cicReg = readSpi(0x108); + cicReg.replace(2, 0, cic == 20 ? 4 : cic == 8 ? 3 : cic == 80 ? 2 : cic == 32 ? 1 : 0); + writeSpi(0x108, cicReg); +} + +void configTxFilters(const Filters& filters) +{ + LOGGING("配置Tx 滤波器"); + const Register firReg = filters.fir == 8 ? 5 : filters.fir; + Register filtersReg; + filtersReg[7] = filters.hbf1 == 2; + filtersReg[6] = filters.hbf2 == 2; + filtersReg.replace(5, 4, filters.hbf3 == 0 ? 1 : filters.hbf3); + filtersReg.replace(2, 0, + filters.cic == 20 ? 4 + : filters.cic == 8 ? 3 + : filters.cic == 80 ? 2 + : filters.cic == 32 ? 1 + : 0); + writeSpi(0x107, firReg); + writeSpi(0x108, filtersReg); +} + +void configTxFIRCoe(const std::vector& coe) +{ + LOGGING("配置Tx FIR系数"); + for (auto i = 0; i < coe.size(); ++i) { + const auto value = round(coe[i] * std::pow(2, 15)) + (coe[i] < 0 ? 65536 : 0); + writeSpi(0x501, i); + writeSpi(0x502, value >> 8 & 0xFF); + writeSpi(0x503, value & 0xFF); + writeSpi(0x501, 0x80 + i); + pause(1); + } + writeSpi(0x500, ceil(coe.size() / 16) - 1); + writeSpi(0x504, 00); + writeSpi(0x504, 01); +} + +CalibPredis readTxCalibPredis(const TxChannel chan) +{ + LOGGING("读取校准系数: " + toString(chan)); + writeSpi(0x000, chan == TxChannel::Tx1 ? 0x12 : 0x22); + writeSpi(0x121, 0x00); + const CalibPredis predis{ .bHigh = readSpi(0x145), + .bLow = readSpi(0x146), + .cHigh = readSpi(0x147), + .cLow = readSpi(0x148), + .dciHigh = readSpi(0x149), + .dciLow = readSpi(0x14A), + .dcqHigh = readSpi(0x14B), + .dcqLow = readSpi(0x14C) }; + writeSpi(0x000, 02); + return predis; +} + +void configFPGA(const Interface& interface) +{ + setPin(33, 1); + setPin(39, 1); + setPin(40, 1); + + setPin(32, 1); + setPin(7, interface.ioMode == IOMode::CMOS ? 0 : 1); + setPin(34, 1); + setPin(34, 0); + + setPin(38, interface.portMode == PortMode::Single); + setPin(37, interface.trMode == TrMode::R2T2); + setPin(36, interface.workMode != WorkMode::FDD); + setPin(35, interface.workMode == WorkMode::Rx); + + setPin(34, 1); + setPin(34, 0); + + setPin(43, 0); + setPin(43, 1); +} + +void rxFFTCalib(const double lo, const double fs, const int firLength, const CX9261AConfiguration& config, + RxChannel chan) +{ + writeSpi(0x000, chan == RxChannel::Rx1 ? 0x12 : 0x22); + writeSpi(0x128, 0x00); + writeSpi(0x11B, 0x01); + writeSpi(0x11E, 0x01); + configTxNcoFreq(0, fs); + writeSpi(0x161, 0x00); + writeSpi(0x162, 0x00); + writeSpi(0x162, 0x01); + writeSpi(0x161, 0x01); + writeSpi(0x000, chan == RxChannel::Rx1 ? 0x12 : chan == RxChannel::Rx2 ? 0x22 : 0x32); + writeSpi(0x102, 0x03); + writeSpi(0x102, 0xFF); + writeSpi(0x15C, 0x00); + writeSpi(0x160, 0x01); + writeSpi(0x77E, chan == RxChannel::Rx1 ? 0x50 : chan == RxChannel::Rx2 ? 0x0A : 0x03); + writeSpi(0x09B, 0x01); // TXPLL4 + writeSpi(0x09D, firLength); + writeSpi(0x096, (round(lo) >> 8) & 0xFF); + writeSpi(0x097, round(lo) & 0xFF); + const double ref = config.clock.ref * (config.clock.refDoubler ? 2 : 1); + writeSpi(0x098, (round(ref * 10) >> 8) & 0xFF); + writeSpi(0x099, round(ref * 10) & 0xFF); + writeSpi(0x09E, (round(fs * 100) >> 8) & 0xFF); + writeSpi(0x09F, round(fs * 100) & 0xFF); + writeSpi(0x09C, static_cast(chan)); + pause(qecDelay); + while (true) { + if (readSpi(0x0B3) == 5) + break; + pause(100); + } + writeSpi(0x09C, 0x00); + writeSpi(0x15C, 0x01); + writeSpi(0x77E, 0x00); + configPllSource(config.pllSource); +} + +void txTSTPhaseDelay(const Clock& clock, const double lo, const double fs, const double f0, const TxChannel chan) +{ + writeSpi(0x000, chan == TxChannel::Tx1 ? 0x12 : 0x22); + writeSpi(0x02E, 0x03); + writeSpi(0x109, 0x00); + writeSpi(0x120, 0x10); + writeSpi(0x122, 0x03); + writeSpi(0x124, 0xFF); + writeSpi(0x11B, 0x01); + writeSpi(0x128, 0x11); + configPll4(lo + 200, clock); + configTxNcoFreq(f0, fs * 4); + writeSpi(0x161, 0x00); + writeSpi(0x162, 0x00); + writeSpi(0x162, 0x01); + writeSpi(0x161, 0x01); + writeSpi(0x11F, 0x73); + writeSpi(0x121, 0x80); + writeSpi(0x126, 0x03); + writeSpi(0x77E, chan == TxChannel::Tx1 ? 0x40 : 0x08); + writeSpi(0x186, 0x10); + writeSpi(0x189, 0x00); + writeSpi(0x18A, 0x00); + writeSpi(0x18B, 0x00); + writeSpi(0x18C, 0x00); + writeSpi(chan == TxChannel::Tx1 ? 0x763 : 0x76D, 0x14); + writeSpi(0x095, 0x00); + writeSpi(0x096, (round(lo) >> 8) & 0xFF); + writeSpi(0x097, round(lo) & 0xFF); + writeSpi(0x09C, chan == TxChannel::Tx1 ? 6 : 7); + pause(qecDelay); + while (true) { + if (readSpi(0x0B3) == 7) + break; + pause(100); + } + writeSpi(0x09C, 0x00); + writeSpi(0x77E, 0x00); + writeSpi(0x128, 0x00); +} + +} // namespace CX9261A diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_1_9_0/cx9261adriver.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_1_9_0/cx9261adriver.h new file mode 100644 index 0000000..ef332a0 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_1_9_0/cx9261adriver.h @@ -0,0 +1,546 @@ +/** + * @file cx9261adriver.h + */ + +#ifndef CX9261ADRIVER_CX9261ADRIVER_H +#define CX9261ADRIVER_CX9261ADRIVER_H + +#define VERSION 1.9.0 + +#include "defs.h" +// 如果不需要输出日志内容,注释掉下面的宏定义 +#define USE_LOGGING + +#ifdef USE_LOGGING +# define LOGGING(x) logging(x); +#else +# define LOGGING(x) +#endif + +namespace CX9261A { +#ifdef USE_LOGGING +/** + * @brief 输出日志的函数 + * @param msg 要输出的内容 + */ +void logging(const std::string& msg); +#endif +/** + * @brief 需要实现的函数,实现SPI写入寄存器功能 + * @param address 要写入的寄存器地址 + * @param value 要写入的寄存器值 + */ +void writeSpi(int address, int value); +/** + * @brief 需要实现的函数,实现SPI读取寄存器功能 + * @param address 要读取的寄存器地址 + * @return 返回的寄存器值 + */ +int readSpi(int address); +/** + * @brief 需要实现的函数,实现等待功能 + * @param ms 等待时间,单位毫秒 + */ +void pause(double ms); +/** + * @brief 需要实现的函数,实现配置FPGA对应寄存器功能 + * @param pin 寄存器地址 + * @param value 寄存器值 + */ +void setPin(int pin, int value); +/** + * @brief 初始化芯片状态,写入与具体配置无关的寄存器值 + */ +void initialChip(); +/** + * @brief 初始化电源配置,如果只自定义配置部分模式下的电源管理,在自定义配置之前需要先调用此函数 + */ +void initialPowerConfig(const CX9261AConfiguration& config); +/** + * @brief 初始化BBPLL相关寄存器,如果调用了initialChip,则无需单独调用此函数 + */ +void initialBBPLL(); +/** + * @brief 初始化PLL1相关寄存器,如果调用了initialChip,则无需单独调用此函数 + */ +void initialPLL1(); +/** + * @brief 初始化PLL2相关寄存器,如果调用了initialChip,则无需单独调用此函数 + */ +void initialPLL2(); +/** + * @brief 初始化PLL3相关寄存器,如果调用了initialChip,则无需单独调用此函数 + */ +void initialPLL3(); +/** + * @brief 初始化PLL4相关寄存器,如果调用了initialChip,则无需单独调用此函数 + */ +void initialPLL4(); +/** + * @brief 初始化PLL5相关寄存器,如果调用了initialChip,则无需单独调用此函数 + */ +void initialPLL5(); +/** + * @brief 配置受参考时钟频率影响的寄存器 + * @param ref 参考时钟频率,单位MHz + */ +void configRef(double ref); +/** + * @brief 配置时钟相关寄存器,包括ADC和BBPLL + * @param config CX9261A参数结构体 + */ +void configBBPLL(const CX9261AConfiguration& config); +/** + * @brief BBPLL AFC + * @return 是否锁定 + */ +bool afcBBPll(); +/** + * @brief 配置PLL1频率 + * @param lo 目标频率 + * @param clock 时钟结构体,使用其中的ref和refDoubler属性 + */ +void configPll1(double lo, const Clock& clock); +/** + * @brief Pll1 AFC + * @return 是否锁定 + */ +bool afcPll1(); +/** + * @brief 配置PLL2频率 + * @param lo 目标频率 + * @param clock 时钟结构体,使用其中的ref和refDoubler属性 + */ +void configPll2(double lo, const Clock& clock); +/** + * @brief Pll2 AFC + * @return 是否锁定 + */ +bool afcPll2(); +/** + * @brief 配置PLL3频率 + * @param lo 目标频率 + * @param clock 时钟结构体,使用其中的ref和refDoubler属性 + */ +void configPll3(double lo, const Clock& clock); +/** + * @brief Pll3 AFC + * @return 是否锁定 + */ +bool afcPll3(); +/** + * @brief 配置PLL4频率 + * @param lo 目标频率 + * @param clock 时钟结构体,使用其中的ref和refDoubler属性 + */ +void configPll4(double lo, const Clock& clock); +/** + * @brief Pll4 AFC + * @return 是否锁定 + */ +bool afcPll4(); +/** + * @brief 配置PLL5频率 + * @param lo 目标频率 + * @param clock 时钟结构体,使用其中的ref和refDoubler属性 + */ +void configPll5(double lo, const Clock& clock); +/** + * @brief Pll5 AFC + * @return 是否锁定 + */ +bool afcPll5(); +/** + * @brief 配置AGC模式 + * @param mode 模式0~13 + */ +void configAGCMode(int mode); +/** + * @brief 配置AGC PDT + * @param pdt AGCPDT结构体 + */ +void configAGCPDT(const AGCPDT& pdt); +/** + * @brief 配置AGC LMT + * @param lmt AGCLMT结构体 + */ +void configAGCLMT(const AGCLMT& lmt); +/** + * @brief 配置AGC DIG + * @param dig AGCDIG结构体 + */ +void configAGCDIG(const AGCDIG& dig); +/** + * @brief 配置AGC + * @param agc AGC结构体 + */ +void configAGC(const AGC& agc); +/** + * @brief 配置片内处理器,仅在使用需要处理器辅助的校准方法时需要开启处理器 + * @param on 开启/关闭 + */ +void configRISCV(bool on); +/** + * @brief 配置温补 + * @param mode 温补模式 DAC/Band + */ +void configTemperatureCompensation(TemperatureComp mode); +/** + * @brief ADC校准 + * @param adc ADC速率 + */ +void configADCCalib(double adc); +/** + * @brief Flash校准 + */ +void configFlashCalib(); +/** + * @brief QEC校准会改变部分寄存器配置,在进行校准之前需要保存这部分寄存器,校准后恢复 + * @return 包含寄存器地址和值的map + */ +std::map beforeQecState(); +/** + * @brief 恢复QEC校准过程中改变的寄存器 + * @param state beforeQecState函数返回的map + */ +void restoreQecState(const std::map& state); +/** + * @brief 根据pll来源配置校准模式下的电源管理 + * @param pllSource PllSource结构体 + */ +void preparePowerForRxCalib(const PllSource& pllSource); +/** + * @brief 调整校准时Pll的配置 + * @param config CX9261A配置结构体 + * @param chan 要进行校准的通道 + */ +void preparePllForRxCalib(const CX9261AConfiguration& config, RxChannel chan); +/** + * @brief 等待RxTST窄带校准结束,可重写 + * @return 校准是否结束 + */ +bool waitRxTSTFinished(); +/** + * @brief RX时域统计校准 + * @param chan 校准的RX通道 + */ +void rxTSTCalib(RxChannel chan); +/** + * @brief RX延时线校准 + * @param fs ADC采样率 + * @param chan 校准的RX通道 + */ +void rxDelayCalib(double fs, RxChannel chan); +/** + * @brief DC Tracking + * @param config CX9261A配置结构体 + */ +void configDcCalib(const Calib& config); +void configDcCalibMethod(DCCalibMethod method); +void configDcTrackingLength(int length); +void configDcHighPassFactor(int factor); +/** + * @brief Rx校准 + * @param config CX9261A配置结构体 + * @param chan 要进行校准的通道 + */ +void rxCalib(const CX9261AConfiguration& config, RxChannel chan); +/** + * @brief 选择校准方法 + * @param calib 校准结构体 + */ +void configCalibMethod(const Calib& calib); +/** + * @brief 读取当前Rx chan通道的校准值 + * @param chan Rx通道 + * @return 校准值结构体 + */ +CalibPredis readRxCalibPredis(RxChannel chan); +/** + * @brief 储存校准值 + * @param index 第index组校准值 + * @param chan 接收通道 + * @param pll 使用的PLL + */ +void saveRxCalibPredis(unsigned char index, RxChannel chan, LoSource pll); +/** + * @brief 加载保存的校准值 + * @param index 第index组校准值 + * @param chan 指定加载校准值的通道 + * @param pll 使用的PLL + */ +void loadRxCalibPredis(unsigned char index, RxChannel chan, LoSource pll); +/** + * @brief Rx窄带多点校准 + * @param config CX9261A配置结构体 + * @param points 校准点 + * @param chan 接收通道 + */ +void configRxMultiPointsQec(const CX9261AConfiguration& config, const std::vector& points, RxChannel chan); +/** + * @brief 根据pll来源配置校准模式下的电源管理 + * @param pllSource PllSource结构体 + */ +void preparePowerForTxCalib(const PllSource& pllSource); +/** + * @brief 调整校准时Pll的配置 + * @param config CX9261A配置结构体 + */ +void preparePllForTxCalib(const CX9261AConfiguration& config); +/** + * @brief TX二分法QEC校准 + * @param f0 校准音频率 + * @param fs adc频率 + */ +void txBisectionQecCalib(double f0, double fs); +/** + * @brief TX时域统计法校准 + * @param config CX9261A配置结构体 + * @param chan 要校准的通道 + */ +void txTSTCalib(const CX9261AConfiguration& config, TxChannel chan); +/** + * @brief TX二分法DC校准 + * @param f0 校准音频率 + * @param fs adc频率 + */ +void txBisectionDcCalib(double f0, double fs); +/** + * @brief Tx多点校准 + * @param config CX9261A配置结构体 + * @param points 校准点 + */ +void configTxMultiPointsQec(const CX9261AConfiguration& config, const std::vector& points); +/** + * @brief Tx通道校准 + * @param config CX9261A配置结构体 + */ +void txCalib(const CX9261AConfiguration& config); +/** + * @brief 储存校准值 + * @param index 第index组校准值 + * @param chan 发射通道 + * @param pll pll + */ +void saveTxCalibPredis(unsigned char index, TxChannel chan, LoSource pll); +/** + * @brief 加载保存的校准值 + * @param index 第index组校准值 + * @param chan 指定加载校准值的通道 + * @param pll 指定加载校准值对应的PLL + */ +void loadTxCalibPredis(unsigned char index, TxChannel chan, LoSource pll); +/** + * @brief Qec校准顶层函数,内部根据配置选择使用对应方法进行校准 + * @param config CX9261A配置结构体 + */ +void configQecCalib(const CX9261AConfiguration& config); +/** + * @brief 配置TX NCO相关寄存器 + * @param nco TX NCO配置结构体 + * @param fs adc速率 + */ +void configTxNco(const TxNCO& nco, double fs); +/** + * @brief 配置TX NCO BIST模式 + * @param bist 开启/关闭BIST + */ +void configTxBIST(bool bist); +/** + * @brief 配置TX NCO频率 + * @param freq 混频频率 + * @param fs adc抽取后速率 + */ +void configTxNcoFreq(double freq, double fs); +/** + * @brief 配置RX NCO相关寄存器 + * @param nco RX NCO配置结构体 + * @param fs adc速率 + */ +void configRxNco(const RxNCO& nco, double fs); +/** + * @brief 配置Rx NCO模式 + * @param mode NCO模式:Bypass/BIST/Up/Down + */ +void configRxNcoMode(NCOMode mode); +/** + * @brief 配置Rx NCO模式 + * @param mode NCO模式:Master/Slaver + */ +void configRxNcoMS(NCOMode mode); +/** + * @brief 配置Rx NCO频率 + * @param freq 混频频率 + * @param fs adc速率 + */ +void configRxNcoFreq(double freq, double fs); +/** + * @brief 配置接口相关寄存器 + * @param interface Interface配置结构体 + */ +void configInterface(const Interface& interface); +/** + * @brief 配置PLL来源 + * @param pllSource PLlSource结构体 + */ +void configPllSource(const PllSource& pllSource); +/** + * @brief 切换电源模式 + * @param pm 电源模式 + */ +void configPowerMode(PowerMode pm); +/** + * @brief 配置电源管理 + * @param power 电源配置结构体 + * @param pm 电源模式 + */ +void configPowerPins(const Power& power, PowerMode pm); +/** + * @brief 配置参考倍频器 + * @param doubler 倍频器开关 + * @param ref 参考时钟频率,单位MHz + */ +void configRefDoubler(bool doubler, double ref); +/** + * @brief 初始化通道,调用initialChip函数会初始化RX1,RX2,RX3三个通道 + * @param chan 通道 RX1/RX2/RX3 + */ +void initialRxChannel(RxChannel chan); +/** + * @brief 配置Rx带宽 + * @note 只改变Rx带宽会导致通道增益改变,建议使用configRxChannel函数进行带宽调整 + * @param bandwidth 带宽,单位(MHz) + * @param chan 通道 Rx1/Rx2/Rx3 + */ +void configRxBandwidth(double bandwidth, RxChannel chan); +/** + * @brief 配置Rx TIA + * @note 只改变TIA会导致通道带宽改变,建议使用configRxChannel函数进行TIA调整 + * @param tia tia档位 + * @param chan 通道 Rx1/Rx2/Rx3 + */ +void configRxTIA(int tia, RxChannel chan); +/** + * @brief 配置Rx Mixer + * @param mixer mixer档位 + */ +void configRxMixer(int mixer); +/** + * @brief 配置Rx通道,包含TIA,Mixer以及Bandwidth + * @param config RxConfig结构体 + * @param chan Rx1/Rx2/Rx3 + */ +void configRxChannel(const RxConfig& config, RxChannel chan); +/** + * @brief 配置Rx FIR滤波器抽取倍数 + * @param fir 抽取倍数 0(Bypass)/1/2/3/4/8 + */ +void configRxFIR(int fir); +/** + * @brief 配置Rx HBF1滤波器抽取倍数 + * @param hbf 抽取倍数 0(Bypass)/2 + */ +void configRxHBF1(int hbf); +/** + * @brief 配置Rx HBF2滤波器抽取倍数 + * @param hbf 抽取倍数 0(Bypass)/2 + */ +void configRxHBF2(int hbf); +/** + * @brief 配置Rx HBF3滤波器抽取倍数 + * @param hbf 抽取倍数 0(Bypass)/2/3 + */ +void configRxHBF3(int hbf); +/** + * @brief 配置Rx CIC滤波器 + * @param cic 抽取倍数 0(Bypass)/20 + */ +void configRxCIC(int cic); +/** + * @brief 配置Rx滤波器 + * @param filters Filters结构体 + */ +void configRxFilters(const Filters& filters); +/** + * @brief 配置Rx FIR滤波器系数 + * @param coe 滤波器系数,长度为16的整倍数,最大128 + */ +void configRxFIRCoe(const std::vector& coe); +/** + * @brief 配置Tx带宽 + * @param bandwidth 带宽,单位(MHz) + * @param chan 通道 TX1/TX2 + */ +void configTxBandwidth(double bandwidth, TxChannel chan); +/** + * @brief 配置Tx Mixer + * @param mixer 混频器值 + * @param chan TX1/TX2 + */ +void configTxMixer(int mixer, TxChannel chan); +/** + * @brief 初始化Tx通道 + * @param chan 通道 TX1/TX2 + */ +void initialTxChannel(TxChannel chan); +/** + * @brief 根据Tx通道的本振频率,配置对应的参数 + * @param lo 本振频率,单位(MHz) + * @param chan 通道(Tx1/Tx2) + */ +void configTxChannelFrequency(double lo, TxChannel chan); +/** + * @brief 配置Tx通道,Mixer以及Bandwidth + * @param config TxConfig结构体 + * @param chan Tx1/Tx2 + */ +void configTxChannel(const TxConfig& config, TxChannel chan); +/** + * @brief 配置Tx FIR滤波器插值倍数 + * @param fir 插值倍数 0(Bypass)/1/2/3/4/8 + */ +void configTxFIR(int fir); +/** + * @brief 配置Tx HBF1滤波器插值倍数 + * @param hbf 插值倍数 0(Bypass)/2 + */ +void configTxHBF1(int hbf); +/** + * @brief 配置Tx HBF2滤波器插值倍数 + * @param hbf 插值倍数 0(Bypass)/2 + */ +void configTxHBF2(int hbf); +/** + * @brief 配置Tx HBF3滤波器插值倍数 + * @param hbf 插值倍数 0(Bypass)/2/3 + */ +void configTxHBF3(int hbf); +/** + * @brief 配置Tx CIC滤波器 + * @param cic 插值倍数 0(Bypass)/8/20/32/80 + */ +void configTxCIC(int cic); +/** + * @brief 配置Tx滤波器 + * @param filters Filters结构体 + */ +void configTxFilters(const Filters& filters); +/** + * @brief 配置Tx FIR滤波器系数 + * @param coe 滤波器系数,长度为16的整倍数,最大128 + */ +void configTxFIRCoe(const std::vector& coe); +/** + * @brief 读取当前Tx chan通道的校准值 + * @param chan Tx通道 + * @return 校准值结构体 + */ +CalibPredis readTxCalibPredis(TxChannel chan); +/** + * @brief 此函数为Demo板配套配置函数,用于配置FPGA接口,用户开发自己的程序时应重写对应功能 + * @param interface Interface结构体 + */ +void configFPGA(const Interface& interface); +} // namespace CX9261A + +#endif // CX9261ADRIVER_CX9261ADRIVER_H diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_1_9_0/defs.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_1_9_0/defs.h new file mode 100644 index 0000000..5d187df --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_1_9_0/defs.h @@ -0,0 +1,424 @@ +/** + * @file defs.h + */ + +#ifndef CX9261ADRIVER_DEFS_H +#define CX9261ADRIVER_DEFS_H + +#include +#include +#include + +#ifdef interface +# undef interface +#endif + +namespace CX9261A { +/** + * @brief 芯片工作模式 + */ +enum class WorkMode : std::uint8_t { Rx, Tx, FDD }; +/** + * @brief 收发数据通道数量 + * @note 此模式并不代表使用几个通道进行收发 + */ +enum class TrMode : std::uint8_t { R1T1, R2T2 }; +/** + * @brief 收发接口通道数量 + */ +enum class PortMode : std::uint8_t { Single, Dual }; +/** + * @brief 接口模式: + * CMOS CMOS接口模式, + * LVDS_S LVDS顺序模式, + * LVDS_E LVDS奇偶模式 + */ +enum class IOMode : std::uint8_t { CMOS, LVDS_S, LVDS_E }; +/** + * @brief 本振来源 + */ +enum class LoSource : std::uint8_t { + Pll1 = 0b00001, + Pll2 = 0b00010, + Pll3 = 0b00100, + Pll4 = 0b01000, + Pll5 = 0b10000, + Off = 0 +}; + +inline std::string toString(const LoSource source) +{ + switch (source) { + case LoSource::Pll1: + return "Pll1"; + case LoSource::Pll2: + return "Pll2"; + case LoSource::Pll3: + return "Pll3"; + case LoSource::Pll4: + return "Pll4"; + case LoSource::Pll5: + return "Pll5"; + default: + return "Off"; + } +} +/** + * @brief 接收通道 + */ +enum class RxChannel : std::uint8_t { Rx1 = 1, Rx2, Rx3 }; + +inline std::string toString(const RxChannel chan) +{ + switch (chan) { + case RxChannel::Rx1: + return "Rx1"; + case RxChannel::Rx2: + return "Rx2"; + case RxChannel::Rx3: + return "Rx3"; + default: + return "Unknown Channel"; + } +} +/** + * @brief 发射通道 + */ +enum class TxChannel : std::uint8_t { Tx1 = 1, Tx2 }; + +inline std::string toString(const TxChannel chan) +{ + switch (chan) { + case TxChannel::Tx1: + return "Tx1"; + case TxChannel::Tx2: + return "Tx2"; + default: + return "Unknown Channel"; + } +} +/** + * @brief 电源模式 + */ +enum class PowerMode : std::uint8_t { Shutdown = 0, Sleep, Alert, Calib, Receive, Transmit, Duplex, Obs }; +/** + * @brief NCO模式 + */ +enum class NCOMode : std::uint8_t { Bypass = 0, Up, Down, BIST, Master, Slaver }; +/** + * @brief 温补模式 DAC: DAC模式 Band: 跳子带模式 + */ +enum class TemperatureComp : std::uint8_t { DAC = 0, Band }; +/** + * @brief 校准方法 TST: 时域统计 Rx通道使用, Bisec: 二分法 Tx通道使用, MultiPoints: 多点校准 + */ +enum class CalibMethod : std::uint8_t { TST = 0, Bisec, MultiPoints, Tracking }; +/** + * @brief DC校准方法 Tracking: 跟踪校准 HighPass: 高通滤波器滤掉直流 + */ +enum class DCCalibMethod : std::uint8_t { Tracking = 0, HighPass }; +/** + * @brief AFC等待延时,单位ms + */ +constexpr double delay = 100; +/** + * @brief QEC等待延时,单位ms + */ +constexpr double qecDelay = 500; +/** + * @brief 接收通道本振来源映射表 + */ +static const std::map, std::bitset<7>> rxSrcMap = { + { { LoSource::Pll2, LoSource::Pll2 }, 0b0010000 }, { { LoSource::Pll1, LoSource::Pll1 }, 0b1101000 }, + { { LoSource::Pll2, LoSource::Pll1 }, 0b0100000 }, { { LoSource::Pll1, LoSource::Pll2 }, 0b1000000 }, + { { LoSource::Off, LoSource::Pll1 }, 0b0101010 }, { { LoSource::Off, LoSource::Pll2 }, 0b1010010 }, + { { LoSource::Pll1, LoSource::Off }, 0b1001001 }, { { LoSource::Pll2, LoSource::Off }, 0b0110001 }, + { { LoSource::Off, LoSource::Off }, 0b0011011 } +}; +/** + * @brief 发射通道本振来源映射表 + */ +static const std::map, std::bitset<11>> txSrcMap = { + { { LoSource::Pll4, LoSource::Pll4 }, 16 }, { { LoSource::Pll3, LoSource::Pll3 }, 104 }, + { { LoSource::Pll4, LoSource::Pll3 }, 32 }, { { LoSource::Pll3, LoSource::Pll4 }, 64 }, + { { LoSource::Pll2, LoSource::Pll2 }, 1560 }, { { LoSource::Pll1, LoSource::Pll1 }, 1944 }, + { { LoSource::Pll2, LoSource::Pll1 }, 1688 }, { { LoSource::Pll1, LoSource::Pll2 }, 1816 }, + { { LoSource::Off, LoSource::Pll3 }, 42 }, { { LoSource::Off, LoSource::Pll4 }, 82 }, + { { LoSource::Off, LoSource::Pll2 }, 794 }, { { LoSource::Off, LoSource::Pll1 }, 666 }, + { { LoSource::Pll3, LoSource::Off }, 73 }, { { LoSource::Pll4, LoSource::Off }, 49 }, + { { LoSource::Pll2, LoSource::Off }, 1177 }, { { LoSource::Pll1, LoSource::Off }, 1305 }, + { { LoSource::Off, LoSource::Off }, 27 }, { { LoSource::Pll1, LoSource::Pll3 }, 1320 }, + { { LoSource::Pll1, LoSource::Pll4 }, 1360 }, { { LoSource::Pll2, LoSource::Pll3 }, 1064 }, + { { LoSource::Pll2, LoSource::Pll4 }, 1104 }, { { LoSource::Pll3, LoSource::Pll1 }, 712 }, + { { LoSource::Pll4, LoSource::Pll1 }, 688 }, { { LoSource::Pll3, LoSource::Pll2 }, 584 }, + { { LoSource::Pll4, LoSource::Pll2 }, 560 } +}; +/** + * @brief 环路参数选择表 + */ +static const std::vector>> coreTable = { + { { 0x0C, 0x30, 0x08 }, { 0x0B, 0x30, 0x09 }, { 0x0C, 0x30, 0x05 }, { 0x0D, 0x30, 0x08 } }, + { { 0x0C, 0x30, 0x0C }, { 0x0B, 0x30, 0x0E }, { 0x0C, 0x30, 0x08 }, { 0x0D, 0x30, 0x0B } }, + { { 0x0C, 0x20, 0x10 }, { 0x0B, 0x20, 0x10 }, { 0x0C, 0x20, 0x0A }, { 0x0D, 0x20, 0x0D } }, + { { 0x0C, 0x20, 0x11 }, { 0x0B, 0x20, 0x13 }, { 0x0C, 0x20, 0x0C }, { 0x0D, 0x20, 0x0F } }, + { { 0x0D, 0x30, 0x14 }, { 0x0D, 0x22, 0x14 }, { 0x0D, 0x20, 0x0D }, { 0x0D, 0x15, 0x0C } }, + { { 0x0E, 0x30, 0x18 }, { 0x0E, 0x25, 0x16 }, { 0x0E, 0x25, 0x0F }, { 0x0E, 0x18, 0x0F } }, + { { 0x0F, 0x38, 0x1A }, { 0x0F, 0x32, 0x1C }, { 0x0F, 0x18, 0x12 }, { 0x0F, 0x18, 0x12 } } +}; + +/** + * @brief AGC PDT模式的结构体 + */ +struct AGCPDT +{ + int pdtN; + int pdtM; +}; + +/** + * @brief AGC LMT模式的结构体 + */ +struct AGCLMT +{ + int count; /**< LMT统计次数阈值*/ + int threshold1; /**< LMT阈值第一档*/ + int threshold2; /**< LMT阈值第二档*/ + int threshold3; /**< LMT阈值第三档*/ + int threshold4; /**< LMT阈值第四档*/ +}; + +/** + * @brief AGC DIG模式的结构体 + */ +struct AGCDIG +{ + int count; /**< DIG统计次数阈值*/ + int threshold1; /**< DIG阈值第一档*/ + int threshold2; /**< DIG阈值第二档*/ + int threshold3; /**< DIG阈值第三档*/ + int threshold4; /**< DIG阈值第四档*/ +}; + +/** + * @brief AGC模式的结构体 + */ +struct AGC +{ + int mode; /**< AGC模式选择*/ + AGCPDT pdt; /**< AGC PDT模式的结构体*/ + AGCLMT lmt; /**< AGC LMT模式的结构体*/ + AGCDIG dig; /**< AGC DIG模式的结构体*/ +}; + +/** + * @brief 时钟相关参数结构体 + */ +struct Clock +{ + double ref; /**< 参考时钟频率,单位MHz*/ + double adc; /**< ADC频率,单位MHz*/ + int refDiv; /**< 参考时钟分频比*/ + bool bbUseIntDiv; /**< 是否使用整数分频模式*/ + bool refDoubler; /**< 是否使用参考时钟倍频器*/ +}; + +/** + * @brief 校准相关参数结构体 + */ +struct Calib +{ + bool calibDC; /**< 是否开启DC Tracking*/ + DCCalibMethod dcCalibMethod; + int dcTrackingLength; /**< DC Tracking长度*/ + int dcHighPassFactor; /**< DC高通滤波系数,越大代表滤波器阶数越高,滤波范围越窄*/ + std::uint8_t rx1CalibPll; /**< rx1校准的pll选择,Pll1 | Pll2*/ + std::uint8_t rx2CalibPll; /**< rx2校准的pll选择,Pll1 | Pll2*/ + std::uint8_t rx3CalibPll; /**< rx3校准的pll选择,Pll5*/ + std::uint8_t tx1CalibPll; /**< tx1校准的pll选择,Pll1 | Pll2 | Pll3 | Pll4*/ + std::uint8_t tx2CalibPll; /**< tx2校准的pll选择,Pll1 | Pll2 | Pll3 | Pll4*/ + CalibMethod rxMethod; + CalibMethod txMethod; + double f0; /**< 校准音频率*/ + std::vector rxMultiPoints; /**< Rx多点校准的频率*/ + std::vector txMultiPoints; /**< Tx多点校准的频率*/ +}; + +/** + * @brief 配置PLL来源相关参数结构体 + */ +struct PllSource +{ + bool rxUseExt; /**< 接收通道是否使用外本振*/ + bool txUseExt; /**< 发射通道是否使用外本振*/ + LoSource rx1Source; /**< Rx1通道本振来源*/ + LoSource rx2Source; /**< Rx2通道本振来源*/ + LoSource rx3Source; /**< Rx3通道本振来源*/ + LoSource tx1Source; /**< Tx1通道本振来源*/ + LoSource tx2Source; /**< Tx2通道本振来源*/ +}; + +/** + * @brief 配置PLL频率相关参数结构体 + */ +struct PllFreq +{ + double pll1Freq; /**< PLL1本振时钟,单位MHz*/ + double pll2Freq; /**< PLL2本振时钟,单位MHz*/ + double pll3Freq; /**< PLL3本振时钟,单位MHz*/ + double pll4Freq; /**< PLL4本振时钟,单位MHz*/ + double pll5Freq; /**< PLL5本振时钟,单位MHz*/ + double rxExtFreq; /**< 接收通道使用外本振时,外本振频率,单位MHz*/ + double txExtFreq; /**< 发射通道使用外本振时,外本振频率,单位MHz*/ +}; + +/** + * @brief 配置接口相关参数结构体 + */ +struct Interface +{ + WorkMode workMode; /**< 芯片工作模式*/ + TrMode trMode; /**< 收发数据通道模式*/ + PortMode portMode; /**< 收发接口通道模式*/ + IOMode ioMode; /**< 接口电平模式*/ +}; + +/** + * @brief 配置TX NCO相关参数结构体 + */ +struct TxNCO +{ + bool txUseBist; /**< 是否开启BIST模式*/ + double txNCOFreq; /**< NCO频率*/ +}; + +/** + * @brief 配置RX NCO相关参数结构体 + */ +struct RxNCO +{ + bool rxUseBist; /**< 是否开启BIST模式*/ + bool rxNCOBypass; /**< 是否Bypass NCO*/ + bool rxNCOUpward; /**< 开启上/下混频模式*/ + bool rxNCOMaster; /**< 使用Master/Slaver模式*/ + double rxNCOFreq; /**< NCO频率*/ +}; + +/** + * @brief 配置接收通道相关参数结构体 + */ +struct RxConfig +{ + double bandwidth; /**< 接收通道1dB带宽,单位MHz*/ + int tia; /**< 接收通道TIA增益配置,推荐配置6*/ + int mixer; /**< 接收通道Mixer增益配置*/ +}; + +/** + * @brief 配置发射通道相关参数结构体 + */ +struct TxConfig +{ + double bandwidth; /**< 发射通道1dB带宽,单位MHz*/ + int mixer; /**< 发射通道Mixer增益配置*/ +}; + +/** + * @brief 配置滤波器相关参数结构体,0代表bypass + */ +struct Filters +{ + int fir; /**< FIR滤波器抽取/插值倍数*/ + int hbf1; /**< HBF1滤波器抽取/插值倍数*/ + int hbf2; /**< HBF2滤波器抽取/插值倍数*/ + int hbf3; /**< HBF3滤波器抽取/插值倍数*/ + int cic; /**< CIC滤波器抽取/插值倍数*/ +}; + +/** + * @brief 配置电源模式结构体,1代表该模块上电 + */ +struct Power +{ + bool dig_tx2; + bool dig_tx1; + bool dig_rx3; + bool dig_rx2; + bool dig_rx1; + bool fref1_rx; + bool auxadc2; + bool auxadc1; + bool auxdac; + bool temp_sensor; + bool bgr; + bool exlo; + bool tx2; + bool tx1; + bool rx3; + bool rx2; + bool rx1; + bool pll5_ldo; + bool pll5; + bool pll4_ldo; + bool pll4; + bool pll3_ldo; + bool pll3; + bool pll2_ldo; + bool pll2; + bool pll1_ldo; + bool pll1; + bool bbpll; +}; + +/** + * @brief 配置电源模式管理,包含模式选择,接收模式,发射模式,FDD模式 + */ +struct PowerManagement +{ + PowerMode powerMode; + Power rxPower; + Power txPower; + Power fddPower; +}; + +/** + * @brief 储存窄带校准结果的结构体 + */ +struct CalibPredis +{ + int bHigh; + int bLow; + int cHigh; + int cLow; + int dciHigh; + int dciLow; + int dcqHigh; + int dcqLow; +}; + +/** + * @brief CX9261A结构体 + */ +struct CX9261AConfiguration +{ + TemperatureComp tempComp; /**< 温度补偿*/ + AGC agc; /**< AGC结构体*/ + Interface interface; /**< Interface结构体*/ + Clock clock; /**< Clock结构体*/ + PllSource pllSource; /**< PllSource结构体*/ + Calib calib; /**< Calib结构体*/ + PllFreq pllFreq; /**< PllFreq结构体*/ + TxNCO txNco; /**< TxNCO结构体*/ + RxNCO rxNco; /**< RxNCO结构体*/ + RxConfig rx1Config; /**< RxConfig结构体*/ + RxConfig rx2Config; /**< RxConfig结构体*/ + RxConfig rx3Config; /**< RxConfig结构体*/ + TxConfig tx1Config; /**< TxConfig结构体*/ + TxConfig tx2Config; /**< TxConfig结构体*/ + Filters rxFilters; /**< Filters结构体*/ + Filters txFilters; /**< Filters结构体*/ + PowerManagement powerManagement; /**< PowerManagement结构体*/ + std::vector rxFIRCoe; /**< Rx FIR滤波器系数*/ + std::vector txFIRCoe; /**< Tx FIR滤波器系数*/ +}; +} // namespace CX9261A +#endif // CX9261ADRIVER_DEFS_H diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_1_9_0/main.cpp b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_1_9_0/main.cpp new file mode 100644 index 0000000..820a7a8 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_1_9_0/main.cpp @@ -0,0 +1,138 @@ +#include "cx9261adriver.h" +#include "defs.h" + +int main() { + + // 芯片配置结构体 + CX9261A::CX9261AConfiguration configuration; + // 电源管理 + // 接收模式电源管理 + configuration.powerManagement.rxPower = {0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1}; + // 发射模式电源管理 + configuration.powerManagement.txPower = {1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1}; + // FDD模式电源管理 + configuration.powerManagement.fddPower = {1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, + 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1}; + // 电源模式 即配置结束后的电源模式 + configuration.powerManagement.powerMode = CX9261A::PowerMode::Receive; + // 接口模式 + configuration.interface = {.workMode = CX9261A::WorkMode::Rx, + .trMode = CX9261A::TrMode::R1T1, + .portMode = CX9261A::PortMode::Single, + .ioMode = CX9261A::IOMode::CMOS}; + // 时钟相关 + configuration.clock = {.ref = 125, .adc = 64, .refDiv = 2, .bbUseIntDiv = false, .refDoubler = false}; + // 本振相关 + using LoSource = CX9261A::LoSource; + // 本振来源 + configuration.pllSource = {.rxUseExt = false, + .txUseExt = false, + .rx1Source = LoSource::Pll1, + .rx2Source = LoSource::Pll1, + .rx3Source = LoSource::Off, + .tx1Source = LoSource::Off, + .tx2Source = LoSource::Off}; + // 本振频率 注意:使用的所有PLL,频率必须设为正数 + configuration.pllFreq = { + .pll1Freq = 5800, .pll2Freq = 5800, .pll3Freq = 800, .pll4Freq = 900, .pll5Freq = 1000}; + // Rx通道 注意:tia推荐配置为6 Mixer为三个通道共用同一个寄存器 + configuration.rx1Config = {.bandwidth = 32, .tia = 6, .mixer = 31}; + configuration.rx2Config = {.bandwidth = 32, .tia = 6, .mixer = 31}; + configuration.rx3Config = {.bandwidth = 32, .tia = 6, .mixer = 31}; + // Tx通道 + configuration.tx1Config = {.bandwidth = 20, .mixer = 63}; + configuration.tx2Config = {.bandwidth = 20, .mixer = 63}; + // 接收滤波器 + configuration.rxFilters = {.fir = 0, .hbf1 = 0, .hbf2 = 0, .hbf3 = 0, .cic = 0}; + // 发射滤波器 + configuration.txFilters = {.fir = 0, .hbf1 = 2, .hbf2 = 2, .hbf3 = 0, .cic = 0}; + // Rx NCO + configuration.rxNco = { + .rxUseBist = false, .rxNCOBypass = true, .rxNCOUpward = true, .rxNCOMaster = true, .rxNCOFreq = 0}; + // Tx NCO + configuration.txNco = {.txUseBist = false, .txNCOFreq = 0}; + // AGC 注意:mode = 0表示关闭AGC,此时其他的参数可以不配置 + configuration.calib = { + .calibDC = true, + .dcCalibMethod = CX9261A::DCCalibMethod::Tracking, + .dcTrackingLength = 16384, + .dcHighPassFactor = 8191, + .rx1CalibPll = int(LoSource::Pll1) | int(LoSource::Pll2), + .rx2CalibPll = int(LoSource::Pll1) | int(LoSource::Pll2), + .rx3CalibPll = int(LoSource::Pll5), + .tx1CalibPll = 0, + .tx2CalibPll = 0, + .rxMethod = CX9261A::CalibMethod::MultiPoints, + .txMethod = CX9261A::CalibMethod::Bisec, + .f0 = 4 + }; + configuration.agc = {.mode = 0}; + // 校准 + configuration.calib.rxMultiPoints = {2200, 2400, 2600, 2800, 3000, 3200, 3400, 3600, 3800, 4000, 4200, + 4400, 4600, 4800, 5000, 5200, 5400, 5600, 5800, 6000}; + // 配置温补模式为跳子带温补 + configuration.tempComp = CX9261A::TemperatureComp::Band; + + // 开始配置芯片 + CX9261A::initialChip(); // 最开始需要调用此函数 + CX9261A::initialPowerConfig(configuration); + // 配置温补 + configTemperatureCompensation(configuration.tempComp); + using PowerMode = CX9261A::PowerMode; + // 不需要自定义电源管理时,这部分代码可以省略 + CX9261A::configPowerPins(configuration.powerManagement.rxPower, PowerMode::Receive); + CX9261A::configPowerPins(configuration.powerManagement.txPower, PowerMode::Transmit); + CX9261A::configPowerPins(configuration.powerManagement.fddPower, PowerMode::Duplex); + // 配置pll + CX9261A::configBBPLL(configuration); + CX9261A::configPllSource(configuration.pllSource); + CX9261A::configPll1(configuration.pllFreq.pll1Freq, configuration.clock); + CX9261A::configPll2(configuration.pllFreq.pll2Freq, configuration.clock); + // 配置Rx通道 + CX9261A::configRxChannel(configuration.rx1Config, CX9261A::RxChannel::Rx1); + CX9261A::configRxChannel(configuration.rx2Config, CX9261A::RxChannel::Rx2); + CX9261A::configRxChannel(configuration.rx3Config, CX9261A::RxChannel::Rx3); + // 配置Tx通道 + using TxChannel = CX9261A::TxChannel; + CX9261A::configTxChannelFrequency(configuration.pllFreq.pll1Freq, TxChannel::Tx1); + CX9261A::configTxChannelFrequency(configuration.pllFreq.pll2Freq, TxChannel::Tx2); + CX9261A::configTxChannel(configuration.tx1Config, TxChannel::Tx1); + CX9261A::configTxChannel(configuration.tx2Config, TxChannel::Tx2); + // 配置滤波器 + CX9261A::configRxFilters(configuration.rxFilters); + CX9261A::configTxFilters(configuration.txFilters); + // 配置NCO + CX9261A::configRxNco(configuration.rxNco, configuration.clock.adc); + const int filters = std::max(configuration.rxFilters.fir, 1) * std::max(configuration.rxFilters.hbf3, 1) * + std::max(configuration.rxFilters.hbf2, 1) * + std::max(configuration.rxFilters.hbf1, 1) * std::max(configuration.rxFilters.cic, 1); + CX9261A::configTxNco(configuration.txNco, configuration.clock.adc / filters); + // 配置AGC + CX9261A::configAGC(configuration.agc); + // 切换到休眠模式 + CX9261A::configPowerMode(PowerMode::Sleep); + CX9261A::pause(1000); + // 切换到校准模式 + CX9261A::configPowerMode(PowerMode::Calib); + CX9261A::pause(100); + // 锁定BBPLL + CX9261A::afcBBPll(); + // 配置校准 + CX9261A::configADCCalib(configuration.clock.adc); + CX9261A::configFlashCalib(); + configDcCalib(configuration.calib); + configQecCalib(configuration); + CX9261A::loadRxCalibPredis(0, CX9261A::RxChannel::Rx1, LoSource::Pll1); + CX9261A::loadRxCalibPredis(0, CX9261A::RxChannel::Rx2, LoSource::Pll1); + // 配置接口 + CX9261A::configInterface(configuration.interface); + // 配置FPGA接口相关 此为示例,仅适用于Demo工程,用户应根据实际情况进行配置 + CX9261A::configFPGA(configuration.interface); + // 锁定PLL1 + CX9261A::afcPll1(); + // 锁定PLL2 + CX9261A::afcPll2(); + return 0; +} \ No newline at end of file diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_1_9_0/utils.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_1_9_0/utils.h new file mode 100644 index 0000000..58058f7 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_1_9_0/utils.h @@ -0,0 +1,99 @@ +/** + * @file utils.h + */ + +#ifndef CX9261ADRIVER_UTILS_H +#define CX9261ADRIVER_UTILS_H +#include +#include +#include +#include +namespace CX9261A { +#if __cplusplus >= 201703L +template +constexpr const T& clamp(const T& v, const T& lo, const T& hi) { + return std::clamp(v, lo, hi); +} +#else +template +constexpr const T& clamp(const T& v, const T& lo, const T& hi) { + return std::max(std::min(hi, v), lo); +} +#endif + +template +int round(Float f) { + return static_cast(std::round(f)); +} + +template +long long round64(Float f) { + return static_cast(std::round(f)); +} + +template +int floor(Float f) { + return static_cast(std::floor(f)); +} + +template +long long floor64(Float f) { + return static_cast(std::floor(f)); +} + +template +int ceil(Float f) { + return static_cast(std::ceil(f)); +} + +template +long long ceil64(Float f) { + return static_cast(std::ceil(f)); +} +/** +* @brief Register类,提供方便的位操作函数 +*/ +class Register { +public: + template + constexpr Register(const T value = 0) { + static_assert(std::is_integral_v, "Register value must be integral!"); + m_value = static_cast(value); + } + /** + * 以十进制返回寄存器值 + */ + unsigned char value() const { return static_cast(m_value.to_ulong()); } + /** + * 对寄存器值进行部分值的替换操作 + * 例:replace(7, 4, 0b1111); 使寄存器的高四位为1 + * @param start 起始位 + * @param end 中止位 + * @param after 要替换的值 + */ + void replace(const unsigned int start, const unsigned int end, unsigned char after) { + for (auto i = end; i <= start; ++i) { + m_value[i] = (after >> (i - end)) & 0b1; + } + } + + auto operator[](const size_t position) { return m_value[position]; } + + template + operator Type() const { + static_assert(std::is_integral_v, "Type must be an integral"); + return static_cast(m_value.to_ulong()); + } + +private: + std::bitset<8> m_value{}; +}; + +template +std::string frac2bin(const double frac) { + const long double num = frac * (pow(2.0, N)); + std::string ret = std::bitset(std::floor(num)).to_string(); + return ret; +} +} // namespace CX9261A +#endif // CX9261ADRIVER_UTILS_H diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/config/app_config.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/config/app_config.h new file mode 100644 index 0000000..1334d12 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/config/app_config.h @@ -0,0 +1,331 @@ +/*****************************************************************************/ +/** + * Description: Global configurations. + * + * Date: 2021-04-28 13:19 + * Last Editors: Fan Hao + * Last Edit Time: [2022-05-23 20:32] + * + * Revisions: Rev 0.0 + * History: + * [2021-04-28]: Initial edition. + * + * Copyright(c) 2024 Beijing Institute of Technology. + * Lab of Communication and Networking + * + ******************************************************************************/ + +#ifndef __APP_CONFIG_H_ +#define __APP_CONFIG_H_ + +#define XILINX_PLATFORM + +#include "xparameters.h" + +/******** Software Version ********/ +#define _APP_VER 0x00010008 + +#define _APP_VER_MAIN ((_APP_VER & 0xFF000000) >> 24) +#define _APP_VER_MINOR ((_APP_VER & 0x00FF0000) >> 16) +#define _APP_VER_PATCH (_APP_VER & 0x0000FFFF) + +/*********** Debug Info Level ************/ +// #define _DEBUG_LEVEL 1 +// 3 - print all debug info +// 2 - print warnings and errors +// 1 - print only errors +// other - print nothing + +#define DATALINK_ENABLE_AUTORECONNECT +#define DATALINK_ENABLE_AUTOTIMESYNC +// #define DATALINK_ENABLE_BERTEST + +/************ Hardware Configs ************/ +#ifdef __aarch64__ // Zynq MPSoC (Zynq UltraScale+) +#define CPU_CLK_HZ XPAR_CPU_CORTEXA53_0_CPU_CLK_FREQ_HZ +#define FCLK_CLK0_HZ 100000000 +#define APP_TIMER_CLK_HZ FCLK_CLK0_HZ +#else // Zynq-7000 +#define CPU_CLK_HZ XPAR_CPU_CORTEXA9_0_CPU_CLK_FREQ_HZ +#define APP_TIMER_CLK_HZ (CPU_CLK_HZ / 2) +#define FCLK_CLK0_HZ 100000000 +#endif +/********* Zynq Platform Configs **********/ +// APP Timer (for logging) +#ifdef __aarch64__ // Zynq MPSoC (Zynq UltraScale+) +#define APPTIMER_DEVICE_ID XPAR_AXI_TIMER_DEVICE_ID +#define APPTIMER_INTR_ID XPAR_FABRIC_AXI_TIMER_INTERRUPT_INTR +#else +#define APPTIMER_DEVICE_ID XPAR_PS7_SCUTIMER_0_DEVICE_ID +#define APPTIMER_INTR_ID XPAR_SCUTIMER_INTR +#endif +// Physical Layer Configs +#define PHY_DEVICE_ID (0x01 | 0x00) +#define PHY_BASEADDR XPAR_PHYC_BASEADDR +#define PHY_BEAM_FORMING_ENABLE +// -- Antenna Bram Configs +#define PHY_ANTCONFIG_BRAM_DEVICE_ID XPAR_PHY_DATA_INTERFACE_ANT_CONFIG_BRAM_CTRL_DEVICE_ID +#define PHY_ANTINFO_BRAM_DEVICE_ID XPAR_PHY_DATA_INTERFACE_ANT_BEAM_BRAM_CTRL_DEVICE_ID +// -- Buffers +#define PHY_TXBUFF_BRAM_DEVICE_ID XPAR_PHY_DATA_INTERFACE_TX_FRAME_BRAM_CTRL_DEVICE_ID +#define PHY_RXBUFF_BRAM_DEVICE_ID XPAR_PHY_DATA_INTERFACE_RX_FRAME_BRAM_CTRL_DEVICE_ID +// -- Xadc +#define PHY_HW_SYSMON_DEVICE_ID XPAR_SYSTEM_MANAGEMENT_DEVICE_ID +// -- Interrupt id +#define PHY_INTR_ID XPAR_FABRIC_PHY_IRQ_INTR +#define PERIODIC_5MS_INTR_ID XPAR_FABRIC_PHY_5MS_DELAY_2MS_INTR + +// Time Controller Configs +#define TIME_CONTROLLER_BASEADDR XPAR_TMC_BASEADDR + +// System Bus Configs +#define SYSBUS_BASE_ADDR XPAR_SYSTEMBUS_BASEADDR +#define SYSBUS_TX_INTR_ID XPAR_FABRIC_SYSTEMBUS_TX_DONE_INTR +#define SYSBUS_RX_INTR_ID XPAR_FABRIC_SYSTEMBUS_RX_DONE_INTR + +// INTC configis +#define INTC_DEVICE_ID XPAR_SCUGIC_0_DEVICE_ID + +/********* Hardware Configurations **********/ + +// Clock Management Configuration +//#define HMC7044 +//#define AD9528 +//#define LMK04828 +//#define LMX2594_ADC +//#define LMX2594_DAC + +// RF Transceiver Configuration +//#define AD9361_REG +//#define AD9361_API +//#define RFSOC +#define CX9261A + +// ADC Configuration +//#define AD9653 +//#define AD9694 + +// RFSoC Configuration +#ifdef RFSOC +#define RFDC_DEVICE_ID XPAR_XRFDC_0_DEVICE_ID +#endif + +// AD9361 Configuration +#define SPI_INTERFACE_SPI_ENGINE +#define HAVE_SPLIT_GAIN_TABLE 1 /* only set to 0 in case split_gain_table_mode_enable = 0*/ +#define HAVE_TDD_SYNTH_TABLE 1 /* only set to 0 in case split_gain_table_mode_enable = 0*/ +#define AD9361_DEVICE 1 /* set it 1 if AD9361 device is used, 0 otherwise */ +#define AD9364_DEVICE 0 /* set it 1 if AD9364 device is used, 0 otherwise */ +#define AD9363A_DEVICE 0 /* set it 1 if AD9363A device is used, 0 otherwise */ +#define HAVE_VERBOSE_MESSAGES /* Recommended during development prints errors and warnings */ +// #define HAVE_DEBUG_MESSAGES /* For Debug purposes only */ +// -- AD9361 SPI Engine Configuration +#define AD9361_SPI_DEVICE_ID 0 +#define AD9361_SPI_ENGINE_REF_CLK FCLK_CLK0_HZ +#define AD9361_SPI_ENGINE_MAX_SPEED 5000000 +//#define AD9361_SPI_ENGINE_BASEADDR 0 +#define AD9361_SPI_ENGINE_BASEADDR 0 +#define AD9361_SPI_ENGINE_DATA_WIDTH 8 +#define AD9361_SPI_ENGINE_CS_DELAY 3 +#define AD9361_1_SPI_CS 0x01 +#define AD9361_2_SPI_CS 0x02 +#define AD9361_TIMEOUT_TH 2000 +// -- AD9361 GPIO Configurations +#define AD9361_GPIO_DEVICE_ID 0 +#define AD9361_GPIO_OFFSET 54 +#define AD9361_GPIO_INPUT 0 +#define AD9361_GPIO_OUTPUT 1 +#define AD9361_GPIO_DISABLE 0 +#define AD9361_GPIO_ENABLE 1 +// -- AD9361-1 +#define AD9361_1_TXNRX (AD9361_GPIO_OFFSET + 6) +#define AD9361_1_ENABLE (AD9361_GPIO_OFFSET + 7) +#define AD9361_1_EN_AGC (AD9361_GPIO_OFFSET + 8) +#define AD9361_1_RESET_B (AD9361_GPIO_OFFSET + 9) +#define AD9361_1_CTRL_OUT0 (AD9361_GPIO_OFFSET + 10) +#define AD9361_1_CTRL_OUT1 (AD9361_GPIO_OFFSET + 11) +#define AD9361_1_CTRL_OUT2 (AD9361_GPIO_OFFSET + 12) +#define AD9361_1_CTRL_OUT3 (AD9361_GPIO_OFFSET + 13) +#define AD9361_1_CTRL_OUT4 (AD9361_GPIO_OFFSET + 14) +#define AD9361_1_CTRL_OUT5 (AD9361_GPIO_OFFSET + 15) +#define AD9361_1_CTRL_OUT6 (AD9361_GPIO_OFFSET + 16) +#define AD9361_1_CTRL_OUT7 (AD9361_GPIO_OFFSET + 17) +#define AD9361_1_CTRL_IN0 (AD9361_GPIO_OFFSET + 18) +#define AD9361_1_CTRL_IN1 (AD9361_GPIO_OFFSET + 19) +#define AD9361_1_CTRL_IN2 (AD9361_GPIO_OFFSET + 20) +#define AD9361_1_CTRL_IN3 (AD9361_GPIO_OFFSET + 21) +// AD9361-2 +#define AD9361_2_TXNRX (AD9361_GPIO_OFFSET + 22) +#define AD9361_2_ENABLE (AD9361_GPIO_OFFSET + 23) +#define AD9361_2_EN_AGC (AD9361_GPIO_OFFSET + 24) +#define AD9361_2_RESET_B (AD9361_GPIO_OFFSET + 25) +#define AD9361_2_CTRL_OUT0 (AD9361_GPIO_OFFSET + 26) +#define AD9361_2_CTRL_OUT1 (AD9361_GPIO_OFFSET + 27) +#define AD9361_2_CTRL_OUT2 (AD9361_GPIO_OFFSET + 28) +#define AD9361_2_CTRL_OUT3 (AD9361_GPIO_OFFSET + 29) +#define AD9361_2_CTRL_OUT4 (AD9361_GPIO_OFFSET + 30) +#define AD9361_2_CTRL_OUT5 (AD9361_GPIO_OFFSET + 31) +#define AD9361_2_CTRL_OUT6 (AD9361_GPIO_OFFSET + 32) +#define AD9361_2_CTRL_OUT7 (AD9361_GPIO_OFFSET + 33) +#define AD9361_2_CTRL_IN0 (AD9361_GPIO_OFFSET + 34) +#define AD9361_2_CTRL_IN1 (AD9361_GPIO_OFFSET + 35) +#define AD9361_2_CTRL_IN2 (AD9361_GPIO_OFFSET + 36) +#define AD9361_2_CTRL_IN3 (AD9361_GPIO_OFFSET + 37) +// AD9361-Status Code +#define AD9361_SYNC (AD9361_GPIO_OFFSET + 38) +#define AD9361_RX_FIFO_RST (AD9361_GPIO_OFFSET + 39) +#define AD9361_1_CAL_START (AD9361_GPIO_OFFSET + 40) +#define AD9361_2_CAL_START (AD9361_GPIO_OFFSET + 41) +#define AD9361_RECAL (AD9361_GPIO_OFFSET + 42) +#define AD9361_TX_SWITCH (AD9361_GPIO_OFFSET + 43) +#define AD9361_1_CAL_DONE (AD9361_GPIO_OFFSET + 44) +#define AD9361_2_CAL_DONE (AD9361_GPIO_OFFSET + 45) +#define AD9361_CAL_RESULT (AD9361_GPIO_OFFSET + 46) + +typedef enum +{ + TX_FREQ_125, + TX_FREQ_126, + TX_FREQ_4125 +} ad9361TxFreq; + +// CX9261A Configurations +#define CX9261A_GPIO_DEVICE_ID 0 +#define CX9261A_GPIO_OFFSET 54 +#define CX9261A_NRST (CX9261A_GPIO_OFFSET+0) +#define CX9261A_TXNRX_TRX1 (CX9261A_GPIO_OFFSET+1) +#define CX9261A_TXNRX_TRX2 (CX9261A_GPIO_OFFSET+2) +#define CX9261A_TXNRX_RXFB (CX9261A_GPIO_OFFSET+3) +#define CX9261A_ENABLE_TRX1 (CX9261A_GPIO_OFFSET+4) +#define CX9261A_ENABLE_TRX2 (CX9261A_GPIO_OFFSET+5) +#define CX9261A_ENABLE_RXFB (CX9261A_GPIO_OFFSET+6) +#define CX9261A_PL_RST (CX9261A_GPIO_OFFSET+7) +#define CX9261A_TR_SEL (CX9261A_GPIO_OFFSET+8) +#define CX9261A_TDD_FDD_SEL (CX9261A_GPIO_OFFSET+9) +#define CX9261A_R1T1_R2T2_SEL (CX9261A_GPIO_OFFSET+10) +#define CX9261A_SP_DP_SEL (CX9261A_GPIO_OFFSET+11) +#define CX9261A_MCLK_DELAY_CTRL0 (CX9261A_GPIO_OFFSET+12) +#define CX9261A_MCLK_DELAY_CTRL1 (CX9261A_GPIO_OFFSET+13) +#define CX9261A_MCLK_DELAY_CTRL2 (CX9261A_GPIO_OFFSET+14) +#define CX9261A_HOP_FLAG (CX9261A_GPIO_OFFSET+15) +// -- CX9261A SPI Engine Configuration +#define CX9261A_SPI_DEVICE_ID 0 +#define CX9261A_SPI_ENGINE_REF_CLK FCLK_CLK0_HZ +#define CX9261A_SPI_ENGINE_MAX_SPEED 5000000 +//#define CX9261A_SPI_ENGINE_BASEADDR 0 +#define CX9261A_SPI_ENGINE_BASEADDR 0 +#define CX9261A_SPI_ENGINE_DATA_WIDTH 8 +#define CX9261A_SPI_ENGINE_CS_DELAY 3 +#define CX9261A_SPI_CS 0x00 + +// HMC7044 Configurations +#define HMC7044_GPIO_DEVICE_ID 0 +#define HMC7044_GPIO_OFFSET 54 +#define HMC7044_RESET (HMC7044_GPIO_OFFSET + 0) +#define HMC7044_SYNC (HMC7044_GPIO_OFFSET + 1) +#define HMC7044_GPIO_0 (HMC7044_GPIO_OFFSET + 2) +#define HMC7044_GPIO_1 (HMC7044_GPIO_OFFSET + 3) +#define HMC7044_GPIO_2 (HMC7044_GPIO_OFFSET + 4) +#define HMC7044_GPIO_3 (HMC7044_GPIO_OFFSET + 5) +// -- HMC7044 SPI Engine Configuration +#define HMC7044_SPI_DEVICE_ID 0 +#define HMC7044_SPI_ENGINE_REF_CLK FCLK_CLK0_HZ +#define HMC7044_SPI_ENGINE_MAX_SPEED 5000000 +#define HMC7044_SPI_ENGINE_BASEADDR 0 +#define HMC7044_SPI_ENGINE_DATA_WIDTH 8 +#define HMC7044_SPI_ENGINE_CS_DELAY 3 +#define HMC7044_SPI_CS 0x00 + +// AD9528 Configurations +#define AD9528_GPIO_DEVICE_ID 0 +#define AD9528_GPIO_OFFSET 54 +#define AD9528_RESETB (AD9528_GPIO_OFFSET+0) +#define AD9528_SYSREF_REQ (AD9528_GPIO_OFFSET+1) +#define AD9528_STATUS_0 (AD9528_GPIO_OFFSET+2) +#define AD9528_STATUS_1 (AD9528_GPIO_OFFSET+3) +// -- AD9528 SPI Engine Configuration +#define AD9528_SPI_DEVICE_ID 0 +#define AD9528_SPI_ENGINE_REF_CLK FCLK_CLK0_HZ +#define AD9528_SPI_ENGINE_MAX_SPEED 5000000 +//#define AD9528_SPI_ENGINE_BASEADDR 0 +#define AD9528_SPI_ENGINE_BASEADDR 0 +#define AD9528_SPI_ENGINE_DATA_WIDTH 8 +#define AD9528_SPI_ENGINE_CS_DELAY 3 +#define AD9528_SPI_CS 0x00 + +// LMK04828 Configurations +#define LMK04828_GPIO_DEVICE_ID 0 +#define LMK04828_GPIO_OFFSET 54 +// -- LMK04828 SPI Engine Configuration +#define LMK04828_SPI_DEVICE_ID 0 +#define LMK04828_SPI_ENGINE_REF_CLK FCLK_CLK0_HZ +#define LMK04828_SPI_ENGINE_MAX_SPEED 5000000 +#define LMK04828_SPI_ENGINE_BASEADDR 0 +#define LMK04828_SPI_ENGINE_DATA_WIDTH 8 +#define LMK04828_SPI_ENGINE_CS_DELAY 3 +#define LMK04828_SPI_CS 0x00 + +// LMX2594_ADC Configurations +#define LMX2594_ADC_GPIO_DEVICE_ID 0 +#define LMX2594_ADC_GPIO_OFFSET 54 +// -- LMX2594_ADC SPI Engine Configuration +#define LMX2594_ADC_SPI_DEVICE_ID 0 +#define LMX2594_ADC_SPI_ENGINE_REF_CLK FCLK_CLK0_HZ +#define LMX2594_ADC_SPI_ENGINE_MAX_SPEED 5000000 +#define LMX2594_ADC_SPI_ENGINE_BASEADDR 0 +#define LMX2594_ADC_SPI_ENGINE_DATA_WIDTH 8 +#define LMX2594_ADC_SPI_ENGINE_CS_DELAY 3 +#define LMX2594_ADC_SPI_CS 0x01 + +// LMX2594_DAC Configurations +#define LMX2594_DAC_GPIO_DEVICE_ID 0 +#define LMX2594_DAC_GPIO_OFFSET 54 +// -- LMX2594_DAC SPI Engine Configuration +#define LMX2594_DAC_SPI_DEVICE_ID 0 +#define LMX2594_DAC_SPI_ENGINE_REF_CLK FCLK_CLK0_HZ +#define LMX2594_DAC_SPI_ENGINE_MAX_SPEED 5000000 +#define LMX2594_DAC_SPI_ENGINE_BASEADDR 0 +#define LMX2594_DAC_SPI_ENGINE_DATA_WIDTH 8 +#define LMX2594_DAC_SPI_ENGINE_CS_DELAY 3 +#define LMX2594_DAC_SPI_CS 0x02 + +// AD9653 Configurations +#define AD9653_GPIO_DEVICE_ID 0 +#define AD9653_GPIO_OFFSET 54 +#define AD9653_POWR_DOWN (AD9653_GPIO_OFFSET+0) +// -- AD9653 SPI Engine Configuration +#define AD9653_SPI_DEVICE_ID 0 +#define AD9653_SPI_ENGINE_REF_CLK FCLK_CLK0_HZ +#define AD9653_SPI_ENGINE_MAX_SPEED 5000000 +//#define AD9653_SPI_ENGINE_BASEADDR 0 +#define AD9653_SPI_ENGINE_BASEADDR 0 +#define AD9653_SPI_ENGINE_DATA_WIDTH 8 +#define AD9653_SPI_ENGINE_CS_DELAY 3 +#define AD9653_SPI_CS 0x03 + +// AD9694 Configurations +#define AD9694_GPIO_DEVICE_ID 0 +#define AD9694_GPIO_OFFSET 54 +// -- AD9694 SPI Engine Configuration +#define AD9694_SPI_DEVICE_ID 0 +#define AD9694_SPI_ENGINE_REF_CLK FCLK_CLK0_HZ +#define AD9694_SPI_ENGINE_MAX_SPEED 5000000 +//#define AD9694_SPI_ENGINE_BASEADDR 0 +#define AD9694_SPI_ENGINE_BASEADDR 0 +#define AD9694_SPI_ENGINE_DATA_WIDTH 8 +#define AD9694_SPI_ENGINE_CS_DELAY 3 +#define AD9694_SPI_CS 0x03 + +// Configuration Check +#if defined(PHY_BEAM_SET_PRESET) && !defined(PHY_BEAM_FORMING_ENABLE) +#define PHY_BEAM_FORMING_ENABLE +#warning To impliment beam scan must enable beam forming... +#endif + +#if defined(DATALINK_ENABLE_BERTEST) +#if !defined(DATALINK_ENABLE_AUTOTIMESYNC) +#warning To enbale bit error rate test, auto time-sync should be enabled... +#endif +#endif + +#endif //__APP_CONFIG_H_ diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/ad9361_profile.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/ad9361_profile.c new file mode 100644 index 0000000..a5d6817 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/ad9361_profile.c @@ -0,0 +1,340 @@ +#include "dev_profile.h" +#include "../config/app_config.h" +#include "../drivers/rf-transceiver/ad9361/ad9361_api.h" +#include "../drivers/axi_core/spi_engine/spi_engine.h" +#include "../drivers/platform/xilinx_gpio.h" + +struct spi_engine_init_param spi_engine_param = { + .ref_clk_hz = AD9361_SPI_ENGINE_REF_CLK, + .type = SPI_ENGINE, + .spi_engine_baseaddr = AD9361_SPI_ENGINE_BASEADDR, + .cs_delay = AD9361_SPI_ENGINE_CS_DELAY, + .data_width = AD9361_SPI_ENGINE_DATA_WIDTH +}; + +struct xil_gpio_init_param xil_gpio_param = { + .type = GPIO_PS, + .device_id = AD9361_GPIO_DEVICE_ID +}; + +AD9361_InitParam ad9361_default_init_param = { + /* Device selection */ + ID_AD9361, // dev_sel + /* Reference Clock */ + 48000000UL, //reference_clk_rate + /* Base Configuration */ + 1, //two_rx_two_tx_mode_enable *** adi,2rx-2tx-mode-enable + 1, //one_rx_one_tx_mode_use_rx_num *** adi,1rx-1tx-mode-use-rx-num + 1, //one_rx_one_tx_mode_use_tx_num *** adi,1rx-1tx-mode-use-tx-num + 1, //frequency_division_duplex_mode_enable *** adi,frequency-division-duplex-mode-enable + 0, //frequency_division_duplex_independent_mode_enable *** adi,frequency-division-duplex-independent-mode-enable + 0, //tdd_use_dual_synth_mode_enable *** adi,tdd-use-dual-synth-mode-enable + 0, //tdd_skip_vco_cal_enable *** adi,tdd-skip-vco-cal-enable + 0, //tx_fastlock_delay_ns *** adi,tx-fastlock-delay-ns + 0, //rx_fastlock_delay_ns *** adi,rx-fastlock-delay-ns + 0, //rx_fastlock_pincontrol_enable *** adi,rx-fastlock-pincontrol-enable + 0, //tx_fastlock_pincontrol_enable *** adi,tx-fastlock-pincontrol-enable + 0, //external_rx_lo_enable *** adi,external-rx-lo-enable + 0, //external_tx_lo_enable *** adi,external-tx-lo-enable + 5, //dc_offset_tracking_update_event_mask *** adi,dc-offset-tracking-update-event-mask + 6, //dc_offset_attenuation_high_range *** adi,dc-offset-attenuation-high-range + 5, //dc_offset_attenuation_low_range *** adi,dc-offset-attenuation-low-range + 0x28, //dc_offset_count_high_range *** adi,dc-offset-count-high-range + 0x32, //dc_offset_count_low_range *** adi,dc-offset-count-low-range + 0, //split_gain_table_mode_enable *** adi,split-gain-table-mode-enable + MAX_SYNTH_FREF, //trx_synthesizer_target_fref_overwrite_hz *** adi,trx-synthesizer-target-fref-overwrite-hz + 0, // qec_tracking_slow_mode_enable *** adi,qec-tracking-slow-mode-enable + /* ENSM Control */ + 0, //ensm_enable_pin_pulse_mode_enable *** adi,ensm-enable-pin-pulse-mode-enable + 0, //ensm_enable_txnrx_control_enable *** adi,ensm-enable-txnrx-control-enable + /* LO Control */ + 4125000000UL, //rx_synthesizer_frequency_hz *** adi,rx-synthesizer-frequency-hz + 4126000000UL, //tx_synthesizer_frequency_hz *** adi,tx-synthesizer-frequency-hz + 1, //tx_lo_powerdown_managed_enable *** adi,tx-lo-powerdown-managed-enable + /* Rate & BW Control */ + {768000000, 192000000, 96000000, 48000000, 24000000, 24000000},// rx_path_clock_frequencies[6] *** adi,rx-path-clock-frequencies + {768000000, 192000000, 96000000, 48000000, 24000000, 24000000},// tx_path_clock_frequencies[6] *** adi,tx-path-clock-frequencies + 18000000,//rf_rx_bandwidth_hz *** adi,rf-rx-bandwidth-hz + 18000000,//rf_tx_bandwidth_hz *** adi,rf-tx-bandwidth-hz + /* RF Port Control */ + 0, //rx_rf_port_input_select *** adi,rx-rf-port-input-select + 0, //tx_rf_port_input_select *** adi,tx-rf-port-input-select + /* TX Attenuation Control */ + 10000, //tx_attenuation_mdB *** adi,tx-attenuation-mdB + 0, //update_tx_gain_in_alert_enable *** adi,update-tx-gain-in-alert-enable + /* Reference Clock Control */ + 0, //xo_disable_use_ext_refclk_enable *** adi,xo-disable-use-ext-refclk-enable + {8, 5920}, //dcxo_coarse_and_fine_tune[2] *** adi,dcxo-coarse-and-fine-tune + CLKOUT_DISABLE, //clk_output_mode_select *** adi,clk-output-mode-select + /* Gain Control */ + 2, //gc_rx1_mode *** adi,gc-rx1-mode + 2, //gc_rx2_mode *** adi,gc-rx2-mode + 58, //gc_adc_large_overload_thresh *** adi,gc-adc-large-overload-thresh + 4, //gc_adc_ovr_sample_size *** adi,gc-adc-ovr-sample-size + 47, //gc_adc_small_overload_thresh *** adi,gc-adc-small-overload-thresh + 8192, //gc_dec_pow_measurement_duration *** adi,gc-dec-pow-measurement-duration + 0, //gc_dig_gain_enable *** adi,gc-dig-gain-enable + 800, //gc_lmt_overload_high_thresh *** adi,gc-lmt-overload-high-thresh + 704, //gc_lmt_overload_low_thresh *** adi,gc-lmt-overload-low-thresh + 24, //gc_low_power_thresh *** adi,gc-low-power-thresh + 15, //gc_max_dig_gain *** adi,gc-max-dig-gain + 0, //gc_use_rx_fir_out_for_dec_pwr_meas_enable *** adi,gc-use-rx-fir-out-for-dec-pwr-meas-enable + /* Gain MGC Control */ + 2, //mgc_dec_gain_step *** adi,mgc-dec-gain-step + 2, //mgc_inc_gain_step *** adi,mgc-inc-gain-step + 0, //mgc_rx1_ctrl_inp_enable *** adi,mgc-rx1-ctrl-inp-enable + 0, //mgc_rx2_ctrl_inp_enable *** adi,mgc-rx2-ctrl-inp-enable + 0, //mgc_split_table_ctrl_inp_gain_mode *** adi,mgc-split-table-ctrl-inp-gain-mode + /* Gain AGC Control */ + 10, //agc_adc_large_overload_exceed_counter *** adi,agc-adc-large-overload-exceed-counter + 2, //agc_adc_large_overload_inc_steps *** adi,agc-adc-large-overload-inc-steps + 0, //agc_adc_lmt_small_overload_prevent_gain_inc_enable *** adi,agc-adc-lmt-small-overload-prevent-gain-inc-enable + 10, //agc_adc_small_overload_exceed_counter *** adi,agc-adc-small-overload-exceed-counter + 4, //agc_dig_gain_step_size *** adi,agc-dig-gain-step-size + 3, //agc_dig_saturation_exceed_counter *** adi,agc-dig-saturation-exceed-counter + 1000, // agc_gain_update_interval_us *** adi,agc-gain-update-interval-us + 0, //agc_immed_gain_change_if_large_adc_overload_enable *** adi,agc-immed-gain-change-if-large-adc-overload-enable + 0, //agc_immed_gain_change_if_large_lmt_overload_enable *** adi,agc-immed-gain-change-if-large-lmt-overload-enable + 10, //agc_inner_thresh_high *** adi,agc-inner-thresh-high + 1, //agc_inner_thresh_high_dec_steps *** adi,agc-inner-thresh-high-dec-steps + 12, //agc_inner_thresh_low *** adi,agc-inner-thresh-low + 1, //agc_inner_thresh_low_inc_steps *** adi,agc-inner-thresh-low-inc-steps + 10, //agc_lmt_overload_large_exceed_counter *** adi,agc-lmt-overload-large-exceed-counter + 2, //agc_lmt_overload_large_inc_steps *** adi,agc-lmt-overload-large-inc-steps + 10, //agc_lmt_overload_small_exceed_counter *** adi,agc-lmt-overload-small-exceed-counter + 5, //agc_outer_thresh_high *** adi,agc-outer-thresh-high + 2, //agc_outer_thresh_high_dec_steps *** adi,agc-outer-thresh-high-dec-steps + 18, //agc_outer_thresh_low *** adi,agc-outer-thresh-low + 2, //agc_outer_thresh_low_inc_steps *** adi,agc-outer-thresh-low-inc-steps + 1, //agc_attack_delay_extra_margin_us; *** adi,agc-attack-delay-extra-margin-us + 0, //agc_sync_for_gain_counter_enable *** adi,agc-sync-for-gain-counter-enable + /* Fast AGC */ + 64, //fagc_dec_pow_measuremnt_duration *** adi,fagc-dec-pow-measurement-duration + 260, //fagc_state_wait_time_ns *** adi,fagc-state-wait-time-ns + /* Fast AGC - Low Power */ + 0, //fagc_allow_agc_gain_increase *** adi,fagc-allow-agc-gain-increase-enable + 5, //fagc_lp_thresh_increment_time *** adi,fagc-lp-thresh-increment-time + 1, //fagc_lp_thresh_increment_steps *** adi,fagc-lp-thresh-increment-steps + /* Fast AGC - Lock Level (Lock Level is set via slow AGC inner high threshold) */ + 1, //fagc_lock_level_lmt_gain_increase_en *** adi,fagc-lock-level-lmt-gain-increase-enable + 5, //fagc_lock_level_gain_increase_upper_limit *** adi,fagc-lock-level-gain-increase-upper-limit + /* Fast AGC - Peak Detectors and Final Settling */ + 1, //fagc_lpf_final_settling_steps *** adi,fagc-lpf-final-settling-steps + 1, //fagc_lmt_final_settling_steps *** adi,fagc-lmt-final-settling-steps + 3, //fagc_final_overrange_count *** adi,fagc-final-overrange-count + /* Fast AGC - Final Power Test */ + 0, //fagc_gain_increase_after_gain_lock_en *** adi,fagc-gain-increase-after-gain-lock-enable + /* Fast AGC - Unlocking the Gain */ + 0, //fagc_gain_index_type_after_exit_rx_mode *** adi,fagc-gain-index-type-after-exit-rx-mode + 1, //fagc_use_last_lock_level_for_set_gain_en *** adi,fagc-use-last-lock-level-for-set-gain-enable + 1, //fagc_rst_gla_stronger_sig_thresh_exceeded_en *** adi,fagc-rst-gla-stronger-sig-thresh-exceeded-enable + 5, //fagc_optimized_gain_offset *** adi,fagc-optimized-gain-offset + 10, //fagc_rst_gla_stronger_sig_thresh_above_ll *** adi,fagc-rst-gla-stronger-sig-thresh-above-ll + 1, //fagc_rst_gla_engergy_lost_sig_thresh_exceeded_en *** adi,fagc-rst-gla-engergy-lost-sig-thresh-exceeded-enable + 1, //fagc_rst_gla_engergy_lost_goto_optim_gain_en *** adi,fagc-rst-gla-engergy-lost-goto-optim-gain-enable + 10, //fagc_rst_gla_engergy_lost_sig_thresh_below_ll *** adi,fagc-rst-gla-engergy-lost-sig-thresh-below-ll + 8, //fagc_energy_lost_stronger_sig_gain_lock_exit_cnt *** adi,fagc-energy-lost-stronger-sig-gain-lock-exit-cnt + 1, //fagc_rst_gla_large_adc_overload_en *** adi,fagc-rst-gla-large-adc-overload-enable + 1, //fagc_rst_gla_large_lmt_overload_en *** adi,fagc-rst-gla-large-lmt-overload-enable + 0, //fagc_rst_gla_en_agc_pulled_high_en *** adi,fagc-rst-gla-en-agc-pulled-high-enable + 0, //fagc_rst_gla_if_en_agc_pulled_high_mode *** adi,fagc-rst-gla-if-en-agc-pulled-high-mode + 64, //fagc_power_measurement_duration_in_state5 *** adi,fagc-power-measurement-duration-in-state5 + 2, //fagc_large_overload_inc_steps *** adi,fagc-adc-large-overload-inc-steps + /* RSSI Control */ + 1, //rssi_delay *** adi,rssi-delay + 1000, //rssi_duration *** adi,rssi-duration + 3, //rssi_restart_mode *** adi,rssi-restart-mode + 0, //rssi_unit_is_rx_samples_enable *** adi,rssi-unit-is-rx-samples-enable + 1, //rssi_wait *** adi,rssi-wait + /* Aux ADC Control */ + 256, //aux_adc_decimation *** adi,aux-adc-decimation + 48000000UL, //aux_adc_rate *** adi,aux-adc-rate + /* AuxDAC Control */ + 1, //aux_dac_manual_mode_enable *** adi,aux-dac-manual-mode-enable + 0, //aux_dac1_default_value_mV *** adi,aux-dac1-default-value-mV + 0, //aux_dac1_active_in_rx_enable *** adi,aux-dac1-active-in-rx-enable + 0, //aux_dac1_active_in_tx_enable *** adi,aux-dac1-active-in-tx-enable + 0, //aux_dac1_active_in_alert_enable *** adi,aux-dac1-active-in-alert-enable + 0, //aux_dac1_rx_delay_us *** adi,aux-dac1-rx-delay-us + 0, //aux_dac1_tx_delay_us *** adi,aux-dac1-tx-delay-us + 0, //aux_dac2_default_value_mV *** adi,aux-dac2-default-value-mV + 0, //aux_dac2_active_in_rx_enable *** adi,aux-dac2-active-in-rx-enable + 0, //aux_dac2_active_in_tx_enable *** adi,aux-dac2-active-in-tx-enable + 0, //aux_dac2_active_in_alert_enable *** adi,aux-dac2-active-in-alert-enable + 0, //aux_dac2_rx_delay_us *** adi,aux-dac2-rx-delay-us + 0, //aux_dac2_tx_delay_us *** adi,aux-dac2-tx-delay-us + /* Temperature Sensor Control */ + 256, //temp_sense_decimation *** adi,temp-sense-decimation + 1000, //temp_sense_measurement_interval_ms *** adi,temp-sense-measurement-interval-ms + 0xCE, //temp_sense_offset_signed *** adi,temp-sense-offset-signed + 1, //temp_sense_periodic_measurement_enable *** adi,temp-sense-periodic-measurement-enable + /* Control Out Setup */ + 0xFF, //ctrl_outs_enable_mask *** adi,ctrl-outs-enable-mask + 0, //ctrl_outs_index *** adi,ctrl-outs-index + /* External LNA Control */ + 0, //elna_settling_delay_ns *** adi,elna-settling-delay-ns + 0, //elna_gain_mdB *** adi,elna-gain-mdB + 0, //elna_bypass_loss_mdB *** adi,elna-bypass-loss-mdB + 0, //elna_rx1_gpo0_control_enable *** adi,elna-rx1-gpo0-control-enable + 0, //elna_rx2_gpo1_control_enable *** adi,elna-rx2-gpo1-control-enable + 0, //elna_gaintable_all_index_enable *** adi,elna-gaintable-all-index-enable + /* Digital Interface Control */ + 0, //digital_interface_tune_skip_mode *** adi,digital-interface-tune-skip-mode + 0, //digital_interface_tune_fir_disable *** adi,digital-interface-tune-fir-disable + 1, //pp_tx_swap_enable *** adi,pp-tx-swap-enable + 1, //pp_rx_swap_enable *** adi,pp-rx-swap-enable + 0, //tx_channel_swap_enable *** adi,tx-channel-swap-enable + 0, //rx_channel_swap_enable *** adi,rx-channel-swap-enable + 1, //rx_frame_pulse_mode_enable *** adi,rx-frame-pulse-mode-enable + 0, //two_t_two_r_timing_enable *** adi,2t2r-timing-enable + 0, //invert_data_bus_enable *** adi,invert-data-bus-enable + 0, //invert_data_clk_enable *** adi,invert-data-clk-enable + 0, //fdd_alt_word_order_enable *** adi,fdd-alt-word-order-enable + 0, //invert_rx_frame_enable *** adi,invert-rx-frame-enable + 0, //fdd_rx_rate_2tx_enable *** adi,fdd-rx-rate-2tx-enable + 0, //swap_ports_enable *** adi,swap-ports-enable + 0, //single_data_rate_enable *** adi,single-data-rate-enable + 1, //lvds_mode_enable *** adi,lvds-mode-enable + 0, //half_duplex_mode_enable *** adi,half-duplex-mode-enable + 0, //single_port_mode_enable *** adi,single-port-mode-enable + 0, //full_port_enable *** adi,full-port-enable + 0, //full_duplex_swap_bits_enable *** adi,full-duplex-swap-bits-enable + 0, //delay_rx_data *** adi,delay-rx-data + 0, //rx_data_clock_delay *** adi,rx-data-clock-delay + 4, //rx_data_delay *** adi,rx-data-delay + 7, //tx_fb_clock_delay *** adi,tx-fb-clock-delay + 0, //tx_data_delay *** adi,tx-data-delay + 150, //lvds_bias_mV *** adi,lvds-bias-mV + 1, //lvds_rx_onchip_termination_enable *** adi,lvds-rx-onchip-termination-enable + 0, //rx1rx2_phase_inversion_en *** adi,rx1-rx2-phase-inversion-enable + 0xFF, //lvds_invert1_control *** adi,lvds-invert1-control + 0x0F, //lvds_invert2_control *** adi,lvds-invert2-control + /* GPO Control */ + 0, //gpo_manual_mode_enable *** adi,gpo-manual-mode-enable + 0, //gpo_manual_mode_enable_mask *** adi,gpo-manual-mode-enable-mask + 0, //gpo0_inactive_state_high_enable *** adi,gpo0-inactive-state-high-enable + 0, //gpo1_inactive_state_high_enable *** adi,gpo1-inactive-state-high-enable + 0, //gpo2_inactive_state_high_enable *** adi,gpo2-inactive-state-high-enable + 0, //gpo3_inactive_state_high_enable *** adi,gpo3-inactive-state-high-enable + 0, //gpo0_slave_rx_enable *** adi,gpo0-slave-rx-enable + 0, //gpo0_slave_tx_enable *** adi,gpo0-slave-tx-enable + 0, //gpo1_slave_rx_enable *** adi,gpo1-slave-rx-enable + 0, //gpo1_slave_tx_enable *** adi,gpo1-slave-tx-enable + 0, //gpo2_slave_rx_enable *** adi,gpo2-slave-rx-enable + 0, //gpo2_slave_tx_enable *** adi,gpo2-slave-tx-enable + 0, //gpo3_slave_rx_enable *** adi,gpo3-slave-rx-enable + 0, //gpo3_slave_tx_enable *** adi,gpo3-slave-tx-enable + 0, //gpo0_rx_delay_us *** adi,gpo0-rx-delay-us + 0, //gpo0_tx_delay_us *** adi,gpo0-tx-delay-us + 0, //gpo1_rx_delay_us *** adi,gpo1-rx-delay-us + 0, //gpo1_tx_delay_us *** adi,gpo1-tx-delay-us + 0, //gpo2_rx_delay_us *** adi,gpo2-rx-delay-us + 0, //gpo2_tx_delay_us *** adi,gpo2-tx-delay-us + 0, //gpo3_rx_delay_us *** adi,gpo3-rx-delay-us + 0, //gpo3_tx_delay_us *** adi,gpo3-tx-delay-us + /* Tx Monitor Control */ + 37000, //low_high_gain_threshold_mdB *** adi,txmon-low-high-thresh + 0, //low_gain_dB *** adi,txmon-low-gain + 24, //high_gain_dB *** adi,txmon-high-gain + 0, //tx_mon_track_en *** adi,txmon-dc-tracking-enable + 0, //one_shot_mode_en *** adi,txmon-one-shot-mode-enable + 511, //tx_mon_delay *** adi,txmon-delay + 8192, //tx_mon_duration *** adi,txmon-duration + 2, //tx1_mon_front_end_gain *** adi,txmon-1-front-end-gain + 2, //tx2_mon_front_end_gain *** adi,txmon-2-front-end-gain + 48, //tx1_mon_lo_cm *** adi,txmon-1-lo-cm + 48, //tx2_mon_lo_cm *** adi,txmon-2-lo-cm + /* GPIO definitions */ + /* Reset_b */ + { + .number = AD9361_1_RESET_B, + .platform_ops = &xil_gpio_ops, + .extra = &xil_gpio_param + }, //gpio_resetb *** reset-gpios + /* MCS Sync */ + { + .number = -1, + .platform_ops = &xil_gpio_ops, + .extra = &xil_gpio_param + }, //gpio_sync *** sync-gpios + /* Cal-SW1 */ + { + .number = -1, + .platform_ops = &xil_gpio_ops, + .extra = &xil_gpio_param + }, //gpio_cal_sw1 *** cal-sw1-gpios + /* Cal-SW2 */ + { + .number = -1, + .platform_ops = &xil_gpio_ops, + .extra = &xil_gpio_param + }, //gpio_cal_sw2 *** cal-sw2-gpios + + { + .device_id = AD9361_SPI_DEVICE_ID, + .mode = SPI_MODE_1, + .chip_select = AD9361_1_SPI_CS, + .platform_ops = &spi_eng_platform_ops, + .extra = &spi_engine_param + }, + + /* External LO clocks */ + NULL, //(*ad9361_rfpll_ext_recalc_rate)() + NULL, //(*ad9361_rfpll_ext_round_rate)() + NULL, //(*ad9361_rfpll_ext_set_rate)() +}; + +AD9361_RXFIRConfig rx_fir_config = { // BPF PASSBAND 3/20 fs to 1/4 fs + 3, // rx + 0, // rx_gain + 1, // rx_dec + { + -4, -6, -37, 35, 186, 86, -284, -315, + 107, 219, -4, 271, 558, -307, -1182, -356, + 658, 157, 207, 1648, 790, -2525, -2553, 748, + 865, -476, 3737, 6560, -3583, -14731, -5278, 14819, + 14819, -5278, -14731, -3583, 6560, 3737, -476, 865, + 748, -2553, -2525, 790, 1648, 207, 157, 658, + -356, -1182, -307, 558, 271, -4, 219, 107, + -315, -284, 86, 186, 35, -37, -6, -4, + 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 + }, // rx_coef[128] + 64, // rx_coef_size + {0, 0, 0, 0, 0, 0}, //rx_path_clks[6] + 0 // rx_bandwidth +}; + +AD9361_TXFIRConfig tx_fir_config = { // BPF PASSBAND 3/20 fs to 1/4 fs + 3, // tx + -6, // tx_gain + 1, // tx_int + { + -4, -6, -37, 35, 186, 86, -284, -315, + 107, 219, -4, 271, 558, -307, -1182, -356, + 658, 157, 207, 1648, 790, -2525, -2553, 748, + 865, -476, 3737, 6560, -3583, -14731, -5278, 14819, + 14819, -5278, -14731, -3583, 6560, 3737, -476, 865, + 748, -2553, -2525, 790, 1648, 207, 157, 658, + -356, -1182, -307, 558, 271, -4, 219, 107, + -315, -284, 86, 186, 35, -37, -6, -4, + 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 + }, // tx_coef[128] + 64, // tx_coef_size + {0, 0, 0, 0, 0, 0}, // tx_path_clks[6] + 0 // tx_bandwidth +}; + diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/ad9361_reg_profile.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/ad9361_reg_profile.c new file mode 100644 index 0000000..9edecb9 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/ad9361_reg_profile.c @@ -0,0 +1,1746 @@ +#include "dev_profile.h" +#include "spi.h" +#include "../config/app_config.h" +#include "../drivers/rf-transceiver/ad9361_reg/ad9361_reg.h" +#include "../drivers/axi_core/spi_engine/spi_engine.h" +#include "../drivers/platform/xilinx_gpio.h" + +#define TIMEOUT_TH AD9361_TIMEOUT_TH + +static struct spi_engine_init_param spi_engine_param = { + .ref_clk_hz = AD9361_SPI_ENGINE_REF_CLK, + .type = SPI_ENGINE, + .spi_engine_baseaddr = AD9361_SPI_ENGINE_BASEADDR, + .cs_delay = AD9361_SPI_ENGINE_CS_DELAY, + .data_width = AD9361_SPI_ENGINE_DATA_WIDTH +}; + +static struct xil_gpio_init_param xil_gpio_param = { + .type = GPIO_PS, + .device_id = AD9361_GPIO_DEVICE_ID +}; + +// ad9361-1 +static struct spi_init_param ad9361_1_reg_spi_init = { + .device_id = AD9361_SPI_DEVICE_ID, + .max_speed_hz = AD9361_SPI_ENGINE_MAX_SPEED, + .mode = SPI_MODE_1, + .chip_select = AD9361_1_SPI_CS, + .platform_ops = &spi_eng_platform_ops, + .extra = &spi_engine_param +}; + +static struct gpio_init_param ad9361_1_reg_gpio_resetb_init = { + .number = AD9361_1_RESET_B, + .platform_ops = &xil_gpio_ops, + .extra = &xil_gpio_param +}; +struct ad9361_reg_init_param ad9361_1_reg_default_init_param = { + /* SPI */ + .spi_init = &ad9361_1_reg_spi_init, + .gpio_resetb = &ad9361_1_reg_gpio_resetb_init +}; + +// ad9361-2 +static struct spi_init_param ad9361_2_reg_spi_init = { + .device_id = AD9361_SPI_DEVICE_ID, + .max_speed_hz = AD9361_SPI_ENGINE_MAX_SPEED, + .mode = SPI_MODE_1, + .chip_select = AD9361_2_SPI_CS, + .platform_ops = &spi_eng_platform_ops, + .extra = &spi_engine_param +}; + +static struct gpio_init_param ad9361_2_reg_gpio_resetb_init = { + .number = AD9361_2_RESET_B, + .platform_ops = &xil_gpio_ops, + .extra = &xil_gpio_param +}; +struct ad9361_reg_init_param ad9361_2_reg_default_init_param = { + /* SPI */ + .spi_init = &ad9361_2_reg_spi_init, + .gpio_resetb = &ad9361_2_reg_gpio_resetb_init +}; + +uint32_t ad9361_reg_config(struct ad9361_reg_dev *dev){ + int32_t status = 0; + uint8_t value = 0; + int32_t time_out_cnt = 0; + +//************************************************************ +// AD9361 R2 Auto Generated Initialization Script: This script was +// generated using the AD9361 Customer software Version 2.1.3 +//************************************************************ +// Profile: Custom +// REFCLK_IN: 25.000 MHz + + + status = ad9361_reg_write(dev, 0x03DF, 0x01); // Required for proper operation + status = ad9361_reg_write(dev, 0x02A6, 0x0E); // Enable Master Bias + status = ad9361_reg_write(dev, 0x02A8, 0x0E); // Set Bandgap Trim + status = ad9361_reg_write(dev, 0x02AB, 0x07); // Set RF PLL reflclk scale to REFCLK * 2 + status = ad9361_reg_write(dev, 0x02AC, 0xFF); // Set RF PLL reflclk scale to REFCLK * 2 + status = ad9361_reg_write(dev, 0x0009, 0x17); // Enable Clocks + mdelay(20) ;// waits 20 ms + +//************************************************************ +// Set BBPLL Frequency: 1152.000000 +//************************************************************ + status = ad9361_reg_write(dev, 0x0045, 0x03); // Set BBPLL reflclk scale to REFCLK * 2 + status = ad9361_reg_write(dev, 0x0046, 0x03); // Set BBPLL Loop Filter Charge Pump current + status = ad9361_reg_write(dev, 0x0048, 0xE8); // Set BBPLL Loop Filter C1, R1 + status = ad9361_reg_write(dev, 0x0049, 0x5B); // Set BBPLL Loop Filter R2, C2, C1 + status = ad9361_reg_write(dev, 0x004A, 0x35); // Set BBPLL Loop Filter C3,R2 + status = ad9361_reg_write(dev, 0x004B, 0xE0); // Allow calibration to occur and set cal count to 1024 for max accuracy + status = ad9361_reg_write(dev, 0x004E, 0x10); // Set calibration clock to REFCLK/4 for more accuracy + status = ad9361_reg_write(dev, 0x0043, 0x66); // BBPLL Freq Word (Fractional[7:0]) + status = ad9361_reg_write(dev, 0x0042, 0x46); // BBPLL Freq Word (Fractional[15:8]) + status = ad9361_reg_write(dev, 0x0041, 0x01); // BBPLL Freq Word (Fractional[23:16]) + status = ad9361_reg_write(dev, 0x0044, 0x17); // BBPLL Freq Word (Integer[7:0]) + status = ad9361_reg_write(dev, 0x003F, 0x05); // Start BBPLL Calibration + status = ad9361_reg_write(dev, 0x003F, 0x01); // Clear BBPLL start calibration bit + status = ad9361_reg_write(dev, 0x004C, 0x86); // Increase BBPLL KV and phase margin + status = ad9361_reg_write(dev, 0x004D, 0x01); // Increase BBPLL KV and phase margin + status = ad9361_reg_write(dev, 0x004D, 0x05); // Increase BBPLL KV and phase margin + + // Wait for BBPLL to lock, Timeout 2sec, Max BBPLL VCO Cal Time: 276.480 us (Done when 0x05E[7]==1) + time_out_cnt = 0; + do{ + status |= ad9361_reg_read(dev, 0x005E, &value); + mdelay(1);// Wait 1ms + time_out_cnt++; + } while((value & 0x80) != 0x80 && time_out_cnt <= AD9361_TIMEOUT_TH); + + + status = ad9361_reg_write(dev, 0x0002, 0xA4); // Setup Tx Digital Filters/ Channels + status = ad9361_reg_write(dev, 0x0003, 0xE5); // Setup Rx Digital Filters/ Channels + status = ad9361_reg_write(dev, 0x0004, 0x03); // Select Rx input pin(A,B,C)/ Tx out pin (A,B) + status = ad9361_reg_write(dev, 0x000A, 0x12); // Set BBPLL post divide rate + +//************************************************************ +// Program Rx FIR: C:\Users\josh\Desktop\Config\rx64.txt +//************************************************************ + status = ad9361_reg_write(dev, 0x00F5, 0x9A); // Enable clock to Rx FIR Filter + status = ad9361_reg_write(dev, 0x00F6, 0x02); // Write Filter Gain setting + status = ad9361_reg_write(dev, 0x00F0, 0x00); // Write FIR coefficient address + status = ad9361_reg_write(dev, 0x00F1, 0x00); // Write FIR coefficient data[7:0] + status = ad9361_reg_write(dev, 0x00F2, 0x00); // Write FIR coefficient data[15:8] + status = ad9361_reg_write(dev, 0x00F5, 0x9E); // Set Write EN to push data into FIR filter register map + status = ad9361_reg_write(dev, 0x00F4, 0x00); // Dummy Write to Read only register to delay ~1us + status = ad9361_reg_write(dev, 0x00F4, 0x00); // Dummy Write to Read only register to delay ~1us + status = ad9361_reg_write(dev, 0x00F0, 0x01); + status = ad9361_reg_write(dev, 0x00F1, 0xB6); + status = ad9361_reg_write(dev, 0x00F2, 0xFF); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x02); + status = ad9361_reg_write(dev, 0x00F1, 0xA5); + status = ad9361_reg_write(dev, 0x00F2, 0xFF); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x03); + status = ad9361_reg_write(dev, 0x00F1, 0xEA); + status = ad9361_reg_write(dev, 0x00F2, 0xFF); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x04); + status = ad9361_reg_write(dev, 0x00F1, 0x61); + status = ad9361_reg_write(dev, 0x00F2, 0x00); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x05); + status = ad9361_reg_write(dev, 0x00F1, 0xA3); + status = ad9361_reg_write(dev, 0x00F2, 0x00); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x06); + status = ad9361_reg_write(dev, 0x00F1, 0x4D); + status = ad9361_reg_write(dev, 0x00F2, 0x00); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x07); + status = ad9361_reg_write(dev, 0x00F1, 0x77); + status = ad9361_reg_write(dev, 0x00F2, 0xFF); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x08); + status = ad9361_reg_write(dev, 0x00F1, 0xD5); + status = ad9361_reg_write(dev, 0x00F2, 0xFE); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x09); + status = ad9361_reg_write(dev, 0x00F1, 0x38); + status = ad9361_reg_write(dev, 0x00F2, 0xFF); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x0A); + status = ad9361_reg_write(dev, 0x00F1, 0xA5); + status = ad9361_reg_write(dev, 0x00F2, 0x00); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x0B); + status = ad9361_reg_write(dev, 0x00F1, 0xF8); + status = ad9361_reg_write(dev, 0x00F2, 0x01); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x0C); + status = ad9361_reg_write(dev, 0x00F1, 0xAD); + status = ad9361_reg_write(dev, 0x00F2, 0x01); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x0D); + status = ad9361_reg_write(dev, 0x00F1, 0x75); + status = ad9361_reg_write(dev, 0x00F2, 0xFF); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x0E); + status = ad9361_reg_write(dev, 0x00F1, 0xFD); + status = ad9361_reg_write(dev, 0x00F2, 0xFC); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x0F); + status = ad9361_reg_write(dev, 0x00F1, 0xD8); + status = ad9361_reg_write(dev, 0x00F2, 0xFC); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x10); + status = ad9361_reg_write(dev, 0x00F1, 0x00); + status = ad9361_reg_write(dev, 0x00F2, 0x00); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x11); + status = ad9361_reg_write(dev, 0x00F1, 0x3A); + status = ad9361_reg_write(dev, 0x00F2, 0x04); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x12); + status = ad9361_reg_write(dev, 0x00F1, 0x6B); + status = ad9361_reg_write(dev, 0x00F2, 0x05); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x13); + status = ad9361_reg_write(dev, 0x00F1, 0x51); + status = ad9361_reg_write(dev, 0x00F2, 0x01); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x14); + status = ad9361_reg_write(dev, 0x00F1, 0x80); + status = ad9361_reg_write(dev, 0x00F2, 0xFA); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x15); + status = ad9361_reg_write(dev, 0x00F1, 0x34); + status = ad9361_reg_write(dev, 0x00F2, 0xF7); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x16); + status = ad9361_reg_write(dev, 0x00F1, 0x08); + status = ad9361_reg_write(dev, 0x00F2, 0xFC); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x17); + status = ad9361_reg_write(dev, 0x00F1, 0xB2); + status = ad9361_reg_write(dev, 0x00F2, 0x06); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x18); + status = ad9361_reg_write(dev, 0x00F1, 0x1A); + status = ad9361_reg_write(dev, 0x00F2, 0x0E); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x19); + status = ad9361_reg_write(dev, 0x00F1, 0x44); + status = ad9361_reg_write(dev, 0x00F2, 0x09); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x1A); + status = ad9361_reg_write(dev, 0x00F1, 0x54); + status = ad9361_reg_write(dev, 0x00F2, 0xF8); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x1B); + status = ad9361_reg_write(dev, 0x00F1, 0xD1); + status = ad9361_reg_write(dev, 0x00F2, 0xE7); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x1C); + status = ad9361_reg_write(dev, 0x00F1, 0xC2); + status = ad9361_reg_write(dev, 0x00F2, 0xE9); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x1D); + status = ad9361_reg_write(dev, 0x00F1, 0x4F); + status = ad9361_reg_write(dev, 0x00F2, 0x08); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x1E); + status = ad9361_reg_write(dev, 0x00F1, 0xB2); + status = ad9361_reg_write(dev, 0x00F2, 0x3B); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x1F); + status = ad9361_reg_write(dev, 0x00F1, 0x2A); + status = ad9361_reg_write(dev, 0x00F2, 0x6C); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x20); + status = ad9361_reg_write(dev, 0x00F1, 0xFF); + status = ad9361_reg_write(dev, 0x00F2, 0x7F); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x21); + status = ad9361_reg_write(dev, 0x00F1, 0x2A); + status = ad9361_reg_write(dev, 0x00F2, 0x6C); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x22); + status = ad9361_reg_write(dev, 0x00F1, 0xB2); + status = ad9361_reg_write(dev, 0x00F2, 0x3B); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x23); + status = ad9361_reg_write(dev, 0x00F1, 0x4F); + status = ad9361_reg_write(dev, 0x00F2, 0x08); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x24); + status = ad9361_reg_write(dev, 0x00F1, 0xC2); + status = ad9361_reg_write(dev, 0x00F2, 0xE9); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x25); + status = ad9361_reg_write(dev, 0x00F1, 0xD1); + status = ad9361_reg_write(dev, 0x00F2, 0xE7); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x26); + status = ad9361_reg_write(dev, 0x00F1, 0x54); + status = ad9361_reg_write(dev, 0x00F2, 0xF8); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x27); + status = ad9361_reg_write(dev, 0x00F1, 0x44); + status = ad9361_reg_write(dev, 0x00F2, 0x09); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x28); + status = ad9361_reg_write(dev, 0x00F1, 0x1A); + status = ad9361_reg_write(dev, 0x00F2, 0x0E); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x29); + status = ad9361_reg_write(dev, 0x00F1, 0xB2); + status = ad9361_reg_write(dev, 0x00F2, 0x06); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x2A); + status = ad9361_reg_write(dev, 0x00F1, 0x08); + status = ad9361_reg_write(dev, 0x00F2, 0xFC); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x2B); + status = ad9361_reg_write(dev, 0x00F1, 0x34); + status = ad9361_reg_write(dev, 0x00F2, 0xF7); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x2C); + status = ad9361_reg_write(dev, 0x00F1, 0x80); + status = ad9361_reg_write(dev, 0x00F2, 0xFA); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x2D); + status = ad9361_reg_write(dev, 0x00F1, 0x51); + status = ad9361_reg_write(dev, 0x00F2, 0x01); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x2E); + status = ad9361_reg_write(dev, 0x00F1, 0x6B); + status = ad9361_reg_write(dev, 0x00F2, 0x05); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x2F); + status = ad9361_reg_write(dev, 0x00F1, 0x3A); + status = ad9361_reg_write(dev, 0x00F2, 0x04); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x30); + status = ad9361_reg_write(dev, 0x00F1, 0x00); + status = ad9361_reg_write(dev, 0x00F2, 0x00); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x31); + status = ad9361_reg_write(dev, 0x00F1, 0xD8); + status = ad9361_reg_write(dev, 0x00F2, 0xFC); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x32); + status = ad9361_reg_write(dev, 0x00F1, 0xFD); + status = ad9361_reg_write(dev, 0x00F2, 0xFC); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x33); + status = ad9361_reg_write(dev, 0x00F1, 0x75); + status = ad9361_reg_write(dev, 0x00F2, 0xFF); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x34); + status = ad9361_reg_write(dev, 0x00F1, 0xAD); + status = ad9361_reg_write(dev, 0x00F2, 0x01); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x35); + status = ad9361_reg_write(dev, 0x00F1, 0xF8); + status = ad9361_reg_write(dev, 0x00F2, 0x01); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x36); + status = ad9361_reg_write(dev, 0x00F1, 0xA5); + status = ad9361_reg_write(dev, 0x00F2, 0x00); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x37); + status = ad9361_reg_write(dev, 0x00F1, 0x38); + status = ad9361_reg_write(dev, 0x00F2, 0xFF); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x38); + status = ad9361_reg_write(dev, 0x00F1, 0xD5); + status = ad9361_reg_write(dev, 0x00F2, 0xFE); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x39); + status = ad9361_reg_write(dev, 0x00F1, 0x77); + status = ad9361_reg_write(dev, 0x00F2, 0xFF); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x3A); + status = ad9361_reg_write(dev, 0x00F1, 0x4D); + status = ad9361_reg_write(dev, 0x00F2, 0x00); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x3B); + status = ad9361_reg_write(dev, 0x00F1, 0xA3); + status = ad9361_reg_write(dev, 0x00F2, 0x00); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x3C); + status = ad9361_reg_write(dev, 0x00F1, 0x61); + status = ad9361_reg_write(dev, 0x00F2, 0x00); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x3D); + status = ad9361_reg_write(dev, 0x00F1, 0xEA); + status = ad9361_reg_write(dev, 0x00F2, 0xFF); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x3E); + status = ad9361_reg_write(dev, 0x00F1, 0xA5); + status = ad9361_reg_write(dev, 0x00F2, 0xFF); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x3F); + status = ad9361_reg_write(dev, 0x00F1, 0xB6); + status = ad9361_reg_write(dev, 0x00F2, 0xFF); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x40); + status = ad9361_reg_write(dev, 0x00F1, 0x00); + status = ad9361_reg_write(dev, 0x00F2, 0x00); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x41); + status = ad9361_reg_write(dev, 0x00F1, 0x00); + status = ad9361_reg_write(dev, 0x00F2, 0x00); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x42); + status = ad9361_reg_write(dev, 0x00F1, 0x00); + status = ad9361_reg_write(dev, 0x00F2, 0x00); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x43); + status = ad9361_reg_write(dev, 0x00F1, 0x00); + status = ad9361_reg_write(dev, 0x00F2, 0x00); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x44); + status = ad9361_reg_write(dev, 0x00F1, 0x00); + status = ad9361_reg_write(dev, 0x00F2, 0x00); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x45); + status = ad9361_reg_write(dev, 0x00F1, 0x00); + status = ad9361_reg_write(dev, 0x00F2, 0x00); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x46); + status = ad9361_reg_write(dev, 0x00F1, 0x00); + status = ad9361_reg_write(dev, 0x00F2, 0x00); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x47); + status = ad9361_reg_write(dev, 0x00F1, 0x00); + status = ad9361_reg_write(dev, 0x00F2, 0x00); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x48); + status = ad9361_reg_write(dev, 0x00F1, 0x00); + status = ad9361_reg_write(dev, 0x00F2, 0x00); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x49); + status = ad9361_reg_write(dev, 0x00F1, 0x00); + status = ad9361_reg_write(dev, 0x00F2, 0x00); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x4A); + status = ad9361_reg_write(dev, 0x00F1, 0x00); + status = ad9361_reg_write(dev, 0x00F2, 0x00); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x4B); + status = ad9361_reg_write(dev, 0x00F1, 0x00); + status = ad9361_reg_write(dev, 0x00F2, 0x00); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x4C); + status = ad9361_reg_write(dev, 0x00F1, 0x00); + status = ad9361_reg_write(dev, 0x00F2, 0x00); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x4D); + status = ad9361_reg_write(dev, 0x00F1, 0x00); + status = ad9361_reg_write(dev, 0x00F2, 0x00); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x4E); + status = ad9361_reg_write(dev, 0x00F1, 0x00); + status = ad9361_reg_write(dev, 0x00F2, 0x00); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F0, 0x4F); + status = ad9361_reg_write(dev, 0x00F1, 0x00); + status = ad9361_reg_write(dev, 0x00F2, 0x00); + status = ad9361_reg_write(dev, 0x00F5, 0x9E); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F4, 0x00); + status = ad9361_reg_write(dev, 0x00F5, 0x98); // Disable clock to Rx Filter + +//************************************************************ +// Setup the Parallel Port (Digital Data Interface) +//************************************************************ + status = ad9361_reg_write(dev, 0x0010, 0xC8); // I/O Config. Tx Swap IQ; Rx Swap IQ; Tx CH Swap, Rx CH Swap; Rx Frame Mode; 2R2T bit; Invert data bus; Invert DATA_CLK + status = ad9361_reg_write(dev, 0x0011, 0x00); // I/O Config. Alt Word Order; -Rx1; -Rx2; -Tx1; -Tx2; Invert Rx Frame; Delay Rx Data + status = ad9361_reg_write(dev, 0x0012, 0x02); // I/O Config. Rx=2*Tx; Swap Ports; SDR; LVDS; Half Duplex; Single Port; Full Port; Swap Bits + status = ad9361_reg_write(dev, 0x0006, 0x00); // PPORT Rx Delay (adjusts Tco Dataclk->Data) + status = ad9361_reg_write(dev, 0x0007, 0x0F); // PPORT TX Delay (adjusts setup/hold FBCLK->Data) + +//************************************************************ +// Setup AuxDAC +//************************************************************ + status = ad9361_reg_write(dev, 0x0018, 0x00); // AuxDAC1 Word[9:2] + status = ad9361_reg_write(dev, 0x0019, 0x00); // AuxDAC2 Word[9:2] + status = ad9361_reg_write(dev, 0x001A, 0x00); // AuxDAC1 Config and Word[1:0] + status = ad9361_reg_write(dev, 0x001B, 0x00); // AuxDAC2 Config and Word[1:0] + status = ad9361_reg_write(dev, 0x0023, 0xFF); // AuxDAC Manaul/Auto Control + status = ad9361_reg_write(dev, 0x0026, 0x00); // AuxDAC Manual Select Bit/GPO Manual Select + status = ad9361_reg_write(dev, 0x0030, 0x00); // AuxDAC1 Rx Delay + status = ad9361_reg_write(dev, 0x0031, 0x00); // AuxDAC1 Tx Delay + status = ad9361_reg_write(dev, 0x0032, 0x00); // AuxDAC2 Rx Delay + status = ad9361_reg_write(dev, 0x0033, 0x00); // AuxDAC2 Tx Delay + +//************************************************************ +// Setup AuxADC +//************************************************************ + status = ad9361_reg_write(dev, 0x000B, 0x00); // Temp Sensor Setup (Offset) + status = ad9361_reg_write(dev, 0x000C, 0x00); // Temp Sensor Setup (Temp Window) + status = ad9361_reg_write(dev, 0x000D, 0x03); // Temp Sensor Setup (Periodic Measure) + status = ad9361_reg_write(dev, 0x000F, 0x04); // Temp Sensor Setup (Decimation) + status = ad9361_reg_write(dev, 0x001C, 0x00); // AuxADC Setup (Clock Div) + status = ad9361_reg_write(dev, 0x001D, 0x01); // AuxADC Setup (Decimation/Enable) + +//************************************************************ +// Setup Control Outs +//************************************************************ + status = ad9361_reg_write(dev, 0x0035, 0x00); // Ctrl Out index + status = ad9361_reg_write(dev, 0x0036, 0xFF); // Ctrl Out [7:0] output enable + +//************************************************************ +// Setup GPO +//************************************************************ + status = ad9361_reg_write(dev, 0x003A, 0x18); // Set number of REFCLK cycles for 1us delay timer + status = ad9361_reg_write(dev, 0x0020, 0x00); // GPO Auto Enable Setup in RX and TX + status = ad9361_reg_write(dev, 0x0027, 0x00); // GPO Manual and GPO auto value in ALERT + status = ad9361_reg_write(dev, 0x0028, 0x00); // GPO_0 RX Delay + status = ad9361_reg_write(dev, 0x0029, 0x00); // GPO_1 RX Delay + status = ad9361_reg_write(dev, 0x002A, 0x00); // GPO_2 RX Delay + status = ad9361_reg_write(dev, 0x002B, 0x00); // GPO_3 RX Delay + status = ad9361_reg_write(dev, 0x002C, 0x00); // GPO_0 TX Delay + status = ad9361_reg_write(dev, 0x002D, 0x00); // GPO_1 TX Delay + status = ad9361_reg_write(dev, 0x002E, 0x00); // GPO_2 TX Delay + status = ad9361_reg_write(dev, 0x002F, 0x00); // GPO_3 TX Delay + +//************************************************************ +// Setup RF PLL non-frequency-dependent registers +//************************************************************ + status = ad9361_reg_write(dev, 0x0261, 0x00); // Set Rx LO Power mode + status = ad9361_reg_write(dev, 0x02A1, 0x00); // Set Tx LO Power mode + status = ad9361_reg_write(dev, 0x0248, 0x0B); // Enable Rx VCO LDO + status = ad9361_reg_write(dev, 0x0288, 0x0B); // Enable Tx VCO LDO + status = ad9361_reg_write(dev, 0x0246, 0x02); // Set VCO Power down TCF bits + status = ad9361_reg_write(dev, 0x0286, 0x02); // Set VCO Power down TCF bits + status = ad9361_reg_write(dev, 0x0249, 0x8E); // Set VCO cal length + status = ad9361_reg_write(dev, 0x0289, 0x8E); // Set VCO cal length + status = ad9361_reg_write(dev, 0x023B, 0x80); // Enable Rx VCO cal + status = ad9361_reg_write(dev, 0x027B, 0x80); // Enable Tx VCO cal + status = ad9361_reg_write(dev, 0x0243, 0x0D); // Set Rx prescaler bias + status = ad9361_reg_write(dev, 0x0283, 0x0D); // Set Tx prescaler bias + status = ad9361_reg_write(dev, 0x023D, 0x00); // Clear Half VCO cal clock setting + status = ad9361_reg_write(dev, 0x027D, 0x00); // Clear Half VCO cal clock setting + + status = ad9361_reg_write(dev, 0x0015, 0x0C); // Set Dual Synth mode bit + status = ad9361_reg_write(dev, 0x0014, 0x1D); // Set Force ALERT State bit + status = ad9361_reg_write(dev, 0x0013, 0x01); // Set ENSM FDD mode + mdelay(1); // waits 1 ms + + status = ad9361_reg_write(dev, 0x023D, 0x04); // Start RX CP cal + + // Wait for CP cal to complete, Max RXCP Cal time: 737.280 (us)(Done when 0x244[7]==1) + time_out_cnt = 0; + do{ + status |= ad9361_reg_read(dev, 0x0244, &value); + mdelay(1);// Wait 1ms + time_out_cnt++; + } while((value & 0x80) != 0x80 && time_out_cnt <= AD9361_TIMEOUT_TH); + + status = ad9361_reg_write(dev, 0x027D, 0x04); // Start TX CP cal + + // Wait for CP cal to complete, Max TXCP Cal time: 737.280 (us)(Done when 0x284[7]==1) + time_out_cnt = 0; + do{ + status |= ad9361_reg_read(dev, 0x0284, &value); + mdelay(1);// Wait 1ms + time_out_cnt++; + } while((value & 0x80) != 0x80 && time_out_cnt <= AD9361_TIMEOUT_TH); + + status = ad9361_reg_write(dev, 0x023D, 0x00); // Disable RX CP Calibration since the CP Cal start bit is not self-clearing. Only important if the script is run again without restting the DUT + status = ad9361_reg_write(dev, 0x027D, 0x00); // Disable TX CP Calibration since the CP Cal start bit is not self-clearing. Only important if the script is run again without restting the DUT +//************************************************************ +// FDD RX,TX Synth Frequency: 125.000000,4125.000000 MHz +//************************************************************ +//************************************************************ +// Setup Rx Frequency-Dependent Syntheisizer Registers +//************************************************************ + status = ad9361_reg_write(dev, 0x023A, 0x4A); // Set VCO Output level[3:0] + status = ad9361_reg_write(dev, 0x0239, 0xC1); // Set Init ALC Value[3:0] and VCO Varactor[3:0] + status = ad9361_reg_write(dev, 0x0242, 0x0E); // Set VCO Bias Tcf[1:0] and VCO Bias Ref[2:0] + status = ad9361_reg_write(dev, 0x0238, 0x78); // Set VCO Cal Offset[3:0] + status = ad9361_reg_write(dev, 0x0245, 0x00); // Set VCO Cal Ref Tcf[2:0] + status = ad9361_reg_write(dev, 0x0251, 0x0B); // Set VCO Varactor Reference[3:0] + status = ad9361_reg_write(dev, 0x0250, 0x70); // Set VCO Varactor Ref Tcf[2:0] and VCO Varactor Offset[3:0] + status = ad9361_reg_write(dev, 0x023B, 0x94); // Set Synth Loop Filter charge pump current (Icp) + status = ad9361_reg_write(dev, 0x023E, 0xF4); // Set Synth Loop Filter C2 and C1 + status = ad9361_reg_write(dev, 0x023F, 0xDF); // Set Synth Loop Filter R1 and C3 + status = ad9361_reg_write(dev, 0x0240, 0x0A); // Set Synth Loop Filter R3 + +//************************************************************ +// Setup Tx Frequency-Dependent Syntheisizer Registers +//************************************************************ + status = ad9361_reg_write(dev, 0x027A, 0x4A); // Set VCO Output level[3:0] + status = ad9361_reg_write(dev, 0x0279, 0xC1); // Set Init ALC Value[3:0] and VCO Varactor[3:0] + status = ad9361_reg_write(dev, 0x0282, 0x0E); // Set VCO Bias Tcf[1:0] and VCO Bias Ref[2:0] + status = ad9361_reg_write(dev, 0x0278, 0x78); // Set VCO Cal Offset[3:0] + status = ad9361_reg_write(dev, 0x0285, 0x00); // Set VCO Cal Ref Tcf[2:0] + status = ad9361_reg_write(dev, 0x0291, 0x0B); // Set VCO Varactor Reference[3:0] + status = ad9361_reg_write(dev, 0x0290, 0x70); // Set VCO Varactor Ref Tcf[2:0] and VCO Varactor Offset[3:0] + status = ad9361_reg_write(dev, 0x027B, 0x93); // Set Synth Loop Filter charge pump current (Icp) + status = ad9361_reg_write(dev, 0x027E, 0xF4); // Set Synth Loop Filter C2 and C1 + status = ad9361_reg_write(dev, 0x027F, 0xDF); // Set Synth Loop Filter R1 and C3 + status = ad9361_reg_write(dev, 0x0280, 0x0A); // Set Synth Loop Filter R3 + +//************************************************************ +// Write Rx and Tx Frequency Words +//************************************************************ + status = ad9361_reg_write(dev, 0x0233, 0x00); // Write Rx Synth Fractional Freq Word[7:0] + status = ad9361_reg_write(dev, 0x0234, 0x00); // Write Rx Synth Fractional Freq Word[15:8] + status = ad9361_reg_write(dev, 0x0235, 0x00); // Write Rx Synth Fractional Freq Word[22:16] + status = ad9361_reg_write(dev, 0x0232, 0x00); // Write Rx Synth Integer Freq Word[10:8] + status = ad9361_reg_write(dev, 0x0231, 0xA0); // Write Rx Synth Integer Freq Word[7:0] + status = ad9361_reg_write(dev, 0x0005, 0x05); // Set LO divider setting + status = ad9361_reg_write(dev, 0x0273, 0x00); // Write Tx Synth Fractional Freq Word[7:0] + status = ad9361_reg_write(dev, 0x0274, 0x00); // Write Tx Synth Fractional Freq Word[15:8] + status = ad9361_reg_write(dev, 0x0275, 0x00); // Write Tx Synth Fractional Freq Word[22:16] + status = ad9361_reg_write(dev, 0x0272, 0x00); // Write Tx Synth Integer Freq Word[10:8] + status = ad9361_reg_write(dev, 0x0271, 0xA5); // Write Tx Synth Integer Freq Word[7:0] (starts VCO cal) + status = ad9361_reg_write(dev, 0x0005, 0x05); // Set LO divider setting + +//************************************************************ +// Program Mixer GM Sub-table +//************************************************************ + status = ad9361_reg_write(dev, 0x013F, 0x02); // Start Clock + status = ad9361_reg_write(dev, 0x0138, 0x0F); // Addr Table Index + status = ad9361_reg_write(dev, 0x0139, 0x78); // Gain + status = ad9361_reg_write(dev, 0x013A, 0x00); // Bias + status = ad9361_reg_write(dev, 0x013B, 0x00); // GM + status = ad9361_reg_write(dev, 0x013F, 0x06); // Write Words + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay for 3 ADCCLK/16 clock cycles (Dummy Write) + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay ~1us (Dummy Write) + status = ad9361_reg_write(dev, 0x0138, 0x0E); // Addr Table Index + status = ad9361_reg_write(dev, 0x0139, 0x74); // Gain + status = ad9361_reg_write(dev, 0x013A, 0x00); // Bias + status = ad9361_reg_write(dev, 0x013B, 0x0D); // GM + status = ad9361_reg_write(dev, 0x013F, 0x06); // Write Words + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay for 3 ADCCLK/16 clock cycles (Dummy Write) + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay ~1us (Dummy Write) + status = ad9361_reg_write(dev, 0x0138, 0x0D); // Addr Table Index + status = ad9361_reg_write(dev, 0x0139, 0x70); // Gain + status = ad9361_reg_write(dev, 0x013A, 0x00); // Bias + status = ad9361_reg_write(dev, 0x013B, 0x15); // GM + status = ad9361_reg_write(dev, 0x013F, 0x06); // Write Words + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay for 3 ADCCLK/16 clock cycles (Dummy Write) + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay ~1us (Dummy Write) + status = ad9361_reg_write(dev, 0x0138, 0x0C); // Addr Table Index + status = ad9361_reg_write(dev, 0x0139, 0x6C); // Gain + status = ad9361_reg_write(dev, 0x013A, 0x00); // Bias + status = ad9361_reg_write(dev, 0x013B, 0x1B); // GM + status = ad9361_reg_write(dev, 0x013F, 0x06); // Write Words + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay for 3 ADCCLK/16 clock cycles (Dummy Write) + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay ~1us (Dummy Write) + status = ad9361_reg_write(dev, 0x0138, 0x0B); // Addr Table Index + status = ad9361_reg_write(dev, 0x0139, 0x68); // Gain + status = ad9361_reg_write(dev, 0x013A, 0x00); // Bias + status = ad9361_reg_write(dev, 0x013B, 0x21); // GM + status = ad9361_reg_write(dev, 0x013F, 0x06); // Write Words + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay for 3 ADCCLK/16 clock cycles (Dummy Write) + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay ~1us (Dummy Write) + status = ad9361_reg_write(dev, 0x0138, 0x0A); // Addr Table Index + status = ad9361_reg_write(dev, 0x0139, 0x64); // Gain + status = ad9361_reg_write(dev, 0x013A, 0x00); // Bias + status = ad9361_reg_write(dev, 0x013B, 0x25); // GM + status = ad9361_reg_write(dev, 0x013F, 0x06); // Write Words + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay for 3 ADCCLK/16 clock cycles (Dummy Write) + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay ~1us (Dummy Write) + status = ad9361_reg_write(dev, 0x0138, 0x09); // Addr Table Index + status = ad9361_reg_write(dev, 0x0139, 0x60); // Gain + status = ad9361_reg_write(dev, 0x013A, 0x00); // Bias + status = ad9361_reg_write(dev, 0x013B, 0x29); // GM + status = ad9361_reg_write(dev, 0x013F, 0x06); // Write Words + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay for 3 ADCCLK/16 clock cycles (Dummy Write) + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay ~1us (Dummy Write) + status = ad9361_reg_write(dev, 0x0138, 0x08); // Addr Table Index + status = ad9361_reg_write(dev, 0x0139, 0x5C); // Gain + status = ad9361_reg_write(dev, 0x013A, 0x00); // Bias + status = ad9361_reg_write(dev, 0x013B, 0x2C); // GM + status = ad9361_reg_write(dev, 0x013F, 0x06); // Write Words + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay for 3 ADCCLK/16 clock cycles (Dummy Write) + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay ~1us (Dummy Write) + status = ad9361_reg_write(dev, 0x0138, 0x07); // Addr Table Index + status = ad9361_reg_write(dev, 0x0139, 0x58); // Gain + status = ad9361_reg_write(dev, 0x013A, 0x00); // Bias + status = ad9361_reg_write(dev, 0x013B, 0x2F); // GM + status = ad9361_reg_write(dev, 0x013F, 0x06); // Write Words + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay for 3 ADCCLK/16 clock cycles (Dummy Write) + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay ~1us (Dummy Write) + status = ad9361_reg_write(dev, 0x0138, 0x06); // Addr Table Index + status = ad9361_reg_write(dev, 0x0139, 0x54); // Gain + status = ad9361_reg_write(dev, 0x013A, 0x00); // Bias + status = ad9361_reg_write(dev, 0x013B, 0x31); // GM + status = ad9361_reg_write(dev, 0x013F, 0x06); // Write Words + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay for 3 ADCCLK/16 clock cycles (Dummy Write) + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay ~1us (Dummy Write) + status = ad9361_reg_write(dev, 0x0138, 0x05); // Addr Table Index + status = ad9361_reg_write(dev, 0x0139, 0x50); // Gain + status = ad9361_reg_write(dev, 0x013A, 0x00); // Bias + status = ad9361_reg_write(dev, 0x013B, 0x33); // GM + status = ad9361_reg_write(dev, 0x013F, 0x06); // Write Words + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay for 3 ADCCLK/16 clock cycles (Dummy Write) + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay ~1us (Dummy Write) + status = ad9361_reg_write(dev, 0x0138, 0x04); // Addr Table Index + status = ad9361_reg_write(dev, 0x0139, 0x4C); // Gain + status = ad9361_reg_write(dev, 0x013A, 0x00); // Bias + status = ad9361_reg_write(dev, 0x013B, 0x34); // GM + status = ad9361_reg_write(dev, 0x013F, 0x06); // Write Words + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay for 3 ADCCLK/16 clock cycles (Dummy Write) + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay ~1us (Dummy Write) + status = ad9361_reg_write(dev, 0x0138, 0x03); // Addr Table Index + status = ad9361_reg_write(dev, 0x0139, 0x48); // Gain + status = ad9361_reg_write(dev, 0x013A, 0x00); // Bias + status = ad9361_reg_write(dev, 0x013B, 0x35); // GM + status = ad9361_reg_write(dev, 0x013F, 0x06); // Write Words + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay for 3 ADCCLK/16 clock cycles (Dummy Write) + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay ~1us (Dummy Write) + status = ad9361_reg_write(dev, 0x0138, 0x02); // Addr Table Index + status = ad9361_reg_write(dev, 0x0139, 0x30); // Gain + status = ad9361_reg_write(dev, 0x013A, 0x00); // Bias + status = ad9361_reg_write(dev, 0x013B, 0x3A); // GM + status = ad9361_reg_write(dev, 0x013F, 0x06); // Write Words + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay for 3 ADCCLK/16 clock cycles (Dummy Write) + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay ~1us (Dummy Write) + status = ad9361_reg_write(dev, 0x0138, 0x01); // Addr Table Index + status = ad9361_reg_write(dev, 0x0139, 0x18); // Gain + status = ad9361_reg_write(dev, 0x013A, 0x00); // Bias + status = ad9361_reg_write(dev, 0x013B, 0x3D); // GM + status = ad9361_reg_write(dev, 0x013F, 0x06); // Write Words + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay for 3 ADCCLK/16 clock cycles (Dummy Write) + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay ~1us (Dummy Write) + status = ad9361_reg_write(dev, 0x0138, 0x00); // Addr Table Index + status = ad9361_reg_write(dev, 0x0139, 0x00); // Gain + status = ad9361_reg_write(dev, 0x013A, 0x00); // Bias + status = ad9361_reg_write(dev, 0x013B, 0x3E); // GM + status = ad9361_reg_write(dev, 0x013F, 0x06); // Write Words + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay for 3 ADCCLK/16 clock cycles (Dummy Write) + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay ~1us (Dummy Write) + status = ad9361_reg_write(dev, 0x013F, 0x02); // Clear Write Bit + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay for 3 ADCCLK/16 clock cycles (Dummy Write) + status = ad9361_reg_write(dev, 0x013C, 0x00); // Delay ~1us (Dummy Write) + status = ad9361_reg_write(dev, 0x013F, 0x00); // Stop Clock + +//************************************************************ +// Program Rx Gain Tables with GainTable800MHz.csv +//************************************************************ + + status = ad9361_reg_write(dev, 0x0137, 0x1A); // Start Gain Table Clock + status = ad9361_reg_write(dev, 0x0130, 0x00); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x00); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x00); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x20); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x01); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x00); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x00); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x02); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x00); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x00); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x03); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x00); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x01); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x04); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x00); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x02); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x05); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x00); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x03); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x06); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x00); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x04); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x07); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x00); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x05); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x08); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x01); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x03); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x20); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x09); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x01); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x04); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x0A); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x01); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x05); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x0B); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x01); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x06); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x0C); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x01); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x07); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x0D); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x01); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x08); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x0E); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x01); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x09); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x0F); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x01); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x0A); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x10); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x01); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x0B); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x11); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x01); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x0C); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x12); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x01); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x0D); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x13); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x01); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x0E); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x14); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x02); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x09); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x20); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x15); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x02); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x0A); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x16); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x02); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x0B); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x17); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x02); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x0C); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x18); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x02); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x0D); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x19); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x02); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x0E); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x1A); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x02); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x0F); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x1B); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x02); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x10); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x1C); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x02); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x2B); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x20); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x1D); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x02); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x2C); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x1E); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x04); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x28); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x20); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x1F); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x04); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x29); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x20); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x04); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x2A); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x21); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x04); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x2B); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x22); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x24); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x20); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x20); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x23); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x24); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x21); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x24); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x44); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x20); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x20); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x25); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x44); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x21); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x26); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x44); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x22); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x27); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x44); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x23); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x28); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x44); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x24); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x29); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x44); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x25); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x2A); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x44); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x26); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x2B); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x44); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x27); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x2C); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x44); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x28); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x2D); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x44); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x29); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x2E); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x44); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x2A); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x2F); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x44); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x2B); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x30); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x44); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x2C); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x31); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x44); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x2D); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x32); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x44); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x2E); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x33); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x44); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x2F); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x34); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x44); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x30); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x35); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x44); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x31); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x36); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x44); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x32); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x37); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x64); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x2E); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x20); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x38); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x64); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x2F); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x39); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x64); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x30); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x3A); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x64); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x31); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x3B); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x64); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x32); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x3C); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x64); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x33); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x3D); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x64); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x34); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x3E); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x64); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x35); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x3F); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x64); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x36); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x40); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x64); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x37); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x41); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x64); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x38); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x42); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x65); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x38); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x20); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x43); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x66); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x38); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x20); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x44); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x67); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x38); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x20); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x45); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x68); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x38); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x20); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x46); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x69); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x38); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x20); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x47); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x6A); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x38); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x20); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x48); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x6B); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x38); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x20); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x49); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x6C); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x38); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x20); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x4A); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x6D); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x38); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x20); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x4B); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x6E); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x38); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x20); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x4C); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x6F); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x38); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x20); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x4D); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x00); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x00); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x4E); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x00); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x00); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x4F); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x00); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x00); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x50); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x00); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x00); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x51); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x00); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x00); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x52); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x00); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x00); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x53); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x00); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x00); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x54); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x00); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x00); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x55); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x00); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x00); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x56); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x00); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x00); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x57); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x00); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x00); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x58); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x00); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x00); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x59); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x00); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x00); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0130, 0x5A); // Gain Table Index + status = ad9361_reg_write(dev, 0x0131, 0x00); // Ext LNA, Int LNA, & Mixer Gain Word + status = ad9361_reg_write(dev, 0x0132, 0x00); // TIA & LPF Word + status = ad9361_reg_write(dev, 0x0133, 0x00); // DC Cal bit & Dig Gain Word + status = ad9361_reg_write(dev, 0x0137, 0x1E); // Write Words + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay 3 ADCCLK/16 cycles + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0137, 0x1A); // Clear Write Bit + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0134, 0x00); // Dummy Write to delay ~1us + status = ad9361_reg_write(dev, 0x0137, 0x00); // Stop Gain Table Clock +//************************************************************ +// Setup Rx Manual Gain Registers +//************************************************************ + status = ad9361_reg_write(dev, 0x00FA, 0xE0); // Gain Control Mode Select + status = ad9361_reg_write(dev, 0x00FB, 0x08); // Table, Digital Gain, Man Gain Ctrl + status = ad9361_reg_write(dev, 0x00FC, 0x23); // Incr Step Size, ADC Overrange Size + status = ad9361_reg_write(dev, 0x00FD, 0x4C); // Max Full/LMT Gain Table Index + status = ad9361_reg_write(dev, 0x00FE, 0x44); // Decr Step Size, Peak Overload Time + status = ad9361_reg_write(dev, 0x0100, 0x6F); // Max Digital Gain + status = ad9361_reg_write(dev, 0x0104, 0x2F); // ADC Small Overload Threshold + status = ad9361_reg_write(dev, 0x0105, 0x3A); // ADC Large Overload Threshold + status = ad9361_reg_write(dev, 0x0107, 0x2B); // Small LMT Overload Threshold + status = ad9361_reg_write(dev, 0x0108, 0x31); // Large LMT Overload Threshold + status = ad9361_reg_write(dev, 0x0109, 0x00); // Rx1 Full/LMT Gain Index + status = ad9361_reg_write(dev, 0x010A, 0x58); // Rx1 LPF Gain Index + status = ad9361_reg_write(dev, 0x010B, 0x00); // Rx1 Digital Gain Index + status = ad9361_reg_write(dev, 0x010C, 0x00); // Rx2 Full/LMT Gain Index + status = ad9361_reg_write(dev, 0x010D, 0x18); // Rx2 LPF Gain Index + status = ad9361_reg_write(dev, 0x010E, 0x00); // Rx2 Digital Gain Index + status = ad9361_reg_write(dev, 0x0114, 0x30); // Low Power Threshold + status = ad9361_reg_write(dev, 0x011A, 0x27); // Initial LMT Gain Limit + status = ad9361_reg_write(dev, 0x0081, 0x00); // Tx Symbol Gain Control +//************************************************************ +// RX Baseband Filter Tuning (Real BW: 7.500000 MHz) 3dB Filter +// Corner @ 10.500000 MHz) +//************************************************************ + status = ad9361_reg_write(dev, 0x01FB, 0x07); // RX Freq Corner (MHz) + status = ad9361_reg_write(dev, 0x01FC, 0x40); // RX Freq Corner (Khz) + status = ad9361_reg_write(dev, 0x01F8, 0x0D); // Rx BBF Tune Divider[7:0] + status = ad9361_reg_write(dev, 0x01F9, 0x1E); // RX BBF Tune Divider[8] + + status = ad9361_reg_write(dev, 0x01D5, 0x3F); // Set Rx Mix LO CM + status = ad9361_reg_write(dev, 0x01C0, 0x03); // Set GM common mode + status = ad9361_reg_write(dev, 0x01E2, 0x02); // Enable Rx1 Filter Tuner + status = ad9361_reg_write(dev, 0x01E3, 0x02); // Enable Rx2 Filter Tuner + status = ad9361_reg_write(dev, 0x0016, 0x80); // Start RX Filter Tune + + // Wait for RX filter to tune, Max Cal Time: 6.884 us (Done when 0x016[7]==0) + time_out_cnt = 0; + do{ + status |= ad9361_reg_read(dev, 0x0016, &value); + mdelay(1);// Wait 1ms + time_out_cnt++; + } while((value & 0x80) != 0x00 && time_out_cnt <= AD9361_TIMEOUT_TH); + + status = ad9361_reg_write(dev, 0x01E2, 0x03); // Disable Rx Filter Tuner (Rx1) + status = ad9361_reg_write(dev, 0x01E3, 0x03); // Disable Rx Filter Tuner (Rx2) +//************************************************************ +// TX Baseband Filter Tuning (Real BW: 7.500000 MHz) 3dB Filter +// Corner @ 12.000000 MHz) +//************************************************************ + status = ad9361_reg_write(dev, 0x00D6, 0x0B); // TX BBF Tune Divider[7:0] + status = ad9361_reg_write(dev, 0x00D7, 0x1E); // TX BBF Tune Divider[8] + + status = ad9361_reg_write(dev, 0x00CA, 0x22); // Enable Tx Filter Tuner + status = ad9361_reg_write(dev, 0x0016, 0x40); // Start Tx Filter Tune + + // Wait for TX filter to tune, Max Cal Time: 3.390 us (Done when 0x016[6]==0) + time_out_cnt = 0; + do{ + status |= ad9361_reg_read(dev, 0x0016, &value); + mdelay(1);// Wait 1ms + time_out_cnt++; + } while((value & 0x40) != 0x00 && time_out_cnt <= AD9361_TIMEOUT_TH); + + status = ad9361_reg_write(dev, 0x00CA, 0x26); // Disable Tx Filter Tuner (Both Channels) +//************************************************************ +// RX TIA Setup: Setup values scale based on RxBBF calibration +// results. See information in Calibration Guide. +//************************************************************ + status = ad9361_reg_write(dev, 0x01DB, 0x60); // Set TIA selcc[2:0] + status = ad9361_reg_write(dev, 0x01DD, 0x00); // Set RX TIA1 C MSB[6:0] + status = ad9361_reg_write(dev, 0x01DF, 0x00); // Set RX TIA2 C MSB[6:0] + status = ad9361_reg_write(dev, 0x01DC, 0x72); // Set RX TIA1 C LSB[5:0] + status = ad9361_reg_write(dev, 0x01DE, 0x72); // Set RX TIA2 C LSB[5:0] + +//************************************************************ +// TX Secondary Filter Calibration Setup: Real Bandwidth +// 7.500000MHz, 3dB Corner @ 37.500000MHz +//************************************************************ + status = ad9361_reg_write(dev, 0x00D2, 0x1E); // TX Secondary Filter PDF Cap cal[5:0] + status = ad9361_reg_write(dev, 0x00D1, 0x0C); // TX Secondary Filter PDF Res cal[3:0] + status = ad9361_reg_write(dev, 0x00D0, 0x56); // Pdampbias + +//************************************************************ +// ADC Setup: Tune ADC Performance based on RX analog filter tune +// corner. Real Bandwidth: 6.982753 MHz, ADC Clock Frequency: +// 288.000000 MHz. The values in registers 0x200 - 0x227 need to be +// calculated using the equations in the Calibration Guide. +//************************************************************ + + status = ad9361_reg_write(dev, 0x0200, 0x00); + status = ad9361_reg_write(dev, 0x0201, 0x00); + status = ad9361_reg_write(dev, 0x0202, 0x00); + status = ad9361_reg_write(dev, 0x0203, 0x24); + status = ad9361_reg_write(dev, 0x0204, 0x24); + status = ad9361_reg_write(dev, 0x0205, 0x00); + status = ad9361_reg_write(dev, 0x0206, 0x00); + status = ad9361_reg_write(dev, 0x0207, 0x7C); + status = ad9361_reg_write(dev, 0x0208, 0x46); + status = ad9361_reg_write(dev, 0x0209, 0x3C); + status = ad9361_reg_write(dev, 0x020A, 0x4C); + status = ad9361_reg_write(dev, 0x020B, 0x2C); + status = ad9361_reg_write(dev, 0x020C, 0x4F); + status = ad9361_reg_write(dev, 0x020D, 0x2A); + status = ad9361_reg_write(dev, 0x020E, 0x00); + status = ad9361_reg_write(dev, 0x020F, 0x7F); + status = ad9361_reg_write(dev, 0x0210, 0x7F); + status = ad9361_reg_write(dev, 0x0211, 0x7F); + status = ad9361_reg_write(dev, 0x0212, 0x4A); + status = ad9361_reg_write(dev, 0x0213, 0x4A); + status = ad9361_reg_write(dev, 0x0214, 0x4A); + status = ad9361_reg_write(dev, 0x0215, 0x4D); + status = ad9361_reg_write(dev, 0x0216, 0x4D); + status = ad9361_reg_write(dev, 0x0217, 0x4D); + status = ad9361_reg_write(dev, 0x0218, 0x2E); + status = ad9361_reg_write(dev, 0x0219, 0x9C); + status = ad9361_reg_write(dev, 0x021A, 0x1F); + status = ad9361_reg_write(dev, 0x021B, 0x15); + status = ad9361_reg_write(dev, 0x021C, 0x9C); + status = ad9361_reg_write(dev, 0x021D, 0x1F); + status = ad9361_reg_write(dev, 0x021E, 0x15); + status = ad9361_reg_write(dev, 0x021F, 0x9C); + status = ad9361_reg_write(dev, 0x0220, 0x1F); + status = ad9361_reg_write(dev, 0x0221, 0x2A); + status = ad9361_reg_write(dev, 0x0222, 0x2A); + status = ad9361_reg_write(dev, 0x0223, 0x40); + status = ad9361_reg_write(dev, 0x0224, 0x40); + status = ad9361_reg_write(dev, 0x0225, 0x2C); + status = ad9361_reg_write(dev, 0x0226, 0x00); + status = ad9361_reg_write(dev, 0x0227, 0x00); +//************************************************************ +// Setup and Run BB DC and RF DC Offset Calibrations +//************************************************************ + status = ad9361_reg_write(dev, 0x0193, 0x3F); + status = ad9361_reg_write(dev, 0x0190, 0x0F); // Set BBDC tracking shift M value, only applies when BB DC tracking enabled + status = ad9361_reg_write(dev, 0x0194, 0x01); // BBDC Cal setting + status = ad9361_reg_write(dev, 0x0016, 0x01); // Start BBDC offset cal + + // BBDC Max Cal Time: 8416.667 us. Cal done when 0x016[0]==0 + time_out_cnt = 0; + do{ + status |= ad9361_reg_read(dev, 0x0016, &value); + mdelay(1);// Wait 1ms + time_out_cnt++; + } while((value & 0x01) != 0x00 && time_out_cnt <= AD9361_TIMEOUT_TH); + + status = ad9361_reg_write(dev, 0x0185, 0x20); // Set RF DC offset Wait Count + status = ad9361_reg_write(dev, 0x0186, 0x32); // Set RF DC Offset Count[7:0] + status = ad9361_reg_write(dev, 0x0187, 0x24); // Settings for RF DC cal + status = ad9361_reg_write(dev, 0x018B, 0x83); // Settings for RF DC cal + status = ad9361_reg_write(dev, 0x0188, 0x05); // Settings for RF DC cal + status = ad9361_reg_write(dev, 0x0189, 0x30); // Settings for RF DC cal + status = ad9361_reg_write(dev, 0x0016, 0x02); // Start RFDC offset cal + + // RFDC Max Cal Time: 114511.250 us + time_out_cnt = 0; + do{ + status |= ad9361_reg_read(dev, 0x0016, &value); + mdelay(1);// Wait 1ms + time_out_cnt++; + } while((value & 0x01) != 0x00 && time_out_cnt <= AD9361_TIMEOUT_TH); + +//************************************************************ +// Tx Quadrature Calibration Settings +//************************************************************ + status = ad9361_reg_write(dev, 0x00A0, 0x15); // Set TxQuadcal NCO frequency + status = ad9361_reg_write(dev, 0x00A3, 0x00); // Set TxQuadcal NCO frequency (Only update bits [7:6]) + status = ad9361_reg_write(dev, 0x00A1, 0x7B); // Tx Quad Cal Configuration, Phase and Gain Cal Enable + status = ad9361_reg_write(dev, 0x00A9, 0xFF); // Set Tx Quad Cal Count + status = ad9361_reg_write(dev, 0x00A2, 0x7F); // Set Tx Quad Cal Kexp + status = ad9361_reg_write(dev, 0x00A5, 0x01); // Set Tx Quad Cal Magnitude Threshhold + status = ad9361_reg_write(dev, 0x00A6, 0x01); // Set Tx Quad Cal Magnitude Threshhold + status = ad9361_reg_write(dev, 0x00AA, 0x28); // Set Tx Quad Cal Gain Table index + status = ad9361_reg_write(dev, 0x00A4, 0xF0); // Set Tx Quad Cal Settle Count + status = ad9361_reg_write(dev, 0x00AE, 0x04); // Set Tx Quad Cal LPF Gain index incase Split table mode used + + status = ad9361_reg_write(dev, 0x0169, 0xC0); // Disable Rx Quadrature Calibration before Running Tx Quadrature Calibration + status = ad9361_reg_write(dev, 0x0016, 0x10); // Start Tx Quad cal + + // Wait for cal to complete (Done when 0x016[4]==0) + time_out_cnt = 0; + do{ + status |= ad9361_reg_read(dev, 0x0016, &value); + mdelay(1);// Wait 1ms + time_out_cnt++; + } while((value & 0x10) != 0x00 && time_out_cnt <= AD9361_TIMEOUT_TH); + + status = ad9361_reg_write(dev, 0x016A, 0x75); // Set Kexp Phase + status = ad9361_reg_write(dev, 0x016B, 0x95); // Set Kexp Amplitude & Prevent Positive Gain Bit + status = ad9361_reg_write(dev, 0x0169, 0xCF); // Enable Rx Quadrature Calibration Tracking + status = ad9361_reg_write(dev, 0x018B, 0xAD); // Enable BB and RF DC Tracking Calibrations + status = ad9361_reg_write(dev, 0x0012, 0x02); // Cals done, Set PPORT Config + status = ad9361_reg_write(dev, 0x0013, 0x01); // Set ENSM FDD/TDD bit + status = ad9361_reg_write(dev, 0x0015, 0x0C); // Set Dual Synth Mode, FDD External Control bits properly + +//************************************************************ +// Set Tx Attenuation: Tx1: 0.00 dB, Tx2: 0.00 dB +//************************************************************ + status = ad9361_reg_write(dev, 0x0073, 0x00); + status = ad9361_reg_write(dev, 0x0074, 0x00); + status = ad9361_reg_write(dev, 0x0075, 0x00); + status = ad9361_reg_write(dev, 0x0076, 0x00); +//************************************************************ +// Setup RSSI and Power Measurement Duration Registers +//************************************************************ + status = ad9361_reg_write(dev, 0x0150, 0x0E); // RSSI Measurement Duration 0, 1 + status = ad9361_reg_write(dev, 0x0151, 0x00); // RSSI Measurement Duration 2, 3 + status = ad9361_reg_write(dev, 0x0152, 0xFF); // RSSI Weighted Multiplier 0 + status = ad9361_reg_write(dev, 0x0153, 0x00); // RSSI Weighted Multiplier 1 + status = ad9361_reg_write(dev, 0x0154, 0x00); // RSSI Weighted Multiplier 2 + status = ad9361_reg_write(dev, 0x0155, 0x00); // RSSI Weighted Multiplier 3 + status = ad9361_reg_write(dev, 0x0156, 0x00); // RSSI Delay + status = ad9361_reg_write(dev, 0x0157, 0x00); // RSSI Wait + status = ad9361_reg_write(dev, 0x0158, 0x0D); // RSSI Mode Select + status = ad9361_reg_write(dev, 0x015C, 0x67); // Power Measurement Duration + +//************************************************************ +// Enable External LO & External VCO Buffer +//************************************************************ + status |= ad9361_reg_write(dev, 0x0005, 0x07); + status |= ad9361_reg_write(dev, 0x0057, 0x1C); + + return status; +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/ad9528_profile.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/ad9528_profile.c new file mode 100644 index 0000000..adf8693 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/ad9528_profile.c @@ -0,0 +1,134 @@ +#include "dev_profile.h" +#include "../config/app_config.h" +#include "../drivers/platform/xilinx_gpio.h" +#include "../drivers/axi_core/spi_engine/spi_engine.h" +#include "../drivers/frequency/ad9528/ad9528.h" + +static struct spi_engine_init_param spi_engine_param = { + .ref_clk_hz = AD9528_SPI_ENGINE_REF_CLK, + .type = SPI_ENGINE, + .spi_engine_baseaddr = AD9528_SPI_ENGINE_BASEADDR, + .cs_delay = AD9528_SPI_ENGINE_CS_DELAY, + .data_width = AD9528_SPI_ENGINE_DATA_WIDTH +}; + +static struct xil_gpio_init_param xil_gpio_param = { + .type = GPIO_PS, + .device_id = AD9528_GPIO_DEVICE_ID +}; + +static struct spi_init_param ad9528_spi_init = { + .device_id = AD9528_SPI_DEVICE_ID, + .max_speed_hz = AD9528_SPI_ENGINE_MAX_SPEED, + .mode = SPI_MODE_0, + .chip_select = AD9528_SPI_CS, + .platform_ops = &spi_eng_platform_ops, + .extra = &spi_engine_param +}; + +static struct gpio_init_param ad9528_gpio_resetb_init = { + .number = AD9528_RESETB, + .platform_ops = &xil_gpio_ops, + .extra = &xil_gpio_param +}; + +static struct gpio_init_param ad9528_gpio_sysref_req_init = { + .number = AD9528_SYSREF_REQ, + .platform_ops = &xil_gpio_ops, + .extra = &xil_gpio_param +}; + +static ad9528pll1Settings_t clockPll1Settings = { + .refA_Frequency_Hz = 50000000, + .refA_Divider = 1, + .refA_bufferCtrl = SINGLE_ENDED, + .refB_Frequency_Hz = 0, + .refB_Divider = 1, + .refB_bufferCtrl = DISABLED, + .vcxo_Frequency_Hz = 50000000, + .vcxoBufferCtrl = SINGLE_ENDED, + .nDivider = 1 +}; + +static ad9528pll2Settings_t clockPll2Settings = { + .rfDivider = 4, + .n2Divider = 20, + .totalNdiv = 80 +}; + +/******************************************************************* + * Output Distribution Settings + * 9369CE02A uses the following clock outputs + * OUT 1: FPGA REFCLK + * OUT 3: FPGA SYSREF + * OUT 12: DUT SYSREF + * OUT 13: DUT REFCLK + ******************************************************************* + */ +static ad9528outputSettings_t clockOutputSettings = { + .outPowerDown = 0xE001, + .outSource = { + AD9528_CHANNEL_DIV, AD9528_CHANNEL_DIV, AD9528_CHANNEL_DIV, AD9528_CHANNEL_DIV, AD9528_CHANNEL_DIV, + AD9528_CHANNEL_DIV, AD9528_CHANNEL_DIV, AD9528_CHANNEL_DIV, AD9528_CHANNEL_DIV, AD9528_CHANNEL_DIV, + AD9528_CHANNEL_DIV, AD9528_CHANNEL_DIV, AD9528_CHANNEL_DIV, AD9528_CHANNEL_DIV + }, + .outBufferCtrl = { + 0,0,0,0,0, + 0,0,0,0,0, + 0,0,0,0 + }, + .outAnalogDelay = { + 0,0,0,0,0, + 0,0,0,0,0, + 0,0,0,0 + }, + .outDigitalDelay = { + 0,0,0,0,0, + 0,0,0,0,0, + 0,0,0,0 + }, + .outChannelDiv = { + 5,25,25,5,5, + 5,5,5,5,5, + 5,5,10,5 + }, + .outFrequency_Hz = { + 200e6, 40e6, 40e6, 200e6, 200e6, + 200e6, 200e6, 200e6, 200e6, 200e6, + 200e6, 200e6, 100e6, 200e6 + } +}; + +static ad9528sysrefSettings_t clockSysrefSettings = { + .sysrefRequestMethod = PIN, + .sysrefSource = INTERNAL, + .sysrefPinEdgeMode = LEVEL_ACTIVE_HIGH, + .sysrefPinBufferMode = DISABLED, + .sysrefPatternMode = CONTINUOUS, + .sysrefNshotMode = FOUR_PULSES, + .sysrefDivide = 256 +}; + +static ad9528spiSettings_t clockSpiSettings = { + .chipSelectIndex = AD9528_SPI_CS, //chip select Index, not used + .writeBitPolarity = 0, //Write bit polarity + .longInstructionWord = 1, //16bit instruction word + .MSBFirst = 1, //MSB first + .CPHA = 0, //Clock phase + .CPOL = 0, //Clock polarity + .enSpiStreaming = 0, //uint8_t enSpiStreaming; + .autoIncAddrUp = 1, //uint8_t autoIncAddrUp; + .fourWireMode = 1 //uint8_t fourWireMode; +}; + +ad9528Init_t ad9528_default_init_param = { + /* SPI */ + .spi_init = &ad9528_spi_init, + .gpio_init_resetb = &ad9528_gpio_resetb_init, + .gpio_init_sysref_req = &ad9528_gpio_sysref_req_init, + .spiSettings = &clockSpiSettings, + .pll1Settings = &clockPll1Settings, + .pll2Settings = &clockPll2Settings, + .outputSettings = &clockOutputSettings, + .sysrefSettings = &clockSysrefSettings +}; diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/ad9653_profile.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/ad9653_profile.c new file mode 100644 index 0000000..8f6ba89 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/ad9653_profile.c @@ -0,0 +1,46 @@ +#include "dev_profile.h" +#include "spi.h" +#include "../config/app_config.h" +#include "../drivers/adc/ad9653/ad9653.h" +#include "../drivers/axi_core/spi_engine/spi_engine.h" +#include "../drivers/platform/xilinx_gpio.h" + +static struct spi_engine_init_param spi_engine_param = { + .ref_clk_hz = AD9653_SPI_ENGINE_REF_CLK, + .type = SPI_ENGINE, + .spi_engine_baseaddr = AD9653_SPI_ENGINE_BASEADDR, + .cs_delay = AD9653_SPI_ENGINE_CS_DELAY, + .data_width = AD9653_SPI_ENGINE_DATA_WIDTH +}; + +static struct xil_gpio_init_param xil_gpio_param = { + .type = GPIO_PS, + .device_id = AD9653_GPIO_DEVICE_ID +}; + +static struct spi_init_param ad9653_spi_init = { + .device_id = AD9653_SPI_DEVICE_ID, + .max_speed_hz = AD9653_SPI_ENGINE_MAX_SPEED, + .mode = SPI_MODE_4, + .chip_select = AD9653_SPI_CS, + .platform_ops = &spi_eng_platform_ops, + .extra = &spi_engine_param +}; + +static struct gpio_init_param ad9653_gpio_resetb_init = { + .number = AD9653_POWR_DOWN, + .platform_ops = &xil_gpio_ops, + .extra = &xil_gpio_param +}; + +struct ad9653_init_param ad9653_default_init_param = { + /* SPI */ + .spi_init = &ad9653_spi_init, + .gpio_pdwn = &ad9653_gpio_resetb_init +}; + +const uint32_t ad9653_default_config_len = 36; +const struct ad9653_config ad9653_default_config[] = { + // configuration + +}; diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/ad9694_profile.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/ad9694_profile.c new file mode 100644 index 0000000..cf08bfa --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/ad9694_profile.c @@ -0,0 +1,72 @@ +#include "dev_profile.h" +#include "spi.h" +#include "../config/app_config.h" +#include "../drivers/adc/ad9694/ad9694.h" +#include "../drivers/axi_core/spi_engine/spi_engine.h" +#include "../drivers/platform/xilinx_gpio.h" + +static struct spi_engine_init_param spi_engine_param = { + .ref_clk_hz = AD9694_SPI_ENGINE_REF_CLK, + .type = SPI_ENGINE, + .spi_engine_baseaddr = AD9694_SPI_ENGINE_BASEADDR, + .cs_delay = AD9694_SPI_ENGINE_CS_DELAY, + .data_width = AD9694_SPI_ENGINE_DATA_WIDTH +}; + +//static struct xil_gpio_init_param xil_gpio_param = { +// .type = GPIO_PS, +// .device_id = AD9694_GPIO_DEVICE_ID +//}; + +static struct spi_init_param ad9694_spi_init = { + .device_id = AD9694_SPI_DEVICE_ID, + .max_speed_hz = AD9694_SPI_ENGINE_MAX_SPEED, + .mode = SPI_MODE_4, + .chip_select = AD9694_SPI_CS, + .platform_ops = &spi_eng_platform_ops, + .extra = &spi_engine_param +}; + +struct ad9694_init_param ad9694_default_init_param = { + /* SPI */ + .spi_init = &ad9694_spi_init +}; + +const uint32_t ad9694_default_config_len = 36; +const struct ad9694_config ad9694_default_config[] = { + // reset + {0x0000, 0x81}, + {0x1228, 0x4F}, // JESD204B start-up circuit reset + {0x1228, 0x0F}, // JESD204B start-up normal operation + {0x1222, 0x04}, // PLL calibration + {0x1222, 0x00}, // PLL normal operation + {0x1262, 0x08}, // PLL loss of lock control - Clear loss of lock + {0x1262, 0x00}, // PLL loss of lock control - Normal operation + + // system clock + {0x0108, 0x00}, // Clock divider = 1 -> device clock = 368.64MHz + {0x0201, 0x00}, // Chip decimation ration = 1 -> full sample rate + + // channel configuration + {0x0009, 0x03}, // ADC Pair AB & CD receive all spi command + {0x0571, 0x15}, // JESD204B map JTX Link Control 1 + {0x056E, 0x00}, // JESD204B map PLL control, lane rate 6.75~13.5Gbps, actual = 7.3728Gbps + {0x0570, 0x49}, // JESD204B map JTX quick config: L=2 M=2 F=2 + {0x058B, 0x01}, // JESD204B map JTX SCR L config: scrambler disabled, L=2 + {0x0590, 0x2F}, // JESD204B map JTX Subclass & NP config: subclass 1, N' = 16 + {0x058D, 0x1F}, // JESD204B map JTX K config: K = 32 + {0x058F, 0x2D}, // JESD204B map JTX N config: CS = 0, N = 14 + {0x0571, 0x14}, // JESD204B map JTX Link Control 1 + {0x05B0, 0x00}, // SERDOUTx0, SERDOUTx1 power on + {0x0120, 0x0A}, // SYSREF COntrol: continuous mode, captured on falling edge + + // fast detect control + {0x0040, 0x00}, // FD_B/FD_D, FD_A/FD_C function as Fast Detect B/D, A/C output + {0x0245, 0x01}, // Fine fast detect enabled + {0x0247, 0xFE}, // Channel map fast detect upper threshold LSB + {0x0248, 0x1F}, // Channel map fast detect upper threshold MSB, -> 8190 + {0x0249, 0xA4}, // Channel map fast detect lower threshold LSB + {0x024A, 0x1F}, // Channel map fast detect lower threshold MSB, -> 8100 + {0x024B, 0x80}, // Channel map fast detect dwell time LSB + {0x024C, 0x00} // Channel map fast detect dwell time MSB, -> 128 +}; diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/cx9261a_profile.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/cx9261a_profile.c new file mode 100644 index 0000000..3005573 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/cx9261a_profile.c @@ -0,0 +1,228 @@ +#include "dev_profile.h" +#include "spi.h" +#include "../config/app_config.h" +#include "../drivers/rf-transceiver/cx9261a/cx9261a.h" +#include "../drivers/axi_core/spi_engine/spi_engine.h" +#include "../drivers/platform/xilinx_gpio.h" + +static struct spi_engine_init_param spi_engine_param = { + .ref_clk_hz = CX9261A_SPI_ENGINE_REF_CLK, + .type = SPI_ENGINE, + .spi_engine_baseaddr = CX9261A_SPI_ENGINE_BASEADDR, + .cs_delay = CX9261A_SPI_ENGINE_CS_DELAY, + .data_width = CX9261A_SPI_ENGINE_DATA_WIDTH +}; + +static struct xil_gpio_init_param xil_gpio_param = { + .type = GPIO_PS, + .device_id = CX9261A_GPIO_DEVICE_ID +}; + +static struct spi_init_param cx9261a_spi_init = { + .device_id = CX9261A_SPI_DEVICE_ID, + .max_speed_hz = CX9261A_SPI_ENGINE_MAX_SPEED, + .mode = SPI_MODE_1, + .chip_select = CX9261A_SPI_CS, + .platform_ops = &spi_eng_platform_ops, + .extra = &spi_engine_param +}; + +static struct gpio_init_param cx9261a_gpio_nrst_init = { + .number = CX9261A_NRST, + .platform_ops = &xil_gpio_ops, + .extra = &xil_gpio_param +}; + +static struct gpio_init_param cx9261a_gpio_txnrx_trx1_init = { + .number = CX9261A_TXNRX_TRX1, + .platform_ops = &xil_gpio_ops, + .extra = &xil_gpio_param +}; + +static struct gpio_init_param cx9261a_gpio_txnrx_trx2_init = { + .number = CX9261A_TXNRX_TRX2, + .platform_ops = &xil_gpio_ops, + .extra = &xil_gpio_param +}; + +static struct gpio_init_param cx9261a_gpio_txnrx_rxfb_init = { + .number = CX9261A_TXNRX_RXFB, + .platform_ops = &xil_gpio_ops, + .extra = &xil_gpio_param +}; + +static struct gpio_init_param cx9261a_gpio_enable_trx1_init = { + .number = CX9261A_ENABLE_TRX1, + .platform_ops = &xil_gpio_ops, + .extra = &xil_gpio_param +}; + +static struct gpio_init_param cx9261a_gpio_enable_trx2_init = { + .number = CX9261A_ENABLE_TRX2, + .platform_ops = &xil_gpio_ops, + .extra = &xil_gpio_param +}; + +static struct gpio_init_param cx9261a_gpio_enable_rxfb_init = { + .number = CX9261A_ENABLE_RXFB, + .platform_ops = &xil_gpio_ops, + .extra = &xil_gpio_param +}; + +static struct gpio_init_param cx9261a_gpio_pl_rst_init = { + .number = CX9261A_PL_RST, + .platform_ops = &xil_gpio_ops, + .extra = &xil_gpio_param +}; + +static struct gpio_init_param cx9261a_gpio_tr_sel_init = { + .number = CX9261A_TR_SEL, + .platform_ops = &xil_gpio_ops, + .extra = &xil_gpio_param +}; + +static struct gpio_init_param cx9261a_gpio_tdd_fdd_sel_init = { + .number = CX9261A_TDD_FDD_SEL, + .platform_ops = &xil_gpio_ops, + .extra = &xil_gpio_param +}; + +static struct gpio_init_param cx9261a_gpio_r1t1_r2t2_sel_init = { + .number = CX9261A_R1T1_R2T2_SEL, + .platform_ops = &xil_gpio_ops, + .extra = &xil_gpio_param +}; + +static struct gpio_init_param cx9261a_gpio_sp_dp_sel_init = { + .number = CX9261A_SP_DP_SEL, + .platform_ops = &xil_gpio_ops, + .extra = &xil_gpio_param +}; + +static struct gpio_init_param cx9261a_gpio_mclk_delay_ctrl0_init = { + .number = CX9261A_MCLK_DELAY_CTRL0, + .platform_ops = &xil_gpio_ops, + .extra = &xil_gpio_param +}; + +static struct gpio_init_param cx9261a_gpio_mclk_delay_ctrl1_init = { + .number = CX9261A_MCLK_DELAY_CTRL1, + .platform_ops = &xil_gpio_ops, + .extra = &xil_gpio_param +}; + +static struct gpio_init_param cx9261a_gpio_mclk_delay_ctrl2_init = { + .number = CX9261A_MCLK_DELAY_CTRL2, + .platform_ops = &xil_gpio_ops, + .extra = &xil_gpio_param +}; + +static struct gpio_init_param cx9261a_gpio_hop_flag_init = { + .number = CX9261A_HOP_FLAG, + .platform_ops = &xil_gpio_ops, + .extra = &xil_gpio_param +}; + +struct cx9261a_init_param cx9261a_default_init_param = { + /* SPI */ + .spi_init = &cx9261a_spi_init, + .gpio_nrst = &cx9261a_gpio_nrst_init, + .gpio_txnrx_trx1 = &cx9261a_gpio_txnrx_trx1_init, + .gpio_txnrx_trx2 = &cx9261a_gpio_txnrx_trx2_init, + .gpio_txnrx_rxfb = &cx9261a_gpio_txnrx_rxfb_init, + .gpio_enable_trx1 = &cx9261a_gpio_enable_trx1_init, + .gpio_enable_trx2 = &cx9261a_gpio_enable_trx2_init, + .gpio_enable_rxfb = &cx9261a_gpio_enable_rxfb_init, + .gpio_pl_rst = &cx9261a_gpio_pl_rst_init, + .gpio_tr_sel = &cx9261a_gpio_tr_sel_init, + .gpio_tdd_fdd_sel = &cx9261a_gpio_tdd_fdd_sel_init, + .gpio_r1t1_r2t2_sel = &cx9261a_gpio_r1t1_r2t2_sel_init, + .gpio_sp_dp_sel = &cx9261a_gpio_sp_dp_sel_init, + .gpio_mclk_delay_ctrl0 = &cx9261a_gpio_mclk_delay_ctrl0_init, + .gpio_mclk_delay_ctrl1 = &cx9261a_gpio_mclk_delay_ctrl1_init, + .gpio_mclk_delay_ctrl2 = &cx9261a_gpio_mclk_delay_ctrl2_init, + .gpio_hop_flag = &cx9261a_gpio_hop_flag_init +}; + +CX9261A_CX9261AConfiguration cx9261a_configuration = { + // 电源管理 (与评估软件电源管理自定义电源里面勾选项相对应,先左再右,从上往下) + .powerManagement = + { + // 接收模式电源管理 + .rxPower = {0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1}, + // 发射模式电源管理 + .txPower = {1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1}, + // FDD模式电源管理 + .fddPower = {1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, + 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + .powerMode = CX9261A_PWM_Duplex, + }, + // 接口模式 + .interface = {.workMode = CX9261A_WKM_FDD, + .trMode = CX9261A_TRM_R1T1, + .portMode = CX9261A_PORTM_Single, + .ioMode = CX9261A_IOM_CMOS}, + // 时钟相关 + .clock = {.ref = 100, + .adc = 64, + .refDiv = 1, + .bbUseIntDiv = false, + .refDoubler = false}, + // 本振相关 + // 本振来源 + .pllSource = {.rxUseExt = false, + .txUseExt = false, + .rx1Source = CX9261A_LOSRC_Pll1, + .rx2Source = CX9261A_LOSRC_Pll2, + .rx3Source = CX9261A_LOSRC_Off, + .tx1Source = CX9261A_LOSRC_Pll1, + .tx2Source = CX9261A_LOSRC_Pll2}, + // 本振频率 注意:使用的所有PLL,频率必须设为正数 + .pllFreq = {.pll1Freq = 121.88, + .pll2Freq = 5000, + .pll3Freq = 800, + .pll4Freq = 900, + .pll5Freq = 1000}, + // Rx通道 注意:tia推荐配置为6 Mixer为三个通道共用同一个寄存器 + .rx1Config = {.bandwidth = 32, .tia = 6, .mixer = 31}, + .rx2Config = {.bandwidth = 32, .tia = 6, .mixer = 31}, + .rx3Config = {.bandwidth = 32, .tia = 6, .mixer = 31}, + // Tx通道 + .tx1Config = {.bandwidth = 20, .mixer = 63}, + .tx2Config = {.bandwidth = 20, .mixer = 63}, + // 接收滤波器 + .rxFilters = {.fir = 0, .hbf1 = 0, .hbf2 = 0, .hbf3 = 0, .cic = 0}, + // 发射滤波器 + .txFilters = {.fir = 0, .hbf1 = 2, .hbf2 = 2, .hbf3 = 0, .cic = 0}, + // Rx NCO + .rxNco = {.rxUseBist = false, + .rxNCOBypass = true, + .rxNCOUpward = true, + .rxNCOMaster = true, + .rxNCOFreq = 0}, + // Tx NCO + .txNco = {.txUseBist = false, .txNCOFreq = 0}, + // AGC 注意:mode = 0表示关闭AGC,此时其他的参数可以不配置 + // 校准 + .calib = {.calibDC = true, + .dcCalibMethod = CX9261A_DCCALM_Tracking, + .dcTrackingLength = 16384, + .dcHighPassFactor = 8191, + .rx1CalibPll = CX9261A_LOSRC_Pll1 | CX9261A_LOSRC_Pll2, + .rx2CalibPll = CX9261A_LOSRC_Pll1 | CX9261A_LOSRC_Pll2, + .rx3CalibPll = CX9261A_LOSRC_Pll5, + .tx1CalibPll = 0, + .tx2CalibPll = 0, + .rxMethod = CX9261A_CALM_Tracking, + .txMethod = CX9261A_CALM_Bisec, + .f0 = 4, +// .rxMultiPoints = {2200, 2400, 2600, 2800, 3000, 3200, 3400, +// 3600, 3800, 4000, 4200, 4400, 4600, 4800, +// 5000, 5200, 5400, 5600, 5800, 6000} + }, + .agc = {.mode = 0}, + .tempComp = CX9261A_TEMPCP_Band +}; + diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/dev_profile.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/dev_profile.h new file mode 100644 index 0000000..c4835bf --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/dev_profile.h @@ -0,0 +1,62 @@ +#ifndef _DEV_PROFILE_H_ +#define _DEV_PROFILE_H_ + +#include "irq.h" +#include "error.h" +#include "../drivers/frequency/hmc7044/hmc7044.h" +#include "../drivers/frequency/ad9528/ad9528.h" +#include "../drivers/frequency/lmk04828/lmk04828.h" +#include "../drivers/frequency/lmx2594/lmx2594.h" +#include "../drivers/rf-transceiver/ad9361_reg/ad9361_reg.h" +#include "../drivers/rf-transceiver/ad9361/ad9361_api.h" +#include "../drivers/rf-transceiver/cx9261a/cx9261a.h" +#include "../drivers/rf-transceiver/cx9261a/cx9261adriver_c.h" +#include "../drivers/adc/ad9694/ad9694.h" +#include "../drivers/adc/ad9653/ad9653.h" +#include "../config/app_config.h" + +// device parameters +extern struct irq_init_param intc_init_param; +extern struct timer_init_param apptimer_init_param; + +// HMC7044 +extern struct hmc7044_init_param hmc7044_default_init_param; + +// AD9528 +extern ad9528Init_t ad9528_default_init_param; + +// LMK04828 +extern struct lmk04828_init_param lmk04828_default_init_param; +extern const struct lmk04828_config lmk04828_default_config[]; +extern const uint32_t lmk04828_default_config_len; + +// LMX2594 +extern struct lmx2594_init_param lmx2594_adc_init_param; +extern struct lmx2594_init_param lmx2594_dac_init_param; +extern const struct lmx2594_config lmx2594_default_config[]; +extern const uint32_t lmx2594_default_config_len; + +// AD9361 +extern AD9361_InitParam ad9361_default_init_param; +extern AD9361_RXFIRConfig rx_fir_config; +extern AD9361_TXFIRConfig tx_fir_config; +extern struct ad9361_reg_init_param ad9361_1_reg_default_init_param; +extern struct ad9361_reg_init_param ad9361_2_reg_default_init_param; +uint32_t ad9361_reg_config(struct ad9361_reg_dev *dev); + +// CX9261A +extern struct cx9261a_init_param cx9261a_default_init_param; +extern CX9261A_CX9261AConfiguration cx9261a_configuration; + +// AD9653 +extern struct ad9653_init_param ad9653_default_init_param; +extern const struct ad9653_config ad9653_default_config[]; +extern const uint32_t ad9653_default_config_len; + +// AD9694 +extern struct ad9694_init_param ad9694_default_init_param; +extern const struct ad9694_config ad9694_default_config[]; +extern const uint32_t ad9694_default_config_len; + + +#endif /* SRC_DEV_PROFILE_H_ */ diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/hmc7044_profile.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/hmc7044_profile.c new file mode 100644 index 0000000..a091b5f --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/hmc7044_profile.c @@ -0,0 +1,132 @@ +#include "dev_profile.h" +#include "../config/app_config.h" +#include "../drivers/platform/xilinx_gpio.h" +#include "../drivers/axi_core/spi_engine/spi_engine.h" +#include "../drivers/frequency/hmc7044/hmc7044.h" + + +static struct spi_engine_init_param spi_engine_param = { + .ref_clk_hz = HMC7044_SPI_ENGINE_REF_CLK, + .type = SPI_ENGINE, + .spi_engine_baseaddr = HMC7044_SPI_ENGINE_BASEADDR, + .cs_delay = HMC7044_SPI_ENGINE_CS_DELAY, + .data_width = HMC7044_SPI_ENGINE_DATA_WIDTH +}; + +//static struct xil_gpio_init_param xil_gpio_param = { +// .type = GPIO_PS, +// .device_id = AD9361_GPIO_DEVICE_ID +//}; + +// hmc7044 spi settings +static struct spi_init_param hmc7044_spi_init_param = { + .device_id = HMC7044_SPI_DEVICE_ID, + .max_speed_hz = HMC7044_SPI_ENGINE_MAX_SPEED, + .mode = SPI_MODE_4, + .chip_select = HMC7044_SPI_CS, + .platform_ops = &spi_eng_platform_ops, + .extra = &spi_engine_param +}; + +// hmc7044 channel specifications +struct hmc7044_chan_spec hmc7044_chan_spec[4] = { + /* AD9361_XTAL */ + { + .disable = 0, + .num = 3, + .divider = 100, + .coarse_delay = 0, + .fine_delay = 0, + .high_performance_mode_dis = true, + .start_up_mode_dynamic_enable = false, + .dynamic_driver_enable = false, + .output_control0_rb4_enable = true, + .force_mute_enable = false, + .driver_mode = CML, + .driver_impedance = INTERNAL_100OHM_PER_PIN, + .out_mux_mode = CHANNEL_DIV + }, + /* FPGA_DAC_LOGIC_CLK */ + { + .disable = 0, + .num = 4, + .divider = 10, + .coarse_delay = 0, + .fine_delay = 0, + .high_performance_mode_dis = true, + .start_up_mode_dynamic_enable = false, + .dynamic_driver_enable = false, + .output_control0_rb4_enable = true, + .force_mute_enable = false, + .driver_mode = CML, + .driver_impedance = INTERNAL_100OHM_PER_PIN, + .out_mux_mode = CHANNEL_DIV + }, + /* B_AD9361_XTAL */ + { + .disable = 0, + .num = 12, + .divider = 100, + .coarse_delay = 0, + .fine_delay = 0, + .high_performance_mode_dis = false, + .start_up_mode_dynamic_enable = false, + .dynamic_driver_enable = false, + .output_control0_rb4_enable = true, + .force_mute_enable = false, + .driver_mode = CML, + .driver_impedance = INTERNAL_100OHM_PER_PIN, + .out_mux_mode = CHANNEL_DIV + }, + /* AD9361_RX_EXT_LO */ + { + .disable = 0, + .num = 13, + .divider = 10, + .coarse_delay = 0, + .fine_delay = 0, + .high_performance_mode_dis = false, + .start_up_mode_dynamic_enable = false, + .dynamic_driver_enable = false, + .output_control0_rb4_enable = true, + .force_mute_enable = false, + .driver_mode = CML, + .driver_impedance = INTERNAL_100OHM_PER_PIN, + .out_mux_mode = CHANNEL_DIV + }, +}; + +// hmc7044 default configurations +struct hmc7044_init_param hmc7044_default_init_param = { + .spi_init = &hmc7044_spi_init_param, + .clkin_freq = {0, 100000000, 0, 0}, + .vcxo_freq = 100000000, + .pll2_freq = 2500000000, + .pll1_loop_bw = 200, + .sysref_timer_div = 1536, + .in_buf_mode = { + BUFFER_DISABLE, + ENABLE_AC_COUPLE | ENABLE_INTERNAL_TERM | BUFFER_ENABLE, + BUFFER_DISABLE, + BUFFER_DISABLE, + ENABLE_AC_COUPLE | ENABLE_INTERNAL_TERM | BUFFER_ENABLE + }, + .gpi_ctrl = { + GPI_DISABLE, + GPI_DISABLE, + CHIP_SLEEP_MODE, + PULSE_GEN_REQUSET + }, + .gpo_ctrl = { + PLL1_LOCK_DETECT | GPO_CMOS_MODE, + PLL2_LOCK_DETECT | GPO_CMOS_MODE, + GPO_DISABLE, + GPO_DISABLE + }, + .num_channels = 4, + .pll1_ref_prio_ctrl = 0xE4, + .sync_pin_mode = SYNC_MULTICHIP, + .high_performance_mode_clock_dist_en = false, + .pulse_gen_mode = PULSE_ONE, + .channels = hmc7044_chan_spec +}; diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/intc_profile.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/intc_profile.c new file mode 100644 index 0000000..ae8990e --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/intc_profile.c @@ -0,0 +1,14 @@ +#include "dev_profile.h" +#include "irq.h" +#include "../config/app_config.h" +#include "../drivers/platform/xilinx_irq.h" + +struct xil_irq_init_param xil_intc_init_param = { + .type = IRQ_PS +}; + +struct irq_init_param intc_init_param = { + .irq_ctrl_id = INTC_DEVICE_ID, + .platform_ops = &xil_irq_ops, + .extra = &xil_intc_init_param +}; diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/lmk04828_profile.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/lmk04828_profile.c new file mode 100644 index 0000000..3965fac --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/lmk04828_profile.c @@ -0,0 +1,182 @@ +#include "dev_profile.h" +#include "spi.h" +#include "../config/app_config.h" +#include "../drivers/frequency/lmk04828/lmk04828.h" +#include "../drivers/axi_core/spi_engine/spi_engine.h" +#include "../drivers/platform/xilinx_gpio.h" + +static struct spi_engine_init_param spi_engine_param = { + .ref_clk_hz = LMK04828_SPI_ENGINE_REF_CLK, + .type = SPI_ENGINE, + .spi_engine_baseaddr = LMK04828_SPI_ENGINE_BASEADDR, + .cs_delay = LMK04828_SPI_ENGINE_CS_DELAY, + .data_width = LMK04828_SPI_ENGINE_DATA_WIDTH +}; + +//static struct xil_gpio_init_param xil_gpio_param = { +// .type = GPIO_PS, +// .device_id = LMK04828_GPIO_DEVICE_ID +//}; + +static struct spi_init_param lmk04828_spi_init = { + .device_id = LMK04828_SPI_DEVICE_ID, + .max_speed_hz = LMK04828_SPI_ENGINE_MAX_SPEED, + .mode = SPI_MODE_0, + .chip_select = LMK04828_SPI_CS, + .platform_ops = &spi_eng_platform_ops, + .extra = &spi_engine_param +}; + +struct lmk04828_init_param lmk04828_default_init_param = { + /* SPI */ + .spi_init = &lmk04828_spi_init +}; + +const uint32_t lmk04828_default_config_len = 137; +const struct lmk04828_config lmk04828_default_config[] = { + // 192MHz + {0x0000, 0x90}, // [7] reset; [4] 3-wire mode disabled + {0x0000, 0x10}, // [7] normal operation; [4] 3-wire mode disabled + {0x0002, 0x00}, // [0] normal operation + {0x0003, 0x06}, // [7-0] product device type, read only, default: 6 + {0x0004, 0xD0}, // [7-0] msb of product identifier, read only, default: 208 + {0x0005, 0x5B}, // [7-0] lsb of product identifier, read only, default: 91 + {0x0006, 0x00}, // [7-0] ic version identifier, read only, default: 32 + {0x000C, 0x51}, // [7-0] msb of vendor identifier, read only, default: 81 + {0x000D, 0x04}, // [7-0] lsb of vendor identifier, read only, default: 4 + // device clock 0 + {0x0100, 0x10}, // [6] output drive level (ODL); [5] input drive level (IDL); [4-0] device clock (DCLK) output divider + {0x0101, 0x55}, // DCLK: [7-4] output digital delay (DDLY) high count; [3-0] DDLY low count + {0x0102, 0x55}, + {0x0103, 0x01}, // DCLK: [7-3] analog delay (ADLY) value, 25 ps per lsb; [2] ADLY input mux; [1-0] DCLK input mux + {0x0104, 0x22}, // [6] DCLK half step value; [5] SDCLK output MUX; [4-1] SDCLK DDLY; [0] SYSREF half step value + {0x0105, 0x00}, // SYSREF: [4] ADLY enable; [3:0] ADLY value, 600 ps + 150 ps per lsb + {0x0106, 0xF0}, // [7] DCLK DDLY enable; [6] DCLK glitchless half step enable; [5] DCLK glitchless ADLY enable; [4] DCLK ADLY enable; [3]: clock group enable; [2-1] SYSREF disable mode; [0]: SYSREF disable + {0x0107, 0x11}, // [7] SDCLK output polarity; [6-4] SDCLK output format; [3] DCLK output polarity; [2:0] DCLK output format + // device clock 2 + {0x0108, 0x10}, // [6] output drive level (ODL); [5] input drive level (IDL); [4-0] device clock (DCLK) output divider + {0x0109, 0x55}, // DCLK: [7-4] output digital delay (DDLY) high count; [3-0] DDLY low count + {0x010A, 0x55}, + {0x010B, 0x00}, // DCLK: [7-3] analog delay (ADLY) value, 25 ps per lsb; [2] ADLY input mux; [1-0] DCLK input mux + {0x010C, 0x22}, // [6] DCLK half step value; [5] SDCLK output MUX; [4-1] SDCLK DDLY; [0] SYSREF half step value + {0x010D, 0x00}, // SYSREF: [4] ADLY enable; [3:0] ADLY value, 600 ps + 150 ps per lsb + {0x010E, 0xF0}, // [7] DCLK DDLY enable; [6] DCLK glitchless half step enable; [5] DCLK glitchless ADLY enable; [4] DCLK ADLY enable; [3]: clock group enable; [2-1] SYSREF disable mode; [0]: SYSREF disable + {0x010F, 0x10}, // [7] SDCLK output polarity; [6-4] SDCLK output format; [3] DCLK output polarity; [2:0] DCLK output format + // device clock 4 + {0x0110, 0x10}, // [6] output drive level (ODL); [5] input drive level (IDL); [4-0] device clock (DCLK) output divider + {0x0111, 0x55}, // DCLK: [7-4] output digital delay (DDLY) high count; [3-0] DDLY low count + {0x0112, 0x55}, + {0x0113, 0x00}, // DCLK: [7-3] analog delay (ADLY) value, 25 ps per lsb; [2] ADLY input mux; [1-0] DCLK input mux + {0x0114, 0x22}, // [6] DCLK half step value; [5] SDCLK output MUX; [4-1] SDCLK DDLY; [0] SYSREF half step value + {0x0115, 0x00}, // SYSREF: [4] ADLY enable; [3:0] ADLY value, 600 ps + 150 ps per lsb + {0x0116, 0xF0}, // [7] DCLK DDLY enable; [6] DCLK glitchless half step enable; [5] DCLK glitchless ADLY enable; [4] DCLK ADLY enable; [3]: clock group enable; [2-1] SYSREF disable mode; [0]: SYSREF disable + {0x0117, 0x11}, // [7] SDCLK output polarity; [6-4] SDCLK output format; [3] DCLK output polarity; [2:0] DCLK output format + // device clock 6 + {0x0118, 0x10}, // [6] output drive level (ODL); [5] input drive level (IDL); [4-0] device clock (DCLK) output divider + {0x0119, 0x55}, // DCLK: [7-4] output digital delay (DDLY) high count; [3-0] DDLY low count + {0x011A, 0x55}, + {0x011B, 0x00}, // DCLK: [7-3] analog delay (ADLY) value, 25 ps per lsb; [2] ADLY input mux; [1-0] DCLK input mux + {0x011C, 0x02}, // [6] DCLK half step value; [5] SDCLK output MUX; [4-1] SDCLK DDLY; [0] SYSREF half step value + {0x011D, 0x00}, // SYSREF: [4] ADLY enable; [3:0] ADLY value, 600 ps + 150 ps per lsb + {0x011E, 0xF0}, // [7] DCLK DDLY enable; [6] DCLK glitchless half step enable; [5] DCLK glitchless ADLY enable; [4] DCLK ADLY enable; [3]: clock group enable; [2-1] SYSREF disable mode; [0]: SYSREF disable + {0x011F, 0x55}, // [7] SDCLK output polarity; [6-4] SDCLK output format; [3] DCLK output polarity; [2:0] DCLK output format + // device clock 8 + {0x0120, 0x10}, // [6] output drive level (ODL); [5] input drive level (IDL); [4-0] device clock (DCLK) output divider + {0x0121, 0x55}, // DCLK: [7-4] output digital delay (DDLY) high count; [3-0] DDLY low count + {0x0122, 0x55}, + {0x0123, 0x00}, // DCLK: [7-3] analog delay (ADLY) value, 25 ps per lsb; [2] ADLY input mux; [1-0] DCLK input mux + {0x0124, 0x22}, // [6] DCLK half step value; [5] SDCLK output MUX; [4-1] SDCLK DDLY; [0] SYSREF half step value + {0x0125, 0x00}, // SYSREF: [4] ADLY enable; [3:0] ADLY value, 600 ps + 150 ps per lsb + {0x0126, 0xF0}, // [7] DCLK DDLY enable; [6] DCLK glitchless half step enable; [5] DCLK glitchless ADLY enable; [4] DCLK ADLY enable; [3]: clock group enable; [2-1] SYSREF disable mode; [0]: SYSREF disable + {0x0127, 0x55}, // [7] SDCLK output polarity; [6-4] SDCLK output format; [3] DCLK output polarity; [2:0] DCLK output format + // device clock 10 + {0x0128, 0x10}, // [6] output drive level (ODL); [5] input drive level (IDL); [4-0] device clock (DCLK) output divider + {0x0129, 0x55}, // DCLK: [7-4] output digital delay (DDLY) high count; [3-0] DDLY low count + {0x012A, 0x55}, + {0x012B, 0x00}, // DCLK: [7-3] analog delay (ADLY) value, 25 ps per lsb; [2] ADLY input mux; [1-0] DCLK input mux + {0x012C, 0x22}, // [6] DCLK half step value; [5] SDCLK output MUX; [4-1] SDCLK DDLY; [0] SYSREF half step value + {0x012D, 0x00}, // SYSREF: [4] ADLY enable; [3:0] ADLY value, 600 ps + 150 ps per lsb + {0x012E, 0xF0}, // [7] DCLK DDLY enable; [6] DCLK glitchless half step enable; [5] DCLK glitchless ADLY enable; [4] DCLK ADLY enable; [3]: clock group enable; [2-1] SYSREF disable mode; [0]: SYSREF disable + {0x012F, 0x51}, // [7] SDCLK output polarity; [6-4] SDCLK output format; [3] DCLK output polarity; [2:0] DCLK output format + // device clock 12 + {0x0130, 0x10}, // [6] output drive level (ODL); [5] input drive level (IDL); [4-0] device clock (DCLK) output divider + {0x0131, 0x55}, // DCLK: [7-4] output digital delay (DDLY) high count; [3-0] DDLY low count + {0x0132, 0x55}, + {0x0133, 0x00}, // DCLK: [7-3] analog delay (ADLY) value, 25 ps per lsb; [2] ADLY input mux; [1-0] DCLK input mux + {0x0134, 0x22}, // [6] DCLK half step value; [5] SDCLK output MUX; [4-1] SDCLK DDLY; [0] SYSREF half step value + {0x0135, 0x00}, // SYSREF: [4] ADLY enable; [3:0] ADLY value, 600 ps + 150 ps per lsb + {0x0136, 0xF0}, // [7] DCLK DDLY enable; [6] DCLK glitchless half step enable; [5] DCLK glitchless ADLY enable; [4] DCLK ADLY enable; [3]: clock group enable; [2-1] SYSREF disable mode; [0]: SYSREF disable + {0x0137, 0x55}, // [7] SDCLK output polarity; [6-4] SDCLK output format; [3] DCLK output polarity; [2:0] DCLK output format + // clock distribution source + {0x0138, 0x20}, // [6-5] VCO mux; [4] OSCout mux; [3:0] OSCout format + {0x0139, 0x03}, // [2] SYSREF output mux; [1-0] SYSREF source + {0x013A, 0x0C}, // [4-0] SYSREF divider [12:8] + {0x013B, 0x00}, // [7-0] SYSREF divider [7:0] + {0x013C, 0x00}, // [4-0] SYSREF DDLY [12:8] + {0x013D, 0x08}, // [7-0] SYSREF DDLY [7:0] + {0x013E, 0x03}, // [1-0] SYSREF pulse count + {0x013F, 0x00}, // [4] pll2 mux; [3] pll1 mux; [2-1] feedback mux; [0] feedback mux enable + {0x0140, 0x0B}, // power-down control: [7] pll1; [6] vco_ldo; [5] vco; [4] OSCin; [3] individual SYSREF; [2] SYSREF; [1] SYSREF DDLY; [0] SYSREF pluse generator + {0x0141, 0x00}, // dynamic DDLY control + {0x0142, 0x00}, // dynamic DDLY step control + {0x0143, 0x10}, + {0x0144, 0xFF}, + {0x0145, 0x7F}, + {0x0146, 0x12}, + {0x0147, 0x1A}, + {0x0148, 0x03}, + {0x0149, 0x05}, + {0x014A, 0x1B}, + {0x014B, 0x16}, + {0x014C, 0x00}, + {0x014D, 0x00}, + {0x014E, 0xC0}, + {0x014F, 0x7F}, + {0x0150, 0x03}, + {0x0151, 0x02}, + {0x0152, 0x00}, + {0x0153, 0x00}, + {0x0154, 0x78}, + {0x0155, 0x00}, + {0x0156, 0x7D}, + {0x0157, 0x00}, + {0x0158, 0x96}, + {0x0159, 0x06}, + {0x015A, 0x00}, + {0x015B, 0xD4}, + {0x015C, 0x20}, + {0x015D, 0x00}, + {0x015E, 0x00}, + {0x015F, 0x3B}, + {0x0160, 0x00}, + {0x0161, 0x02}, + {0x0162, 0x44}, + {0x0163, 0x00}, + {0x0164, 0x00}, + {0x0165, 0x0C}, + {0x0171, 0xAA}, + {0x0172, 0x02}, + {0x017C, 0x15}, + {0x017D, 0x33}, + {0x0166, 0x00}, + {0x0167, 0x00}, + {0x0168, 0x19}, + {0x0169, 0x59}, + {0x016A, 0x20}, + {0x016B, 0x00}, + {0x016C, 0x00}, + {0x016D, 0x00}, + {0x016E, 0x1B}, + {0x0173, 0x00}, + {0x0182, 0x00}, + {0x0183, 0x00}, + {0x0184, 0x00}, + {0x0185, 0x00}, + {0x0188, 0x00}, + {0x0189, 0x00}, + {0x018A, 0x00}, + {0x018B, 0x00}, + {0x1FFD, 0x00}, + {0x1FFE, 0x00}, + {0x1FFF, 0x53} +}; diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/lmx2594_profile.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/lmx2594_profile.c new file mode 100644 index 0000000..6238ebb --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/lmx2594_profile.c @@ -0,0 +1,181 @@ +#include "dev_profile.h" +#include "spi.h" +#include "../config/app_config.h" +#include "../drivers/frequency/lmx2594/lmx2594.h" +#include "../drivers/axi_core/spi_engine/spi_engine.h" +#include "../drivers/platform/xilinx_gpio.h" + +static struct spi_engine_init_param spi_engine_param = { + .ref_clk_hz = AD9653_SPI_ENGINE_REF_CLK, + .type = SPI_ENGINE, + .spi_engine_baseaddr = AD9653_SPI_ENGINE_BASEADDR, + .cs_delay = AD9653_SPI_ENGINE_CS_DELAY, + .data_width = AD9653_SPI_ENGINE_DATA_WIDTH +}; + +//static struct xil_gpio_init_param xil_gpio_param = { +// .type = GPIO_PS, +// .device_id = AD9653_GPIO_DEVICE_ID +//}; + +static struct spi_init_param lmx2594_adc_spi_init = { + .device_id = LMX2594_ADC_SPI_DEVICE_ID, + .max_speed_hz = LMX2594_ADC_SPI_ENGINE_MAX_SPEED, + .mode = SPI_MODE_0, + .chip_select = LMX2594_ADC_SPI_CS, + .platform_ops = &spi_eng_platform_ops, + .extra = &spi_engine_param +}; + +static struct spi_init_param lmx2594_dac_spi_init = { + .device_id = LMX2594_DAC_SPI_DEVICE_ID, + .max_speed_hz = LMX2594_DAC_SPI_ENGINE_MAX_SPEED, + .mode = SPI_MODE_0, + .chip_select = LMX2594_DAC_SPI_CS, + .platform_ops = &spi_eng_platform_ops, + .extra = &spi_engine_param +}; + +struct lmx2594_init_param lmx2594_adc_init_param = { + /* SPI */ + .spi_init = &lmx2594_adc_spi_init +}; + +struct lmx2594_init_param lmx2594_dac_init_param = { + /* SPI */ + .spi_init = &lmx2594_dac_spi_init +}; + +const uint32_t lmx2594_default_config_len = 110; +const struct lmx2594_config lmx2594_default_config[] = { + // 0 ~ 78: General configuration + // 79 ~ 106: Ramping configuration + // 107 ~ 112: Readback registers + + // Recommended Initial Power-Up Sequence + // For the most reliable programming, TI recommends this procedure:: + // 1. Apply power to device. + // 2. Program RESET = 1 to reset registers. + // 3. Program RESET = 0 to remove reset. + // 4. Program registers as shown in the register map in REVERSE order from highest to lowest. + // 5. Wait 10 ms. + // 6. Program register R0 one additional time with FCAL_EN = 1 to ensure that the VCO calibration runs from a stable state. + + // Reset + { 0, 0x0002}, + + // Remove Reset + { 0, 0x0000}, + + // Reverse order programming + {106, 0x0000}, + {105, 0x0021}, + {104, 0x0000}, + {103, 0xD555}, + {102, 0x3F97}, + {101, 0x0011}, + {100, 0x0000}, + { 99, 0x2AAB}, + { 98, 0x01A0}, + { 97, 0x0888}, + { 96, 0x0000}, + { 95, 0x0000}, // fixed + { 94, 0x0000}, // fixed + { 93, 0x0000}, // fixed + { 92, 0x0000}, // fixed + { 91, 0x0000}, // fixed + { 90, 0x0000}, // fixed + { 89, 0x0000}, // fixed + { 88, 0x0000}, // fixed + { 87, 0x0000}, // fixed + { 86, 0x5879}, + { 85, 0xEC58}, + { 84, 0x0001}, + { 83, 0x54C9}, + { 82, 0x2961}, + { 81, 0x0000}, + { 80, 0x4000}, + { 79, 0x001F}, + { 78, 0x0003}, // [8:1] VCO_CAPCTRL_STRT + { 77, 0x0000}, // fixed + { 76, 0x000C}, // fixed + { 75, 0x0800}, // [10:6] CHDIV + { 74, 0x0000}, // [11:6] JESD_DAC4_CTRL; [5:0] JESD_DAC3_CTRL + { 73, 0x003F}, // [11:6] JESD_DAC2_CTRL; [5:0] JESD_DAC1_CTRL + { 72, 0x0001}, // [10:0] SYSREF_DIV + { 71, 0x0081}, // [7:5] SYSREF_DIV_PRE; [4] SYSREF_PULSE; [3] SYSREF_EN; [2] SYSREF_REPEAT + { 70, 0xC350}, // [15:0] MASH_RST_COUNT + { 69, 0x0000}, // [15:0] MASH_RST_COUNT + { 68, 0x03E8}, // fixed + { 67, 0x0000}, // fixed + { 66, 0x01F4}, // fixed + { 65, 0x0000}, // fixed + { 64, 0x1388}, // fixed + { 63, 0x0000}, // fixed + { 62, 0x0322}, // fixed + { 61, 0x00A8}, // fixed + { 60, 0x0000}, // [15:0] LD_DLY + { 59, 0x0001}, // [0] LD_TYPE + { 58, 0x9001}, // [15] INPIN_IGNORE; [14] INPIN_HYST; [13:12] INPIN_LVL; [11:9] INPIN_FMT + { 57, 0x0020}, // fixed + { 56, 0x0000}, // fixed + { 55, 0x0000}, // fixed + { 54, 0x0000}, // fixed + { 53, 0x0000}, // fixed + { 52, 0x0820}, // fixed + { 51, 0x0080}, // fixed + { 50, 0x0000}, // fixed + { 49, 0x4180}, // fixed + { 48, 0x0300}, // fixed + { 47, 0x0300}, // fixed + { 46, 0x07FC}, // [1:0] OUTB_MUX + { 45, 0xC0DF}, // [12:11] OUTA_MUX; [10:9] OUT_ISET; [5:0] OUTB_PWR + { 44, 0x1AA3}, // [13:8] OUTA_PWR; [7] OUTB_PD; [6] OUTA_PD; [5] MASH_RESET_N; [2:0] MASH_ORDER + { 43, 0x0000}, // [15:0] PLL_NUM + { 42, 0x0000}, // [15:0] PLL_NUM + { 41, 0x0000}, // [15:0] MASH_SEE + { 40, 0x0000}, // [15:0] MASH_SEE + { 39, 0x03E8}, // [15:0] PLL_DEN + { 38, 0x0000}, // [15:0] PLL_DEN + { 37, 0x0304}, // [37] MASH_SEED_EN; [13:8] PFD_DLY_SEL + { 36, 0x0028}, // [15:0] PLL_N + { 35, 0x0004}, // fixed + { 34, 0x0000}, // [2:0] PLL_N + { 33, 0x1E21}, // fixed + { 32, 0x0393}, // fixed + { 31, 0x43EC}, // [14] SEG1_EN + { 30, 0x318C}, // fixed + { 29, 0x318C}, // fixed + { 28, 0x0488}, // fixed + { 27, 0x0002}, // fixed + { 26, 0x0DB0}, // fixed + { 25, 0x0C2B}, // fixed + { 24, 0x071A}, // fixed + { 23, 0x007C}, // fixed + { 22, 0x0001}, // fixed + { 21, 0x0401}, // fixed + { 20, 0xE048}, // [13:11] VCO_SEL. [10] VCO_SEL_FORCE + { 19, 0x27B7}, // [7:0] VCO_CAPCTRL + { 18, 0x0064}, // fixed + { 17, 0x012C}, // [8:0] VCO_DACISET_STRT + { 16, 0x0080}, // [8:0] VCO_DACISET + { 15, 0x064F}, // fixed + { 14, 0x1E70}, // [6:4] CPG + { 13, 0x4000}, // fixed + { 12, 0x5001}, // [11:0] PLL_R_PR + { 11, 0x0018}, // [11:4] PLL_R + { 10, 0x10D8}, // [11:7] MULT + { 9, 0x0604}, // [12] OSC_2X + { 8, 0x2000}, // [14] VCO_DACISET_FORCE; [11] VCO_CAPCTRL_FORCE + { 7, 0x40B2}, // [14] OUT_FORCE + { 6, 0xC802}, // fixed + { 5, 0x00C8}, // fixed + { 4, 0x0C43}, // [15:8] ACAL_CMP_DLY + { 3, 0x0642}, // fixed + { 2, 0x0500}, // fixed + { 1, 0x0809}, // [2:0] CAL_CLK_DIV + { 0, 0x2598}, // [15] RAMP_EN; [14] VCO_PHASE_SYNC; [9] OUT_MUTE; [8:7] FCAL_HPFD_ADJ; [6:5] FCAL_LPFD_ADJ; [3] FCAL_EN; [2] MUXOUT_LD_SEL; [1] RESET; [0] POWERDOWN + + // one more time + { 0, 0x2598} +}; diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/timer_profile.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/timer_profile.c new file mode 100644 index 0000000..1f82c10 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/dev_profile/timer_profile.c @@ -0,0 +1,21 @@ +#include "dev_profile.h" +#include "timer.h" +#include "../config/app_config.h" +#include "../drivers/platform/xilinx_timer.h" + +struct xil_timer_init_param xil_apptimer_init_param = { + .active_tmr = 0, +#ifdef __aarch64__ + .type = TIMER_PL +#else + .type = TIMER_PS +#endif +}; + +struct timer_init_param apptimer_init_param = { + .id = APPTIMER_DEVICE_ID, + .freq_hz = APP_TIMER_CLK_HZ, + .ticks_count = (APP_TIMER_CLK_HZ/10), //update log every 100ms + .platform_ops = &xil_timer_ops, + .extra = &xil_apptimer_init_param +}; diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/adc/ad9653/ad9653.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/adc/ad9653/ad9653.c new file mode 100644 index 0000000..688458d --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/adc/ad9653/ad9653.c @@ -0,0 +1,129 @@ +/***************************************************************************//** + * @file ad9653.c + * @brief Implementation of AD9653 Driver. + * @author DBogdan (dragos.bogdan@analog.com) + ******************************************************************************** + * Copyright 2014-2016(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. + *******************************************************************************/ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include "ad9653.h" + +#include +#include +#include "../../../include/error.h" + +/***************************************************************************//** + * @brief ad9653_read + *******************************************************************************/ +int32_t ad9653_read(struct ad9653_dev *dev, + uint16_t reg_addr, + uint8_t *reg_data) +{ + uint8_t tx_buf[2]; + uint8_t rx_buf[1]; + int32_t ret; + + tx_buf[0] = 0x80 | (reg_addr >> 8); + tx_buf[1] = reg_addr & 0xFF; + + + ret = spi_write_and_read_3wire(dev->spi_desc, + tx_buf, rx_buf, 2, 1); + + *reg_data = rx_buf[0]; + + return ret; +} + +/***************************************************************************//** + * @brief ad9653_write + *******************************************************************************/ +int32_t ad9653_write(struct ad9653_dev *dev, + uint16_t reg_addr, + uint8_t reg_data) +{ + uint8_t buf[3]; + + int32_t ret; + + buf[0] = reg_addr >> 8; + buf[1] = reg_addr & 0xFF; + buf[2] = reg_data; + + ret = spi_write_and_read_3wire(dev->spi_desc, + buf, NULL, 3, 0); + return ret; +} + +/***************************************************************************//** + * @brief ad9653_init + *******************************************************************************/ +int32_t ad9653_init(struct ad9653_dev **device, + const struct ad9653_init_param *init_param) +{ + int ret = SUCCESS; + struct ad9653_dev *dev; + + dev = (struct ad9653_dev *)malloc(sizeof(*dev)); + if (!dev) + return FAILURE; + + /* SPI */ + ret = spi_init(&dev->spi_desc, init_param->spi_init); + + *device = dev; + + return ret; +} + +/***************************************************************************//** + * @brief Free the resources allocated by ad9653_setup(). + * + * @param dev - The device structure. + * + * @return SUCCESS in case of success, negative error code otherwise. +*******************************************************************************/ +int32_t ad9653_remove(struct ad9653_dev *dev) +{ + int32_t ret; + + ret = spi_remove(dev->spi_desc); + + free(dev); + + return ret; +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/adc/ad9653/ad9653.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/adc/ad9653/ad9653.h new file mode 100644 index 0000000..8271886 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/adc/ad9653/ad9653.h @@ -0,0 +1,233 @@ +/***************************************************************************//** + * @file ad9653.h + * @brief Header file of AD9653 Driver. + * @author DBogdan (dragos.bogdan@analog.com) + ******************************************************************************** + * Copyright 2014-2016(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. + *******************************************************************************/ +#ifndef AD9653_H_ +#define AD9653_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include +#include "delay.h" +#include "spi.h" + +/******************************************************************************/ +/********************** Macros and Constants Definitions **********************/ +/******************************************************************************/ +#define AD9694_REG_GLOBAL_MAP_SPI_CONF_A 0x0000 +#define AD9694_REG_GLOBAL_MAP_SPI_CONF_B 0x0001 +#define AD9694_REG_CH_MAP_CHIP_CONF 0x0002 +#define AD9694_REG_PAIR_MAP_CHIP_TYPE 0x0003 +#define AD9694_REG_PAIR_MAP_CHIP_ID_LSB 0x0004 +#define AD9694_REG_PAIR_MAP_CHIP_GRADE 0x0006 +#define AD9694_REG_PAIR_MAP_DEV_INDEX 0x0008 +#define AD9694_REG_GLOBAL_MAP_PAIR_INDEX 0x0009 +#define AD9694_REG_PAIR_MAP_SCRATCH_PAD 0x000A +#define AD9694_REG_PAIR_MAP_SPI_REVISION 0x000B +#define AD9694_REG_PAIR_MAP_VENDOR_ID_LSB 0x000C +#define AD9694_REG_PAIR_MAP_VENDOR_ID_MSB 0x000D +#define AD9694_REG_CH_MAP_CHIP_PD_PIN 0x003F +#define AD9694_REG_PAIR_MAP_CHIP_PIN_CTRL_1 0x0040 +#define AD9694_REG_PAIR_MAP_CLK_DIV_CTRL 0x0108 +#define AD9694_REG_CH_MAP_CLK_DIV_PHASE 0x0109 +#define AD9694_REG_PAIR_MAP_CLK_DIV_SYSREF_CTRL 0x010A +#define AD9694_REG_PAIR_MAP_CLK_DELAY_CTRL 0x0110 +#define AD9694_REG_CH_MAP_CLK_SUPER_FINE_DELAY 0x0111 +#define AD9694_REG_CH_MAP_CLK_FINE_DELAY 0x0112 +#define AD9694_REG_CLK_DETN_CTRL 0x011A +#define AD9694_REG_PAIR_MAP_CLK_STAT 0x011B +#define AD9694_REG_CLK_DCS_CTRL_1 0x011C +#define AD9694_REG_CLK_DCS_CTRL_2 0x011E +#define AD9694_REG_CLK_DCS_CTRL_3 0x011F +#define AD9694_REG_PAIR_MAP_SYSREF_CTRL_1 0x0120 +#define AD9694_REG_PAIR_MAP_SYSREF_CTRL_2 0x0121 +#define AD9694_REG_PAIR_MAP_SYSREF_CTRL_4 0x0123 +#define AD9694_REG_PAIR_MAP_SYSREF_STAT_1 0x0128 +#define AD9694_REG_PAIR_MAP_SYSREF_STAT_2 0x0129 +#define AD9694_REG_PAIR_MAP_SYSREF_STAT_3 0x012A +#define AD9694_REG_PAIR_MAP_CHIP_SYNC 0x01FF +#define AD9694_REG_PAIR_MAP_CHIP_MODE 0x0200 +#define AD9694_REG_PAIR_MAP_CHIP_DEC_RATIO 0x0201 +#define AD9694_REG_CH_MAP_CUSTOM_OFFSET 0x0228 +#define AD9694_REG_CH_MAP_FD_CTRL 0x0245 +#define AD9694_REG_CH_MAP_FD_UT_LSB 0x0247 +#define AD9694_REG_CH_MAP_FD_UT_MSB 0x0248 +#define AD9694_REG_CH_MAP_FD_LT_LSB 0x0249 +#define AD9694_REG_CH_MAP_FD_LT_MSB 0x024A +#define AD9694_REG_CH_MAP_FD_DWELL_TIME_LSB 0x024B +#define AD9694_REG_CH_MAP_FD_DWELL_TIME_MSB 0x024C +#define AD9694_REG_PAIR_MAP_SIG_MON_SYNC_CTRL 0x026F +#define AD9694_REG_CH_MAP_SIG_MON_CTRL 0x0270 +#define AD9694_REG_CH_MAP_SIG_MON_PERIOD_0 0x0271 +#define AD9694_REG_CH_MAP_SIG_MON_PERIOD_1 0x0272 +#define AD9694_REG_CH_MAP_SIG_MON_PERIOD_2 0x0273 +#define AD9694_REG_CH_MAP_SIG_MON_STAT_CTRL 0x0274 +#define AD9694_REG_CH_MAP_SIG_MON_STAT_0 0x0275 +#define AD9694_REG_CH_MAP_SIG_MON_STAT_1 0x0276 +#define AD9694_REG_CH_MAP_SIG_MON_STAT_2 0x0277 +#define AD9694_REG_CH_MAP_SIG_MON_STAT_FRAME_CNT 0x0278 +#define AD9694_REG_CH_MAP_SIG_MON_SERIAL_FRAMER_CTRL 0x0279 +#define AD9694_REG_SPORT_OVER_JESD204B_INPUT_SEL 0x027A +#define AD9694_REG_PAIR_MAP_DDC_SYNC_CTRL 0x0300 +#define AD9694_REG_PAIR_MAP_DDC_0_CTRL 0x0310 +#define AD9694_REG_PAIR_MAP_DDC_0_INPUT_SEL 0x0311 +#define AD9694_REG_PAIR_MAP_DDC_0_PHASE_INC_0 0x0314 +#define AD9694_REG_PAIR_MAP_DDC_0_PHASE_INC_1 0x0315 +#define AD9694_REG_PAIR_MAP_DDC_0_PHASE_INC_2 0x0316 +#define AD9694_REG_PAIR_MAP_DDC_0_PHASE_INC_3 0x0317 +#define AD9694_REG_PAIR_MAP_DDC_0_PHASE_INC_4 0x0318 +#define AD9694_REG_PAIR_MAP_DDC_0_PHASE_INC_5 0x031A +#define AD9694_REG_PAIR_MAP_DDC_0_PHASE_OFFSET_0 0x031D +#define AD9694_REG_PAIR_MAP_DDC_0_PHASE_OFFSET_1 0x031E +#define AD9694_REG_PAIR_MAP_DDC_0_PHASE_OFFSET_2 0x031F +#define AD9694_REG_PAIR_MAP_DDC_0_PHASE_OFFSET_3 0x0320 +#define AD9694_REG_PAIR_MAP_DDC_0_PHASE_OFFSET_4 0x0321 +#define AD9694_REG_PAIR_MAP_DDC_0_PHASE_OFFSET_5 0x0322 +#define AD9694_REG_PAIR_MAP_DDC_0_TEST_ENABLE 0x0327 +#define AD9694_REG_PAIR_MAP_DDC_1_CTRL 0x0330 +#define AD9694_REG_PAIR_MAP_DDC_1_INPUT_SEL 0x0331 +#define AD9694_REG_PAIR_MAP_DDC_1_PHASE_INC_0 0x0334 +#define AD9694_REG_PAIR_MAP_DDC_1_PHASE_INC_1 0x0335 +#define AD9694_REG_PAIR_MAP_DDC_1_PHASE_INC_2 0x0336 +#define AD9694_REG_PAIR_MAP_DDC_1_PHASE_INC_3 0x0337 +#define AD9694_REG_PAIR_MAP_DDC_1_PHASE_INC_4 0x0338 +#define AD9694_REG_PAIR_MAP_DDC_1_PHASE_INC_5 0x033A +#define AD9694_REG_PAIR_MAP_DDC_1_PHASE_OFFSET_0 0x033D +#define AD9694_REG_PAIR_MAP_DDC_1_PHASE_OFFSET_1 0x033E +#define AD9694_REG_PAIR_MAP_DDC_1_PHASE_OFFSET_2 0x033F +#define AD9694_REG_PAIR_MAP_DDC_1_PHASE_OFFSET_3 0x0340 +#define AD9694_REG_PAIR_MAP_DDC_1_PHASE_OFFSET_4 0x0341 +#define AD9694_REG_PAIR_MAP_DDC_1_PHASE_OFFSET_5 0x0342 +#define AD9694_REG_PAIR_MAP_DDC_1_TEST_ENABLE 0x0347 +#define AD9694_REG_CH_MAP_TEST_MODE_CTRL 0x0550 +#define AD9694_REG_PAIR_MAP_USER_PATTERN_1_LSB 0x0551 +#define AD9694_REG_PAIR_MAP_USER_PATTERN_1_MSB 0x0552 +#define AD9694_REG_PAIR_MAP_USER_PATTERN_2_LSB 0x0553 +#define AD9694_REG_PAIR_MAP_USER_PATTERN_2_MSB 0x0554 +#define AD9694_REG_PAIR_MAP_USER_PATTERN_3_LSB 0x0555 +#define AD9694_REG_PAIR_MAP_USER_PATTERN_3_MSB 0x0556 +#define AD9694_REG_PAIR_MAP_USER_PATTERN_4_LSB 0x0557 +#define AD9694_REG_PAIR_MAP_USER_PATTERN_4_MSB 0x0558 +#define AD9694_REG_PAIR_MAP_OUTPUT_CTRL_MODE_0 0x0559 +#define AD9694_REG_PAIR_MAP_OUTPUT_CTRL_MODE_1 0x055A +#define AD9694_REG_PAIR_MAP_OUTPUT_SAMPLE_MODE 0x0561 +#define AD9694_REG_PAIR_MAP_OUTPUT_CH_SEL 0x0564 +#define AD9694_REG_JESD204B_MAP_PLL_CTRL 0x056E +#define AD9694_REG_JESD204B_MAP_PLL_STAT 0x056F +#define AD9694_REG_JESD204B_MAP_JTX_QUICK_CONF 0x0570 +#define AD9694_REG_JESD204B_MAP_JTX_LINK_CTRL_1 0x0571 +#define AD9694_REG_JESD204B_MAP_JTX_LINK_CTRL_2 0x0572 +#define AD9694_REG_JESD204B_MAP_JTX_LINK_CTRL_3 0x0573 +#define AD9694_REG_JESD204B_MAP_JTX_LINK_CTRL_4 0x0574 +#define AD9694_REG_JESD204B_MAP_JTX_LMFC_OFFSET 0x0578 +#define AD9694_REG_JESD204B_MAP_JTX_DID_CONF 0x0580 +#define AD9694_REG_JESD204B_MAP_JTX_BID_CONF 0x0581 +#define AD9694_REG_JESD204B_MAP_JTX_LID_0_CONF 0x0583 +#define AD9694_REG_JESD204B_MAP_JTX_LID_1_CONF 0x0585 +#define AD9694_REG_JESD204B_MAP_JTX_SCR_L_CONF 0x058B +#define AD9694_REG_JESD204B_MAP_JTX_F_CONF 0x058C +#define AD9694_REG_JESD204B_MAP_JTX_K_CONF 0x058D +#define AD9694_REG_JESD204B_MAP_JTX_M_CONF 0x058E +#define AD9694_REG_JESD204B_MAP_JTX_CS_N_CONF 0x058F +#define AD9694_REG_JESD204B_MAP_JTX_SUBCLASS_CONF 0x0590 +#define AD9694_REG_JESD204B_MAP_JTX_S_CONF 0x0591 +#define AD9694_REG_JESD204B_MAP_JTX_HD_CF_CONF 0x0592 +#define AD9694_REG_JESD204B_MAP_JTX_CHECKSUM_0_CONF 0x05A0 +#define AD9694_REG_JESD204B_MAP_JTX_CHECKSUM_1_CONF 0x05A1 +#define AD9694_REG_SERDOUTX0_SERDOUTX1_LANE_PD 0x05B0 +#define AD9694_REG_JESD204B_MAP_JTX_LANE_ASSIGN_1 0x05B2 +#define AD9694_REG_JESD204B_MAP_JTX_LANE_ASSIGN_2 0x05B3 +#define AD9694_REG_JESD204B_MAP_SERD_ADJUST_0 0x05C0 +#define AD9694_REG_JESD204B_MAP_SERD_ADJUST_1 0x05C1 +#define AD9694_REG_JESD204B_SER_PE_SEL_LANE_0 0x05C4 +#define AD9694_REG_JESD204B_SER_PE_SEL_LANE_1 0x05C6 +#define AD9694_REG_LARGE_DITHER_CTRL 0x0922 +#define AD9694_REG_PLL_CAL 0x1222 +#define AD9694_REG_JESD204B_STARTUP_CIRCUIT_RST 0x1228 +#define AD9694_REG_PLL_LOSS_OF_LOCK_CTRL 0x1262 +#define AD9694_REG_DC_OFFSET_CAL_CTRL 0x0701 +#define AD9694_REG_DC_OFFSET_CAL_CTRL_2 0x073B +#define AD9694_REG_PAIR_MAP_VREF_CTRL 0x18A6 +#define AD9694_REG_EXT_VCM_BUF_CTRL_1 0x18E0 +#define AD9694_REG_EXT_VCM_BUF_CTRL_2 0x18E1 +#define AD9694_REG_EXT_VCM_BUF_CTRL_3 0x18E2 +#define AD9694_REG_EXT_VCM_BUF_CTRL 0x18E3 +#define AD9694_REG_TEMP_DIODE_EXPORT 0x18E6 +#define AD9694_REG_CH_MAP_ANALOG_INPUT_CTRL 0x1908 +#define AD9694_REG_CH_MAP_INPUT_FULLSCALE_RANGE 0x1910 +#define AD9694_REG_CH_MAP_BUF_CTRL_1 0x1A4C +#define AD9694_REG_CH_MAP_BUF_CTRL_2 0x1A4D + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ +struct ad9653_dev { + /* SPI */ + struct spi_desc *spi_desc; + struct gpio_desc *gpio_desc_pdwn; +}; + +struct ad9653_init_param { + /* SPI */ + struct spi_init_param *spi_init; + struct gpio_init_param *gpio_pdwn; +}; + +struct ad9653_config { + uint16_t addr; + uint8_t value; +}; + +/******************************************************************************/ +/************************ Functions Declarations ******************************/ +/******************************************************************************/ + +int32_t ad9653_read(struct ad9653_dev *dev, + uint16_t reg_addr, + uint8_t *reg_data); + +int32_t ad9653_write(struct ad9653_dev *dev, + uint16_t reg_addr, + uint8_t reg_data); + +int32_t ad9653_init(struct ad9653_dev **device, + const struct ad9653_init_param *init_param); + +int32_t ad9653_remove(struct ad9653_dev *dev); + +#endif diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/adc/ad9694/ad9694.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/adc/ad9694/ad9694.c new file mode 100644 index 0000000..a928a49 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/adc/ad9694/ad9694.c @@ -0,0 +1,128 @@ +/***************************************************************************//** + * @file ad9694.c + * @brief Implementation of AD9680 Driver. + * @author DBogdan (dragos.bogdan@analog.com) + ******************************************************************************** + * Copyright 2014-2016(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. + *******************************************************************************/ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include +#include +#include "ad9694.h" +#include "../../../include/error.h" + +/***************************************************************************//** + * @brief ad9694_read + *******************************************************************************/ +int32_t ad9694_read(struct ad9694_dev *dev, + uint16_t reg_addr, + uint8_t *reg_data) +{ + uint8_t tx_buf[2]; + uint8_t rx_buf[1]; + int32_t ret; + + tx_buf[0] = 0x80 | (reg_addr >> 8); + tx_buf[1] = reg_addr & 0xFF; + + + ret = spi_write_and_read_3wire(dev->spi_desc, + tx_buf, rx_buf, 2, 1); + + *reg_data = rx_buf[0]; + + return ret; +} + +/***************************************************************************//** + * @brief ad9694_write + *******************************************************************************/ +int32_t ad9694_write(struct ad9694_dev *dev, + uint16_t reg_addr, + uint8_t reg_data) +{ + uint8_t buf[3]; + + int32_t ret; + + buf[0] = reg_addr >> 8; + buf[1] = reg_addr & 0xFF; + buf[2] = reg_data; + + ret = spi_write_and_read_3wire(dev->spi_desc, + buf, NULL, 3, 0); + return ret; +} + +/***************************************************************************//** + * @brief ad9694_init + *******************************************************************************/ +int32_t ad9694_init(struct ad9694_dev **device, + const struct ad9694_init_param *init_param) +{ + int ret = SUCCESS; + struct ad9694_dev *dev; + + dev = (struct ad9694_dev *)malloc(sizeof(*dev)); + if (!dev) + return FAILURE; + + /* SPI */ + ret = spi_init(&dev->spi_desc, init_param->spi_init); + + *device = dev; + + return ret; +} + +/***************************************************************************//** + * @brief Free the resources allocated by ad9694_setup(). + * + * @param dev - The device structure. + * + * @return SUCCESS in case of success, negative error code otherwise. +*******************************************************************************/ +int32_t ad9694_remove(struct ad9694_dev *dev) +{ + int32_t ret; + + ret = spi_remove(dev->spi_desc); + + free(dev); + + return ret; +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/adc/ad9694/ad9694.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/adc/ad9694/ad9694.h new file mode 100644 index 0000000..9622c55 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/adc/ad9694/ad9694.h @@ -0,0 +1,231 @@ +/***************************************************************************//** + * @file ad9694.h + * @brief Header file of AD9694 Driver. + * @author DBogdan (dragos.bogdan@analog.com) + ******************************************************************************** + * Copyright 2014-2016(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. + *******************************************************************************/ +#ifndef AD9694_H_ +#define AD9694_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include +#include "delay.h" +#include "spi.h" + +/******************************************************************************/ +/********************** Macros and Constants Definitions **********************/ +/******************************************************************************/ +#define AD9694_REG_GLOBAL_MAP_SPI_CONF_A 0x0000 +#define AD9694_REG_GLOBAL_MAP_SPI_CONF_B 0x0001 +#define AD9694_REG_CH_MAP_CHIP_CONF 0x0002 +#define AD9694_REG_PAIR_MAP_CHIP_TYPE 0x0003 +#define AD9694_REG_PAIR_MAP_CHIP_ID_LSB 0x0004 +#define AD9694_REG_PAIR_MAP_CHIP_GRADE 0x0006 +#define AD9694_REG_PAIR_MAP_DEV_INDEX 0x0008 +#define AD9694_REG_GLOBAL_MAP_PAIR_INDEX 0x0009 +#define AD9694_REG_PAIR_MAP_SCRATCH_PAD 0x000A +#define AD9694_REG_PAIR_MAP_SPI_REVISION 0x000B +#define AD9694_REG_PAIR_MAP_VENDOR_ID_LSB 0x000C +#define AD9694_REG_PAIR_MAP_VENDOR_ID_MSB 0x000D +#define AD9694_REG_CH_MAP_CHIP_PD_PIN 0x003F +#define AD9694_REG_PAIR_MAP_CHIP_PIN_CTRL_1 0x0040 +#define AD9694_REG_PAIR_MAP_CLK_DIV_CTRL 0x0108 +#define AD9694_REG_CH_MAP_CLK_DIV_PHASE 0x0109 +#define AD9694_REG_PAIR_MAP_CLK_DIV_SYSREF_CTRL 0x010A +#define AD9694_REG_PAIR_MAP_CLK_DELAY_CTRL 0x0110 +#define AD9694_REG_CH_MAP_CLK_SUPER_FINE_DELAY 0x0111 +#define AD9694_REG_CH_MAP_CLK_FINE_DELAY 0x0112 +#define AD9694_REG_CLK_DETN_CTRL 0x011A +#define AD9694_REG_PAIR_MAP_CLK_STAT 0x011B +#define AD9694_REG_CLK_DCS_CTRL_1 0x011C +#define AD9694_REG_CLK_DCS_CTRL_2 0x011E +#define AD9694_REG_CLK_DCS_CTRL_3 0x011F +#define AD9694_REG_PAIR_MAP_SYSREF_CTRL_1 0x0120 +#define AD9694_REG_PAIR_MAP_SYSREF_CTRL_2 0x0121 +#define AD9694_REG_PAIR_MAP_SYSREF_CTRL_4 0x0123 +#define AD9694_REG_PAIR_MAP_SYSREF_STAT_1 0x0128 +#define AD9694_REG_PAIR_MAP_SYSREF_STAT_2 0x0129 +#define AD9694_REG_PAIR_MAP_SYSREF_STAT_3 0x012A +#define AD9694_REG_PAIR_MAP_CHIP_SYNC 0x01FF +#define AD9694_REG_PAIR_MAP_CHIP_MODE 0x0200 +#define AD9694_REG_PAIR_MAP_CHIP_DEC_RATIO 0x0201 +#define AD9694_REG_CH_MAP_CUSTOM_OFFSET 0x0228 +#define AD9694_REG_CH_MAP_FD_CTRL 0x0245 +#define AD9694_REG_CH_MAP_FD_UT_LSB 0x0247 +#define AD9694_REG_CH_MAP_FD_UT_MSB 0x0248 +#define AD9694_REG_CH_MAP_FD_LT_LSB 0x0249 +#define AD9694_REG_CH_MAP_FD_LT_MSB 0x024A +#define AD9694_REG_CH_MAP_FD_DWELL_TIME_LSB 0x024B +#define AD9694_REG_CH_MAP_FD_DWELL_TIME_MSB 0x024C +#define AD9694_REG_PAIR_MAP_SIG_MON_SYNC_CTRL 0x026F +#define AD9694_REG_CH_MAP_SIG_MON_CTRL 0x0270 +#define AD9694_REG_CH_MAP_SIG_MON_PERIOD_0 0x0271 +#define AD9694_REG_CH_MAP_SIG_MON_PERIOD_1 0x0272 +#define AD9694_REG_CH_MAP_SIG_MON_PERIOD_2 0x0273 +#define AD9694_REG_CH_MAP_SIG_MON_STAT_CTRL 0x0274 +#define AD9694_REG_CH_MAP_SIG_MON_STAT_0 0x0275 +#define AD9694_REG_CH_MAP_SIG_MON_STAT_1 0x0276 +#define AD9694_REG_CH_MAP_SIG_MON_STAT_2 0x0277 +#define AD9694_REG_CH_MAP_SIG_MON_STAT_FRAME_CNT 0x0278 +#define AD9694_REG_CH_MAP_SIG_MON_SERIAL_FRAMER_CTRL 0x0279 +#define AD9694_REG_SPORT_OVER_JESD204B_INPUT_SEL 0x027A +#define AD9694_REG_PAIR_MAP_DDC_SYNC_CTRL 0x0300 +#define AD9694_REG_PAIR_MAP_DDC_0_CTRL 0x0310 +#define AD9694_REG_PAIR_MAP_DDC_0_INPUT_SEL 0x0311 +#define AD9694_REG_PAIR_MAP_DDC_0_PHASE_INC_0 0x0314 +#define AD9694_REG_PAIR_MAP_DDC_0_PHASE_INC_1 0x0315 +#define AD9694_REG_PAIR_MAP_DDC_0_PHASE_INC_2 0x0316 +#define AD9694_REG_PAIR_MAP_DDC_0_PHASE_INC_3 0x0317 +#define AD9694_REG_PAIR_MAP_DDC_0_PHASE_INC_4 0x0318 +#define AD9694_REG_PAIR_MAP_DDC_0_PHASE_INC_5 0x031A +#define AD9694_REG_PAIR_MAP_DDC_0_PHASE_OFFSET_0 0x031D +#define AD9694_REG_PAIR_MAP_DDC_0_PHASE_OFFSET_1 0x031E +#define AD9694_REG_PAIR_MAP_DDC_0_PHASE_OFFSET_2 0x031F +#define AD9694_REG_PAIR_MAP_DDC_0_PHASE_OFFSET_3 0x0320 +#define AD9694_REG_PAIR_MAP_DDC_0_PHASE_OFFSET_4 0x0321 +#define AD9694_REG_PAIR_MAP_DDC_0_PHASE_OFFSET_5 0x0322 +#define AD9694_REG_PAIR_MAP_DDC_0_TEST_ENABLE 0x0327 +#define AD9694_REG_PAIR_MAP_DDC_1_CTRL 0x0330 +#define AD9694_REG_PAIR_MAP_DDC_1_INPUT_SEL 0x0331 +#define AD9694_REG_PAIR_MAP_DDC_1_PHASE_INC_0 0x0334 +#define AD9694_REG_PAIR_MAP_DDC_1_PHASE_INC_1 0x0335 +#define AD9694_REG_PAIR_MAP_DDC_1_PHASE_INC_2 0x0336 +#define AD9694_REG_PAIR_MAP_DDC_1_PHASE_INC_3 0x0337 +#define AD9694_REG_PAIR_MAP_DDC_1_PHASE_INC_4 0x0338 +#define AD9694_REG_PAIR_MAP_DDC_1_PHASE_INC_5 0x033A +#define AD9694_REG_PAIR_MAP_DDC_1_PHASE_OFFSET_0 0x033D +#define AD9694_REG_PAIR_MAP_DDC_1_PHASE_OFFSET_1 0x033E +#define AD9694_REG_PAIR_MAP_DDC_1_PHASE_OFFSET_2 0x033F +#define AD9694_REG_PAIR_MAP_DDC_1_PHASE_OFFSET_3 0x0340 +#define AD9694_REG_PAIR_MAP_DDC_1_PHASE_OFFSET_4 0x0341 +#define AD9694_REG_PAIR_MAP_DDC_1_PHASE_OFFSET_5 0x0342 +#define AD9694_REG_PAIR_MAP_DDC_1_TEST_ENABLE 0x0347 +#define AD9694_REG_CH_MAP_TEST_MODE_CTRL 0x0550 +#define AD9694_REG_PAIR_MAP_USER_PATTERN_1_LSB 0x0551 +#define AD9694_REG_PAIR_MAP_USER_PATTERN_1_MSB 0x0552 +#define AD9694_REG_PAIR_MAP_USER_PATTERN_2_LSB 0x0553 +#define AD9694_REG_PAIR_MAP_USER_PATTERN_2_MSB 0x0554 +#define AD9694_REG_PAIR_MAP_USER_PATTERN_3_LSB 0x0555 +#define AD9694_REG_PAIR_MAP_USER_PATTERN_3_MSB 0x0556 +#define AD9694_REG_PAIR_MAP_USER_PATTERN_4_LSB 0x0557 +#define AD9694_REG_PAIR_MAP_USER_PATTERN_4_MSB 0x0558 +#define AD9694_REG_PAIR_MAP_OUTPUT_CTRL_MODE_0 0x0559 +#define AD9694_REG_PAIR_MAP_OUTPUT_CTRL_MODE_1 0x055A +#define AD9694_REG_PAIR_MAP_OUTPUT_SAMPLE_MODE 0x0561 +#define AD9694_REG_PAIR_MAP_OUTPUT_CH_SEL 0x0564 +#define AD9694_REG_JESD204B_MAP_PLL_CTRL 0x056E +#define AD9694_REG_JESD204B_MAP_PLL_STAT 0x056F +#define AD9694_REG_JESD204B_MAP_JTX_QUICK_CONF 0x0570 +#define AD9694_REG_JESD204B_MAP_JTX_LINK_CTRL_1 0x0571 +#define AD9694_REG_JESD204B_MAP_JTX_LINK_CTRL_2 0x0572 +#define AD9694_REG_JESD204B_MAP_JTX_LINK_CTRL_3 0x0573 +#define AD9694_REG_JESD204B_MAP_JTX_LINK_CTRL_4 0x0574 +#define AD9694_REG_JESD204B_MAP_JTX_LMFC_OFFSET 0x0578 +#define AD9694_REG_JESD204B_MAP_JTX_DID_CONF 0x0580 +#define AD9694_REG_JESD204B_MAP_JTX_BID_CONF 0x0581 +#define AD9694_REG_JESD204B_MAP_JTX_LID_0_CONF 0x0583 +#define AD9694_REG_JESD204B_MAP_JTX_LID_1_CONF 0x0585 +#define AD9694_REG_JESD204B_MAP_JTX_SCR_L_CONF 0x058B +#define AD9694_REG_JESD204B_MAP_JTX_F_CONF 0x058C +#define AD9694_REG_JESD204B_MAP_JTX_K_CONF 0x058D +#define AD9694_REG_JESD204B_MAP_JTX_M_CONF 0x058E +#define AD9694_REG_JESD204B_MAP_JTX_CS_N_CONF 0x058F +#define AD9694_REG_JESD204B_MAP_JTX_SUBCLASS_CONF 0x0590 +#define AD9694_REG_JESD204B_MAP_JTX_S_CONF 0x0591 +#define AD9694_REG_JESD204B_MAP_JTX_HD_CF_CONF 0x0592 +#define AD9694_REG_JESD204B_MAP_JTX_CHECKSUM_0_CONF 0x05A0 +#define AD9694_REG_JESD204B_MAP_JTX_CHECKSUM_1_CONF 0x05A1 +#define AD9694_REG_SERDOUTX0_SERDOUTX1_LANE_PD 0x05B0 +#define AD9694_REG_JESD204B_MAP_JTX_LANE_ASSIGN_1 0x05B2 +#define AD9694_REG_JESD204B_MAP_JTX_LANE_ASSIGN_2 0x05B3 +#define AD9694_REG_JESD204B_MAP_SERD_ADJUST_0 0x05C0 +#define AD9694_REG_JESD204B_MAP_SERD_ADJUST_1 0x05C1 +#define AD9694_REG_JESD204B_SER_PE_SEL_LANE_0 0x05C4 +#define AD9694_REG_JESD204B_SER_PE_SEL_LANE_1 0x05C6 +#define AD9694_REG_LARGE_DITHER_CTRL 0x0922 +#define AD9694_REG_PLL_CAL 0x1222 +#define AD9694_REG_JESD204B_STARTUP_CIRCUIT_RST 0x1228 +#define AD9694_REG_PLL_LOSS_OF_LOCK_CTRL 0x1262 +#define AD9694_REG_DC_OFFSET_CAL_CTRL 0x0701 +#define AD9694_REG_DC_OFFSET_CAL_CTRL_2 0x073B +#define AD9694_REG_PAIR_MAP_VREF_CTRL 0x18A6 +#define AD9694_REG_EXT_VCM_BUF_CTRL_1 0x18E0 +#define AD9694_REG_EXT_VCM_BUF_CTRL_2 0x18E1 +#define AD9694_REG_EXT_VCM_BUF_CTRL_3 0x18E2 +#define AD9694_REG_EXT_VCM_BUF_CTRL 0x18E3 +#define AD9694_REG_TEMP_DIODE_EXPORT 0x18E6 +#define AD9694_REG_CH_MAP_ANALOG_INPUT_CTRL 0x1908 +#define AD9694_REG_CH_MAP_INPUT_FULLSCALE_RANGE 0x1910 +#define AD9694_REG_CH_MAP_BUF_CTRL_1 0x1A4C +#define AD9694_REG_CH_MAP_BUF_CTRL_2 0x1A4D + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ +struct ad9694_dev { + /* SPI */ + struct spi_desc *spi_desc; +}; + +struct ad9694_init_param { + /* SPI */ + struct spi_init_param *spi_init; +}; + +struct ad9694_config { + uint16_t addr; + uint8_t value; +}; + +/******************************************************************************/ +/************************ Functions Declarations ******************************/ +/******************************************************************************/ + +int32_t ad9694_read(struct ad9694_dev *dev, + uint16_t reg_addr, + uint8_t *reg_data); + +int32_t ad9694_write(struct ad9694_dev *dev, + uint16_t reg_addr, + uint8_t reg_data); + +int32_t ad9694_init(struct ad9694_dev **device, + const struct ad9694_init_param *init_param); + +int32_t ad9694_remove(struct ad9694_dev *dev); + +#endif diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/api/gpio.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/api/gpio.c new file mode 100644 index 0000000..587a461 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/api/gpio.c @@ -0,0 +1,241 @@ +/***************************************************************************//** + * @file gpio.c + * @brief Implementation of the GPIO Interface + * @author Antoniu Miclaus (antoniu.miclaus@analog.com) +******************************************************************************** + * Copyright 2020(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +#include +#include "gpio.h" +#include +#include "error.h" + +/******************************************************************************/ +/************************ Functions Definitions *******************************/ +/******************************************************************************/ + +/** + * @brief Obtain the GPIO decriptor. + * @param desc - The GPIO descriptor. + * @param param - GPIO Initialization parameters. + * @return 0 in case of success, -1 otherwise. + */ +int32_t gpio_get(struct gpio_desc **desc, + const struct gpio_init_param *param) +{ + int32_t ret; + + if (!param || !param->platform_ops) + return -EINVAL; + + if (!param->platform_ops->gpio_ops_get) + return -ENOSYS; + + ret = param->platform_ops->gpio_ops_get(desc, param); + if (ret) + return ret; + + (*desc)->platform_ops = param->platform_ops; + + return 0; +} + +/** + * @brief Get the value of an optional GPIO. + * @param desc - The GPIO descriptor. + * @param param - GPIO Initialization parameters. + * @return 0 in case of success, -1 otherwise. + */ +int32_t gpio_get_optional(struct gpio_desc **desc, + const struct gpio_init_param *param) +{ + int32_t ret; + + if (!param || (param->number == -1)) { + *desc = NULL; + return 0; + } + + if (!param->platform_ops) + return -EINVAL; + + if (!param->platform_ops->gpio_ops_get_optional) + return -ENOSYS; + + ret = param->platform_ops->gpio_ops_get_optional(desc, param); + if (ret) + return ret; + + (*desc)->platform_ops = param->platform_ops; + + return 0; +} +/** + * @brief Free the resources allocated by gpio_get(). + * @param desc - The GPIO descriptor. + * @return 0 in case of success, -1 otherwise. + */ +int32_t gpio_remove(struct gpio_desc *desc) +{ + if (desc) { + if (!desc->platform_ops) + return -EINVAL; + + if (!desc->platform_ops->gpio_ops_remove) + return -ENOSYS; + + return desc->platform_ops->gpio_ops_remove(desc); + } + + return 0; +} + +/** + * @brief Enable the input direction of the specified GPIO. + * @param desc - The GPIO descriptor. + * @return 0 in case of success, -1 otherwise. + */ +int32_t gpio_direction_input(struct gpio_desc *desc) +{ + if (desc) { + if (!desc->platform_ops) + return -EINVAL; + + if (!desc->platform_ops->gpio_ops_direction_input) + return -ENOSYS; + + return desc->platform_ops->gpio_ops_direction_input(desc); + } + + return 0; +} + +/** + * @brief Enable the output direction of the specified GPIO. + * @param desc - The GPIO descriptor. + * @param value - The value. + * Example: GPIO_HIGH + * GPIO_LOW + * @return 0 in case of success, -1 otherwise. + */ +int32_t gpio_direction_output(struct gpio_desc *desc, + uint8_t value) +{ + if (desc) { + if (!desc->platform_ops) + return -EINVAL; + + if (!desc->platform_ops->gpio_ops_direction_output) + return -ENOSYS; + + return desc->platform_ops-> + gpio_ops_direction_output(desc, value); + } + + return 0; +} + +/** + * @brief Get the direction of the specified GPIO. + * @param desc - The GPIO descriptor. + * @param direction - The direction. + * Example: GPIO_OUT + * GPIO_IN + * @return 0 in case of success, -1 otherwise. + */ +int32_t gpio_get_direction(struct gpio_desc *desc, + uint8_t *direction) +{ + if (desc) { + if (!desc->platform_ops) + return -EINVAL; + + if (!desc->platform_ops->gpio_ops_get_direction) + return -ENOSYS; + + return desc->platform_ops-> + gpio_ops_get_direction(desc, direction); + } + + return 0; +} + +/** + * @brief Set the value of the specified GPIO. + * @param desc - The GPIO descriptor. + * @param value - The value. + * Example: GPIO_HIGH + * GPIO_LOW + * @return 0 in case of success, -1 otherwise. + */ +int32_t gpio_set_value(struct gpio_desc *desc, + uint8_t value) +{ + if (desc) { + if (!desc->platform_ops) + return -EINVAL; + + if (!desc->platform_ops->gpio_ops_set_value) + return -ENOSYS; + + return desc->platform_ops->gpio_ops_set_value(desc, value); + } + + return 0; +} + +/** + * @brief Get the value of the specified GPIO. + * @param desc - The GPIO descriptor. + * @param value - The value. + * Example: GPIO_HIGH + * GPIO_LOW + * @return 0 in case of success, -1 otherwise. + */ +int32_t gpio_get_value(struct gpio_desc *desc, + uint8_t *value) +{ + if (desc) { + if (!desc->platform_ops) + return -EINVAL; + + if (!desc->platform_ops->gpio_ops_set_value) + return -ENOSYS; + + return desc->platform_ops->gpio_ops_get_value(desc, value); + } + + return 0; +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/api/i2c.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/api/i2c.c new file mode 100644 index 0000000..0ad78e0 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/api/i2c.c @@ -0,0 +1,135 @@ +/***************************************************************************//** + * @file i2c.c + * @brief Implementation of the I2C Interface + * @author Antoniu Miclaus (antoniu.miclaus@analog.com) +******************************************************************************** + * Copyright 2020(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +#include +#include "i2c.h" +#include +#include "error.h" + +/** + * @brief Initialize the I2C communication peripheral. + * @param desc - The I2C descriptor. + * @param param - The structure that contains the I2C parameters. + * @return 0 in case of success, -1 otherwise. + */ +int32_t i2c_init(struct i2c_desc **desc, + const struct i2c_init_param *param) +{ + int32_t ret; + + if (!param || !param->platform_ops) + return -EINVAL; + + if (!param->platform_ops->i2c_ops_init) + return -ENOSYS; + + ret = param->platform_ops->i2c_ops_init(desc, param); + if (ret) + return ret; + + (*desc)->platform_ops = param->platform_ops; + + return 0; +} + +/** + * @brief Free the resources allocated by i2c_init(). + * @param desc - The I2C descriptor. + * @return 0 in case of success, -1 otherwise. + */ +int32_t i2c_remove(struct i2c_desc *desc) +{ + if (!desc || !desc->platform_ops) + return -EINVAL; + + if (!desc->platform_ops->i2c_ops_remove) + return -ENOSYS; + + return desc->platform_ops->i2c_ops_remove(desc); +} + +/** + * @brief I2C Write data to slave device. + * @param desc - The I2C descriptor. + * @param data - The buffer with the transmitted/received data. + * @param bytes_number - Number of bytes to write. + * @param stop_bit - Stop conditional control. + * Example: 0 - A stop condition will not be generated. + * 1 - A stop condition will be generated. + * @return 0 in case of success, -1 otherwise. + */ +int32_t i2c_write(struct i2c_desc *desc, + uint8_t *data, + uint8_t bytes_number, + uint8_t stop_bit) +{ + if (!desc || !desc->platform_ops) + return -EINVAL; + + if (!desc->platform_ops->i2c_ops_write) + return -ENOSYS; + + return desc->platform_ops->i2c_ops_write(desc, data, bytes_number, + stop_bit); +} + +/** + * @brief I2C Read data from slave device. + * @param desc - The i2c descriptor. + * @param data - The buffer with the transmitted/received data. + * @param bytes_number - Number of bytes to read. + * @param stop_bit - Stop conditional control. + * Example: 0 - A stop condition will not be generated. + * 1 - A stop condition will be generated. + * @return 0 in case of success, -1 otherwise. + */ +int32_t i2c_read(struct i2c_desc *desc, + uint8_t *data, + uint8_t bytes_number, + uint8_t stop_bit) +{ + if (!desc || !desc->platform_ops) + return -EINVAL; + + if (!desc->platform_ops->i2c_ops_read) + return -ENOSYS; + + return desc->platform_ops->i2c_ops_read(desc, data, bytes_number, + stop_bit); +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/api/irq.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/api/irq.c new file mode 100644 index 0000000..6a962c4 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/api/irq.c @@ -0,0 +1,229 @@ +/***************************************************************************//** + * @file irq.c + * @brief Implementation of the IRQ Interface + * @author Andrei Porumb (andrei.porumb@analog.com) +******************************************************************************** + * Copyright 2021(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +#include +#include +#include "irq.h" +#include "error.h" + +/** + * @brief Initialize the IRQ interrupts. + * @param desc - The IRQ descriptor. + * @param param - The structure that contains the IRQ parameters. + * @return 0 in case of success, -1 otherwise. + */ +int32_t irq_ctrl_init(struct irq_ctrl_desc **desc, + const struct irq_init_param *param) +{ + int32_t ret; + + if (!param || !param->platform_ops) + return -EINVAL; + + if (!param->platform_ops->init) + return -ENOSYS; + + ret = param->platform_ops->init(desc, param); + if (ret) + return ret; + + (*desc)->platform_ops = param->platform_ops; + + return 0; +} + +/** + * @brief Free the resources allocated by irq_ctrl_init(). + * @param desc - The SPI descriptor. + * @return 0 in case of success, -1 otherwise. + */ +int32_t irq_ctrl_remove(struct irq_ctrl_desc *desc) +{ + if (!desc || !desc->platform_ops) + return -EINVAL; + + if (!desc->platform_ops->remove) + return -ENOSYS; + + return desc->platform_ops->remove(desc); +} + +/** + * @brief Register a callback to handle the irq events. + * @param desc - The IRQ controller descriptor. + * @param irq_id - Interrupt identifier. + * @param callback - Callback descriptor - platform specific type. + * @return SUCCESS in case of success, FAILURE otherwise. + */ +int32_t irq_register_callback(struct irq_ctrl_desc *desc, + uint32_t irq_id, + struct callback_desc *callback) +{ + if (!desc || !desc->platform_ops) + return -EINVAL; + + if (!desc->platform_ops->register_callback) + return -ENOSYS; + + return desc->platform_ops->register_callback(desc, irq_id, callback); +} + +/** + * @brief Unregisters a generic IRQ handling function. + * @param desc - The IRQ controller descriptor. + * @param irq_id - Interrupt identifier. + * @param callback - Callback descriptor - platform specific type. + * @return SUCCESS in case of success, FAILURE otherwise. + */ +int32_t irq_unregister_callback(struct irq_ctrl_desc *desc, + uint32_t irq_id, + struct callback_desc *callback) +{ + if (!desc || !desc->platform_ops) + return -EINVAL; + + if (!desc->platform_ops->unregister_callback) + return -ENOSYS; + + return desc->platform_ops->unregister_callback(desc, irq_id, callback); +} + +/** + * @brief Enable global interrupts. + * @return 0 in case of success, -1 otherwise. + */ +int32_t irq_global_enable(struct irq_ctrl_desc *desc) +{ + if (!desc || !desc->platform_ops) + return -EINVAL; + + if (!desc->platform_ops->global_enable) + return -ENOSYS; + + return desc->platform_ops->global_enable(desc); +} + +/** + * @brief Disable global interrupts. + * @return 0 in case of success, -1 otherwise. + */ +int32_t irq_global_disable(struct irq_ctrl_desc *desc) +{ + if (!desc || !desc->platform_ops) + return -EINVAL; + + if (!desc->platform_ops->global_disable) + return -ENOSYS; + + return desc->platform_ops->global_disable(desc); +} + +/** + * @brief Set interrupt trigger level. + * @param desc - The IRQ controller descriptor. + * @param irq_id - Interrupt identifier. + * @param trig - New trigger level for the interrupt. + * @return 0 in case of success, -1 otherwise. + */ +int32_t irq_trigger_level_set(struct irq_ctrl_desc *desc, + uint32_t irq_id, + enum irq_trig_level trig) +{ + if (!desc || !desc->platform_ops) + return -EINVAL; + + if(!desc->platform_ops->trigger_level_set) + return -ENOSYS; + + return desc->platform_ops->trigger_level_set(desc, irq_id, trig); +} + +/** + * @brief Enable specific interrupt. + * @param desc - The IRQ controller descriptor. + * @param irq_id - Interrupt identifier. + * @return 0 in case of success, -1 otherwise. + */ +int32_t irq_enable(struct irq_ctrl_desc *desc, uint32_t irq_id) +{ + if (!desc || !desc->platform_ops) + return -EINVAL; + + if (!desc->platform_ops->enable) + return -ENOSYS; + + return desc->platform_ops->enable(desc, irq_id); +} + +/** + * @brief Disable specific interrupt. + * @param desc - The IRQ controller descriptor. + * @param irq_id - Interrupt identifier. + * @return 0 in case of success, -1 otherwise. + */ +int32_t irq_disable(struct irq_ctrl_desc *desc, uint32_t irq_id) +{ + if (!desc || !desc->platform_ops) + return -EINVAL; + + if (!desc->platform_ops->disable) + return -ENOSYS; + + return desc->platform_ops->disable(desc, irq_id); +} + +/** + * @brief Set the priority for an interrupt. + * @param desc - The IRQ controller descriptor. + * @param irq_id - Interrupt identifier. + * @param priority_level - The priority level of the interrupt. + * @return 0 in case of success, negative errno error codes. + */ +int32_t irq_set_priority(struct irq_ctrl_desc *desc, + uint32_t irq_id, + uint32_t priority_level) +{ + if (!desc || !desc->platform_ops) + return -EINVAL; + + if (!desc->platform_ops->set_priority) + return -ENOSYS; + + return desc->platform_ops->set_priority(desc, irq_id, priority_level); +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/api/spi.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/api/spi.c new file mode 100644 index 0000000..bddea5d --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/api/spi.c @@ -0,0 +1,161 @@ +/***************************************************************************//** + * @file spi.c + * @brief Implementation of the SPI Interface + * @author Antoniu Miclaus (antoniu.miclaus@analog.com) +******************************************************************************** + * Copyright 2020(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +#include +#include "spi.h" +#include +#include "error.h" + +/** + * @brief Initialize the SPI communication peripheral. + * @param desc - The SPI descriptor. + * @param param - The structure that contains the SPI parameters. + * @return 0 in case of success, -1 otherwise. + */ +int32_t spi_init(struct spi_desc **desc, + const struct spi_init_param *param) +{ + int32_t ret; + + if (!param || !param->platform_ops) + return -EINVAL; + + if (!param->platform_ops->init) + return -ENOSYS; + + ret = param->platform_ops->init(desc, param); + if (ret) + return ret; + + (*desc)->platform_ops = param->platform_ops; + (*desc)->parent = param->parent; + + return 0; +} + +/** + * @brief Free the resources allocated by spi_init(). + * @param desc - The SPI descriptor. + * @return 0 in case of success, -1 otherwise. + */ +int32_t spi_remove(struct spi_desc *desc) +{ + if (!desc || !desc->platform_ops) + return -EINVAL; + + if (!desc->platform_ops->remove) + return -ENOSYS; + + return desc->platform_ops->remove(desc); +} + +/** + * @brief Write and read data to/from SPI. + * @param desc - The SPI descriptor. + * @param data - The buffer with the transmitted/received data. + * @param bytes_number - Number of bytes to write/read. + * @return 0 in case of success, -1 otherwise. + */ +int32_t spi_write_and_read(struct spi_desc *desc, + uint8_t *data, + uint16_t bytes_number) +{ + if (!desc || !desc->platform_ops) + return -EINVAL; + + if (!desc->platform_ops->write_and_read) + return -ENOSYS; + + return desc->platform_ops->write_and_read(desc, data, bytes_number); +} + +/** + * @brief Write and read data to/from SPI in 3 wire mode. + * @param desc - The SPI descriptor. + * @param write_data - The buffer with the transmitted data. + * @param read_data - The buffer with the received data. + * @param write_bytes_number - Number of bytes to write. + * @param read_bytes_number - Number of bytes to read. + * @return SUCCESS in case of success, FAILURE otherwise. + */ +int32_t spi_write_and_read_3wire(struct spi_desc *desc, + uint8_t *write_data, + uint8_t *read_data, + uint16_t write_bytes_number, + uint16_t read_bytes_number) +{ + if (!desc || !desc->platform_ops) + return -EINVAL; + + if (!desc->platform_ops->write_and_read_3wire) + return -ENOSYS; + return desc->platform_ops->write_and_read_3wire(desc, write_data, read_data, + write_bytes_number, read_bytes_number); +} +/** + * @brief Iterate over head list and send all spi messages + * @param desc - The SPI descriptor. + * @param msgs - Array of messages. + * @param len - Number of messages in the array. + * @return 0 in case of success, negativ error code otherwise. + */ +int32_t spi_transfer(struct spi_desc *desc, + struct spi_msg *msgs, + uint32_t len) +{ + int32_t ret; + uint32_t i; + + if (!desc || !desc->platform_ops) + return -EINVAL; + + if (desc->platform_ops->transfer) + return desc->platform_ops->transfer(desc, msgs, len); + + for (i = 0; i < len; i++) { + if (msgs[i].rx_buff != msgs[i].tx_buff || !msgs[i].tx_buff) + return -EINVAL; + ret = spi_write_and_read(desc, msgs[i].rx_buff, + msgs[i].bytes_number); + if (IS_ERR_VALUE(ret)) + return ret; + } + + return 0; +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/api/timer.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/api/timer.c new file mode 100644 index 0000000..b33d88d --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/api/timer.c @@ -0,0 +1,211 @@ +/***************************************************************************//** +* @file timer.c +* @brief Implementation of the timer Interface +* @author RBolboac (ramona.bolboaca@analog.com) +******************************************************************************** +* Copyright 2022(c) Analog Devices, Inc. +* +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* - Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* - 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. +* - Neither the name of Analog Devices, Inc. nor the names of its +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* - The use of this software may or may not infringe the patent rights +* of one or more patent holders. This license does not release you +* from the requirement that you obtain separate licenses from these +* patent holders to use this software. +* - Use of the software either in source or binary form, must be run +* on or directly connected to an Analog Devices Inc. component. +* +* THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR IMPLIED +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, MERCHANTABILITY +* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include "error.h" +#include "timer.h" + +/******************************************************************************/ +/************************ Functions Definitions *******************************/ +/******************************************************************************/ +/** + * @brief Initialize hardware timer and the handler structure associated with + * it. + * @param [out] desc - Pointer to the reference of the device handler. + * @param [in] param - Initialization structure. + * @return 0 in case of success, negative error code otherwise + */ +int32_t timer_init(struct timer_desc **desc, + struct timer_init_param *param) +{ + int32_t ret; + + if (!param || !param->platform_ops) + return -EINVAL; + + if (!param->platform_ops->init) + return -ENOSYS; + + ret = param->platform_ops->init(desc, param); + if (ret) + return ret; + + (*desc)->platform_ops = param->platform_ops; + + return 0; +} + +/** + * @brief Free the memory allocated by timer_init(). + * @param [in] desc - Pointer to the device handler. + * @return 0 in case of success, negative error code otherwise + */ +int32_t timer_remove(struct timer_desc *desc) +{ + if (!desc || !desc->platform_ops) + return -EINVAL; + + if (!desc->platform_ops->remove) + return -ENOSYS; + + return desc->platform_ops->remove(desc); +} + +/** + * @brief Start a timer. + * @param [in] desc - Pointer to the device handler. + * @return 0 in case of success, negative error code otherwise + */ +int32_t timer_start(struct timer_desc *desc) +{ + if (!desc || !desc->platform_ops) + return -EINVAL; + + if (!desc->platform_ops->start) + return -ENOSYS; + + return desc->platform_ops->start(desc); +} + +/** + * @brief Stop a timer from counting. + * @param [in] desc - Pointer to the device handler. + * @return 0 in case of success, negative error code otherwise + */ +int32_t timer_stop(struct timer_desc *desc) +{ + if (!desc || !desc->platform_ops) + return -EINVAL; + + if (!desc->platform_ops->stop) + return -ENOSYS; + + return desc->platform_ops->stop(desc); +} + +/** + * @brief Get the value of the counter register for the timer. + * @param [in] desc - Pointer to the device handler. + * @param [out] counter - Pointer to the counter value. + * @return 0 in case of success, error code otherwise. + */ +int32_t timer_counter_get(struct timer_desc *desc, + uint32_t *counter) +{ + if (!desc || !desc->platform_ops || !counter) + return -EINVAL; + + if (!desc->platform_ops->counter_get) + return -ENOSYS; + + return desc->platform_ops->counter_get(desc, counter); +} + +/** + * @brief Set the timer counter register value. + * @param [in] desc - Pointer to the device handler. + * @param [in] new_val - The new value of the counter register. + * @return 0 in case of success, error code otherwise. + */ +int32_t timer_counter_set(struct timer_desc *desc, uint32_t new_val) +{ + if (!desc || !desc->platform_ops) + return -EINVAL; + + if (!desc->platform_ops->counter_set) + return -ENOSYS; + + return desc->platform_ops->counter_set(desc, new_val); +} + +/** + * @brief Get the timer clock frequency. + * @param [in] desc - Pointer to the device handler. + * @param [out] freq_hz - The value in Hz of the timer clock. + * @return 0 in case of success, error code otherwise. + */ +int32_t timer_count_clk_get(struct timer_desc *desc, + uint32_t *freq_hz) +{ + if (!desc || !desc->platform_ops || !freq_hz) + return -EINVAL; + + if (!desc->platform_ops->count_clk_get) + return -ENOSYS; + + return desc->platform_ops->count_clk_get(desc, freq_hz); +} + +/** + * @brief Set the timer clock frequency. + * @param [in] desc - Pointer to the device handler. + * @param [in] freq_hz - The value in Hz of the new timer clock. + * @return 0 in case of success, error code otherwise. + */ +int32_t timer_count_clk_set(struct timer_desc *desc, + uint32_t freq_hz) +{ + if (!desc || !desc->platform_ops) + return -EINVAL; + + if (!desc->platform_ops->count_clk_set) + return -ENOSYS; + + return desc->platform_ops->count_clk_set(desc, freq_hz); +} + +/** + * @brief Get the elapsed time in nsec for the timer. + * @param [in] desc - Pointer to the device handler. + * @param [in] elapsed_time - The elapsed time in nsec. + * @return 0 in case of success, error code otherwise. + */ +int32_t timer_get_elapsed_time_nsec(struct timer_desc *desc, + uint64_t *elapsed_time) +{ + if (!desc || !desc->platform_ops) + return -EINVAL; + + if (!desc->platform_ops->get_elapsed_time_nsec) + return -ENOSYS; + + return desc->platform_ops->get_elapsed_time_nsec(desc, elapsed_time); +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/api/trng.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/api/trng.c new file mode 100644 index 0000000..de093c3 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/api/trng.c @@ -0,0 +1,111 @@ +/***************************************************************************//** + * @file trng.c + * @brief Implementation of the TRNG Interface + * @author Antoniu Miclaus (antoniu.miclaus@analog.com) +******************************************************************************** + * Copyright 2023(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include +#include "trng.h" +#include +#include "error.h" + +/******************************************************************************/ +/************************ Functions Definitions *******************************/ +/******************************************************************************/ + +/** + * @brief Initialize the TRNG. + * @param desc - The TRNG descriptor. + * @param param - The structure that contains the TRNG parameters. + * @return 0 in case of success, -1 otherwise. + */ +int trng_init(struct trng_desc **desc, + const struct trng_init_param *param) +{ + int ret; + + if (!param || !param->platform_ops) + return -EINVAL; + + if (!param->platform_ops->init) + return -ENOSYS; + + ret = param->platform_ops->init(desc, param); + if (ret) + return ret; + + (*desc)->platform_ops = param->platform_ops; + + return 0; +} + +/** + * @brief Free the resources allocated by trng_init(). + * @param desc - The TRNG descriptor. + * @return 0 in case of success, -1 otherwise. + */ +int trng_remove(struct trng_desc *desc) +{ + if (!desc || !desc->platform_ops) + return -EINVAL; + + if (!desc->platform_ops->remove) + return -ENOSYS; + + return desc->platform_ops->remove(desc); +} + +/** + * @brief Fill buffer with rng data. + * @param desc - The TRNG descriptor. + * @param buff - Buffer to be filled + * @param len - Size of the buffer + * @return 0 in case of success, -1 otherwise. + */ +int trng_fill_buffer(struct trng_desc *desc, uint8_t *buff, + uint32_t len) +{ + if (!desc || !desc->platform_ops) + return -EINVAL; + + if (!desc->platform_ops->fill_buffer) + return -ENOSYS; + + return desc->platform_ops->fill_buffer(desc, buff, len); +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/axi_core/axi_dmac/axi_dmac.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/axi_core/axi_dmac/axi_dmac.c new file mode 100644 index 0000000..32388ae --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/axi_core/axi_dmac/axi_dmac.c @@ -0,0 +1,547 @@ +/******************************************************************************* + * @file axi_dmac.c + * @brief Driver for the Analog Devices AXI-DMAC core. + * @author DBogdan (dragos.bogdan@analog.com) +******************************************************************************** + * Copyright 2018(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include +#include +#include +#include +#include "axi_io.h" +#include "error.h" +#include "delay.h" +#include "axi_dmac.h" + +/******************************************************************************* + * @brief ISR for dev to mem DMA transfer. It computes the next transfer params, + * if any, and sets the transfer structure fields accordingly. + * + * @param instance - the instance that triggered the ISR. + * + * @return None. +*******************************************************************************/ +void axi_dmac_dev_to_mem_isr(void *instance) +{ + struct axi_dmac *dmac = (struct axi_dmac *)instance; + uint32_t burst_size; + uint32_t reg_val; + + /* Get interrupt sources and clear interrupts. */ + axi_dmac_read(dmac, AXI_DMAC_REG_IRQ_PENDING, ®_val); + axi_dmac_write(dmac, AXI_DMAC_REG_IRQ_PENDING, reg_val); + + if (reg_val & AXI_DMAC_IRQ_SOT) { + if (dmac->remaining_size) { + /* See if remaining size is bigger than max transfer size and + * set burst size. */ + if (dmac->remaining_size > dmac->max_length) { + burst_size = dmac->max_length; + } else { + burst_size = dmac->remaining_size - 1; + } + + /* The current transfer was started; set up the new one. */ + axi_dmac_write(dmac, AXI_DMAC_REG_DEST_ADDRESS, dmac->next_dest_addr); + axi_dmac_write(dmac, AXI_DMAC_REG_DEST_STRIDE, 0x0); + axi_dmac_write(dmac, AXI_DMAC_REG_X_LENGTH, burst_size); + axi_dmac_write(dmac, AXI_DMAC_REG_Y_LENGTH, 0x0); + + /* Compute size of the next transfer. */ + dmac->remaining_size = dmac->remaining_size - (burst_size + 1); + /* Address must advance with +1 since the DMAC writes X_LENGTH+1. */ + dmac->next_dest_addr = dmac->next_dest_addr + (burst_size + 1); + + /* Trigger the next transfer. */ + axi_dmac_write(dmac, AXI_DMAC_REG_TRANSFER_SUBMIT, AXI_DMAC_TRANSFER_SUBMIT); + } + } + if (reg_val & AXI_DMAC_IRQ_EOT) { + if (!dmac->remaining_size) { + dmac->transfer.transfer_done = true; + dmac->next_dest_addr = 0; + } + } +} + +/******************************************************************************* + * @brief ISR for mem DMA to dev transfer. It computes the next transfer params, + * if any, and sets the transfer structure fields accordingly. + * + * @param instance - the instance that triggered the ISR. + * + * @return None. +*******************************************************************************/ +void axi_dmac_mem_to_dev_isr(void *instance) +{ + struct axi_dmac *dmac = (struct axi_dmac *)instance; + uint32_t burst_size; + uint32_t reg_val; + + /* Get interrupt sources and clear interrupts. */ + axi_dmac_read(dmac, AXI_DMAC_REG_IRQ_PENDING, ®_val); + axi_dmac_write(dmac, AXI_DMAC_REG_IRQ_PENDING, reg_val); + + if (reg_val & AXI_DMAC_IRQ_SOT) { + if ((dmac->transfer.cyclic == CYCLIC) && + (dmac->next_src_addr >= (dmac->init_addr + dmac->transfer.size - 1))) { + dmac->remaining_size = dmac->transfer.size; + dmac->next_src_addr = dmac->init_addr; + } + + if (dmac->remaining_size) { + /** See if remaining size is bigger than max transfer size and + * set burst size. */ + if (dmac->remaining_size > dmac->max_length) { + burst_size = dmac->max_length; + } else { + burst_size = dmac->remaining_size - 1; + } + + /* The current transfer was started; set up the new one. */ + axi_dmac_write(dmac, AXI_DMAC_REG_SRC_ADDRESS, dmac->next_src_addr); + axi_dmac_write(dmac, AXI_DMAC_REG_SRC_STRIDE, 0x0); + axi_dmac_write(dmac, AXI_DMAC_REG_X_LENGTH, burst_size); + axi_dmac_write(dmac, AXI_DMAC_REG_Y_LENGTH, 0x0); + + /* Compute parameters for the next transfer. */ + dmac->remaining_size = dmac->remaining_size - (burst_size + 1); + /* Address must advance with +1 since the DMAC writes X_LENGTH+1. */ + dmac->next_src_addr = dmac->next_src_addr + (burst_size + 1); + + /* Trigger the current transfer */ + axi_dmac_write(dmac, AXI_DMAC_REG_TRANSFER_SUBMIT, AXI_DMAC_TRANSFER_SUBMIT); + } + } + if (reg_val & AXI_DMAC_IRQ_EOT) { + if ((!dmac->remaining_size) && (dmac->transfer.cyclic != CYCLIC)) { + dmac->transfer.transfer_done = true; + dmac->next_src_addr = 0; + } + } +} + +/******************************************************************************* + * @brief ISR for mem DMA to mem DMA transfer. It computes the next transfer + * params, if any, and sets the transfer structure fields accordingly. + * + * @param instance - the instance that triggered the ISR. + * + * @return None. +*******************************************************************************/ +void axi_dmac_mem_to_mem_isr(void *instance) +{ + struct axi_dmac *dmac = (struct axi_dmac *)instance; + uint32_t burst_size; + uint32_t reg_val; + + /* Get interrupt sources and clear interrupts. */ + axi_dmac_read(dmac, AXI_DMAC_REG_IRQ_PENDING, ®_val); + axi_dmac_write(dmac, AXI_DMAC_REG_IRQ_PENDING, reg_val); + + if (reg_val & AXI_DMAC_IRQ_SOT) { + if (dmac->remaining_size) { + /** See if remaining size is bigger than max transfer size and + * set burst size. */ + if (dmac->remaining_size > dmac->max_length) { + burst_size = dmac->max_length; + } else { + burst_size = dmac->remaining_size - 1; + } + + /* The current transfer was started; set up the new one. */ + axi_dmac_write(dmac, AXI_DMAC_REG_SRC_ADDRESS, dmac->next_src_addr); + axi_dmac_write(dmac, AXI_DMAC_REG_SRC_STRIDE, 0x0); + axi_dmac_write(dmac, AXI_DMAC_REG_DEST_ADDRESS, dmac->next_dest_addr); + axi_dmac_write(dmac, AXI_DMAC_REG_DEST_STRIDE, 0x0); + axi_dmac_write(dmac, AXI_DMAC_REG_X_LENGTH, burst_size); + axi_dmac_write(dmac, AXI_DMAC_REG_Y_LENGTH, 0x0); + + /* Compute parameters for the next transfer. */ + dmac->remaining_size = dmac->remaining_size - (burst_size + 1); + /* Address must advance with +1 since the DMAC writes X_LENGTH+1. */ + dmac->next_src_addr = dmac->next_src_addr + (burst_size + 1); + dmac->next_dest_addr = dmac->next_dest_addr + (burst_size + 1); + + /* If transfer finished advance src address so that transfer_done can be set*/ + if (!dmac->remaining_size) { + dmac->next_src_addr = dmac->next_src_addr + (burst_size + 1); + } + + /* Trigger the current transfer */ + axi_dmac_write(dmac, AXI_DMAC_REG_TRANSFER_SUBMIT, AXI_DMAC_TRANSFER_SUBMIT); + } + } + if (reg_val & AXI_DMAC_IRQ_EOT) { + if (!dmac->remaining_size) { + if(dmac->next_src_addr > (dmac->init_addr + dmac->transfer.size)) { + dmac->transfer.transfer_done = true; + dmac->next_src_addr = 0; + dmac->next_dest_addr = 0; + } + } + } +} + +/******************************************************************************* + * @brief Wrapper for AXI IO through UIO/devmem read function. + * + * @param dmac - DMAC istance containing UIO index (/dev/uioX)/base address. + * @param reg_addr - Address to read from. + * @param reg_data - Location where read data will be stored. + * + * @return 0 - success. +*******************************************************************************/ +int32_t axi_dmac_read(struct axi_dmac *dmac, + uint32_t reg_addr, + uint32_t *reg_data) +{ + axi_io_read(dmac->base, reg_addr, reg_data); + + return 0; +} + +/******************************************************************************* + * @brief Wrapper for AXI IO through UIO/devmem wrrite function. + * + * @param dmac - DMAC istance containing UIO index (/dev/uioX)/base address. + * @param reg_addr - Address to write to. + * @param reg_data - Data to be written. + * + * @return 0 - success. +*******************************************************************************/ +int32_t axi_dmac_write(struct axi_dmac *dmac, + uint32_t reg_addr, + uint32_t reg_data) +{ + axi_io_write(dmac->base, reg_addr, reg_data); + + return 0; +} + +/******************************************************************************* + * @brief Get transfer status (value of dmac->transfer.transfer_done flag). + * + * @note This function should not be used if DMA IRQ is not used. + * + * @param dmac - DMAC istance. + * @param rdy - Transfer status (0 - not ready, 1 - ready). + * + * @return 0 - success. +*******************************************************************************/ +int32_t axi_dmac_is_transfer_ready(struct axi_dmac *dmac, bool *rdy) +{ + *rdy = dmac->transfer.transfer_done; + + return 0; +} + +/******************************************************************************* + * @brief Get DMAC capabilities. + * + * @param dmac - DMAC istance. + * + * @return 0 for success, -1 in case of failure. +*******************************************************************************/ +static int32_t axi_dmac_detect_caps(struct axi_dmac *dmac) +{ + uint32_t reg_val, initial_reg_val = 0; + uint32_t src_mem_mapped = 0; + uint32_t dest_mem_mapped = 0; + + dmac->max_length = -1; + dmac->direction = INVALID_DIR; + + /* Check if HW cyclic possible */ + axi_dmac_read(dmac, AXI_DMAC_REG_FLAGS, &initial_reg_val); + axi_dmac_write(dmac, AXI_DMAC_REG_FLAGS, DMA_CYCLIC); + axi_dmac_read(dmac, AXI_DMAC_REG_FLAGS, ®_val); + if (reg_val == DMA_CYCLIC) + dmac->hw_cyclic = true; + /* Restore initial value for AXI_DMAC_REG_FLAGS register */ + axi_dmac_write(dmac, AXI_DMAC_REG_FLAGS, initial_reg_val); + + /* Get maximum burst size and set value. */ + axi_dmac_write(dmac, AXI_DMAC_REG_X_LENGTH, dmac->max_length); + axi_dmac_read(dmac, AXI_DMAC_REG_X_LENGTH, &dmac->max_length); + + /* Get transfer direction and set value. */ + axi_dmac_write(dmac, AXI_DMAC_REG_DEST_ADDRESS, 0xffffffff); + axi_dmac_read(dmac, AXI_DMAC_REG_DEST_ADDRESS, &dest_mem_mapped); + axi_dmac_write(dmac, AXI_DMAC_REG_SRC_ADDRESS, 0xffffffff); + axi_dmac_read(dmac, AXI_DMAC_REG_SRC_ADDRESS, &src_mem_mapped); + + if (dest_mem_mapped && !src_mem_mapped) { + dmac->direction = DMA_DEV_TO_MEM; + } else if (!dest_mem_mapped && src_mem_mapped) { + dmac->direction = DMA_MEM_TO_DEV; + } else if (dest_mem_mapped && src_mem_mapped) { + dmac->direction = DMA_MEM_TO_MEM; + } else { + printf("Destination and source memory-mapped interfaces not supported.\n"); + return -1; + } + return 0; +} + +/******************************************************************************* + * @brief Get transfer status (value of dmac->transfer.transfer_done flag). + * + * @param dmac_core - DMAC istance. + * @param init - Structure containing initializing data. + * + * @return 0 for success, -1 in case of failure. +*******************************************************************************/ +int32_t axi_dmac_init(struct axi_dmac **dmac_core, + const struct axi_dmac_init *init) +{ + struct axi_dmac *dmac; + + dmac = (struct axi_dmac *)calloc(1, sizeof(*dmac)); + if (!dmac) + return -1; + + dmac->name = init->name; + dmac->base = init->base; + dmac->irq_option = init->irq_option; + + *dmac_core = dmac; + + int32_t status = axi_dmac_detect_caps(*dmac_core); + if (status < 0) + return -1; + + return 0; +} + +/******************************************************************************* + * @brief Remove the DMAC instance. + * + * @param dmac - DMAC istance. + * + * @return 0 for success, -1 in case of failure. +*******************************************************************************/ +int32_t axi_dmac_remove(struct axi_dmac *dmac) +{ + if (!dmac) + return -1; + + free(dmac); + + return 0; +} + +/******************************************************************************* + * @brief Start a DMA transfer. + * + * @param dmac - DMAC istance. + * @param dma_transfer - Structure containing transfer details. + * + * @return 0 for success, -1 in case of failure. +*******************************************************************************/ +int32_t axi_dmac_transfer_start(struct axi_dmac *dmac, + struct axi_dma_transfer *dma_transfer) +{ + uint32_t reg_val, burst_size; + + if (dma_transfer->size == 0) + return 0; /* Nothing to do. */ + + /* Set current transfer parameters. */ + dmac->transfer.size = dma_transfer->size; + dmac->transfer.cyclic = dma_transfer->cyclic; + dmac->transfer.dest_addr = dma_transfer->dest_addr; + dmac->transfer.src_addr = dma_transfer->src_addr; + + dmac->remaining_size = dma_transfer->size; + dmac->next_dest_addr = dma_transfer->dest_addr; + dmac->next_src_addr = dma_transfer->src_addr; + + /* If HW cyclic transfer selected and not available, show error */ + /* HW cyclic transfer available only for MEM to DEV transfers. */ + if ((!dmac->hw_cyclic) && (dma_transfer->cyclic == CYCLIC)) { + printf("Transfer mode not supported!\n"); + return -1; + } + + /* Cyclic transfers not possible for DEV_TO_MEM and MEM_TO_MEM transmissions. */ + if ((dmac->direction == DMA_DEV_TO_MEM) + || (dmac->direction == DMA_MEM_TO_MEM)) { + if (dma_transfer->cyclic == CYCLIC) { + printf("Transfer mode not supported!\n"); + return -1; + } + } + + /* Cyclic transfers set to HW or SW depending on size for MEM to DEV. */ + if ((dmac->direction == DMA_MEM_TO_DEV) && (dmac->transfer.cyclic == CYCLIC)) { + if ((dmac->remaining_size - 1) <= dmac->max_length) { + if (dmac->hw_cyclic) { + axi_dmac_read(dmac, AXI_DMAC_REG_FLAGS, ®_val); + reg_val = reg_val | DMA_CYCLIC; + axi_dmac_write(dmac, AXI_DMAC_REG_FLAGS, reg_val); + } + } else { + axi_dmac_read(dmac, AXI_DMAC_REG_FLAGS, ®_val); + reg_val = reg_val & ~DMA_CYCLIC; + axi_dmac_write(dmac, AXI_DMAC_REG_FLAGS, reg_val); + } + } + + /* Enable DMA if not already enabled. */ + axi_dmac_read(dmac, AXI_DMAC_REG_CTRL, ®_val); + if (!(reg_val & AXI_DMAC_CTRL_ENABLE)) { + axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, 0x0); + axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, AXI_DMAC_CTRL_ENABLE); + axi_dmac_write(dmac, AXI_DMAC_REG_IRQ_MASK, 0x0); + } + + axi_dmac_read(dmac, AXI_DMAC_REG_TRANSFER_SUBMIT, ®_val); + /* If we don't have a start of transfer then start compute + * values and trigger next transfer. */ + if (!(reg_val & AXI_DMAC_QUEUE_FULL)) { + switch (dmac->direction) { + case DMA_DEV_TO_MEM: + dmac->init_addr = dmac->next_dest_addr; + axi_dmac_write(dmac, AXI_DMAC_REG_DEST_ADDRESS, dmac->next_dest_addr); + axi_dmac_write(dmac, AXI_DMAC_REG_DEST_STRIDE, 0x0); + break; + case DMA_MEM_TO_DEV: + dmac->init_addr = dmac->next_src_addr; + axi_dmac_write(dmac, AXI_DMAC_REG_SRC_ADDRESS, dmac->next_src_addr); + axi_dmac_write(dmac, AXI_DMAC_REG_SRC_STRIDE, 0x0); + break; + case DMA_MEM_TO_MEM: + dmac->init_addr = dmac->next_src_addr; + axi_dmac_write(dmac, AXI_DMAC_REG_DEST_ADDRESS, dmac->next_dest_addr); + axi_dmac_write(dmac, AXI_DMAC_REG_DEST_STRIDE, 0x0); + axi_dmac_write(dmac, AXI_DMAC_REG_SRC_ADDRESS, dmac->next_src_addr); + axi_dmac_write(dmac, AXI_DMAC_REG_SRC_STRIDE, 0x0); + break; + default: + return -1; /* Other directions are not supported yet. */ + } + + /* Compute the burst size. */ + if (dmac->remaining_size > dmac->max_length) { + burst_size = dmac->max_length; + } else { + burst_size = dmac->remaining_size - 1; + } + + /* Compute next address (src or dest, or both, depending on type of transfer). */ + switch (dmac->direction) { + case DMA_DEV_TO_MEM: + dmac->next_dest_addr = dmac->next_dest_addr + (burst_size + 1); + break; + case DMA_MEM_TO_DEV: + dmac->next_src_addr = dmac->next_src_addr + (burst_size + 1); + break; + case DMA_MEM_TO_MEM: + dmac->next_dest_addr = dmac->next_dest_addr + (burst_size + 1); + dmac->next_src_addr = dmac->next_src_addr + (burst_size + 1); + break; + default: + return -1; + } + + /* Compute remaining size. */ + dmac->remaining_size = dmac->remaining_size - (burst_size + 1); + + /* Specify the length of the transfer and trigger transfer. */ + axi_dmac_write(dmac, AXI_DMAC_REG_X_LENGTH, burst_size); + axi_dmac_write(dmac, AXI_DMAC_REG_Y_LENGTH, 0x0); + axi_dmac_write(dmac, AXI_DMAC_REG_TRANSFER_SUBMIT, AXI_DMAC_TRANSFER_SUBMIT); + } else { + return -1; + } + + return 0; +} + +/******************************************************************************* + * @brief Wait for DMA transfer to be completed. + * + * @param dmac - DMAC istance. + * @param timeout_ms - Number of ms to wait for completion of transfer. + * + * @return 0 for success, -1 in case trasnfer not completed in specified time. +*******************************************************************************/ +int32_t axi_dmac_transfer_wait_completion(struct axi_dmac *dmac, + uint32_t timeout_ms) +{ + uint32_t timeout = 0; + uint32_t reg_val = 0; + + if (dmac->irq_option == IRQ_ENABLED) { + while (!dmac->transfer.transfer_done) { + timeout++; + mdelay(1); + if (timeout == timeout_ms) { + printf("Error transferring data using DMA.\n"); + return -1; + } + } + } else if (dmac->irq_option == IRQ_DISABLED) { + axi_dmac_read(dmac, AXI_DMAC_REG_IRQ_PENDING, ®_val); + while (reg_val != (AXI_DMAC_IRQ_SOT | AXI_DMAC_IRQ_EOT)) { + timeout++; + mdelay(1); + if (timeout == timeout_ms) { + printf("Error transferring data using DMA.\n"); + return -1; + } + axi_dmac_read(dmac, AXI_DMAC_REG_IRQ_PENDING, ®_val); + } + } + + return 0; +} + +/******************************************************************************* + * @brief Stop a DMA transfer. + * + * @param dmac - DMAC istance. + * + * @return None +*******************************************************************************/ +void axi_dmac_transfer_stop(struct axi_dmac *dmac) +{ + axi_dmac_write(dmac, AXI_DMAC_REG_CTRL, AXI_DMAC_CTRL_DISABLE); +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/axi_core/axi_dmac/axi_dmac.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/axi_core/axi_dmac/axi_dmac.h new file mode 100644 index 0000000..d47d1cd --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/axi_core/axi_dmac/axi_dmac.h @@ -0,0 +1,156 @@ +/***************************************************************************//** + * @file axi_dmac.h + * @brief Driver for the Analog Devices AXI-DMAC core. + * @author DBogdan (dragos.bogdan@analog.com) +******************************************************************************** + * Copyright 2018(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ +#ifndef AXI_DMAC_H_ +#define AXI_DMAC_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include +#include "util.h" + +/******************************************************************************/ +/********************** Macros and Constants Definitions **********************/ +/******************************************************************************/ +#define AXI_DMAC_REG_IRQ_MASK 0x80 +#define AXI_DMAC_REG_IRQ_PENDING 0x84 +#define AXI_DMAC_IRQ_SOT BIT(0) +#define AXI_DMAC_IRQ_EOT BIT(1) + +#define AXI_DMAC_REG_INTF_DESC 0x010 +#define AXI_DMAC_DMA_TYPE_DEST GENMASK(5,4) +#define AXI_DMAC_DMA_TYPE_SRC GENMASK(13,12) +//Define macro for src and dest + +#define AXI_DMAC_REG_CTRL 0x400 +#define AXI_DMAC_CTRL_ENABLE BIT(0) +#define AXI_DMAC_CTRL_DISABLE 0u +#define AXI_DMAC_CTRL_PAUSE BIT(1) + +#define AXI_DMAC_REG_TRANSFER_ID 0x404 +#define AXI_DMAC_REG_TRANSFER_SUBMIT 0x408 +#define AXI_DMAC_TRANSFER_SUBMIT BIT(0) +#define AXI_DMAC_QUEUE_FULL BIT(0) +#define AXI_DMAC_REG_FLAGS 0x40c +#define AXI_DMAC_REG_DEST_ADDRESS 0x410 +#define AXI_DMAC_REG_SRC_ADDRESS 0x414 +#define AXI_DMAC_REG_X_LENGTH 0x418 +#define AXI_DMAC_REG_Y_LENGTH 0x41c +#define AXI_DMAC_REG_DEST_STRIDE 0x420 +#define AXI_DMAC_REG_SRC_STRIDE 0x424 +#define AXI_DMAC_REG_TRANSFER_DONE 0x428 + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ +enum use_irq { + IRQ_DISABLED = 0, + IRQ_ENABLED = 1 +}; + +enum dma_direction { + INVALID_DIR = 0, + DMA_DEV_TO_MEM = 1, + DMA_MEM_TO_DEV = 2, + DMA_MEM_TO_MEM = 3 +}; + +enum dma_flags { + DMA_CYCLIC = 1, + DMA_LAST = 2, + DMA_PARTIAL_REPORTING_EN = 4 +}; + +// Could be transformed to a bool +enum cyclic_transfer { + NO = 0, + CYCLIC = 1 +}; + +struct axi_dma_transfer { + uint32_t size; + volatile bool transfer_done; + enum cyclic_transfer cyclic; + uint32_t src_addr; + uint32_t dest_addr; +}; + +struct axi_dmac { + const char *name; + uint32_t base; + enum use_irq irq_option; + enum dma_direction direction; + bool hw_cyclic; + uint32_t max_length; + volatile struct axi_dma_transfer transfer; + //Current sub-transfer properties + uint32_t init_addr; + uint32_t remaining_size; + uint32_t next_src_addr; + uint32_t next_dest_addr; +}; + +struct axi_dmac_init { + const char *name; + uint32_t base; + enum use_irq irq_option; +}; + +/******************************************************************************/ +/************************ Functions Declarations ******************************/ +/******************************************************************************/ +void axi_dmac_dev_to_mem_isr(void *instance); +void axi_dmac_mem_to_dev_isr(void *instance); +void axi_dmac_mem_to_mem_isr(void *instance); +void axi_dmac_write_isr(void *instance); +int32_t axi_dmac_read(struct axi_dmac *dmac, uint32_t reg_addr, + uint32_t *reg_data); +int32_t axi_dmac_write(struct axi_dmac *dmac, uint32_t reg_addr, + uint32_t reg_data); +int32_t axi_dmac_is_transfer_ready(struct axi_dmac *dmac, bool *rdy); +int32_t axi_dmac_init(struct axi_dmac **adc_core, + const struct axi_dmac_init *init); +int32_t axi_dmac_remove(struct axi_dmac *dmac); +int32_t axi_dmac_transfer_start(struct axi_dmac *dmac, + struct axi_dma_transfer *dma_transfer); +int32_t axi_dmac_transfer_wait_completion(struct axi_dmac *dmac, + uint32_t timeout_ms); +void axi_dmac_transfer_stop(struct axi_dmac *dmac); + +#endif diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/axi_core/spi_engine/spi_engine.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/axi_core/spi_engine/spi_engine.c new file mode 100644 index 0000000..82cd1e6 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/axi_core/spi_engine/spi_engine.c @@ -0,0 +1,996 @@ +/******************************************************************************* + * @file spi_engine.c + * @brief Core implementation of the SPI Engine Driver. + * @author Sergiu Cuciurean (sergiu.cuciurean@analog.com) +******************************************************************************** + * Copyright 2019(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. + ******************************************************************************/ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ + +/* In debug mode the printf function used in displaying the messages is causing +significant delays */ +//#define DEBUG_LEVEL 2 + +#include +#include +#include +#include +#include +#include + +#include "axi_io.h" +#include "error.h" +#include "spi_engine.h" + +/** + * @brief Spi engine platform specific SPI platform ops structure + */ +const struct spi_platform_ops spi_eng_platform_ops = { + .init = &spi_engine_init, + .write_and_read = &spi_engine_write_and_read, + .write_and_read_3wire = &spi_engine_write_and_read_3wire, + .remove = &spi_engine_remove +}; + +/******************************************************************************/ +/***************************** Static variables *******************************/ +/******************************************************************************/ + +/** + * @brief Static variable used to sync transfers + * + */ +static uint8_t _sync_id = 0x01; + +/******************************************************************************/ +/************************** Functions Implementation **************************/ +/******************************************************************************/ + +/** + * @brief Write SPI Engine's axi registers + * + * @param desc Decriptor containing SPI Engine's parameters + * @param reg_addr The address of the SPI Engine's axi register where the data + * will be written + * @param reg_data Data that will be written + * @return int32_t This function allways returns SUCCESS + */ +int32_t spi_engine_write(struct spi_engine_desc *desc, + uint32_t reg_addr, + uint32_t reg_data) +{ + axi_io_write(desc->spi_engine_baseaddr, reg_addr, reg_data); + + return SUCCESS; +} + +/** + * @brief Read SPI Engine's axi registers + * + * @param desc Decriptor containing SPI Engine's parameters + * @param reg_addr The address of the SPI Engine's axi register from where the + * data where the data will be read + * @param reg_data Pointer where the read that will be stored + * @return int32_t This function allways returns SUCCESS + */ +int32_t spi_engine_read(struct spi_engine_desc *desc, + uint32_t reg_addr, + uint32_t *reg_data) +{ + axi_io_read(desc->spi_engine_baseaddr, reg_addr, reg_data); + return SUCCESS; +} + +/** + * @brief Set width of the transferred word over SPI + * + * @param desc Decriptor containing SPI interface parameters + * @param data_width The desired data width + * The supported values are: + * - 8 + * - 16 + * - 24 + * - 32 + */ +int32_t spi_engine_set_transfer_width(struct spi_desc *desc, + uint8_t data_width) +{ + struct spi_engine_desc *desc_extra; + + desc_extra = desc->extra; + + if (data_width > desc_extra->max_data_width) + desc_extra->data_width = desc_extra->max_data_width; + else + desc_extra->data_width = data_width; + + return SUCCESS; +} + +/** + * @brief Set SPI engine clock frequency + * + * @param desc Decriptor containing SPI Engine's parameters + * @param speed_hz SPI engine transfer speed + */ +void spi_engine_set_speed(struct spi_desc *desc, + uint32_t speed_hz) +{ + struct spi_engine_desc *desc_extra; + + desc_extra = desc->extra; + + desc_extra->clk_div = desc_extra->ref_clk_hz / + (2 * speed_hz) - 1; +} + +/** + * @brief Set the number of words transferred in a single transaction + * + * @param desc Decriptor containing SPI Engine's parameters + * @param bytes_number The number of bytes to be converted + * @return uint8_t A number of words in which bytes_number can be grouped + */ +static uint8_t spi_get_words_number(struct spi_engine_desc *desc, + uint8_t bytes_number) +{ + uint8_t xfer_word_len; + uint8_t words_number; + + xfer_word_len = desc->data_width / 8; + words_number = bytes_number / xfer_word_len; + + if ((bytes_number % xfer_word_len) != 0) + words_number++; + + return words_number; +} + +/** + * @brief Get the word length in bytes + * + * @param desc Decriptor containing SPI Engine's parameters + * @return uint8_t Number of bytes that fit in one word + */ +static uint8_t spi_get_word_length(struct spi_engine_desc *desc) +{ + uint8_t word_length; + + word_length = desc->data_width / 8; + + return word_length; +} + +/** + * @brief Compute the prescaler used to set the sleep period. + * + * @param desc Decriptor containing SPI interface parameters + * @param sleep_time_ns The amount of time where the transfer hangs + * @param sleep_div Clock prescaler + * @return int32_t This function allways returns SUCCESS + */ +static int32_t spi_get_sleep_div(struct spi_desc *desc, + uint32_t sleep_time_ns, + uint32_t *sleep_div) +{ + struct spi_engine_desc *eng_desc; + + eng_desc = desc->extra; + + /* + * Engine Wiki: + * + * The frequency of the SCLK signal is derived from the + * module clock frequency using the following formula: + * f_sclk = f_clk / ((div + 1) * 2) + */ + + *sleep_div = (desc->max_speed_hz / 1000000 * sleep_time_ns / 1000) / + ((eng_desc->clk_div + 1) * 2) - 1; + + return SUCCESS; +} + +/** + * @brief Create a new commands queue used in a spi transfer + * + * @param fifo Command buffer, usually used in fifo mode + * @param cmd Command with which the buffer will be initiated + * @return int32_t Failure if the memory allocation failed + */ +static int32_t spi_engine_queue_new_cmd(struct spi_engine_cmd_queue **fifo, + uint32_t cmd) +{ + struct spi_engine_cmd_queue *local_fifo; + + local_fifo = (spi_engine_cmd_queue*)malloc(sizeof(*local_fifo)); + + if(!local_fifo) + return FAILURE; + + local_fifo->cmd = cmd; + local_fifo->next = NULL; + *fifo = local_fifo; + + return SUCCESS; +} + +/** + * @brief Add a command at the end of an existing queue + * + * @param fifo Command buffer, usually used in fifo mode + * @param cmd Command to be added + */ +static void spi_engine_queue_add_cmd(struct spi_engine_cmd_queue **fifo, + uint32_t cmd) +{ + struct spi_engine_cmd_queue *to_add = NULL; + struct spi_engine_cmd_queue *local_fifo; + + local_fifo = *fifo; + while (local_fifo->next != NULL) { + // Get the last element + local_fifo = local_fifo->next; + } + + // Create a new element + spi_engine_queue_new_cmd(&to_add, cmd); + // Add as the next element + local_fifo->next = to_add; +} + +/** + * @brief Add a command at the beginning of an existing queue + * + * @param fifo Command buffer, usually used in fifo mode + * @param cmd Command to be added + */ +static void spi_engine_queue_append_cmd(struct spi_engine_cmd_queue **fifo, + uint32_t cmd) +{ + struct spi_engine_cmd_queue *to_add = NULL; + + // Create a new element + spi_engine_queue_new_cmd(&to_add, cmd); + // Interchange the addresses + to_add->next = *fifo; + *fifo = to_add; +} + + +/** + * @brief Get the command from the beginning of the queue + * + * @param fifo Command buffer, usually used in fifo mode + * @param cmd Value of the command that was extracted + * @return int32_t Return FAILURE if the queue is empty + */ +static int32_t spi_engine_queue_get_cmd(struct spi_engine_cmd_queue **fifo, + uint32_t *cmd) +{ + struct spi_engine_cmd_queue *local_fifo; + + if (!*fifo) + return FAILURE; + + local_fifo = *fifo; + *cmd = local_fifo->cmd; + if ((*fifo)->next) { + *fifo = local_fifo->next; + free(local_fifo); + } else { + free(*fifo); + *fifo = NULL; + } + + return SUCCESS; +} + +/** + * @brief Free the memory allocated by a queue + * + * @param fifo The queue that needs it's memory freed + * @return int32_t This function always returns SUCCESS + */ +static int32_t spi_engine_queue_free(struct spi_engine_cmd_queue **fifo) +{ + if(*fifo && (*fifo)->next) + spi_engine_queue_free(&(*fifo)->next); + if((*fifo) != NULL) { + free(*fifo); + *fifo = NULL; + } + + return SUCCESS; +} + +/** + * @brief Write the SPI engine's command fifo + * + * @param desc Decriptor containing SPI Engine's parameters + * @param cmd The command that will be written + * @return int32_t Returns FAILURE if the the axi transfer failed + */ +static int32_t spi_engine_write_cmd_reg(struct spi_engine_desc *desc, + uint32_t cmd) +{ + int32_t ret; + + /* Check if offload is enabled */ + if(desc->offload_config & (OFFLOAD_TX_EN | OFFLOAD_RX_EN)) { + ret = spi_engine_write(desc, + SPI_ENGINE_REG_OFFLOAD_CMD_MEM(0), + cmd); + + } else { + ret = spi_engine_write(desc, + SPI_ENGINE_REG_CMD_FIFO, + cmd); + } + + return ret; +} + +/** + * @brief Write a transfer command to the SPI engine's command fifo + * + * @param desc Decriptor containing SPI Engine's parameters + * @param read_write Read/Write operation flag + * @param bytes_number Number of bytes to transfer + * @return int32_t This function always returns SUCCESS + */ +static int32_t spi_engine_transfer(struct spi_engine_desc *desc, + uint8_t read_write, + uint8_t bytes_number) +{ + uint8_t words_number; + + words_number = spi_get_words_number(desc, bytes_number); + + desc->offload_tx_len += words_number; + + /* + * Engine Wiki: + * + * https://wiki.analog.com/resources/fpga/peripherals/spi_engine + * + * The words number is zero based + */ + + spi_engine_write_cmd_reg(desc, + SPI_ENGINE_CMD_TRANSFER(read_write, + words_number - 1)); + + return SUCCESS; +} + +/** + * @brief Change the state of the chip select port + * + * @param desc Decriptor containing SPI interface parameters + * @param assert Chip select state. + * The supported values are : + * -true (HIGH) + * -false (LOW) + */ +static void spi_engine_set_cs(struct spi_desc *desc, + bool assert) +{ + uint8_t mask; + struct spi_engine_desc *eng_desc; + + eng_desc = desc->extra; + + mask = 0xFF; + /* Switch the state only of the selected chip select */ + if (!assert) + mask ^= BIT(desc->chip_select); + + spi_engine_write_cmd_reg(eng_desc, + SPI_ENGINE_CMD_ASSERT(eng_desc->cs_delay, + mask)); +} + +/** + * @brief Add a delay between the engine commands + * + * @param desc Decriptor containing SPI interface parameters + * @param sleep_time_ns Number of nanoseconds to sleep between commands + */ +static void spi_gen_sleep_ns(struct spi_desc *desc, + uint32_t sleep_time_ns) +{ + uint32_t sleep_div; + struct spi_engine_desc *eng_desc; + + eng_desc = desc->extra; + + spi_get_sleep_div(desc, sleep_time_ns, &sleep_div); + + spi_engine_write_cmd_reg(eng_desc, SPI_ENGINE_CMD_SLEEP(sleep_div)); +} + +/** + * @brief Spi engine command interpreter + * + * @param desc Decriptor containing SPI interface parameters + * @param cmd Command to send to the engine + * @return int32_t - SUCCESS if the command is transferred + * - FAILURE if the command format is invalid + */ +static int32_t spi_engine_write_cmd(struct spi_desc *desc, + uint32_t cmd) +{ + uint8_t engine_command; + uint8_t parameter; + uint8_t modifier; + struct spi_engine_desc *desc_extra; + + desc_extra = desc->extra; + + engine_command = (cmd >> 12) & 0x0F; + modifier = (cmd >> 8) & 0x0F; + parameter = cmd & 0xFF; + + switch(engine_command) { + case SPI_ENGINE_INST_TRANSFER: + spi_engine_transfer(desc_extra, modifier, parameter); + break; + + case SPI_ENGINE_INST_ASSERT: + if(parameter == 0xFF) { + /* Set the CS HIGH */ + spi_engine_set_cs(desc, true); + } else if(parameter == 0x00) { + /* Set the CS LOW */ + spi_engine_set_cs(desc, false); + } + break; + + /* The SYNC and SLEEP commands got the same value but different + modifier */ + case SPI_ENGINE_INST_SYNC_SLEEP: + /* SYNC instruction */ + if(modifier == 0x00) { + spi_engine_write_cmd_reg(desc_extra, cmd); + } else if(modifier == 0x01) { + spi_gen_sleep_ns(desc, parameter); + } + break; + case SPI_ENGINE_INST_CONFIG: + spi_engine_write_cmd_reg(desc_extra, cmd); + + break; + + default: + + return FAILURE; + } + + return SUCCESS; +} + +/** + * @brief Prepare the command queue before sending it to the engine + * + * @param desc Decriptor containing SPI interface parameters + * @param msg Structure used to store the transfer messages + * @return int32_t This function always returns SUCCESS + */ +static int32_t spi_engine_compile_message(struct spi_desc *desc, + struct spi_engine_msg *msg) +{ + struct spi_engine_desc *desc_extra; + + desc_extra = desc->extra; + + /* Configure the prescaler */ + spi_engine_queue_append_cmd(&msg->cmds, + SPI_ENGINE_CMD_CONFIG( + SPI_ENGINE_CMD_REG_CLK_DIV, + desc_extra->clk_div)); + + /* Set the data transfer length */ + spi_engine_queue_append_cmd(&msg->cmds, + SPI_ENGINE_CMD_CONFIG( + SPI_ENGINE_CMD_DATA_TRANSFER_LEN, + desc_extra->data_width)); + /* + * Configure the spi mode : + * - 3 wire + * - CPOL + * - CPHA + */ + spi_engine_queue_append_cmd(&msg->cmds, + SPI_ENGINE_CMD_CONFIG( + SPI_ENGINE_CMD_REG_CONFIG, + desc->mode)); + + /* Add a sync command to signal that the transfer has finished */ + spi_engine_queue_add_cmd(&msg->cmds, SPI_ENGINE_CMD_SYNC(_sync_id)); + + return SUCCESS; +} + +/** + * @brief Initiate a spi transfer (Blocking) + * + * @param desc Decriptor containing SPI interface parameters + * @param msg Structure used to store the transfer messages + * @return int32_t - SUCCESS if the transfer finished + * - FAILURE if the memory allocation failed + */ +static int32_t spi_engine_transfer_message(struct spi_desc *desc, + struct spi_engine_msg *msg) +{ + uint32_t i; + uint32_t data; + uint32_t sync_id; + bool offload_en; + struct spi_engine_desc *desc_extra; + + desc_extra = desc->extra; + + spi_engine_compile_message(desc, msg); + + offload_en = (desc_extra->offload_config & OFFLOAD_TX_EN) | + (desc_extra->offload_config & OFFLOAD_RX_EN); + + /* Write the command fifo buffer */ + while(msg->cmds != NULL) { + spi_engine_queue_get_cmd(&msg->cmds, &data); + spi_engine_write_cmd(desc, data); + } + + /* Write a number of tx_length WORDS on the SDO line */ + if(offload_en) { + for(i = 0; i < desc_extra->offload_tx_len; i++) + spi_engine_write(desc_extra, + SPI_ENGINE_REG_OFFLOAD_SDO_MEM(0), + msg->tx_buf[i]); + + } else { + if(msg->tx_buf != NULL){ + for(i = 0; i < msg->tx_length; i++) + spi_engine_write(desc_extra, + SPI_ENGINE_REG_SDO_DATA_FIFO, + msg->tx_buf[i]); + } + do { + spi_engine_read(desc_extra, + SPI_ENGINE_REG_SYNC_ID, + &sync_id); + } + /* Wait for the end sync signal */ + while(sync_id != _sync_id); + _sync_id++; + + /* Read a number of rx_length WORDS from the SDI line and store + them */ + for(i = 0; i < msg->rx_length; i++) { + spi_engine_read(desc_extra, + SPI_ENGINE_REG_SDI_DATA_FIFO, + &data); + /* Store the rx data if rx buffer valid, flush sdi_fifo if + rx buffer is invalid.*/ + if(msg->rx_buf != NULL){ + msg->rx_buf[i] = data; + } + } + } + + return SUCCESS; +} + +/** + * @brief Initialize the spi engine + * + * @param desc Decriptor containing SPI interface parameters + * @param param Structure containing the spi init parameters + * @return int32_t - SUCCESS if the transfer finished + * - FAILURE if the memory allocation failed + */ +int32_t spi_engine_init(struct spi_desc **desc, + const struct spi_init_param *param) +{ + uint32_t data_width; + uint32_t spi_engine_version; + struct spi_engine_desc *eng_desc; + struct spi_engine_init_param *spi_engine_init; + + if (!param) { + return FAILURE; + } + + *desc = malloc(sizeof(**desc)); + if(! *desc) { + free(*desc); + return FAILURE; + } + + eng_desc = (struct spi_engine_desc*)malloc(sizeof(*eng_desc)); + + if (!eng_desc) + return FAILURE; + + spi_engine_init = param->extra; + + (*desc)->max_speed_hz = param->max_speed_hz; + (*desc)->chip_select = param->chip_select; + (*desc)->mode = param->mode; + (*desc)->extra = eng_desc; + + eng_desc->offload_config = OFFLOAD_DISABLED; + eng_desc->spi_engine_baseaddr = spi_engine_init->spi_engine_baseaddr; + eng_desc->type = spi_engine_init->type; + eng_desc->cs_delay = spi_engine_init->cs_delay; + eng_desc->ref_clk_hz = spi_engine_init->ref_clk_hz; + eng_desc->clk_div = eng_desc->ref_clk_hz / + (2 * param->max_speed_hz) - 1; + + /* Perform a reset */ + spi_engine_write(eng_desc, SPI_ENGINE_REG_RESET, 0x01); + usleep(1000); + spi_engine_write(eng_desc, SPI_ENGINE_REG_RESET, 0x00); + + /* Get current data width */ + spi_engine_read(eng_desc, SPI_ENGINE_REG_DATA_WIDTH, &data_width); + eng_desc->max_data_width = data_width; + + spi_engine_set_transfer_width(*desc, spi_engine_init->data_width); + + /* Get spi engine version */ + spi_engine_read(eng_desc, SPI_ENGINE_REG_VERSION, &spi_engine_version); + + printf("Spi engine v%"PRIu32".%"PRIu32".%"PRIu32" successfully initialized.\n", + (spi_engine_version >> 16), + ((spi_engine_version >> 8) & 0xFF), + (spi_engine_version & 0xFF)); + + return SUCCESS; +} + +/** + * @brief Write/read on the spi interface in 4-wire mode (full-duplex) + * + * @param desc Decriptor containing SPI interface parameters + * @param data Pointer to data buffer + * @param bytes_number Number of bytes to transfer + * @return int32_t - SUCCESS if the transfer finished + * - FAILURE if the memory allocation or transfer failed + */ +int32_t spi_engine_write_and_read(struct spi_desc *desc, + uint8_t *data, + uint16_t bytes_number) +{ + uint8_t word_len; + uint8_t words_number; + int32_t ret; + struct spi_engine_msg msg; + struct spi_engine_desc *desc_extra; + + desc_extra = desc->extra; + + /* If we want to access SPI interface and SPI engine offload module was + * activated, we need to disable it + * This is set in spi_engine_offload_init() */ + desc_extra->offload_config = OFFLOAD_DISABLED; + /* This is set in spi_engine_offload_transfer() */ + spi_engine_write(desc_extra, SPI_ENGINE_REG_OFFLOAD_CTRL(0), 0); + + words_number = spi_get_words_number(desc_extra, bytes_number); + + msg.cmds = (spi_engine_cmd_queue*)malloc(sizeof(*msg.cmds)); + if (!msg.cmds) + return FAILURE; + + msg.tx_buf =(uint32_t*)calloc(words_number, sizeof(msg.tx_buf[0])); + msg.rx_buf =(uint32_t*)calloc(words_number, sizeof(msg.rx_buf[0])); + msg.tx_length = words_number; + msg.rx_length = words_number; + + /* Get the length of transferred word */ + word_len = spi_get_word_length(desc_extra); + + /* Make sure the CS is HIGH before starting a transaction */ + msg.cmds->next = NULL; + msg.cmds->cmd = CS_HIGH; + // Assert select. + spi_engine_queue_add_cmd(&msg.cmds, CS_LOW); + // add read/write command + spi_engine_queue_add_cmd(&msg.cmds, WRITE_READ(bytes_number)); + // Disable select. + spi_engine_queue_add_cmd(&msg.cmds, CS_HIGH); + + /* Pack the bytes into engine WORDS */ + for (int i = 0; i < bytes_number; i++) + msg.tx_buf[i / word_len] |= data[i] << (desc_extra->data_width- + (i % word_len + 1) * 8); + + ret = spi_engine_transfer_message(desc, &msg); + + for (int i = 0; i < bytes_number; i++) + data[i] = msg.rx_buf[(i) / word_len] >> + (desc_extra->data_width - + ((i) % word_len + 1) * 8); + + spi_engine_queue_free(&msg.cmds); + free(msg.tx_buf); + free(msg.rx_buf); + + return ret; +} + +/** + * @brief Write and read on the spi interface in 3-wire mode (half-duplex) + * + * @param desc Decriptor containing SPI interface parameters + * @param data Pointer to data buffer + * @param bytes_number Number of bytes to transfer + * @return int32_t - SUCCESS if the transfer finished + * - FAILURE if the memory allocation or transfer failed + */ +int32_t spi_engine_write_and_read_3wire(struct spi_desc *desc, + uint8_t *write_data, + uint8_t *read_data, + uint16_t write_bytes_number, + uint16_t read_bytes_number) +{ + uint8_t word_len; + uint8_t wr_words_number; + uint8_t rd_words_number; + int32_t ret; + struct spi_engine_msg msg; + struct spi_engine_desc *desc_extra; + + desc_extra = desc->extra; + + /* If we want to access SPI interface and SPI engine offload module was + * activated, we need to disable it + * This is set in spi_engine_offload_init() */ + desc_extra->offload_config = OFFLOAD_DISABLED; + /* This is set in spi_engine_offload_transfer() */ + spi_engine_write(desc_extra, SPI_ENGINE_REG_OFFLOAD_CTRL(0), 0); + + wr_words_number = spi_get_words_number(desc_extra, write_bytes_number); + rd_words_number = spi_get_words_number(desc_extra, read_bytes_number); + + msg.cmds = (spi_engine_cmd_queue*)malloc(sizeof(*msg.cmds)); + if (!msg.cmds) + return FAILURE; + + msg.tx_buf =(wr_words_number>0)?(uint32_t*)calloc(wr_words_number, sizeof(msg.tx_buf[0])):NULL; + msg.rx_buf =(rd_words_number>0)?(uint32_t*)calloc(rd_words_number, sizeof(msg.rx_buf[0])):NULL; + msg.tx_length = wr_words_number; + msg.rx_length = rd_words_number; + + /* Get the length of transferred word */ + word_len = spi_get_word_length(desc_extra); + + /* Make sure the CS is HIGH before starting a transaction */ + msg.cmds->next = NULL; + msg.cmds->cmd = CS_HIGH; + + // Assert select. + spi_engine_queue_add_cmd(&msg.cmds, CS_LOW); + + // add read/write command + if(write_bytes_number > 0) + spi_engine_queue_add_cmd(&msg.cmds, WRITE(write_bytes_number)); + if(read_bytes_number > 0) + spi_engine_queue_add_cmd(&msg.cmds, READ(read_bytes_number)); + + // Disable select. + spi_engine_queue_add_cmd(&msg.cmds, CS_HIGH); + + /* Pack the bytes into engine WORDS */ + for (int i = 0; i < write_bytes_number; i++) + msg.tx_buf[i / word_len] |= write_data[i] << (desc_extra->data_width- + (i % word_len + 1) * 8); + + ret = spi_engine_transfer_message(desc, &msg); + + for (int i = 0; i < read_bytes_number; i++) + read_data[i] = msg.rx_buf[(i) / word_len] >> + (desc_extra->data_width - + ((i) % word_len + 1) * 8); + + spi_engine_queue_free(&msg.cmds); + free(msg.tx_buf); + free(msg.rx_buf); + + return ret; +} + +/** + * @brief Initialize the SPI engine's offload module + * + * @param desc Decriptor containing SPI interface parameters + * @param param Structure containing the offload init parameters + * @return int32_t This function always returns SUCCESS + */ +int32_t spi_engine_offload_init(struct spi_desc *desc, + const struct spi_engine_offload_init_param *param) +{ + struct spi_engine_desc *eng_desc; + struct axi_dmac_init dmac_init; + + eng_desc = desc->extra; + + eng_desc->offload_config = param->offload_config; + + if(!(param->dma_flags)) { + eng_desc->cyclic = CYCLIC; + } else { + if((*(param->dma_flags)) & DMA_CYCLIC) + eng_desc->cyclic = CYCLIC; + else + eng_desc->cyclic = NO; + } + + + if(param->offload_config & OFFLOAD_TX_EN) { + dmac_init.name = "DAC DMAC"; + dmac_init.base = param->tx_dma_baseaddr; + axi_dmac_init(&eng_desc->offload_tx_dma, &dmac_init); + if(!eng_desc->offload_tx_dma) + return FAILURE; + } + if(param->offload_config & OFFLOAD_RX_EN) { + dmac_init.name = "ADC DMAC"; + dmac_init.base = param->rx_dma_baseaddr; + axi_dmac_init(&eng_desc->offload_rx_dma, &dmac_init); + if(!eng_desc->offload_rx_dma) + return FAILURE; + } + + return SUCCESS; +} + +/** + * @brief Initiate a SPI transfer in offload mode + * + * @param desc Decriptor containing SPI interface parameters + * @param msg Offload message that get's to be transferred + * @param no_samples Number of time the messages will be transferred + * @return int32_t This function always returns SUCCESS + */ +int32_t spi_engine_offload_transfer(struct spi_desc *desc, + struct spi_engine_offload_message msg, + uint32_t no_samples) +{ + struct spi_engine_msg transfer; + struct spi_engine_desc *eng_desc; + uint32_t i; + uint8_t word_length; + + eng_desc = desc->extra; + + /* Check if offload is disabled */ + if(!((eng_desc->offload_config & OFFLOAD_TX_EN) | + (eng_desc->offload_config & OFFLOAD_RX_EN))) + return FAILURE; + + spi_engine_write(eng_desc, SPI_ENGINE_REG_OFFLOAD_RESET(0), 1); + spi_engine_write(eng_desc, SPI_ENGINE_REG_OFFLOAD_RESET(0), 0); + + eng_desc->offload_tx_len = 0; + eng_desc->offload_rx_len = 0; + + transfer.cmds = (spi_engine_cmd_queue*)malloc(sizeof(*transfer.cmds)); + + if (!transfer.cmds) + return -1; + + transfer.tx_buf = msg.commands_data; + + /* Load the commands into the message */ + transfer.cmds->next = NULL; + transfer.cmds->cmd = msg.commands[0]; + i = 1; + while(i < msg.no_commands) { + spi_engine_queue_add_cmd(&transfer.cmds, msg.commands[i++]); + + } + + spi_engine_transfer_message(desc, &transfer); + + /* Start transfer */ + spi_engine_write(eng_desc, SPI_ENGINE_REG_OFFLOAD_CTRL(0), 0x0001); + + word_length = spi_get_word_length(eng_desc); + if(eng_desc->offload_config & OFFLOAD_TX_EN) { + struct axi_dma_transfer tx_transfer = { + // Number of bytes to write/read + .size = word_length * eng_desc->offload_tx_len * no_samples, + // Transfer done flag + .transfer_done = 0, + // Signal transfer mode + .cyclic = eng_desc->cyclic, + // Address of data source + .src_addr = (uintptr_t)msg.tx_addr, + // Address of data destination + .dest_addr = 0 + }; + axi_dmac_transfer_start(eng_desc->offload_tx_dma, &tx_transfer); + } + + if(eng_desc->offload_config & OFFLOAD_RX_EN) { + struct axi_dma_transfer rx_transfer = { + // Number of bytes to write/read + .size = word_length * eng_desc->offload_tx_len * no_samples, + // Transfer done flag + .transfer_done = 0, + // Signal transfer mode + .cyclic = NO, + // Address of data source + .src_addr = 0, + // Address of data destination + .dest_addr = (uintptr_t)msg.rx_addr + }; + axi_dmac_transfer_start(eng_desc->offload_rx_dma, &rx_transfer); + axi_dmac_transfer_wait_completion(eng_desc->offload_rx_dma, 500); + } + + usleep(1000); + + spi_engine_queue_free(&transfer.cmds); + + return SUCCESS; +} + +/** + * @brief Free the resources allocated by spi_init(). + * + * @param desc Decriptor containing SPI interface parameters + * @return int32_t This function allways returns SUCCESS + */ +int32_t spi_engine_remove(struct spi_desc *desc) +{ + struct spi_engine_desc *eng_desc; + + eng_desc = desc->extra; + + if(eng_desc->offload_config & OFFLOAD_TX_EN) + axi_dmac_remove(eng_desc->offload_tx_dma); + if(eng_desc->offload_config & OFFLOAD_RX_EN) + axi_dmac_remove(eng_desc->offload_rx_dma); + free(desc->extra); + free(desc); + + return SUCCESS; +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/axi_core/spi_engine/spi_engine.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/axi_core/spi_engine/spi_engine.h new file mode 100644 index 0000000..8ae7af8 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/axi_core/spi_engine/spi_engine.h @@ -0,0 +1,229 @@ +/******************************************************************************* + * @file spi_engine.h + * @brief Header file of SPI Engine core features. + * @author Sergiu Cuciurean (sergiu.cuciurean@analog.com) +******************************************************************************** + * Copyright 2019(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +#ifndef SPI_ENGINE_H +#define SPI_ENGINE_H + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ + +#include + +#include "spi_engine_private.h" +#include "../axi_dmac/axi_dmac.h" +#include "../../platform/xilinx_spi.h" + +/******************************************************************************/ +/********************** Macros and Constants Definitions **********************/ +/******************************************************************************/ + +#define OFFLOAD_DISABLED 0x00 +#define OFFLOAD_TX_EN BIT(0) +#define OFFLOAD_RX_EN BIT(1) +#define OFFLOAD_TX_RX_EN OFFLOAD_TX_EN | OFFLOAD_RX_EN + +#define SPI_ENGINE_MSG_QUEUE_END 0xFFFFFFFF + +/* Spi engine commands */ +#define WRITE(no_bytes) ((SPI_ENGINE_INST_TRANSFER << 12) |\ + (SPI_ENGINE_INSTRUCTION_TRANSFER_W << 8) | no_bytes) + +#define READ(no_bytes) ((SPI_ENGINE_INST_TRANSFER << 12) |\ + (SPI_ENGINE_INSTRUCTION_TRANSFER_R << 8) | no_bytes) + +#define WRITE_READ(no_bytes) ((SPI_ENGINE_INST_TRANSFER << 12) |\ + (SPI_ENGINE_INSTRUCTION_TRANSFER_RW << 8) | no_bytes) + +#define SLEEP(time) SPI_ENGINE_CMD_SLEEP(time & 0xF) + +/* The delay and chip select parmeters are passed over the engine descriptor +and will be used inside the function call */ +#define CS_HIGH SPI_ENGINE_CMD_ASSERT(0x01, 0xFF) +#define CS_LOW SPI_ENGINE_CMD_ASSERT(0x01, 0x00) + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ + +/** + * @struct spi_engine_init_param + * @brief Structure containing the init parameters needed by the SPI engine + */ +struct spi_engine_init_param { + /** SPI engine reference clock */ + uint32_t ref_clk_hz; + /** Type of implementation */ + enum xil_spi_type type; + /** Base address where the HDL core is situated */ + uint32_t spi_engine_baseaddr; + /** Delay between the CS toggle and the start of SCLK */ + uint32_t cs_delay; + /** Data with of one SPI transfer ( in bits ) */ + uint8_t data_width; +}; + + +/** + * @struct spi_engine_desc + * @brief Structure representing an SPI engine device + */ +struct spi_engine_desc { + /** SPI engine reference clock */ + uint32_t ref_clk_hz; + /** Type of implementation */ + enum xil_spi_type type; + /** Pointer to a DMAC used in transmission */ + struct axi_dmac *offload_tx_dma; + /** Pointer to a DMAC used in reception */ + struct axi_dmac *offload_rx_dma; + /** Transfer mode for Tx DMAC */ + enum cyclic_transfer cyclic; + /** Offload's module transfer direction : TX, RX or both */ + uint8_t offload_config; + /** Number of words that the module has to send */ + uint8_t offload_tx_len; + /** Number of words that the module has to receive */ + uint8_t offload_rx_len; + /** Base address where the HDL core is situated */ + uint32_t spi_engine_baseaddr; + /** Base address where the RX DMAC core is situated */ + uint32_t rx_dma_baseaddr; + /** Base address where the TX DMAC core is situated */ + uint32_t tx_dma_baseaddr; + /** Delay between the CS toggle and the start of SCLK */ + uint8_t cs_delay; + /** Clock divider used in transmission delays */ + uint32_t clk_div; + /** Data with of one SPI transfer ( in bits ) */ + uint8_t data_width; + /** The maximum data width supported by the engine */ + uint8_t max_data_width; +}; + + +/** + * @struct spi_engine_offload_init_param + * @brief Structure containing the init parameters needed by the offload module + */ +struct spi_engine_offload_init_param { + /** Base address where the RX DMAC core is situated */ + uint32_t rx_dma_baseaddr; + /** Base address where the TX DMAC core is situated */ + uint32_t tx_dma_baseaddr; + /** DMAC flags - if not initialized, the default value is DMA_CYCLIC */ + uint32_t *dma_flags; + /** Offload's module transfer direction : TX, RX or both */ + uint8_t offload_config; +}; + +/** + * @struct spi_engine_offload_message + * @brief Structure representing an offload message + */ +struct spi_engine_offload_message { + /** Pointer to the SPI engine commands buffer that will be sent. + * The available commands can be found at: + * https://wiki.analog.com/resources/fpga/peripherals/spi_engine/ + */ + uint32_t *commands; + /** Length of the SPI engine commands buffer */ + uint32_t no_commands; + /** Pointer of the data that will be sent over spi */ + uint32_t *commands_data; + /** The address where the data that will be transmitted is situated */ + uint32_t tx_addr; + /** The address where the data that will be received is situated */ + uint32_t rx_addr; +}; + +/******************************************************************************/ +/************************ Functions Declarations ******************************/ +/******************************************************************************/ + +/** + * @brief Spi engine platform specific SPI platform ops structure + */ +extern const struct spi_platform_ops xil_spi_ops; + +/* Write SPI Engine's axi registers */ +int32_t spi_engine_write(struct spi_engine_desc *desc, + uint32_t reg_addr, + uint32_t reg_data); + +/* Read SPI Engine's axi registers */ +int32_t spi_engine_read(struct spi_engine_desc *desc, + uint32_t reg_addr, + uint32_t *reg_data); +/* Initialize the SPI engine device */ +int32_t spi_engine_init(struct spi_desc **desc, + const struct spi_init_param *param); + +/* Write and read data over SPI using the SPI engine */ +int32_t spi_engine_write_and_read(struct spi_desc *desc, + uint8_t *data, + uint16_t bytes_number); + +/* Write and read data over SPI using the SPI engine in 3-wire mode */ +int32_t spi_engine_write_and_read_3wire(struct spi_desc *desc, + uint8_t *write_data, + uint8_t *read_data, + uint16_t write_bytes_number, + uint16_t read_bytes_number); +/* Free the resources used by the SPI engine device */ +int32_t spi_engine_remove(struct spi_desc *desc); + +/* Initialize the SPI engine offload module */ +int32_t spi_engine_offload_init(struct spi_desc *desc, + const struct spi_engine_offload_init_param *param); + +/* Write and read data over SPI using the offload module */ +int32_t spi_engine_offload_transfer(struct spi_desc *desc, + struct spi_engine_offload_message msg, + uint32_t no_samples); + +/* Set SPI transfer width */ +int32_t spi_engine_set_transfer_width(struct spi_desc *desc, + uint8_t data_wdith); + +/* Set SPI transfer speed */ +void spi_engine_set_speed(struct spi_desc *desc, + uint32_t speed_hz); + +#endif // SPI_ENGINE_H diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/axi_core/spi_engine/spi_engine_private.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/axi_core/spi_engine/spi_engine_private.h new file mode 100644 index 0000000..2d46906 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/axi_core/spi_engine/spi_engine_private.h @@ -0,0 +1,154 @@ +/******************************************************************************* + * @file spi_engine_private.h + * @brief Private definitions of the SPI Engine + * @author Sergiu Cuciurean (sergiu.cuciurean@analog.com) +******************************************************************************** + * Copyright 2019(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +#ifndef SPI_ENGINE_PRIVATE_H +#define SPI_ENGINE_PRIVATE_H + +#include +#include + +#include "spi.h" +#include "util.h" + +/******************************************************************************/ +/*************************** Spi Engine registers *****************************/ +/******************************************************************************/ + +#define SPI_ENGINE_REG_VERSION 0x00 +#define SPI_ENGINE_REG_DATA_WIDTH 0x0C +#define SPI_ENGINE_REG_RESET 0x40 +#define SPI_ENGINE_REG_INT_ENABLE 0x80 +#define SPI_ENGINE_REG_INT_PENDING 0x84 +#define SPI_ENGINE_REG_INT_SOURCE 0x88 +#define SPI_ENGINE_REG_SYNC_ID 0xC0 +#define SPI_ENGINE_REG_CMD_FIFO_ROOM 0xD0 +#define SPI_ENGINE_REG_SDO_FIFO_ROOM 0xD4 +#define SPI_ENGINE_REG_SDI_FIFO_LEVEL 0xD8 +#define SPI_ENGINE_REG_CMD_FIFO 0xE0 +#define SPI_ENGINE_REG_SDO_DATA_FIFO 0xE4 +#define SPI_ENGINE_REG_SDI_DATA_FIFO 0xE8 +#define SPI_ENGINE_REG_SDI_DATA_FIFO_PEEK 0xEC + +/******************************************************************************/ +/************************ Spi Engine register parameters **********************/ +/******************************************************************************/ + +#define SPI_ENGINE_INST_TRANSFER 0x00 +#define SPI_ENGINE_INST_ASSERT 0x01 +#define SPI_ENGINE_INST_CONFIG 0x02 +#define SPI_ENGINE_INST_SYNC_SLEEP 0x03 +#define SPI_ENGINE_INST_MISC 0x03 +#define SPI_ENGINE_CMD_REG_CLK_DIV 0x00 +#define SPI_ENGINE_CMD_REG_CONFIG 0x01 +#define SPI_ENGINE_CMD_DATA_TRANSFER_LEN 0x02 +#define SPI_ENGINE_MISC_SYNC 0x00 +#define SPI_ENGINE_MISC_SLEEP 0x01 +#define SPI_ENGINE_SYNC_TRANSFER_BEGIN BIT(1) +#define SPI_ENGINE_SYNC_TRANSFER_END BIT(2) +#define SPI_ENGINE_INT_CMD_ALMOST_EMPTY BIT(0) +#define SPI_ENGINE_INT_SDO_ALMOST_EMPTY BIT(1) +#define SPI_ENGINE_INT_SDI_ALMOST_FULL BIT(2) +#define SPI_ENGINE_INT_SYNC BIT(3) +#define SPI_ENGINE_OFFLOAD_CTRL_ENABLE BIT(0) +#define SPI_ENGINE_OFFLOAD_STATUS_ENABLED BIT(0) +#define SPI_ENGINE_CONFIG_CPHA BIT(0) +#define SPI_ENGINE_CONFIG_CPOL BIT(1) +#define SPI_ENGINE_CONFIG_3WIRE BIT(2) +#define SPI_ENGINE_VERSION_MAJOR(x) ((x >> 16) & 0xff) +#define SPI_ENGINE_VERSION_MINOR(x) ((x >> 8) & 0xff) +#define SPI_ENGINE_VERSION_PATCH(x) (x & 0xff) + +/******************************************************************************/ +/**************************** Spi Engine commands *****************************/ +/******************************************************************************/ + +#define SPI_ENGINE_INSTRUCTION_TRANSFER_W 0x01 +#define SPI_ENGINE_INSTRUCTION_TRANSFER_R 0x02 +#define SPI_ENGINE_INSTRUCTION_TRANSFER_RW 0x03 + +#define SPI_ENGINE_REG_OFFLOAD_CTRL(x) (0x100 + (0x20 * x)) +#define SPI_ENGINE_REG_OFFLOAD_STATUS(x) (0x104 + (0x20 * x)) +#define SPI_ENGINE_REG_OFFLOAD_RESET(x) (0x108 + (0x20 * x)) +#define SPI_ENGINE_REG_OFFLOAD_CMD_MEM(x) (0x110 + (0x20 * x)) +#define SPI_ENGINE_REG_OFFLOAD_SDO_MEM(x) (0x114 + (0x20 * x)) + +#define SPI_ENGINE_CMD(inst, arg1, arg2) \ + (((inst & 0x03) << 12) | \ + ((arg1 & 0x03) << 8) | arg2 ) + +#define SPI_ENGINE_CMD_TRANSFER(readwrite, n) \ + SPI_ENGINE_CMD(SPI_ENGINE_INST_TRANSFER, \ + (readwrite), (n)) + +#define SPI_ENGINE_CMD_ASSERT(delay, cs) \ + SPI_ENGINE_CMD(SPI_ENGINE_INST_ASSERT, \ + (delay), (cs)) + +#define SPI_ENGINE_CMD_CONFIG(reg, val) \ + SPI_ENGINE_CMD(SPI_ENGINE_INST_CONFIG, \ + (reg), (val)) + +#define SPI_ENGINE_CMD_SLEEP(delay) \ + SPI_ENGINE_CMD(SPI_ENGINE_INST_MISC, \ + SPI_ENGINE_MISC_SLEEP, \ + (delay)) + +#define SPI_ENGINE_CMD_SYNC(id) \ + SPI_ENGINE_CMD(SPI_ENGINE_INST_MISC, \ + SPI_ENGINE_MISC_SYNC, \ + (id)) + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ + +typedef struct spi_engine_cmd_queue { + uint32_t cmd; + struct spi_engine_cmd_queue *next; +} spi_engine_cmd_queue; + +typedef struct spi_engine_msg { + uint32_t *tx_buf; + uint32_t *rx_buf; + uint32_t tx_length; + uint32_t rx_length; + struct spi_engine_cmd_queue *cmds; +} spi_engine_msg; + +#endif // SPI_ENGINE_PRIVATE_H diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/frequency/ad9528/ad9528.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/frequency/ad9528/ad9528.c new file mode 100644 index 0000000..7894fd6 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/frequency/ad9528/ad9528.c @@ -0,0 +1,783 @@ +/** + * \file ad9528.c + * \brief Contains source code for configuring and initializing AD9528 clock device + */ + +/** +* \page Disclaimer Legal Disclaimer +* Copyright 2015-2017 Analog Devices Inc. +* Released under the AD9371 API license. +* +*/ + +#include +#include +#include "ad9528.h" +#include "../../../include/error.h" + +static uint32_t _desired_time_to_elapse_ms = 0; + +/** + * \brief Write data to a register + */ +int32_t AD9528_spiWriteByte(ad9528Device_t *device, uint16_t addr, + uint8_t data) +{ + uint8_t buf[3]; + int32_t status; + + buf[0] = (addr >> 8) & 0x7F; + buf[1] = addr & 0xFF; + buf[2] = data; + status = spi_write_and_read_3wire(device->spi_desc, + buf, NULL, 3, 0); + + return status; +} + +/** + * \brief Read data from a register + */ +int32_t AD9528_spiReadByte(ad9528Device_t *device, uint16_t addr, + uint8_t *readdata) +{ + uint8_t tx_buf[2]; + uint8_t rx_buf[1]; + int32_t status; + + tx_buf[0] = 0x80 | addr >> 8; + tx_buf[1] = addr & 0xFF; + + status = spi_write_and_read_3wire(device->spi_desc, + tx_buf, rx_buf, 2, 1); + + *readdata = rx_buf[0]; + + return status; +} + +/** + * \brief Modify a field of bits from a register + */ +int32_t AD9528_spiWriteField(ad9528Device_t *device, + uint16_t addr, uint8_t fieldVal, uint8_t mask, uint8_t startBit) +{ + int32_t errVal; + uint8_t readVal; + + errVal = AD9528_spiReadByte(device, addr, &readVal); + if (errVal < 0) + return errVal; + + readVal = (readVal & ~mask) | ((fieldVal << startBit) & mask); + + return AD9528_spiWriteByte(device, addr, readVal); +} + +/** + * \brief Set the desired timeout value + */ +int32_t AD9528_setTimeout_ms(uint32_t timeOut_ms) +{ + _desired_time_to_elapse_ms = timeOut_ms; + + return SUCCESS; +} + +/** + * \brief Verify if the desired time has elapsed + */ +int32_t AD9528_hasTimeoutExpired() +{ + udelay(1000); + + _desired_time_to_elapse_ms--; + if (_desired_time_to_elapse_ms > 0) + return SUCCESS; + + return FAILURE; +} + +/** + * \brief Performs a hard reset on the AD9528 DUT + * + * Dependencies + * - device->spiSettings->chipSelectIndex + * + * \param device is structure pointer to the AD9528 data structure containing settings + * \return Returns enum ADI_ERR, ADIERR_OK=pass, !ADIERR_OK=fail + */ +int32_t AD9528_resetDevice(ad9528Device_t *device) +{ + gpio_direction_output(device->gpio_resetb, 0); + mdelay(100); + gpio_direction_output(device->gpio_resetb, 1); + mdelay(100); + + AD9528_spiWriteByte(device, AD9528_ADDR_ADI_SPI_CONFIG_A, 0x81); + mdelay(100); + AD9528_spiWriteByte(device, AD9528_ADDR_ADI_SPI_CONFIG_A, 0x00); + mdelay(100); + + return SUCCESS; +} + +/** + * \brief Sets the AD9528 device SPI settings (3wire/4wire, MSBFirst, etc). + * + * This function will use the settings in the device->spiSettings structure + * to set SPI stream mode, address auto increment direction, MSBFirst/LSBfirst, + * and 3wire/4wire mode. The AD9528 device always uses SPI MODE 0 (CPHA=0, CPOL=0). + * This function will update your device->spiSettings to set CHPA=0 and CPOL=0 and + * longInstructionWord =1 to use a 16bit instruction word. + * + * Dependencies + * - writes device->spiSettings->CPHA = 0 + * - writes device->spiSettings->CPOL = 0 + * - writes device->spiSettings->longInstructionWord = 1 + * + * - device->spiSettings->MSBFirst + * - device->spiSettings->enSpiStreaming + * - device->spiSettings->autoIncAddrUp + * - device->spiSettings->fourWireMode + * + * \param device is structure pointer to the AD9528 data structure containing settings + * + * \return Returns enum ADI_ERR, ADIERR_OK=pass, !ADIERR_OK=fail + */ +int32_t AD9528_setSpiSettings(ad9528Device_t *device) +{ + uint8_t spiReg = 0; + + device->spiSettings->CPHA = 0; + device->spiSettings->CPOL = 0; + device->spiSettings->longInstructionWord = 1; + + if (device->spiSettings->MSBFirst == 0) { + spiReg |= 0x42; /* SPI bit is 1=LSB first */ + } + + if (device->spiSettings->autoIncAddrUp > 0) { + spiReg |= 0x24; + } + + if (device->spiSettings->fourWireMode > 0) { + spiReg |= 0x18; + } + + AD9528_spiWriteByte(device, AD9528_ADDR_ADI_SPI_CONFIG_A, spiReg); + + if (device->spiSettings->enSpiStreaming > 0) { + /* Allow SPI streaming mode: SPI message ends when chip select deasserts */ + AD9528_spiWriteByte(device, AD9528_ADDR_ADI_SPI_CONFIG_B, 0x00); + } else { + /* Force single instruction mode */ + AD9528_spiWriteByte(device, AD9528_ADDR_ADI_SPI_CONFIG_B, 0x80); + } + + return SUCCESS; +} + +/** + * \brief Helper function for ADI transceiver eval boards to init the AD9528 data structure + * + * This function inits the AD9528 device data structure with defaults that + * are known to work with the ADI AD9369 and AD9371 evaluation boards. NOTE: This function will + * modify the contents of the AD9528 device data structure. + * + * Dependencies + * - spi->chipSelectIndex + * - spi + * + * \param device Pointer to the AD9528 device data structure to initialize + * \param vcxoFrequency_Hz The VCXO frequency that is connected to the AD9528 + * \param refAFrequency_Hz The reference clock frequency being input into the AD9528 REFCLK A input + * \param outputDeviceClock_Hz Desired output clock frequency to the FPGA and transceiver device + * + * \return Returns enum ADI_ERR, 0=pass, >0 = fail +*/ +int32_t AD9528_initDeviceDataStruct(ad9528Device_t *device, + uint32_t vcxoFrequency_Hz, uint32_t refAFrequency_Hz, + uint32_t outputDeviceClock_Hz) +{ +// uint16_t chDiv; +// uint8_t i = 0; +// +// uint32_t pll1PfdFreq_Hz = 0; +// uint8_t pll1Ndiv = 0; +// uint8_t validPll1DivSetting = 0; +// uint8_t refAdiv = 1; +// +// +// uint8_t pll2RfDiv = 3; /* Valid values 3,4,5 */ +// uint16_t pll2Ndiv = 1; /* Valid values 1-256 */ +// uint32_t pll2InputFreq_Hz = 0; +// uint64_t pll2VcoFreq_Hz = 0; +// uint16_t pll2NdivStart = 0; +// uint8_t validPll2DivSetting = 0; +// uint64_t chDivU64 = 0; +// +// static const uint64_t PLL2_MIN_VCO_FREQ_HZ = 3450000000ULL; +// static const uint64_t PLL2_MAX_VCO_FREQ_HZ = 4025000000ULL; + +// struct spi_init_param spi_param; +// int32_t status; +// +// status = gpio_get(&device->gpio_resetb, CLK_RESETB); +// status |= gpio_get(&device->gpio_sysref_req, ADRV_SYSREF_REQ); +// +// spi_param.id = 0; +// spi_param.mode = SPI_MODE_0; +// spi_param.chip_select = CLK_CS; +// status |= spi_init(&device->spi_desc, &spi_param); +// +// if (status != SUCCESS) +// return FAILURE; + + /******************************************************************* + * PLL1 Settings + ******************************************************************* + */ + + /* find valid PLL1 Ndiv and REFA div */ +// for (pll1Ndiv = 1; pll1Ndiv < 20; pll1Ndiv++) { +// pll1PfdFreq_Hz = vcxoFrequency_Hz / pll1Ndiv; +// +// if ((pll1PfdFreq_Hz <= 110000000) && (pll1PfdFreq_Hz >= 1000000) && +// (pll1PfdFreq_Hz <= refAFrequency_Hz) && +// (refAFrequency_Hz % pll1PfdFreq_Hz == 0) +// ) { +// validPll1DivSetting = 1; +// refAdiv = refAFrequency_Hz / pll1PfdFreq_Hz ; +// break; +// } +// } +// +// if (validPll1DivSetting == 0) { +// return FAILURE; +// } + +// device->pll1Settings->nDivider = +// pll1Ndiv; //This init function always runs PLL1 at 30.72MHz Pfd rate +// device->pll1Settings->refA_bufferCtrl = +// DIFFERENTIAL; //DISABLED = 0, SINGLE_ENDED, NEG_SINGLE_ENDED, DIFFERENTIAL +// device->pll1Settings->refA_Divider = refAdiv; +// device->pll1Settings->refA_Frequency_Hz = refAFrequency_Hz; +// device->pll1Settings->refB_bufferCtrl = +// DISABLED; //DISABLED = 0, SINGLE_ENDED, NEG_SINGLE_ENDED, DIFFERENTIAL +// device->pll1Settings->refB_Divider = 1; +// device->pll1Settings->refB_Frequency_Hz = 0; +// device->pll1Settings->vcxoBufferCtrl = +// NEG_SINGLE_ENDED; //DISABLED = 0, SINGLE_ENDED, NEG_SINGLE_ENDED, DIFFERENTIAL +// device->pll1Settings->vcxo_Frequency_Hz = vcxoFrequency_Hz; + + /******************************************************************* + * PLL2 Settings + ******************************************************************* + */ + + /******************************************************************* + * Output Distribution Settings + * 9369CE02A uses the following clock outputs + * OUT 1: FPGA REFCLK + * OUT 3: FPGA SYSREF + * OUT 12: DUT SYSREF + * OUT 13: DUT REFCLK + ******************************************************************* + */ + + /* PLL 2 input clock can be several different things. Currently hard coded by this init funciton to use + * PLL 1 output (VCXO Frequency) with no scaling + */ +// pll2InputFreq_Hz = device->pll1Settings->vcxo_Frequency_Hz; +// validPll2DivSetting = 0; + + /* Sweep PLL2 Divider settings to find something that has a valid integer divide (3,4,5)*/ +// for (pll2RfDiv = 3; pll2RfDiv <= 5; pll2RfDiv++) { +// +// pll2NdivStart = (PLL2_MIN_VCO_FREQ_HZ / pll2RfDiv / pll2InputFreq_Hz); +// /* find valid PLL2 Ndiv (1-256) */ +// for (pll2Ndiv = pll2NdivStart; pll2Ndiv <= 256; pll2Ndiv++) { +// pll2VcoFreq_Hz = (pll2InputFreq_Hz * pll2RfDiv * pll2Ndiv); +// +// if (pll2VcoFreq_Hz < PLL2_MIN_VCO_FREQ_HZ) { +// /* Continue to next PLL2 N div */ +// continue; +// } else if (pll2VcoFreq_Hz > PLL2_MAX_VCO_FREQ_HZ) { +// /* Break to next PLL2 RF div */ +// break; +// } +// +// if (((pll2VcoFreq_Hz / pll2RfDiv) % outputDeviceClock_Hz) == 0) { +// +// device->pll2Settings->rfDivider = pll2RfDiv; +// device->pll2Settings->n2Divider = pll2Ndiv; +// device->pll2Settings->totalNdiv = pll2RfDiv * pll2Ndiv; +// chDivU64 = ((pll2VcoFreq_Hz / pll2RfDiv) / outputDeviceClock_Hz); +// +// if ((chDivU64 < 1) || (chDivU64 > 256)) { +// return FAILURE; +// } else { +// chDiv = (uint16_t)(chDivU64); +// } +// +// validPll2DivSetting = 1; +// break; +// } +// } +// +// if (validPll2DivSetting == 1) { +// break; +// } +// } +// +// if (validPll2DivSetting == 0) { +// return FAILURE; +// } +// +// device->outputSettings->outPowerDown = (uint16_t)( +// ~0x318A); //enable outputs 13,12,8,7,3 and 1 +// +// for (i=0; i<14; i++) { +// device->outputSettings->outSource[i] = +// CHANNEL_DIV; //CHANNEL_DIV , PLL1_OUTPUT, SYSREF, INV_PLL1_OUTPUT +// device->outputSettings->outBufferCtrl[i] = LVDS; //LVDS, LVDS_BOOST, HSTL +// device->outputSettings->outChannelDiv[i] = chDiv; +// device->outputSettings->outFrequency_Hz[i] = outputDeviceClock_Hz; +// device->outputSettings->outAnalogDelay[i] = 0; +// device->outputSettings->outDigitalDelay[i] = 0; +// } +// +// +// device->outputSettings->outSource[3] = SYSREF; +// device->outputSettings->outSource[7] = SYSREF; +// device->outputSettings->outSource[12] = SYSREF; +// +// +// device->sysrefSettings->sysrefDivide = +// 512; //SYSREF frequency = VCXO / 2 / sysrefDivde +// device->sysrefSettings->sysrefNshotMode = +// ONE_PULSE; //ONE_PULSE=0, TWO_PULSES=2, FOUR_PULSES=3, SIX_PULSES=4, EIGHT_PULSES=5 +// device->sysrefSettings->sysrefPatternMode = +// CONTINUOUS; //NSHOT=0, CONTINUOUS=1, PRBS=2, STOP=3 +// device->sysrefSettings->sysrefPinBufferMode = +// DISABLED; //DISABLED = 0, SINGLE_ENDED, NEG_SINGLE_ENDED, DIFFERENTIAL +// device->sysrefSettings->sysrefPinEdgeMode = +// LEVEL_ACTIVE_HIGH; //LEVEL_ACTIVE_HIGH=0, LEVEL_ACTIVE_LOW=1, RISING_EDGE=2, FALLING_EDGE=3 +// device->sysrefSettings->sysrefRequestMethod = PIN; //SPI=0,PIN=1 +// device->sysrefSettings->sysrefSource = +// INTERNAL; //EXTERNAL=0, EXT_RESAMPLED=1, INTERNAL=2 + + return SUCCESS; +} + +int32_t AD9528_init(ad9528Device_t **device, const ad9528Init_t *init_device) +{ + int ret = SUCCESS; + ad9528Device_t *dev; + + dev = (ad9528Device_t *)malloc(sizeof(*dev)); + if (!dev) + return FAILURE; + + /* SPI */ + ret = spi_init(&dev->spi_desc, init_device->spi_init); + ret |= gpio_get(&dev->gpio_resetb, init_device->gpio_init_resetb); + ret |= gpio_get(&dev->gpio_sysref_req, init_device->gpio_init_sysref_req); + + gpio_direction_output(dev->gpio_resetb, 0); + gpio_direction_output(dev->gpio_sysref_req, 0); + + AD9528_resetDevice(dev); + + dev->spiSettings = init_device->spiSettings; + dev->pll1Settings = init_device->pll1Settings; + dev->pll2Settings = init_device->pll2Settings; + dev->outputSettings = init_device->outputSettings; + dev->sysrefSettings = init_device->sysrefSettings; + + *device = dev; + + return ret; +} + +/** + * \brief Initializes the AD9528 by writing all SPI registers + * + * Initializes the AD9528 for use with the AD9369 transceiver. Designed to work with + * a reference clock of 122.88MHz. Reference clock is expected to be 30.72MHz. + * The output clocks from the AD9528 can run at 122.88M, 153.6M, 184.32M, 245.76M, or 307.2M. + * Typical setup: REFA frequency = 30.72, VCXO = 122.88MHz + * SYNC(SYSREF) pulses output at rate of 120kHz + * + * Dependencies + * - All parameters in device structure + * + * \param device is structure pointer to AD9528 clock device structure + * + * \return Returns enum ADI_ERR, 0=pass, >0 = fail + */ +int32_t AD9528_config(ad9528Device_t + *device) //writes all registers to desired values, leaves clock outputs off, SYSREF output disabled. +{ + uint8_t pll2_Adiv=0; + uint8_t pll2_Bdiv=0; + uint8_t reg108=0; + uint8_t reg109=0x4; + uint8_t i = 0; + uint8_t j = 0; + uint8_t reg508; + int32_t retval = SUCCESS; + + retval = AD9528_setSpiSettings(device); + if (retval != SUCCESS) { + return retval; + } + + /********************************************************************** + * Configure PLL1 + ********************************************************************** + */ + + AD9528_spiWriteByte(device, AD9528_ADDR_REF_A_DIVIDER_LSB, + device->pll1Settings->refA_Divider & 0xFF); //REF A div[7:0] + AD9528_spiWriteByte(device, AD9528_ADDR_REF_A_DIVIDER_MSB, + (device->pll1Settings->refA_Divider >> 8) & 0x03); //REF A div[9:8] + AD9528_spiWriteByte(device, AD9528_ADDR_REF_B_DIVIDER_LSB, + device->pll1Settings->refB_Divider & 0xFF); //REF B div[7:0] + AD9528_spiWriteByte(device, AD9528_ADDR_REF_B_DIVIDER_MSB, + (device->pll1Settings->refB_Divider >> 8) & 0x03); //REF B div[9:8] + AD9528_spiWriteByte(device, AD9528_ADDR_PLL1_N_DIV_LSB, + device->pll1Settings->nDivider & 0xFF); //PLL1 N div[7:0] + AD9528_spiWriteByte(device, AD9528_ADDR_PLL1_N_DIV_MSB, + (device->pll1Settings->nDivider >> 8) & 0x03); //PLL1 N div[9:8] + AD9528_spiWriteByte(device, AD9528_ADDR_PLL1_CHARGEPUMP, + 0x0A); //PLL1 Charge Pump control 1 + AD9528_spiWriteByte(device, AD9528_ADDR_PLL1_CP_CTRL2, + 0x03); //PLL1 Charge Pump control 2 + + switch (device->pll1Settings->refA_bufferCtrl) { + case DISABLED: + reg108 &= ~0x28; + reg109 &= ~0x01; + break; //clear all REFA enable bits + case SINGLE_ENDED: + reg108 |= 0x08; + reg109 &= ~0x01; + break; //Set REFA receiver enable, clear REFA SE negative input enable + case NEG_SINGLE_ENDED: + reg108 |= 0x08; + reg108 &= ~0x10; + reg109 |= 0x01; + break; //Disable REFA diff receiver, enable REFA receiver and use negative REFA input + case DIFFERENTIAL: + reg108 |= 0x28; + reg109 &= ~0x01; + break; + default: + reg108 |= 0x08; + reg109 &= ~0x01; + break; //default to single ended case + } + switch (device->pll1Settings->refB_bufferCtrl) { + case DISABLED: + reg108 &= ~0x50; + reg109 &= ~0x02; + break; //clear all REFB enable bits + case SINGLE_ENDED: + reg108 |= 0x10; + reg109 &= ~0x02; + break; //Set REFB receiver enable, clear REFB SE negative input enable + case NEG_SINGLE_ENDED: + reg108 |= 0x10; + reg108 &= ~0x40; + reg109 |= 0x02; + break; //Disable REFB diff receiver, enable REFB receiver and use negative REFB input + case DIFFERENTIAL: + reg108 |= 0x50; + reg109 &= ~0x02; + break; + default: + reg108 |= 0x10; + reg109 &= ~0x02; + break; //default to single ended case + } + switch (device->pll1Settings->vcxoBufferCtrl) { + case DISABLED: + reg108 &= ~0x03; + reg108 |= 0x04; + break; //power down VCXO input + case SINGLE_ENDED: + reg108 &= ~0x07; + break; //Clear VCXO receiver power down, defaults to Single ended mode + case NEG_SINGLE_ENDED: + reg108 &= ~0x07; + reg108 |= 0x02; + break; //Clear VCXO power down, enable Negative single ended mode[1] + case DIFFERENTIAL: + reg108 &= ~0x07; + reg108 |= 0x01; + break; //Clear VCXO power down, enable differential input buffer[0] + default: + reg108 &= ~0x07; + break; //default to positive single ended case + } + + + AD9528_spiWriteByte(device, AD9528_ADDR_INPUT_RECEIVERS1, + reg108); //Input receiver settings (single ended or differential clock inputs) + AD9528_spiWriteByte(device, AD9528_ADDR_INPUT_RECEIVERS2, + reg109); //Input receiver settings + AD9528_spiWriteByte(device, AD9528_ADDR_INPUT_RECEIVERS3, + 0x0A); //Reference selection mode + AD9528_spiWriteByte(device, AD9528_ADDR_PLL1_FASTLOCK, + 0x00); //Disable Fast lock + + /********************************************************************** + * Configure PLL2 + ********************************************************************** + */ + pll2_Bdiv = device->pll2Settings->totalNdiv / 4; + pll2_Adiv = device->pll2Settings->totalNdiv % 4; + + if (pll2_Adiv > 3) { //A div valid range (0 to 3) + return FAILURE; + } + if (pll2_Bdiv < 3 || pll2_Bdiv > 63) { + return FAILURE; + } + AD9528_spiWriteByte(device, AD9528_ADDR_PLL2_CHARGEPUMP, + 0xE6); //PLL2 Charge Pump[7:0] + AD9528_spiWriteByte(device, AD9528_ADDR_PLL2_N_DIV, + ((pll2_Adiv & 0x3) << 6) | (pll2_Bdiv & 0x3F)); //PLL2 A and B divider + AD9528_spiWriteByte(device, AD9528_ADDR_PLL2_CTRL, 0x03); //PLL2 control + AD9528_spiWriteByte(device, AD9528_ADDR_PLL2_VCO_CTRL, 0x01); //Manual VCO CAL + + if (device->pll2Settings->rfDivider < 3 + || device->pll2Settings->rfDivider > 5) { + return FAILURE; + } + + AD9528_spiWriteByte(device, AD9528_ADDR_PLL2_VCO_DIV, + device->pll2Settings->rfDivider); //VCO divider + AD9528_spiWriteByte(device, AD9528_ADDR_PLL2_LF_CTRL1, + 0x3A); //PLL2 Loop filter [7:0] + AD9528_spiWriteByte(device, AD9528_ADDR_PLL2_LF_CTRL2, + 0x00); //PLL2 Loop filter [8] + AD9528_spiWriteByte(device, AD9528_ADDR_PLL2_RDIV, 0x01); //R divider + AD9528_spiWriteByte(device, AD9528_ADDR_PLL2_REPLICA_CHDIV, + (device->pll2Settings->totalNdiv / device->pll2Settings->rfDivider) + -1 ); //Replica channel divider: Ndivider = rfDivider * R divider + AD9528_spiWriteByte(device, AD9528_ADDR_PLL2_REPLICA_DIV_PHASE, + 0x00); //Replica Channel Divider phase + + /********************************************************************** + * Configure Output Distribution + ********************************************************************** + */ + for (i=0; i<14; i=i+1) { + AD9528_spiWriteByte(device, AD9528_ADDR_CH_OUT0_CTRL1 + j, + ((device->outputSettings->outSource[i] & 0x7) << 5) | + (device->outputSettings->outAnalogDelay[i] & 0x1F)); + AD9528_spiWriteByte(device, AD9528_ADDR_CH_OUT0_CTRL2 + j, + ((device->outputSettings->outBufferCtrl[i] & 0x3) << 6) | + (device->outputSettings->outDigitalDelay[i] & 0x3F)); + AD9528_spiWriteByte(device, AD9528_ADDR_CH_OUT0_CHDIV + j, + device->outputSettings->outChannelDiv[i]-1); + j = j+3; + } + + AD9528_spiWriteByte(device, AD9528_ADDR_OUTPUT_SYNC, 0x00); //Sync outputs + AD9528_spiWriteByte(device, AD9528_ADDR_MASK_SYNC1, 0x00); //Mask SYNC[7:0] + AD9528_spiWriteByte(device, AD9528_ADDR_MASK_SYNC2, 0x00); //Mask SYNC[13:8] + AD9528_spiWriteByte(device, AD9528_ADDR_EN_OUTPUT_PATH_SEL1, + 0x00); //Bypass SYSREF resample [6:0], bit0:Enable VCXO to feed output distrubution + AD9528_spiWriteByte(device, AD9528_ADDR_EN_OUTPUT_PATH_SEL2, + 0x00); //Bypass SYSREF resample [13:7] + + /********************************************************************** + * Configure SYSREF operation + ********************************************************************** + */ + AD9528_spiWriteByte(device, AD9528_ADDR_SYSERF_DIV_LSB, + device->sysrefSettings->sysrefDivide & 0xFF); //SYSREF divider [7:0] + AD9528_spiWriteByte(device, AD9528_ADDR_SYSERF_DIV_MSB, + (device->sysrefSettings->sysrefDivide >> 8) & 0xFF); //SYSREF divider [7:0] + + AD9528_spiWriteByte(device, AD9528_ADDR_SYSREF_CTRL3, + ((device->sysrefSettings->sysrefRequestMethod & 0x1) << 7) | + ((device->sysrefSettings->sysrefPinEdgeMode & 0x3) << 5) ); + + AD9528_spiWriteByte(device, AD9528_ADDR_SYSREF_CTRL4, + ((device->sysrefSettings->sysrefSource & 0x03) << 6) | + ((device->sysrefSettings->sysrefPatternMode & 0x3) << 4 ) | + ((device->sysrefSettings->sysrefNshotMode & 0x7) << 1) ); + + if(device->sysrefSettings->sysrefPinBufferMode == DISABLED) { + AD9528_spiWriteByte(device, AD9528_ADDR_SYSREF_CTRL5, + 0x04); //power down input buffer for external SYSREF + } else if(device->sysrefSettings->sysrefPinBufferMode == SINGLE_ENDED) { + AD9528_spiWriteByte(device, AD9528_ADDR_SYSREF_CTRL5, + 0x00); //enable positive singled ended input + } else if(device->sysrefSettings->sysrefPinBufferMode == NEG_SINGLE_ENDED) { + AD9528_spiWriteByte(device, AD9528_ADDR_SYSREF_CTRL5, + 0x02); //enable negative singled ended input + } else if(device->sysrefSettings->sysrefPinBufferMode == DIFFERENTIAL) { + AD9528_spiWriteByte(device, AD9528_ADDR_SYSREF_CTRL5, + 0x01); //enable differential input buffer + } else { + AD9528_spiWriteByte(device, AD9528_ADDR_SYSREF_CTRL5, + 0x04); //power down input buffer for external SYSREF + } + + /********************************************************************** + * Power down control + ********************************************************************** + */ + + if (device->pll1Settings->bypassPll1 > 0) { + AD9528_spiWriteByte(device, AD9528_ADDR_POWERDOWN_CTRL, + 0x14);// power up PLL2 and output distribution, power down PLL1 + } else { + AD9528_spiWriteByte(device, AD9528_ADDR_POWERDOWN_CTRL, + 0x10);// power up PLL1, PLL2 and output distribution + } + + AD9528_spiWriteByte(device, AD9528_ADDR_CH_POWERDOWN1, + device->outputSettings->outPowerDown & 0xFF); //power down out[7:0] + AD9528_spiWriteByte(device, AD9528_ADDR_CH_POWERDOWN2, + ((device->outputSettings->outPowerDown >> 8) & 0x3F)); //power down out[13:8] + AD9528_spiWriteByte(device, AD9528_ADDR_LDO_ENABLES1, + 0xFF); //LDO enable per output channel [7:0] + AD9528_spiWriteByte(device, AD9528_ADDR_LDO_ENABLES2, + 0xFF); //LDO enable per output channel [15:8] + AD9528_spiWriteByte(device, AD9528_ADDR_STATUS0_CTRL, + 0x07); //STATUS 0 outputs REFA present + AD9528_spiWriteByte(device, AD9528_ADDR_STATUS1_CTRL, + 0x01); //STATUS 1 outputs PLL1 and PLL2 locked + AD9528_spiWriteByte(device, AD9528_ADDR_STATUS_OE, + 0x0C);// Make Status0 and Status1 outputs after reset. + + AD9528_spiWriteByte(device, AD9528_ADDR_IO_UPDATE, + 0x01); //IO update to make all these settings valid in the 9528 + + AD9528_setTimeout_ms(1000); + + do { + AD9528_spiReadByte(device, AD9528_ADDR_STATUS_READBACK0, ®508); + if(AD9528_hasTimeoutExpired() > 0) { + return FAILURE; + } + } while((reg508 & 0x26) != 0x26); + + return SUCCESS; +} + + +/// Send a SPI message to request a SYSREF pulse or continuous SYSREF from the AD9528. +/*******************************************************************************//** + * Requests a SYSREF from the AD9528. It will use whatever settings for SYSREF + * that are current configured in the AD9528. This could be a single pulse, + * multiple pulses, or continuous pulses. + * Dependencies + * - device->spiSettings + * - device->sysrefSettings->sysrefPatternMode + * + * \param device is structure pointer to AD9528 clock device structure + * \param enableSYSREF If NSHOT SYSREF mode, this parameter is ignored. If PRBS/CONTINUOUS SYSREF mode, 1= enable SYSREF, 0= disable SYSREF. + * + * \return Returns enum ADI_ERR, 0=pass, >0 = fail + ***********************************************************************************/ +int32_t AD9528_requestSysref(ad9528Device_t *device, uint8_t enableSYSREF) +{ + uint8_t _enableSYSREF=0; + + if (device->sysrefSettings->sysrefPatternMode == NSHOT || enableSYSREF > 0) { + _enableSYSREF = 1; + } else { + _enableSYSREF = 0; + } + + AD9528_spiWriteField(device, AD9528_ADDR_SYSREF_CTRL4, _enableSYSREF, 0x01, + 0x00); //Set SPI SYSREF request bit + AD9528_spiWriteByte(device, AD9528_ADDR_IO_UPDATE, + 0x01); //IO update to make all these settings valid in the 9528 + + return SUCCESS; +} + +/// Allow changing the 9528 SYSREF frequency and pattern mode(CONTINUOUS, PRBS vs NSHOT mode) +/*******************************************************************************//** + * NOTE: this function will update the device structure with the parameter values passed + * into this function. The device->sysrefSettings->sysrefPatternMode, device->sysrefSettings->sysrefNshotMode, + * and device->sysrefSettings->sysrefDivide values will be updated in the device structure. + * + * Dependencies + * - device->spiSettings + * + * \param device is structure pointer to AD9528 clock device structure + * \param divideFromPll1Out Sets the SYSREF pulse frequency. SYSREF frequency = PLL1_VCXO frequency/divideFromPll1Out + * \param sysrefPatternMode Valid settings: NSHOT, CONTINUOUS, PRBS, STOP + * \param nShotPulses Valid settings: ONE_PULSE=0, TWO_PULSES=2, FOUR_PULSES=3, SIX_PULSES=4, EIGHT_PULSES=5 + * + * \return Returns enum ADI_ERR, 0=pass, >0 = fail + ***********************************************************************************/ +int32_t AD9528_setupSYSREF(ad9528Device_t *device, + uint16_t divideFromPll1Out, sysrefPatternMode_t sysrefPatternMode, + sysrefNshotMode_t nShotPulses) +{ + uint8_t reg403=0; + + device->sysrefSettings->sysrefPatternMode = sysrefPatternMode; + device->sysrefSettings->sysrefNshotMode = nShotPulses; + device->sysrefSettings->sysrefDivide = divideFromPll1Out >> + 1; //SYSREF frequency is (PLL1)VCXO frequency / 2 / device->sysrefSettings->sysrefDivide + + AD9528_spiReadByte(device, AD9528_ADDR_SYSREF_CTRL4, ®403); + + reg403 = reg403 & ~0x01; //clear bit 0 to stop a continuous sysref if it is on. + AD9528_spiWriteByte(device, AD9528_ADDR_SYSREF_CTRL4, reg403); + + reg403 = reg403 & ~0x3E; //clear bits for pattern mode and Nshot mode bitfields + reg403 = reg403 | ((sysrefPatternMode & 0x3) << 4) | ((nShotPulses & 0x7) << 1); + AD9528_spiWriteByte(device, AD9528_ADDR_SYSREF_CTRL4, + reg403); //update internal sysref pattern mode and Nshot mode (number of pulses) + + //SYSREF request bit x403[0] is left 0. The next call to AD9528_requestSysref will start the SYSREF + AD9528_spiWriteByte(device, AD9528_ADDR_IO_UPDATE, + 0x01); //IO update to make all these settings valid in the 9528 + + + return SUCCESS; +} + +int32_t AD9528_sysrefReq(ad9528Device_t *device, + ad9528SysrefReqMode_t mode) +{ + if (mode == SYSREF_CONT_ON) + gpio_direction_output(device->gpio_sysref_req, 1); + else if (mode == SYSREF_CONT_OFF) + gpio_direction_output(device->gpio_sysref_req, 0); + else if (mode == SYSREF_PULSE) { + gpio_direction_output(device->gpio_sysref_req, 1); + mdelay(1); + gpio_direction_output(device->gpio_sysref_req, 0); + } else + return FAILURE; + + return SUCCESS; +} + +/** + * \brief Free the allocated resources + */ +int32_t AD9528_remove(ad9528Device_t *device) +{ + int32_t status; + + status = gpio_remove(device->gpio_resetb); + status |= gpio_remove(device->gpio_sysref_req); + status |= spi_remove(device->spi_desc); + + return SUCCESS; +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/frequency/ad9528/ad9528.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/frequency/ad9528/ad9528.h new file mode 100644 index 0000000..9f89655 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/frequency/ad9528/ad9528.h @@ -0,0 +1,245 @@ +/** + * \file ad9528.h + * \brief Contains function declarations and ad9528Device_t structure typedef for ad9528.c + */ + +/** +* \page Disclaimer Legal Disclaimer +* Copyright 2015-2017 Analog Devices Inc. +* Released under the AD9371 API license. +* +*/ + +#ifndef _AD9528_H_ +#define _AD9528_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include +#include "delay.h" +#include "spi.h" +#include "gpio.h" + +/******************************************************************************/ +/********************** Macros and Constants Definitions **********************/ +/******************************************************************************/ + +/* AD9528 SPI Address defines */ +#define AD9528_ADDR_ADI_SPI_CONFIG_A 0x000 +#define AD9528_ADDR_ADI_SPI_CONFIG_B 0x001 +#define AD9528_ADDR_IO_UPDATE 0x00F + +#define AD9528_ADDR_REF_A_DIVIDER_LSB 0x100 +#define AD9528_ADDR_REF_A_DIVIDER_MSB 0x101 +#define AD9528_ADDR_REF_B_DIVIDER_LSB 0x102 +#define AD9528_ADDR_REF_B_DIVIDER_MSB 0x103 +#define AD9528_ADDR_PLL1_N_DIV_LSB 0x104 +#define AD9528_ADDR_PLL1_N_DIV_MSB 0x105 +#define AD9528_ADDR_PLL1_CHARGEPUMP 0x106 +#define AD9528_ADDR_PLL1_CP_CTRL2 0x107 +#define AD9528_ADDR_INPUT_RECEIVERS1 0x108 +#define AD9528_ADDR_INPUT_RECEIVERS2 0x109 +#define AD9528_ADDR_INPUT_RECEIVERS3 0x10A +#define AD9528_ADDR_PLL1_FASTLOCK 0x10B + +#define AD9528_ADDR_PLL2_CHARGEPUMP 0x200 +#define AD9528_ADDR_PLL2_N_DIV 0x201 +#define AD9528_ADDR_PLL2_CTRL 0x202 +#define AD9528_ADDR_PLL2_VCO_CTRL 0x203 +#define AD9528_ADDR_PLL2_VCO_DIV 0x204 +#define AD9528_ADDR_PLL2_LF_CTRL1 0x205 +#define AD9528_ADDR_PLL2_LF_CTRL2 0x206 +#define AD9528_ADDR_PLL2_RDIV 0x207 +#define AD9528_ADDR_PLL2_REPLICA_CHDIV 0x208 +#define AD9528_ADDR_PLL2_REPLICA_DIV_PHASE 0x209 + +#define AD9528_ADDR_CH_OUT0_CTRL1 0x300 +#define AD9528_ADDR_CH_OUT0_CTRL2 0x301 +#define AD9528_ADDR_CH_OUT0_CHDIV 0x302 +#define AD9528_ADDR_OUTPUT_SYNC 0x32A +#define AD9528_ADDR_MASK_SYNC1 0x32B +#define AD9528_ADDR_MASK_SYNC2 0x32C +#define AD9528_ADDR_EN_OUTPUT_PATH_SEL1 0x32D +#define AD9528_ADDR_EN_OUTPUT_PATH_SEL2 0x32E + +#define AD9528_ADDR_SYSERF_DIV_LSB 0x400 +#define AD9528_ADDR_SYSERF_DIV_MSB 0x401 +#define AD9528_ADDR_SYSREF_CTRL3 0x402 +#define AD9528_ADDR_SYSREF_CTRL4 0x403 +#define AD9528_ADDR_SYSREF_CTRL5 0x404 + +#define AD9528_ADDR_POWERDOWN_CTRL 0x500 +#define AD9528_ADDR_CH_POWERDOWN1 0x501 +#define AD9528_ADDR_CH_POWERDOWN2 0x502 +#define AD9528_ADDR_LDO_ENABLES1 0x503 +#define AD9528_ADDR_LDO_ENABLES2 0x504 +#define AD9528_ADDR_STATUS0_CTRL 0x505 +#define AD9528_ADDR_STATUS1_CTRL 0x506 +#define AD9528_ADDR_STATUS_OE 0x507 +#define AD9528_ADDR_STATUS_READBACK0 0x508 +#define AD9528_ADDR_STATUS_READBACK1 0x509 + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ + +///Enum to select what source outputs from each AD9528 output channel +typedef enum { + AD9528_CHANNEL_DIV = 0, PLL1_OUTPUT = 1, //VCXO frequency + SYSREF = 2, + INV_PLL1_OUTPUT = 5 //VCXO inverted +} ad9528outSourceSel_t; + +///Enum to select what output buffer standard is used for the clock outputs +typedef enum { + AD9528_LVDS = 0, AD9528_LVDS_BOOST = 1, AD9528_HSTL = 2 +} ad9528outBufferControl_t; + +///Enum to select the clock input buffer mode. +typedef enum { + DISABLED = 0, SINGLE_ENDED, NEG_SINGLE_ENDED, DIFFERENTIAL +} ad9528RefBuffer_t; + +/// Structure to hold AD9528 PLL1 settings +typedef struct { + uint32_t refA_Frequency_Hz; + uint16_t refA_Divider; + ad9528RefBuffer_t refA_bufferCtrl; + + uint32_t refB_Frequency_Hz; + uint16_t refB_Divider; + ad9528RefBuffer_t refB_bufferCtrl; + + uint32_t vcxo_Frequency_Hz; + ad9528RefBuffer_t vcxoBufferCtrl; + + uint16_t nDivider; + uint8_t bypassPll1; +} ad9528pll1Settings_t; + +/// Structure to hold AD9528 PLL2 settings +typedef struct { + uint8_t rfDivider; //VCO divider: Valid range (3,4,5) + uint8_t n2Divider; //PLL2 N2 Divider + uint8_t totalNdiv; //NDiv = 4*Bdiv + Adiv //Bdiv valid range (3 to 63), Adiv valid range (0-3) +} ad9528pll2Settings_t; + +/// Structure to hold AD9528 Output Clock settings +typedef struct { + uint16_t outPowerDown; //bit per output, if 1 power down that output. + ad9528outSourceSel_t + outSource[14]; //CHANNEL_DIV, PLL1_OUTPUT, SYSREF, INV_PLL1_OUTPUT + ad9528outBufferControl_t outBufferCtrl[14]; //LVDS, LVDS_BOOST, HSTL + uint8_t outAnalogDelay[14]; //5 bits + uint8_t outDigitalDelay[14]; //6 bits, 1/2 clock resolution @ channel div input frequency + uint8_t outChannelDiv[14]; //8 bit channel divider + uint32_t outFrequency_Hz[14]; //output clock frequency per clock output +} ad9528outputSettings_t; + +///Enum to select the SYSREF output source +typedef enum { + EXTERNAL = 0, EXT_RESAMPLED = 1, INTERNAL = 2 +} sysrefSourceSelection_t; + +///Enum to choose the SYSREF pattern mode. +typedef enum { + NSHOT = 0, CONTINUOUS = 1, PRBS = 2, STOP = 3 +} sysrefPatternMode_t; + +///Enum to select the SYSREF input pin mode. +typedef enum { + LEVEL_ACTIVE_HIGH = 0, LEVEL_ACTIVE_LOW = 1, RISING_EDGE = 2, FALLING_EDGE = 3 +} sysrefPinEdgeMode_t; + +///Enum to select the SYSREF output # of pulses for NSHOT mode. +typedef enum { + ONE_PULSE = 0, TWO_PULSES = 2, FOUR_PULSES = 3, SIX_PULSES = 4, EIGHT_PULSES = 5 +} sysrefNshotMode_t; + +//Enum to select how a SYSREF is requested. +typedef enum { + SPI = 0, PIN = 1 +} sysrefRequestMethod_t; + +/// Structure to hold AD9528 SYSREF output settings +typedef struct { + sysrefRequestMethod_t sysrefRequestMethod; + sysrefSourceSelection_t sysrefSource; + sysrefPinEdgeMode_t sysrefPinEdgeMode; + ad9528RefBuffer_t sysrefPinBufferMode; + sysrefPatternMode_t sysrefPatternMode; + sysrefNshotMode_t sysrefNshotMode; + uint16_t sysrefDivide; + +} ad9528sysrefSettings_t; + +/// Structure to hold AD9528 SPI settings +typedef struct { + uint8_t chipSelectIndex; ///< valid 1~8 + uint8_t writeBitPolarity; ///< the level of the write bit of a SPI write instruction word, value is inverted for SPI read operation + uint8_t longInstructionWord; ///< 1 = 16bit instruction word, 0 = 8bit instruction word + uint8_t MSBFirst; ///< 1 = MSBFirst, 0 = LSBFirst + uint8_t CPHA; ///< clock phase, sets which clock edge the data updates (valid 0 or 1) + uint8_t CPOL; ///< clock polarity 0 = clock starts low, 1 = clock starts high + uint8_t enSpiStreaming; ///< Not implemented. SW feature to improve SPI throughput. + uint8_t autoIncAddrUp; ///< Not implemented. For SPI Streaming, set address increment direction. 1= next addr = addr+1, 0:addr = addr-1 + uint8_t fourWireMode; ///< 1: Use 4-wire SPI, 0: 3-wire SPI (SDIO pin is bidirectional). NOTE: ADI's FPGA platform always uses 4-wire mode. + uint32_t spiClkFreq_Hz; ///< SPI Clk frequency in Hz (default 25000000), platform will use next lowest frequency that it's baud rate generator can create */ +} ad9528spiSettings_t; + +/// Structure to hold AD9528 settings +typedef struct { + struct gpio_desc *gpio_resetb; + struct gpio_desc *gpio_sysref_req; + struct spi_desc *spi_desc; + ad9528spiSettings_t *spiSettings; + ad9528pll1Settings_t *pll1Settings; + ad9528pll2Settings_t *pll2Settings; + ad9528outputSettings_t *outputSettings; + ad9528sysrefSettings_t *sysrefSettings; +} ad9528Device_t; + +typedef struct { + struct gpio_init_param *gpio_init_resetb; + struct gpio_init_param *gpio_init_sysref_req; + struct spi_init_param *spi_init; + ad9528spiSettings_t *spiSettings; + ad9528pll1Settings_t *pll1Settings; + ad9528pll2Settings_t *pll2Settings; + ad9528outputSettings_t *outputSettings; + ad9528sysrefSettings_t *sysrefSettings; +} ad9528Init_t; + +typedef enum { + SYSREF_CONT_ON, + SYSREF_CONT_OFF, + SYSREF_PULSE, +} ad9528SysrefReqMode_t; + +/******************************************************************************/ +/************************ Functions Declarations ******************************/ +/******************************************************************************/ + +//Helper function to setup the 9528 device data structure specifically for the ADI transceiver evaluation cards +int32_t AD9528_initDeviceDataStruct(ad9528Device_t *device, + uint32_t vcxoFrequency_Hz, uint32_t refAFrequency_Hz, + uint32_t outputDeviceClock_Hz); +int32_t AD9528_spiReadByte(ad9528Device_t *device, uint16_t addr, + uint8_t *readdata); + +//AD9528 API functions +int32_t AD9528_hasTimeoutExpired(); +int32_t AD9528_resetDevice(ad9528Device_t *device); +int32_t AD9528_init(ad9528Device_t **device, const ad9528Init_t *init_device); +int32_t AD9528_config(ad9528Device_t + *device); //writes all registers to desired values, leaves clock outputs off. +int32_t AD9528_setupSYSREF(ad9528Device_t *device, + uint16_t divideFromPll1Out, sysrefPatternMode_t sysrefPatternMode, + sysrefNshotMode_t nShotPulses); +int32_t AD9528_requestSysref(ad9528Device_t *device, uint8_t enableSYSREF); +int32_t AD9528_sysrefReq(ad9528Device_t *device, + ad9528SysrefReqMode_t mode); +int32_t AD9528_remove(ad9528Device_t *device); + +#endif diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/frequency/hmc7044/hmc7044.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/frequency/hmc7044/hmc7044.c new file mode 100644 index 0000000..711d18b --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/frequency/hmc7044/hmc7044.c @@ -0,0 +1,927 @@ +/***************************************************************************//** + * @file hmc7044.c + * @brief Implementation of HMC7044, HMC7043 Driver. + * @author DBogdan (dragos.bogdan@analog.com) +******************************************************************************** + * Copyright 2018-2020(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include +#include +#include "hmc7044.h" +#include "../../../include/error.h" +#include "../../../include/util.h" + +/******************************************************************************/ +/********************** Macros and Constants Definitions **********************/ +/******************************************************************************/ +#define HMC7044_WRITE (0 << 15) +#define HMC7044_READ (1 << 15) +#define HMC7044_CNT(x) (((x) - 1) << 13) +#define HMC7044_ADDR(x) ((x) & 0xFFF) + +/* Global Control */ +#define HMC7044_REG_SOFT_RESET 0x0000 +#define HMC7044_SOFT_RESET BIT(0) + +#define HMC7044_REG_REQ_MODE_0 0x0001 +#define HMC7044_RESEED_REQ BIT(7) +#define HMC7044_HIGH_PERF_DISTRIB_PATH BIT(6) +#define HMC7044_HIGH_PERF_PLL_VCO BIT(5) +#define HMC7044_FORCE_HOLDOVER BIT(4) +#define HMC7044_MUTE_OUT_DIV BIT(3) +#define HMC7044_PULSE_GEN_REQ BIT(2) +#define HMC7044_RESTART_DIV_FSM BIT(1) +#define HMC7044_SLEEP_MODE BIT(0) + +#define HMC7044_REG_REQ_MODE_1 0x0002 +#define HMC7044_PLL2_AUTOTUNE_TRIG BIT(2) +#define HMC7044_SLIP_REQ BIT(1) + +#define HMC7044_REG_EN_CTRL_0 0x0003 +#define HMC7044_RF_RESEEDER_EN BIT(5) +#define HMC7044_VCO_SEL(x) (((x) & 0x3) << 3) +#define HMC7044_VCO_EXT 0 +#define HMC7044_VCO_HIGH 1 +#define HMC7044_VCO_LOW 2 +#define HMC7044_SYSREF_TIMER_EN BIT(2) +#define HMC7044_PLL2_EN BIT(1) +#define HMC7044_PLL1_EN BIT(0) + +#define HMC7044_REG_EN_CTRL_1 0x0004 +#define HMC7044_SEVEN_PAIRS(x) ((x) & 0x7f) + +#define HMC7044_REG_GLOB_MODE 0x0005 +#define HMC7044_REF_PATH_EN(x) ((x) & 0xf) +#define HMC7044_RFSYNC_EN BIT(4) +#define HMC7044_VCOIN_MODE_EN BIT(5) +#define HMC7044_SYNC_PIN_MODE(x) (((x) & 0x3) << 6) + +/* PLL1 */ +#define HMC7044_REG_CLKIN0_BUF_CTRL 0x000A +#define HMC7044_REG_CLKIN1_BUF_CTRL 0x000B +#define HMC7044_REG_CLKIN2_BUF_CTRL 0x000C +#define HMC7044_REG_CLKIN3_BUF_CTRL 0x000D +#define HMC7044_REG_OSCIN_BUF_CTRL 0x000E + +#define HMC7044_REG_PLL1_REF_PRIO_CTRL 0x0014 + +#define HMC7044_HIGH_Z_EN BIT(4) +#define HMC7044_LVPECL_EN BIT(3) +#define HMC7044_AC_COUPLING_EN BIT(2) +#define HMC7044_100_OHM_EN BIT(1) +#define HMC7044_BUF_EN BIT(0) + +#define HMC7044_REG_CLKIN_PRESCALER(x) (0x001C + (x)) +#define HMC7044_REG_OSCIN_PRESCALER 0x0020 + +#define HMC7044_REG_PLL1_R_LSB 0x0021 +#define HMC7044_R1_LSB(x) ((x) & 0xff) + +#define HMC7044_REG_PLL1_R_MSB 0x0022 +#define HMC7044_R1_MSB(x) (((x) & 0xff00) >> 8) + +#define HMC7044_REG_PLL1_N_LSB 0x0026 +#define HMC7044_N1_LSB(x) ((x) & 0xff) + +#define HMC7044_REG_PLL1_N_MSB 0x0027 +#define HMC7044_N1_MSB(x) (((x) & 0xff00) >> 8) + +#define HMC7044_REG_PLL1_LOCK_DETECT 0x0028 +#define HMC7044_LOCK_DETECT_SLIP BIT(5) +#define HMC7044_LOCK_DETECT_TIMER(x) ((x) & 0x1f) + +/* PLL2 */ +#define HMC7044_REG_PLL2_FREQ_DOUBLER 0x0032 +#define HMC7044_PLL2_FREQ_DOUBLER_DIS BIT(0) + +#define HMC7044_REG_PLL2_R_LSB 0x0033 +#define HMC7044_R2_LSB(x) ((x) & 0xff) + +#define HMC7044_REG_PLL2_R_MSB 0x0034 +#define HMC7044_R2_MSB(x) (((x) & 0xf00) >> 8) + +#define HMC7044_REG_PLL2_N_LSB 0x0035 +#define HMC7044_N2_LSB(x) ((x) & 0xff) + +#define HMC7044_REG_PLL2_N_MSB 0x0036 +#define HMC7044_N2_MSB(x) (((x) & 0xff00) >> 8) + +#define HMC7044_REG_OSCOUT_PATH 0x0039 +#define HMC7044_REG_OSCOUT_DRIVER_0 0x003A +#define HMC7044_REG_OSCOUT_DRIVER_1 0x003B + +/* GPIO/SDATA Control */ +#define HMC7044_REG_GPI_CTRL(x) (0x0046 + (x)) +#define HMC7044_REG_GPI_SEL(x) ((x) & 0xf) + +#define HMC7044_REG_GPO_CTRL(x) (0x0050 + (x)) +#define HMC7044_GPO_SEL(x) (((x) & 0x3f) << 2) +#define HMC7044_GPO_MODE BIT(1) +#define HMC7044_GPO_EN BIT(0) + +/* SYSREF/SYNC Control */ +#define HMC7044_REG_PULSE_GEN 0x005A +#define HMC7044_PULSE_GEN_MODE(x) ((x) & 0x7) + +#define HMC7044_REG_SYNC 0x005B +#define HMC7044_SYNC_RETIME BIT(2) +#define HMC7044_SYNC_THROUGH_PLL2 BIT(1) +#define HMC7044_SYNC_POLARITY BIT(0) + +#define HMC7044_REG_SYSREF_TIMER_LSB 0x005C +#define HMC7044_SYSREF_TIMER_LSB(x) ((x) & 0xff) + +#define HMC7044_REG_SYSREF_TIMER_MSB 0x005D +#define HMC7044_SYSREF_TIMER_MSB(x) (((x) & 0xf00) >> 8) + +#define HMC7044_CLK_INPUT_CTRL 0x0064 +#define HMC7044_LOW_FREQ_INPUT_MODE BIT(0) +#define HMC7044_DIV_2_INPUT_MODE BIT(1) + +/* Status and Alarm readback */ +#define HMC7044_REG_ALARM_READBACK 0x007D +#define HMC7044_REG_PLL1_STATUS 0x0082 + +#define HMC7044_PLL1_FSM_STATE(x) ((x) & 0x7) +#define HMC7044_PLL1_ACTIVE_CLKIN(x) (((x) >> 3) & 0x3) + +#define HMC7044_PLL2_LOCK_DETECT(x) ((x) & 0x1) +#define HMC7044_SYSREF_SYNC_STAT(x) ((x) & 0x2) +#define HMC7044_CLK_OUT_PH_STATUS(x) ((x) & 0x4) +#define HMC7044_PLL1_PLL2_LOCK_STAT(x) ((x) & 0x8) +#define HMC7044_SYNC_REQ_STATUS(x) ((x) & 0x10) + +/* Other Controls */ +#define HMC7044_REG_CLK_OUT_DRV_LOW_PW 0x009F +#define HMC7044_REG_CLK_OUT_DRV_HIGH_PW 0x00A0 +#define HMC7044_REG_PLL1_DELAY 0x00A5 +#define HMC7044_REG_PLL1_HOLDOVER 0x00A8 +#define HMC7044_REG_VTUNE_PRESET 0x00B0 + +/* Clock Distribution */ +#define HMC7044_REG_CH_OUT_CRTL_0(ch) (0x00C8 + 0xA * (ch)) +#define HMC7044_HI_PERF_MODE BIT(7) +#define HMC7044_SYNC_EN BIT(6) +#define HMC7044_CH_EN BIT(0) +#define HMC7044_START_UP_MODE_DYN_EN (BIT(3) | BIT(2)) + +#define HMC7044_REG_CH_OUT_CRTL_1(ch) (0x00C9 + 0xA * (ch)) +#define HMC7044_DIV_LSB(x) ((x) & 0xFF) + +#define HMC7044_REG_CH_OUT_CRTL_2(ch) (0x00CA + 0xA * (ch)) +#define HMC7044_DIV_MSB(x) (((x) >> 8) & 0xFF) + +#define HMC7044_REG_CH_OUT_CRTL_3(ch) (0x00CB + 0xA * (ch)) +#define HMC7044_REG_CH_OUT_CRTL_4(ch) (0x00CC + 0xA * (ch)) +#define HMC7044_REG_CH_OUT_CRTL_5(ch) (0x00CD + 0xA * (ch)) +#define HMC7044_REG_CH_OUT_CRTL_6(ch) (0x00CE + 0xA * (ch)) +#define HMC7044_REG_CH_OUT_CRTL_7(ch) (0x00CF + 0xA * (ch)) + +#define HMC7044_REG_CH_OUT_CRTL_8(ch) (0x00D0 + 0xA * (ch)) +#define HMC7044_DRIVER_MODE(x) (((x) & 0x3) << 3) +#define HMC7044_DRIVER_Z_MODE(x) (((x) & 0x3) << 0) +#define HMC7044_DYN_DRIVER_EN BIT(5) +#define HMC7044_FORCE_MUTE_EN BIT(7) + +#define HMC7044_NUM_CHAN 14 + +#define HMC7044_LOW_VCO_MIN 2150000 +#define HMC7044_LOW_VCO_MAX 2880000 +#define HMC7044_HIGH_VCO_MIN 2650000 +#define HMC7044_HIGH_VCO_MAX 3200000 + +#define HMC7044_RECOMM_LCM_MIN 30000 +#define HMC7044_RECOMM_LCM_MAX 70000 +#define HMC7044_RECOMM_FPD1 10000 + +#define HMC7044_R1_MAX 65535 +#define HMC7044_N1_MAX 65535 + +#define HMC7044_R2_MIN 1 +#define HMC7044_R2_MAX 4095 +#define HMC7044_N2_MIN 8 +#define HMC7044_N2_MAX 65535 + +#define HMC7044_OUT_DIV_MIN 1 +#define HMC7044_OUT_DIV_MAX 4094 + +/******************************************************************************/ +/************************** Functions Implementation **************************/ +/******************************************************************************/ + +/** + * SPI register write to device. + * @param dev - The device structure. + * @param reg - The register address. + * @param val - The register data. + * @return SUCCESS in case of success, negative error code otherwise. + */ +int32_t hmc7044_write(struct hmc7044_dev *dev, + uint16_t reg, + uint8_t val) +{ + uint8_t buf[3]; + uint16_t cmd; + + cmd = HMC7044_WRITE | HMC7044_CNT(1) | HMC7044_ADDR(reg); + buf[0] = cmd >> 8; + buf[1] = cmd & 0xFF; + buf[2] = val; + return spi_write_and_read_3wire(dev->spi_desc, buf, NULL, + ARRAY_SIZE(buf), 0); +} + +/** + * SPI register read from device. + * @param dev - The device structure. + * @param reg - The register address. + * @param val - The register data. + * @return SUCCESS in case of success, negative error code otherwise. + */ + +int32_t hmc7044_read(struct hmc7044_dev *dev, uint16_t reg, uint8_t *val) +{ + uint8_t wr_buf[2]; + uint8_t rd_data[1]; + uint16_t cmd; + int ret; + + cmd = HMC7044_READ | HMC7044_CNT(1) | HMC7044_ADDR(reg); + wr_buf[0] = cmd >> 8; + wr_buf[1] = cmd & 0xFF; + + ret = spi_write_and_read_3wire(dev->spi_desc, wr_buf, rd_data, + ARRAY_SIZE(wr_buf), ARRAY_SIZE(rd_data)); + if (ret != SUCCESS) + return ret; + + *val = rd_data[0]; + + return SUCCESS; +} + +/** + * Calculate the output channel divider. + * @param rate - The desired rate. + * @param parent_rate - The parent rate. + * @return The output divider. + */ +uint32_t hmc7044_calc_out_div(uint32_t rate, + uint32_t parent_rate) +{ + uint32_t div; + + div = DIV_ROUND_CLOSEST(parent_rate, rate); + + /* Supported odd divide ratios are 1, 3, and 5 */ + if ((div != 1) && (div != 3) && (div != 5) && (div % 2)) + div = DIV_ROUND_CLOSEST(parent_rate, rate * 2) * 2; + + div = clamp_t(unsigned int, + div, + HMC7044_OUT_DIV_MIN, + HMC7044_OUT_DIV_MAX); + + return div; +} + +/** + * Recalculate rate corresponding to a channel. + * @param dev - The device structure. + * @param chan_num - Channel number. + * @param rate - Channel rate. + * @return SUCCESS in case of success, negative error code otherwise. + */ +int32_t hmc7044_clk_recalc_rate(struct hmc7044_dev *dev, uint32_t chan_num, + uint64_t *rate) +{ + int i; + struct hmc7044_chan_spec *chan = NULL; + + /* Find the reqested channel number */ + for (i = 0; i < dev->num_channels; i++) { + if (dev->channels[i].num == chan_num) { + chan = &dev->channels[i]; + break; + } + } + if (chan == NULL ) + return FAILURE; + + *rate = dev->pll2_freq / chan->divider; + + return SUCCESS; +} + +/** + * Calculate closest possible rate + * @param dev - The device structure + * @param rate - The desired rate. + * @param rounded_rate - The closest possible rate of desired rate. + * @return SUCCESS in case of success, negative error code otherwise. + */ +int32_t hmc7044_clk_round_rate(struct hmc7044_dev *dev, uint32_t rate, + uint64_t *rounded_rate) +{ + uint32_t div = hmc7044_calc_out_div(rate, dev->pll2_freq); + + *rounded_rate = DIV_ROUND_CLOSEST(dev->pll2_freq, div); + + return SUCCESS; +} + +/** + * Set channel rate. + * @param dev - The device structure. + * @param chan_num - Channel number. + * @param rate - Channel rate. + * @return SUCCESS in case of success, negative error code otherwise. + */ +int32_t hmc7044_clk_set_rate(struct hmc7044_dev *dev, uint32_t chan_num, + uint64_t rate) +{ + uint32_t div; + int32_t ret; + int i; + struct hmc7044_chan_spec *chan = NULL; + + /* Find the reqested channel number */ + for (i = 0; i < dev->num_channels; i++) { + if (dev->channels[i].num == chan_num) { + chan = &dev->channels[i]; + break; + } + } + if (chan == NULL ) + return FAILURE; + + div = hmc7044_calc_out_div(rate, dev->pll2_freq); + chan->divider = div; + + ret = hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_1(chan->num), + HMC7044_DIV_LSB(div)); + if(ret < 0) + return ret; + + return hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_2(chan->num), + HMC7044_DIV_MSB(div)); +} + +/** + * Setup the device. + * @param dev - The device structure. + * @return SUCCESS in case of success, negative error code otherwise. + */ +static int32_t hmc7044_setup(struct hmc7044_dev *dev) +{ + struct hmc7044_chan_spec *chan; + bool high_vco_en; + bool pll2_freq_doubler_en; + uint32_t vcxo_freq, pll2_freq; + uint32_t clkin_freq[4]; + uint32_t lcm_freq; + uint32_t in_prescaler[5]; + uint32_t pll1_lock_detect; + uint32_t n1, r1; + uint32_t pfd1_freq; + uint32_t vco_limit; + uint32_t n2[2], r2[2]; + uint32_t i, ref_en = 0; + + vcxo_freq = dev->vcxo_freq / 1000; + pll2_freq = dev->pll2_freq / 1000; + + lcm_freq = vcxo_freq; + for (i = 0; i < ARRAY_SIZE(clkin_freq); i++) { + if (dev->clkin_freq_ccf[i]) + clkin_freq[i] = dev->clkin_freq_ccf[i] / 1000; + else + clkin_freq[i] = dev->clkin_freq[i] / 1000; + + if (clkin_freq[i]) { + lcm_freq = greatest_common_divisor(clkin_freq[i], lcm_freq); + ref_en |= BIT(i); + } + } + + while (lcm_freq > HMC7044_RECOMM_LCM_MAX) + lcm_freq /= 2; + + for (i = 0; i < ARRAY_SIZE(clkin_freq); i++) { + if (clkin_freq[i]) + in_prescaler[i] = clkin_freq[i] / lcm_freq; + else + in_prescaler[i] = 1; + } + in_prescaler[4] = vcxo_freq / lcm_freq; + + pll1_lock_detect = log_base_2((lcm_freq * 4000) / dev->pll1_loop_bw); + + /* fVCXO / N1 = fLCM / R1 */ + rational_best_approximation(vcxo_freq, lcm_freq, + HMC7044_N1_MAX, HMC7044_R1_MAX, + &n1, &r1); + + pfd1_freq = vcxo_freq / n1; + while ((pfd1_freq > HMC7044_RECOMM_FPD1) && + (n1 <= HMC7044_N1_MAX / 2) && + (r1 <= HMC7044_R1_MAX / 2)) { + pfd1_freq /= 2; + n1 *= 2; + r1 *= 2; + } + + dev->pll1_pfd = pfd1_freq; + + if (pll2_freq < HMC7044_LOW_VCO_MIN || + pll2_freq > HMC7044_HIGH_VCO_MAX) + return FAILURE; + + vco_limit = (HMC7044_LOW_VCO_MAX + HMC7044_HIGH_VCO_MIN) / 2; + if (pll2_freq >= vco_limit) + high_vco_en = true; + else + high_vco_en = false; + + /* fVCO / N2 = fVCXO * doubler / R2 */ + pll2_freq_doubler_en = true; + rational_best_approximation(pll2_freq, vcxo_freq * 2, + HMC7044_N2_MAX, HMC7044_R2_MAX, + &n2[0], &r2[0]); + + if (pll2_freq != vcxo_freq * n2[0] / r2[0]) { + rational_best_approximation(pll2_freq, vcxo_freq, + HMC7044_N2_MAX, HMC7044_R2_MAX, + &n2[1], &r2[1]); + + if (abs((int)pll2_freq - (int)(vcxo_freq * 2 * n2[0] / r2[0])) > + abs((int)pll2_freq - (int)(vcxo_freq * n2[1] / r2[1]))) { + n2[0] = n2[1]; + r2[0] = r2[1]; + pll2_freq_doubler_en = false; + } + } + + while ((n2[0] < HMC7044_N2_MIN) && (r2[0] <= HMC7044_R2_MAX / 2)) { + n2[0] *= 2; + r2[0] *= 2; + } + if (n2[0] < HMC7044_N2_MIN) + return -FAILURE; + + /* Resets all registers to default values */ + hmc7044_write(dev, HMC7044_REG_SOFT_RESET, HMC7044_SOFT_RESET); + mdelay(10); + hmc7044_write(dev, HMC7044_REG_SOFT_RESET, 0); + mdelay(10); + + /* Disable all channels */ + for (i = 0; i < HMC7044_NUM_CHAN; i++) + hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_0(i), 0); + + /* Load the configuration updates (provided by Analog Devices) */ + hmc7044_write(dev, HMC7044_REG_CLK_OUT_DRV_LOW_PW, 0x4d); + hmc7044_write(dev, HMC7044_REG_CLK_OUT_DRV_HIGH_PW, 0xdf); + hmc7044_write(dev, HMC7044_REG_PLL1_DELAY, 0x06); + hmc7044_write(dev, HMC7044_REG_PLL1_HOLDOVER, 0x06); + hmc7044_write(dev, HMC7044_REG_VTUNE_PRESET, 0x04); + + uint8_t rd_back; + hmc7044_read(dev, HMC7044_REG_CLK_OUT_DRV_LOW_PW, &rd_back); + hmc7044_read(dev, HMC7044_REG_CLK_OUT_DRV_HIGH_PW, &rd_back); + hmc7044_read(dev, HMC7044_REG_PLL1_DELAY, &rd_back); + hmc7044_read(dev, HMC7044_REG_PLL1_HOLDOVER, &rd_back); + hmc7044_read(dev, HMC7044_REG_VTUNE_PRESET, &rd_back); + + hmc7044_write(dev, HMC7044_REG_GLOB_MODE, + HMC7044_SYNC_PIN_MODE(dev->sync_pin_mode) | + (dev->clkin0_rfsync_en ? HMC7044_RFSYNC_EN : 0) | + (dev->clkin1_vcoin_en ? HMC7044_VCOIN_MODE_EN : 0) | + HMC7044_REF_PATH_EN(ref_en)); + + /* Program PLL2 */ + + /* Select the VCO range */ + hmc7044_write(dev, HMC7044_REG_EN_CTRL_0, + (dev->rf_reseeder_en ? HMC7044_RF_RESEEDER_EN : 0) | + HMC7044_VCO_SEL(high_vco_en ? + HMC7044_VCO_HIGH : + HMC7044_VCO_LOW) | + HMC7044_SYSREF_TIMER_EN | HMC7044_PLL2_EN | + HMC7044_PLL1_EN); + + /* Program the dividers */ + hmc7044_write(dev, HMC7044_REG_PLL2_R_LSB, + HMC7044_R2_LSB(r2[0])); + hmc7044_write(dev, HMC7044_REG_PLL2_R_MSB, + HMC7044_R2_MSB(r2[0])); + hmc7044_write(dev, HMC7044_REG_PLL2_N_LSB, + HMC7044_N2_LSB(n2[0])); + hmc7044_write(dev, HMC7044_REG_PLL2_N_MSB, + HMC7044_N2_MSB(n2[0])); + + /* Program the reference doubler */ + hmc7044_write(dev, HMC7044_REG_PLL2_FREQ_DOUBLER, + pll2_freq_doubler_en ? 0 : HMC7044_PLL2_FREQ_DOUBLER_DIS); + + /* Program PLL1 */ + + /* Set the lock detect timer threshold */ + hmc7044_write(dev, HMC7044_REG_PLL1_LOCK_DETECT, + HMC7044_LOCK_DETECT_TIMER(pll1_lock_detect)); + + /* Set the LCM */ + for (i = 0; i < ARRAY_SIZE(clkin_freq); i++) { + hmc7044_write(dev, HMC7044_REG_CLKIN_PRESCALER(i), + in_prescaler[i]); + } + hmc7044_write(dev, HMC7044_REG_OSCIN_PRESCALER, + in_prescaler[4]); + + /* Program the dividers */ + hmc7044_write(dev, HMC7044_REG_PLL1_R_LSB, + HMC7044_R2_LSB(r1)); + hmc7044_write(dev, HMC7044_REG_PLL1_R_MSB, + HMC7044_R2_MSB(r1)); + hmc7044_write(dev, HMC7044_REG_PLL1_N_LSB, + HMC7044_N2_LSB(n1)); + hmc7044_write(dev, HMC7044_REG_PLL1_N_MSB, + HMC7044_N2_MSB(n1)); + + hmc7044_write(dev, HMC7044_REG_PLL1_REF_PRIO_CTRL, + dev->pll1_ref_prio_ctrl); + + /* Program the SYSREF timer */ + + /* Set the divide ratio */ + hmc7044_write(dev, HMC7044_REG_SYSREF_TIMER_LSB, + HMC7044_SYSREF_TIMER_LSB(dev->sysref_timer_div)); + hmc7044_write(dev, HMC7044_REG_SYSREF_TIMER_MSB, + HMC7044_SYSREF_TIMER_MSB(dev->sysref_timer_div)); + + /* Set the pulse generator mode configuration */ + hmc7044_write(dev, HMC7044_REG_PULSE_GEN, + HMC7044_PULSE_GEN_MODE(dev->pulse_gen_mode)); + + /* Enable the input buffers */ + hmc7044_write(dev, HMC7044_REG_CLKIN0_BUF_CTRL, + dev->in_buf_mode[0]); + hmc7044_write(dev, HMC7044_REG_CLKIN1_BUF_CTRL, + dev->in_buf_mode[1]); + hmc7044_write(dev, HMC7044_REG_CLKIN2_BUF_CTRL, + dev->in_buf_mode[2]); + hmc7044_write(dev, HMC7044_REG_CLKIN3_BUF_CTRL, + dev->in_buf_mode[3]); + hmc7044_write(dev, HMC7044_REG_OSCIN_BUF_CTRL, + dev->in_buf_mode[4]); + + /* Set GPIOs */ + for (i = 0; i < ARRAY_SIZE(dev->gpi_ctrl); i++) { + hmc7044_write(dev, HMC7044_REG_GPI_CTRL(i), + dev->gpi_ctrl[i]); + } + + for (i = 0; i < ARRAY_SIZE(dev->gpo_ctrl); i++) { + hmc7044_write(dev, HMC7044_REG_GPO_CTRL(i), + dev->gpo_ctrl[i]); + } + + mdelay(10); + + /* Program the output channels */ + for (i = 0; i < dev->num_channels; i++) { + chan = &dev->channels[i]; + if (chan->num >= HMC7044_NUM_CHAN || chan->disable) + continue; + + hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_1(chan->num), + HMC7044_DIV_LSB(chan->divider)); + hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_2(chan->num), + HMC7044_DIV_MSB(chan->divider)); + hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_8(chan->num), + HMC7044_DRIVER_MODE(chan->driver_mode) | + HMC7044_DRIVER_Z_MODE(chan->driver_impedance) | + (chan->dynamic_driver_enable ? + HMC7044_DYN_DRIVER_EN : 0) | + (chan->force_mute_enable ? + HMC7044_FORCE_MUTE_EN : 0)); + + hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_3(chan->num), + chan->fine_delay & 0x1F); + hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_4(chan->num), + chan->coarse_delay & 0x1F); + hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_7(chan->num), + chan->out_mux_mode & 0x3); + + hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_0(chan->num), + (chan->start_up_mode_dynamic_enable ? + HMC7044_START_UP_MODE_DYN_EN : 0) | + (chan->output_control0_rb4_enable ? BIT(4) : 0) | + (chan->high_performance_mode_dis ? + 0 : HMC7044_HI_PERF_MODE) | HMC7044_SYNC_EN | + HMC7044_CH_EN); + } + mdelay(10); + + /* Do a restart to reset the system and initiate calibration */ + hmc7044_write(dev, HMC7044_REG_REQ_MODE_0, + HMC7044_RESTART_DIV_FSM); + mdelay(1); + hmc7044_write(dev, HMC7044_REG_REQ_MODE_0, + (dev->high_performance_mode_clock_dist_en ? + HMC7044_HIGH_PERF_DISTRIB_PATH : 0)); + mdelay(1); + + return SUCCESS; +} + +/** + * Setup the device. + * @param dev - The device structure. + * @return SUCCESS in case of success, negative error code otherwise. + */ +static int32_t hmc7043_setup(struct hmc7044_dev *dev) +{ + struct hmc7044_chan_spec *chan; + uint32_t i; + + if (dev->clkin_freq_ccf[0]) + dev->pll2_freq = dev->clkin_freq_ccf[0]; + else + dev->pll2_freq = dev->clkin_freq[0]; + + if (!dev->pll2_freq) { + printf("%s: Failed to get valid parent rate\n", __func__); + return FAILURE; + } + + /* Resets all registers to default values */ + hmc7044_write(dev, HMC7044_REG_SOFT_RESET, HMC7044_SOFT_RESET); + mdelay(10); + hmc7044_write(dev, HMC7044_REG_SOFT_RESET, 0); + mdelay(10); + + /* Load the configuration updates (provided by Analog Devices) */ + hmc7044_write(dev, HMC7044_REG_CLK_OUT_DRV_LOW_PW, 0x4d); + hmc7044_write(dev, HMC7044_REG_CLK_OUT_DRV_HIGH_PW, 0xdf); + + /* Disable all channels */ + for (i = 0; i < HMC7044_NUM_CHAN; i++) + hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_0(i), 0); + + if (dev->pll2_freq < 1000000000U) + hmc7044_write(dev, HMC7044_CLK_INPUT_CTRL, + HMC7044_LOW_FREQ_INPUT_MODE); + + hmc7044_write(dev, HMC7044_REG_EN_CTRL_0, + (dev->rf_reseeder_en ? HMC7044_RF_RESEEDER_EN : 0) | + HMC7044_SYSREF_TIMER_EN); + + /* Program the SYSREF timer */ + + /* Set the divide ratio */ + hmc7044_write(dev, HMC7044_REG_SYSREF_TIMER_LSB, + HMC7044_SYSREF_TIMER_LSB(dev->sysref_timer_div)); + hmc7044_write(dev, HMC7044_REG_SYSREF_TIMER_MSB, + HMC7044_SYSREF_TIMER_MSB(dev->sysref_timer_div)); + + /* Set the pulse generator mode configuration */ + hmc7044_write(dev, HMC7044_REG_PULSE_GEN, + HMC7044_PULSE_GEN_MODE(dev->pulse_gen_mode)); + + /* Enable the input buffers */ + hmc7044_write(dev, HMC7044_REG_CLKIN0_BUF_CTRL, + dev->in_buf_mode[0]); + hmc7044_write(dev, HMC7044_REG_CLKIN1_BUF_CTRL, + dev->in_buf_mode[1]); + + /* Set GPIOs */ + hmc7044_write(dev, HMC7044_REG_GPI_CTRL(0), + dev->gpi_ctrl[0]); + + hmc7044_write(dev, HMC7044_REG_GPO_CTRL(0), + dev->gpo_ctrl[0]); + + /* Program the output channels */ + for (i = 0; i < dev->num_channels; i++) { + chan = &dev->channels[i]; + + if (chan->num >= HMC7044_NUM_CHAN || chan->disable) + continue; + + hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_1(chan->num), + HMC7044_DIV_LSB(chan->divider)); + hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_2(chan->num), + HMC7044_DIV_MSB(chan->divider)); + hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_8(chan->num), + HMC7044_DRIVER_MODE(chan->driver_mode) | + HMC7044_DRIVER_Z_MODE(chan->driver_impedance) | + (chan->dynamic_driver_enable ? + HMC7044_DYN_DRIVER_EN : 0) | + (chan->force_mute_enable ? + HMC7044_FORCE_MUTE_EN : 0)); + + hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_3(chan->num), + chan->fine_delay & 0x1F); + hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_4(chan->num), + chan->coarse_delay & 0x1F); + hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_7(chan->num), + chan->out_mux_mode & 0x3); + + hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_0(chan->num), + (chan->start_up_mode_dynamic_enable ? + HMC7044_START_UP_MODE_DYN_EN : 0) | + (chan->output_control0_rb4_enable ? BIT(4) : 0) | + (chan->high_performance_mode_dis ? + 0 : HMC7044_HI_PERF_MODE) | HMC7044_SYNC_EN | + HMC7044_CH_EN); + } + mdelay(10); + + + /* Do a restart to reset the system and initiate calibration */ + hmc7044_write(dev, HMC7044_REG_REQ_MODE_0, + HMC7044_RESTART_DIV_FSM); + mdelay(1); + hmc7044_write(dev, HMC7044_REG_REQ_MODE_0, + (dev->high_performance_mode_clock_dist_en ? + HMC7044_HIGH_PERF_DISTRIB_PATH : 0)); + mdelay(1); + + return SUCCESS; +} + +/***************************************************************************//** + * @brief hmc7044_reg_init + *******************************************************************************/ +int32_t hmc7044_reg_init(struct hmc7044_dev **device, + const struct hmc7044_init_param *init_param) +{ + int ret = SUCCESS; + struct hmc7044_dev *dev; + + dev = (struct hmc7044_dev *)malloc(sizeof(*dev)); + if (!dev) + return FAILURE; + + /* SPI */ + ret = spi_init(&dev->spi_desc, init_param->spi_init); + + *device = dev; + + return ret; +} + +/** + * Initialize the device. + * @param device - The device structure. + * @param init_param - The structure that contains the device initial + * parameters. + * @return SUCCESS in case of success, negative error code otherwise. + */ +int32_t hmc7044_init(struct hmc7044_dev **device, + const struct hmc7044_init_param *init_param) +{ + struct hmc7044_dev *dev; + int32_t ret; + unsigned int i; + + dev = (struct hmc7044_dev *)malloc(sizeof(*dev)); + if (!dev) + return FAILURE; + + ret = spi_init(&dev->spi_desc, init_param->spi_init); + if (ret < 0) + return ret; + + dev->is_hmc7043 = init_param->is_hmc7043; + + dev->clkin_freq[0] = init_param->clkin_freq[0]; + dev->clkin_freq[1] = init_param->clkin_freq[1]; + dev->clkin_freq[2] = init_param->clkin_freq[2]; + dev->clkin_freq[3] = init_param->clkin_freq[3]; + + dev->clkin_freq_ccf[0] = init_param->clkin_freq_ccf[0]; + dev->clkin_freq_ccf[1] = init_param->clkin_freq_ccf[1]; + dev->clkin_freq_ccf[2] = init_param->clkin_freq_ccf[2]; + dev->clkin_freq_ccf[3] = init_param->clkin_freq_ccf[3]; + + dev->vcxo_freq = init_param->vcxo_freq; + dev->pll1_pfd = init_param->pll1_pfd; + dev->pll2_freq = init_param->pll2_freq; + dev->pll1_loop_bw = init_param->pll1_loop_bw; + + dev->sysref_timer_div = init_param->sysref_timer_div; + dev->pll1_ref_prio_ctrl = init_param->pll1_ref_prio_ctrl; + dev->clkin0_rfsync_en = init_param->clkin0_rfsync_en; + dev->clkin1_vcoin_en = init_param->clkin1_vcoin_en; + dev->high_performance_mode_clock_dist_en = + init_param->high_performance_mode_clock_dist_en; + dev->rf_reseeder_en = !init_param->rf_reseeder_disable; + dev->sync_pin_mode = init_param->sync_pin_mode; + dev->pulse_gen_mode = init_param->pulse_gen_mode; + + dev->in_buf_mode[0] = init_param->in_buf_mode[0]; + dev->in_buf_mode[1] = init_param->in_buf_mode[1]; + dev->in_buf_mode[2] = init_param->in_buf_mode[2]; + dev->in_buf_mode[3] = init_param->in_buf_mode[3]; + dev->in_buf_mode[4] = init_param->in_buf_mode[4]; + + dev->gpi_ctrl[0] = init_param->gpi_ctrl[0]; + dev->gpi_ctrl[1] = init_param->gpi_ctrl[1]; + dev->gpi_ctrl[2] = init_param->gpi_ctrl[2]; + dev->gpi_ctrl[3] = init_param->gpi_ctrl[3]; + + dev->gpo_ctrl[0] = init_param->gpo_ctrl[0]; + dev->gpo_ctrl[1] = init_param->gpo_ctrl[1]; + dev->gpo_ctrl[2] = init_param->gpo_ctrl[2]; + dev->gpo_ctrl[3] = init_param->gpo_ctrl[3]; + + dev->num_channels = init_param->num_channels; + dev->channels = (struct hmc7044_chan_spec *) + malloc(sizeof(*dev->channels) * dev->num_channels); + + for (i = 0; i < dev->num_channels; i++) { + dev->channels[i].num = init_param->channels[i].num; + dev->channels[i].disable = init_param->channels[i].disable; + dev->channels[i].divider = init_param->channels[i].divider; + dev->channels[i].driver_mode = + init_param->channels[i].driver_mode; + dev->channels[i].high_performance_mode_dis = + init_param->channels[i].high_performance_mode_dis; + dev->channels[i].start_up_mode_dynamic_enable = + init_param->channels[i].start_up_mode_dynamic_enable; + dev->channels[i].dynamic_driver_enable = + init_param->channels[i].dynamic_driver_enable; + dev->channels[i].output_control0_rb4_enable = + init_param->channels[i].output_control0_rb4_enable; + dev->channels[i].force_mute_enable = + init_param->channels[i].force_mute_enable; + dev->channels[i].driver_impedance = + init_param->channels[i].driver_impedance; + dev->channels[i].coarse_delay = + init_param->channels[i].coarse_delay; + dev->channels[i].fine_delay = + init_param->channels[i].fine_delay; + dev->channels[i].out_mux_mode = + init_param->channels[i].out_mux_mode; + } + + *device = dev; + + if (!dev->is_hmc7043) + return hmc7044_setup(dev); + else + return hmc7043_setup(dev); +} + +/** + * Remove the device - release resources. + * @param device - The device structure. + * @return SUCCESS in case of success, negative error code otherwise. + */ +int32_t hmc7044_remove(struct hmc7044_dev *device) +{ + int32_t ret; + + ret = spi_remove(device->spi_desc); + free(device->channels); + free(device); + + return ret; +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/frequency/hmc7044/hmc7044.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/frequency/hmc7044/hmc7044.h new file mode 100644 index 0000000..b3916c7 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/frequency/hmc7044/hmc7044.h @@ -0,0 +1,276 @@ +/***************************************************************************//** + * @file hmc7044.h + * @brief Header file of HMC7044, HMC7043 Driver. + * @author DBogdan (dragos.bogdan@analog.com) +******************************************************************************** + * Copyright 2018-2020(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ +#ifndef HMC7044_H_ +#define HMC7044_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include +#include +#include "delay.h" +#include "spi.h" + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ +// Enum to select SYNC pin configuration with respect to PLL2 (0x0005) +typedef enum +{ + SYNC_DISABLED = 0, + SYNC_MULTICHIP = 1, + SYNC_PULSE_GEN = 2, + SYNC_ALARM = 3 +} hmc7044SyncPinModeSelect_t; + +// Enum to select input buffer mode (0x000A, 0x000B, 0x000C, 0x000D, 0x000E) +typedef enum +{ + BUFFER_ENABLE = (1) << 0, + BUFFER_DISABLE = (0) << 0, + ENABLE_INTERNAL_TERM = (1) << 1, + ENABLE_AC_COUPLE = (1) << 2, + ENABLE_LVPECL = (1) << 3, + ENABLE_HIGH_Z = (1) << 4 +} hmc7044InputBufferMode_t; + +// Enum to select output driver mode (0x003A, 0x003B, 0x00D0, 0x00DA, 0x00E4, +// 0x00EE, 0x00F8, 0x0102, 0x010C, 0x0116, 0x0120, 0x012A, 0x0134, 0x013E, +// 0x0148, 0x0152) +typedef enum +{ + CML = 0, + LVPECL = 1, + LVDS = 2, + CMOS = 3 +} hmc7044DriverMode_t; + +// Enum to select the GPIx functionality. (0x0046, 0x0047, 0x0048, 0x0049) +typedef enum +{ + GPI_ENABLE = (1) << 0, + GPI_DISABLE = (0) << 0, + FORCE_PLL1_HOLDOVER = (0x01) << 1 | GPI_ENABLE, + SELECT_PLL1_REF_BIT1 = (0x02) << 1 | GPI_ENABLE, + SELECT_PLL1_REF_BIT2 = (0x03) << 1 | GPI_ENABLE, + CHIP_SLEEP_MODE = (0x04) << 1 | GPI_ENABLE, + MUTE_REQUSET = (0x05) << 1 | GPI_ENABLE, + SELECT_INTERNAL_VCO_TYPE = (0x06) << 1 | GPI_ENABLE, + SELECT_HI_PERF_MODE = (0x07) << 1 | GPI_ENABLE, + PULSE_GEN_REQUSET = (0x08) << 1 | GPI_ENABLE, + RESEED_REQUEST = (0x09) << 1 | GPI_ENABLE, + RESTART_REQUSET = (0x0A) << 1 | GPI_ENABLE, + FORCE_FANOUT_MODE = (0x0B) << 1 | GPI_ENABLE, + SLIP_REQUEST = (0x0D) << 1 | GPI_ENABLE, +} hmc7044GPIControl_t; + +// Enum to select the GPOx functionality. (0x0050, 0x0051, 0x0052, 0x0053) +typedef enum +{ + GPO_ENABLE = (1) << 0, + GPO_DISABLE = (0) << 0, + GPO_CMOS_MODE = (1) << 1, + GPO_OPEN_DRAIN_MODE = (0) << 1, + ALARM_SIGNAL = (0x00) << 3 | GPO_ENABLE, + SPI_SDATA = (0x01) << 3 | GPO_ENABLE, + CLKIN3_SIGNAL_LOSS = (0x02) << 3 | GPO_ENABLE, + CLKIN2_SIGNAL_LOSS = (0x03) << 3 | GPO_ENABLE, + CLKIN1_SIGNAL_LOSS = (0x04) << 3 | GPO_ENABLE, + CLKIN0_SIGNAL_LOSS = (0x05) << 3 | GPO_ENABLE, + PLL1_HOLDOVER_ENABLE = (0x06) << 3 | GPO_ENABLE, + PLL1_LOCK_DETECT = (0x07) << 3 | GPO_ENABLE, + PLL1_ACQUIRING_LOCK = (0x08) << 3 | GPO_ENABLE, + PLL1_NEAR_CLOCK_ACQ_STAT = (0x09) << 3 | GPO_ENABLE, + PLL2_LOCK_DETECT = (0x0A) << 3 | GPO_ENABLE, + PLL2_SYSREF_SYNC = (0x0B) << 3 | GPO_ENABLE, + CLOCK_OUTPUT_PHASE_STAT = (0x0C) << 3 | GPO_ENABLE, + PLL1_PLL2_LOCKED = (0x0D) << 3 | GPO_ENABLE, + SYNC_REQUSRT_STAT = (0x0E) << 3 | GPO_ENABLE, + PLL1_ACTIVE_CLKIN0 = (0x0F) << 3 | GPO_ENABLE, + PLL1_ACTIVE_CLKIN1 = (0x10) << 3 | GPO_ENABLE, + PLL1_HOLDOVER_IN_RANGE_STAT = (0x11) << 3 | GPO_ENABLE, + PLL1_HOLDOVER_IN_STAT = (0x12) << 3 | GPO_ENABLE, + PLL1_VCXO_STAT = (0x13) << 3 | GPO_ENABLE, + PLL1_ACTIVE_CLKINx_STAT = (0x14) << 3 | GPO_ENABLE, + PLL1_FSM_BIT0 = (0x15) << 3 | GPO_ENABLE, + PLL1_FSM_BIT1 = (0x16) << 3 | GPO_ENABLE, + PLL1_FSM_BIT2 = (0x17) << 3 | GPO_ENABLE, + PLL1_HOLDOVER_EXIT_PHASE_BIT0 = (0x18) << 3 | GPO_ENABLE, + PLL1_HOLDOVER_EXIT_PHASE_BIT1 = (0x19) << 3 | GPO_ENABLE, + CHANNEL_OUTPUT_FSM_BUSY = (0x1A) << 3 | GPO_ENABLE, + SYSREF_FSM_STAT_BIT0 = (0x1B) << 3 | GPO_ENABLE, + SYSREF_FSM_STAT_BIT1 = (0x1C) << 3 | GPO_ENABLE, + SYSREF_FSM_STAT_BIT2 = (0x1D) << 3 | GPO_ENABLE, + SYSREF_FSM_STAT_BIT3 = (0x1E) << 3 | GPO_ENABLE, + FORCE_LOGIC_1 = (0x1F) << 3 | GPO_ENABLE, + FORCE_LOGIC_0 = (0x20) << 3 | GPO_ENABLE, + PLL1_HOLDOVER_AVG_VALUE_BIT0 = (0x27) << 3 | GPO_ENABLE, + PLL1_HOLDOVER_AVG_VALUE_BIT1 = (0x28) << 3 | GPO_ENABLE, + PLL1_HOLDOVER_AVG_VALUE_BIT2 = (0x29) << 3 | GPO_ENABLE, + PLL1_HOLDOVER_AVG_VALUE_BIT3 = (0x2A) << 3 | GPO_ENABLE, + PLL1_HOLDOVER_CURR_VALUE_BIT0 = (0x2B) << 3 | GPO_ENABLE, + PLL1_HOLDOVER_CURR_VALUE_BIT1 = (0x2C) << 3 | GPO_ENABLE, + PLL1_HOLDOVER_CURR_VALUE_BIT2 = (0x2D) << 3 | GPO_ENABLE, + PLL1_HOLDOVER_CURR_VALUE_BIT3 = (0x2E) << 3 | GPO_ENABLE +} hmc7044GPOControl_t; + +// Enum to select output channel (0x00CF, 0x00D9, 0x00E3, 0x00ED, 0x00F7, +// 0x0101, 0x010B, 0x0115, 0x011F, 0x0129, 0x0133, 0x013D, 0x0147, 0x0151 +typedef enum +{ + CHANNEL_DIV = 0, + ANALOG_DELAY = 1, + OTHER_CHANNEL = 2, + VCO_CLOCK = 3 +} hmc7044OutputMuxSelect_t; + +// Enum to select output driver impedance selection for CML mode (0x00D0, +// 0x00DA, 0x00E4, 0x00EE, 0x00F8, 0x0102, 0x010C, 0x0116, 0x0120, 0x012A, +// 0x0134, 0x013E, 0x0148, 0x0152) +typedef enum +{ + INTERNAL_RES_DISABLE = 0, + INTERNAL_100OHM_PER_PIN = 1, +// INTERNAL_RES_RESERVED = 2, + INTERNAL_50OHM_PER_PIN = 3 +} hmc7044DriverImpedance_t; + +// Enum to select pulse generator mode (0x005A) +typedef enum +{ + PULSE_LEVEL_SENSITIVE = 0, + PULSE_ONE = 1, + PULSE_TWO = 2, + PULSE_FOUR = 3, + PULSE_EIGHT = 4, + PULSE_SIXTEEN = 5, +// PULSE_SIXTEEN = 6, + PULSE_CONTINOUS = 7 +} hmc7044SyncPulseGenControl_t; + +struct hmc7044_chan_spec { + unsigned int num; + bool disable; + bool high_performance_mode_dis; + bool start_up_mode_dynamic_enable; + bool dynamic_driver_enable; + bool output_control0_rb4_enable; + bool force_mute_enable; + unsigned int divider; + unsigned int coarse_delay; + unsigned int fine_delay; + hmc7044DriverMode_t driver_mode; + hmc7044DriverImpedance_t driver_impedance; + hmc7044OutputMuxSelect_t out_mux_mode; +}; + +struct hmc7044_config { + uint16_t addr; + uint8_t value; +}; + +struct hmc7044_dev { + struct spi_desc *spi_desc; + bool is_hmc7043; + uint32_t clkin_freq[4]; + uint32_t clkin_freq_ccf[4]; + uint32_t vcxo_freq; + uint32_t pll1_pfd; + uint32_t pll2_freq; + uint32_t pll1_loop_bw; + uint32_t sysref_timer_div; + unsigned int pll1_ref_prio_ctrl; + bool clkin0_rfsync_en; + bool clkin1_vcoin_en; + bool high_performance_mode_clock_dist_en; + bool rf_reseeder_en; + hmc7044SyncPinModeSelect_t sync_pin_mode; + hmc7044SyncPulseGenControl_t pulse_gen_mode; + uint32_t in_buf_mode[5]; + uint32_t gpi_ctrl[4]; + uint32_t gpo_ctrl[4]; + uint32_t num_channels; + struct hmc7044_chan_spec *channels; +}; + +struct hmc7044_init_param { + struct spi_init_param *spi_init; + bool is_hmc7043; + uint32_t clkin_freq[4]; + uint32_t clkin_freq_ccf[4]; + uint32_t vcxo_freq; + uint32_t pll1_pfd; + uint32_t pll2_freq; + uint32_t pll1_loop_bw; + uint32_t sysref_timer_div; + unsigned int pll1_ref_prio_ctrl; + bool clkin0_rfsync_en; + bool clkin1_vcoin_en; + bool high_performance_mode_clock_dist_en; + bool rf_reseeder_disable; + hmc7044SyncPinModeSelect_t sync_pin_mode; + hmc7044SyncPulseGenControl_t pulse_gen_mode; + uint32_t in_buf_mode[5]; + uint32_t gpi_ctrl[4]; + uint32_t gpo_ctrl[4]; + uint32_t num_channels; + struct hmc7044_chan_spec *channels; +}; + +/******************************************************************************/ +/************************ Functions Declarations ******************************/ +/******************************************************************************/ +/* Initialize the device. */ +int32_t hmc7044_reg_init(struct hmc7044_dev **device, + const struct hmc7044_init_param *init_param); +int32_t hmc7044_init(struct hmc7044_dev **device, + const struct hmc7044_init_param *init_param); +/* Remove the device. */ +int32_t hmc7044_remove(struct hmc7044_dev *device); +int32_t hmc7044_write(struct hmc7044_dev *dev, + uint16_t reg, uint8_t val); +int32_t hmc7044_read(struct hmc7044_dev *dev, uint16_t reg, uint8_t *val); +int32_t hmc7044_clk_recalc_rate(struct hmc7044_dev *dev, uint32_t chan_num, + uint64_t *rate); +int32_t hmc7044_clk_round_rate(struct hmc7044_dev *dev, uint32_t rate, + uint64_t *rounded_rate); +int32_t hmc7044_clk_set_rate(struct hmc7044_dev *dev, uint32_t chan_num, + uint64_t rate); + +#endif // HMC7044_H_ diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/frequency/lmk04828/lmk04828.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/frequency/lmk04828/lmk04828.c new file mode 100644 index 0000000..c76fb10 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/frequency/lmk04828/lmk04828.c @@ -0,0 +1,128 @@ +/***************************************************************************//** + * @file lmk04828.c + * @brief Implementation of AD9680 Driver. + * @author DBogdan (dragos.bogdan@analog.com) + ******************************************************************************** + * Copyright 2014-2016(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. + *******************************************************************************/ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include +#include +#include "lmk04828.h" +#include "../../../include/error.h" + +/***************************************************************************//** + * @brief lmk04828_read + *******************************************************************************/ +int32_t lmk04828_read(struct lmk04828_dev *dev, + uint16_t reg_addr, + uint8_t *reg_data) +{ + uint8_t tx_buf[2]; + uint8_t rx_buf[1]; + int32_t ret; + + tx_buf[0] = 0x80 | (reg_addr >> 8); + tx_buf[1] = reg_addr & 0xFF; + + + ret = spi_write_and_read_3wire(dev->spi_desc, + tx_buf, rx_buf, 2, 1); + + *reg_data = rx_buf[0]; + + return ret; +} + +/***************************************************************************//** + * @brief lmk04828_write + *******************************************************************************/ +int32_t lmk04828_write(struct lmk04828_dev *dev, + uint16_t reg_addr, + uint8_t reg_data) +{ + uint8_t buf[3]; + + int32_t ret; + + buf[0] = reg_addr >> 8; + buf[1] = reg_addr & 0xFF; + buf[2] = reg_data; + + ret = spi_write_and_read_3wire(dev->spi_desc, + buf, NULL, 3, 0); + return ret; +} + +/***************************************************************************//** + * @brief lmk04828_init + *******************************************************************************/ +int32_t lmk04828_init(struct lmk04828_dev **device, + const struct lmk04828_init_param *init_param) +{ + int ret = SUCCESS; + struct lmk04828_dev *dev; + + dev = (struct lmk04828_dev *)malloc(sizeof(*dev)); + if (!dev) + return FAILURE; + + /* SPI */ + ret = spi_init(&dev->spi_desc, init_param->spi_init); + + *device = dev; + + return ret; +} + +/***************************************************************************//** + * @brief Free the resources allocated by lmk04828_setup(). + * + * @param dev - The device structure. + * + * @return SUCCESS in case of success, negative error code otherwise. +*******************************************************************************/ +int32_t lmk04828_remove(struct lmk04828_dev *dev) +{ + int32_t ret; + + ret = spi_remove(dev->spi_desc); + + free(dev); + + return ret; +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/frequency/lmk04828/lmk04828.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/frequency/lmk04828/lmk04828.h new file mode 100644 index 0000000..7c16aca --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/frequency/lmk04828/lmk04828.h @@ -0,0 +1,90 @@ +/***************************************************************************//** + * @file lmk04828.h + * @brief Header file of lmk04828 Driver. + * @author DBogdan (dragos.bogdan@analog.com) + ******************************************************************************** + * Copyright 2014-2016(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. + *******************************************************************************/ +#ifndef LMK04828_H_ +#define LMK04828_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include +#include "delay.h" +#include "spi.h" +#include "gpio.h" + +/******************************************************************************/ +/********************** Macros and Constants Definitions **********************/ +/******************************************************************************/ + + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ +struct lmk04828_dev { + /* SPI */ + struct spi_desc *spi_desc; +}; + +struct lmk04828_init_param { + /* SPI */ + struct spi_init_param *spi_init; +}; + +struct lmk04828_config { + uint16_t addr; + uint8_t value; +}; + +/******************************************************************************/ +/************************ Functions Declarations ******************************/ +/******************************************************************************/ + +int32_t lmk04828_read(struct lmk04828_dev *dev, + uint16_t reg_addr, + uint8_t *reg_data); + +int32_t lmk04828_write(struct lmk04828_dev *dev, + uint16_t reg_addr, + uint8_t reg_data); + +int32_t lmk04828_init(struct lmk04828_dev **device, + const struct lmk04828_init_param *init_param); + +int32_t lmk04828_remove(struct lmk04828_dev *dev); + +#endif diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/frequency/lmx2594/lmx2594.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/frequency/lmx2594/lmx2594.c new file mode 100644 index 0000000..b305b86 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/frequency/lmx2594/lmx2594.c @@ -0,0 +1,129 @@ +/***************************************************************************//** + * @file lmx2594.c + * @brief Implementation of AD9680 Driver. + * @author DBogdan (dragos.bogdan@analog.com) + ******************************************************************************** + * Copyright 2014-2016(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. + *******************************************************************************/ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include "../lmx2594/lmx2594.h" + +#include +#include +#include "../../../include/error.h" + +/***************************************************************************//** + * @brief lmx2594_read + *******************************************************************************/ +int32_t lmx2594_read(struct lmx2594_dev *dev, + uint8_t reg_addr, + uint16_t *reg_data) +{ + uint8_t tx_buf[1]; + uint8_t rx_buf[2]; + int32_t ret; + + tx_buf[0] = 0x80 | reg_addr; + + + ret = spi_write_and_read_3wire(dev->spi_desc, + tx_buf, rx_buf, 1, 2); + + *reg_data = rx_buf[1]; + *reg_data |= ((uint16_t)rx_buf[0]) << 8; + + return ret; +} + +/***************************************************************************//** + * @brief lmx2594_write + *******************************************************************************/ +int32_t lmx2594_write(struct lmx2594_dev *dev, + uint8_t reg_addr, + uint16_t reg_data) +{ + uint8_t buf[3]; + + int32_t ret; + + buf[0] = reg_addr; + buf[1] = reg_data >> 8; + buf[2] = reg_data & 0xFF; + + ret = spi_write_and_read_3wire(dev->spi_desc, + buf, NULL, 3, 0); + return ret; +} + +/***************************************************************************//** + * @brief lmx2594_init + *******************************************************************************/ +int32_t lmx2594_init(struct lmx2594_dev **device, + const struct lmx2594_init_param *init_param) +{ + int ret = SUCCESS; + struct lmx2594_dev *dev; + + dev = (struct lmx2594_dev *)malloc(sizeof(*dev)); + if (!dev) + return FAILURE; + + /* SPI */ + ret = spi_init(&dev->spi_desc, init_param->spi_init); + + *device = dev; + + return ret; +} + +/***************************************************************************//** + * @brief Free the resources allocated by lmx2594_setup(). + * + * @param dev - The device structure. + * + * @return SUCCESS in case of success, negative error code otherwise. +*******************************************************************************/ +int32_t lmx2594_remove(struct lmx2594_dev *dev) +{ + int32_t ret; + + ret = spi_remove(dev->spi_desc); + + free(dev); + + return ret; +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/frequency/lmx2594/lmx2594.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/frequency/lmx2594/lmx2594.h new file mode 100644 index 0000000..eeb5caf --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/frequency/lmx2594/lmx2594.h @@ -0,0 +1,90 @@ +/***************************************************************************//** + * @file lmx2594.h + * @brief Header file of lmx2594 Driver. + * @author DBogdan (dragos.bogdan@analog.com) + ******************************************************************************** + * Copyright 2014-2016(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. + *******************************************************************************/ +#ifndef LMX2594_H_ +#define LMX2594_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include +#include "delay.h" +#include "spi.h" +#include "gpio.h" + +/******************************************************************************/ +/********************** Macros and Constants Definitions **********************/ +/******************************************************************************/ + + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ +struct lmx2594_dev { + /* SPI */ + struct spi_desc *spi_desc; +}; + +struct lmx2594_init_param { + /* SPI */ + struct spi_init_param *spi_init; +}; + +struct lmx2594_config { + uint8_t addr; + uint16_t value; +}; + +/******************************************************************************/ +/************************ Functions Declarations ******************************/ +/******************************************************************************/ + +int32_t lmx2594_read(struct lmx2594_dev *dev, + uint8_t reg_addr, + uint16_t *reg_data); + +int32_t lmx2594_write(struct lmx2594_dev *dev, + uint8_t reg_addr, + uint16_t reg_data); + +int32_t lmx2594_init(struct lmx2594_dev **device, + const struct lmx2594_init_param *init_param); + +int32_t lmx2594_remove(struct lmx2594_dev *dev); + +#endif diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_axi_io.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_axi_io.c new file mode 100644 index 0000000..b900a68 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_axi_io.c @@ -0,0 +1,79 @@ +/***************************************************************************//** + * @file xilinx/xilinx_axi_io.c + * @brief Implementation of Xilinx AXI IO. + * @author Antoniu Miclaus (antoniu.miclaus@analog.com) +******************************************************************************** + * Copyright 2019(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ + +#include +#include "error.h" +#include "axi_io.h" + +/******************************************************************************/ +/************************ Functions Definitions *******************************/ +/******************************************************************************/ + +/** + * @brief AXI IO Xilinx specific read function. + * @param base - Base address + * @param offset - Address offset + * @param data - variable where returned data is stored + * @return 0 in case of success, -1 otherwise. + */ +int32_t axi_io_read(uint32_t base, uint32_t offset, uint32_t *data) +{ + *data = Xil_In32(base + offset); + + return 0; +} + +/** + * @brief AXI IO Xilinx specific write function. + * @param base - Base address + * @param offset - Address offset + * @param data - data to be written. + * @return 0 in case of success, -1 otherwise. + */ +int32_t axi_io_write(uint32_t base, uint32_t offset, uint32_t data) +{ + Xil_Out32(base + offset, data); + + return 0; +} + diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_delay.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_delay.c new file mode 100644 index 0000000..d7e8640 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_delay.c @@ -0,0 +1,100 @@ +/***************************************************************************//** + * @file xilinx/xilinx_delay.c + * @brief Implementation of Xilinx Delay Functions. + * @author Antoniu Miclaus (antoniu.miclaus@analog.com) +******************************************************************************** + * Copyright 2019(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ + +#include "delay.h" +#include +#ifdef _XPARAMETERS_PS_H_ +#include "util.h" +#include "xtime_l.h" +#endif + +/******************************************************************************/ +/************************ Functions Definitions *******************************/ +/******************************************************************************/ + +/** + * @brief Generate microseconds delay. + * @param usecs - Delay in microseconds. + * @return None. + */ +void udelay(uint32_t usecs) +{ +#ifdef _XPARAMETERS_PS_H_ + usleep(usecs); +#else + usleep_MB(usecs); +#endif +} + +/** + * @brief Generate miliseconds delay. + * @param msecs - Delay in miliseconds. + * @return None. + */ +void mdelay(uint32_t msecs) +{ +#ifdef _XPARAMETERS_PS_H_ + usleep(msecs * 1000); +#else + usleep_MB(msecs * 1000); +#endif +} + +/** + * @brief Get current time. + * @return Current time structure from system start (seconds, microseconds). + */ +struct time get_time(void) +{ + struct time t = {0, 0}; +#ifdef _XPARAMETERS_PS_H_ + unsigned long long Xtime_Global; + uint32_t rem; + + XTime_GetTime(&Xtime_Global); + t.s = div_u64_rem(Xtime_Global, COUNTS_PER_SECOND, &rem); + t.us = div_u64((uint64_t)rem * 1000000, COUNTS_PER_SECOND); +#endif + + return t; +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_gpio.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_gpio.c new file mode 100644 index 0000000..abcab51 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_gpio.c @@ -0,0 +1,464 @@ +/***************************************************************************//** + * @file xilinx/xilinx_gpio.c + * @brief Implementation of Xilinx GPIO Generic Driver. + * @author Antoniu Miclaus (antoniu.miclaus@analog.com) +******************************************************************************** + * Copyright 2019(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ + +#include + +#include +#ifdef XPAR_XGPIO_NUM_INSTANCES +#include +#endif +#ifdef XPAR_XGPIOPS_NUM_INSTANCES +#include +#endif + +#include "error.h" +#include "gpio.h" +#include "xilinx_gpio.h" + +/******************************************************************************/ +/************************ Functions Definitions *******************************/ +/******************************************************************************/ + +/** + * @brief Prepare the GPIO decriptor. + * @param desc - The GPIO descriptor. + * @param param - The structure that contains the GPIO parameters. + * @return 0 in case of success, -1 otherwise. + */ +int32_t _gpio_init(struct gpio_desc *desc, + const struct gpio_init_param *param) +{ + int32_t ret; + struct xil_gpio_desc *xdesc; + struct xil_gpio_init_param *xinit; + + ret = -1; + + xdesc = desc->extra; + xinit = param->extra; + + xdesc->type = xinit->type; + desc->number = param->number; + + switch (xinit->type) { + case GPIO_PL: +#ifdef XGPIO_H + xdesc->instance = (XGpio *)malloc(sizeof(XGpio)); + if(!xdesc->instance) + goto pl_error; + + xdesc->config = XGpio_LookupConfig(xinit->device_id); + if(xdesc->config == NULL) + goto pl_error; + + ret = XGpio_CfgInitialize(xdesc->instance, + xdesc->config, + ((XGpio_Config*)xdesc->config) + ->BaseAddress); + if(ret < 0) + goto pl_error; + + break; +pl_error: + free(xdesc->instance); +#endif + goto error; + case GPIO_PS: +#ifdef XGPIOPS_H + xdesc->instance = (XGpioPs *)malloc(sizeof(XGpioPs)); + if(!xdesc->instance) + goto ps_error; + + xdesc->config = XGpioPs_LookupConfig(xinit->device_id); + if(xdesc->config == NULL) + goto ps_error; + + ret = XGpioPs_CfgInitialize(xdesc->instance, + xdesc->config, + ((XGpioPs_Config*)xdesc->config)->BaseAddr); + if(ret < 0) + goto ps_error; + break; +ps_error: + free(xdesc->instance); +#endif + goto error; +error: + default: + return -1; + } + + return 0; +} + +/** + * @brief Obtain the GPIO decriptor. + * @param desc - The GPIO descriptor. + * @param param - GPIO initialization parameters + * @return 0 in case of success, -1 otherwise. + */ +int32_t xil_gpio_get(struct gpio_desc **desc, + const struct gpio_init_param *param) +{ + struct gpio_desc *descriptor; + struct xil_gpio_desc *extra; + int32_t ret; + + descriptor = (struct gpio_desc *)malloc(sizeof(*descriptor)); + extra = (struct xil_gpio_desc*)malloc(sizeof(*extra)); + + if (!descriptor || !extra) + return -1; + + descriptor->extra = extra; + ret = _gpio_init(descriptor, param); + + if(ret != 0) + goto error; + + *desc = descriptor; + + return 0; +error: + free(extra); + free(descriptor); + + return -1; +} + +/** + * @brief Get the value of an optional GPIO. + * @param desc - The GPIO descriptor. + * @param param - GPIO Initialization parameters. + * @return 0 in case of success, -1 otherwise. + */ +int32_t xil_gpio_get_optional(struct gpio_desc **desc, + const struct gpio_init_param *param) +{ + if(param == NULL) { + *desc = NULL; + return 0; + } + + return xil_gpio_get(desc, param); +} + +/** + * @brief Free the resources allocated by gpio_get(). + * @param desc - The GPIO descriptor. + * @return 0 in case of success, -1 otherwise. + */ +int32_t xil_gpio_remove(struct gpio_desc *desc) +{ + if (desc != NULL) { + free(((struct xil_gpio_desc *)(desc->extra))->instance); + free(desc->extra); + free(desc); + } + + return 0; +} + +/** + * @brief Enable the input direction of the specified GPIO. + * @param desc - The GPIO descriptor. + * @return 0 in case of success, -1 otherwise. + */ +int32_t xil_gpio_direction_input(struct gpio_desc *desc) +{ + struct xil_gpio_desc *extra; +#ifdef XGPIO_H + uint8_t pin = desc->number; + uint32_t channel = 1; + uint32_t config = 0; +#endif + extra = desc->extra; + + switch (extra->type) { + case GPIO_PL: +#ifdef XGPIO_H + /* We assume that pin 32 is the first pin from channel 2 */ + if (pin >= 32) { + channel = 2; + pin -= 32; + } + + config = XGpio_GetDataDirection((XGpio *)extra->instance, + channel); + config |= (1 << pin); + XGpio_SetDataDirection((XGpio *)extra->instance, + channel, + config); +#endif + break; + case GPIO_PS: +#ifdef XGPIOPS_H + XGpioPs_SetDirectionPin(extra->instance, + desc->number, + GPIO_IN); +#endif + break; + default: + return -1; + } + + return 0; +} + +/** + * @brief Enable the output direction of the specified GPIO. + * @param desc - The GPIO descriptor. + * @param value - The value. + * Example: GPIO_HIGH + * GPIO_LOW + * @return 0 in case of success, -1 otherwise. + */ +int32_t xil_gpio_direction_output(struct gpio_desc *desc, + uint8_t value) +{ + struct xil_gpio_desc *extra; +#ifdef XGPIO_H + uint8_t pin = desc->number; + uint8_t channel; + uint32_t reg_val; +#endif + extra = desc->extra; + + switch (extra->type) { + case GPIO_PL: +#ifdef XGPIO_H + if (pin >= 32) { + channel = 2; + pin -= 32; + } else + channel = 1; + + reg_val = XGpio_GetDataDirection(extra->instance, + channel); + reg_val &= ~(1 << pin); + XGpio_SetDataDirection(extra->instance, + channel, + reg_val); + reg_val = XGpio_DiscreteRead(extra->instance, + channel); + if(value) + reg_val |= (1 << pin); + else + reg_val &= ~(1 << pin); + XGpio_DiscreteWrite(extra->instance, + channel, + reg_val); +#endif + break; + case GPIO_PS: +#ifdef XGPIOPS_H + XGpioPs_SetDirectionPin(extra->instance, + desc->number, + GPIO_OUT); + XGpioPs_SetOutputEnablePin(extra->instance, + desc->number, + GPIO_OUT); + XGpioPs_WritePin(extra->instance, + desc->number, + value); +#endif + break; + default: + return -1; + } + + return 0; +} + +/** + * @brief Get the direction of the specified GPIO. + * @param desc - The GPIO descriptor. + * @param direction - The direction. + * Example: GPIO_OUT + * GPIO_IN + * @return 0 in case of success, -1 otherwise. + */ +int32_t xil_gpio_get_direction(struct gpio_desc *desc, + uint8_t *direction) +{ + if (!desc) + return -1; + + struct xil_gpio_desc *extra; + uint8_t pin = desc->number; + +#ifdef XGPIO_H + uint8_t channel; +#endif + extra = desc->extra; + + switch (extra->type) { + case GPIO_PL: +#ifdef XGPIO_H + if (pin >= 32) { + channel = 2; + pin -= 32; + } else + channel = 1; + *direction = ((XGpio_GetDataDirection(extra->instance, + channel) >> pin) & 0x1) ^ 0x1; +#endif + break; + case GPIO_PS: +#ifdef XGPIOPS_H + *direction = XGpioPs_GetDirectionPin(extra->instance, pin); +#endif + break; + default: + return -1; + } + + return 0; +} + +/** + * @brief Set the value of the specified GPIO. + * @param desc - The GPIO descriptor. + * @param value - The value. + * Example: GPIO_HIGH + * GPIO_LOW + * @return 0 in case of success, -1 otherwise. + */ +int32_t xil_gpio_set_value(struct gpio_desc *desc, + uint8_t value) +{ + struct xil_gpio_desc *extra; +#ifdef XGPIO_H + uint8_t pin = desc->number; + uint8_t channel; + uint32_t reg_val; +#endif + extra = desc->extra; + + switch (extra->type) { + case GPIO_PL: +#ifdef XGPIO_H + if (pin >= 32) { + channel = 2; + pin -= 32; + } else + channel = 1; + + reg_val = XGpio_DiscreteRead(extra->instance, channel); + if(value) + reg_val |= (1 << pin); + else + reg_val &= ~(1 << pin); + XGpio_DiscreteWrite(extra->instance, channel, reg_val); +#endif + break; + case GPIO_PS: +#ifdef XGPIOPS_H + XGpioPs_WritePin(extra->instance, desc->number, value); +#endif + break; + default: + return -1; + } + + return 0; +} + +/** + * @brief Get the value of the specified GPIO. + * @param desc - The GPIO descriptor. + * @param value - The value. + * Example: GPIO_HIGH + * GPIO_LOW + * @return 0 in case of success, -1 otherwise. + */ +int32_t xil_gpio_get_value(struct gpio_desc *desc, + uint8_t *value) +{ + if (!desc) + return -1; + + struct xil_gpio_desc *extra; + uint8_t pin = desc->number; + +#ifdef XGPIO_H + uint8_t channel; +#endif + extra = desc->extra; + + switch (extra->type) { + case GPIO_PL: +#ifdef XGPIO_H + if (pin >= 32) { + channel = 2; + pin -= 32; + } else + channel = 1; + *value = (XGpio_DiscreteRead(extra->instance, channel) >> pin) & 0x01; +#endif + break; + case GPIO_PS: +#ifdef XGPIOPS_H + *value = XGpioPs_ReadPin(extra->instance, pin); +#endif + break; + default: + return -1; + } + + return 0; +} + +/** + * @brief Xilinx platform specific GPIO platform ops structure + */ +const struct gpio_platform_ops xil_gpio_ops = { + .gpio_ops_get = &xil_gpio_get, + .gpio_ops_get_optional = &xil_gpio_get_optional, + .gpio_ops_remove = &xil_gpio_remove, + .gpio_ops_direction_input = &xil_gpio_direction_input, + .gpio_ops_direction_output = &xil_gpio_direction_output, + .gpio_ops_get_direction = &xil_gpio_get_direction, + .gpio_ops_set_value = &xil_gpio_set_value, + .gpio_ops_get_value = &xil_gpio_get_value, +}; diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_gpio.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_gpio.h new file mode 100644 index 0000000..2cf61e6 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_gpio.h @@ -0,0 +1,95 @@ +/******************************************************************************* + * @file xilinx/xilinx_gpio.h + * @brief Header containing extra types used in the gpio driver + * @author scuciurean (sergiu.cuciurean@analog.com) +******************************************************************************** + * Copyright 2019(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +#ifndef XILINX_GPIO_H_ +#define XILINX_GPIO_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ + +#include +#include + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ + +/** + * @enum xil_gpio_type + * @brief Xilinx platform architecture sections + */ +enum xil_gpio_type { + /** Programmable Logic */ + GPIO_PL, + /** Processing System */ + GPIO_PS +}; + +/** + * @struct xil_gpio_init_param + * @brief Structure holding the initialization parameters for Xilinx platform + * specific GPIO parameters. + */ +struct xil_gpio_init_param { + /** Xilinx architecture */ + enum xil_gpio_type type; + /** Device ID */ + uint32_t device_id; +}; + +/** + * @struct xil_gpio_desc + * @brief Xilinx platform specific GPIO descriptor + */ +struct xil_gpio_desc { + /** Xilinx architecture */ + enum xil_gpio_type type; + /** Xilinx GPIO configuration */ + void *config; + /** Xilinx GPIO Instance */ + void *instance; +}; + +/** + * @brief Xilinx platform specific gpio platform ops structure + */ +extern const struct gpio_platform_ops xil_gpio_ops; + +#endif diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_gpio_irq.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_gpio_irq.c new file mode 100644 index 0000000..f210f84 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_gpio_irq.c @@ -0,0 +1,360 @@ +/***************************************************************************//** + * @file xilinx/xilinx_gpio_irq.c + * @brief Implementation of Xilinx GPIO IRQ Generic Driver. + * @author Porumb Andrei (andrei.porumb@analog.com) +******************************************************************************** + * Copyright 2021(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ + +#include +#include "xparameters.h" +#include "error.h" +#include "xilinx_gpio_irq.h" +#include "util.h" +#include "list.h" +#include "irq.h" + +/******************************************************************************/ +/************************ Functions Definitions *******************************/ +/******************************************************************************/ + +/** + * @brief Comparing function used by list. + * @param desc_a - Compared item + * @param desc_b - Reference item + * @return - 0 if items match + */ +static int32_t call_cmp(struct xil_callback_desc *desc_a, + struct xil_callback_desc *desc_b) +{ + return desc_a->pin_nb - desc_b->pin_nb; +} + +/** + * @brief Function called when GPIO IRQ occurs. + * @param param - GPIO IRQ desc's extra field + * @return - None. + */ +static void xil_gpio_irq_handler(struct xil_gpio_irq_desc *param) +{ + int32_t status = 0; + int32_t pin_nb; + struct xil_callback_desc *callback_desc; + + status = iterator_move_to_idx(param->it, 0); + while(!status) { + iterator_read(param->it, &callback_desc); + status = iterator_move(param->it, 1); + if(XGpioPs_IntrGetStatusPin(¶m->my_Gpio, callback_desc->pin_nb)) + callback_desc->triggered = true; + } + + status = iterator_move_to_idx(param->it, 0); + while(!status) { + iterator_read(param->it, &callback_desc); + status = iterator_move(param->it, 1); + if(callback_desc->triggered == true) { + callback_desc->triggered = false; + XGpioPs_IntrDisablePin(¶m->my_Gpio, callback_desc->pin_nb); + XGpioPs_IntrClearPin(¶m->my_Gpio, callback_desc->pin_nb); + callback_desc->callback.legacy_callback(callback_desc->callback.ctx, 0U, NULL); + if(callback_desc->enabled) + XGpioPs_IntrEnablePin(¶m->my_Gpio, callback_desc->pin_nb); + } + } +} + +/** + * @brief Disable specific GPIO IRQ pin. + * @param desc - The GPIO IRQ controller descriptor. + * @param irq_id - Pin number. + * @return 0 in case of success, -1 otherwise. + */ +int32_t xil_gpio_irq_disable(struct irq_ctrl_desc *desc, uint32_t irq_id) +{ + int32_t status; + struct xil_gpio_irq_desc *extra; + struct xil_callback_desc *callback_desc; + + extra = desc->extra; + + status = iterator_move_to_idx(extra->it, 0); + while(!status) { + iterator_read(extra->it, &callback_desc); + status = iterator_move(extra->it, 1); + if(callback_desc->pin_nb == irq_id) { + callback_desc->enabled = false; + break; + } + } + + XGpioPs_IntrDisablePin(&extra->my_Gpio, irq_id); + + return 0; +} + +/** + * @brief Initialize the GPIO IRQ controller. + * @param desc - The GPIO IRQ controller descriptor. + * @param param - The GPIO IRQ controller initial parameters. + * irq_ctrl_id from irq_init_param is the interrupt id for the parent controller + * @return 0 in case of success, -1 otherwise. + */ +int32_t xil_gpio_irq_ctrl_init(struct irq_ctrl_desc **desc, + const struct irq_init_param *param) +{ + int32_t i; + int32_t status; + struct irq_ctrl_desc *ldesc; + static XGpioPs_Config *GPIO_Config; + struct xil_gpio_irq_desc *xil_desc; + struct xil_gpio_irq_init_param *xil_ip; + struct callback_desc callback; + + xil_desc = (struct xil_gpio_irq_desc *)calloc(1, sizeof(*xil_desc)); + if(!xil_desc) + return -ENOMEM; + + ldesc = (struct irq_ctrl_desc *)calloc(1, sizeof(*ldesc)); + if(!ldesc) { + free(xil_desc); + return -ENOMEM; + } + + xil_ip = param->extra; + GPIO_Config = XGpioPs_LookupConfig(xil_ip->gpio_device_id); + status = XGpioPs_CfgInitialize(&xil_desc->my_Gpio, GPIO_Config, + GPIO_Config->BaseAddr); + if(status) + goto error_desc; + + ldesc->extra = xil_desc; + xil_desc->parent_desc = xil_ip->parent_desc; + + status = list_init(&xil_desc->callback_list, LIST_DEFAULT, + call_cmp); + if(status) + goto error_list; + + status = iterator_init(&xil_desc->it, xil_desc->callback_list, 0); + if(status) { + iterator_remove(xil_desc->it); + goto error_list; + } + + ldesc->irq_ctrl_id = param->irq_ctrl_id; + status = irq_trigger_level_set(xil_desc->parent_desc, ldesc->irq_ctrl_id, + IRQ_EDGE_RISING); + if(status) + goto error_op; + + status = irq_enable(xil_desc->parent_desc, ldesc->irq_ctrl_id); + if(status) + goto error_op; + + callback.legacy_callback = &xil_gpio_irq_handler; + callback.ctx = ldesc->extra; + status = irq_register_callback(xil_desc->parent_desc, ldesc->irq_ctrl_id, + &callback); + if(status) + goto error_op; + + *desc = ldesc; + + return 0; + +error_op: + iterator_remove(xil_desc->it); +error_list: + list_remove(xil_desc->callback_list); +error_desc: + free(xil_desc); + free(ldesc); + return status; +} + +/** + * @brief Set GPIO interrupt trigger level. + * @param desc - The GPIO IRQ controller descriptor. + * @param irq_id - Pin number. + * @param trig - New trigger level for the GPIO interrupt. + * @return 0 in case of success, -1 otherwise. + */ +int32_t xil_gpio_irq_trigger_level_set(struct irq_ctrl_desc *desc, + uint32_t irq_id, + enum irq_trig_level trig) +{ + static const int32_t trig_level[5] = {XGPIOPS_IRQ_TYPE_LEVEL_LOW, + XGPIOPS_IRQ_TYPE_LEVEL_HIGH, + XGPIOPS_IRQ_TYPE_EDGE_FALLING, + XGPIOPS_IRQ_TYPE_EDGE_RISING, + XGPIOPS_IRQ_TYPE_EDGE_BOTH + }; + struct xil_gpio_irq_desc *extra; + + extra = desc->extra; + XGpioPs_SetIntrTypePin(&extra->my_Gpio, irq_id, trig_level[trig]); + + return 0; +} + +/** + * @brief Register pin specific GPIO interrupt. + * @param desc - The GPIO IRQ controller descriptor. + * @param irq_id - Pin number. + * @param callback_desc - Callback. + * @return 0 in case of success, -1 otherwise. + */ +int32_t xil_gpio_irq_register_callback(struct irq_ctrl_desc *desc, + uint32_t irq_id, + struct callback_desc *callback_desc) +{ + struct xil_callback_desc *dev_callback; + struct xil_gpio_irq_desc *extra; + + dev_callback = (struct xil_callback_desc *)calloc(1, + sizeof(*dev_callback)); + if(!dev_callback) + return -1; + + dev_callback->pin_nb = irq_id; + dev_callback->callback.legacy_callback = callback_desc->legacy_callback; + dev_callback->callback.ctx = callback_desc->ctx; + dev_callback->triggered = false; + + extra = desc->extra; + XGpioPs_SetDirectionPin(&extra->my_Gpio, irq_id, 0); + list_add_last(extra->callback_list, dev_callback); + + return 0; +} + +/** + * @brief Unregister pin specific GPIO interrupt. + * @param desc - The GPIO IRQ controller descriptor. + * @param irq_id - Pin number. + * @param cb - Callback descriptor. + * @return 0 in case of success, -1 otherwise. + */ +int32_t xil_gpio_irq_unregister_callback(struct irq_ctrl_desc *desc, + uint32_t irq_id, struct callback_desc *cb) +{ + int32_t status; + struct xil_gpio_irq_desc *extra; + struct xil_callback_desc *dev_callback; + struct xil_callback_desc search_callback; + + extra = desc->extra; + search_callback.pin_nb = irq_id; + status = list_get_find(extra->callback_list, &dev_callback, + &search_callback); + if(status) + return -ENXIO; + + free(dev_callback); + + return 0; +} + +/** + * @brief Enable specific GPIO IRQ pin. + * @param desc - The GPIO IRQ controller descriptor. + * @param irq_id - Pin number. + * @return 0 in case of success, -1 otherwise. + */ +int32_t xil_gpio_irq_enable(struct irq_ctrl_desc *desc, uint32_t irq_id) +{ + int32_t status; + struct xil_gpio_irq_desc *extra; + struct xil_callback_desc *callback_desc; + + extra = desc->extra; + + status = iterator_move_to_idx(extra->it, 0); + while(!status) { + iterator_read(extra->it, &callback_desc); + status = iterator_move(extra->it, 1); + if(callback_desc->pin_nb == irq_id) { + callback_desc->enabled = true; + XGpioPs_IntrClearPin(&extra->my_Gpio, irq_id); + break; + } + } + + XGpioPs_IntrEnablePin(&extra->my_Gpio, irq_id); + + return 0; +} + +/** + * @brief Remove the GPIO IRQ controller. + * @param desc - The GPIO IRQ controller descriptor. + * @return 0 in case of success, -1 otherwise. + */ +int32_t xil_gpio_irq_ctrl_remove(struct irq_ctrl_desc *desc) +{ + struct xil_gpio_irq_desc *extra; + struct xil_callback_desc *callback_desc; + + if (!desc) + return -EINVAL; + + extra = desc->extra; + while (0 == list_get_first(extra->callback_list, &callback_desc)) + free(callback_desc); + + iterator_remove(extra->it); + list_remove(extra->callback_list); + free(extra); + free(desc); + + return 0; +} + +/** + * @brief Xilinx platform specific GPIO IRQ platform ops structure + */ +const struct irq_platform_ops xil_gpio_irq_ops = { + .init = &xil_gpio_irq_ctrl_init, + .enable = &xil_gpio_irq_enable, + .disable = &xil_gpio_irq_disable, + .trigger_level_set = &xil_gpio_irq_trigger_level_set, + .register_callback = &xil_gpio_irq_register_callback, + .unregister_callback = xil_gpio_irq_unregister_callback, + .remove = &xil_gpio_irq_ctrl_remove +}; diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_gpio_irq.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_gpio_irq.h new file mode 100644 index 0000000..7c1e3f3 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_gpio_irq.h @@ -0,0 +1,92 @@ +/***************************************************************************//** + * @file xilinx_gpio_irq.h + * @brief Implementation of Xilinx GPIO IRQ Generic Driver. + * @author Porumb Andrei (andrei.porumb@analog.com) +******************************************************************************** + * Copyright 2021(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +#ifndef XILINX_GPIO_IRQ_H_ +#define XILINX_GPIO_IRQ_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ + +#include "stdbool.h" +#include "list.h" +#include "xgpiops.h" +#include "irq.h" + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ +/** + * @struct xil_callback_desc + * @brief Structure holding callback parameters for each pin + */ +struct xil_callback_desc { + int32_t pin_nb; + struct callback_desc callback; + bool triggered; + bool enabled; +}; + +/** + * @struct xil_gpio_irq_init_param + * @brief Structure holding the initialization parameters for Xilinx platform + * specific GPIO IRQ parameters. + */ +struct xil_gpio_irq_init_param { + struct irq_ctrl_desc *parent_desc; + int32_t gpio_device_id; +}; + +/** + * @struct xil_gpio_irq_desc + * @brief Xilinx platform specific GPIO IRQ descriptor + */ +struct xil_gpio_irq_desc { + struct irq_ctrl_desc *parent_desc; + XGpioPs my_Gpio; + struct list_desc *callback_list; + struct iterator *it; +}; + +/** + * @brief Xilinx specific GPIO IRQ platform ops structure + */ +extern const struct irq_platform_ops xil_gpio_irq_ops; + +#endif diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_i2c.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_i2c.c new file mode 100644 index 0000000..bfd7d99 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_i2c.c @@ -0,0 +1,514 @@ +/***************************************************************************//** + * @file xilinx/xilinx_i2c.c + * @brief Implementation of Xilinx I2C Generic Driver. + * @author Antoniu Miclaus (antoniu.miclaus@analog.com) +******************************************************************************** + * Copyright 2019(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ + +#include + +#include +#ifdef XPAR_XIIC_NUM_INSTANCES +#include +#endif +#ifdef XPAR_XIICPS_NUM_INSTANCES +#include +#endif + +#include "error.h" +#include "i2c.h" +#include "xilinx_i2c.h" +#include "list.h" + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ + +static struct list_desc *pl_list = NULL; +static struct iterator *pl_it = NULL; + +#ifdef XIICPS_H +static struct list_desc *ps_list = NULL; +static struct iterator *ps_it = NULL; +#endif /* XIICPS_H */ + +/** + * @struct inst_table_item + * @brief Table item for I2C cores already started. + */ +struct inst_table_item { + /** Instance the BSP driver uses for this core. */ + void *instance; + /** Core ID in BSP. */ + uint32_t device_id; + /** Number of ADI driver instances running on this core. */ + uint32_t inst_no; +}; + +#ifdef XPAR_XIICPS_NUM_INSTANCES +/** Save the current state of the bitrate to not change it each time */ +static uint32_t ps_last_bitrate = 0; +#endif + +/** + * @brief Xilinx platform specific I2C platform ops structure + */ +const struct i2c_platform_ops xil_i2c_ops = { + .i2c_ops_init = &xil_i2c_init, + .i2c_ops_write = &xil_i2c_write, + .i2c_ops_read = &xil_i2c_read, + .i2c_ops_remove = &xil_i2c_remove +}; + +/******************************************************************************/ +/************************ Functions Definitions *******************************/ +/******************************************************************************/ + +/** + * @brief Compare two I2C cores by ID. + * @param el1 - First I2C core to compare. + * @param el2 - Second I2C core to compare. + * @return \ref 0 if the two cores have the same ID, + * \ref difference between the first and the second IDs otherwise. + */ +static int32_t xil_i2c_cmp(void *el1, void *el2) +{ + struct inst_table_item *sel1 = el1, *sel2 = el2; + + return (sel1->device_id - sel2->device_id); +} + +/** + * @brief Configure slave address and bitrate if needed + * @param desc - Descriptor of the I2C device + * @return 0 in case of success, -1 otherwise. + */ +static int32_t xil_i2c_set_transmission_config(struct i2c_desc *desc) +{ + struct xil_i2c_desc *xil_i2c_desc = desc->extra; +#ifdef XIICPS_H + int32_t ret; +#endif + + switch (xil_i2c_desc->type) { + case IIC_PL: +#ifdef XIIC_H + break; +#endif + return -1; + case IIC_PS: +#ifdef XIICPS_H + if (desc->max_speed_hz != ps_last_bitrate) { + ret = XIicPs_SetSClk(xil_i2c_desc->instance, desc->max_speed_hz); + if (ret != XST_SUCCESS) + return -1; + ps_last_bitrate = desc->max_speed_hz; + } + break; +#endif + return -1; + default: + + return -1; + } + + return 0; +} + +/** + * @brief Initialize the I2C communication peripheral. + * @param desc - The I2C descriptor. + * @param param - The structure that contains the I2C parameters. + * @return 0 in case of success, -1 otherwise. + */ +int32_t xil_i2c_init(struct i2c_desc **desc, + const struct i2c_init_param *param) +{ + int32_t ret; + struct i2c_desc *idesc; + struct xil_i2c_desc *xdesc; + struct xil_i2c_init_param *xinit; + + idesc = (struct i2c_desc *)malloc(sizeof(*idesc)); + xdesc = (struct xil_i2c_desc *)malloc(sizeof(*xdesc)); + + if(!idesc || !xdesc) + goto error; + + idesc->max_speed_hz = param->max_speed_hz; + idesc->slave_address = param->slave_address; + xinit = param->extra; + + idesc->extra = xdesc; + xdesc->type = xinit->type; + xdesc->device_id = xinit->device_id; + switch (xinit->type) { + case IIC_PL: +#ifdef XIIC_H + ; /** Intentional empty statement */ + struct inst_table_item tab_check_pl; + struct inst_table_item *temp_el_pl; + + if (!pl_list) + list_init(&pl_list, LIST_DEFAULT, xil_i2c_cmp); + if (!pl_it) + iterator_init(&pl_it, pl_list, true); + + tab_check_pl.device_id = xdesc->device_id; + + if (!IS_ERR_VALUE(list_read_find(pl_list, (void **)&temp_el_pl, + &tab_check_pl))) { + xdesc->instance = temp_el_pl->instance; + temp_el_pl->inst_no++; + break; + } + + xdesc->instance = (XIic *)malloc(sizeof(XIic)); + if(!xdesc->instance) + goto pl_error; + + xdesc->config = XIic_LookupConfig(xinit->device_id); + if(xdesc->config == NULL) + goto pl_error; + + ret = XIic_CfgInitialize(xdesc->instance, + xdesc->config, + ((XIic_Config*)xdesc->config) + ->BaseAddress); + if(ret != 0) + goto pl_error; + + ret = XIic_Start(xdesc->instance); + if(ret != 0) + goto pl_error; + + ret = XIic_SetAddress(xdesc->instance, + XII_ADDR_TO_SEND_TYPE, + param->slave_address); + if(ret != 0) + goto pl_error; + + ret = XIic_SelfTest(xdesc->instance); + if(ret < 0) + goto pl_error; + + ret = XIic_SetGpOutput(xdesc->instance, 1); + if(ret < 0) + goto pl_error; + + temp_el_pl = (struct inst_table_item*)calloc(1, sizeof(*temp_el_pl)); + temp_el_pl->device_id = xinit->device_id; + temp_el_pl->inst_no = 1; + temp_el_pl->instance = xdesc->instance; + pl_list->push(pl_list, temp_el_pl); + + break; +pl_error: + free(xdesc->instance); +#endif + goto error; + case IIC_PS: +#ifdef XIICPS_H + ; /** Intentional empty statement */ + struct inst_table_item tab_check_ps; + struct inst_table_item *temp_el_ps; + + if (!pl_list) + list_init(&ps_list, LIST_DEFAULT, xil_i2c_cmp); + if (!pl_it) + iterator_init(&ps_it, ps_list, true); + + tab_check_ps.device_id = xdesc->device_id; + + if (!IS_ERR_VALUE(list_read_find(ps_list, + (void **)&temp_el_ps, + &tab_check_ps))) { + xdesc->instance = temp_el_ps->instance; + temp_el_ps->inst_no++; + break; + } + + xdesc->instance = (XIicPs *)malloc(sizeof(XIicPs)); + if(!xdesc->instance) + goto ps_error; + + xdesc->config = XIicPs_LookupConfig(xinit->device_id); + if(xdesc->config == NULL) + goto ps_error; + + ret = XIicPs_CfgInitialize(xdesc->instance, + xdesc->config, + ((XIicPs_Config*)xdesc->config) + ->BaseAddress); + if(ret != 0) + goto ps_error; + + XIicPs_SetSClk(xdesc->instance, param->max_speed_hz); + + temp_el_ps = (struct inst_table_item*)calloc(1, sizeof(*temp_el_ps)); + temp_el_ps->device_id = xinit->device_id; + temp_el_ps->inst_no = 1; + temp_el_ps->instance = xdesc->instance; + ps_list->push(ps_list, temp_el_ps); + + break; +ps_error: + free(xdesc->instance); +#endif + goto error; + + default: + goto error; + break; + } + + *desc = idesc; + + return 0; + +error: + free(idesc); + free(xdesc); + + return -1; +} + +/** + * @brief Free the resources allocated by i2c_init(). + * @param desc - The I2C descriptor. + * @return 0 in case of success, -1 otherwise. + */ +int32_t xil_i2c_remove(struct i2c_desc *desc) +{ + struct xil_i2c_desc *xdesc; + int32_t ret; + + xdesc = desc->extra; + + switch (xdesc->type) { + case IIC_PL: +#ifdef XIIC_H + ; /** Intentional empty statement */ + struct inst_table_item tab_check_pl; + struct inst_table_item *temp_el_pl; + tab_check_pl.device_id = xdesc->device_id; + ret = iterator_find(pl_it, &tab_check_pl); + if (ret != 0) + goto error; + iterator_read(pl_it, (void **)&temp_el_pl); + temp_el_pl->inst_no--; + if (temp_el_pl->inst_no != 0) + break; + + ret = XIic_Stop(((XIic *)xdesc->instance)); + + if(ret != 0) + goto error; + + /** Remove list element */ + iterator_get(pl_it, (void **)&temp_el_pl); + free(temp_el_pl); + break; +#endif + goto error; + case IIC_PS: +#ifdef XIICPS_H + ; /** Intentional empty statement */ + struct inst_table_item tab_check_ps; + struct inst_table_item *temp_el_ps; + tab_check_ps.device_id = xdesc->device_id; + ret = iterator_find(ps_it, &tab_check_ps); + if (ret != 0) + goto error; + iterator_read(ps_it, (void **)&temp_el_ps); + temp_el_ps->inst_no--; + if (temp_el_ps->inst_no != 0) + break; + + /** Remove list element */ + iterator_get(ps_it, (void **)&temp_el_ps); + free(temp_el_ps); + break; +#endif + /* Intended fallthrough */ +error: + default: + + return -1; + } + + free(desc->extra); + free(desc); + + return 0; +} + +/** + * @brief Write data to a slave device. + * @param desc - The I2C descriptor. + * @param data - Buffer that stores the transmission data. + * @param bytes_number - Number of bytes to write. + * @param stop_bit - Stop condition control. + * Example: 0 - A stop condition will not be generated; + * 1 - A stop condition will be generated. + * @return 0 in case of success, -1 otherwise. + */ +int32_t xil_i2c_write(struct i2c_desc *desc, + uint8_t *data, + uint8_t bytes_number, + uint8_t stop_bit) +{ + struct xil_i2c_desc *xdesc; + int32_t ret; + + xdesc = desc->extra; + + switch (xdesc->type) { + case IIC_PL: +#ifdef XIIC_H + ret = xil_i2c_set_transmission_config(desc); + if (ret != 0) + return -1; + + XIic_Send(((XIic*)xdesc->instance)->BaseAddress, + desc->slave_address, + data, + bytes_number, + stop_bit ? XIIC_STOP : XIIC_REPEATED_START); + break; +#endif + goto error; + case IIC_PS: +#ifdef XIICPS_H + ret = xil_i2c_set_transmission_config(desc); + if (ret != 0) + return -1; + + ret = XIicPs_SetOptions(xdesc->instance, + stop_bit ? 0 : XIICPS_REP_START_OPTION); + if(ret != 0) + goto error; + + XIicPs_MasterSend(xdesc->instance, + data, + bytes_number, + desc->slave_address); + if(ret != 0) + goto error; + + break; +#endif + /* Intended fallthrough */ +error: + default: + + return -1; + } + + return 0; +} + +/** + * @brief Read data from a slave device. + * @param desc - The I2C descriptor. + * @param data - Buffer that will store the received data. + * @param bytes_number - Number of bytes to read. + * @param stop_bit - Stop condition control. + * Example: 0 - A stop condition will not be generated; + * 1 - A stop condition will be generated. + * @return 0 in case of success, -1 otherwise. + */ +int32_t xil_i2c_read(struct i2c_desc *desc, + uint8_t *data, + uint8_t bytes_number, + uint8_t stop_bit) +{ + struct xil_i2c_desc *xdesc; + int32_t ret; + + xdesc = desc->extra; + + switch (xdesc->type) { + case IIC_PL: +#ifdef XIIC_H + ret = xil_i2c_set_transmission_config(desc); + if (ret != 0) + return -1; + + ret = XIic_Recv(((XIic*)xdesc->instance)->BaseAddress, + desc->slave_address, + data, + bytes_number, + stop_bit ? XIIC_STOP : XIIC_REPEATED_START); + if(ret != bytes_number) + goto error; + + break; +#endif + goto error; + case IIC_PS: +#ifdef XIICPS_H + ret = xil_i2c_set_transmission_config(desc); + if (ret != 0) + return -1; + + ret = XIicPs_SetOptions(xdesc->instance, + stop_bit ? 0 : XIICPS_REP_START_OPTION); + if(ret != 0) + goto error; + + XIicPs_MasterRecv(xdesc->instance, + data, + bytes_number, + desc->slave_address); + if(ret != 0) + goto error; + + break; +#endif + /* Intended fallthrough */ +error: + default: + return -1; + + break; + } + + return 0; +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_i2c.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_i2c.h new file mode 100644 index 0000000..250db84 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_i2c.h @@ -0,0 +1,115 @@ +/******************************************************************************* + * @file xilinx/xilinx_i2c.h + * @brief Header containing types used in the i2c driver. + * @author scuciurean (sergiu.cuciurean@analog.com) +******************************************************************************** + * Copyright 2019(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +#ifndef XILINX_I2C_H_ +#define XILINX_I2C_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ + +#include + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ + +/** + * @enum xil_i2c_type + * @brief Xilinx platform architecture sections + */ +enum xil_i2c_type { + /** Programmable Logic */ + IIC_PL, + /** Processing System */ + IIC_PS +}; + +/** + * @struct xil_i2c_init + * @brief Structure holding the initialization parameters for Xilinx platform + * specific I2C parameters. + */ +struct xil_i2c_init_param { + /** Xilinx architecture */ + enum xil_i2c_type type; + /** Device ID */ + uint32_t device_id; +}; + +/** + * @struct xil_i2c_desc + * @brief Xilinx platform specific I2C descriptor + */ +struct xil_i2c_desc { + /** Xilinx architecture */ + enum xil_i2c_type type; + /** Device ID */ + uint32_t device_id; + /** Xilinx I2C configuration */ + void *config; + /** Xilinx I2C Instance */ + void *instance; +}; + +/** + * @brief Xilinx platform specific i2c platform ops structure + */ +extern const struct i2c_platform_ops xil_i2c_ops; + +/******************************************************************************/ +/************************ Functions Declarations ******************************/ +/******************************************************************************/ + +/* Initialize the I2C communication peripheral. */ +int32_t xil_i2c_init(struct i2c_desc **desc, + const struct i2c_init_param *param); + +/* Free the resources allocated by i2c_init(). */ +int32_t xil_i2c_remove(struct i2c_desc *desc); + +/* I2C Write data */ +int32_t xil_i2c_write(struct i2c_desc *desc, uint8_t *data, + uint8_t bytes_number, uint8_t stop_bit); + +/* I2C Read data. */ +int32_t xil_i2c_read(struct i2c_desc *desc, uint8_t *data, + uint8_t bytes_number, uint8_t stop_bit); + +#endif // XILINX_I2C_H_ diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_irq.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_irq.c new file mode 100644 index 0000000..02f965c --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_irq.c @@ -0,0 +1,433 @@ +/***************************************************************************//** + * @file xilinx/xilinx_irq.c + * @brief Implementation of Xilinx IRQ Generic Driver. + * @author Cristian Pop (cristian.pop@analog.com) +******************************************************************************** + * Copyright 2019(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ + +#include +#include +#include "error.h" +#include "irq.h" +#include "xilinx_irq.h" +#ifdef XPAR_XSCUGIC_NUM_INSTANCES +#include +#endif +#ifdef XPAR_XINTC_NUM_INSTANCES +#include +#endif +#ifdef XPAR_XTMRCTR_NUM_INSTANCES +#include +#endif + +/******************************************************************************/ +/************************ Functions Definitions *******************************/ +/******************************************************************************/ + +/** + * @brief Initialize the IRQ interrupts. + * @param desc - The IRQ controller descriptor. + * @param param - The structure that contains the IRQ parameters. + * @return 0 in case of success, -1 otherwise. + */ +int32_t xil_irq_ctrl_init(struct irq_ctrl_desc **desc, + const struct irq_init_param *param) +{ + int32_t status; + struct irq_ctrl_desc *descriptor; + struct xil_irq_desc *xil_dev; +#ifdef XSCUGIC_H + void *config; +#endif + + descriptor = (struct irq_ctrl_desc *)calloc(1, sizeof *descriptor); + if(!descriptor) + return -1; + xil_dev = (struct xil_irq_desc *)calloc(1, sizeof *xil_dev); + if(!xil_dev) { + free(descriptor); + return -1; + } + + Xil_ExceptionInit(); + + descriptor->irq_ctrl_id = param->irq_ctrl_id; + descriptor->extra = xil_dev; + xil_dev->type = ((struct xil_irq_init_param *)param->extra)->type; + + switch(xil_dev->type) { + case IRQ_PS: +#ifdef XSCUGIC_H + xil_dev->instance = calloc(1, sizeof(XScuGic)); + if(!xil_dev->instance) + goto error; + + config = XScuGic_LookupConfig(descriptor->irq_ctrl_id); + if(!config) + goto ps_error; + + status = XScuGic_CfgInitialize(xil_dev->instance, config, + ((XScuGic_Config *)config)->CpuBaseAddress); + if (status != 0) + goto ps_error; + + Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, + (Xil_ExceptionHandler)XScuGic_InterruptHandler, + xil_dev->instance); + + break; +ps_error: + free(xil_dev->instance); +#endif + goto error; + case IRQ_PL: +#ifdef XINTC_H + xil_dev->instance = calloc(1, sizeof(XIntc)); + if(!xil_dev->instance) + goto error; + + status = XIntc_Initialize(xil_dev->instance, descriptor->irq_ctrl_id); + if(status != 0) + goto pl_error; + + status = XIntc_Start(xil_dev->instance, XIN_REAL_MODE); + if(status != 0) + goto pl_error; + + break; +pl_error: + free(xil_dev->instance); +#endif + goto error; + default: + goto error; + + break; + } + + *desc = descriptor; + + return 0; + +error: + free(xil_dev); + free(descriptor); + + return -1; +} + +/** + * @brief Enable global interrupts. + * @return 0 in case of success, -1 otherwise. + */ +int32_t xil_irq_global_enable(struct irq_ctrl_desc *desc) +{ + /* Enable interrupts */ + Xil_ExceptionEnable(); + + return 0; +} + +/** + * @brief Disable global interrupts. + * @return 0 in case of success, -1 otherwise. + */ +int32_t xil_irq_global_disable(struct irq_ctrl_desc *desc) +{ + /* Disable interrupts */ + Xil_ExceptionDisable(); + + return 0; +} + +/** + * @brief Enable specific interrupt. + * @param desc - The IRQ controller descriptor. + * @param irq_id - Interrupt identifier. + * @return 0 in case of success, -1 otherwise. + */ +int32_t xil_irq_enable(struct irq_ctrl_desc *desc, uint32_t irq_id) +{ + struct xil_irq_desc *xil_dev = desc->extra; + + switch(xil_dev->type) { + case IRQ_PS: +#ifdef XSCUGIC_H + XScuGic_Enable(xil_dev->instance, irq_id); +#endif + break; + case IRQ_PL: +#ifdef XINTC_H + XIntc_Enable(xil_dev->instance, irq_id); +#endif + break; + default: + + return -1; + } + + return 0; +} + +/** + * @brief Disable specific interrupt. + * @param desc - The IRQ controller descriptor. + * @param irq_id - Interrupt identifier. + * @return 0 in case of success, -1 otherwise. + */ +int32_t xil_irq_disable(struct irq_ctrl_desc *desc, uint32_t irq_id) +{ + struct xil_irq_desc *xil_dev = desc->extra; + + switch(xil_dev->type) { + case IRQ_PS: +#ifdef XSCUGIC_H + XScuGic_Disable(xil_dev->instance, irq_id); +#endif + break; + case IRQ_PL: +#ifdef XINTC_H + XIntc_Disable(xil_dev->instance, irq_id); +#endif + break; + default: + + return -1; + } + + return 0; +} + +/** + * @brief Register a callback to handle the irq events. + * @param desc - The IRQ controller descriptor. + * @param irq_id - Interrupt identifier. + * @param callback_desc - Callback descriptor + * @return 0 in case of success, -1 otherwise. + */ +int32_t xil_irq_register_callback(struct irq_ctrl_desc *desc, + uint32_t irq_id, + struct callback_desc *callback_desc) +{ + struct xil_irq_desc *xil_dev = desc->extra; + + switch(xil_dev->type) { + case IRQ_PS: +#ifdef XSCUGIC_H + return XScuGic_Connect(xil_dev->instance, irq_id, + (Xil_InterruptHandler) callback_desc->callback, + callback_desc->ctx); +#endif + break; + case IRQ_PL: +#ifdef XINTC_H + if (irq_id == XPAR_AXI_INTC_AXI_TIMER_INTERRUPT_INTR) { + int32_t ret; + + ret = XIntc_Connect(xil_dev->instance, irq_id, + XTmrCtr_InterruptHandler, callback_desc->legacy_config); + if (IS_ERR_VALUE(ret)) + return -1; + XTmrCtr_SetHandler(callback_desc->legacy_config, + (XTmrCtr_Handler)callback_desc->callback, + callback_desc->ctx); + + return 0; + } + return XIntc_Connect(xil_dev->instance, irq_id, + (XInterruptHandler) callback_desc->callback, + callback_desc->ctx); +#endif + break; + default: + break; + } + + return -1; +} + +/** + * @brief Set interrupt trigger level. + * @param desc - The IRQ controller descriptor. + * @param irq_id - Interrupt identifier. + * @param trig - New trigger level for the interrupt. + * @return 0 in case of success, -1 otherwise. + */ +int32_t xil_irq_trigger_level_set(struct irq_ctrl_desc *desc, + uint32_t irq_id, + enum irq_trig_level trig) +{ + struct xil_irq_desc *xil_dev = desc->extra; + + if(trig == IRQ_EDGE_BOTH) + return -EINVAL; + + switch(xil_dev->type) { + case IRQ_PS: +#ifdef XSCUGIC_H + ; + uint8_t priority, trigger; + + XScuGic_GetPriorityTriggerType(xil_dev->instance, irq_id, &priority, + &trigger); + trigger = trig; + XScuGic_SetPriorityTriggerType(xil_dev->instance, irq_id, priority, + trigger); + + return 0; +#endif + break; + case IRQ_PL: +#ifdef XINTC_H + /* Interrupt trigger determined in hardware. TODO: verify */ + return 0; +#endif + break; + default: + break; + } + + return -1; +} + +/** + * @brief Set interrupt trigger level. + * @param desc - The IRQ controller descriptor. + * @param irq_id - Interrupt identifier. + * @param priority_level - New priority level for the interrupt. + * @return 0 in case of success, -1 otherwise. + */ +int32_t xil_irq_set_priority(struct irq_ctrl_desc *desc, + uint32_t irq_id, + uint32_t priority_level) +{ + struct xil_irq_desc *xil_dev = desc->extra; + + if(priority_level % 8 != 0) + return -EINVAL; + + switch(xil_dev->type) { + case IRQ_PS: +#ifdef XSCUGIC_H + ; + uint8_t priority, trigger; + + XScuGic_GetPriorityTriggerType(xil_dev->instance, irq_id, &priority, + &trigger); + priority = priority_level; + XScuGic_SetPriorityTriggerType(xil_dev->instance, irq_id, priority, + trigger); + + return 0; +#endif + break; + case IRQ_PL: +#ifdef XINTC_H + /* Interrupt priority level determined in hardware. TODO: verify */ + return 0; +#endif + break; + default: + break; + } + + return -1; +} + +/** + * @brief Unregisters a generic IRQ handling function. + * @param desc - The IRQ controller descriptor. + * @param irq_id - Interrupt identifier. + * @param cb - Callback descriptor. + * @return 0 in case of success, -1 otherwise. + */ +int32_t xil_irq_unregister_callback(struct irq_ctrl_desc *desc, + uint32_t irq_id, struct callback_desc *cb) +{ + struct xil_irq_desc *xil_dev = desc->extra; + + switch(xil_dev->type) { + case IRQ_PS: +#ifdef XSCUGIC_H + XScuGic_Disconnect(xil_dev->instance, irq_id); +#endif + break; + case IRQ_PL: +#ifdef XINTC_H + XIntc_Disconnect(xil_dev->instance, irq_id); +#endif + break; + default: + + return -1; + } + + return 0; +} + +/** + * @brief Free the resources allocated by irq_ctrl_init(). + * @param desc - The IRQ control descriptor. + * @return 0 in case of success, -1 otherwise. + */ +int32_t xil_irq_ctrl_remove(struct irq_ctrl_desc *desc) +{ + struct xil_irq_desc *xil_dev = desc->extra; + free(xil_dev->instance); + free(desc->extra); + free(desc); + + return 0; +} + +/** + * @brief Xilinx platform specific IRQ platform ops structure + */ +const struct irq_platform_ops xil_irq_ops = { + .init = &xil_irq_ctrl_init, + .register_callback = &xil_irq_register_callback, + .unregister_callback = &xil_irq_unregister_callback, + .global_enable = &xil_irq_global_enable, + .global_disable = &xil_irq_global_disable, + .trigger_level_set = &xil_irq_trigger_level_set, + .set_priority = &xil_irq_set_priority, + .enable = &xil_irq_enable, + .disable = &xil_irq_disable, + .remove = &xil_irq_ctrl_remove +}; diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_irq.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_irq.h new file mode 100644 index 0000000..7e0f771 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_irq.h @@ -0,0 +1,91 @@ +/******************************************************************************* + * @file xilinx_irq.h + * @brief Header containing types used in the irq driver. + * @author Cristian Pop (cristian.pop@analog.com) +******************************************************************************** + * Copyright 2019(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +#ifndef XILINX_IRQ_H_ +#define XILINX_IRQ_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ + +#include +#include "irq.h" + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ + +/** + * @enum xil_irq_type + * @brief Xilinx platform architecture sections + */ +enum xil_irq_type { + /** Programmable Logic */ + IRQ_PL, + /** Processing System */ + IRQ_PS +}; + +/** + * @struct xil_irq_init_param + * @brief Structure holding the initialization parameters for Xilinx platform + * specific IRQ parameters. + */ +struct xil_irq_init_param { + /** Xilinx architecture */ + enum xil_irq_type type; +}; + +/** + * @struct xil_irq_desc + * @brief Xilinx platform specific IRQ descriptor + */ +struct xil_irq_desc { + /** Xilinx architecture */ + enum xil_irq_type type; + /** Xilinx IRQ Instance */ + void *instance; +}; + +/** + * @brief Xilinx specific IRQ platform ops structure + */ +extern const struct irq_platform_ops xil_irq_ops; + +#endif diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_spi.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_spi.c new file mode 100644 index 0000000..8a73a7b --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_spi.c @@ -0,0 +1,462 @@ +/***************************************************************************//** + * @file xilinx/xilinx_spi.c + * @brief Implementation of Xilinx SPI Generic Driver. + * @author Antoniu Miclaus (antoniu.miclaus@analog.com) +******************************************************************************** + * Copyright 2019(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ + +#include + +#include +#ifdef XPAR_XSPI_NUM_INSTANCES +#include +#endif +#ifdef XPAR_XSPIPS_NUM_INSTANCES +#include +#endif + +#include "error.h" +#include "spi.h" +#include "xilinx_spi.h" + +#if defined(PLATFORM_ZYNQ) +#define SPI_CLK_FREQ_HZ(dev) (XPAR_PS7_SPI_ ## dev ## _SPI_CLK_FREQ_HZ) +#define SPI_NUM_INSTANCES XPAR_XSPIPS_NUM_INSTANCES +#elif defined(PLATFORM_ZYNQMP) +#define SPI_CLK_FREQ_HZ(dev) (XPAR_PSU_SPI_ ## dev ## _SPI_CLK_FREQ_HZ) +#define SPI_NUM_INSTANCES XPAR_XSPIPS_NUM_INSTANCES +#else +#define SPI_NUM_INSTANCES 0 +#endif + +#warning SPI delays are not supported on the xilinx platform + +/******************************************************************************/ +/************************ Functions Definitions *******************************/ +/******************************************************************************/ + +/** + * @brief Initialize the hardware SPI peripherial + * + * @param xdesc Platform specific SPI descriptor + * @param xinit Platform specific SPI init param + * @return int32_t -1 if the device couldn't be found or configured + */ +static int32_t spi_init_pl(struct spi_desc *desc, + const struct spi_init_param *param) +{ +#ifdef XSPI_H + int32_t ret; + struct xil_spi_desc *xdesc; + struct xil_spi_init_param *xinit; + + xdesc = (struct xil_spi_desc*)malloc(sizeof(struct xil_spi_desc)); + if(!xdesc) { + free(xdesc); + return -1; + } + + desc->extra = xdesc; + xinit = param->extra; + xdesc->type = xinit->type; + xdesc->flags = xinit->flags; + + xdesc->instance = (XSpi*)calloc(1, sizeof(XSpi)); + if(!xdesc->instance) + goto pl_error; + + xdesc->config = XSpi_LookupConfig(param->device_id); + if(xdesc->config == NULL) + goto pl_error; + + ret = XSpi_CfgInitialize(xdesc->instance, + xdesc->config, + ((XSpi_Config*)xdesc->config) + ->BaseAddress); + if(ret != 0) + goto pl_error; + + ret = XSpi_Initialize(xdesc->instance, param->device_id); + if (ret != 0) + goto pl_error; + + ret = XSpi_SetOptions(xdesc->instance, + XSP_MASTER_OPTION | + ((desc->mode & SPI_CPOL) ? + XSP_CLK_ACTIVE_LOW_OPTION : 0) | + ((desc->mode & SPI_CPHA) ? + XSP_CLK_PHASE_1_OPTION : 0)); + if (ret != 0) + goto pl_error; + + ret = XSpi_Start(xdesc->instance); + if (ret != 0) + goto pl_error; + + XSpi_IntrGlobalDisable((XSpi *)(xdesc->instance)); + + return 0; + +pl_error: + free(xdesc->instance); + free(xdesc); +#endif + return -1; +} + +/** + * @brief Initialize the hardware SPI peripherial + * + * @param xdesc Platform specific SPI descriptor + * @param xinit Platform specific SPI init param + * @return int32_t -1 if the device couldn't be found or configured + */ +static int32_t spi_init_ps(struct spi_desc *desc, + const struct spi_init_param *param) +{ +#ifdef XSPIPS_H + int32_t ret; + struct xil_spi_desc *xdesc; + struct xil_spi_init_param *xinit; + const uint32_t prescaler_default = XSPIPS_CLK_PRESCALE_64; + const uint32_t prescaler_min = XSPIPS_CLK_PRESCALE_4; + const uint32_t prescaler_max = XSPIPS_CLK_PRESCALE_256; + uint32_t prescaler = 0u; + uint32_t input_clock = 0u; + + xdesc = (struct xil_spi_desc*)malloc(sizeof(struct xil_spi_desc)); + if(!xdesc) { + free(xdesc); + return -1; + } + + desc->extra = xdesc; + xinit = param->extra; + xdesc->type = xinit->type; + xdesc->flags = xinit->flags; + + xdesc->instance = (XSpiPs*)malloc(sizeof(XSpiPs)); + if(!xdesc->instance) + goto ps_error; + + xdesc->config = XSpiPs_LookupConfig(param->device_id); + if(xdesc->config == NULL) + goto ps_error; + + ret = XSpiPs_CfgInitialize(xdesc->instance, + xdesc->config, + ((XSpiPs_Config*)xdesc->config) + ->BaseAddress); + if(ret != 0) + goto ps_error; + + switch (param->device_id) { +#if (SPI_NUM_INSTANCES >= 1) + case 0: + input_clock = SPI_CLK_FREQ_HZ(0); + break; +#endif +#if (SPI_NUM_INSTANCES >= 2) + case 1: + input_clock = SPI_CLK_FREQ_HZ(1); + break; +#endif + default: + goto ps_error; + }; + + if (desc->max_speed_hz != 0u) { + uint32_t div = input_clock / desc->max_speed_hz; + uint32_t rem = input_clock % desc->max_speed_hz; + uint32_t po2 = !(div & (div - 1)) && !rem; + + // find the power of two just higher than div and + // store the exponent in prescaler + while(div) { + prescaler += 1; + div >>= 1u; + } + + // this exponent - 1 is needed because of the way + // xilinx stores it into registers + if (prescaler) + prescaler -= 1; + + // this exponent - 1 is needed when initial div was + // precisely a power of two + if (prescaler && po2) + prescaler -= 1; + + if (prescaler < prescaler_min) + prescaler = prescaler_min; + + if (prescaler > prescaler_max) + prescaler = prescaler_max; + } else + prescaler = prescaler_default; + + ret = XSpiPs_SetClkPrescaler(xdesc->instance, prescaler); + + if(ret != 0) + goto ps_error; + + return 0; + +ps_error: + free(xdesc->instance); + free(xdesc); +#endif + return -1; +} + +/** + * @brief Initialize the SPI communication peripheral. + * @param desc - The SPI descriptor. + * @param param - The structure that contains the SPI parameters. + * @return 0 in case of success, -1 otherwise. + */ +int32_t xil_spi_init(struct spi_desc **desc, + const struct spi_init_param *param) +{ + int32_t ret; + enum xil_spi_type *spi_type; + + if (!param) { + return -1; + } + + *desc = malloc(sizeof(**desc)); + if(! *desc) { + free(*desc); + return -1; + } + + spi_type = param->extra; + + if(!spi_type) + goto init_error; + + (*desc)->max_speed_hz = param->max_speed_hz; + (*desc)->mode = param->mode; + (*desc)->bit_order = param->bit_order; + (*desc)->chip_select = param->chip_select; + + switch (*spi_type) { + case SPI_PL: + ret = spi_init_pl(*desc, param); + break; + case SPI_PS: + ret = spi_init_ps(*desc, param); + break; + + default: + goto init_error; + break; + } + + if (ret != 0) { +init_error: + free(*desc); + + return -1; + } + + return ret; +} + +/** + * @brief Free the resources allocated by spi_init(). + * @param desc - The SPI descriptor. + * @return 0 in case of success, -1 otherwise. + */ +int32_t xil_spi_remove(struct spi_desc *desc) +{ +#ifdef XSPI_H + int32_t ret; +#endif + struct xil_spi_desc *xdesc = NULL; + enum xil_spi_type *spi_type; + + spi_type = desc->extra; + + if(!spi_type) + return -1; + + switch (*spi_type ) { + case SPI_PL: +#ifdef XSPI_H + xdesc = desc->extra; + + if(!xdesc) + return -1; + + ret = XSpi_Stop((XSpi *)(xdesc->instance)); + if(ret != 0) + goto error; +#endif + break; + case SPI_PS: +#ifdef XSPIPS_H + xdesc = desc->extra; + + if(!xdesc) + return -1; +#endif + break; + + /* Intended fallthrough */ +#ifdef XSPI_H +error: +#endif + default: + return -1; + } + + if(xdesc) + free(xdesc->instance); + free(desc->extra); + free(desc); + + return 0; +} + +/** + * @brief Write and read data to/from SPI. + * @param desc - The SPI descriptor. + * @param data - The buffer with the transmitted/received data. + * @param bytes_number - Number of bytes to write/read. + * @return 0 in case of success, -1 otherwise. + */ +int32_t xil_spi_write_and_read(struct spi_desc *desc, + uint8_t *data, + uint16_t bytes_number) +{ + int32_t ret; +#ifdef XSPI_H + uint32_t ctrl_reg; +#endif + struct xil_spi_desc *xdesc; + enum xil_spi_type *spi_type; + + ret = -1; + + spi_type = desc->extra; + + if(!spi_type) + return -1; + + xdesc = desc->extra; + + switch (*spi_type) { + case SPI_PL: +#ifdef XSPI_H + ret = XSpi_SetOptions(xdesc->instance, + XSP_MASTER_OPTION | + ((desc->mode & SPI_CPOL) ? + XSP_CLK_ACTIVE_LOW_OPTION : 0) | + ((desc->mode & SPI_CPHA) ? + XSP_CLK_PHASE_1_OPTION : 0)); + if (ret != 0) + goto error; + + ctrl_reg = XSpi_GetControlReg(((XSpi*)xdesc->instance)); + if (desc->bit_order == SPI_BIT_ORDER_LSB_FIRST) + ctrl_reg |= XSP_CR_LSB_MSB_FIRST_MASK; + else + ctrl_reg &= ~ XSP_CR_LSB_MSB_FIRST_MASK; + XSpi_SetControlReg(((XSpi*)xdesc->instance), ctrl_reg); + + ret = XSpi_SetSlaveSelect(xdesc->instance, + 0x01 << desc->chip_select); + if (ret != 0) + goto error; + + ret = XSpi_Transfer(xdesc->instance, + data, + data, + bytes_number); + if (ret != 0) + goto error; +#endif + break; + case SPI_PS: +#ifdef XSPIPS_H + ret = XSpiPs_SetOptions(xdesc->instance, + XSPIPS_MASTER_OPTION | + ((xdesc->flags & SPI_CS_DECODE) ? + XSPIPS_DECODE_SSELECT_OPTION : 0) | + XSPIPS_FORCE_SSELECT_OPTION | + ((desc->mode & SPI_CPOL) ? + XSPIPS_CLK_ACTIVE_LOW_OPTION : 0) | + ((desc->mode & SPI_CPHA) ? + XSPIPS_CLK_PHASE_1_OPTION : 0)); + if (ret != 0) + goto error; + + ret = XSpiPs_SetSlaveSelect(xdesc->instance, + desc->chip_select); + if (ret != 0) + goto error; + ret = XSpiPs_PolledTransfer(xdesc->instance, + data, + data, + bytes_number); + if (ret != 0) + goto error; + ret = XSpiPs_SetSlaveSelect(xdesc->instance, SPI_DEASSERT_CURRENT_SS); + if (ret != 0) + goto error; +#endif + break; +error: + default: + return -1; + } + + return ret; +} + +/** + * @brief Xilinx platform specific SPI platform ops structure + */ +const struct spi_platform_ops xil_spi_ops = { + .init = &xil_spi_init, + .write_and_read = &xil_spi_write_and_read, + .remove = &xil_spi_remove +}; diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_spi.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_spi.h new file mode 100644 index 0000000..b0c4681 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_spi.h @@ -0,0 +1,115 @@ +/******************************************************************************* + * @file xilinx/xilinx_spi.h + * @brief Header containing extra types used in the spi driver. + * @author scuciurean (sergiu.cuciurean@analog.com) +******************************************************************************** + * Copyright 2019(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ +#ifndef XILINX_SPI_H_ +#define XILINX_SPI_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ + +#include +#include "spi.h" + +/******************************************************************************/ +/********************** Macros and Constants Definitions **********************/ +/******************************************************************************/ + +#define SPI_CS_DECODE 0x01 +#define SPI_DEASSERT_CURRENT_SS 0x0F + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ + +/** + * @enum xil_spi_type + * @brief Xilinx platform architecture sections + */ +enum xil_spi_type { + /** Programmable Logic */ + SPI_PL, + /** Processing System */ + SPI_PS, + /** SPI Engine */ + SPI_ENGINE +}; + +/** + * @struct xil_spi_init_param + * @brief Structure holding the initialization parameters for Xilinx platform + * specific SPI parameters when using xil_spi_ops. + */ +struct xil_spi_init_param { + /** Xilinx architecture */ + enum xil_spi_type type; + /** SPI flags */ + uint32_t flags; +}; + +/** + * @struct xil_spi_desc + * @brief Xilinx platform specific SPI descriptor + */ +struct xil_spi_desc { + /** Xilinx architecture */ + enum xil_spi_type type; + /** SPI flags */ + uint32_t flags; + /** SPI configuration */ + void *config; + /** SPI instance */ + void *instance; +}; + +/** + * @brief SPI engine platform ops structure + */ +extern const struct spi_platform_ops spi_eng_platform_ops; + +/** + * @brief Xilinx specific SPI platform ops structure + */ +extern const struct spi_platform_ops xil_spi_ops; + +/** + * @brief Xilinx SPI PS register functions. No extra param needed + */ +extern const struct spi_platform_ops xil_spi_pl_ops; + +#endif // XILINX_SPI_H_ diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_spi_pl.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_spi_pl.c new file mode 100644 index 0000000..bef2cf4 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_spi_pl.c @@ -0,0 +1,533 @@ +/******************************************************************************* + * @file xilinx/xilinx_spi_pl.c + * @brief Driver for spi pl device using registers + * @author Mihail Chindris (mihail.chindris@analog.com) +******************************************************************************** + * Copyright 2021(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +#include + +#ifdef XPAR_XSPI_NUM_INSTANCES + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ + +#include +#include +#include +#include "spi.h" +#include "error.h" +#include "delay.h" +#include "util.h" +#include "alloc.h" + +/******************************************************************************/ +/********************** Macros and Constants Definitions **********************/ +/******************************************************************************/ + +#define XPS_TYPE_WRITE 0x1 +#define XPS_TYPE_READ 0x2 +#define XSP_DEFAULT_SR_VALUE 0x25 +#define XSP_DUMMY_DATA 0xFF + +/* + * Used to store current spi device configuration in order to not update it for + * each spi transaction if not needed. + */ +static struct spi_desc g_spi_desc[XPAR_XSPI_NUM_INSTANCES]; + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ + +/* Xilinx PL spi specific data */ +struct xspi_desc { + uint32_t base_addr; + uint32_t disable_slaves_mask; + uint32_t fifo_depth; + uint8_t cs_asserted; +}; + +/* Structure used to iterate over SPI messages */ +struct msg_iter { + struct spi_msg *msgs; + uint32_t len; + uint32_t i; + uint32_t buff_i; + uint8_t is_wr; +}; + +/******************************************************************************/ +/************************ Functions Definitions *******************************/ +/******************************************************************************/ + +/* Read device register */ +static inline uint32_t _read(struct xspi_desc *xdesc, uint32_t reg) +{ + return Xil_In32(xdesc->base_addr + reg); +} + +/* Write device register */ +static inline void _write(struct xspi_desc *xdesc, uint32_t reg, uint32_t val) +{ + Xil_Out32(xdesc->base_addr + reg, val); +} + +/* Update device register. Only bits set to 1 in mask will be modified */ +static inline void _wr_mask(struct xspi_desc *xdesc, uint32_t reg, + uint32_t val, uint32_t mask) +{ + uint32_t tmp; + + tmp = _read(xdesc, reg); + tmp &= ~mask; + tmp |= val & mask; + _write(xdesc, reg, tmp); +} + +/* Update spi device configuration if needed */ +static void _update_mode(struct spi_desc *desc) +{ + uint32_t val; + uint32_t mask; + + if (desc->mode == g_spi_desc[desc->device_id].mode && + desc->bit_order == g_spi_desc[desc->device_id].bit_order) + return ; + + /* Update mode */ + val = desc->mode | SPI_CPOL ? XSP_CR_CLK_POLARITY_MASK : 0; + val |= desc->mode | SPI_CPHA ? XSP_CR_CLK_PHASE_MASK : 0; + mask = XSP_CR_CLK_POLARITY_MASK | XSP_CR_CLK_PHASE_MASK; + + val |= desc->bit_order == SPI_BIT_ORDER_LSB_FIRST ? + XSP_CR_LSB_MSB_FIRST_MASK : 0; + mask |= XSP_CR_LSB_MSB_FIRST_MASK; + + _wr_mask(desc->extra, XSP_CR_OFFSET, val, mask); + g_spi_desc[desc->device_id] = *desc; +} + +/* Configure SPI PL device */ +static int32_t _xil_spi_init_dev(struct xspi_desc *xdesc) +{ + uint32_t val; + + /* Software reset */ + _write(xdesc, XSP_SRR_OFFSET, XSP_SRR_RESET_MASK); + mdelay(1); + + /* Check if success reset */ + val = _read(xdesc, XSP_SR_OFFSET); + if (val != XSP_DEFAULT_SR_VALUE) + return -ENODEV; + + /* Disable interrupts. Drivers only works with pooled mode */ + _write(xdesc, XSP_IIER_OFFSET, 0); + + /* Deasert chip selects */ + _write(xdesc, XSP_SSR_OFFSET, 0xFFFFFFFF); + + /* Get fifo depth */ + while (!(_read(xdesc, XSP_SR_OFFSET) & XSP_SR_TX_FULL_MASK)) + _write(xdesc, XSP_DTR_OFFSET, XSP_DUMMY_DATA); + xdesc->fifo_depth = _read(xdesc, XSP_TFO_OFFSET) + 1; + + /* Configure spi device */ + val = XSP_CR_ENABLE_MASK | + XSP_CR_MANUAL_SS_MASK | + XSP_CR_TXFIFO_RESET_MASK | + XSP_CR_RXFIFO_RESET_MASK | + XSP_CR_MASTER_MODE_MASK | + XSP_CR_TRANS_INHIBIT_MASK; + + _write(xdesc, XSP_CR_OFFSET, val); + + return 0; +} + +/* Initialize spi_desc structure and device */ +static int32_t xil_spi_init_pl(struct spi_desc **desc, + const struct spi_init_param *param) +{ + struct spi_desc *ldesc; + struct xspi_desc *xdesc; + int32_t err; + XSpi_Config *cfg; + + if (!desc || !param || param->device_id >= XPAR_XSPI_NUM_INSTANCES) + return -EINVAL; + + cfg = XSpi_LookupConfig(param->device_id); + if (!cfg) + return -EINVAL; + + if (param->chip_select > cfg->NumSlaveBits) + return -EINVAL; + + ldesc = (struct spi_desc *)calloc(1, sizeof(*ldesc)); + if (!ldesc) + return -ENOMEM; + + xdesc = (struct xspi_desc *)calloc(1, sizeof(*xdesc)); + if (!xdesc) { + err = -ENOMEM; + goto error; + } + + ldesc->bit_order = param->bit_order; + ldesc->chip_select = param->chip_select; + ldesc->mode = param->mode; + xdesc->base_addr = cfg->BaseAddress; + xdesc->disable_slaves_mask = (1 << cfg->NumSlaveBits) - 1; + xdesc->cs_asserted = 0; + + err = _xil_spi_init_dev(xdesc); + if (IS_ERR_VALUE(err)) + goto error; + + ldesc->extra = xdesc; + *desc = ldesc; + + return 0; + +error: + free(xdesc); + free(ldesc); + + return err; +} + +/* Remove allocated resources */ +static int32_t xil_spi_remove_pl(struct spi_desc *desc) +{ + if (!desc) + return -EINVAL; + + free(desc->extra); + free(desc); + + return 0; +} + +/* + * Fill fifo until fifo is full or no more data available. + * Return the number of written bytes. + */ +static uint32_t _wr_fifo(struct xspi_desc *xdesc, uint8_t *data, + uint32_t len) +{ + uint32_t i; + uint32_t to_write; + uint32_t tx_available; + + tx_available = xdesc->fifo_depth - _read(xdesc, XSP_TFO_OFFSET); + to_write = min(tx_available, len); + for (i = 0; i < to_write; ++i) + if (data) + _write(xdesc, XSP_DTR_OFFSET, data[i]); + else + _write(xdesc, XSP_DTR_OFFSET, XSP_DUMMY_DATA); + + return to_write; +} + +/* + * Read fifo until fifo is empty or no more buffer available to read into + * Returns the number of read bytes + */ +static uint32_t _rd_fifo(struct xspi_desc *xdesc, uint8_t *data, + uint32_t len) +{ + uint32_t rx_available; + uint32_t to_read; + uint32_t tmp; + uint32_t i; + + tmp = _read(xdesc, XSP_SR_OFFSET); + if (tmp & XSP_SR_RX_EMPTY_MASK) + return 0; + + rx_available = _read(xdesc, XSP_RFO_OFFSET) + 1; + to_read = min(rx_available, len); + for (i = 0; i < to_read; ++i) { + tmp = _read(xdesc, XSP_DRR_OFFSET); + if (data) + data[i] = tmp; + } + + return to_read; +} + +/* If start is 0 stop transfer, else start it. + * CS will be asserted if needed. If different CS othere than the one needed + * is asserted. Deassert it and assert correct one */ +static void _xil_spi_start_transfer(struct spi_desc *desc, uint8_t start) +{ + struct xspi_desc *xdesc; + + xdesc = desc->extra; + if (start) { + if (xdesc->cs_asserted) { + if (desc->chip_select == + g_spi_desc[desc->device_id].chip_select) + return ; + else + /* Stop transfer to start again with new CS */ + _xil_spi_start_transfer(desc, 0); + } + _write(xdesc, XSP_SSR_OFFSET, ~(1 << desc->chip_select)); + _wr_mask(xdesc, XSP_CR_OFFSET, 0, XSP_CR_TRANS_INHIBIT_MASK); + } else { + _wr_mask(xdesc, XSP_CR_OFFSET, XSP_CR_TRANS_INHIBIT_MASK, + XSP_CR_TRANS_INHIBIT_MASK); + _write(xdesc, XSP_SSR_OFFSET, xdesc->disable_slaves_mask); + } + xdesc->cs_asserted = start; +} + + +/* SPI polled transfer */ +static int32_t xil_spi_write_and_read_pl(struct spi_desc *desc, + uint8_t *data, + uint16_t bytes_number) +{ + struct xspi_desc *xdesc; + uint32_t rx; + uint32_t tx; + + if (!desc || !desc->extra) + return -EINVAL; + + xdesc = desc->extra; + _update_mode(desc); + + _xil_spi_start_transfer(desc, 1); + rx = 0; + tx = 0; + while (rx < bytes_number) { + if (tx < bytes_number) + tx += _wr_fifo(xdesc, data + tx, bytes_number - tx); + rx += _rd_fifo(xdesc, data + rx, bytes_number - rx); + } + _xil_spi_start_transfer(desc, 0); + + return 0; +} + +/* Get type of message. It can be READ, WRITE or both */ +static uint32_t _get_msg_type(struct spi_msg *msg) +{ + uint32_t type; + + type = 0; + if (msg->tx_buff) + type |= XPS_TYPE_WRITE; + if (msg->rx_buff) + type |= XPS_TYPE_READ; + + return type; +} + +/* Get information based on current iterator position */ +static void _get_data(struct msg_iter *it, uint8_t **buff, uint32_t *len, + uint32_t *type) +{ + struct spi_msg *msg; + + if (it->i == it->len) { + *len = 0; + return ; + } + + msg = it->msgs + it->i; + if (it->is_wr) + *buff = msg->tx_buff; + else + *buff = msg->rx_buff; + + if (*buff) + *buff += it->buff_i; + + *len = msg->bytes_number - it->buff_i; + if (type) + *type = _get_msg_type(msg); +} + +/* + * Increment iterator position with desired length. + * If the new position has a different message type, new_type will be set to 1 + * If a CS change is needed, cs_change is set + */ +static void _increment_iter(struct msg_iter *it, uint32_t len, + uint8_t *cs_change, uint8_t *new_type) +{ + struct spi_msg *msg; + uint32_t type; + uint32_t extra_len; + + if (!len) + return ; + + *cs_change = 0; + *new_type = 0; + msg = it->msgs + it->i; + if (it->buff_i + len > msg->bytes_number) + extra_len = it->buff_i + len - msg->bytes_number; + else + extra_len = 0; + len -= extra_len; + it->buff_i += len; + if (it->buff_i == msg->bytes_number) { + type = _get_msg_type(msg); + ++it->i; + it->buff_i = 0; + if (cs_change && msg->cs_change) + *cs_change = 1; + if (new_type) + if (it->i == it->len || + type != _get_msg_type(it->msgs + it->i)) + *new_type = 1; + } + if (extra_len) + _increment_iter(it, extra_len, cs_change, new_type); +} + +/* + * SPI polling transfer of multiple messages. + */ +static int32_t xil_spi_transfer_pl(struct spi_desc *desc, + struct spi_msg *msgs, + uint32_t len) + +{ + struct xspi_desc *xdesc; + uint8_t cs_change; + uint8_t new_type; + struct msg_iter rx = {.msgs = msgs, .len = len, .is_wr = 0}; + struct msg_iter tx = {.msgs = msgs, .len = len, .is_wr = 1}; + uint8_t *buff; + uint32_t bytes; + uint32_t type; + /* Bytes to increment rx when only a write transaction is executed */ + uint32_t to_inc_rx; + /* Difference between rx and tx iterators in bytes */ + uint32_t tx_advance; + /* Save bytes sent during a write loop */ + uint32_t tx_sent; + /* Used to loop if need to empty the RX FIFO */ + uint32_t loop; + + if (!desc || !desc->extra) + return -EINVAL; + + xdesc = desc->extra; + _update_mode(desc); + + tx_advance = 0; + to_inc_rx = 0; + buff = NULL; + type = 0; + do { + _xil_spi_start_transfer(desc, 1); + + tx_sent = 0; + /* Writing loop */ + do { + _get_data(&tx, &buff, &bytes, &type); + if (type & XPS_TYPE_READ) + /* + * Do not write more bytes than fifo_depth if + * they haven't been read. + */ + bytes = min(bytes, + xdesc->fifo_depth - tx_advance); + bytes = _wr_fifo(xdesc, buff, bytes); + tx_sent += bytes; + _increment_iter(&tx, bytes, &cs_change, &new_type); + tx_advance += bytes; + } while (!cs_change && !new_type && bytes); + + if (type & XPS_TYPE_READ) { + loop = 1; + /* + * Loop only when no more data to write otherwise read + * only available data. + */ + while (loop) { + if (!new_type) + loop = 0; + _get_data(&rx, &buff, &bytes, NULL); + bytes = _rd_fifo(xdesc, buff, bytes); + _increment_iter(&rx, bytes, &cs_change, &new_type); + tx_advance -= bytes; + if (loop && new_type) + break; + } + } else { + /* Clean RX FIFO if it was only a WRITE message */ + to_inc_rx += tx_sent; + if (new_type) { + /* Wait for TX to be sent */ + while (!(_read(xdesc, XSP_SR_OFFSET) & + XSP_SR_TX_EMPTY_MASK)) + ; + /* Reset fifo. Only write was performed */ + _wr_mask(xdesc, XSP_CR_OFFSET, + XSP_CR_RXFIFO_RESET_MASK, + XSP_CR_RXFIFO_RESET_MASK); + + _increment_iter(&rx, to_inc_rx, NULL, NULL); + tx_advance -= to_inc_rx; + to_inc_rx = 0; + } + } + + if ((cs_change && rx.i != len) || (!cs_change && rx.i == len)) + _xil_spi_start_transfer(desc, 0); + } while (rx.i < len); + + return 0; +} + +const struct spi_platform_ops xil_spi_pl_ops = { + .init = xil_spi_init_pl, + .remove = xil_spi_remove_pl, + .write_and_read = xil_spi_write_and_read_pl, + .transfer = xil_spi_transfer_pl +}; + +#endif // XPAR_XSPI_NUM_INSTANCES diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_timer.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_timer.c new file mode 100644 index 0000000..af83aa2 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_timer.c @@ -0,0 +1,456 @@ +/***************************************************************************//** +* @file xilinx/xilinx_timer.c +* @brief Timer and GIC control module source. +* @author Andrei Drimbarean (andrei.drimbarean@analog.com) +******************************************************************************** +* Copyright 2020(c) Analog Devices, Inc. +* +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* - Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* - 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. +* - Neither the name of Analog Devices, Inc. nor the names of its +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* - The use of this software may or may not infringe the patent rights +* of one or more patent holders. This license does not release you +* from the requirement that you obtain separate licenses from these +* patent holders to use this software. +* - Use of the software either in source or binary form, must be run +* on or directly connected to an Analog Devices Inc. component. +* +* THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR IMPLIED +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, MERCHANTABILITY +* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ + +#include +#include +#include "timer.h" +#include "xilinx_timer.h" +#include "error.h" + +/******************************************************************************/ +/********************** Macros and Constants Definitions **********************/ +/******************************************************************************/ + +#if defined(_XPARAMETERS_PS_H_) +#define CORE_PRIVATE_TIMER_CLOCK (XPAR_CPU_CORTEXA9_CORE_CLOCK_FREQ_HZ / 2) +#endif + +/******************************************************************************/ +/************************ Functions Definitions *******************************/ +/******************************************************************************/ + +/** + * @brief Set the current counter value of the timer. + * @param [in] desc - Pointer to the device handler. + * @param [in] new_val - New value for the timer counter. + * @return 0 in case of success, -1 otherwise + */ +int32_t xilinx_timer_counter_set(struct timer_desc *desc, + uint32_t new_val) +{ + struct xil_timer_desc *xdesc; + + if (!desc) + return -1; + + xdesc = desc->extra; + + switch (xdesc->type) { + case TIMER_PS: +#ifdef XSCUTIMER_H + XScuTimer_LoadTimer((XScuTimer *)xdesc->instance, new_val - 1); + break; +#endif + return -1; + case TIMER_PL: +#ifdef XTMRCTR_H + XTmrCtr_SetResetValue(xdesc->instance, xdesc->active_tmr, new_val); + XTmrCtr_Reset(xdesc->instance, xdesc->active_tmr); + break; +#endif + return -1; + default: + return -1; + } + + return 0; +} + +/** + * @brief Set the timer clock frequency. + * @param [in] desc - Pointer to the device handler. + * @param [in] freq_hz - New timer frequency in hertz. + * @return 0 in case of success, -1 otherwise + */ +int32_t xilinx_timer_count_clk_set(struct timer_desc *desc, + uint32_t freq_hz) +{ + struct xil_timer_desc *xdesc; + + if (!desc || !freq_hz) + return -1; + + xdesc = desc->extra; + + switch (xdesc->type) { + case TIMER_PS: +#ifdef XSCUTIMER_H + ; + uint32_t prescaler = CORE_PRIVATE_TIMER_CLOCK / freq_hz; + if (prescaler == 0 || prescaler >= 256) + return -1; + XScuTimer_SetPrescaler(xdesc->instance, prescaler - 1); + desc->freq_hz = CORE_PRIVATE_TIMER_CLOCK / prescaler; + break; +#endif + return -1; + case TIMER_PL: +#ifdef XTMRCTR_H + (void *)xdesc; + /** Cannot be done. */ +#endif + return -1; + default: + return -1; + } + + return 0; +} + +/** + * @brief Initialize hardware timer and the handler structure associated with + * it. + * @param [out] desc - Pointer to the reference of the device handler. + * @param [in] param - Initialization structure. + * @return 0 in case of success, -1 otherwise + */ +int32_t xilinx_timer_init(struct timer_desc **desc, + const struct timer_init_param *param) +{ + int32_t ret; + struct timer_desc *dev; + struct xil_timer_desc *xdesc; + struct xil_timer_init_param *xinit; + + if (!desc || !param) + return -1; + + xinit = param->extra; + + dev = (struct timer_desc *)calloc(1, sizeof(*dev)); + if(!dev) + return -1; + xdesc = (struct xil_timer_desc *)calloc(1, sizeof(*xdesc)); + if(!xdesc) + goto error_desc; + + dev->id = param->id; + dev->freq_hz = param->freq_hz; + dev->ticks_count = param->ticks_count; + dev->extra = xdesc; + xdesc->active_tmr = xinit->active_tmr; + xdesc->type = xinit->type; + + switch (xdesc->type) { + case TIMER_PS: +#ifdef XSCUTIMER_H + xdesc->config = XScuTimer_LookupConfig(dev->id); + xdesc->instance = calloc(1, sizeof(XScuTimer)); + if (!xdesc->instance) + goto error_xdesc; + ret = XScuTimer_CfgInitialize(xdesc->instance, xdesc->config, + ((XScuTimer_Config *)xdesc->config)->BaseAddr); + if (ret != XST_SUCCESS) { + free(xdesc->instance); + goto error_xdesc; + } + ret = xilinx_timer_count_clk_set(dev, param->freq_hz); + if (IS_ERR_VALUE(ret)) + goto error_xdesc; + + ret = xilinx_timer_counter_set(dev, dev->ticks_count); + if (IS_ERR_VALUE(ret)) + goto error_xdesc; + + XScuTimer_EnableAutoReload((XScuTimer *)xdesc->instance); + XScuTimer_EnableInterrupt((XScuTimer *)xdesc->instance); + break; +#endif + goto error_xdesc; + case TIMER_PL: +#ifdef XTMRCTR_H + xdesc->instance = (XTmrCtr *)calloc(1, sizeof(XTmrCtr)); + if (!xdesc->instance) + return -1; + + xdesc->config = XTmrCtr_LookupConfig(dev->id); + if (!xdesc->config) { + free(xdesc->instance); + goto error_desc; + } + XTmrCtr_CfgInitialize(xdesc->instance, xdesc->config, + ((XTmrCtr_Config *)xdesc->config)->BaseAddress); + ret = XTmrCtr_InitHw(xdesc->instance); + if (ret != 0) { + free(xdesc->instance); + goto error_desc; + } + dev->freq_hz = ((XTmrCtr_Config *)xdesc->config)->SysClockFreqHz; + + ret = XTmrCtr_SelfTest(xdesc->instance, dev->id); + if(ret != 0) + goto error_desc; + XTmrCtr_SetOptions(xdesc->instance, xdesc->active_tmr, + XTC_DOWN_COUNT_OPTION | XTC_INT_MODE_OPTION | + XTC_AUTO_RELOAD_OPTION); + XTmrCtr_SetResetValue(xdesc->instance, xdesc->active_tmr, + dev->ticks_count); + break; +#endif + goto error_xdesc; + default: + goto error_xdesc; + } + + *desc = dev; + + return 0; + +error_xdesc: + free(xdesc); +error_desc: + free(dev); + + return -1; +} + +/** + * @brief Free the memory allocated by timer_setup(). + * @param [in] desc - Pointer to the device handler. + * @return 0 in case of success, -1 otherwise + */ +int32_t xilinx_timer_remove(struct timer_desc *desc) +{ + struct xil_timer_desc *xdesc; + + if(!desc) + return -1; + + xdesc = desc->extra; + + switch (xdesc->type) { + case TIMER_PS: +#ifdef XSCUTIMER_H + XScuTimer_Stop(xdesc->instance); + free(xdesc->instance); + break; +#endif + return -1; + case TIMER_PL: +#ifdef XTMRCTR_H + XTmrCtr_Stop(xdesc->instance, xdesc->active_tmr); + break; +#endif + return -1; + default: + return -1; + } + + free(xdesc); + free(desc); + + return 0; +} + +/** + * @brief Start a timer. + * @param [in] desc - Pointer to the device handler. + * @return 0 in case of success, -1 otherwise + */ +int32_t xilinx_timer_start(struct timer_desc *desc) +{ + struct xil_timer_desc *xdesc; + + if (!desc) + return -1; + + xdesc = desc->extra; + + switch (xdesc->type) { + case TIMER_PS: +#ifdef XSCUTIMER_H + XScuTimer_Start(xdesc->instance); + break; +#endif + return -1; + case TIMER_PL: +#ifdef XTMRCTR_H + XTmrCtr_Start(xdesc->instance, xdesc->active_tmr); + break; +#endif + return -1; + } + + return 0; +} + +/** + * @brief Stop a timer from counting. + * @param [in] desc - Pointer to the device handler. + * @return 0 in case of success, -1 otherwise + */ +int32_t xilinx_timer_stop(struct timer_desc *desc) +{ + struct xil_timer_desc *xdesc; + + if (!desc) + return -1; + + xdesc = desc->extra; + + switch (xdesc->type) { + case TIMER_PS: +#ifdef XSCUTIMER_H + XScuTimer_Stop(xdesc->instance); + break; +#endif + return -1; + case TIMER_PL: +#ifdef XTMRCTR_H + XTmrCtr_Stop(xdesc->instance, xdesc->active_tmr); + break; +#endif + return -1; + default: + return -1; + } + + return 0; +} + +/** + * @brief Get the current counter value of the timer. + * @param [in] desc - Pointer to the device handler. + * @param [out] counter - Pointer to the current counter value. + * @return 0 in case of success, -1 otherwise + */ +int32_t xilinx_timer_counter_get(struct timer_desc *desc, + uint32_t *counter) +{ + struct xil_timer_desc *xdesc; + + if (!desc || !counter) + return -1; + + xdesc = desc->extra; + + switch (xdesc->type) { + case TIMER_PS: +#ifdef XSCUTIMER_H + *counter = XScuTimer_GetCounterValue((XScuTimer *)xdesc->instance); + ++(*counter); + break; +#endif + return -1; + case TIMER_PL: +#ifdef XTMRCTR_H + *counter = XTmrCtr_GetValue(xdesc->instance, xdesc->active_tmr); + break; +#endif + return -1; + default: + *counter = 0; + + return -1; + } + + return 0; +} + + +/** + * @brief Get the timer clock frequency. + * @param [in] desc - Pointer to the device handler. + * @param [out] freq_hz - Pointer to the returned frequency. + * @return 0 in case of success, -1 otherwise + */ +int32_t xilinx_timer_count_clk_get(struct timer_desc *desc, + uint32_t *freq_hz) +{ + struct xil_timer_desc *xdesc; + + if (!desc || !freq_hz) + return -1; + + xdesc = desc->extra; + + switch (xdesc->type) { + case TIMER_PS: +#ifdef XSCUTIMER_H + *freq_hz = desc->freq_hz; + break; +#endif + return -1; + case TIMER_PL: +#ifdef XTMRCTR_H + *freq_hz = ((XTmrCtr_Config *)xdesc->config)->SysClockFreqHz; + break; +#endif + return -1; + default: + *freq_hz = 0; + + return -1; + } + + return 0; +} + + +/** + * @brief Get the elapsed time in nsec for the timer. + * @param [in] desc - Pointer to the device handler. + * @param [in] elapsed_time - The elapsed time in nsec. + * @return -ENOSYS. + */ +int32_t xilinx_timer_get_elapsed_time_nsec(struct timer_desc *desc, + uint64_t *elapsed_time) +{ + /* Function not implemented */ + return -ENOSYS; +} + +/** + * @brief Xilinx platform specific timer platform ops structure + */ +const struct timer_platform_ops xil_timer_ops = { + .init = &xilinx_timer_init, + .start = &xilinx_timer_start, + .stop = &xilinx_timer_stop, + .counter_get = &xilinx_timer_counter_get, + .counter_set = &xilinx_timer_counter_set, + .count_clk_get = &xilinx_timer_count_clk_get, + .count_clk_set = &xilinx_timer_count_clk_set, + .get_elapsed_time_nsec = &xilinx_timer_get_elapsed_time_nsec, + .remove = &xilinx_timer_remove +}; diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_timer.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_timer.h new file mode 100644 index 0000000..17b0d5a --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/platform/xilinx_timer.h @@ -0,0 +1,101 @@ +/***************************************************************************//** +* @file xilinx_timer.h +* @brief Timer control module header. +* @author Andrei Drimbarean (andrei.drimbarean@analog.com) +******************************************************************************** +* Copyright 2020(c) Analog Devices, Inc. +* +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* - Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* - 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. +* - Neither the name of Analog Devices, Inc. nor the names of its +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* - The use of this software may or may not infringe the patent rights +* of one or more patent holders. This license does not release you +* from the requirement that you obtain separate licenses from these +* patent holders to use this software. +* - Use of the software either in source or binary form, must be run +* on or directly connected to an Analog Devices Inc. component. +* +* THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR IMPLIED +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, MERCHANTABILITY +* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +#ifndef XILINX_TIMER_H_ +#define XILINX_TIMER_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ + +#include +#ifdef XPAR_XSCUTIMER_NUM_INSTANCES +#include +#endif +#ifdef XPAR_XTMRCTR_NUM_INSTANCES +#include +#endif + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ + +/** + * @enum xil_timer_type + * @brief Xilinx platform architecture sections + */ +enum xil_timer_type { + /** Programmable Logic */ + TIMER_PL, + /** Processing System */ + TIMER_PS +}; + +/** + * @struct xil_timer_desc + * @brief Xilinx specific timer descriptor + */ +struct xil_timer_desc { + /** Pointer to the BSP driver handler */ + void *instance; + /** Pointer to the BSP driver configuration handler */ + void *config; + /** Timer used from the PL 3-timer core */ + uint8_t active_tmr; + /** Platform selection parameter */ + enum xil_timer_type type; +}; + +/** + * @struct xil_timer_init_param + * @brief Xilinx specific timer initialization structure + */ +struct xil_timer_init_param { + /** Timer used from the PL 3-timer core */ + uint8_t active_tmr; + /** Platform selection parameter */ + enum xil_timer_type type; +}; + +/** + * @brief Xilinx platform specific timer platform ops structure + */ +extern const struct timer_platform_ops xil_timer_ops; + +#endif /* XILINX_TIMER_H_ */ diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361/ad9361.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361/ad9361.c new file mode 100644 index 0000000..7a47535 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361/ad9361.c @@ -0,0 +1,7475 @@ +/***************************************************************************//** + * @file ad9361.c + * @brief Implementation of AD9361 Driver. +******************************************************************************** + * Copyright 2014-2015(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include "ad9361.h" +#include "ad9361_util.h" +#include "spi.h" +#include "gpio.h" +#include "delay.h" +#include "util.h" +#include "../../../config/app_config.h" + +#define diff_abs(x, y) ((x) > (y) ? (x - y) : (y - x)) + +#define NO_GAIN_TABLE ((uint32_t)-1) + +/* Used for static code size optimization: please see app_config.h */ +const bool has_split_gt = HAVE_SPLIT_GAIN_TABLE; +const bool have_tdd_tables = HAVE_TDD_SYNTH_TABLE; + +#define SYNTH_LUT_SIZE 53 + +static const struct SynthLUT SynthLUT_FDD[LUT_FTDD_ENT][SYNTH_LUT_SIZE] = { + { + {12605, 13, 1, 4, 2, 15, 12, 7, 14, 6, 14, 5, 15}, /* 40 MHz */ + {12245, 13, 1, 4, 2, 15, 12, 7, 14, 6, 14, 5, 15}, + {11906, 13, 1, 4, 2, 15, 12, 7, 15, 6, 14, 5, 15}, + {11588, 13, 1, 4, 2, 15, 12, 8, 15, 6, 14, 5, 15}, + {11288, 13, 1, 4, 2, 15, 12, 8, 15, 6, 14, 5, 15}, + {11007, 13, 1, 4, 2, 15, 12, 9, 15, 6, 14, 5, 15}, + {10742, 13, 1, 4, 2, 15, 12, 9, 15, 6, 14, 5, 15}, + {10492, 13, 1, 6, 2, 15, 12, 10, 15, 6, 14, 5, 15}, + {10258, 13, 1, 6, 2, 15, 12, 10, 15, 6, 14, 5, 15}, + {10036, 13, 1, 6, 2, 15, 12, 11, 15, 6, 14, 5, 15}, + {9827, 13, 1, 6, 2, 14, 12, 11, 15, 6, 14, 5, 15}, + {9631, 13, 1, 6, 2, 13, 12, 12, 15, 6, 14, 5, 15}, + {9445, 13, 1, 6, 2, 12, 12, 12, 15, 6, 14, 5, 15}, + {9269, 13, 1, 6, 2, 12, 12, 13, 15, 6, 14, 5, 15}, + {9103, 13, 1, 6, 2, 12, 12, 13, 15, 6, 14, 5, 15}, + {8946, 13, 1, 6, 2, 12, 12, 14, 15, 6, 14, 5, 15}, + {8797, 12, 1, 7, 2, 12, 12, 13, 15, 6, 14, 5, 15}, + {8655, 12, 1, 7, 2, 12, 12, 14, 15, 6, 14, 5, 15}, + {8520, 12, 1, 7, 2, 12, 12, 14, 15, 6, 14, 5, 15}, + {8392, 12, 1, 7, 2, 12, 12, 15, 15, 6, 14, 5, 15}, + {8269, 12, 1, 7, 2, 12, 12, 15, 15, 6, 14, 5, 15}, + {8153, 12, 1, 7, 2, 12, 12, 16, 15, 6, 14, 5, 15}, + {8041, 12, 1, 7, 2, 13, 12, 16, 15, 6, 14, 5, 15}, + {7934, 11, 1, 7, 2, 12, 12, 16, 15, 6, 14, 5, 15}, + {7831, 11, 1, 7, 2, 12, 12, 16, 15, 6, 14, 5, 15}, + {7733, 10, 1, 7, 3, 13, 12, 16, 15, 6, 14, 5, 15}, + {7638, 10, 1, 7, 2, 12, 12, 16, 15, 6, 14, 5, 15}, + {7547, 10, 1, 7, 2, 12, 12, 17, 15, 6, 14, 5, 15}, + {7459, 10, 1, 7, 2, 12, 12, 17, 15, 6, 14, 5, 15}, + {7374, 10, 2, 7, 3, 14, 13, 14, 15, 6, 14, 5, 15}, + {7291, 10, 2, 7, 3, 14, 13, 14, 15, 6, 14, 5, 15}, + {7212, 10, 2, 7, 3, 14, 13, 14, 15, 6, 14, 5, 15}, + {7135, 10, 2, 7, 3, 14, 13, 15, 15, 7, 14, 5, 15}, + {7061, 10, 2, 7, 3, 14, 13, 15, 15, 6, 14, 5, 15}, + {6988, 10, 1, 7, 3, 12, 14, 20, 15, 6, 14, 5, 15}, + {6918, 9, 2, 7, 3, 14, 13, 15, 15, 6, 14, 5, 15}, + {6850, 9, 2, 7, 3, 14, 13, 15, 15, 6, 14, 5, 15}, + {6784, 9, 2, 7, 2, 13, 13, 15, 15, 6, 14, 5, 15}, + {6720, 9, 2, 7, 2, 13, 13, 16, 15, 6, 14, 5, 15}, + {6658, 8, 2, 7, 3, 14, 13, 15, 15, 6, 14, 5, 15}, + {6597, 8, 2, 7, 2, 13, 13, 15, 15, 6, 14, 5, 15}, + {6539, 8, 2, 7, 2, 13, 13, 15, 15, 6, 14, 5, 15}, + {6482, 8, 2, 7, 2, 13, 13, 16, 15, 6, 14, 5, 15}, + {6427, 7, 2, 7, 3, 14, 13, 15, 15, 6, 14, 5, 15}, + {6373, 7, 2, 7, 3, 15, 13, 15, 15, 6, 14, 5, 15}, + {6321, 7, 2, 7, 3, 15, 13, 15, 15, 6, 14, 5, 15}, + {6270, 7, 2, 7, 3, 15, 13, 16, 15, 6, 14, 5, 15}, + {6222, 7, 2, 7, 3, 15, 13, 16, 15, 6, 14, 5, 15}, + {6174, 6, 2, 7, 3, 15, 13, 15, 15, 6, 14, 5, 15}, + {6128, 6, 2, 7, 3, 15, 13, 15, 15, 6, 14, 5, 15}, + {6083, 6, 2, 7, 3, 15, 13, 16, 15, 6, 14, 5, 15}, + {6040, 6, 2, 7, 3, 15, 13, 16, 15, 6, 14, 5, 15}, + {5997, 6, 2, 7, 3, 15, 13, 16, 15, 6, 14, 5, 15}, + }, { + {12605, 13, 1, 4, 2, 15, 12, 13, 15, 12, 12, 5, 14}, /* 60 MHz */ + {12245, 13, 1, 4, 2, 15, 12, 13, 15, 12, 12, 5, 14}, + {11906, 13, 1, 4, 2, 15, 12, 13, 15, 13, 12, 5, 13}, + {11588, 13, 1, 4, 2, 15, 12, 14, 15, 13, 12, 5, 13}, + {11288, 13, 1, 5, 2, 15, 12, 15, 15, 13, 12, 5, 13}, + {11007, 13, 1, 5, 2, 15, 12, 16, 15, 13, 12, 5, 13}, + {10742, 13, 1, 5, 2, 15, 12, 16, 15, 12, 12, 5, 14}, + {10492, 13, 1, 6, 2, 15, 12, 17, 15, 12, 12, 5, 14}, + {10258, 13, 1, 6, 2, 15, 12, 18, 15, 13, 12, 5, 13}, + {10036, 13, 1, 6, 2, 15, 12, 19, 15, 13, 12, 5, 13}, + {9827, 13, 1, 6, 2, 14, 12, 20, 15, 13, 12, 5, 13}, + {9631, 13, 1, 6, 2, 13, 12, 21, 15, 13, 12, 5, 13}, + {9445, 13, 1, 6, 2, 12, 12, 22, 15, 13, 12, 5, 13}, + {9269, 13, 1, 6, 2, 12, 12, 22, 15, 12, 12, 5, 14}, + {9103, 13, 1, 6, 2, 12, 12, 23, 15, 13, 12, 5, 13}, + {8946, 13, 1, 6, 2, 12, 12, 24, 15, 13, 12, 5, 13}, + {8797, 12, 1, 7, 2, 12, 12, 24, 15, 13, 12, 5, 13}, + {8655, 12, 1, 7, 2, 12, 12, 25, 15, 13, 12, 5, 13}, + {8520, 12, 1, 7, 2, 12, 12, 25, 15, 13, 12, 5, 13}, + {8392, 12, 1, 7, 2, 12, 12, 26, 15, 13, 12, 5, 13}, + {8269, 12, 1, 7, 2, 12, 12, 27, 15, 13, 12, 5, 13}, + {8153, 12, 1, 7, 2, 12, 12, 28, 15, 13, 12, 5, 13}, + {8041, 12, 1, 7, 2, 13, 12, 29, 15, 13, 12, 5, 13}, + {7934, 11, 1, 7, 2, 12, 12, 28, 15, 13, 12, 5, 13}, + {7831, 11, 1, 7, 2, 12, 12, 29, 15, 13, 12, 5, 13}, + {7733, 10, 1, 7, 3, 13, 12, 28, 15, 13, 12, 5, 13}, + {7638, 10, 1, 7, 2, 12, 12, 29, 15, 13, 12, 5, 13}, + {7547, 10, 1, 7, 2, 12, 12, 29, 15, 13, 12, 5, 13}, + {7459, 10, 1, 7, 2, 12, 12, 30, 15, 13, 12, 5, 13}, + {7374, 10, 2, 7, 3, 14, 13, 24, 15, 13, 12, 5, 13}, + {7291, 10, 2, 7, 3, 14, 13, 25, 15, 13, 12, 5, 13}, + {7212, 10, 2, 7, 3, 14, 13, 25, 15, 13, 12, 5, 13}, + {7135, 10, 2, 7, 3, 14, 13, 26, 15, 13, 12, 5, 13}, + {7061, 10, 2, 7, 3, 14, 13, 26, 15, 13, 12, 5, 13}, + {6988, 10, 1, 7, 3, 12, 14, 35, 15, 13, 12, 5, 13}, + {6918, 9, 1, 7, 3, 12, 14, 33, 15, 13, 12, 5, 13}, + {6850, 9, 1, 7, 3, 12, 14, 34, 15, 13, 12, 5, 13}, + {6784, 9, 1, 7, 2, 11, 14, 35, 15, 13, 12, 5, 13}, + {6720, 9, 1, 7, 2, 11, 14, 35, 15, 13, 12, 5, 13}, + {6658, 8, 2, 7, 3, 15, 13, 26, 15, 13, 12, 5, 13}, + {6597, 8, 2, 7, 2, 15, 13, 27, 15, 13, 12, 5, 13}, + {6539, 8, 2, 7, 2, 15, 13, 27, 15, 13, 12, 5, 13}, + {6482, 8, 2, 7, 2, 15, 13, 28, 15, 13, 12, 5, 13}, + {6427, 7, 2, 7, 3, 14, 13, 27, 15, 13, 12, 5, 13}, + {6373, 7, 2, 7, 3, 15, 13, 27, 15, 13, 12, 5, 13}, + {6321, 7, 2, 7, 3, 15, 13, 27, 15, 13, 12, 5, 13}, + {6270, 7, 2, 7, 3, 15, 13, 28, 15, 13, 12, 5, 13}, + {6222, 7, 2, 7, 3, 15, 13, 28, 15, 13, 12, 5, 13}, + {6174, 6, 2, 7, 3, 15, 13, 27, 15, 13, 12, 5, 13}, + {6128, 6, 2, 7, 3, 15, 13, 27, 15, 13, 12, 5, 13}, + {6083, 6, 2, 7, 3, 15, 13, 28, 15, 13, 12, 5, 13}, + {6040, 6, 2, 7, 3, 15, 13, 28, 15, 13, 12, 5, 13}, + {5997, 6, 2, 7, 3, 15, 13, 29, 15, 13, 12, 5, 13}, + }, { + {12605, 13, 1, 4, 2, 15, 12, 7, 15, 6, 13, 5, 14}, /* 80 MHz */ + {12245, 13, 1, 4, 2, 15, 12, 7, 15, 6, 13, 5, 14}, + {11906, 13, 1, 4, 2, 15, 12, 7, 15, 6, 13, 5, 14}, + {11588, 13, 1, 4, 2, 15, 12, 7, 14, 6, 14, 4, 14}, + {11288, 13, 1, 4, 2, 15, 12, 8, 15, 6, 13, 5, 14}, + {11007, 13, 1, 4, 2, 15, 12, 8, 14, 6, 13, 5, 14}, + {10742, 13, 1, 4, 2, 15, 12, 9, 15, 6, 13, 5, 14}, + {10492, 13, 1, 6, 2, 15, 12, 9, 14, 6, 13, 5, 14}, + {10258, 13, 1, 6, 2, 15, 12, 10, 15, 6, 13, 5, 14}, + {10036, 13, 1, 6, 2, 15, 12, 10, 15, 6, 13, 5, 14}, + {9827, 13, 1, 6, 2, 14, 12, 11, 15, 6, 13, 5, 14}, + {9631, 13, 1, 6, 2, 13, 12, 11, 15, 6, 13, 5, 14}, + {9445, 13, 1, 6, 2, 12, 12, 12, 15, 6, 13, 5, 14}, + {9269, 13, 1, 6, 2, 12, 12, 12, 15, 6, 13, 5, 14}, + {9103, 13, 1, 6, 2, 12, 12, 13, 15, 6, 13, 5, 14}, + {8946, 13, 1, 6, 2, 12, 12, 13, 15, 6, 13, 5, 14}, + {8797, 12, 1, 7, 2, 12, 12, 13, 15, 6, 13, 5, 14}, + {8655, 12, 1, 7, 2, 12, 12, 14, 15, 6, 13, 5, 14}, + {8520, 12, 1, 7, 2, 12, 12, 14, 15, 6, 13, 5, 14}, + {8392, 12, 1, 7, 2, 12, 12, 15, 15, 7, 13, 5, 14}, + {8269, 12, 1, 7, 2, 12, 12, 15, 15, 6, 13, 5, 14}, + {8153, 12, 1, 7, 2, 12, 12, 15, 15, 6, 13, 5, 14}, + {8041, 12, 1, 7, 2, 13, 12, 16, 15, 6, 13, 5, 14}, + {7934, 11, 1, 7, 2, 12, 12, 15, 15, 6, 13, 5, 14}, + {7831, 11, 1, 7, 2, 12, 12, 16, 15, 6, 13, 5, 14}, + {7733, 10, 1, 7, 3, 13, 12, 15, 15, 6, 13, 5, 14}, + {7638, 10, 1, 7, 2, 12, 12, 16, 15, 6, 13, 5, 14}, + {7547, 10, 1, 7, 2, 12, 12, 16, 15, 6, 13, 5, 14}, + {7459, 10, 1, 7, 2, 12, 12, 17, 15, 6, 13, 5, 14}, + {7374, 10, 2, 7, 3, 14, 13, 13, 15, 6, 13, 5, 14}, + {7291, 10, 2, 7, 3, 14, 13, 14, 15, 6, 13, 5, 14}, + {7212, 10, 2, 7, 3, 14, 13, 14, 15, 6, 13, 5, 14}, + {7135, 10, 2, 7, 3, 14, 13, 14, 15, 6, 13, 5, 14}, + {7061, 10, 2, 7, 3, 14, 13, 15, 15, 6, 13, 5, 14}, + {6988, 10, 1, 7, 3, 12, 14, 19, 15, 6, 13, 5, 14}, + {6918, 9, 2, 7, 3, 14, 13, 14, 15, 6, 13, 5, 14}, + {6850, 9, 2, 7, 3, 14, 13, 15, 15, 6, 13, 5, 14}, + {6784, 9, 2, 7, 2, 13, 13, 15, 15, 6, 13, 5, 14}, + {6720, 9, 2, 7, 2, 13, 13, 15, 15, 6, 13, 5, 14}, + {6658, 8, 2, 7, 3, 14, 13, 15, 15, 6, 13, 5, 14}, + {6597, 8, 2, 7, 2, 13, 13, 15, 15, 6, 13, 5, 14}, + {6539, 8, 2, 7, 2, 13, 13, 15, 15, 6, 13, 5, 14}, + {6482, 8, 2, 7, 2, 13, 13, 15, 15, 6, 13, 5, 14}, + {6427, 7, 2, 7, 3, 14, 13, 15, 15, 6, 13, 5, 14}, + {6373, 7, 2, 7, 3, 15, 13, 15, 15, 6, 13, 5, 14}, + {6321, 7, 2, 7, 3, 15, 13, 15, 15, 6, 13, 5, 14}, + {6270, 7, 2, 7, 3, 15, 13, 15, 15, 6, 13, 5, 14}, + {6222, 7, 2, 7, 3, 15, 13, 16, 15, 6, 13, 5, 14}, + {6174, 6, 2, 7, 3, 15, 13, 15, 15, 6, 13, 5, 14}, + {6128, 6, 2, 7, 3, 15, 13, 15, 15, 6, 13, 5, 14}, + {6083, 6, 2, 7, 3, 15, 13, 15, 15, 6, 13, 5, 14}, + {6040, 6, 2, 7, 3, 15, 13, 16, 15, 6, 13, 5, 14}, + {5997, 6, 2, 7, 3, 15, 13, 16, 15, 6, 13, 5, 14}, + } +}; + +static const struct SynthLUT SynthLUT_TDD[LUT_FTDD_ENT][SYNTH_LUT_SIZE] = { + { + {12605, 13, 1, 4, 2, 15, 12, 27, 12, 15, 12, 4, 13}, /* 40 MHz */ + {12245, 13, 1, 4, 2, 15, 12, 27, 12, 15, 12, 4, 13}, + {11906, 13, 1, 4, 2, 15, 12, 26, 11, 15, 12, 4, 13}, + {11588, 13, 1, 4, 2, 15, 12, 28, 12, 15, 12, 4, 13}, + {11288, 13, 1, 4, 2, 15, 12, 30, 12, 15, 12, 4, 13}, + {11007, 13, 1, 4, 2, 15, 12, 32, 12, 15, 12, 4, 13}, + {10742, 13, 1, 4, 2, 15, 12, 33, 12, 15, 12, 4, 13}, + {10492, 13, 1, 6, 2, 15, 12, 35, 12, 15, 12, 4, 13}, + {10258, 13, 1, 6, 2, 15, 12, 37, 12, 15, 12, 4, 13}, + {10036, 13, 1, 6, 2, 15, 12, 38, 12, 15, 12, 4, 13}, + {9827, 13, 1, 6, 2, 14, 12, 40, 12, 15, 12, 4, 13}, + {9631, 13, 1, 6, 2, 13, 12, 42, 12, 15, 12, 4, 13}, + {9445, 13, 1, 6, 2, 12, 12, 44, 12, 15, 12, 4, 13}, + {9269, 13, 1, 6, 2, 12, 12, 45, 12, 15, 12, 4, 13}, + {9103, 13, 1, 6, 2, 12, 12, 47, 12, 15, 12, 4, 13}, + {8946, 13, 1, 6, 2, 12, 12, 49, 12, 15, 12, 4, 13}, + {8797, 12, 1, 7, 2, 12, 12, 48, 12, 15, 12, 4, 13}, + {8655, 12, 1, 7, 2, 12, 12, 50, 12, 15, 12, 4, 13}, + {8520, 12, 1, 7, 2, 12, 12, 51, 12, 15, 12, 4, 13}, + {8392, 12, 1, 7, 2, 12, 12, 53, 12, 15, 12, 4, 13}, + {8269, 12, 1, 7, 2, 12, 12, 55, 12, 15, 12, 4, 13}, + {8153, 12, 1, 7, 2, 12, 12, 56, 12, 15, 12, 4, 13}, + {8041, 12, 1, 7, 2, 13, 12, 58, 12, 15, 12, 4, 13}, + {7934, 11, 1, 7, 2, 12, 12, 57, 12, 15, 12, 4, 13}, + {7831, 11, 1, 7, 2, 12, 12, 58, 12, 15, 12, 4, 13}, + {7733, 10, 1, 7, 3, 13, 12, 56, 12, 15, 12, 4, 13}, + {7638, 10, 1, 7, 2, 12, 12, 58, 12, 15, 12, 4, 13}, + {7547, 10, 1, 7, 2, 12, 12, 59, 12, 15, 12, 4, 13}, + {7459, 10, 1, 7, 2, 12, 12, 61, 12, 15, 12, 4, 13}, + {7374, 10, 2, 7, 3, 14, 13, 49, 12, 15, 12, 4, 13}, + {7291, 10, 2, 7, 3, 14, 13, 50, 12, 15, 12, 4, 13}, + {7212, 10, 2, 7, 3, 14, 13, 51, 12, 15, 12, 4, 13}, + {7135, 10, 2, 7, 3, 14, 13, 52, 12, 15, 12, 4, 13}, + {7061, 10, 2, 7, 3, 14, 13, 53, 12, 15, 12, 4, 13}, + {6988, 10, 1, 7, 3, 12, 14, 63, 11, 14, 12, 3, 13}, + {6918, 9, 2, 7, 3, 14, 13, 52, 12, 15, 12, 4, 13}, + {6850, 9, 2, 7, 3, 14, 13, 53, 12, 15, 12, 4, 13}, + {6784, 9, 2, 7, 2, 13, 13, 54, 12, 15, 12, 4, 13}, + {6720, 9, 2, 7, 2, 13, 13, 56, 12, 15, 12, 4, 13}, + {6658, 8, 2, 7, 3, 14, 13, 53, 12, 15, 12, 4, 13}, + {6597, 8, 2, 7, 2, 13, 13, 54, 12, 15, 12, 4, 13}, + {6539, 8, 2, 7, 2, 13, 13, 55, 12, 15, 12, 4, 13}, + {6482, 8, 2, 7, 2, 13, 13, 56, 12, 15, 12, 4, 13}, + {6427, 7, 2, 7, 3, 14, 13, 54, 12, 15, 12, 4, 13}, + {6373, 7, 2, 7, 3, 15, 13, 54, 12, 15, 12, 4, 13}, + {6321, 7, 2, 7, 3, 15, 13, 55, 12, 15, 12, 4, 13}, + {6270, 7, 2, 7, 3, 15, 13, 56, 12, 15, 12, 4, 13}, + {6222, 7, 2, 7, 3, 15, 13, 57, 12, 15, 12, 4, 13}, + {6174, 6, 2, 7, 3, 15, 13, 54, 12, 15, 12, 4, 13}, + {6128, 6, 2, 7, 3, 15, 13, 55, 12, 15, 12, 4, 13}, + {6083, 6, 2, 7, 3, 15, 13, 56, 12, 15, 12, 4, 13}, + {6040, 6, 2, 7, 3, 15, 13, 57, 12, 15, 12, 4, 13}, + {5997, 6, 2, 7, 3, 15, 13, 58, 12, 15, 12, 4, 13}, + }, { + {12605, 13, 1, 4, 2, 15, 12, 26, 11, 15, 11, 4, 13}, /* 60 MHz */ + {12245, 13, 1, 4, 2, 15, 12, 26, 11, 15, 11, 4, 13}, + {11906, 13, 1, 4, 2, 15, 12, 26, 12, 15, 11, 4, 12}, + {11588, 13, 1, 4, 2, 15, 12, 30, 12, 15, 11, 4, 12}, + {11288, 13, 1, 4, 2, 15, 12, 32, 12, 15, 10, 4, 12}, + {11007, 13, 1, 4, 2, 15, 12, 31, 12, 15, 11, 4, 12}, + {10742, 13, 1, 4, 2, 15, 12, 33, 12, 15, 10, 4, 12}, + {10492, 13, 1, 6, 2, 15, 12, 37, 12, 15, 10, 4, 12}, + {10258, 13, 1, 6, 2, 15, 12, 38, 12, 15, 11, 4, 13}, + {10036, 13, 1, 6, 2, 15, 12, 38, 12, 15, 10, 4, 12}, + {9827, 13, 1, 6, 2, 14, 12, 42, 12, 15, 11, 4, 12}, + {9631, 13, 1, 6, 2, 13, 12, 41, 12, 15, 11, 4, 12}, + {9445, 13, 1, 6, 2, 12, 12, 45, 12, 15, 11, 4, 12}, + {9269, 13, 1, 6, 2, 12, 12, 47, 12, 15, 11, 4, 12}, + {9103, 13, 1, 6, 2, 12, 12, 46, 12, 15, 11, 4, 12}, + {8946, 13, 1, 6, 2, 12, 12, 48, 12, 15, 10, 4, 12}, + {8797, 12, 1, 7, 2, 12, 12, 49, 12, 15, 11, 4, 13}, + {8655, 12, 1, 7, 2, 12, 12, 51, 12, 15, 11, 4, 12}, + {8520, 12, 1, 7, 2, 12, 12, 50, 12, 15, 11, 4, 12}, + {8392, 12, 1, 7, 2, 12, 12, 52, 12, 15, 10, 4, 12}, + {8269, 12, 1, 7, 2, 12, 12, 56, 12, 15, 10, 4, 12}, + {8153, 12, 1, 7, 2, 12, 12, 55, 12, 15, 11, 4, 12}, + {8041, 12, 1, 7, 2, 13, 12, 57, 12, 15, 10, 4, 12}, + {7934, 11, 1, 7, 2, 12, 12, 55, 12, 15, 11, 4, 12}, + {7831, 11, 1, 7, 2, 12, 12, 57, 12, 15, 10, 4, 12}, + {7733, 10, 1, 7, 3, 13, 12, 55, 12, 15, 11, 4, 12}, + {7638, 10, 1, 7, 2, 12, 12, 59, 12, 15, 10, 4, 12}, + {7547, 10, 1, 7, 2, 12, 12, 60, 12, 15, 11, 4, 12}, + {7459, 10, 1, 7, 2, 12, 12, 48, 12, 15, 11, 4, 12}, + {7374, 10, 2, 7, 3, 14, 13, 47, 12, 15, 11, 4, 13}, + {7291, 10, 2, 7, 3, 14, 13, 49, 12, 15, 10, 4, 12}, + {7212, 10, 2, 7, 3, 14, 13, 50, 12, 15, 10, 4, 12}, + {7135, 10, 2, 7, 3, 14, 13, 52, 12, 15, 11, 4, 13}, + {7061, 10, 2, 7, 3, 14, 13, 52, 12, 15, 11, 4, 12}, + {6988, 10, 1, 7, 3, 12, 14, 63, 11, 15, 11, 4, 13}, + {6918, 9, 1, 7, 3, 12, 14, 63, 11, 15, 11, 4, 13}, + {6850, 9, 1, 7, 3, 12, 14, 63, 11, 15, 11, 4, 13}, + {6784, 9, 1, 7, 2, 11, 14, 63, 11, 15, 11, 4, 13}, + {6720, 9, 1, 7, 2, 11, 14, 63, 11, 14, 11, 3, 13}, + {6658, 8, 1, 7, 3, 12, 14, 63, 11, 15, 11, 4, 13}, + {6597, 8, 1, 7, 2, 11, 14, 63, 11, 14, 11, 3, 13}, + {6539, 8, 1, 7, 2, 11, 14, 63, 10, 14, 11, 3, 13}, + {6482, 8, 1, 7, 2, 11, 14, 63, 10, 14, 11, 3, 13}, + {6427, 7, 2, 7, 3, 14, 13, 54, 12, 15, 10, 4, 12}, + {6373, 7, 2, 7, 3, 15, 13, 53, 12, 15, 11, 4, 12}, + {6321, 7, 2, 7, 3, 15, 13, 54, 12, 15, 11, 4, 12}, + {6270, 7, 2, 7, 3, 15, 13, 55, 12, 15, 11, 4, 12}, + {6222, 7, 2, 7, 3, 15, 13, 56, 12, 15, 11, 4, 12}, + {6174, 6, 2, 7, 3, 15, 13, 53, 12, 15, 11, 4, 12}, + {6128, 6, 2, 7, 3, 15, 13, 55, 12, 15, 11, 4, 12}, + {6083, 6, 2, 7, 3, 15, 13, 55, 12, 15, 10, 4, 12}, + {6040, 6, 2, 7, 3, 15, 13, 56, 12, 15, 10, 4, 12}, + {5997, 6, 2, 7, 3, 15, 13, 57, 12, 15, 10, 4, 12}, + }, { + {12605, 13, 1, 4, 2, 15, 12, 21, 12, 15, 11, 4, 13}, /* 80 MHz */ + {12245, 13, 1, 4, 2, 15, 12, 21, 12, 15, 11, 4, 13}, + {11906, 13, 1, 4, 2, 15, 12, 20, 11, 15, 11, 4, 13}, + {11588, 13, 1, 4, 2, 15, 12, 22, 12, 15, 11, 4, 12}, + {11288, 13, 1, 5, 2, 15, 12, 23, 12, 15, 11, 4, 13}, + {11007, 13, 1, 5, 2, 15, 12, 25, 12, 15, 10, 4, 12}, + {10742, 13, 1, 5, 2, 15, 12, 26, 12, 15, 11, 4, 13}, + {10492, 13, 1, 6, 2, 15, 12, 27, 11, 15, 11, 4, 13}, + {10258, 13, 1, 6, 2, 15, 12, 29, 12, 15, 10, 4, 12}, + {10036, 13, 1, 6, 2, 15, 12, 30, 12, 15, 11, 4, 12}, + {9827, 13, 1, 6, 2, 14, 12, 31, 12, 15, 11, 4, 13}, + {9631, 13, 1, 6, 2, 13, 12, 33, 12, 15, 10, 4, 12}, + {9445, 13, 1, 6, 2, 12, 12, 34, 12, 15, 11, 4, 12}, + {9269, 13, 1, 6, 2, 12, 12, 35, 12, 15, 11, 4, 13}, + {9103, 13, 1, 6, 2, 12, 12, 37, 12, 15, 10, 4, 12}, + {8946, 13, 1, 6, 2, 12, 12, 38, 12, 15, 11, 4, 12}, + {8797, 12, 1, 7, 2, 12, 12, 37, 12, 15, 11, 4, 13}, + {8655, 12, 1, 7, 2, 12, 12, 39, 12, 15, 11, 4, 12}, + {8520, 12, 1, 7, 2, 12, 12, 40, 12, 15, 11, 4, 12}, + {8392, 12, 1, 7, 2, 12, 12, 41, 12, 15, 11, 4, 13}, + {8269, 12, 1, 7, 2, 12, 12, 43, 12, 15, 10, 4, 12}, + {8153, 12, 1, 7, 2, 12, 12, 44, 12, 15, 11, 4, 12}, + {8041, 12, 1, 7, 2, 13, 12, 45, 12, 15, 11, 4, 12}, + {7934, 11, 1, 7, 2, 12, 12, 44, 12, 15, 11, 4, 12}, + {7831, 11, 1, 7, 2, 12, 12, 45, 12, 15, 11, 4, 13}, + {7733, 10, 1, 7, 3, 13, 12, 44, 12, 15, 11, 4, 12}, + {7638, 10, 1, 7, 2, 12, 12, 45, 12, 15, 11, 4, 12}, + {7547, 10, 1, 7, 2, 12, 12, 46, 12, 15, 11, 4, 12}, + {7459, 10, 1, 7, 2, 12, 12, 47, 12, 15, 11, 4, 13}, + {7374, 10, 2, 7, 3, 14, 13, 38, 12, 15, 11, 4, 12}, + {7291, 10, 2, 7, 3, 14, 13, 39, 12, 15, 10, 4, 12}, + {7212, 10, 2, 7, 3, 14, 13, 40, 12, 15, 10, 4, 12}, + {7135, 10, 2, 7, 3, 14, 13, 41, 12, 15, 10, 4, 12}, + {7061, 10, 2, 7, 3, 14, 13, 41, 12, 15, 11, 4, 13}, + {6988, 10, 1, 7, 3, 12, 14, 54, 12, 15, 11, 4, 12}, + {6918, 9, 2, 7, 3, 14, 13, 41, 12, 15, 10, 4, 12}, + {6850, 9, 2, 7, 3, 14, 13, 42, 12, 15, 10, 4, 12}, + {6784, 9, 2, 7, 2, 13, 13, 42, 12, 15, 11, 4, 13}, + {6720, 9, 2, 7, 2, 13, 13, 43, 12, 15, 11, 4, 13}, + {6658, 8, 2, 7, 3, 14, 13, 41, 12, 15, 11, 4, 13}, + {6597, 8, 2, 7, 2, 13, 13, 42, 12, 15, 11, 4, 12}, + {6539, 8, 2, 7, 2, 13, 13, 43, 12, 15, 11, 4, 12}, + {6482, 8, 2, 7, 2, 13, 13, 44, 12, 15, 11, 4, 12}, + {6427, 7, 2, 7, 3, 14, 13, 42, 12, 15, 10, 4, 12}, + {6373, 7, 2, 7, 3, 15, 13, 42, 12, 15, 11, 4, 13}, + {6321, 7, 2, 7, 3, 15, 13, 43, 12, 15, 11, 4, 12}, + {6270, 7, 2, 7, 3, 15, 13, 44, 12, 15, 11, 4, 12}, + {6222, 7, 2, 7, 3, 15, 13, 45, 12, 15, 10, 4, 12}, + {6174, 6, 2, 7, 3, 15, 13, 42, 12, 15, 11, 4, 13}, + {6128, 6, 2, 7, 3, 15, 13, 43, 12, 15, 11, 4, 12}, + {6083, 6, 2, 7, 3, 15, 13, 44, 12, 15, 10, 4, 12}, + {6040, 6, 2, 7, 3, 15, 13, 44, 12, 15, 11, 4, 13}, + {5997, 6, 2, 7, 3, 15, 13, 45, 12, 15, 11, 4, 12}, + } +}; + +/* Rx Gain Tables */ + +#define SIZE_FULL_TABLE 77 + +static const uint8_t full_gain_table[RXGAIN_TBLS_END][SIZE_FULL_TABLE][3] = { + { /* 800 MHz */ + {0x00, 0x00, 0x20}, {0x00, 0x00, 0x00}, {0x00, 0x00, 0x00}, + {0x00, 0x01, 0x00}, {0x00, 0x02, 0x00}, {0x00, 0x03, 0x00}, + {0x00, 0x04, 0x00}, {0x00, 0x05, 0x00}, {0x01, 0x03, 0x20}, + {0x01, 0x04, 0x00}, {0x01, 0x05, 0x00}, {0x01, 0x06, 0x00}, + {0x01, 0x07, 0x00}, {0x01, 0x08, 0x00}, {0x01, 0x09, 0x00}, + {0x01, 0x0A, 0x00}, {0x01, 0x0B, 0x00}, {0x01, 0x0C, 0x00}, + {0x01, 0x0D, 0x00}, {0x01, 0x0E, 0x00}, {0x02, 0x09, 0x20}, + {0x02, 0x0A, 0x00}, {0x02, 0x0B, 0x00}, {0x02, 0x0C, 0x00}, + {0x02, 0x0D, 0x00}, {0x02, 0x0E, 0x00}, {0x02, 0x0F, 0x00}, + {0x02, 0x10, 0x00}, {0x02, 0x2B, 0x20}, {0x02, 0x2C, 0x00}, + {0x04, 0x28, 0x20}, {0x04, 0x29, 0x00}, {0x04, 0x2A, 0x00}, + {0x04, 0x2B, 0x00}, {0x24, 0x20, 0x20}, {0x24, 0x21, 0x00}, + {0x44, 0x20, 0x20}, {0x44, 0x21, 0x00}, {0x44, 0x22, 0x00}, + {0x44, 0x23, 0x00}, {0x44, 0x24, 0x00}, {0x44, 0x25, 0x00}, + {0x44, 0x26, 0x00}, {0x44, 0x27, 0x00}, {0x44, 0x28, 0x00}, + {0x44, 0x29, 0x00}, {0x44, 0x2A, 0x00}, {0x44, 0x2B, 0x00}, + {0x44, 0x2C, 0x00}, {0x44, 0x2D, 0x00}, {0x44, 0x2E, 0x00}, + {0x44, 0x2F, 0x00}, {0x44, 0x30, 0x00}, {0x44, 0x31, 0x00}, + {0x44, 0x32, 0x00}, {0x64, 0x2E, 0x20}, {0x64, 0x2F, 0x00}, + {0x64, 0x30, 0x00}, {0x64, 0x31, 0x00}, {0x64, 0x32, 0x00}, + {0x64, 0x33, 0x00}, {0x64, 0x34, 0x00}, {0x64, 0x35, 0x00}, + {0x64, 0x36, 0x00}, {0x64, 0x37, 0x00}, {0x64, 0x38, 0x00}, + {0x65, 0x38, 0x20}, {0x66, 0x38, 0x20}, {0x67, 0x38, 0x20}, + {0x68, 0x38, 0x20}, {0x69, 0x38, 0x20}, {0x6A, 0x38, 0x20}, + {0x6B, 0x38, 0x20}, {0x6C, 0x38, 0x20}, {0x6D, 0x38, 0x20}, + {0x6E, 0x38, 0x20}, {0x6F, 0x38, 0x20} + },{ /* 2300 MHz */ + {0x00, 0x00, 0x20}, {0x00, 0x00, 0x00}, {0x00, 0x00, 0x00}, + {0x00, 0x01, 0x00}, {0x00, 0x02, 0x00}, {0x00, 0x03, 0x00}, + {0x00, 0x04, 0x00}, {0x00, 0x05, 0x00}, {0x01, 0x03, 0x20}, + {0x01, 0x04, 0x00}, {0x01, 0x05, 0x00}, {0x01, 0x06, 0x00}, + {0x01, 0x07, 0x00}, {0x01, 0x08, 0x00}, {0x01, 0x09, 0x00}, + {0x01, 0x0A, 0x00}, {0x01, 0x0B, 0x00}, {0x01, 0x0C, 0x00}, + {0x01, 0x0D, 0x00}, {0x01, 0x0E, 0x00}, {0x02, 0x09, 0x20}, + {0x02, 0x0A, 0x00}, {0x02, 0x0B, 0x00}, {0x02, 0x0C, 0x00}, + {0x02, 0x0D, 0x00}, {0x02, 0x0E, 0x00}, {0x02, 0x0F, 0x00}, + {0x02, 0x10, 0x00}, {0x02, 0x2B, 0x20}, {0x02, 0x2C, 0x00}, + {0x04, 0x27, 0x20}, {0x04, 0x28, 0x00}, {0x04, 0x29, 0x00}, + {0x04, 0x2A, 0x00}, {0x04, 0x2B, 0x00}, {0x24, 0x21, 0x20}, + {0x24, 0x22, 0x00}, {0x44, 0x20, 0x20}, {0x44, 0x21, 0x00}, + {0x44, 0x22, 0x00}, {0x44, 0x23, 0x00}, {0x44, 0x24, 0x00}, + {0x44, 0x25, 0x00}, {0x44, 0x26, 0x00}, {0x44, 0x27, 0x00}, + {0x44, 0x28, 0x00}, {0x44, 0x29, 0x00}, {0x44, 0x2A, 0x00}, + {0x44, 0x2B, 0x00}, {0x44, 0x2C, 0x00}, {0x44, 0x2D, 0x00}, + {0x44, 0x2E, 0x00}, {0x44, 0x2F, 0x00}, {0x44, 0x30, 0x00}, + {0x44, 0x31, 0x00}, {0x64, 0x2E, 0x20}, {0x64, 0x2F, 0x00}, + {0x64, 0x30, 0x00}, {0x64, 0x31, 0x00}, {0x64, 0x32, 0x00}, + {0x64, 0x33, 0x00}, {0x64, 0x34, 0x00}, {0x64, 0x35, 0x00}, + {0x64, 0x36, 0x00}, {0x64, 0x37, 0x00}, {0x64, 0x38, 0x00}, + {0x65, 0x38, 0x20}, {0x66, 0x38, 0x20}, {0x67, 0x38, 0x20}, + {0x68, 0x38, 0x20}, {0x69, 0x38, 0x20}, {0x6A, 0x38, 0x20}, + {0x6B, 0x38, 0x20}, {0x6C, 0x38, 0x20}, {0x6D, 0x38, 0x20}, + {0x6E, 0x38, 0x20}, {0x6F, 0x38, 0x20}, + },{ /* 5500 MHz */ + {0x00, 0x00, 0x20}, {0x00, 0x00, 0x00}, {0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00}, {0x00, 0x00, 0x00}, {0x00, 0x01, 0x00}, + {0x00, 0x02, 0x00}, {0x00, 0x03, 0x00}, {0x01, 0x01, 0x20}, + {0x01, 0x02, 0x00}, {0x01, 0x03, 0x00}, {0x01, 0x04, 0x20}, + {0x01, 0x05, 0x00}, {0x01, 0x06, 0x00}, {0x01, 0x07, 0x00}, + {0x01, 0x08, 0x00}, {0x01, 0x09, 0x00}, {0x01, 0x0A, 0x00}, + {0x01, 0x0B, 0x00}, {0x01, 0x0C, 0x00}, {0x02, 0x08, 0x20}, + {0x02, 0x09, 0x00}, {0x02, 0x0A, 0x00}, {0x02, 0x0B, 0x20}, + {0x02, 0x0C, 0x00}, {0x02, 0x0D, 0x00}, {0x02, 0x0E, 0x00}, + {0x02, 0x0F, 0x00}, {0x02, 0x2A, 0x20}, {0x02, 0x2B, 0x00}, + {0x04, 0x27, 0x20}, {0x04, 0x28, 0x00}, {0x04, 0x29, 0x00}, + {0x04, 0x2A, 0x00}, {0x04, 0x2B, 0x00}, {0x04, 0x2C, 0x00}, + {0x04, 0x2D, 0x00}, {0x24, 0x20, 0x20}, {0x24, 0x21, 0x00}, + {0x24, 0x22, 0x00}, {0x44, 0x20, 0x20}, {0x44, 0x21, 0x00}, + {0x44, 0x22, 0x00}, {0x44, 0x23, 0x00}, {0x44, 0x24, 0x00}, + {0x44, 0x25, 0x00}, {0x44, 0x26, 0x00}, {0x44, 0x27, 0x00}, + {0x44, 0x28, 0x00}, {0x44, 0x29, 0x00}, {0x44, 0x2A, 0x00}, + {0x44, 0x2B, 0x00}, {0x44, 0x2C, 0x00}, {0x44, 0x2D, 0x00}, + {0x44, 0x2E, 0x00}, {0x64, 0x2E, 0x20}, {0x64, 0x2F, 0x00}, + {0x64, 0x30, 0x00}, {0x64, 0x31, 0x00}, {0x64, 0x32, 0x00}, + {0x64, 0x33, 0x00}, {0x64, 0x34, 0x00}, {0x64, 0x35, 0x00}, + {0x64, 0x36, 0x00}, {0x64, 0x37, 0x00}, {0x64, 0x38, 0x00}, + {0x65, 0x38, 0x20}, {0x66, 0x38, 0x20}, {0x67, 0x38, 0x20}, + {0x68, 0x38, 0x20}, {0x69, 0x38, 0x20}, {0x6A, 0x38, 0x20}, + {0x6B, 0x38, 0x20}, {0x6C, 0x38, 0x20}, {0x6D, 0x38, 0x20}, + {0x6E, 0x38, 0x20}, {0x6F, 0x38, 0x20} + } +}; + +static const int8_t full_gain_table_abs_gain[RXGAIN_TBLS_END][SIZE_FULL_TABLE] = { + { /* 800 MHz */ + -1, -1, -1, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, + 53, 54, 55, 56, 57, 58, 59, 60, + 61, 62, 63, 64, 65, 66, 67, 68, + 69, 70, 71, 72, 73 + }, { /* 2300 MHz */ + -3, -3, -3, -2, -1, 0, 1, 2, + 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, + 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71 + }, { /* 5500 MHz */ + -10, -10, -10, -10, -10, -9, -8, -7, + -6, -5, -4, -3, -2, -1, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62 + } +}; + +#define SIZE_SPLIT_TABLE 41 + +#if HAVE_SPLIT_GAIN_TABLE +static const uint8_t split_gain_table[RXGAIN_TBLS_END][SIZE_SPLIT_TABLE][3] = { + { /* 800 MHz */ + {0x00, 0x18, 0x20}, {0x00, 0x18, 0x00}, {0x00, 0x18, 0x00}, + {0x00, 0x18, 0x00}, {0x00, 0x18, 0x00}, {0x00, 0x18, 0x00}, + {0x00, 0x18, 0x20}, {0x01, 0x18, 0x20}, {0x02, 0x18, 0x20}, + {0x04, 0x18, 0x20}, {0x04, 0x38, 0x20}, {0x05, 0x38, 0x20}, + {0x06, 0x38, 0x20}, {0x07, 0x38, 0x20}, {0x08, 0x38, 0x20}, + {0x09, 0x38, 0x20}, {0x0A, 0x38, 0x20}, {0x0B, 0x38, 0x20}, + {0x0C, 0x38, 0x20}, {0x0D, 0x38, 0x20}, {0x0E, 0x38, 0x20}, + {0x0F, 0x38, 0x20}, {0x24, 0x38, 0x20}, {0x25, 0x38, 0x20}, + {0x44, 0x38, 0x20}, {0x45, 0x38, 0x20}, {0x46, 0x38, 0x20}, + {0x47, 0x38, 0x20}, {0x48, 0x38, 0x20}, {0x64, 0x38, 0x20}, + {0x65, 0x38, 0x20}, {0x66, 0x38, 0x20}, {0x67, 0x38, 0x20}, + {0x68, 0x38, 0x20}, {0x69, 0x38, 0x20}, {0x6A, 0x38, 0x20}, + {0x6B, 0x38, 0x20}, {0x6C, 0x38, 0x20}, {0x6D, 0x38, 0x20}, + {0x6E, 0x38, 0x20}, {0x6F, 0x38, 0x20}, + },{ /* 2300 MHz */ + {0x00, 0x18, 0x20}, {0x00, 0x18, 0x00}, {0x00, 0x18, 0x00}, + {0x00, 0x18, 0x00}, {0x00, 0x18, 0x00}, {0x00, 0x18, 0x00}, + {0x00, 0x18, 0x00}, {0x00, 0x18, 0x20}, {0x01, 0x18, 0x20}, + {0x02, 0x18, 0x20}, {0x04, 0x18, 0x20}, {0x04, 0x38, 0x20}, + {0x05, 0x38, 0x20}, {0x06, 0x38, 0x20}, {0x07, 0x38, 0x20}, + {0x08, 0x38, 0x20}, {0x09, 0x38, 0x20}, {0x0A, 0x38, 0x20}, + {0x0B, 0x38, 0x20}, {0x0C, 0x38, 0x20}, {0x0D, 0x38, 0x20}, + {0x0E, 0x38, 0x20}, {0x0F, 0x38, 0x20}, {0x25, 0x38, 0x20}, + {0x26, 0x38, 0x20}, {0x44, 0x38, 0x20}, {0x45, 0x38, 0x20}, + {0x46, 0x38, 0x20}, {0x47, 0x38, 0x20}, {0x64, 0x38, 0x20}, + {0x65, 0x38, 0x20}, {0x66, 0x38, 0x20}, {0x67, 0x38, 0x20}, + {0x68, 0x38, 0x20}, {0x69, 0x38, 0x20}, {0x6A, 0x38, 0x20}, + {0x6B, 0x38, 0x20}, {0x6C, 0x38, 0x20}, {0x6D, 0x38, 0x20}, + {0x6E, 0x38, 0x20}, {0x6F, 0x38, 0x20}, + },{ /* 5500 MHz */ + {0x00, 0x18, 0x20}, {0x00, 0x18, 0x00}, {0x00, 0x18, 0x00}, + {0x00, 0x18, 0x00}, {0x00, 0x18, 0x00}, {0x00, 0x18, 0x00}, + {0x00, 0x18, 0x00}, {0x00, 0x18, 0x00}, {0x00, 0x18, 0x00}, + {0x00, 0x18, 0x00}, {0x01, 0x18, 0x20}, {0x02, 0x18, 0x20}, + {0x04, 0x18, 0x20}, {0x04, 0x38, 0x20}, {0x05, 0x38, 0x20}, + {0x06, 0x38, 0x20}, {0x07, 0x38, 0x20}, {0x08, 0x38, 0x20}, + {0x09, 0x38, 0x20}, {0x0A, 0x38, 0x20}, {0x0B, 0x38, 0x20}, + {0x0C, 0x38, 0x20}, {0x0D, 0x38, 0x20}, {0x0E, 0x38, 0x20}, + {0x0F, 0x38, 0x20}, {0x62, 0x38, 0x20}, {0x25, 0x38, 0x20}, + {0x26, 0x38, 0x20}, {0x44, 0x38, 0x20}, {0x64, 0x38, 0x20}, + {0x65, 0x38, 0x20}, {0x66, 0x38, 0x20}, {0x67, 0x38, 0x20}, + {0x68, 0x38, 0x20}, {0x69, 0x38, 0x20}, {0x6A, 0x38, 0x20}, + {0x6B, 0x38, 0x20}, {0x6C, 0x38, 0x20}, {0x6D, 0x38, 0x20}, + {0x6E, 0x38, 0x20}, {0x6F, 0x38, 0x20}, + } +}; + +static const uint8_t +split_gain_table_abs_gain[RXGAIN_TBLS_END][SIZE_SPLIT_TABLE] = { + { /* 800 MHz */ + -1, -1, -1, -1, -1, -1, -1, 2, + 8, 13, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, + 49 + }, { /* 2300 MHz */ + -3, -3, -3, -3, -3, -3, -3, -3, + 0, 6, 12, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, + 47 + }, { /* 5500 MHz */ + -10, -10, -10, -10, -10, -10, -10, -10, + -10, -10, -7, -2, 3, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, + 20, 22, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, + 38 + } +}; +#endif + +struct gain_table_info ad9361_adi_gt_info[] = { + { + .start = 0, + .end = 1300000000ULL, + .max_index = SIZE_FULL_TABLE, + .abs_gain_tbl = (int8_t *) &full_gain_table_abs_gain[TBL_200_1300_MHZ], + .tab = (uint8_t (*)[3]) full_gain_table[TBL_200_1300_MHZ], + },{ + .start = 1300000000ULL, + .end = 4000000000ULL, + .max_index = SIZE_FULL_TABLE, + .abs_gain_tbl = (int8_t *) &full_gain_table_abs_gain[TBL_1300_4000_MHZ], + .tab = (uint8_t (*)[3]) full_gain_table[TBL_1300_4000_MHZ], + },{ + .start = 4000000000ULL, + .end = 6000000000ULL, + .max_index = SIZE_FULL_TABLE, + .abs_gain_tbl = (int8_t *) &full_gain_table_abs_gain[TBL_4000_6000_MHZ], + .tab = (uint8_t (*)[3]) full_gain_table[TBL_4000_6000_MHZ], +#if HAVE_SPLIT_GAIN_TABLE + },{ + .start = 0, + .end = 1300000000ULL, + .max_index = SIZE_SPLIT_TABLE, + .split_table = 1, + .abs_gain_tbl = (int8_t *) &split_gain_table_abs_gain[TBL_200_1300_MHZ], + .tab = (uint8_t (*)[3]) split_gain_table[TBL_200_1300_MHZ], + },{ + .start = 1300000000ULL, + .end = 4000000000ULL, + .max_index = SIZE_SPLIT_TABLE, + .split_table = 1, + .abs_gain_tbl = (int8_t *) &split_gain_table_abs_gain[TBL_1300_4000_MHZ], + .tab = (uint8_t (*)[3]) split_gain_table[TBL_1300_4000_MHZ], + },{ + .start = 4000000000ULL, + .end = 6000000000ULL, + .max_index = SIZE_SPLIT_TABLE, + .split_table = 1, + .abs_gain_tbl = (int8_t *) &split_gain_table_abs_gain[TBL_4000_6000_MHZ], + .tab = (uint8_t (*)[3]) split_gain_table[TBL_4000_6000_MHZ], +#endif + },{ + .start = 0, + .end = 0, + .max_index = 0, + .split_table = 0, + .abs_gain_tbl = NULL, + .tab = NULL, + }, /* Don't Remove */ +}; + +/* Mixer GM Sub-table */ + +static const uint8_t gm_st_gain[16]= {0x78, 0x74, 0x70, 0x6C, 0x68, 0x64, 0x60, + 0x5C, 0x58, 0x54, 0x50, 0x4C, 0x48, 0x30, 0x18, 0x0}; +static const uint8_t gm_st_ctrl[16]= {0x0, 0xD, 0x15, 0x1B, 0x21, 0x25, 0x29, + 0x2C, 0x2F, 0x31, 0x33, 0x34, 0x35, 0x3A, 0x3D, 0x3E}; + + +static const int8_t lna_table[RXGAIN_TBLS_END][4] = { + {5, 17, 19, 24}, {3, 14, 17, 21}, {-4, 10, 13, 14}}; +static const int8_t tia_table[] = {-6, 0}; +static const int8_t mixer_table[RXGAIN_TBLS_END][16] = { + {0, 3, 9, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25}, + {0, 3, 9, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26}, + {0, 3, 8, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24} +}; + +static const uint32_t gain_step_calib_reg_val[4][5] = { + {0xC0, 0x2E, 0x10, 0x06, 0x00}, // LO Frequency Range: 600 to 1300 MHz + {0xC0, 0x2C, 0x10, 0x06, 0x00}, // LO Frequency Range: 1300 to 3300 MHz + {0xB8, 0x2C, 0x10, 0x06, 0x00}, // LO Frequency Range: 2700 to 4100 MHz + {0xA0, 0x24, 0x10, 0x06, 0x00}, // LO Frequency Range: 4000 to 6000 MHz +}; + +/******************************************************************************/ +/********************** Macros and Constants Definitions **********************/ +/******************************************************************************/ +const char *ad9361_ensm_states[] = { + "sleep", "", "", "", "", "alert", "tx", "tx flush", + "rx", "rx_flush", "fdd", "fdd_flush" +}; + +/** + * SPI multiple bytes register read. + * @param spi + * @param reg The register address. + * @param rbuf The data buffer. + * @param num The number of bytes to read. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_spi_readm(struct spi_desc *spi, uint32_t reg, + uint8_t *rbuf, uint32_t num) +{ + int32_t ret = 0; + uint16_t cmd; + uint8_t *rbuffer; + if (num > MAX_MBYTE_SPI) + return -EINVAL; + + cmd = AD_READ | AD_CNT(num) | AD_ADDR(reg); + rbuffer = malloc(num + 2); + if(!rbuffer) + return -ENOMEM; + rbuffer[0] = cmd >> 8; + rbuffer[1] = cmd & 0xFF; + ret = spi_write_and_read(spi, &rbuffer[0], 2 + num); + + if (ret < 0) + dev_err(&spi->dev, "Read Error %"PRId32, ret); + else + memcpy(rbuf, &rbuffer[2], num); + + free(rbuffer); +#ifdef _DEBUG + { + int32_t i; + for (i = 0; i < num; i++) + dev_dbg(&spi->dev, "%s: reg 0x%"PRIX32" val 0x%X", + __func__, reg--, rbuf[i]); + } +#endif + + return ret; +} + +/** + * SPI register read. + * @param spi + * @param reg The register address. + * @return The register value or negative error code in case of failure. + */ +int32_t ad9361_spi_read(struct spi_desc *spi, uint32_t reg) +{ + uint8_t buf; + int32_t ret; + + ret = ad9361_spi_readm(spi, reg, &buf, 1); + if (ret < 0) + return ret; + + return buf; +} + +/** + * SPI register bits read. + * @param spi + * @param reg The register address. + * @param mask The bits mask. + * @param offset The mask offset. + * @return The bits value or negative error code in case of failure. + */ +static int32_t __ad9361_spi_readf(struct spi_desc *spi, uint32_t reg, + uint32_t mask, uint32_t offset) +{ + uint8_t buf; + int32_t ret; + + if (!mask) + return -EINVAL; + + ret = ad9361_spi_readm(spi, reg, &buf, 1); + if (ret < 0) + return ret; + + buf &= mask; + buf >>= offset; + + return buf; +} + +/** + * SPI register bits read. + * @param spi + * @param reg The register address. + * @param mask The bits mask. + * @return The bits value or negative error code in case of failure. + */ +#define ad9361_spi_readf(spi, reg, mask) \ + __ad9361_spi_readf(spi, reg, mask, find_first_bit(mask)) + +/** + * SPI register write. + * @param spi + * @param reg The register address. + * @param val The value of the register. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_spi_write(struct spi_desc *spi, + uint32_t reg, uint32_t val) +{ + uint8_t buf[3]; + int32_t ret; + uint16_t cmd; + + cmd = AD_WRITE | AD_CNT(1) | AD_ADDR(reg); + buf[0] = cmd >> 8; + buf[1] = cmd & 0xFF; + buf[2] = val; + + ret = spi_write_and_read(spi, buf, 3); + if (ret < 0) { + dev_err(&spi->dev, "Write Error %"PRId32, ret); + return ret; + } + +#ifdef _DEBUG + dev_dbg(&spi->dev, "%s: reg 0x%"PRIX32" val 0x%X", __func__, reg, buf[2]); +#endif + + return 0; +} + +/** + * SPI register bits write. + * @param spi + * @param reg The register address. + * @param mask The bits mask. + * @param offset The mask offset. + * @param val The bits value. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t __ad9361_spi_writef(struct spi_desc *spi, uint32_t reg, + uint32_t mask, uint32_t offset, uint32_t val) +{ + uint8_t buf; + int32_t ret; + + if (!mask) + return -EINVAL; + + ret = ad9361_spi_readm(spi, reg, &buf, 1); + if (ret < 0) + return ret; + + buf &= ~mask; + buf |= ((val << offset) & mask); + + return ad9361_spi_write(spi, reg, buf); +} + +/** + * SPI register bits write. + * @param spi + * @param reg The register address. + * @param mask The bits mask. + * @param val The bits value. + * @return 0 in case of success, negative error code otherwise. + */ +#define ad9361_spi_writef(spi, reg, mask, val) \ + __ad9361_spi_writef(spi, reg, mask, find_first_bit(mask), val) + +/** + * SPI multiple bytes register write. + * @param spi + * @param reg The register address. + * @param tbuf The data buffer. + * @param num The number of bytes to read. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_spi_writem(struct spi_desc *spi, + uint32_t reg, uint8_t *tbuf, uint32_t num) +{ + uint8_t buf[10]; + int32_t ret; + uint16_t cmd; + + if (num > MAX_MBYTE_SPI) + return -EINVAL; + + cmd = AD_WRITE | AD_CNT(num) | AD_ADDR(reg); + buf[0] = cmd >> 8; + buf[1] = cmd & 0xFF; + +#ifndef ALTERA_PLATFORM + memcpy(&buf[2], tbuf, num); +#else + int32_t i; + for (i = 0; i < num; i++) + buf[2 + i] = tbuf[i]; +#endif + ret = spi_write_and_read(spi, buf, num + 2); + if (ret < 0) { + dev_err(&spi->dev, "Write Error %"PRId32, ret); + return ret; + } + +#ifdef _DEBUG + { + int32_t i; + for (i = 0; i < num; i++) + dev_dbg(&spi->dev, "Reg 0x%"PRIX32" val 0x%X", reg--, tbuf[i]); + } +#endif + + return 0; +} + +/** + * Validate RF BW frequency. + * @param phy The AD9361 state structure. + * @param bw The RF BW frequency. + * @return The validated RF BW frequency. + */ +uint32_t ad9361_validate_rf_bw(struct ad9361_rf_phy *phy, uint32_t bw) +{ + switch (phy->dev_sel) { + case ID_AD9363A: + return clamp_t(uint32_t, bw, 200000UL, 20000000UL); + default: + return clamp_t(uint32_t, bw, 200000UL, 56000000UL); + } +} + +/** + * Validate RF PLL frequency. + * @param phy The AD9361 state structure. + * @param is_tx TX enabled. + * @param freq The RF PLL frequency. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_validate_rfpll(struct ad9361_rf_phy *phy, bool is_tx, + uint64_t freq) +{ + switch (phy->dev_sel) { + case ID_AD9363A: + if (freq > AD9363A_MAX_CARRIER_FREQ_HZ || + freq < AD9363A_MIN_CARRIER_FREQ_HZ) + return -EINVAL; + break; + default: + if (freq > MAX_CARRIER_FREQ_HZ || freq < (is_tx ? + MIN_TX_CARRIER_FREQ_HZ : MIN_RX_CARRIER_FREQ_HZ)) + return -EINVAL; + } + + return 0; +} + +/** + * Find optimal value. + * @param field + * @param size + * @param ret_start + * @return The optimal delay in case of success, negative error code otherwise. + */ +int32_t ad9361_find_opt(uint8_t *field, uint32_t size, uint32_t *ret_start) +{ + int32_t i, cnt = 0, max_cnt = 0, start, max_start = 0; + + for(i = 0, start = -1; i < (int64_t)size; i++) { + if (field[i] == 0) { + if (start == -1) + start = i; + cnt++; + } else { + if (cnt > max_cnt) { + max_cnt = cnt; + max_start = start; + } + start = -1; + cnt = 0; + } + } + + if (cnt > max_cnt) { + max_cnt = cnt; + max_start = start; + } + + *ret_start = max_start; + + return max_cnt; +} + +/** + * Select the channel mapping in 1rx1tx mode. + * @param phy The AD9361 state structure. + * @param tx TX + * @param channel Channel + * @return The channel number. + */ +int32_t ad9361_1rx1tx_channel_map(struct ad9361_rf_phy *phy, bool tx, + int32_t channel) +{ + uint32_t map; + + if (phy->pdata->rx2tx2) + return channel; + + if (tx) + map = phy->pdata->rx1tx1_mode_use_tx_num; + else + map = phy->pdata->rx1tx1_mode_use_rx_num; + + if (map == 2) + return channel + 1; + + return channel; +} + +/** + * AD9361 Device Reset + * @param phy The AD9361 state structure. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_reset(struct ad9361_rf_phy *phy) +{ + if (phy->gpio_desc_resetb) { + gpio_set_value(phy->gpio_desc_resetb, 0); + mdelay(1); + gpio_set_value(phy->gpio_desc_resetb, 1); + mdelay(1); + dev_dbg(&phy->spi->dev, "%s: by GPIO", __func__); + return 0; + } + + /* SPI Soft Reset was removed from the register map, since it doesn't + * work reliably. Without a prober HW reset randomness may happen. + * Please specify a RESET GPIO. + */ + + ad9361_spi_write(phy->spi, REG_SPI_CONF, SOFT_RESET | _SOFT_RESET); + ad9361_spi_write(phy->spi, REG_SPI_CONF, 0x0); + dev_err(&phy->spi->dev, + "%s: by SPI, this may cause unpredicted behavior!", __func__); + + return -ENODEV; +} + +/** + * Enable/disable the desired TX channel. + * @param phy The AD9361 state structure. + * @param tx_if The desired channel number [1, 2]. + * @param enable Enable/disable option. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_en_dis_tx(struct ad9361_rf_phy *phy, uint32_t tx_if, + uint32_t enable) +{ + if ((tx_if & enable) > 1 && AD9364_DEVICE && enable) + return -EINVAL; + + return ad9361_spi_writef(phy->spi, REG_TX_ENABLE_FILTER_CTRL, + TX_CHANNEL_ENABLE(tx_if), enable); +} + +/** + * Enable/disable the desired RX channel. + * @param phy The AD9361 state structure. + * @param rx_if The desired channel number [1, 2]. + * @param enable Enable/disable option. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_en_dis_rx(struct ad9361_rf_phy *phy, uint32_t rx_if, + uint32_t enable) +{ + if ((rx_if & enable) > 1 && AD9364_DEVICE && enable) + return -EINVAL; + + return ad9361_spi_writef(phy->spi, REG_RX_ENABLE_FILTER_CTRL, + RX_CHANNEL_ENABLE(rx_if), enable); +} + +/** + * Loopback works only TX1->RX1 or RX2->RX2. + * @param phy The AD9361 state structure. + * @param enable Enable. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_int_loopback_fix_ch_cross(struct ad9361_rf_phy *phy, + bool enable) +{ + /* Loopback works only TX1->RX1 or RX2->RX2 */ + if (!phy->pdata->rx2tx2 && phy->pdata->rx1tx1_mode_use_rx_num != + phy->pdata->rx1tx1_mode_use_tx_num) + return ad9361_en_dis_tx(phy, TX_1 | TX_2, + enable ? phy->pdata->rx1tx1_mode_use_rx_num : + phy->pdata->rx1tx1_mode_use_tx_num); + + return 0; +} + +/** + * BIST loopback mode. + * @param phy The AD9361 state structure. + * @param mode BIST loopback mode. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_bist_loopback(struct ad9361_rf_phy *phy, int32_t mode) +{ + uint32_t sp_hd, reg; + + dev_dbg(&phy->spi->dev, "%s: mode %"PRId32, __func__, mode); + + reg = ad9361_spi_read(phy->spi, REG_OBSERVE_CONFIG); + + phy->bist_loopback_mode = mode; + + switch (mode) { + case 0: + ad9361_hdl_loopback(phy, false); + ad9361_int_loopback_fix_ch_cross(phy, false); + reg &= ~(DATA_PORT_SP_HD_LOOP_TEST_OE | + DATA_PORT_LOOP_TEST_ENABLE); + return ad9361_spi_write(phy->spi, REG_OBSERVE_CONFIG, reg); + case 1: + /* loopback (AD9361 internal) TX->RX */ + ad9361_hdl_loopback(phy, false); + ad9361_int_loopback_fix_ch_cross(phy, true); + sp_hd = ad9361_spi_read(phy->spi, REG_PARALLEL_PORT_CONF_3); + if ((sp_hd & SINGLE_PORT_MODE) && (sp_hd & HALF_DUPLEX_MODE)) + reg |= DATA_PORT_SP_HD_LOOP_TEST_OE; + else + reg &= ~DATA_PORT_SP_HD_LOOP_TEST_OE; + + reg |= DATA_PORT_LOOP_TEST_ENABLE; + + return ad9361_spi_write(phy->spi, REG_OBSERVE_CONFIG, reg); + case 2: + /* loopback (FPGA internal) RX->TX */ + ad9361_hdl_loopback(phy, true); + ad9361_int_loopback_fix_ch_cross(phy, false); + reg &= ~(DATA_PORT_SP_HD_LOOP_TEST_OE | + DATA_PORT_LOOP_TEST_ENABLE); + return ad9361_spi_write(phy->spi, REG_OBSERVE_CONFIG, reg); + default: + return -EINVAL; + } +} + +/** + * Get BIST loopback mode. + * @param phy The AD9361 state structure. + * @param mode BIST loopback mode. + * @return 0 in case of success, negative error code otherwise. + */ +void ad9361_get_bist_loopback(struct ad9361_rf_phy *phy, int32_t *mode) +{ + *mode = phy->bist_loopback_mode; +} + +/** + * BIST mode. + * @param phy The AD9361 state structure. + * @param mode Bist mode. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_bist_prbs(struct ad9361_rf_phy *phy, enum ad9361_bist_mode mode) +{ + uint32_t reg = 0; + + dev_dbg(&phy->spi->dev, "%s: mode %d", __func__, mode); + + phy->bist_prbs_mode = mode; + + switch (mode) { + case BIST_DISABLE: + reg = 0; + break; + case BIST_INJ_TX: + reg = BIST_CTRL_POINT(0) | BIST_ENABLE; + break; + case BIST_INJ_RX: + reg = BIST_CTRL_POINT(2) | BIST_ENABLE; + break; + }; + + phy->bist_config = reg; + + return ad9361_spi_write(phy->spi, REG_BIST_CONFIG, reg); +} + +/** + * Get BIST mode settings. + * @param phy The AD9361 state structure. + * @param mode Bist mode. + * @return 0 in case of success, negative error code otherwise. + */ +void ad9361_get_bist_prbs(struct ad9361_rf_phy *phy, + enum ad9361_bist_mode *mode) +{ + *mode = phy->bist_prbs_mode; +} + +/** + * BIST tone. + * @param phy The AD9361 state structure. + * @param mode Bist tone mode. + * @param freq_Hz Bist tone frequency. + * @param level_dB Bist tone level. + * @param mask Bist reg mask. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_bist_tone(struct ad9361_rf_phy *phy, + enum ad9361_bist_mode mode, uint32_t freq_Hz, + uint32_t level_dB, uint32_t mask) +{ + uint32_t clk = 0; + uint32_t reg = 0, reg1, reg_mask; + + dev_dbg(&phy->spi->dev, "%s: mode %d", __func__, mode); + + phy->bist_tone_mode = mode; + phy->bist_tone_freq_Hz = freq_Hz; + phy->bist_tone_level_dB = level_dB; + phy->bist_tone_mask = mask; + + switch (mode) { + case BIST_DISABLE: + reg = 0; + break; + case BIST_INJ_TX: + clk = clk_get_rate(phy, phy->ref_clk_scale[TX_SAMPL_CLK]); + reg = BIST_CTRL_POINT(0) | BIST_ENABLE; + break; + case BIST_INJ_RX: + clk = clk_get_rate(phy, phy->ref_clk_scale[RX_SAMPL_CLK]); + reg = BIST_CTRL_POINT(2) | BIST_ENABLE; + break; + }; + + reg |= TONE_PRBS; + reg |= TONE_LEVEL(level_dB / 6); + + if (freq_Hz < 4) { + reg |= TONE_FREQ(freq_Hz); + } else { + if (clk) + reg |= TONE_FREQ(DIV_ROUND_CLOSEST(freq_Hz * 32, clk) - 1); + } + + reg_mask = BIST_MASK_CHANNEL_1_I_DATA | BIST_MASK_CHANNEL_1_Q_DATA | + BIST_MASK_CHANNEL_2_I_DATA | BIST_MASK_CHANNEL_2_Q_DATA; + + reg1 = ((mask << 2) & reg_mask); + ad9361_spi_write(phy->spi, REG_BIST_AND_DATA_PORT_TEST_CONFIG, reg1); + + phy->bist_config = reg; + + return ad9361_spi_write(phy->spi, REG_BIST_CONFIG, reg); +} + +/** + * Get BIST tone settings. + * @param phy The AD9361 state structure. + * @param mode Bist tone mode. + * @param freq_Hz Bist tone frequency. + * @param level_dB Bist tone level. + * @param mask Bist reg mask. + * @return 0 in case of success, negative error code otherwise. + */ +void ad9361_get_bist_tone(struct ad9361_rf_phy *phy, + enum ad9361_bist_mode *mode, uint32_t *freq_Hz, + uint32_t *level_dB, uint32_t *mask) +{ + *mode = phy->bist_tone_mode; + *freq_Hz = phy->bist_tone_freq_Hz; + *level_dB = phy->bist_tone_level_dB; + *mask = phy->bist_tone_mask; +} + +/** + * Check the calibration done bit. + * @param phy The AD9361 state structure. + * @param reg The register address. + * @param mask The bit mask. + * @param done_state The done state [0,1]. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_check_cal_done(struct ad9361_rf_phy *phy, uint32_t reg, + uint32_t mask, uint32_t done_state) +{ + uint32_t timeout = 20000; /* RFDC_CAL can take long */ + uint32_t state; + + do { + state = ad9361_spi_readf(phy->spi, reg, mask); + if (state == done_state) + return 0; + + if (reg == REG_CALIBRATION_CTRL) + udelay(1200); + else + udelay(120); + } while (timeout--); + + dev_err(&phy->spi->dev, "Calibration TIMEOUT (0x%"PRIX32", 0x%"PRIX32")", reg, + mask); + + return -ETIMEDOUT; +} + +/** + * Run an AD9361 calibration and check the calibration done bit. + * @param phy The AD9361 state structure. + * @param mask The calibration bit mask[RX_BB_TUNE_CAL, TX_BB_TUNE_CAL, + * RX_QUAD_CAL, TX_QUAD_CAL, RX_GAIN_STEP_CAL, TXMON_CAL, + * RFDC_CAL, BBDC_CAL]. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_run_calibration(struct ad9361_rf_phy *phy, uint32_t mask) +{ + int32_t ret = ad9361_spi_write(phy->spi, REG_CALIBRATION_CTRL, mask); + if (ret < 0) + return ret; + + dev_dbg(&phy->spi->dev, "%s: CAL Mask 0x%"PRIx32, __func__, mask); + + return ad9361_check_cal_done(phy, REG_CALIBRATION_CTRL, mask, 0); +} + +/** + * Choose the right RX gain table index for the selected frequency. + * @param freq The frequency value [Hz]. + * @return The index to the RX gain table. + */ +static int32_t ad9361_gt_tableindex(struct ad9361_rf_phy *phy, uint64_t freq) +{ + int32_t i; + + for (i = 0; phy->gt_info[i].tab != NULL; i++) { + if ((phy->pdata->split_gt == phy->gt_info[i].split_table) && + (phy->gt_info[i].start < freq) && + (freq <= phy->gt_info[i].end)) { + return i; + } + } + + dev_err(&phy->spi->dev, + "%s: Failed to find suitable gain table (%"PRIu64")", __func__, freq); + + return 0; +} + +/** + * Return the current gain table index. + * @param phy The AD9361 state structure. + * @return The current gain table index or 0 if the table was not chosen. + */ +uint32_t ad9361_gt(struct ad9361_rf_phy *phy) +{ + if (phy->current_table == NO_GAIN_TABLE) { + dev_err(&phy->spi->dev, "%s: ERROR", __func__); + return 0; + } + + return phy->current_table; +} + +/** + * Shift the real frequency value, so it fits type unsigned long + * Note: PLL operates between 47 .. 6000 MHz which is > 2^32. + * @param freq The frequency value [Hz]. + * @return The shifted frequency value. + */ +uint32_t ad9361_to_clk(uint64_t freq) +{ + return (uint32_t)(freq >> 1); +} + +/** + * Shift back the frequency value, so it reflects the real value. + * Note: PLL operates between 47 .. 6000 MHz which is > 2^32. + * @param freq The frequency value [Hz]. + * @return The shifted frequency value. + */ +uint64_t ad9361_from_clk(uint32_t freq) +{ + return ((uint64_t)freq << 1); +} + +/** + * Find the corresponding table index for a specific gain value. + * @param phy The AD9361 state structure. + * @param gain The gain value. + * @return The table index. + */ +static int find_table_index(struct ad9361_rf_phy *phy, int gain) +{ + uint32_t i, nm1, n; + + for (i = 0; i < phy->gt_info[ad9361_gt(phy)].max_index; i++) { + if (phy->gt_info[ad9361_gt(phy)].abs_gain_tbl[i] >= gain) { + nm1 = abs(phy->gt_info[ad9361_gt(phy)].abs_gain_tbl[ + (i > 0) ? i - 1 : i] - gain); + n = abs(phy->gt_info[ad9361_gt(phy)].abs_gain_tbl[i] + - gain); + if (nm1 < n) + return (i > 0) ? i - 1 : i; + else + return i; + } + } + + return -EINVAL; +} + +/** + * Load the gain table for the selected frequency range and receiver. + * @param phy The AD9361 state structure. + * @param freq The frequency value [Hz]. + * @param dest The destination [GT_RX1, GT_RX2]. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_load_gt(struct ad9361_rf_phy *phy, uint64_t freq, + uint32_t dest) +{ + struct spi_desc *spi = phy->spi; + uint8_t (*tab)[3]; + uint32_t band, index_max, i, lna, lpf_tia_mask, set_gain; + int32_t ret, rx1_gain, rx2_gain; + + dev_dbg(&phy->spi->dev, "%s: frequency %"PRIu64, __func__, freq); + + band = ad9361_gt_tableindex(phy, freq); + + dev_dbg(&phy->spi->dev, "%s: frequency %"PRIu64" (band %"PRIu32")", + __func__, freq, band); + + /* check if table is present */ + if (phy->current_table == band) + return 0; + + tab = phy->gt_info[band].tab; + index_max = phy->gt_info[band].max_index; + + ad9361_spi_writef(spi, REG_AGC_CONFIG_2, + AGC_USE_FULL_GAIN_TABLE, !phy->pdata->split_gt); + + ad9361_spi_write(spi, REG_MAX_LMT_FULL_GAIN, + index_max - 1); /* Max Full/LMT Gain Table Index */ + + set_gain = ad9361_spi_readf(spi, REG_RX1_MANUAL_LMT_FULL_GAIN, + RX_FULL_TBL_IDX_MASK); + + if (phy->current_table != NO_GAIN_TABLE) { + rx1_gain = phy->gt_info[phy->current_table].abs_gain_tbl[set_gain]; + } else { + if (set_gain > (index_max - 1)) + set_gain = index_max - 1; + + rx1_gain = phy->gt_info[band].abs_gain_tbl[set_gain]; + } + + set_gain = ad9361_spi_readf(spi, REG_RX2_MANUAL_LMT_FULL_GAIN, + RX_FULL_TBL_IDX_MASK); + + if (phy->current_table != NO_GAIN_TABLE) { + rx2_gain = phy->gt_info[phy->current_table].abs_gain_tbl[set_gain]; + } else { + if (set_gain > (index_max - 1)) + set_gain = index_max - 1; + + rx2_gain = phy->gt_info[band].abs_gain_tbl[set_gain]; + } + + lna = phy->pdata->elna_ctrl.elna_in_gaintable_all_index_en ? + EXT_LNA_CTRL : 0; + + ad9361_spi_write(spi, REG_GAIN_TABLE_CONFIG, START_GAIN_TABLE_CLOCK | + RECEIVER_SELECT(dest)); /* Start Gain Table Clock */ + + /* TX QUAD Calibration */ + if (phy->pdata->split_gt) + lpf_tia_mask = 0x20; + else + lpf_tia_mask = 0x3F; + + phy->tx_quad_lpf_tia_match = -EINVAL; + + for (i = 0; i < index_max; i++) { + ad9361_spi_write(spi, REG_GAIN_TABLE_ADDRESS, i); /* Gain Table Index */ + ad9361_spi_write(spi, REG_GAIN_TABLE_WRITE_DATA1, + tab[i][0] | lna); /* Ext LNA, Int LNA, & Mixer Gain Word */ + ad9361_spi_write(spi, REG_GAIN_TABLE_WRITE_DATA2, + tab[i][1]); /* TIA & LPF Word */ + ad9361_spi_write(spi, REG_GAIN_TABLE_WRITE_DATA3, + tab[i][2]); /* DC Cal bit & Dig Gain Word */ + ad9361_spi_write(spi, REG_GAIN_TABLE_CONFIG, + START_GAIN_TABLE_CLOCK | + WRITE_GAIN_TABLE | + RECEIVER_SELECT(dest)); /* Gain Table Index */ + ad9361_spi_write(spi, REG_GAIN_TABLE_READ_DATA1, + 0); /* Dummy Write to delay 3 ADCCLK/16 cycles */ + ad9361_spi_write(spi, REG_GAIN_TABLE_READ_DATA1, + 0); /* Dummy Write to delay ~1u */ + + if ((tab[i][1] & lpf_tia_mask) == 0x20) + phy->tx_quad_lpf_tia_match = i; + + } + + ad9361_spi_write(spi, REG_GAIN_TABLE_CONFIG, START_GAIN_TABLE_CLOCK | + RECEIVER_SELECT(dest)); /* Clear Write Bit */ + ad9361_spi_write(spi, REG_GAIN_TABLE_READ_DATA1, + 0); /* Dummy Write to delay ~1u */ + ad9361_spi_write(spi, REG_GAIN_TABLE_READ_DATA1, + 0); /* Dummy Write to delay ~1u */ + ad9361_spi_write(spi, REG_GAIN_TABLE_CONFIG, 0); /* Stop Gain Table Clock */ + + phy->current_table = band; + + ret = find_table_index(phy, rx1_gain); + if (ret < 0) + ret = phy->gt_info[band].max_index - 1; + + ad9361_spi_writef(spi, REG_RX1_MANUAL_LMT_FULL_GAIN, + RX_FULL_TBL_IDX_MASK, ret); /* Rx1 Full/LMT Gain Index */ + + ret = find_table_index(phy, rx2_gain); + if (ret < 0) + ret = phy->gt_info[band].max_index - 1; + + ad9361_spi_write(spi, REG_RX2_MANUAL_LMT_FULL_GAIN, + ret); /* Rx2 Full/LMT Gain Index */ + + return 0; +} + +/** + * Setup the external low-noise amplifier (LNA). + * @param phy The AD9361 state structure. + * @param ctrl Pointer to eLNA control structure. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_setup_ext_lna(struct ad9361_rf_phy *phy, + struct elna_control *ctrl) +{ + ad9361_spi_writef(phy->spi, REG_EXTERNAL_LNA_CTRL, EXTERNAL_LNA1_CTRL, + ctrl->elna_1_control_en); + + ad9361_spi_writef(phy->spi, REG_EXTERNAL_LNA_CTRL, EXTERNAL_LNA2_CTRL, + ctrl->elna_2_control_en); + + ad9361_spi_write(phy->spi, REG_EXT_LNA_HIGH_GAIN, + EXT_LNA_HIGH_GAIN(ctrl->gain_mdB / 500)); + + return ad9361_spi_write(phy->spi, REG_EXT_LNA_LOW_GAIN, + EXT_LNA_LOW_GAIN(ctrl->bypass_loss_mdB / 500)); +} + +/** + * Set the clock output mode. + * @param phy The AD9361 state structure. + * @param mode The clock output mode []. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_clkout_control(struct ad9361_rf_phy *phy, + enum ad9361_clkout mode) +{ + if (mode == CLKOUT_DISABLE) + return ad9361_spi_writef(phy->spi, REG_BBPLL, CLKOUT_ENABLE, 0); + + return ad9361_spi_writef(phy->spi, REG_BBPLL, + CLKOUT_ENABLE | CLKOUT_SELECT(~0), + ((mode - 1) << 1) | 0x1); +} + +/** + * Load the Gm Sub Table. + * @param phy The AD9361 state structure. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_load_mixer_gm_subtable(struct ad9361_rf_phy *phy) +{ + int32_t i, addr; + dev_dbg(&phy->spi->dev, "%s", __func__); + + ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_CONFIG, + START_GM_SUB_TABLE_CLOCK); /* Start Clock */ + + for (i = 0, addr = ARRAY_SIZE(gm_st_ctrl); i < (int64_t)ARRAY_SIZE(gm_st_ctrl); + i++) { + ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_ADDRESS, + --addr); /* Gain Table Index */ + ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_BIAS_WRITE, 0); /* Bias */ + ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_GAIN_WRITE, + gm_st_gain[i]); /* Gain */ + ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_CTRL_WRITE, + gm_st_ctrl[i]); /* Control */ + ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_CONFIG, + WRITE_GM_SUB_TABLE | START_GM_SUB_TABLE_CLOCK); /* Write Words */ + ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_GAIN_READ, 0); /* Dummy Delay */ + ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_GAIN_READ, 0); /* Dummy Delay */ + } + + ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_CONFIG, + START_GM_SUB_TABLE_CLOCK); /* Clear Write */ + ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_GAIN_READ, 0); /* Dummy Delay */ + ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_GAIN_READ, 0); /* Dummy Delay */ + ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_CONFIG, 0); /* Stop Clock */ + + return 0; +} + +/** + * Set the attenuation for the selected TX channels. + * @param phy The AD9361 state structure. + * @param atten_mdb Attenuation value [mdB]. + * @param tx1 Set true, the attenuation of the TX1 will be affected. + * @param tx2 Set true, the attenuation of the TX2 will be affected. + * @param immed Set true, an immediate update will take place. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_tx_atten(struct ad9361_rf_phy *phy, uint32_t atten_mdb, + bool tx1, bool tx2, bool immed) +{ + uint8_t buf[2]; + int32_t ret = 0; + + dev_dbg(&phy->spi->dev, "%s : attenuation %"PRIu32" mdB tx1=%d tx2=%d", + __func__, atten_mdb, tx1, tx2); + + if (atten_mdb > MAX_TX_ATTENUATION_DB) /* 89.75 dB */ + return -EINVAL; + + atten_mdb /= 250; /* Scale to 0.25dB / LSB */ + + buf[0] = atten_mdb >> 8; + buf[1] = atten_mdb & 0xFF; + + ad9361_spi_writef(phy->spi, REG_TX2_DIG_ATTEN, + IMMEDIATELY_UPDATE_TPC_ATTEN, 0); + + if (tx1) + ret = ad9361_spi_writem(phy->spi, REG_TX1_ATTEN_1, buf, 2); + + if (tx2) + ret = ad9361_spi_writem(phy->spi, REG_TX2_ATTEN_1, buf, 2); + + if (immed) + ad9361_spi_writef(phy->spi, REG_TX2_DIG_ATTEN, + IMMEDIATELY_UPDATE_TPC_ATTEN, 1); + + return ret; +} + +/** + * Get the attenuation for the selected TX channel. + * @param phy The AD9361 state structure. + * @param tx_num The selected channel [1, 2]. + * @return The attenuation value [mdB] or negative error code in case of failure. + */ +int32_t ad9361_get_tx_atten(struct ad9361_rf_phy *phy, uint32_t tx_num) +{ + uint8_t buf[2]; + int32_t ret = 0; + uint32_t code; + + ret = ad9361_spi_readm(phy->spi, (tx_num == 1) ? + REG_TX1_ATTEN_1 : REG_TX2_ATTEN_1, buf, 2); + + if (ret < 0) + return ret; + + code = (buf[0] << 8) | buf[1]; + + code *= 250; + + return code; +} + +/** + * Mute TX. + * @param phy The AD9361 state structure. + * @param state The state - 0 mute; 1 - unmute. + * @return 0 in case of success + */ +int32_t ad9361_tx_mute(struct ad9361_rf_phy *phy, uint32_t state) +{ + int32_t ret; + + if (state) { + phy->tx1_atten_cached = ad9361_get_tx_atten(phy, 1); + phy->tx2_atten_cached = ad9361_get_tx_atten(phy, 2); + + return ad9361_set_tx_atten(phy, 89750, true, true, true); + } else { + if (phy->tx1_atten_cached == phy->tx2_atten_cached) + return ad9361_set_tx_atten(phy, phy->tx1_atten_cached, + true, true, true); + + ret = ad9361_set_tx_atten(phy, phy->tx1_atten_cached, + true, false, true); + ret |= ad9361_set_tx_atten(phy, phy->tx2_atten_cached, + false, true, true); + + return ret; + } +} + +/** + * Choose the right RF VCO table index for the selected frequency. + * @param freq The frequency value [Hz]. + * @return The index from the RF VCO table. + */ +static uint32_t ad9361_rfvco_tableindex(uint32_t freq) +{ + if (freq < 50000000UL) + return LUT_FTDD_40; + + if (freq <= 70000000UL) + return LUT_FTDD_60; + + return LUT_FTDD_80; +} + +/** + * Initialize the RFPLL VCO. + * @param phy The AD9361 state structure. + * @param tx Set true for TX_RFPLL. + * @param vco_freq The VCO frequency [Hz]. + * @param ref_clk The reference clock frequency [Hz]. + * @return 0 in case of success + */ +static int32_t ad9361_rfpll_vco_init(struct ad9361_rf_phy *phy, + bool tx, uint64_t vco_freq, + uint32_t ref_clk) +{ + struct spi_desc *spi = phy->spi; + const struct SynthLUT(*tab); + int32_t i = 0; + uint32_t range, offs = 0; + + range = ad9361_rfvco_tableindex(ref_clk); + + dev_dbg(&phy->spi->dev, + "%s : vco_freq %"PRIu64" : ref_clk %"PRIu32" : range %"PRIu32, + __func__, vco_freq, ref_clk, range); + + do_div(&vco_freq, 1000000UL); /* vco_freq in MHz */ + + if ((phy->pdata->fdd && !phy->pdata->fdd_independent_mode) + && (phy->current_tx_lo_freq != phy->current_rx_lo_freq)) { + tab = &SynthLUT_FDD[range][0]; + if (tx) + phy->current_tx_use_tdd_table = false; + else + phy->current_rx_use_tdd_table = false; + } else { + if (have_tdd_tables) { + tab = &SynthLUT_TDD[range][0]; + if (tx) + phy->current_tx_use_tdd_table = true; + else + phy->current_rx_use_tdd_table = true; + } + } + + if (tx) + offs = REG_TX_VCO_OUTPUT - REG_RX_VCO_OUTPUT; + + while (i < SYNTH_LUT_SIZE && tab[i].VCO_MHz > vco_freq) + i++; + + dev_dbg(&phy->spi->dev, "%s : freq %d MHz : index %"PRId32, + __func__, tab[i].VCO_MHz, i); + + ad9361_spi_write(spi, REG_RX_VCO_OUTPUT + offs, + VCO_OUTPUT_LEVEL(tab[i].VCO_Output_Level) | + PORB_VCO_LOGIC); + ad9361_spi_writef(spi, REG_RX_ALC_VARACTOR + offs, + VCO_VARACTOR(~0), tab[i].VCO_Varactor); + ad9361_spi_write(spi, REG_RX_VCO_BIAS_1 + offs, + VCO_BIAS_REF(tab[i].VCO_Bias_Ref) | + VCO_BIAS_TCF(tab[i].VCO_Bias_Tcf)); + + ad9361_spi_write(spi, REG_RX_FORCE_VCO_TUNE_1 + offs, + VCO_CAL_OFFSET(tab[i].VCO_Cal_Offset)); + ad9361_spi_write(spi, REG_RX_VCO_VARACTOR_CTRL_1 + offs, + VCO_VARACTOR_REFERENCE( + tab[i].VCO_Varactor_Reference)); + + ad9361_spi_write(spi, REG_RX_VCO_CAL_REF + offs, VCO_CAL_REF_TCF(0)); + + ad9361_spi_write(spi, REG_RX_VCO_VARACTOR_CTRL_0 + offs, + VCO_VARACTOR_OFFSET(0) | + VCO_VARACTOR_REFERENCE_TCF(7)); + + ad9361_spi_writef(spi, REG_RX_CP_CURRENT + offs, CHARGE_PUMP_CURRENT(~0), + tab[i].Charge_Pump_Current); + ad9361_spi_write(spi, REG_RX_LOOP_FILTER_1 + offs, + LOOP_FILTER_C2(tab[i].LF_C2) | + LOOP_FILTER_C1(tab[i].LF_C1)); + ad9361_spi_write(spi, REG_RX_LOOP_FILTER_2 + offs, + LOOP_FILTER_R1(tab[i].LF_R1) | + LOOP_FILTER_C3(tab[i].LF_C3)); + ad9361_spi_write(spi, REG_RX_LOOP_FILTER_3 + offs, + LOOP_FILTER_R3(tab[i].LF_R3)); + + return 0; +} + +/** + * Get the current gain in Split Gain Table Mode + * @param phy The AD9361 state structure. + * @param idx_reg Register base address for the selected receiver + * @param rx_gain A rf_rx_gain struct to store the RF gain. + * @return 0 in case of success, + */ +static int32_t ad9361_get_split_table_gain(struct ad9361_rf_phy *phy, + uint32_t idx_reg, + struct rf_rx_gain *rx_gain) +{ + struct spi_desc *spi = phy->spi; + uint32_t val, tbl_addr; + int32_t rc = 0; + + + rx_gain->fgt_lmt_index = ad9361_spi_readf(spi, idx_reg, + FULL_TABLE_GAIN_INDEX(~0)); + tbl_addr = ad9361_spi_read(spi, REG_GAIN_TABLE_ADDRESS); + + ad9361_spi_write(spi, REG_GAIN_TABLE_ADDRESS, rx_gain->fgt_lmt_index); + + val = ad9361_spi_read(spi, REG_GAIN_TABLE_READ_DATA1); + rx_gain->lna_index = TO_LNA_GAIN(val); + rx_gain->mixer_index = TO_MIXER_GM_GAIN(val); + + rx_gain->tia_index = ad9361_spi_readf(spi, REG_GAIN_TABLE_READ_DATA2, TIA_GAIN); + + rx_gain->lmt_gain = lna_table[ad9361_gt(phy) - RXGAIN_TBLS_END][rx_gain->lna_index] + + mixer_table[ad9361_gt(phy) - RXGAIN_TBLS_END][rx_gain->mixer_index] + + tia_table[rx_gain->tia_index]; + + ad9361_spi_write(spi, REG_GAIN_TABLE_ADDRESS, tbl_addr); + + /* Read LPF Index */ + rx_gain->lpf_gain = ad9361_spi_readf(spi, idx_reg + 1, LPF_GAIN_RX(~0)); + + /* Read Digital Gain */ + rx_gain->digital_gain = ad9361_spi_readf(spi, idx_reg + 2, + DIGITAL_GAIN_RX(~0)); + + rx_gain->gain_db = rx_gain->lmt_gain + rx_gain->lpf_gain + + rx_gain->digital_gain; + return rc; +} + +/** + * Get the current gain in Full Gain Table Mode + * @param phy The AD9361 state structure. + * @param idx_reg Register base address for the selected receiver + * @param rx_gain A rf_rx_gain struct to store the RF gain. + * @return 0 in case of success + */ +static int32_t ad9361_get_full_table_gain(struct ad9361_rf_phy *phy, + uint32_t idx_reg, + struct rf_rx_gain *rx_gain) +{ + struct spi_desc *spi = phy->spi; + uint32_t val; + + rx_gain->fgt_lmt_index = val = ad9361_spi_readf(spi, idx_reg, + FULL_TABLE_GAIN_INDEX(~0)); + /* Read Digital Gain */ + rx_gain->digital_gain = ad9361_spi_readf(spi, idx_reg + 2, + DIGITAL_GAIN_RX(~0)); + + rx_gain->gain_db = phy->gt_info[ad9361_gt(phy)].abs_gain_tbl[val]; + + return 0; +} + +/** + * Get current RX gain for the selected channel. + * @param phy The AD9361 state structure. + * @param rx_id The desired channel number (0, 1). + * @param rx_gain A rf_rx_gain struct to store the RF gain. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_rx_gain(struct ad9361_rf_phy *phy, + uint32_t rx_id, struct rf_rx_gain *rx_gain) +{ + struct spi_desc *spi = phy->spi; + uint32_t val, idx_reg; + uint8_t gain_ctl_shift, rx_enable_mask; + uint8_t fast_atk_shift; + int32_t rc = 0; + + if (rx_id == 1) { + gain_ctl_shift = RX1_GAIN_CTRL_SHIFT; + idx_reg = REG_GAIN_RX1; + rx_enable_mask = RX_CHANNEL_ENABLE(RX_1); + fast_atk_shift = RX1_FAST_ATK_SHIFT; + + } else if (rx_id == 2) { + gain_ctl_shift = RX2_GAIN_CTRL_SHIFT; + idx_reg = REG_GAIN_RX2; + rx_enable_mask = RX_CHANNEL_ENABLE(RX_2); + fast_atk_shift = RX2_FAST_ATK_SHIFT; + } else { + dev_err(dev, "Unknown Rx path %"PRIu32, rx_id); + rc = -EINVAL; + goto out; + } + + val = ad9361_spi_readf(spi, REG_RX_ENABLE_FILTER_CTRL, rx_enable_mask); + + if (!val) { + dev_dbg(dev, "Rx%"PRIu32" is not enabled", rx_gain->ant); + rc = -EAGAIN; + goto out; + } + + val = ad9361_spi_read(spi, REG_AGC_CONFIG_1); + + val = (val >> gain_ctl_shift) & RX_GAIN_CTL_MASK; + + if (val == RX_GAIN_CTL_AGC_FAST_ATK) { + /* In fast attack mode check whether Fast attack state machine + * has locked gain, if not then we can not read gain. + */ + val = ad9361_spi_read(spi, REG_FAST_ATTACK_STATE); + val = (val >> fast_atk_shift) & FAST_ATK_MASK; + if (val != FAST_ATK_GAIN_LOCKED) { + dev_warn(dev, "Failed to read gain, state m/c at %"PRIx32, + val); + rc = -EAGAIN; + goto out; + } + } + + if (has_split_gt && phy->pdata->split_gt) + rc = ad9361_get_split_table_gain(phy, idx_reg, rx_gain); + else + rc = ad9361_get_full_table_gain(phy, idx_reg, rx_gain); + +out: + return rc; +} + +/** + * Get Enable State Machine (ENSM) state. + * @param phy The AD9361 state structure. + * @return The state. + */ +uint8_t ad9361_ensm_get_state(struct ad9361_rf_phy *phy) +{ + return ad9361_spi_readf(phy->spi, REG_STATE, ENSM_STATE(~0)); +} + +/** + * Force Enable State Machine (ENSM) to the desired state (internally used only). + * @param phy The AD9361 state structure. + * @param ensm_state The ENSM state [ENSM_STATE_SLEEP_WAIT, ENSM_STATE_ALERT, + * ENSM_STATE_TX, ENSM_STATE_TX_FLUSH, ENSM_STATE_RX, + * ENSM_STATE_RX_FLUSH, ENSM_STATE_FDD, ENSM_STATE_FDD_FLUSH]. + * @return None. + */ +void ad9361_ensm_force_state(struct ad9361_rf_phy *phy, uint8_t ensm_state) +{ + struct spi_desc *spi = phy->spi; + uint8_t dev_ensm_state; + int32_t rc, timeout = 10; + uint32_t val; + + dev_ensm_state = ad9361_spi_readf(spi, REG_STATE, ENSM_STATE(~0)); + + phy->prev_ensm_state = dev_ensm_state; + + if (dev_ensm_state == ensm_state) { + dev_dbg(dev, "Nothing to do, device is already in %d state", + ensm_state); + goto out; + } + + dev_dbg(dev, "Device is in %x state, forcing to %x", dev_ensm_state, + ensm_state); + + val = ad9361_spi_read(spi, REG_ENSM_CONFIG_1); + + /* Enable control through SPI writes, and take out from + * Alert + */ + if (val & ENABLE_ENSM_PIN_CTRL) { + val &= ~ENABLE_ENSM_PIN_CTRL; + phy->ensm_pin_ctl_en = true; + } else { + phy->ensm_pin_ctl_en = false; + } + + if (dev_ensm_state) + val &= ~(TO_ALERT); + + switch (ensm_state) { + + case ENSM_STATE_TX: + case ENSM_STATE_FDD: + val |= FORCE_TX_ON; + break; + case ENSM_STATE_RX: + val |= FORCE_RX_ON; + break; + case ENSM_STATE_ALERT: + val &= ~(FORCE_TX_ON | FORCE_RX_ON); + val |= TO_ALERT | FORCE_ALERT_STATE; + break; + default: + dev_err(dev, "No handling for forcing %d ensm state", + ensm_state); + goto out; + } + + ad9361_spi_write(spi, REG_ENSM_CONFIG_1, TO_ALERT | FORCE_ALERT_STATE); + + rc = ad9361_spi_write(spi, REG_ENSM_CONFIG_1, val); + if (rc) { + dev_err(dev, "Failed to write ENSM_CONFIG_1\n"); + goto out; + } + + while (ad9361_ensm_get_state(phy) != ensm_state && --timeout) { + mdelay(1); + } + + if (timeout == 0) + dev_err(dev, "Failed to restore state"); + +out: + return; + +} + +/** + * Restore an Enable State Machine (ENSM) state. + * @param phy The AD9361 state structure. + * @param ensm_state The state. + * @return None. + */ +void ad9361_ensm_restore_state(struct ad9361_rf_phy *phy, uint8_t ensm_state) +{ + struct spi_desc *spi = phy->spi; + int32_t rc; + uint32_t val; + + val = ad9361_spi_read(spi, REG_ENSM_CONFIG_1); + + /* We are restoring state only, so clear State bits first + * which might have set while forcing a particular state + */ + val &= ~(FORCE_TX_ON | FORCE_RX_ON | FORCE_ALERT_STATE); + val |= TO_ALERT; + + switch (ensm_state) { + case ENSM_STATE_TX: + case ENSM_STATE_FDD: + val |= FORCE_TX_ON; + break; + case ENSM_STATE_RX: + val |= FORCE_RX_ON; + break; + case ENSM_STATE_ALERT: + val |= TO_ALERT; + break; + case ENSM_STATE_INVALID: + dev_dbg(dev, "No need to restore, ENSM state wasn't saved"); + return; + default: + dev_dbg(dev, "Could not restore to %d ENSM state", + ensm_state); + return; + } + + ad9361_spi_write(spi, REG_ENSM_CONFIG_1, TO_ALERT | FORCE_ALERT_STATE); + + rc = ad9361_spi_write(spi, REG_ENSM_CONFIG_1, val); + if (rc) { + dev_err(dev, "Failed to write ENSM_CONFIG_1"); + return; + } + + if (phy->ensm_pin_ctl_en) { + val |= ENABLE_ENSM_PIN_CTRL; + rc = ad9361_spi_write(spi, REG_ENSM_CONFIG_1, val); + if (rc) + dev_err(dev, "Failed to write ENSM_CONFIG_1"); + } +} + +/** + * Restore the previous Enable State Machine (ENSM) state. + * @param phy The AD9361 state structure. + * @return None. + */ +void ad9361_ensm_restore_prev_state(struct ad9361_rf_phy *phy) +{ + return ad9361_ensm_restore_state(phy, phy->prev_ensm_state); +} + +/** + * Set gain in Split Gain Table Mode (used only in Manual Gain Control Mode). + * @param phy The AD9361 state structure. + * @param idx_reg Register base address for the selected receiver + * @param rx_gain The rf_rx_gain struct containing the RF gain. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t set_split_table_gain(struct ad9361_rf_phy *phy, uint32_t idx_reg, + struct rf_rx_gain *rx_gain) +{ + struct spi_desc *spi = phy->spi; + int32_t rc = 0; + + if ((rx_gain->fgt_lmt_index > MAX_LMT_INDEX) || + (rx_gain->lpf_gain > MAX_LPF_GAIN) || + (rx_gain->digital_gain > MAX_DIG_GAIN)) { + dev_err(dev, "LMT_INDEX missing or greater than max value %d", + MAX_LMT_INDEX); + dev_err(dev, "LPF_GAIN missing or greater than max value %d", + MAX_LPF_GAIN); + dev_err(dev, "DIGITAL_GAIN cannot be more than %d", + MAX_DIG_GAIN); + rc = -EINVAL; + goto out; + } + + rc = find_table_index(phy, rx_gain->gain_db); + if (rc < 0) { + dev_err(dev, "Invalid gain %"PRId32", supported range [%d - %d]\n", + rx_gain->gain_db, phy->gt_info[ad9361_gt(phy)]. + abs_gain_tbl[0], + phy->gt_info[ad9361_gt(phy)].abs_gain_tbl + [phy->gt_info[ad9361_gt(phy)].max_index - 1]); + goto out; + } + + rx_gain->fgt_lmt_index = rc; + + rc = ad9361_spi_writef(spi, idx_reg, RX_FULL_TBL_IDX_MASK, + rx_gain->fgt_lmt_index); + if (rc < 0) + goto out; + rc = ad9361_spi_writef(spi, idx_reg + 1, RX_LPF_IDX_MASK, rx_gain->lpf_gain); + if (rc < 0) + goto out; + if (phy->pdata->gain_ctrl.dig_gain_en) { + rc = ad9361_spi_writef(spi, idx_reg + 2, RX_DIGITAL_IDX_MASK, + rx_gain->digital_gain); + } else if (rx_gain->digital_gain > 0) { + dev_err(dev, "Digital gain is disabled and cannot be set"); + } +out: + return rc; +} + +/** + * Set gain in Full Gain Table Mode (used only in Manual Gain Control Mode). + * @param phy The AD9361 state structure. + * @param idx_reg Register base address for the selected receiver + * @param rx_gain The rf_rx_gain struct containing the RF gain. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t set_full_table_gain(struct ad9361_rf_phy *phy, uint32_t idx_reg, + struct rf_rx_gain *rx_gain) +{ + struct spi_desc *spi = phy->spi; + int rc = 0; + + if (rx_gain->fgt_lmt_index != ((uint32_t)~0) || + rx_gain->lpf_gain != ((uint32_t)~0) || + rx_gain->digital_gain > 0) + dev_dbg(dev, + "Ignoring lmt/lpf/digital gains in Single Table mode"); + + rc = find_table_index(phy, rx_gain->gain_db); + if (rc < 0) { + dev_err(dev, "Invalid gain %"PRId32", supported range [%d - %d]\n", + rx_gain->gain_db, phy->gt_info[ad9361_gt(phy)]. + abs_gain_tbl[0], + phy->gt_info[ad9361_gt(phy)].abs_gain_tbl + [phy->gt_info[ad9361_gt(phy)].max_index - 1]); + goto out; + } + + rc = ad9361_spi_writef(spi, idx_reg, RX_FULL_TBL_IDX_MASK, rc); +out: + return rc; +} + +/** + * Set the RX gain for the selected channel. + * @param phy The AD9361 state structure. + * @param rx_id The desired channel number (0, 1). + * @param rx_gain The rf_rx_gain struct containing the RF gain. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_rx_gain(struct ad9361_rf_phy *phy, + uint32_t rx_id, struct rf_rx_gain *rx_gain) +{ + struct spi_desc *spi = phy->spi; + uint32_t val, idx_reg; + uint8_t gain_ctl_shift; + int32_t rc = 0; + + if (rx_id == 1) { + gain_ctl_shift = RX1_GAIN_CTRL_SHIFT; + idx_reg = REG_RX1_MANUAL_LMT_FULL_GAIN; + + } else if (rx_id == 2) { + gain_ctl_shift = RX2_GAIN_CTRL_SHIFT; + idx_reg = REG_RX2_MANUAL_LMT_FULL_GAIN; + } else { + dev_err(dev, "Unknown Rx path %"PRIu32, rx_id); + rc = -EINVAL; + goto out; + + } + + val = ad9361_spi_read(spi, REG_AGC_CONFIG_1); + val = (val >> gain_ctl_shift) & RX_GAIN_CTL_MASK; + + if (val != RX_GAIN_CTL_MGC) { + dev_dbg(dev, "Rx gain can be set in MGC mode only"); + rc = -EOPNOTSUPP; + goto out; + } + + if (has_split_gt && phy->pdata->split_gt) + rc = set_split_table_gain(phy, idx_reg, rx_gain); + else + rc = set_full_table_gain(phy, idx_reg, rx_gain); + + if (rc) { + dev_err(dev, "Unable to write gain tbl idx reg: %"PRIu32, idx_reg); + goto out; + } + +out: + return rc; + +} + +/** + * Update the Gain Control. + * @param phy The AD9361 state structure. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_gc_update(struct ad9361_rf_phy *phy) +{ + struct spi_desc *spi = phy->spi; + uint32_t clkrf; + uint32_t reg, delay_lna, settling_delay, dec_pow_meas_dur; + int32_t ret; + uint32_t fir_div; + + clkrf = clk_get_rate(phy, phy->ref_clk_scale[CLKRF_CLK]); + delay_lna = phy->pdata->elna_ctrl.settling_delay_ns; + + /* + * AGC Attack Delay (us)=ceiling((((0.2+Delay_LNA)*ClkRF+14))/(2*ClkRF))+1 + * ClkRF in MHz, delay in us + */ + + reg = (200 + delay_lna) / 2 + (14000000UL / (clkrf / 500U)); + reg = DIV_ROUND_UP(reg, 1000UL) + + phy->pdata->gain_ctrl.agc_attack_delay_extra_margin_us; + reg = clamp_t(uint8_t, reg, 0U, 31U); + ret = ad9361_spi_writef(spi, REG_AGC_ATTACK_DELAY, + AGC_ATTACK_DELAY(~0), reg); + + /* + * Peak Overload Wait Time (ClkRF cycles)=ceiling((0.1+Delay_LNA) *clkRF+1) + */ + + reg = (delay_lna + 100UL) * (clkrf / 1000UL); + reg = DIV_ROUND_UP(reg, 1000000UL) + 1; + reg = clamp_t(uint8_t, reg, 0U, 31U); + ret |= ad9361_spi_writef(spi, REG_PEAK_WAIT_TIME, + PEAK_OVERLOAD_WAIT_TIME(~0), reg); + + /* + * Settling Delay in 0x111. Applies to all gain control modes: + * 0x111[D4:D0]= ceiling(((0.2+Delay_LNA)*clkRF + dodebug = false;+14)/2) + */ + + reg = (delay_lna + 200UL) * (clkrf / 2000UL); + reg = DIV_ROUND_UP(reg, 1000000UL) + 7; + reg = settling_delay = clamp_t(uint8_t, reg, 0U, 31U); + ret |= ad9361_spi_writef(spi, REG_FAST_CONFIG_2_SETTLING_DELAY, + SETTLING_DELAY(~0), reg); + + /* + * Gain Update Counter [15:0]= round((((time*ClkRF-0x111[D4:D0]*2)-2))/2) + */ + reg = phy->pdata->gain_ctrl.gain_update_interval_us * (clkrf / 1000UL) - + settling_delay * 2000UL - 2000UL; + + reg = DIV_ROUND_CLOSEST(reg, 2000UL); + reg = clamp_t(uint32_t, reg, 0U, 131071UL); + + if (phy->agc_mode[0] == RF_GAIN_FASTATTACK_AGC || + phy->agc_mode[1] == RF_GAIN_FASTATTACK_AGC) { + dec_pow_meas_dur = + phy->pdata->gain_ctrl.f_agc_dec_pow_measuremnt_duration; + } else { + fir_div = DIV_ROUND_CLOSEST(clkrf, + clk_get_rate(phy, phy->ref_clk_scale[RX_SAMPL_CLK])); + dec_pow_meas_dur = phy->pdata->gain_ctrl.dec_pow_measuremnt_duration; + + if (((reg * 2 / fir_div) / dec_pow_meas_dur) < 2) { + dec_pow_meas_dur = reg / fir_div; + } + } + + /* Power Measurement Duration */ + ad9361_spi_writef(spi, REG_DEC_POWER_MEASURE_DURATION_0, + DEC_POWER_MEASUREMENT_DURATION(~0), + ilog2(dec_pow_meas_dur / 16)); + + + ret |= ad9361_spi_writef(spi, REG_DIGITAL_SAT_COUNTER, + DOUBLE_GAIN_COUNTER, reg > 65535); + + if (reg > 65535) + reg /= 2; + + ret |= ad9361_spi_write(spi, REG_GAIN_UPDATE_COUNTER1, reg & 0xFF); + ret |= ad9361_spi_write(spi, REG_GAIN_UPDATE_COUNTER2, reg >> 8); + + /* + * Fast AGC State Wait Time - Energy Detect Count + */ + + reg = DIV_ROUND_CLOSEST(phy->pdata->gain_ctrl.f_agc_state_wait_time_ns * + (clkrf / 1000UL), 1000000UL); + reg = clamp_t(uint32_t, reg, 0U, 31U); + ret |= ad9361_spi_writef(spi, REG_FAST_ENERGY_DETECT_COUNT, + ENERGY_DETECT_COUNT(~0), reg); + + return ret; +} + +/** + * Set the gain control mode. + * @param phy The AD9361 state structure. + * @param gain_ctrl A rf_gain_ctrl struct that contains the the desired + * channel information and the gain control mode. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_gain_ctrl_mode(struct ad9361_rf_phy *phy, + struct rf_gain_ctrl *gain_ctrl) +{ + struct spi_desc *spi = phy->spi; + int32_t rc = 0; + uint32_t gain_ctl_shift, mode; + uint8_t val; + + rc = ad9361_spi_readm(spi, REG_AGC_CONFIG_1, &val, 1); + if (rc) { + dev_err(dev, "Unable to read AGC config1 register: %x", + REG_AGC_CONFIG_1); + goto out; + } + + switch (gain_ctrl->mode) { + case RF_GAIN_MGC: + mode = RX_GAIN_CTL_MGC; + break; + case RF_GAIN_FASTATTACK_AGC: + mode = RX_GAIN_CTL_AGC_FAST_ATK; + break; + case RF_GAIN_SLOWATTACK_AGC: + mode = RX_GAIN_CTL_AGC_SLOW_ATK; + break; + case RF_GAIN_HYBRID_AGC: + mode = RX_GAIN_CTL_AGC_SLOW_ATK_HYBD; + break; + default: + rc = -EINVAL; + goto out; + } + + if (gain_ctrl->ant == 1) { + gain_ctl_shift = RX1_GAIN_CTRL_SHIFT; + } else if (gain_ctrl->ant == 2) { + gain_ctl_shift = RX2_GAIN_CTRL_SHIFT; + } else { + dev_err(dev, "Unknown Rx path %"PRIu32, gain_ctrl->ant); + rc = -EINVAL; + goto out; + } + + rc = ad9361_en_dis_rx(phy, gain_ctrl->ant, RX_DISABLE); + if (rc) { + dev_err(dev, "Unable to disable rx%"PRIu32, gain_ctrl->ant); + goto out; + } + + val &= ~(RX_GAIN_CTL_MASK << gain_ctl_shift); + val |= mode << gain_ctl_shift; + if (mode == RX_GAIN_CTL_AGC_SLOW_ATK_HYBD) + val |= SLOW_ATTACK_HYBRID_MODE; + else + val &= ~SLOW_ATTACK_HYBRID_MODE; + + rc = ad9361_spi_write(spi, REG_AGC_CONFIG_1, val); + if (rc) { + dev_err(dev, "Unable to write AGC config1 register: %x", + REG_AGC_CONFIG_1); + goto out; + } + + ad9361_en_dis_rx(phy, gain_ctrl->ant, RX_ENABLE); + rc = ad9361_gc_update(phy); +out: + return rc; +} + +/** + * Get the RSSI. + * @param phy The AD9361 state structure. + * @param rssi A rf_rssi struct to store the RSSI. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_read_rssi(struct ad9361_rf_phy *phy, struct rf_rssi *rssi) +{ + struct spi_desc *spi = phy->spi; + uint8_t reg_val_buf[6]; + int32_t rc; + + rc = ad9361_spi_readm(spi, REG_PREAMBLE_LSB, + reg_val_buf, ARRAY_SIZE(reg_val_buf)); + if (rssi->ant == 1) { + rssi->symbol = RSSI_RESOLUTION * + ((reg_val_buf[5] << RSSI_LSB_SHIFT) + + (reg_val_buf[1] & RSSI_LSB_MASK1)); + rssi->preamble = RSSI_RESOLUTION * + ((reg_val_buf[4] << RSSI_LSB_SHIFT) + + (reg_val_buf[0] & RSSI_LSB_MASK1)); + } else if (rssi->ant == 2) { + rssi->symbol = RSSI_RESOLUTION * + ((reg_val_buf[3] << RSSI_LSB_SHIFT) + + ((reg_val_buf[1] & RSSI_LSB_MASK2) >> 1)); + rssi->preamble = RSSI_RESOLUTION * + ((reg_val_buf[2] << RSSI_LSB_SHIFT) + + ((reg_val_buf[0] & RSSI_LSB_MASK2) >> 1)); + } else + rc = -EFAULT; + + rssi->multiplier = RSSI_MULTIPLIER; + + return rc; +} + +/** + * Setup the RX ADC. + * @param phy The AD9361 state structure. + * @param bbpll_freq The BBPLL frequency [Hz]. + * @param adc_sampl_freq_Hz The ADC sampling frequency [Hz]. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_rx_adc_setup(struct ad9361_rf_phy *phy, + uint32_t bbpll_freq, + uint32_t adc_sampl_freq_Hz) +{ + + uint32_t scale_snr_1e3, maxsnr, sqrt_inv_rc_tconst_1e3, tmp_1e3, + scaled_adc_clk_1e6, inv_scaled_adc_clk_1e3, sqrt_term_1e3, + min_sqrt_term_1e3, bb_bw_Hz; + uint64_t tmp, invrc_tconst_1e6; + uint8_t data[40]; + uint32_t i; + int32_t ret; + + uint8_t c3_msb = ad9361_spi_read(phy->spi, REG_RX_BBF_C3_MSB); + uint8_t c3_lsb = ad9361_spi_read(phy->spi, REG_RX_BBF_C3_LSB); + uint8_t r2346 = ad9361_spi_read(phy->spi, REG_RX_BBF_R2346); + + /* + * BBBW = (BBPLL / RxTuneDiv) * ln(2) / (1.4 * 2PI ) + * We assume ad9361_rx_bb_analog_filter_calib() is always run prior + */ + + tmp = bbpll_freq * 10000ULL; + do_div(&tmp, 126906UL * phy->rxbbf_div); + bb_bw_Hz = tmp; + + dev_dbg(&phy->spi->dev, "%s : BBBW %"PRIu32" : ADCfreq %"PRIu32, + __func__, bb_bw_Hz, adc_sampl_freq_Hz); + + dev_dbg(&phy->spi->dev, "c3_msb 0x%X : c3_lsb 0x%X : r2346 0x%X : ", + c3_msb, c3_lsb, r2346); + + bb_bw_Hz = clamp(bb_bw_Hz, 200000UL, 28000000UL); + + if (adc_sampl_freq_Hz < 80000000) + scale_snr_1e3 = 1000; + else + scale_snr_1e3 = 1585; /* pow(10, scale_snr_dB/10); */ + + if (bb_bw_Hz >= 18000000) { + invrc_tconst_1e6 = (160975ULL * r2346 * + (160 * c3_msb + 10 * c3_lsb + 140) * + (bb_bw_Hz)* (1000 + (10 * (bb_bw_Hz - 18000000) / 1000000))); + + do_div(&invrc_tconst_1e6, 1000UL); + + } else { + invrc_tconst_1e6 = (160975ULL * r2346 * + (160 * c3_msb + 10 * c3_lsb + 140) * + (bb_bw_Hz)); + } + + do_div(&invrc_tconst_1e6, 1000000000UL); + + if (invrc_tconst_1e6 > ULONG_MAX) + dev_err(&phy->spi->dev, "invrc_tconst_1e6 > ULONG_MAX"); + + sqrt_inv_rc_tconst_1e3 = int_sqrt((uint32_t)invrc_tconst_1e6); + maxsnr = 640 / 160; + scaled_adc_clk_1e6 = DIV_ROUND_CLOSEST(adc_sampl_freq_Hz, 640); + inv_scaled_adc_clk_1e3 = DIV_ROUND_CLOSEST(640000000, + DIV_ROUND_CLOSEST(adc_sampl_freq_Hz, 1000)); + tmp_1e3 = DIV_ROUND_CLOSEST(980000 + 20 * max_t(uint32_t, 1000U, + DIV_ROUND_CLOSEST(inv_scaled_adc_clk_1e3, maxsnr)), 1000); + sqrt_term_1e3 = int_sqrt(scaled_adc_clk_1e6); + min_sqrt_term_1e3 = min_t(uint32_t, 1000U, + int_sqrt(maxsnr * scaled_adc_clk_1e6)); + + dev_dbg(&phy->spi->dev, + "invrc_tconst_1e6 %"PRIu64", sqrt_inv_rc_tconst_1e3 %"PRIu32, + invrc_tconst_1e6, sqrt_inv_rc_tconst_1e3); + dev_dbg(&phy->spi->dev, + "scaled_adc_clk_1e6 %"PRIu32", inv_scaled_adc_clk_1e3 %"PRIu32, + scaled_adc_clk_1e6, inv_scaled_adc_clk_1e3); + dev_dbg(&phy->spi->dev, + "tmp_1e3 %"PRIu32", sqrt_term_1e3 %"PRIu32", min_sqrt_term_1e3 %"PRIu32, + tmp_1e3, sqrt_term_1e3, min_sqrt_term_1e3); + + data[0] = 0; + data[1] = 0; + data[2] = 0; + data[3] = 0x24; + data[4] = 0x24; + data[5] = 0; + data[6] = 0; + + tmp = -50000000 + 8ULL * scale_snr_1e3 * sqrt_inv_rc_tconst_1e3 * + min_sqrt_term_1e3; + do_div(&tmp, 100000000UL); + data[7] = min_t(uint64_t, 124U, tmp); + + tmp = (invrc_tconst_1e6 >> 1) + 20 * inv_scaled_adc_clk_1e3 * + data[7] / 80 * 1000ULL; + do_div(&tmp, invrc_tconst_1e6); + data[8] = min_t(uint64_t, 255U, tmp); + + tmp = (-500000 + 77ULL * sqrt_inv_rc_tconst_1e3 * min_sqrt_term_1e3); + do_div(&tmp, 1000000UL); + data[10] = min_t(uint64_t, 127U, tmp); + + data[9] = min_t(uint32_t, 127U, ((800 * data[10]) / 1000)); + tmp = ((invrc_tconst_1e6 >> 1) + (20 * inv_scaled_adc_clk_1e3 * + data[10] * 1000ULL)); + do_div(&tmp, invrc_tconst_1e6 * 77); + data[11] = min_t(uint64_t, 255U, tmp); + data[12] = min_t(uint32_t, 127U, (-500000 + 80 * sqrt_inv_rc_tconst_1e3 * + min_sqrt_term_1e3) / 1000000UL); + + tmp = -3 * (long)(invrc_tconst_1e6 >> 1) + inv_scaled_adc_clk_1e3 * + data[12] * (1000ULL * 20 / 80); + do_div(&tmp, invrc_tconst_1e6); + data[13] = min_t(uint64_t, 255, tmp); + + data[14] = 21 * (inv_scaled_adc_clk_1e3 / 10000); + data[15] = min_t(uint32_t, 127U, (500 + 1025 * data[7]) / 1000); + data[16] = min_t(uint32_t, 127U, (data[15] * tmp_1e3) / 1000); + data[17] = data[15]; + data[18] = min_t(uint32_t, 127U, (500 + 975 * data[10]) / 1000); + data[19] = min_t(uint32_t, 127U, (data[18] * tmp_1e3) / 1000); + data[20] = data[18]; + data[21] = min_t(uint32_t, 127U, (500 + 975 * data[12]) / 1000); + data[22] = min_t(uint32_t, 127, (data[21] * tmp_1e3) / 1000); + data[23] = data[21]; + data[24] = 0x2E; + data[25] = (128 + min_t(uint32_t, 63000U, DIV_ROUND_CLOSEST(63 * + scaled_adc_clk_1e6, 1000)) / 1000); + data[26] = min_t(uint32_t, 63U, 63 * scaled_adc_clk_1e6 / 1000000 * + (920 + 80 * inv_scaled_adc_clk_1e3 / 1000) / 1000); + data[27] = min_t(uint32_t, 63, (32 * sqrt_term_1e3) / 1000); + data[28] = data[25]; + data[29] = data[26]; + data[30] = data[27]; + data[31] = data[25]; + data[32] = data[26]; + data[33] = min_t(uint32_t, 63U, 63 * sqrt_term_1e3 / 1000); + data[34] = min_t(uint32_t, 127U, 64 * sqrt_term_1e3 / 1000); + data[35] = 0x40; + data[36] = 0x40; + data[37] = 0x2C; + data[38] = 0x00; + data[39] = 0x00; + + for (i = 0; i < 40; i++) { + ret = ad9361_spi_write(phy->spi, 0x200 + i, data[i]); + if (ret < 0) + return ret; + } + + return 0; +} + +/** + * Perform a RX TIA calibration. + * @param phy The AD9361 state structure. + * @param bb_bw_Hz The baseband bandwidth [Hz]. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_rx_tia_calib(struct ad9361_rf_phy *phy, uint32_t bb_bw_Hz) +{ + uint32_t Cbbf, R2346; + uint64_t CTIA_fF; + + uint8_t reg1EB = ad9361_spi_read(phy->spi, REG_RX_BBF_C3_MSB); + uint8_t reg1EC = ad9361_spi_read(phy->spi, REG_RX_BBF_C3_LSB); + uint8_t reg1E6 = ad9361_spi_read(phy->spi, REG_RX_BBF_R2346); + uint8_t reg1DB, reg1DF, reg1DD, reg1DC, reg1DE, temp; + + dev_dbg(&phy->spi->dev, "%s : bb_bw_Hz %"PRIu32, + __func__, bb_bw_Hz); + + bb_bw_Hz = clamp(bb_bw_Hz, 200000UL, 20000000UL); + + Cbbf = (reg1EB * 160) + (reg1EC * 10) + 140; /* fF */ + R2346 = 18300 * RX_BBF_R2346(reg1E6); + + CTIA_fF = Cbbf * R2346 * 560ULL; + do_div(&CTIA_fF, 3500000UL); + + if (bb_bw_Hz <= 3000000UL) + reg1DB = 0xE0; + else if (bb_bw_Hz <= 10000000UL) + reg1DB = 0x60; + else + reg1DB = 0x20; + + if (CTIA_fF > 2920ULL) { + reg1DC = 0x40; + reg1DE = 0x40; + temp = min(127U, DIV_ROUND_CLOSEST((uint32_t)CTIA_fF - 400, 320U)); + reg1DD = temp; + reg1DF = temp; + } else { + temp = DIV_ROUND_CLOSEST((uint32_t)CTIA_fF - 400, 40U) + 0x40; + reg1DC = temp; + reg1DE = temp; + reg1DD = 0; + reg1DF = 0; + } + + ad9361_spi_write(phy->spi, REG_RX_TIA_CONFIG, reg1DB); + ad9361_spi_write(phy->spi, REG_TIA1_C_LSB, reg1DC); + ad9361_spi_write(phy->spi, REG_TIA1_C_MSB, reg1DD); + ad9361_spi_write(phy->spi, REG_TIA2_C_LSB, reg1DE); + ad9361_spi_write(phy->spi, REG_TIA2_C_MSB, reg1DF); + + return 0; +} + +/** + * Perform a baseband RX analog filter calibration. + * @param phy The AD9361 state structure. + * @param rx_bb_bw The baseband bandwidth [Hz]. + * @param bbpll_freq The BBPLL frequency [Hz]. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_rx_bb_analog_filter_calib(struct ad9361_rf_phy *phy, + uint32_t rx_bb_bw, + uint32_t bbpll_freq) +{ + uint32_t target; + uint8_t tmp; + int32_t ret; + + dev_dbg(&phy->spi->dev, "%s : rx_bb_bw %"PRIu32" bbpll_freq %"PRIu32, + __func__, rx_bb_bw, bbpll_freq); + + rx_bb_bw = clamp(rx_bb_bw, 200000UL, 28000000UL); + + /* 1.4 * BBBW * 2PI / ln(2) */ + target = 126906UL * (rx_bb_bw / 10000UL); + phy->rxbbf_div = min_t(uint32_t, 511UL, DIV_ROUND_UP(bbpll_freq, target)); + + /* Set RX baseband filter divide value */ + ad9361_spi_write(phy->spi, REG_RX_BBF_TUNE_DIVIDE, phy->rxbbf_div); + ad9361_spi_writef(phy->spi, REG_RX_BBF_TUNE_CONFIG, BIT(0), + phy->rxbbf_div >> 8); + + /* Write the BBBW into registers 0x1FB and 0x1FC */ + ad9361_spi_write(phy->spi, REG_RX_BBBW_MHZ, rx_bb_bw / 1000000UL); + + tmp = DIV_ROUND_CLOSEST((rx_bb_bw % 1000000UL) * 128, 1000000UL); + ad9361_spi_write(phy->spi, REG_RX_BBBW_KHZ, min_t(uint8_t, 127, tmp)); + + ad9361_spi_write(phy->spi, REG_RX_MIX_LO_CM, + RX_MIX_LO_CM(0x3F)); /* Set Rx Mix LO CM */ + ad9361_spi_write(phy->spi, REG_RX_MIX_GM_CONFIG, + RX_MIX_GM_PLOAD(3)); /* Set GM common mode */ + + /* Enable the RX BBF tune circuit by writing 0x1E2=0x02 and 0x1E3=0x02 */ + ad9361_spi_write(phy->spi, REG_RX1_TUNE_CTRL, RX1_TUNE_RESAMPLE); + ad9361_spi_write(phy->spi, REG_RX2_TUNE_CTRL, RX2_TUNE_RESAMPLE); + + /* Start the RX Baseband Filter calibration in register 0x016[7] */ + /* Calibration is complete when register 0x016[7] self clears */ + ret = ad9361_run_calibration(phy, RX_BB_TUNE_CAL); + + /* Disable the RX baseband filter tune circuit, write 0x1E2=3, 0x1E3=3 */ + ad9361_spi_write(phy->spi, REG_RX1_TUNE_CTRL, + RX1_TUNE_RESAMPLE | RX1_PD_TUNE); + ad9361_spi_write(phy->spi, REG_RX2_TUNE_CTRL, + RX2_TUNE_RESAMPLE | RX2_PD_TUNE); + + return ret; +} + +/** + * Perform a baseband TX analog filter calibration. + * @param phy The AD9361 state structure. + * @param tx_bb_bw The baseband bandwidth [Hz]. + * @param bbpll_freq The BBPLL frequency [Hz]. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_tx_bb_analog_filter_calib(struct ad9361_rf_phy *phy, + uint32_t tx_bb_bw, + uint32_t bbpll_freq) +{ + uint32_t target, txbbf_div; + int32_t ret; + + dev_dbg(&phy->spi->dev, "%s : tx_bb_bw %"PRIu32" bbpll_freq %"PRIu32, + __func__, tx_bb_bw, bbpll_freq); + + tx_bb_bw = clamp(tx_bb_bw, 625000UL, 20000000UL); + + /* 1.6 * BBBW * 2PI / ln(2) */ + target = 145036 * (tx_bb_bw / 10000UL); + txbbf_div = min_t(uint32_t, 511UL, DIV_ROUND_UP(bbpll_freq, target)); + + /* Set TX baseband filter divide value */ + ad9361_spi_write(phy->spi, REG_TX_BBF_TUNE_DIVIDER, txbbf_div); + ad9361_spi_writef(phy->spi, REG_TX_BBF_TUNE_MODE, + TX_BBF_TUNE_DIVIDER, txbbf_div >> 8); + + /* Enable the TX baseband filter tune circuit by setting 0x0CA=0x22. */ + ad9361_spi_write(phy->spi, REG_TX_TUNE_CTRL, TUNER_RESAMPLE | TUNE_CTRL(1)); + + /* Start the TX Baseband Filter calibration in register 0x016[6] */ + /* Calibration is complete when register 0x016[] self clears */ + ret = ad9361_run_calibration(phy, TX_BB_TUNE_CAL); + + /* Disable the TX baseband filter tune circuit by writing 0x0CA=0x26. */ + ad9361_spi_write(phy->spi, REG_TX_TUNE_CTRL, + TUNER_RESAMPLE | TUNE_CTRL(1) | PD_TUNE); + + return ret; +} + +/** + * Perform a baseband TX secondary filter calibration. + * @param phy The AD9361 state structure. + * @param tx_rf_bw The RF bandwidth [Hz]. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_tx_bb_second_filter_calib(struct ad9361_rf_phy *phy, + uint32_t tx_bb_bw) +{ + uint64_t cap; + uint32_t corner, res, div; + uint32_t reg_conf, reg_res; + int32_t ret, i; + + dev_dbg(&phy->spi->dev, "%s : tx_bb_bw %"PRIu32, + __func__, tx_bb_bw); + + tx_bb_bw = clamp(tx_bb_bw, 530000UL, 20000000UL); + + /* BBBW * 5PI */ + corner = 15708 * (tx_bb_bw / 10000UL); + + for (i = 0, res = 1; i < 4; i++) { + div = corner * res; + cap = (500000000ULL) + (div >> 1); + do_div(&cap, div); + cap -= 12ULL; + if (cap < 64ULL) + break; + + res <<= 1; + } + + if (cap > 63ULL) + cap = 63ULL; + + if (tx_bb_bw <= 4500000UL) + reg_conf = 0x59; + else if (tx_bb_bw <= 12000000UL) + reg_conf = 0x56; + else + reg_conf = 0x57; + + switch (res) { + case 1: + reg_res = 0x0C; + break; + case 2: + reg_res = 0x04; + break; + case 4: + reg_res = 0x03; + break; + case 8: + reg_res = 0x01; + break; + default: + reg_res = 0x01; + } + + ret = ad9361_spi_write(phy->spi, REG_CONFIG0, reg_conf); + ret |= ad9361_spi_write(phy->spi, REG_RESISTOR, reg_res); + ret |= ad9361_spi_write(phy->spi, REG_CAPACITOR, (uint8_t)cap); + + return ret; +} + +/** + * Perform a RF synthesizer charge pump calibration. + * @param phy The AD9361 state structure. + * @param ref_clk_hz The reference clock rate [Hz]. + * @param tx The Synthesizer TX = 1, RX = 0. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_txrx_synth_cp_calib(struct ad9361_rf_phy *phy, + uint32_t ref_clk_hz, bool tx) +{ + uint32_t offs = tx ? 0x40 : 0; + uint32_t vco_cal_cnt; + dev_dbg(&phy->spi->dev, "%s : ref_clk_hz %"PRIu32" : is_tx %d", + __func__, ref_clk_hz, tx); + + /* REVIST: */ + ad9361_spi_write(phy->spi, REG_RX_CP_LEVEL_DETECT + offs, 0x17); + + ad9361_spi_write(phy->spi, REG_RX_DSM_SETUP_1 + offs, 0x0); + + ad9361_spi_write(phy->spi, REG_RX_LO_GEN_POWER_MODE + offs, 0x00); + ad9361_spi_write(phy->spi, REG_RX_VCO_LDO + offs, 0x0B); + ad9361_spi_write(phy->spi, REG_RX_VCO_PD_OVERRIDES + offs, 0x02); + ad9361_spi_write(phy->spi, REG_RX_CP_CURRENT + offs, 0x80); + ad9361_spi_write(phy->spi, REG_RX_CP_CONFIG + offs, CP_OFFSET_OFF); + + /* see Table 70 Example Calibration Times for RF VCO Cal */ + if (phy->pdata->fdd) { + vco_cal_cnt = VCO_CAL_EN | VCO_CAL_COUNT(3) | FB_CLOCK_ADV(2); + } else { + if (ref_clk_hz > 40000000UL) + vco_cal_cnt = VCO_CAL_EN | VCO_CAL_COUNT(1) | + FB_CLOCK_ADV(2); + else + vco_cal_cnt = VCO_CAL_EN | VCO_CAL_COUNT(0) | + FB_CLOCK_ADV(2); + } + + ad9361_spi_write(phy->spi, REG_RX_VCO_CAL + offs, vco_cal_cnt); + + /* Enable FDD mode during calibrations */ + + if (!phy->pdata->fdd) { + ad9361_spi_writef(phy->spi, REG_PARALLEL_PORT_CONF_3, + HALF_DUPLEX_MODE, 0); + } + + ad9361_spi_write(phy->spi, REG_ENSM_CONFIG_2, DUAL_SYNTH_MODE); + ad9361_spi_write(phy->spi, REG_ENSM_CONFIG_1, + FORCE_ALERT_STATE | + TO_ALERT); + ad9361_spi_write(phy->spi, REG_ENSM_MODE, FDD_MODE); + + ad9361_spi_write(phy->spi, REG_RX_CP_CONFIG + offs, + CP_OFFSET_OFF | CP_CAL_ENABLE); + + return ad9361_check_cal_done(phy, REG_RX_CAL_STATUS + offs, + CP_CAL_VALID, 1); +} + +/** + * Perform a baseband DC offset calibration. + * @param phy The AD9361 state structure. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_bb_dc_offset_calib(struct ad9361_rf_phy *phy) +{ + dev_dbg(&phy->spi->dev, "%s", __func__); + + ad9361_spi_write(phy->spi, REG_BB_DC_OFFSET_COUNT, 0x3F); + ad9361_spi_write(phy->spi, REG_BB_DC_OFFSET_SHIFT, BB_DC_M_SHIFT(0xF)); + ad9361_spi_write(phy->spi, REG_BB_DC_OFFSET_ATTEN, BB_DC_OFFSET_ATTEN(1)); + + return ad9361_run_calibration(phy, BBDC_CAL); +} + +/** + * Perform a RF DC offset calibration. + * @param phy The AD9361 state structure. + * @param ref_clk_hz The RX LO frequency [Hz]. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_rf_dc_offset_calib(struct ad9361_rf_phy *phy, + uint64_t rx_freq) +{ + struct spi_desc *spi = phy->spi; + + dev_dbg(&phy->spi->dev, "%s : rx_freq %"PRIu64, + __func__, rx_freq); + + ad9361_spi_write(spi, REG_WAIT_COUNT, 0x20); + + if (rx_freq <= 4000000000ULL) { + ad9361_spi_write(spi, REG_RF_DC_OFFSET_COUNT, + phy->pdata->rf_dc_offset_count_low); + ad9361_spi_write(spi, REG_RF_DC_OFFSET_CONFIG_1, + RF_DC_CALIBRATION_COUNT(4) | DAC_FS(2)); + ad9361_spi_write(spi, REG_RF_DC_OFFSET_ATTEN, + RF_DC_OFFSET_ATTEN( + phy->pdata->dc_offset_attenuation_low)); + } else { + ad9361_spi_write(spi, REG_RF_DC_OFFSET_COUNT, + phy->pdata->rf_dc_offset_count_high); + ad9361_spi_write(spi, REG_RF_DC_OFFSET_CONFIG_1, + RF_DC_CALIBRATION_COUNT(4) | DAC_FS(3)); + ad9361_spi_write(spi, REG_RF_DC_OFFSET_ATTEN, + RF_DC_OFFSET_ATTEN( + phy->pdata->dc_offset_attenuation_high)); + } + + ad9361_spi_write(spi, REG_DC_OFFSET_CONFIG2, + USE_WAIT_COUNTER_FOR_RF_DC_INIT_CAL | + DC_OFFSET_UPDATE(3)); + + if (phy->pdata->rx1rx2_phase_inversion_en || + (phy->pdata->port_ctrl.pp_conf[1] & INVERT_RX2)) { + ad9361_spi_write(spi, REG_INVERT_BITS, + INVERT_RX1_RF_DC_CGOUT_WORD); + } else { + ad9361_spi_write(spi, REG_INVERT_BITS, + INVERT_RX1_RF_DC_CGOUT_WORD | + INVERT_RX2_RF_DC_CGOUT_WORD); + } + + return ad9361_run_calibration(phy, RFDC_CAL); +} + +/** + * Update RF bandwidth. + * @param phy The AD9361 state structure. + * @param rf_rx_bw RF RX bandwidth [Hz]. + * @param rf_tx_bw RF TX bandwidth [Hz]. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t __ad9361_update_rf_bandwidth(struct ad9361_rf_phy *phy, + uint32_t rf_rx_bw, uint32_t rf_tx_bw) +{ + uint32_t real_rx_bandwidth = rf_rx_bw / 2; + uint32_t real_tx_bandwidth = rf_tx_bw / 2; + uint32_t bbpll_freq; + int32_t ret; + + dev_dbg(&phy->spi->dev, "%s: %"PRIu32" %"PRIu32, + __func__, rf_rx_bw, rf_tx_bw); + + bbpll_freq = clk_get_rate(phy, phy->ref_clk_scale[BBPLL_CLK]); + + ret = ad9361_rx_bb_analog_filter_calib(phy, + real_rx_bandwidth, + bbpll_freq); + if (ret < 0) + return ret; + + ret = ad9361_tx_bb_analog_filter_calib(phy, + real_tx_bandwidth, + bbpll_freq); + if (ret < 0) + return ret; + + ret = ad9361_rx_tia_calib(phy, real_rx_bandwidth); + if (ret < 0) + return ret; + + ret = ad9361_tx_bb_second_filter_calib(phy, real_tx_bandwidth); + if (ret < 0) + return ret; + + ret = ad9361_rx_adc_setup(phy, + bbpll_freq, + clk_get_rate(phy, phy->ref_clk_scale[ADC_CLK])); + if (ret < 0) + return ret; + + return 0; +} + +/** + * TX Quad Calib. + * @param phy The AD9361 state structure. + * @param phase phase + * @param rxnco_word Rx NCO word. + * @param decim decim + * @param res res + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t __ad9361_tx_quad_calib(struct ad9361_rf_phy *phy, uint32_t phase, + uint32_t rxnco_word, uint32_t decim, uint8_t *res) +{ + int32_t ret; + + ad9361_spi_write(phy->spi, REG_QUAD_CAL_NCO_FREQ_PHASE_OFFSET, + RX_NCO_FREQ(rxnco_word) | RX_NCO_PHASE_OFFSET(phase)); + ad9361_spi_write(phy->spi, REG_QUAD_CAL_CTRL, + SETTLE_MAIN_ENABLE | DC_OFFSET_ENABLE | QUAD_CAL_SOFT_RESET | + GAIN_ENABLE | PHASE_ENABLE | M_DECIM(decim)); + ad9361_spi_write(phy->spi, REG_QUAD_CAL_CTRL, + SETTLE_MAIN_ENABLE | DC_OFFSET_ENABLE | + GAIN_ENABLE | PHASE_ENABLE | M_DECIM(decim)); + + ret = ad9361_run_calibration(phy, TX_QUAD_CAL); + if (ret < 0) + return ret; + + if (res) { + *res = ad9361_spi_read(phy->spi, + (phy->pdata->rx1tx1_mode_use_tx_num == 2) ? + REG_QUAD_CAL_STATUS_TX2 : REG_QUAD_CAL_STATUS_TX1) & + (TX1_LO_CONV | TX1_SSB_CONV); + if (phy->pdata->rx2tx2) + *res &= ad9361_spi_read(phy->spi, REG_QUAD_CAL_STATUS_TX2) & + (TX2_LO_CONV | TX2_SSB_CONV); + } + + return 0; +} + +/** + * Loop through all possible phase offsets in case the QUAD CAL doesn't converge. + * @param phy The AD9361 state structure. + * @param rxnco_word Rx NCO word. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_tx_quad_phase_search(struct ad9361_rf_phy *phy, + uint32_t rxnco_word, uint8_t decim) +{ + int32_t i, ret; + uint8_t field[64], val; + uint32_t start; + + dev_dbg(&phy->spi->dev, "%s", __func__); + + for (i = 0; i < (int64_t)(ARRAY_SIZE(field) / 2); i++) { + ret = __ad9361_tx_quad_calib(phy, i, rxnco_word, decim, &val); + if (ret < 0) + return ret; + + /* Handle 360/0 wrap around */ + field[i] = field[i + 32] = !((val & TX1_LO_CONV) && (val & TX1_SSB_CONV)); + } + + ret = ad9361_find_opt(field, ARRAY_SIZE(field), &start); + + phy->last_tx_quad_cal_phase = (start + ret / 2) & 0x1F; + +#ifdef _DEBUG + for (i = 0; i < 64; i++) { + printk("%c", (field[i] ? '#' : 'o')); + } +#ifdef WIN32 + printk(" RX_NCO_PHASE_OFFSET(%d, 0x%X) \n", phy->last_tx_quad_cal_phase, + phy->last_tx_quad_cal_phase); +#else + printk(" RX_NCO_PHASE_OFFSET(%"PRIu32", 0x%"PRIX32") \n", + phy->last_tx_quad_cal_phase, + phy->last_tx_quad_cal_phase); +#endif +#endif + + ret = __ad9361_tx_quad_calib(phy, phy->last_tx_quad_cal_phase, rxnco_word, + decim, NULL); + if (ret < 0) + return ret; + + return 0; +} + +/** + * Perform a TX quadrature calibration. + * @param phy The AD9361 state structure. + * @param bw The bandwidth [Hz]. + * @param rx_phase The optional RX phase value overwrite (set to zero). + * @return 0 in case of success, negative error code otherwise. + */ + +static int ad9361_tx_quad_calib(struct ad9361_rf_phy *phy, + uint32_t bw_rx, uint32_t bw_tx, + int32_t rx_phase) +{ + struct spi_desc *spi = phy->spi; + uint32_t clktf, clkrf; + int32_t txnco_word, rxnco_word, txnco_freq, ret; + uint8_t __rx_phase = 0, reg_inv_bits = 0, val, decim; + bool phase_inversion_en; + + ret = 0; + if (phy->cached_synth_pd[0] & TX_LO_POWER_DOWN) { + if (phy->pdata->lo_powerdown_managed_en) { + ad9361_spi_writef(spi, REG_TX_SYNTH_POWER_DOWN_OVERRIDE, + TX_LO_POWER_DOWN, 0); + } else { + dev_err(dev, + "%s : Tx QUAD Cal abort due to TX LO in powerdown\n", + __func__); + return -EFAULT; + } + } + /* + * Find NCO frequency that matches this equation: + * BW / 4 = Rx NCO freq = Tx NCO freq: + * Rx NCO = ClkRF * (rxNCO <1:0> + 1) / 32 + * Tx NCO = ClkTF * (txNCO <1:0> + 1) / 32 + */ + + clkrf = clk_get_rate(phy, phy->ref_clk_scale[CLKRF_CLK]); + clktf = clk_get_rate(phy, phy->ref_clk_scale[CLKTF_CLK]); + + dev_dbg(&phy->spi->dev, "%s : bw_tx %"PRIu32" clkrf %"PRIu32" clktf %"PRIu32, + __func__, bw_tx, clkrf, clktf); + + txnco_word = DIV_ROUND_CLOSEST(bw_tx * 8, clktf) - 1; + txnco_word = clamp_t(int, txnco_word, 0, 3); + rxnco_word = txnco_word; + + dev_dbg(dev, "Tx NCO frequency: %"PRIu32" (BW/4: %"PRIu32") txnco_word %"PRId32, + clktf * (txnco_word + 1) / 32, bw_tx / 4, txnco_word); + + if (clktf <= 4000000UL) + decim = 2; + else + decim = 3; + + if (clkrf == (2 * clktf)) { + __rx_phase = 0x0E; + switch (txnco_word) { + case 0: + txnco_word++; + break; + case 1: + rxnco_word--; + break; + case 2: + rxnco_word -= 2; + txnco_word--; + break; + case 3: + rxnco_word -= 2; /* REVISIT */ + __rx_phase = 0x08; + break; + } + } else if (clkrf == clktf) { + switch (txnco_word) { + case 0: + case 3: + __rx_phase = 0x15; + break; + case 2: + __rx_phase = 0x1F; + break; + case 1: + if (ad9361_spi_readf(spi, + REG_TX_ENABLE_FILTER_CTRL, 0x3F) == 0x22) + __rx_phase = 0x15; /* REVISIT */ + else + __rx_phase = 0x1A; + break; + } + } else + dev_err(dev, "Unhandled case in %s line %d clkrf %"PRIu32" clktf %"PRIu32, + __func__, __LINE__, clkrf, clktf); + + if (rx_phase >= 0) + __rx_phase = rx_phase; + + txnco_freq = clktf * (txnco_word + 1) / 32; + + if (txnco_freq > (int64_t)(bw_rx / 4) || txnco_freq > (int64_t)(bw_tx / 4)) { + /* Make sure the BW during calibration is wide enough */ + ret = __ad9361_update_rf_bandwidth(phy, txnco_freq * 8, txnco_freq * 8); + if (ret < 0) + goto out_restore; + } + + phase_inversion_en = phy->pdata->rx1rx2_phase_inversion_en || + (phy->pdata->port_ctrl.pp_conf[1] & INVERT_RX2); + + if (phase_inversion_en) { + ad9361_spi_writef(spi, REG_PARALLEL_PORT_CONF_2, INVERT_RX2, 0); + + reg_inv_bits = ad9361_spi_read(spi, REG_INVERT_BITS); + + ad9361_spi_write(spi, REG_INVERT_BITS, + INVERT_RX1_RF_DC_CGOUT_WORD | + INVERT_RX2_RF_DC_CGOUT_WORD); + } + + ad9361_spi_writef(spi, REG_KEXP_2, TX_NCO_FREQ(~0), txnco_word); + ad9361_spi_write(spi, REG_QUAD_CAL_COUNT, 0xFF); + ad9361_spi_write(spi, REG_KEXP_1, KEXP_TX(1) | KEXP_TX_COMP(3) | + KEXP_DC_I(3) | KEXP_DC_Q(3)); + ad9361_spi_write(spi, REG_MAG_FTEST_THRESH, 0x03); + ad9361_spi_write(spi, REG_MAG_FTEST_THRESH_2, 0x03); + + if (phy->tx_quad_lpf_tia_match < 0) /* set in ad9361_load_gt() */ + dev_err(dev, "failed to find suitable LPF TIA value in gain table\n"); + else + ad9361_spi_write(spi, REG_TX_QUAD_FULL_LMT_GAIN, + phy->tx_quad_lpf_tia_match); + + ad9361_spi_write(spi, REG_QUAD_SETTLE_COUNT, 0xF0); + ad9361_spi_write(spi, REG_TX_QUAD_LPF_GAIN, 0x00); + + if (rx_phase != -2) { + ret = __ad9361_tx_quad_calib(phy, __rx_phase, rxnco_word, decim, &val); + + dev_dbg(dev, "LO leakage: %d Quadrature Calibration: %d : rx_phase %d\n", + !!(val & TX1_LO_CONV), !!(val & TX1_SSB_CONV), __rx_phase); + + /* Calibration failed -> try last phase offset */ + if (val != (TX1_LO_CONV | TX1_SSB_CONV)) { + if (phy->last_tx_quad_cal_phase < 31) + ret = __ad9361_tx_quad_calib(phy, phy->last_tx_quad_cal_phase, + rxnco_word, decim, &val); + } else { + phy->last_tx_quad_cal_phase = __rx_phase; + } + } else { + /* force phase search */ + val = 0; + } + /* Calibration failed -> loop through all 32 phase offsets */ + if (val != (TX1_LO_CONV | TX1_SSB_CONV)) + ret = ad9361_tx_quad_phase_search(phy, rxnco_word, decim); + + if (phase_inversion_en) { + ad9361_spi_writef(spi, REG_PARALLEL_PORT_CONF_2, INVERT_RX2, 1); + ad9361_spi_write(spi, REG_INVERT_BITS, reg_inv_bits); + } + + if (txnco_freq > (int64_t)(bw_rx / 4) || txnco_freq > (int64_t)(bw_tx / 4)) { + __ad9361_update_rf_bandwidth(phy, + phy->current_rx_bw_Hz, + phy->current_tx_bw_Hz); + } + +out_restore: + /* Restore synthesizer powerdown configuration */ + if (phy->pdata->lo_powerdown_managed_en && + (phy->cached_synth_pd[0] & TX_LO_POWER_DOWN)) + ad9361_synth_lo_powerdown(phy, LO_DONTCARE, LO_DONTCARE); + + return ret; +} + +/** + * Setup RX tracking calibrations. + * @param phy The AD9361 state structure. + * @param bbdc_track Set true, will enable the BBDC tracking. + * @param rfdc_track Set true, will enable the RFDC tracking. + * @param rxquad_track Set true, will enable the RXQUAD tracking. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_tracking_control(struct ad9361_rf_phy *phy, bool bbdc_track, + bool rfdc_track, bool rxquad_track) +{ + struct spi_desc *spi = phy->spi; + uint32_t qtrack = 0; + + dev_dbg(&spi->dev, "%s : bbdc_track=%d, rfdc_track=%d, rxquad_track=%d", + __func__, bbdc_track, rfdc_track, rxquad_track); + + ad9361_spi_write(spi, REG_CALIBRATION_CONFIG_2, + CALIBRATION_CONFIG2_DFLT | K_EXP_PHASE(0x15)); + ad9361_spi_write(spi, REG_CALIBRATION_CONFIG_3, + PREVENT_POS_LOOP_GAIN | K_EXP_AMPLITUDE(0x15)); + + ad9361_spi_write(spi, REG_DC_OFFSET_CONFIG2, + USE_WAIT_COUNTER_FOR_RF_DC_INIT_CAL | + DC_OFFSET_UPDATE(phy->pdata->dc_offset_update_events) | + (bbdc_track ? ENABLE_BB_DC_OFFSET_TRACKING : 0) | + (rfdc_track ? ENABLE_RF_OFFSET_TRACKING : 0)); + + ad9361_spi_writef(spi, REG_RX_QUAD_GAIN2, + CORRECTION_WORD_DECIMATION_M(~0), + phy->pdata->qec_tracking_slow_mode_en ? 4 : 0); + + if (rxquad_track) { + if (phy->pdata->rx2tx2) + qtrack = ENABLE_TRACKING_MODE_CH1 | ENABLE_TRACKING_MODE_CH2; + else + qtrack = (phy->pdata->rx1tx1_mode_use_rx_num == 1) ? + ENABLE_TRACKING_MODE_CH1 : ENABLE_TRACKING_MODE_CH2; + } + + ad9361_spi_write(spi, REG_CALIBRATION_CONFIG_1, + ENABLE_PHASE_CORR | ENABLE_GAIN_CORR | + FREE_RUN_MODE | ENABLE_CORR_WORD_DECIMATION | + qtrack); + + return 0; +} + +/** + * Enable/disable the VCO cal. + * @param phy The AD9361 state structure. + * @param rx Set true for rx. + * @param enable Set true to enable. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_trx_vco_cal_control(struct ad9361_rf_phy *phy, + bool tx, bool enable) +{ + dev_dbg(&phy->spi->dev, "%s : state %d", + __func__, enable); + + return ad9361_spi_writef(phy->spi, + tx ? REG_TX_PFD_CONFIG : REG_RX_PFD_CONFIG, + BYPASS_LD_SYNTH, !enable); +} + +/** + * Enable/disable the ext. LO. + * @param phy The AD9361 state structure. + * @param rx Set true for rx. + * @param enable Set true to enable. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_trx_ext_lo_control(struct ad9361_rf_phy *phy, + bool tx, bool enable) +{ + uint32_t val = enable ? ~0 : 0; + int32_t ret; + + /* REVIST: + * POWER_DOWN_TRX_SYNTH and MCS_RF_ENABLE somehow conflict + */ + + bool mcs_rf_enable = ad9361_spi_readf(phy->spi, + REG_MULTICHIP_SYNC_AND_TX_MON_CTRL, + MCS_RF_ENABLE); + + dev_dbg(&phy->spi->dev, "%s : %s state %d", __func__, + tx ? "TX" : "RX", enable); + + if (tx) { + ret = ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2, + POWER_DOWN_TX_SYNTH, mcs_rf_enable ? 0 : enable); + + ret = ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2, + TX_SYNTH_READY_MASK, enable); + + ret |= ad9361_spi_writef(phy->spi, REG_RFPLL_DIVIDERS, + TX_VCO_DIVIDER(~0), enable ? 7 : + phy->cached_tx_rfpll_div); + + if (enable) + phy->cached_synth_pd[0] |= TX_SYNTH_VCO_ALC_POWER_DOWN | + TX_SYNTH_PTAT_POWER_DOWN | + TX_SYNTH_VCO_POWER_DOWN; + else + phy->cached_synth_pd[0] &= ~(TX_SYNTH_VCO_ALC_POWER_DOWN | + TX_SYNTH_PTAT_POWER_DOWN | + TX_SYNTH_VCO_POWER_DOWN); + + + ret |= ad9361_spi_write(phy->spi, REG_TX_SYNTH_POWER_DOWN_OVERRIDE, + phy->cached_synth_pd[0]); + + ret |= ad9361_spi_writef(phy->spi, REG_ANALOG_POWER_DOWN_OVERRIDE, + TX_EXT_VCO_BUFFER_POWER_DOWN, !enable); + + ret |= ad9361_spi_write(phy->spi, REG_TX_LO_GEN_POWER_MODE, + TX_LO_GEN_POWER_MODE(val)); + } else { + ret = ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2, + POWER_DOWN_RX_SYNTH, mcs_rf_enable ? 0 : enable); + + ret = ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2, + RX_SYNTH_READY_MASK, enable); + + ret |= ad9361_spi_writef(phy->spi, REG_RFPLL_DIVIDERS, + RX_VCO_DIVIDER(~0), enable ? 7 : + phy->cached_rx_rfpll_div); + + if (enable) + phy->cached_synth_pd[1] |= RX_SYNTH_VCO_ALC_POWER_DOWN | + RX_SYNTH_PTAT_POWER_DOWN | + RX_SYNTH_VCO_POWER_DOWN; + else + phy->cached_synth_pd[1] &= ~(TX_SYNTH_VCO_ALC_POWER_DOWN | + RX_SYNTH_PTAT_POWER_DOWN | + RX_SYNTH_VCO_POWER_DOWN); + + ret |= ad9361_spi_write(phy->spi, REG_RX_SYNTH_POWER_DOWN_OVERRIDE, + phy->cached_synth_pd[1]); + + ret |= ad9361_spi_writef(phy->spi, REG_ANALOG_POWER_DOWN_OVERRIDE, + RX_EXT_VCO_BUFFER_POWER_DOWN, !enable); + + ret |= ad9361_spi_write(phy->spi, REG_RX_LO_GEN_POWER_MODE, + RX_LO_GEN_POWER_MODE(val)); + } + + return ret; +} + +/** + * Power down the TX and/or RX Local Oscillators. + * @param phy The AD9361 state structure. + * @param rx The RX LO setting. + * @param tx The TX LO setting. + * @return 0 in case of success, negative error code otherwise. + */ +int ad9361_synth_lo_powerdown(struct ad9361_rf_phy *phy, + enum synth_pd_ctrl rx, + enum synth_pd_ctrl tx) +{ + dev_dbg(&phy->spi->dev, "%s : RX(%d) TX(%d)", __func__, rx, tx); + + switch (rx) { + case LO_OFF: + phy->cached_synth_pd[1] |= RX_LO_POWER_DOWN; + break; + case LO_ON: + phy->cached_synth_pd[1] &= ~RX_LO_POWER_DOWN; + break; + case LO_DONTCARE: + break; + } + + switch (tx) { + case LO_OFF: + phy->cached_synth_pd[0] |= TX_LO_POWER_DOWN; + break; + case LO_ON: + phy->cached_synth_pd[0] &= ~TX_LO_POWER_DOWN; + break; + case LO_DONTCARE: + break; + } + + return ad9361_spi_writem(phy->spi, REG_TX_SYNTH_POWER_DOWN_OVERRIDE, + phy->cached_synth_pd, 2); +} + +/** + * Setup the reference clock delay unit counter register. + * @param phy The AD9361 state structure. + * @param ref_clk_hz The reference clock frequency [Hz]. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_set_ref_clk_cycles(struct ad9361_rf_phy *phy, + uint32_t ref_clk_hz) +{ + dev_dbg(&phy->spi->dev, "%s : ref_clk_hz %"PRIu32, + __func__, ref_clk_hz); + + return ad9361_spi_write(phy->spi, REG_REFERENCE_CLOCK_CYCLES, + REFERENCE_CLOCK_CYCLES_PER_US((ref_clk_hz / 1000000UL) - 1)); +} + +/** + * Setup the DCXO tune. + * @param phy The AD9361 state structure. + * @param coarse The DCXO tune coarse. + * @param fine The DCXO tune fine. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_dcxo_tune(struct ad9361_rf_phy *phy, + uint32_t coarse, uint32_t fine) +{ + dev_dbg(&phy->spi->dev, "%s : coarse %"PRIu32" fine %"PRIu32, + __func__, coarse, fine); + + if (phy->pdata->use_extclk) + return -ENODEV; + + ad9361_spi_write(phy->spi, REG_DCXO_COARSE_TUNE, + DCXO_TUNE_COARSE(coarse)); + ad9361_spi_write(phy->spi, REG_DCXO_FINE_TUNE_LOW, + DCXO_TUNE_FINE_LOW(fine)); + return ad9361_spi_write(phy->spi, REG_DCXO_FINE_TUNE_HIGH, + DCXO_TUNE_FINE_HIGH(fine)); +} + +/** + * Setup TXMON. + * @param phy The AD9361 state structure. + * @param ctrl The TXMON settings. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_txmon_setup(struct ad9361_rf_phy *phy, + struct tx_monitor_control *ctrl) +{ + struct spi_desc *spi = phy->spi; + + dev_dbg(&phy->spi->dev, "%s", __func__); + + ad9361_spi_write(spi, REG_TPM_MODE_ENABLE, + (ctrl->one_shot_mode_en ? ONE_SHOT_MODE : 0) | + TX_MON_DURATION(ilog2(ctrl->tx_mon_duration / 16))); + + ad9361_spi_write(spi, REG_TX_MON_DELAY, ctrl->tx_mon_delay & 0xFF); + ad9361_spi_writef(spi, REG_TX_LEVEL_THRESH, + TX_MON_DELAY_COUNTER(~0), ctrl->tx_mon_delay >> 8); + + ad9361_spi_write(spi, REG_TX_MON_1_CONFIG, + TX_MON_1_LO_CM(ctrl->tx1_mon_lo_cm) | + TX_MON_1_GAIN(ctrl->tx1_mon_front_end_gain)); + ad9361_spi_write(spi, REG_TX_MON_2_CONFIG, + TX_MON_2_LO_CM(ctrl->tx2_mon_lo_cm) | + TX_MON_2_GAIN(ctrl->tx2_mon_front_end_gain)); + + ad9361_spi_write(spi, REG_TX_ATTEN_THRESH, + ctrl->low_high_gain_threshold_mdB / 250); + + ad9361_spi_write(spi, REG_TX_MON_HIGH_GAIN, + TX_MON_HIGH_GAIN(ctrl->high_gain_dB)); + + ad9361_spi_write(spi, REG_TX_MON_LOW_GAIN, + (ctrl->tx_mon_track_en ? TX_MON_TRACK : 0) | + TX_MON_LOW_GAIN(ctrl->low_gain_dB)); + + return 0; +} + +/** + * Enable TXMON. + * @param phy The AD9361 state structure. + * @param en_mask The enable mask. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_txmon_control(struct ad9361_rf_phy *phy, + int32_t en_mask) +{ + dev_dbg(&phy->spi->dev, "%s: mask 0x%"PRIx32, __func__, en_mask); + +#if 0 + if (!phy->pdata->fdd && en_mask) { + ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_1, + ENABLE_RX_DATA_PORT_FOR_CAL, 1); + phy->txmon_tdd_en = true; + } else { + ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_1, + ENABLE_RX_DATA_PORT_FOR_CAL, 0); + phy->txmon_tdd_en = false; + } +#endif + + ad9361_spi_writef(phy->spi, REG_ANALOG_POWER_DOWN_OVERRIDE, + TX_MONITOR_POWER_DOWN(~0), ~en_mask); + + ad9361_spi_writef(phy->spi, REG_TPM_MODE_ENABLE, + TX1_MON_ENABLE, !!(en_mask & TX_1)); + + return ad9361_spi_writef(phy->spi, REG_TPM_MODE_ENABLE, + TX2_MON_ENABLE, !!(en_mask & TX_2)); +} + +/** +* Setup the RF port. +* Note: +* val +* 0 (RX1A_N & RX1A_P) and (RX2A_N & RX2A_P) enabled; balanced +* 1 (RX1B_N & RX1B_P) and (RX2B_N & RX2B_P) enabled; balanced +* 2 (RX1C_N & RX1C_P) and (RX2C_N & RX2C_P) enabled; balanced +* +* 3 RX1A_N and RX2A_N enabled; unbalanced +* 4 RX1A_P and RX2A_P enabled; unbalanced +* 5 RX1B_N and RX2B_N enabled; unbalanced +* 6 RX1B_P and RX2B_P enabled; unbalanced +* 7 RX1C_N and RX2C_N enabled; unbalanced +* 8 RX1C_P and RX2C_P enabled; unbalanced +* 9 TX_MON1 +* 10 TX_MON2 +* 11 TX_MON1 & TX_MON2 +* @param phy The AD9361 state structure. +* @param is_out TX RF output port enabled. +* @param rx_inputs RX input option identifier +* @param txb TX output option identifier +* @return 0 in case of success, negative error code otherwise. +*/ +int32_t ad9361_rf_port_setup(struct ad9361_rf_phy *phy, bool is_out, + uint32_t rx_inputs, uint32_t txb) +{ + uint32_t val; + + if (rx_inputs > 11) + return -EINVAL; + + if (!is_out) { + if (rx_inputs > 8) + return ad9361_txmon_control(phy, rx_inputs & (TX_1 | TX_2)); + else + ad9361_txmon_control(phy, 0); + } + + if (rx_inputs < 3) + val = 3 << (rx_inputs * 2); + else + val = 1 << (rx_inputs - 3); + + if (txb) + val |= TX_OUTPUT; /* Select TX1B, TX2B */ + + dev_dbg(&phy->spi->dev, "%s : INPUT_SELECT 0x%"PRIx32, + __func__, val); + + return ad9361_spi_write(phy->spi, REG_INPUT_SELECT, val); +} + +/** + * Setup the Parallel Port (Digital Data Interface). + * @param phy The AD9361 state structure. + * @param restore_c3 Set true, will restore the Parallel Port Configuration 3 + * register. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_pp_port_setup(struct ad9361_rf_phy *phy, bool restore_c3) +{ + struct spi_desc *spi = phy->spi; + struct ad9361_phy_platform_data *pd = phy->pdata; + + dev_dbg(&phy->spi->dev, "%s", __func__); + + if (restore_c3) { + return ad9361_spi_write(spi, REG_PARALLEL_PORT_CONF_3, + pd->port_ctrl.pp_conf[2]); + } + + /* Sanity check */ + if (pd->port_ctrl.pp_conf[2] & LVDS_MODE) + pd->port_ctrl.pp_conf[2] &= + ~(HALF_DUPLEX_MODE | SINGLE_DATA_RATE | SINGLE_PORT_MODE); + + if (pd->port_ctrl.pp_conf[2] & FULL_PORT) + pd->port_ctrl.pp_conf[2] &= ~(HALF_DUPLEX_MODE | SINGLE_PORT_MODE); + + ad9361_spi_write(spi, REG_PARALLEL_PORT_CONF_1, pd->port_ctrl.pp_conf[0]); + ad9361_spi_write(spi, REG_PARALLEL_PORT_CONF_2, pd->port_ctrl.pp_conf[1]); + ad9361_spi_write(spi, REG_PARALLEL_PORT_CONF_3, pd->port_ctrl.pp_conf[2]); + ad9361_spi_write(spi, REG_RX_CLOCK_DATA_DELAY, pd->port_ctrl.rx_clk_data_delay); + ad9361_spi_write(spi, REG_TX_CLOCK_DATA_DELAY, pd->port_ctrl.tx_clk_data_delay); + + ad9361_spi_write(spi, REG_LVDS_BIAS_CTRL, pd->port_ctrl.lvds_bias_ctrl); + // ad9361_spi_write(spi, REG_DIGITAL_IO_CTRL, pd->port_ctrl.digital_io_ctrl); + ad9361_spi_write(spi, REG_LVDS_INVERT_CTRL1, pd->port_ctrl.lvds_invert[0]); + ad9361_spi_write(spi, REG_LVDS_INVERT_CTRL2, pd->port_ctrl.lvds_invert[1]); + + if (pd->rx1rx2_phase_inversion_en || + (pd->port_ctrl.pp_conf[1] & INVERT_RX2)) { + + ad9361_spi_writef(spi, REG_PARALLEL_PORT_CONF_2, INVERT_RX2, 1); + ad9361_spi_writef(spi, REG_INVERT_BITS, + INVERT_RX2_RF_DC_CGOUT_WORD, 0); + } + + return 0; +} + +/** + * Setup the Gain Control Blocks (common function for MGC, AGC modes) + * @param phy The AD9361 state structure. + * @param ctrl The gain control settings. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_gc_setup(struct ad9361_rf_phy *phy, + struct gain_control *ctrl) +{ + struct spi_desc *spi = phy->spi; + uint32_t reg, tmp1, tmp2; + + dev_dbg(&phy->spi->dev, "%s", __func__); + + reg = DEC_PWR_FOR_GAIN_LOCK_EXIT | DEC_PWR_FOR_LOCK_LEVEL | + DEC_PWR_FOR_LOW_PWR; + + if (ctrl->rx1_mode == RF_GAIN_HYBRID_AGC || + ctrl->rx2_mode == RF_GAIN_HYBRID_AGC) + reg |= SLOW_ATTACK_HYBRID_MODE; + + reg |= RX1_GAIN_CTRL_SETUP(ctrl->rx1_mode) | + RX2_GAIN_CTRL_SETUP(ctrl->rx2_mode); + + phy->agc_mode[0] = ctrl->rx1_mode; + phy->agc_mode[1] = ctrl->rx2_mode; + + ad9361_spi_write(spi, REG_AGC_CONFIG_1, reg); // Gain Control Mode Select + + /* AGC_USE_FULL_GAIN_TABLE handled in ad9361_load_gt() */ + ad9361_spi_writef(spi, REG_AGC_CONFIG_2, MAN_GAIN_CTRL_RX1, + ctrl->mgc_rx1_ctrl_inp_en); + ad9361_spi_writef(spi, REG_AGC_CONFIG_2, MAN_GAIN_CTRL_RX2, + ctrl->mgc_rx2_ctrl_inp_en); + ad9361_spi_writef(spi, REG_AGC_CONFIG_2, DIG_GAIN_EN, + ctrl->dig_gain_en); + + ctrl->adc_ovr_sample_size = clamp_t(uint8_t, ctrl->adc_ovr_sample_size, 1U, 8U); + reg = ADC_OVERRANGE_SAMPLE_SIZE(ctrl->adc_ovr_sample_size - 1); + + if (has_split_gt && phy->pdata->split_gt && + (ctrl->mgc_rx1_ctrl_inp_en || ctrl->mgc_rx2_ctrl_inp_en)) { + switch (ctrl->mgc_split_table_ctrl_inp_gain_mode) { + case 1: + reg &= ~INCDEC_LMT_GAIN; + break; + case 2: + reg |= INCDEC_LMT_GAIN; + break; + default: + case 0: + reg |= USE_AGC_FOR_LMTLPF_GAIN; + break; + } + } + + ctrl->mgc_inc_gain_step = clamp_t(uint8_t, ctrl->mgc_inc_gain_step, 1U, 8U); + reg |= MANUAL_INCR_STEP_SIZE(ctrl->mgc_inc_gain_step - 1); + ad9361_spi_write(spi, REG_AGC_CONFIG_3, + reg); // Incr Step Size, ADC Overrange Size + + ctrl->mgc_dec_gain_step = clamp_t(uint8_t, ctrl->mgc_dec_gain_step, 1U, 8U); + reg = MANUAL_CTRL_IN_DECR_GAIN_STP_SIZE(ctrl->mgc_dec_gain_step - 1); + ad9361_spi_write(spi, REG_PEAK_WAIT_TIME, + reg); // Decr Step Size, Peak Overload Time + + if (ctrl->dig_gain_en) + ad9361_spi_write(spi, REG_DIGITAL_GAIN, + MAXIMUM_DIGITAL_GAIN(ctrl->max_dig_gain) | + DIG_GAIN_STP_SIZE(ctrl->dig_gain_step_size)); + + if (ctrl->adc_large_overload_thresh >= ctrl->adc_small_overload_thresh) { + ad9361_spi_write(spi, REG_ADC_SMALL_OVERLOAD_THRESH, + ctrl->adc_small_overload_thresh); // ADC Small Overload Threshold + ad9361_spi_write(spi, REG_ADC_LARGE_OVERLOAD_THRESH, + ctrl->adc_large_overload_thresh); // ADC Large Overload Threshold + } else { + ad9361_spi_write(spi, REG_ADC_SMALL_OVERLOAD_THRESH, + ctrl->adc_large_overload_thresh); // ADC Small Overload Threshold + ad9361_spi_write(spi, REG_ADC_LARGE_OVERLOAD_THRESH, + ctrl->adc_small_overload_thresh); // ADC Large Overload Threshold + } + + reg = (ctrl->lmt_overload_high_thresh / 16) - 1; + reg = clamp(reg, 0U, 63U); + ad9361_spi_write(spi, REG_LARGE_LMT_OVERLOAD_THRESH, reg); + reg = (ctrl->lmt_overload_low_thresh / 16) - 1; + reg = clamp(reg, 0U, 63U); + ad9361_spi_writef(spi, REG_SMALL_LMT_OVERLOAD_THRESH, + SMALL_LMT_OVERLOAD_THRESH(~0), reg); + + if (has_split_gt && phy->pdata->split_gt) { + /* REVIST */ + ad9361_spi_write(spi, REG_RX1_MANUAL_LPF_GAIN, 0x58); // Rx1 LPF Gain Index + ad9361_spi_write(spi, REG_RX2_MANUAL_LPF_GAIN, 0x18); // Rx2 LPF Gain Index + ad9361_spi_write(spi, REG_FAST_INITIAL_LMT_GAIN_LIMIT, + 0x27); // Initial LMT Gain Limit + } + + ad9361_spi_write(spi, REG_RX1_MANUAL_DIGITALFORCED_GAIN, + 0x00); // Rx1 Digital Gain Index + ad9361_spi_write(spi, REG_RX2_MANUAL_DIGITALFORCED_GAIN, + 0x00); // Rx2 Digital Gain Index + + reg = clamp_t(uint8_t, ctrl->low_power_thresh, 0U, 64U) * 2; + ad9361_spi_write(spi, REG_FAST_LOW_POWER_THRESH, reg); // Low Power Threshold + ad9361_spi_write(spi, REG_TX_SYMBOL_ATTEN_CONFIG, + 0x00); // Tx Symbol Gain Control + + ad9361_spi_writef(spi, REG_DEC_POWER_MEASURE_DURATION_0, + USE_HB1_OUT_FOR_DEC_PWR_MEAS, + !ctrl->use_rx_fir_out_for_dec_pwr_meas); // USE HB1 or FIR output for power measurements + + ad9361_spi_writef(spi, REG_DEC_POWER_MEASURE_DURATION_0, + ENABLE_DEC_PWR_MEAS, 1); // Power Measurement Duration + + if (ctrl->rx1_mode == RF_GAIN_FASTATTACK_AGC || + ctrl->rx2_mode == RF_GAIN_FASTATTACK_AGC) + reg = ilog2(ctrl->f_agc_dec_pow_measuremnt_duration / 16); + else + reg = ilog2(ctrl->dec_pow_measuremnt_duration / 16); + + ad9361_spi_writef(spi, REG_DEC_POWER_MEASURE_DURATION_0, + DEC_POWER_MEASUREMENT_DURATION(~0), reg); // Power Measurement Duration + + /* AGC */ + + tmp1 = reg = clamp_t(uint8_t, ctrl->agc_inner_thresh_high, 0U, 127U); + ad9361_spi_writef(spi, REG_AGC_LOCK_LEVEL, + AGC_LOCK_LEVEL_FAST_AGC_INNER_HIGH_THRESH_SLOW(~0), + reg); + + tmp2 = reg = clamp_t(uint8_t, ctrl->agc_inner_thresh_low, 0U, 127U); + reg |= (ctrl->adc_lmt_small_overload_prevent_gain_inc ? + PREVENT_GAIN_INC : 0); + ad9361_spi_write(spi, REG_AGC_INNER_LOW_THRESH, reg); + + reg = AGC_OUTER_HIGH_THRESH(tmp1 - ctrl->agc_outer_thresh_high) | + AGC_OUTER_LOW_THRESH(ctrl->agc_outer_thresh_low - tmp2); + ad9361_spi_write(spi, REG_OUTER_POWER_THRESHS, reg); + + reg = AGC_OUTER_HIGH_THRESH_EXED_STP_SIZE(ctrl->agc_outer_thresh_high_dec_steps) + | + AGC_OUTER_LOW_THRESH_EXED_STP_SIZE(ctrl->agc_outer_thresh_low_inc_steps); + ad9361_spi_write(spi, REG_GAIN_STP_2, reg); + + reg = ((ctrl->immed_gain_change_if_large_adc_overload) ? + IMMED_GAIN_CHANGE_IF_LG_ADC_OVERLOAD : 0) | + ((ctrl->immed_gain_change_if_large_lmt_overload) ? + IMMED_GAIN_CHANGE_IF_LG_LMT_OVERLOAD : 0) | + AGC_INNER_HIGH_THRESH_EXED_STP_SIZE(ctrl->agc_inner_thresh_high_dec_steps) | + AGC_INNER_LOW_THRESH_EXED_STP_SIZE(ctrl->agc_inner_thresh_low_inc_steps); + ad9361_spi_write(spi, REG_GAIN_STP1, reg); + + reg = LARGE_ADC_OVERLOAD_EXED_COUNTER(ctrl->adc_large_overload_exceed_counter) | + SMALL_ADC_OVERLOAD_EXED_COUNTER(ctrl->adc_small_overload_exceed_counter); + ad9361_spi_write(spi, REG_ADC_OVERLOAD_COUNTERS, reg); + + reg = DECREMENT_STP_SIZE_FOR_SMALL_LPF_GAIN_CHANGE( + ctrl->f_agc_large_overload_inc_steps) | + LARGE_LPF_GAIN_STEP(ctrl->adc_large_overload_inc_steps); + ad9361_spi_write(spi, REG_GAIN_STP_CONFIG_2, reg); + + reg = LARGE_LMT_OVERLOAD_EXED_COUNTER(ctrl->lmt_overload_large_exceed_counter) | + SMALL_LMT_OVERLOAD_EXED_COUNTER(ctrl->lmt_overload_small_exceed_counter); + ad9361_spi_write(spi, REG_LMT_OVERLOAD_COUNTERS, reg); + + ad9361_spi_writef(spi, REG_GAIN_STP_CONFIG1, + DEC_STP_SIZE_FOR_LARGE_LMT_OVERLOAD(~0), + ctrl->lmt_overload_large_inc_steps); + + reg = DIG_SATURATION_EXED_COUNTER(ctrl->dig_saturation_exceed_counter) | + (ctrl->sync_for_gain_counter_en ? + ENABLE_SYNC_FOR_GAIN_COUNTER : 0); + ad9361_spi_write(spi, REG_DIGITAL_SAT_COUNTER, reg); + + /* + * Fast AGC + */ + + /* Fast AGC - Low Power */ + ad9361_spi_writef(spi, REG_FAST_CONFIG_1, + ENABLE_INCR_GAIN, + ctrl->f_agc_allow_agc_gain_increase); + + ad9361_spi_write(spi, REG_FAST_INCREMENT_TIME, + ctrl->f_agc_lp_thresh_increment_time); + + reg = ctrl->f_agc_lp_thresh_increment_steps - 1; + reg = clamp_t(uint32_t, reg, 0U, 7U); + ad9361_spi_writef(spi, REG_FAST_ENERGY_DETECT_COUNT, + INCREMENT_GAIN_STP_LPFLMT(~0), reg); + + /* Fast AGC - Lock Level */ + /* Dual use see also agc_inner_thresh_high */ + ad9361_spi_writef(spi, REG_FAST_CONFIG_2_SETTLING_DELAY, + ENABLE_LMT_GAIN_INC_FOR_LOCK_LEVEL, + ctrl->f_agc_lock_level_lmt_gain_increase_en); + + reg = ctrl->f_agc_lock_level_gain_increase_upper_limit; + reg = clamp_t(uint32_t, reg, 0U, 63U); + ad9361_spi_writef(spi, REG_FAST_AGCLL_UPPER_LIMIT, + AGCLL_MAX_INCREASE(~0), reg); + + /* Fast AGC - Peak Detectors and Final Settling */ + reg = ctrl->f_agc_lpf_final_settling_steps; + reg = clamp_t(uint32_t, reg, 0U, 3U); + ad9361_spi_writef(spi, REG_FAST_ENERGY_LOST_THRESH, + POST_LOCK_LEVEL_STP_SIZE_FOR_LPF_TABLE_FULL_TABLE(~0), + reg); + + reg = ctrl->f_agc_lmt_final_settling_steps; + reg = clamp_t(uint32_t, reg, 0U, 3U); + ad9361_spi_writef(spi, REG_FAST_STRONGER_SIGNAL_THRESH, + POST_LOCK_LEVEL_STP_FOR_LMT_TABLE(~0), reg); + + reg = ctrl->f_agc_final_overrange_count; + reg = clamp_t(uint32_t, reg, 0U, 7U); + ad9361_spi_writef(spi, REG_FAST_FINAL_OVER_RANGE_AND_OPT_GAIN, + FINAL_OVER_RANGE_COUNT(~0), reg); + + /* Fast AGC - Final Power Test */ + ad9361_spi_writef(spi, REG_FAST_CONFIG_1, + ENABLE_GAIN_INC_AFTER_GAIN_LOCK, + ctrl->f_agc_gain_increase_after_gain_lock_en); + + /* Fast AGC - Unlocking the Gain */ + /* 0 = MAX Gain, 1 = Optimized Gain, 2 = Set Gain */ + + reg = ctrl->f_agc_gain_index_type_after_exit_rx_mode; + ad9361_spi_writef(spi, REG_FAST_CONFIG_1, + GOTO_SET_GAIN_IF_EXIT_RX_STATE, reg == SET_GAIN); + ad9361_spi_writef(spi, REG_FAST_CONFIG_1, + GOTO_OPTIMIZED_GAIN_IF_EXIT_RX_STATE, + reg == OPTIMIZED_GAIN); + + ad9361_spi_writef(spi, REG_FAST_CONFIG_2_SETTLING_DELAY, + USE_LAST_LOCK_LEVEL_FOR_SET_GAIN, + ctrl->f_agc_use_last_lock_level_for_set_gain_en); + + reg = ctrl->f_agc_optimized_gain_offset; + reg = clamp_t(uint32_t, reg, 0U, 15U); + ad9361_spi_writef(spi, REG_FAST_FINAL_OVER_RANGE_AND_OPT_GAIN, + OPTIMIZE_GAIN_OFFSET(~0), reg); + + tmp1 = !ctrl->f_agc_rst_gla_stronger_sig_thresh_exceeded_en || + !ctrl->f_agc_rst_gla_engergy_lost_sig_thresh_exceeded_en || + !ctrl->f_agc_rst_gla_large_adc_overload_en || + !ctrl->f_agc_rst_gla_large_lmt_overload_en || + ctrl->f_agc_rst_gla_en_agc_pulled_high_en; + + ad9361_spi_writef(spi, REG_AGC_CONFIG_2, + AGC_GAIN_UNLOCK_CTRL, tmp1); + + reg = !ctrl->f_agc_rst_gla_stronger_sig_thresh_exceeded_en; + ad9361_spi_writef(spi, REG_FAST_STRONG_SIGNAL_FREEZE, + DONT_UNLOCK_GAIN_IF_STRONGER_SIGNAL, reg); + + reg = ctrl->f_agc_rst_gla_stronger_sig_thresh_above_ll; + reg = clamp_t(uint32_t, reg, 0U, 63U); + ad9361_spi_writef(spi, REG_FAST_STRONGER_SIGNAL_THRESH, + STRONGER_SIGNAL_THRESH(~0), reg); + + reg = ctrl->f_agc_rst_gla_engergy_lost_sig_thresh_below_ll; + reg = clamp_t(uint32_t, reg, 0U, 63U); + ad9361_spi_writef(spi, REG_FAST_ENERGY_LOST_THRESH, + ENERGY_LOST_THRESH(~0), reg); + + reg = ctrl->f_agc_rst_gla_engergy_lost_goto_optim_gain_en; + ad9361_spi_writef(spi, REG_FAST_CONFIG_1, + GOTO_OPT_GAIN_IF_ENERGY_LOST_OR_EN_AGC_HIGH, reg); + + reg = !ctrl->f_agc_rst_gla_engergy_lost_sig_thresh_exceeded_en; + ad9361_spi_writef(spi, REG_FAST_CONFIG_1, + DONT_UNLOCK_GAIN_IF_ENERGY_LOST, reg); + + reg = ctrl->f_agc_energy_lost_stronger_sig_gain_lock_exit_cnt; + reg = clamp_t(uint32_t, reg, 0U, 63U); + ad9361_spi_writef(spi, REG_FAST_GAIN_LOCK_EXIT_COUNT, + GAIN_LOCK_EXIT_COUNT(~0), reg); + + reg = !ctrl->f_agc_rst_gla_large_adc_overload_en || + !ctrl->f_agc_rst_gla_large_lmt_overload_en; + ad9361_spi_writef(spi, REG_FAST_CONFIG_1, + DONT_UNLOCK_GAIN_IF_LG_ADC_OR_LMT_OVRG, reg); + + reg = !ctrl->f_agc_rst_gla_large_adc_overload_en; + ad9361_spi_writef(spi, REG_FAST_LOW_POWER_THRESH, + DONT_UNLOCK_GAIN_IF_ADC_OVRG, reg); + + /* 0 = Max Gain, 1 = Set Gain, 2 = Optimized Gain, 3 = No Gain Change */ + + if (ctrl->f_agc_rst_gla_en_agc_pulled_high_en) { + switch (ctrl->f_agc_rst_gla_if_en_agc_pulled_high_mode) { + case MAX_GAIN: + ad9361_spi_writef(spi, REG_FAST_CONFIG_2_SETTLING_DELAY, + GOTO_MAX_GAIN_OR_OPT_GAIN_IF_EN_AGC_HIGH, 1); + + ad9361_spi_writef(spi, REG_FAST_CONFIG_1, + GOTO_SET_GAIN_IF_EN_AGC_HIGH, 0); + + ad9361_spi_writef(spi, REG_FAST_CONFIG_1, + GOTO_OPT_GAIN_IF_ENERGY_LOST_OR_EN_AGC_HIGH, 0); + break; + case SET_GAIN: + ad9361_spi_writef(spi, REG_FAST_CONFIG_2_SETTLING_DELAY, + GOTO_MAX_GAIN_OR_OPT_GAIN_IF_EN_AGC_HIGH, 0); + + ad9361_spi_writef(spi, REG_FAST_CONFIG_1, + GOTO_SET_GAIN_IF_EN_AGC_HIGH, 1); + break; + case OPTIMIZED_GAIN: + ad9361_spi_writef(spi, REG_FAST_CONFIG_2_SETTLING_DELAY, + GOTO_MAX_GAIN_OR_OPT_GAIN_IF_EN_AGC_HIGH, 1); + + ad9361_spi_writef(spi, REG_FAST_CONFIG_1, + GOTO_SET_GAIN_IF_EN_AGC_HIGH, 0); + + ad9361_spi_writef(spi, REG_FAST_CONFIG_1, + GOTO_OPT_GAIN_IF_ENERGY_LOST_OR_EN_AGC_HIGH, 1); + break; + case NO_GAIN_CHANGE: + ad9361_spi_writef(spi, REG_FAST_CONFIG_1, + GOTO_SET_GAIN_IF_EN_AGC_HIGH, 0); + ad9361_spi_writef(spi, REG_FAST_CONFIG_2_SETTLING_DELAY, + GOTO_MAX_GAIN_OR_OPT_GAIN_IF_EN_AGC_HIGH, 0); + break; + } + } else { + ad9361_spi_writef(spi, REG_FAST_CONFIG_1, + GOTO_SET_GAIN_IF_EN_AGC_HIGH, 0); + ad9361_spi_writef(spi, REG_FAST_CONFIG_2_SETTLING_DELAY, + GOTO_MAX_GAIN_OR_OPT_GAIN_IF_EN_AGC_HIGH, 0); + } + + reg = ilog2(ctrl->f_agc_power_measurement_duration_in_state5 / 16); + reg = clamp_t(uint32_t, reg, 0U, 15U); + ad9361_spi_writef(spi, REG_RX1_MANUAL_LPF_GAIN, + POWER_MEAS_IN_STATE_5(~0), reg); + ad9361_spi_writef(spi, REG_RX1_MANUAL_LMT_FULL_GAIN, + POWER_MEAS_IN_STATE_5_MSB, reg >> 3); + + return ad9361_gc_update(phy); +} + +/** + * Set the Aux DAC. + * @param phy The AD9361 state structure. + * @param dac The DAC. + * @param val_mV The value. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_auxdac_set(struct ad9361_rf_phy *phy, int32_t dac, + int32_t val_mV) +{ + struct spi_desc *spi = phy->spi; + uint32_t val, tmp; + + dev_dbg(&phy->spi->dev, "%s DAC%"PRId32" = %"PRId32" mV", __func__, dac, + val_mV); + + /* Disable DAC if val == 0, Ignored in ENSM Auto Mode */ + ad9361_spi_writef(spi, REG_AUXDAC_ENABLE_CTRL, + AUXDAC_MANUAL_BAR(dac), val_mV ? 0 : 1); + + if (val_mV < 306) + val_mV = 306; + + if (val_mV < 1888) { + val = ((val_mV - 306) * 1000) / 1469; /* Vref = 1V, Step = 2 */ + tmp = AUXDAC_1_VREF(0); + } else { + val = ((val_mV - 1761) * 1000) / 1512; /* Vref = 2.5V, Step = 2 */ + tmp = AUXDAC_1_VREF(3); + } + + val = clamp_t(uint32_t, val, 0, 1023); + + switch (dac) { + case 1: + ad9361_spi_write(spi, REG_AUXDAC_1_WORD, val >> 2); + ad9361_spi_write(spi, REG_AUXDAC_1_CONFIG, AUXDAC_1_WORD_LSB(val) | tmp); + phy->auxdac1_value = val_mV; + break; + case 2: + ad9361_spi_write(spi, REG_AUXDAC_2_WORD, val >> 2); + ad9361_spi_write(spi, REG_AUXDAC_2_CONFIG, AUXDAC_2_WORD_LSB(val) | tmp); + phy->auxdac2_value = val_mV; + break; + default: + return -EINVAL; + } + + return 0; +} + +/** + * Get the Aux DAC value. + * @param phy The AD9361 state structure. + * @param dac The DAC. + * @return The value in case of success, negative error code otherwise. + */ +int32_t ad9361_auxdac_get(struct ad9361_rf_phy *phy, int32_t dac) +{ + + switch (dac) { + case 1: + return phy->auxdac1_value; + case 2: + return phy->auxdac2_value; + default: + return -EINVAL; + } + + return 0; +} + +/** + * Setup the AuxDAC. + * @param phy The AD9361 state structure. + * @param ctrl Pointer to auxdac_control structure. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_auxdac_setup(struct ad9361_rf_phy *phy, + struct auxdac_control *ctrl) +{ + struct spi_desc *spi = phy->spi; + uint8_t tmp; + + dev_dbg(&phy->spi->dev, "%s", __func__); + + ad9361_auxdac_set(phy, 1, ctrl->dac1_default_value); + ad9361_auxdac_set(phy, 2, ctrl->dac2_default_value); + + tmp = ~(AUXDAC_AUTO_TX_BAR(ctrl->dac2_in_tx_en << 1 | ctrl->dac1_in_tx_en) | + AUXDAC_AUTO_RX_BAR(ctrl->dac2_in_rx_en << 1 | ctrl->dac1_in_rx_en) | + AUXDAC_INIT_BAR(ctrl->dac2_in_alert_en << 1 | ctrl->dac1_in_alert_en)); + + ad9361_spi_writef(spi, REG_AUXDAC_ENABLE_CTRL, + AUXDAC_AUTO_TX_BAR(~0) | + AUXDAC_AUTO_RX_BAR(~0) | + AUXDAC_INIT_BAR(~0), + tmp); /* Auto Control */ + + ad9361_spi_writef(spi, REG_EXTERNAL_LNA_CTRL, + AUXDAC_MANUAL_SELECT, ctrl->auxdac_manual_mode_en); + ad9361_spi_write(spi, REG_AUXDAC1_RX_DELAY, ctrl->dac1_rx_delay_us); + ad9361_spi_write(spi, REG_AUXDAC1_TX_DELAY, ctrl->dac1_tx_delay_us); + ad9361_spi_write(spi, REG_AUXDAC2_RX_DELAY, ctrl->dac2_rx_delay_us); + ad9361_spi_write(spi, REG_AUXDAC2_TX_DELAY, ctrl->dac2_tx_delay_us); + + return 0; +} + +/** + * Setup the AuxADC. + * @param phy The AD9361 state structure. + * @param ctrl The AuxADC settings. + * @param bbpll_freq The BBPLL frequency [Hz]. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_auxadc_setup(struct ad9361_rf_phy *phy, + struct auxadc_control *ctrl, + uint32_t bbpll_freq) +{ + struct spi_desc *spi = phy->spi; + uint32_t val; + + dev_dbg(&phy->spi->dev, "%s", __func__); + + val = DIV_ROUND_CLOSEST(ctrl->temp_time_inteval_ms * + (bbpll_freq / 1000UL), (1 << 29)); + + ad9361_spi_write(spi, REG_TEMP_OFFSET, ctrl->offset); + ad9361_spi_write(spi, REG_START_TEMP_READING, 0x00); + ad9361_spi_write(spi, REG_TEMP_SENSE2, + MEASUREMENT_TIME_INTERVAL(val) | + (ctrl->periodic_temp_measuremnt ? + TEMP_SENSE_PERIODIC_ENABLE : 0)); + ad9361_spi_write(spi, REG_TEMP_SENSOR_CONFIG, + TEMP_SENSOR_DECIMATION( + ilog2(ctrl->temp_sensor_decimation) - 8)); + ad9361_spi_write(spi, REG_AUXADC_CLOCK_DIVIDER, + bbpll_freq / ctrl->auxadc_clock_rate); + ad9361_spi_write(spi, REG_AUXADC_CONFIG, + AUX_ADC_DECIMATION( + ilog2(ctrl->auxadc_decimation) - 8)); + + return 0; +} + +/** + * Get the measured temperature of the device. + * @param phy The AD9361 state structure. + * @return The measured temperature of the device. + */ +int32_t ad9361_get_temp(struct ad9361_rf_phy *phy) +{ + uint32_t val; + + ad9361_spi_writef(phy->spi, REG_AUXADC_CONFIG, AUXADC_POWER_DOWN, 1); + val = ad9361_spi_read(phy->spi, REG_TEMPERATURE); + ad9361_spi_writef(phy->spi, REG_AUXADC_CONFIG, AUXADC_POWER_DOWN, 0); + + return DIV_ROUND_CLOSEST(val * 1000000, 1140); +} + +/** + * Get the Aux ADC value. + * @param phy The AD9361 state structure. + * @return The value in case of success, negative error code otherwise. + */ +int32_t ad9361_get_auxadc(struct ad9361_rf_phy *phy) +{ + uint8_t buf[2]; + + ad9361_spi_writef(phy->spi, REG_AUXADC_CONFIG, AUXADC_POWER_DOWN, 1); + ad9361_spi_readm(phy->spi, REG_AUXADC_LSB, buf, 2); + ad9361_spi_writef(phy->spi, REG_AUXADC_CONFIG, AUXADC_POWER_DOWN, 0); + + return (buf[1] << 4) | AUXADC_WORD_LSB(buf[0]); +} + +/** + * Setup the Control Output pins. + * @param phy The AD9361 state structure. + * @param ctrl The Control Output pins settings. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_ctrl_outs_setup(struct ad9361_rf_phy *phy, + struct ctrl_outs_control *ctrl) +{ + struct spi_desc *spi = phy->spi; + + dev_dbg(&phy->spi->dev, "%s", __func__); + + ad9361_spi_write(spi, REG_CTRL_OUTPUT_POINTER, ctrl->index); // Ctrl Out index + return ad9361_spi_write(spi, REG_CTRL_OUTPUT_ENABLE, + ctrl->en_mask); // Ctrl Out [7:0] output enable +} + +/** + * Setup the GPO pins. + * @param phy The AD9361 state structure. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_gpo_setup(struct ad9361_rf_phy *phy, + struct gpo_control *ctrl) +{ + struct spi_desc *spi = phy->spi; + + dev_dbg(&phy->spi->dev, "%s", __func__); + + ad9361_spi_write(spi, REG_AUTO_GPO, + GPO_ENABLE_AUTO_RX(ctrl->gpo0_slave_rx_en | + (ctrl->gpo1_slave_rx_en << 1) | + (ctrl->gpo2_slave_rx_en << 2) | + (ctrl->gpo3_slave_rx_en << 3)) | + GPO_ENABLE_AUTO_TX(ctrl->gpo0_slave_tx_en | + (ctrl->gpo1_slave_tx_en << 1) | + (ctrl->gpo2_slave_tx_en << 2) | + (ctrl->gpo3_slave_tx_en << 3))); + + ad9361_spi_write(spi, REG_GPO_FORCE_AND_INIT, + GPO_MANUAL_CTRL(ctrl->gpo_manual_mode_enable_mask) | + GPO_INIT_STATE(ctrl->gpo0_inactive_state_high_en | + (ctrl->gpo1_inactive_state_high_en << 1) | + (ctrl->gpo2_inactive_state_high_en << 2) | + (ctrl->gpo3_inactive_state_high_en << 3))); + + ad9361_spi_write(spi, REG_GPO0_RX_DELAY, ctrl->gpo0_rx_delay_us); + ad9361_spi_write(spi, REG_GPO0_TX_DELAY, ctrl->gpo0_tx_delay_us); + ad9361_spi_write(spi, REG_GPO1_RX_DELAY, ctrl->gpo1_rx_delay_us); + ad9361_spi_write(spi, REG_GPO1_TX_DELAY, ctrl->gpo1_tx_delay_us); + ad9361_spi_write(spi, REG_GPO2_RX_DELAY, ctrl->gpo2_rx_delay_us); + ad9361_spi_write(spi, REG_GPO2_TX_DELAY, ctrl->gpo2_tx_delay_us); + ad9361_spi_write(spi, REG_GPO3_RX_DELAY, ctrl->gpo3_rx_delay_us); + ad9361_spi_write(spi, REG_GPO3_TX_DELAY, ctrl->gpo3_tx_delay_us); + + /* + * GPO manual mode conflicts with automatic ENSM slave and eLNA mode + */ + ad9361_spi_writef(phy->spi, REG_EXTERNAL_LNA_CTRL, GPO_MANUAL_SELECT, + ctrl->gpo_manual_mode_en); + + return 0; +} + +/** + * Setup the RSSI. + * @param phy The AD9361 state structure. + * @param ctrl The RSSI settings. + * @param is_update True if update + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_rssi_setup(struct ad9361_rf_phy *phy, + struct rssi_control *ctrl, + bool is_update) +{ + struct spi_desc *spi = phy->spi; + uint32_t total_weight, weight[4], total_dur = 0, temp; + uint8_t dur_buf[4] = { 0 }; + int32_t val, ret, i, j = 0; + uint32_t rssi_delay; + uint32_t rssi_wait; + int32_t rssi_duration; + uint32_t rate; + + dev_dbg(&phy->spi->dev, "%s", __func__); + + if (ctrl->rssi_unit_is_rx_samples) { + if (is_update) + return 0; /* no update required */ + + rssi_delay = ctrl->rssi_delay; + rssi_wait = ctrl->rssi_wait; + rssi_duration = ctrl->rssi_duration; + } else { + /* update sample based on RX rate */ + rate = DIV_ROUND_CLOSEST( + clk_get_rate(phy, phy->ref_clk_scale[RX_SAMPL_CLK]), 1000); + /* units are in us */ + rssi_delay = DIV_ROUND_CLOSEST(ctrl->rssi_delay * rate, 1000); + rssi_wait = DIV_ROUND_CLOSEST(ctrl->rssi_wait * rate, 1000); + rssi_duration = DIV_ROUND_CLOSEST( + ctrl->rssi_duration * rate, 1000); + } + + if (ctrl->restart_mode == EN_AGC_PIN_IS_PULLED_HIGH) + rssi_delay = 0; + + rssi_delay = clamp(rssi_delay / 8, 0U, 255U); + rssi_wait = clamp(rssi_wait / 4, 0U, 255U); + + do { + for (i = 14; rssi_duration > 0 && i >= 0; i--) { + val = 1 << i; + if ((int64_t)rssi_duration >= val) { + dur_buf[j++] = i; + total_dur += val; + rssi_duration -= val; + break; + } + } + + } while (j < 4 && rssi_duration > 0); + + for (i = 0, total_weight = 0; i < 4; i++) { + if (i < j) + total_weight += weight[i] = + DIV_ROUND_CLOSEST(RSSI_MAX_WEIGHT * + (1 << dur_buf[i]), total_dur); + else + total_weight += weight[i] = 0; + } + + /* total of all weights must be 0xFF */ + val = total_weight - 0xFF; + weight[j - 1] -= val; + + ad9361_spi_write(spi, REG_MEASURE_DURATION_01, + (dur_buf[1] << 4) | dur_buf[0]); // RSSI Measurement Duration 0, 1 + ad9361_spi_write(spi, REG_MEASURE_DURATION_23, + (dur_buf[3] << 4) | dur_buf[2]); // RSSI Measurement Duration 2, 3 + ad9361_spi_write(spi, REG_RSSI_WEIGHT_0, + weight[0]); // RSSI Weighted Multiplier 0 + ad9361_spi_write(spi, REG_RSSI_WEIGHT_1, + weight[1]); // RSSI Weighted Multiplier 1 + ad9361_spi_write(spi, REG_RSSI_WEIGHT_2, + weight[2]); // RSSI Weighted Multiplier 2 + ad9361_spi_write(spi, REG_RSSI_WEIGHT_3, + weight[3]); // RSSI Weighted Multiplier 3 + ad9361_spi_write(spi, REG_RSSI_DELAY, rssi_delay); // RSSI Delay + ad9361_spi_write(spi, REG_RSSI_WAIT_TIME, rssi_wait); // RSSI Wait + + temp = RSSI_MODE_SELECT(ctrl->restart_mode); + if (ctrl->restart_mode == SPI_WRITE_TO_REGISTER) + temp |= START_RSSI_MEAS; + + if (rssi_duration == 0 && j == 1) /* Power of two */ + temp |= DEFAULT_RSSI_MEAS_MODE; + + ret = ad9361_spi_write(spi, REG_RSSI_CONFIG, temp); // RSSI Mode Select + + if (ret < 0) + dev_err(&phy->spi->dev, "Unable to write rssi config"); + + return 0; +} + +/** + * This function needs to be called whenever BBPLL changes. + * @param phy The AD9361 state structure. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_bb_clk_change_handler(struct ad9361_rf_phy *phy) +{ + int32_t ret; + + ret = ad9361_gc_update(phy); + ret |= ad9361_rssi_setup(phy, &phy->pdata->rssi_ctrl, true); + ret |= ad9361_auxadc_setup(phy, &phy->pdata->auxadc_ctrl, + clk_get_rate(phy, phy->ref_clk_scale[BBPLL_CLK])); + + return ret; +} + +/** + * Set the desired Enable State Machine (ENSM) state. + * @param phy The AD9361 state structure. + * @param ensm_state The ENSM state [ENSM_STATE_SLEEP_WAIT, ENSM_STATE_ALERT, + * ENSM_STATE_TX, ENSM_STATE_TX_FLUSH, ENSM_STATE_RX, + * ENSM_STATE_RX_FLUSH, ENSM_STATE_FDD, ENSM_STATE_FDD_FLUSH]. + * @param pinctrl Set true, will enable the ENSM pin control. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_ensm_set_state(struct ad9361_rf_phy *phy, uint8_t ensm_state, + bool pinctrl) +{ + struct spi_desc *spi = phy->spi; + int32_t rc = 0; + uint32_t val; + uint32_t tmp; + + dev_dbg(dev, "Device is in %x state, moving to %x", phy->curr_ensm_state, + ensm_state); + + + if (phy->curr_ensm_state == ENSM_STATE_SLEEP) { + ad9361_spi_write(spi, REG_CLOCK_ENABLE, + DIGITAL_POWER_UP | CLOCK_ENABLE_DFLT | BBPLL_ENABLE | + (phy->pdata->use_extclk ? XO_BYPASS : 0)); /* Enable Clocks */ + udelay(20); + ad9361_spi_write(spi, REG_ENSM_CONFIG_1, TO_ALERT | FORCE_ALERT_STATE); + ad9361_trx_vco_cal_control(phy, false, true); /* Enable VCO Cal */ + ad9361_trx_vco_cal_control(phy, true, true); + } + + val = (phy->pdata->ensm_pin_pulse_mode ? 0 : LEVEL_MODE) | + (pinctrl ? ENABLE_ENSM_PIN_CTRL : 0) | + (phy->txmon_tdd_en ? ENABLE_RX_DATA_PORT_FOR_CAL : 0) | + TO_ALERT; + + switch (ensm_state) { + case ENSM_STATE_TX: + val |= FORCE_TX_ON; + if (phy->pdata->fdd) + rc = -EINVAL; + else if (phy->curr_ensm_state != ENSM_STATE_ALERT) + rc = -EINVAL; + break; + case ENSM_STATE_RX: + val |= FORCE_RX_ON; + if (phy->pdata->fdd) + rc = -EINVAL; + else if (phy->curr_ensm_state != ENSM_STATE_ALERT) + rc = -EINVAL; + break; + case ENSM_STATE_FDD: + val |= FORCE_TX_ON; + if (!phy->pdata->fdd) + rc = -EINVAL; + break; + case ENSM_STATE_ALERT: + val &= ~(FORCE_TX_ON | FORCE_RX_ON); + val |= TO_ALERT | FORCE_ALERT_STATE; + break; + case ENSM_STATE_SLEEP_WAIT: + break; + case ENSM_STATE_SLEEP: + ad9361_trx_vco_cal_control(phy, false, false); /* Disable VCO Cal */ + ad9361_trx_vco_cal_control(phy, true, false); + ad9361_spi_write(spi, REG_ENSM_CONFIG_1, 0); /* Clear To Alert */ + ad9361_spi_write(spi, REG_ENSM_CONFIG_1, + phy->pdata->fdd ? FORCE_TX_ON : FORCE_RX_ON); + /* Delay Flush Time 384 ADC clock cycles */ + udelay(384000000UL / clk_get_rate(phy, phy->ref_clk_scale[ADC_CLK])); + ad9361_spi_write(spi, REG_ENSM_CONFIG_1, 0); /* Move to Wait*/ + udelay(1); /* Wait for ENSM settle */ + ad9361_spi_write(spi, REG_CLOCK_ENABLE, + (phy->pdata->use_extclk ? XO_BYPASS : 0)); /* Turn off all clocks */ + phy->curr_ensm_state = ensm_state; + return 0; + + default: + dev_err(dev, "No handling for forcing %d ensm state", + ensm_state); + goto out; + } + + if (rc) { + if ((phy->curr_ensm_state != ENSM_STATE_ALERT) + && (val & (FORCE_RX_ON | FORCE_TX_ON))) { + uint32_t val2 = val; + + val2 &= ~(FORCE_TX_ON | FORCE_RX_ON); + val2 |= TO_ALERT | FORCE_ALERT_STATE; + ad9361_spi_write(spi, REG_ENSM_CONFIG_1, val2); + + ad9361_check_cal_done(phy, REG_STATE, ENSM_STATE(~0), ENSM_STATE_ALERT); + } else { + dev_err(dev, "Invalid ENSM state transition in %s mode", + phy->pdata->fdd ? "FDD" : "TDD"); + goto out; + } + } + + if (!phy->pdata->fdd && !pinctrl && !phy->pdata->tdd_use_dual_synth && + (ensm_state == ENSM_STATE_TX || ensm_state == ENSM_STATE_RX)) { + uint32_t reg, check; + + if (ensm_state == ENSM_STATE_TX) { + reg = REG_TX_CP_OVERRANGE_VCO_LOCK; + check = !(phy->cached_synth_pd[0] & + TX_SYNTH_VCO_POWER_DOWN); + } else { + reg = REG_RX_CP_OVERRANGE_VCO_LOCK; + check = !(phy->cached_synth_pd[1] & + RX_SYNTH_VCO_POWER_DOWN); + } + + ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2, + TXNRX_SPI_CTRL, ensm_state == ENSM_STATE_TX); + + if (check) + ad9361_check_cal_done(phy, reg, VCO_LOCK, 1); + } + + rc = ad9361_spi_write(spi, REG_ENSM_CONFIG_1, val); + if (rc) + dev_err(dev, "Failed to restore state"); + + if ((val & FORCE_RX_ON) && + (phy->agc_mode[0] == RF_GAIN_MGC || + phy->agc_mode[1] == RF_GAIN_MGC)) { + tmp = ad9361_spi_read(spi, REG_SMALL_LMT_OVERLOAD_THRESH); + ad9361_spi_write(spi, REG_SMALL_LMT_OVERLOAD_THRESH, + (tmp & SMALL_LMT_OVERLOAD_THRESH(~0)) | + (phy->agc_mode[0] == RF_GAIN_MGC ? FORCE_PD_RESET_RX1 : 0) | + (phy->agc_mode[1] == RF_GAIN_MGC ? FORCE_PD_RESET_RX2 : 0)); + ad9361_spi_write(spi, REG_SMALL_LMT_OVERLOAD_THRESH, + tmp & SMALL_LMT_OVERLOAD_THRESH(~0)); + } + + phy->curr_ensm_state = ensm_state; + +out: + return rc; + +} + +/** + * Check if at least one of the clock rates is equal to the DATA_CLK (lvds) rate. + * @param phy The AD9361 state structure. + * @param rx_path_clks RX path rates buffer. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_validate_trx_clock_chain(struct ad9361_rf_phy *phy, + uint32_t *rx_path_clks, uint32_t *tx_path_clks) +{ + static const uint32_t max_rx_rates[] = {MAX_BBPLL_FREQ, MAX_ADC_CLK, + MAX_RX_HB3, MAX_RX_HB2, MAX_RX_HB1, MAX_BASEBAND_RATE + }; + static const uint32_t max_tx_rates[] = {MAX_BBPLL_FREQ, MAX_DAC_CLK, + MAX_TX_HB3, MAX_TX_HB2, MAX_TX_HB1, MAX_BASEBAND_RATE + }; + uint32_t i; + uint32_t data_clk; + + data_clk = (phy->pdata->rx2tx2 ? 4 : 2) / + ((phy->pdata->port_ctrl.pp_conf[2] & LVDS_MODE) ? 1 : 2) * + rx_path_clks[RX_SAMPL_FREQ]; + + /* CMOS Mode */ + if (!(phy->pdata->port_ctrl.pp_conf[2] & LVDS_MODE) && + (data_clk > MAX_BASEBAND_RATE)) { + dev_err(&phy->spi->dev, + "%s: Failed CMOS MODE DATA_CLK > 61.44MSPS", __func__); + return -EINVAL; + } + + /* Validate MAX PLL, ADC, DAC and HB filter rates */ + for (i = 0; i < ARRAY_SIZE(max_rx_rates); i++) { + if (rx_path_clks[i] > max_rx_rates[i]) { + dev_err(&phy->spi->dev, + "%s: Failed RX max rate check (%"PRIu32" > %"PRIu32")", + __func__, rx_path_clks[i], max_rx_rates[i]); + return -EINVAL; + } + + if (tx_path_clks[i] > max_tx_rates[i]) { + dev_err(&phy->spi->dev, + "%s: Failed TX max rate check (%"PRIu32" > %"PRIu32")", + __func__, tx_path_clks[i], max_tx_rates[i]); + return -EINVAL; + } + } + + /* Validate that DATA_CLK exist within the clock chain */ + for (i = 1; i <= 3; i++) { + if (diff_abs(rx_path_clks[ADC_FREQ] / i, data_clk) < 4) + return 0; + } + + for (i = 1; i <= 4; i++) { + if (diff_abs((rx_path_clks[R2_FREQ] >> i), data_clk) < 4) + return 0; + } + + dev_err(&phy->spi->dev, "%s: Failed - at least one of the clock rates" + " must be equal to the DATA_CLK (lvds) rate", __func__); + + return -EINVAL; +} + +/** + * Set the RX and TX path rates. + * @param phy The AD9361 state structure. + * @param rx_path_clks RX path rates buffer. + * @param tx_path_clks TX path rates buffer. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_trx_clock_chain(struct ad9361_rf_phy *phy, + uint32_t *rx_path_clks, + uint32_t *tx_path_clks) +{ + int32_t ret, i, j, n; + + dev_dbg(&phy->spi->dev, "%s", __func__); + + if (!rx_path_clks || !tx_path_clks) + return -EINVAL; + + dev_dbg(&phy->spi->dev, + "%s: %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32, + __func__, rx_path_clks[BBPLL_FREQ], rx_path_clks[ADC_FREQ], + rx_path_clks[R2_FREQ], rx_path_clks[R1_FREQ], + rx_path_clks[CLKRF_FREQ], rx_path_clks[RX_SAMPL_FREQ]); + + dev_dbg(&phy->spi->dev, + "%s: %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32, + __func__, tx_path_clks[BBPLL_FREQ], tx_path_clks[ADC_FREQ], + tx_path_clks[R2_FREQ], tx_path_clks[R1_FREQ], + tx_path_clks[CLKRF_FREQ], tx_path_clks[RX_SAMPL_FREQ]); + + ret = ad9361_validate_trx_clock_chain(phy, rx_path_clks, tx_path_clks); + if (ret < 0) + return ret; + + ret = clk_set_rate(phy, phy->ref_clk_scale[BBPLL_CLK], + rx_path_clks[BBPLL_FREQ]); + if (ret < 0) + return ret; + + phy->current_rx_path_clks[BBPLL_FREQ] = rx_path_clks[BBPLL_FREQ]; + + for (i = ADC_CLK, j = DAC_CLK, n = ADC_FREQ; + i <= RX_SAMPL_CLK; i++, j++, n++) { + ret = clk_set_rate(phy, phy->ref_clk_scale[i], rx_path_clks[n]); + if (ret < 0) { + dev_err(dev, "Failed to set BB ref clock rate (%"PRId32")", + ret); + return ret; + } + phy->current_rx_path_clks[n] = rx_path_clks[n]; + ret = clk_set_rate(phy, phy->ref_clk_scale[j], tx_path_clks[n]); + if (ret < 0) { + dev_err(dev, "Failed to set BB ref clock rate (%"PRId32")", + ret); + return ret; + } + phy->current_tx_path_clks[n] = tx_path_clks[n]; + } + + /* + * Workaround for clock framework since clocks don't change we + * manually need to enable the filter + */ + + if (phy->rx_fir_dec == 1 || phy->bypass_rx_fir) { + ad9361_spi_writef(phy->spi, REG_RX_ENABLE_FILTER_CTRL, + RX_FIR_ENABLE_DECIMATION(~0), !phy->bypass_rx_fir); + } + + if (phy->tx_fir_int == 1 || phy->bypass_tx_fir) { + ad9361_spi_writef(phy->spi, REG_TX_ENABLE_FILTER_CTRL, + TX_FIR_ENABLE_INTERPOLATION(~0), !phy->bypass_tx_fir); + } + + /* The FIR filter once enabled causes the interface timing to change. + * It's typically not a problem if the timing margin is big enough. + * However at 61.44 MSPS it causes problems on some systems. + * So we always run the digital tune in case the filter is enabled. + * If it is disabled we restore the values from the initial calibration. + */ + + if (!phy->pdata->dig_interface_tune_fir_disable && + !(phy->bypass_tx_fir && phy->bypass_rx_fir)) + ret = ad9361_dig_tune(phy, 0, SKIP_STORE_RESULT); + + return ad9361_bb_clk_change_handler(phy); +} + +/** + * Get the RX and TX path rates. + * @param phy The AD9361 state structure. + * @param rx_path_clks RX path rates buffer. + * @param tx_path_clks TX path rates buffer. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_trx_clock_chain(struct ad9361_rf_phy *phy, + uint32_t *rx_path_clks, + uint32_t *tx_path_clks) +{ + int32_t i, j, n; + uint32_t bbpll_freq; + + if (!rx_path_clks && !tx_path_clks) + return -EINVAL; + + bbpll_freq = clk_get_rate(phy, phy->ref_clk_scale[BBPLL_CLK]); + + if (rx_path_clks) + rx_path_clks[BBPLL_FREQ] = bbpll_freq; + + if (tx_path_clks) + tx_path_clks[BBPLL_FREQ] = bbpll_freq; + + for (i = ADC_CLK, j = DAC_CLK, n = ADC_FREQ; + i <= RX_SAMPL_CLK; i++, j++, n++) { + if (rx_path_clks) + rx_path_clks[n] = clk_get_rate(phy, phy->ref_clk_scale[i]); + if (tx_path_clks) + tx_path_clks[n] = clk_get_rate(phy, phy->ref_clk_scale[j]); + } + + return 0; +} + +/** + * Calculate the RX and TX path rates to obtain the desired sample rate. + * @param phy The AD9361 state structure. + * @param tx_sample_rate The desired sample rate. + * @param rate_gov The rate governor option. + * @param rx_path_clks RX path rates buffer. + * @param tx_path_clks TX path rates buffer. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_calculate_rf_clock_chain(struct ad9361_rf_phy *phy, + uint32_t tx_sample_rate, + uint32_t rate_gov, + uint32_t *rx_path_clks, + uint32_t *tx_path_clks) +{ + uint32_t clktf, clkrf, adc_rate = 0, dac_rate = 0; + uint64_t bbpll_rate; + int32_t i, index_rx = -1, index_tx = -1, tmp; + uint32_t div, tx_intdec, rx_intdec, recursion = 1; + const int8_t clk_dividers[][4] = { + { 12, 3, 2, 2 }, + { 8, 2, 2, 2 }, + { 6, 3, 1, 2 }, + { 4, 2, 2, 1 }, + { 3, 3, 1, 1 }, + { 2, 2, 1, 1 }, + { 1, 1, 1, 1 }, + }; + + if (phy->bypass_rx_fir) + rx_intdec = 1; + else + rx_intdec = phy->rx_fir_dec; + + if (phy->bypass_tx_fir) + tx_intdec = 1; + else + tx_intdec = phy->tx_fir_int; + + if ((rate_gov == 1) && ((rx_intdec * tx_sample_rate * 8) < MIN_ADC_CLK)) { + recursion = 0; + rate_gov = 0; + } + + dev_dbg(&phy->spi->dev, + "%s: requested rate %"PRIu32" TXFIR int %"PRIu32" RXFIR dec %"PRIu32" mode %s", + __func__, tx_sample_rate, tx_intdec, rx_intdec, + rate_gov ? "Nominal" : "Highest OSR"); + + if (tx_sample_rate > MAX_BASEBAND_RATE) + return -EINVAL; + + clktf = tx_sample_rate * tx_intdec; + clkrf = tx_sample_rate * rx_intdec * (phy->rx_eq_2tx ? 2 : 1); + + for (i = rate_gov; i < 7; i++) { + adc_rate = clkrf * clk_dividers[i][0]; + dac_rate = clktf * clk_dividers[i][0]; + + if ((adc_rate <= MAX_ADC_CLK) && (adc_rate >= MIN_ADC_CLK)) { + + + if (dac_rate > adc_rate) + tmp = (dac_rate / adc_rate) * -1; + else + tmp = adc_rate / dac_rate; + + if (adc_rate <= MAX_DAC_CLK) { + index_rx = i; + index_tx = i - ((tmp == 1) ? 0 : tmp); + dac_rate = adc_rate; /* ADC_CLK */ + break; + } else { + dac_rate = adc_rate / 2; /* ADC_CLK/2 */ + index_rx = i; + + if (i == 4 && tmp >= 0) + index_tx = 7; /* STOP: 3/2 != 1 */ + else + index_tx = i + ((i == 5 && tmp >= 0) ? 1 : 2) - + ((tmp == 1) ? 0 : tmp); + + break; + } + } + } + + if ((index_tx < 0 || index_tx > 6 || index_rx < 0 || index_rx > 6) + && rate_gov < 7 && recursion) { + return ad9361_calculate_rf_clock_chain(phy, tx_sample_rate, + ++rate_gov, rx_path_clks, tx_path_clks); + } else if ((index_tx < 0 || index_tx > 6 || index_rx < 0 || index_rx > 6)) { + dev_err(&phy->spi->dev, "%s: Failed to find suitable dividers: %s", + __func__, (adc_rate < MIN_ADC_CLK) ? "ADC clock below limit" : + "BBPLL rate above limit"); + + return -EINVAL; + } + + /* Calculate target BBPLL rate */ + div = MAX_BBPLL_DIV; + + do { + bbpll_rate = (uint64_t)adc_rate * div; + div >>= 1; + + } while ((bbpll_rate > MAX_BBPLL_FREQ) && (div >= MIN_BBPLL_DIV)); + + rx_path_clks[BBPLL_FREQ] = bbpll_rate; + rx_path_clks[ADC_FREQ] = adc_rate; + rx_path_clks[R2_FREQ] = rx_path_clks[ADC_FREQ] / clk_dividers[index_rx][1]; + rx_path_clks[R1_FREQ] = rx_path_clks[R2_FREQ] / clk_dividers[index_rx][2]; + rx_path_clks[CLKRF_FREQ] = rx_path_clks[R1_FREQ] / clk_dividers[index_rx][3]; + rx_path_clks[RX_SAMPL_FREQ] = rx_path_clks[CLKRF_FREQ] / rx_intdec; + + tx_path_clks[BBPLL_FREQ] = bbpll_rate; + tx_path_clks[DAC_FREQ] = dac_rate; + tx_path_clks[T2_FREQ] = tx_path_clks[DAC_FREQ] / clk_dividers[index_tx][1]; + tx_path_clks[T1_FREQ] = tx_path_clks[T2_FREQ] / clk_dividers[index_tx][2]; + tx_path_clks[CLKTF_FREQ] = tx_path_clks[T1_FREQ] / clk_dividers[index_tx][3]; + tx_path_clks[TX_SAMPL_FREQ] = tx_path_clks[CLKTF_FREQ] / tx_intdec; + + return 0; +} + +/** + * Set the desired sample rate. + * @param phy The AD9361 state structure. + * @param freq The desired sample rate. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_trx_clock_chain_freq(struct ad9361_rf_phy *phy, + uint32_t freq) +{ + uint32_t rx[6], tx[6]; + int32_t ret; + + ret = ad9361_calculate_rf_clock_chain(phy, freq, + phy->rate_governor, rx, tx); + if (ret < 0) + return ret; + return ad9361_set_trx_clock_chain(phy, rx, tx); +} + +/** + * Internal ENSM mode options helper function. + * @param phy The AD9361 state structure. + * @param fdd + * @param pinctrl + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_ensm_mode(struct ad9361_rf_phy *phy, bool fdd, bool pinctrl) +{ + struct ad9361_phy_platform_data *pd = phy->pdata; + int32_t ret; + uint32_t val = 0; + + ad9361_spi_write(phy->spi, REG_ENSM_MODE, fdd ? FDD_MODE : 0); + + val = ad9361_spi_read(phy->spi, REG_ENSM_CONFIG_2); + val &= POWER_DOWN_RX_SYNTH | POWER_DOWN_TX_SYNTH | + RX_SYNTH_READY_MASK | TX_SYNTH_READY_MASK; + + if (fdd) + ret = ad9361_spi_write(phy->spi, REG_ENSM_CONFIG_2, + val | DUAL_SYNTH_MODE | + (pd->fdd_independent_mode ? FDD_EXTERNAL_CTRL_ENABLE : 0)); + else + ret = ad9361_spi_write(phy->spi, REG_ENSM_CONFIG_2, val | + (pd->tdd_use_dual_synth ? DUAL_SYNTH_MODE : 0) | + (pd->tdd_use_dual_synth ? 0 : + (pinctrl ? SYNTH_ENABLE_PIN_CTRL_MODE : 0))); + + return ret; +} + +/** + * Fastlock read value. + * @param spi + * @param tx + * @param profile + * @param word + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_fastlock_readval(struct spi_desc *spi, bool tx, + uint32_t profile, uint32_t word) +{ + uint32_t offs = 0; + + if (tx) + offs = REG_TX_FAST_LOCK_SETUP - REG_RX_FAST_LOCK_SETUP; + + ad9361_spi_write(spi, REG_RX_FAST_LOCK_PROGRAM_ADDR + offs, + RX_FAST_LOCK_PROFILE_ADDR(profile) | + RX_FAST_LOCK_PROFILE_WORD(word)); + + return ad9361_spi_read(spi, REG_RX_FAST_LOCK_PROGRAM_READ + offs); +} + +/** + * Fastlock write value. + * @param spi + * @param tx + * @param profile + * @param word + * @param val + * @param last + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_fastlock_writeval(struct spi_desc *spi, bool tx, + uint32_t profile, uint32_t word, uint8_t val, bool last) +{ + uint32_t offs = 0; + int32_t ret; + + if (tx) + offs = REG_TX_FAST_LOCK_SETUP - REG_RX_FAST_LOCK_SETUP; + + ret = ad9361_spi_write(spi, REG_RX_FAST_LOCK_PROGRAM_ADDR + offs, + RX_FAST_LOCK_PROFILE_ADDR(profile) | + RX_FAST_LOCK_PROFILE_WORD(word)); + ret |= ad9361_spi_write(spi, REG_RX_FAST_LOCK_PROGRAM_DATA + offs, val); + ret |= ad9361_spi_write(spi, REG_RX_FAST_LOCK_PROGRAM_CTRL + offs, + RX_FAST_LOCK_PROGRAM_WRITE | + RX_FAST_LOCK_PROGRAM_CLOCK_ENABLE); + + if (last) /* Stop Clocks */ + ret |= ad9361_spi_write(spi, + REG_RX_FAST_LOCK_PROGRAM_CTRL + offs, 0); + + return ret; +} + +/** + * Fastlock load values. + * @param phy The AD9361 state structure. + * @param tx + * @param profile + * @param values + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_fastlock_load(struct ad9361_rf_phy *phy, bool tx, + uint32_t profile, uint8_t *values) +{ + uint32_t offs = 0; + int32_t i, ret = 0; + uint8_t buf[4]; + + dev_dbg(&phy->spi->dev, "%s: %s Profile %"PRIu32":", + __func__, tx ? "TX" : "RX", profile); + + if (tx) + offs = REG_TX_FAST_LOCK_SETUP - REG_RX_FAST_LOCK_SETUP; + + buf[0] = values[0]; + buf[1] = RX_FAST_LOCK_PROFILE_ADDR(profile) | RX_FAST_LOCK_PROFILE_WORD(0); + ad9361_spi_writem(phy->spi, REG_RX_FAST_LOCK_PROGRAM_DATA + offs, buf, 2); + + for (i = 1; i < RX_FAST_LOCK_CONFIG_WORD_NUM; i++) { + buf[0] = RX_FAST_LOCK_PROGRAM_WRITE | RX_FAST_LOCK_PROGRAM_CLOCK_ENABLE; + buf[1] = 0; + buf[2] = values[i]; + buf[3] = RX_FAST_LOCK_PROFILE_ADDR(profile) | RX_FAST_LOCK_PROFILE_WORD(i); + ad9361_spi_writem(phy->spi, REG_RX_FAST_LOCK_PROGRAM_CTRL + offs, buf, 4); + } + + ad9361_spi_write(phy->spi, REG_RX_FAST_LOCK_PROGRAM_CTRL + offs, + RX_FAST_LOCK_PROGRAM_WRITE | RX_FAST_LOCK_PROGRAM_CLOCK_ENABLE); + ad9361_spi_write(phy->spi, REG_RX_FAST_LOCK_PROGRAM_CTRL + offs, 0); + + phy->fastlock.entry[tx][profile].flags = FASTLOOK_INIT; + phy->fastlock.entry[tx][profile].alc_orig = values[15]; + phy->fastlock.entry[tx][profile].alc_written = values[15]; + + return ret; +} + +/** + * Fastlock store. + * @param phy The AD9361 state structure. + * @param tx + * @param profile + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_fastlock_store(struct ad9361_rf_phy *phy, bool tx, + uint32_t profile) +{ + struct spi_desc *spi = phy->spi; + uint8_t val[16]; + uint32_t offs = 0, x, y; + + dev_dbg(&phy->spi->dev, "%s: %s Profile %"PRIu32":", + __func__, tx ? "TX" : "RX", profile); + + if (tx) + offs = REG_TX_FAST_LOCK_SETUP - REG_RX_FAST_LOCK_SETUP; + + val[0] = ad9361_spi_read(spi, REG_RX_INTEGER_BYTE_0 + offs); + val[1] = ad9361_spi_read(spi, REG_RX_INTEGER_BYTE_1 + offs); + val[2] = ad9361_spi_read(spi, REG_RX_FRACT_BYTE_0 + offs); + val[3] = ad9361_spi_read(spi, REG_RX_FRACT_BYTE_1 + offs); + val[4] = ad9361_spi_read(spi, REG_RX_FRACT_BYTE_2 + offs); + + x = ad9361_spi_readf(spi, REG_RX_VCO_BIAS_1 + offs, VCO_BIAS_REF(~0)); + y = ad9361_spi_readf(spi, REG_RX_ALC_VARACTOR + offs, VCO_VARACTOR(~0)); + val[5] = (x << 4) | y; + + x = ad9361_spi_readf(spi, REG_RX_VCO_BIAS_1 + offs, VCO_BIAS_TCF(~0)); + y = ad9361_spi_readf(spi, REG_RX_CP_CURRENT + offs, CHARGE_PUMP_CURRENT(~0)); + /* Wide BW option: N = 1 + * Set init and steady state values to the same - let user space handle it + */ + val[6] = (x << 3) | y; + val[7] = y; + + x = ad9361_spi_readf(spi, REG_RX_LOOP_FILTER_3 + offs, LOOP_FILTER_R3(~0)); + val[8] = (x << 4) | x; + + x = ad9361_spi_readf(spi, REG_RX_LOOP_FILTER_2 + offs, LOOP_FILTER_C3(~0)); + val[9] = (x << 4) | x; + + x = ad9361_spi_readf(spi, REG_RX_LOOP_FILTER_1 + offs, LOOP_FILTER_C1(~0)); + y = ad9361_spi_readf(spi, REG_RX_LOOP_FILTER_1 + offs, LOOP_FILTER_C2(~0)); + val[10] = (x << 4) | y; + + x = ad9361_spi_readf(spi, REG_RX_LOOP_FILTER_2 + offs, LOOP_FILTER_R1(~0)); + val[11] = (x << 4) | x; + + x = ad9361_spi_readf(spi, REG_RX_VCO_VARACTOR_CTRL_0 + offs, + VCO_VARACTOR_REFERENCE_TCF(~0)); + y = ad9361_spi_readf(spi, REG_RFPLL_DIVIDERS, + tx ? TX_VCO_DIVIDER(~0) : RX_VCO_DIVIDER(~0)); + val[12] = (x << 4) | y; + + x = ad9361_spi_readf(spi, REG_RX_FORCE_VCO_TUNE_1 + offs, VCO_CAL_OFFSET(~0)); + y = ad9361_spi_readf(spi, REG_RX_VCO_VARACTOR_CTRL_1 + offs, + VCO_VARACTOR_REFERENCE(~0)); + val[13] = (x << 4) | y; + + val[14] = ad9361_spi_read(spi, REG_RX_FORCE_VCO_TUNE_0 + offs); + + x = ad9361_spi_readf(spi, REG_RX_FORCE_ALC + offs, FORCE_ALC_WORD(~0)); + y = ad9361_spi_readf(spi, REG_RX_FORCE_VCO_TUNE_1 + offs, FORCE_VCO_TUNE); + val[15] = (x << 1) | y; + + return ad9361_fastlock_load(phy, tx, profile, val); +} + +/** + * Fastlock prepare. + * @param phy The AD9361 state structure. + * @param tx + * @param profile + * @param prepare + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_fastlock_prepare(struct ad9361_rf_phy *phy, bool tx, + uint32_t profile, bool prepare) +{ + uint32_t offs, ready_mask; + bool is_prepared; + + dev_dbg(&phy->spi->dev, "%s: %s Profile %"PRIu32": %s", + __func__, tx ? "TX" : "RX", profile, + prepare ? "Prepare" : "Un-Prepare"); + + if (tx) { + offs = REG_TX_FAST_LOCK_SETUP - REG_RX_FAST_LOCK_SETUP; + ready_mask = TX_SYNTH_READY_MASK; + } else { + offs = 0; + ready_mask = RX_SYNTH_READY_MASK; + } + + is_prepared = !!phy->fastlock.current_profile[tx]; + + if (prepare && !is_prepared) { + ad9361_spi_write(phy->spi, + REG_RX_FAST_LOCK_SETUP_INIT_DELAY + offs, + (tx ? phy->pdata->tx_fastlock_delay_ns : + phy->pdata->rx_fastlock_delay_ns) / 250); + ad9361_spi_write(phy->spi, REG_RX_FAST_LOCK_SETUP + offs, + RX_FAST_LOCK_PROFILE(profile) | + RX_FAST_LOCK_MODE_ENABLE); + ad9361_spi_write(phy->spi, REG_RX_FAST_LOCK_PROGRAM_CTRL + offs, + 0); + + ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2, ready_mask, 1); + ad9361_trx_vco_cal_control(phy, tx, false); + } else if (!prepare && is_prepared) { + ad9361_spi_write(phy->spi, REG_RX_FAST_LOCK_SETUP + offs, 0); + + /* Workaround: Exiting Fastlock Mode */ + ad9361_spi_writef(phy->spi, REG_RX_FORCE_ALC + offs, FORCE_ALC_ENABLE, 1); + ad9361_spi_writef(phy->spi, REG_RX_FORCE_VCO_TUNE_1 + offs, FORCE_VCO_TUNE, 1); + ad9361_spi_writef(phy->spi, REG_RX_FORCE_ALC + offs, FORCE_ALC_ENABLE, 0); + ad9361_spi_writef(phy->spi, REG_RX_FORCE_VCO_TUNE_1 + offs, FORCE_VCO_TUNE, 0); + + ad9361_trx_vco_cal_control(phy, tx, true); + ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2, ready_mask, 0); + + phy->fastlock.current_profile[tx] = 0; + } + + return 0; +} + +/** + * Fastlock recall. + * @param phy The AD9361 state structure. + * @param tx + * @param profile + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_fastlock_recall(struct ad9361_rf_phy *phy, bool tx, + uint32_t profile) +{ + uint32_t offs = 0; + uint8_t curr, _new, orig, current_profile; + + dev_dbg(&phy->spi->dev, "%s: %s Profile %"PRIu32":", + __func__, tx ? "TX" : "RX", profile); + + if (tx) + offs = REG_TX_FAST_LOCK_SETUP - REG_RX_FAST_LOCK_SETUP; + + if (phy->fastlock.entry[tx][profile].flags != FASTLOOK_INIT) + return -EINVAL; + + /* Workaround: Lock problem with same ALC word */ + + current_profile = phy->fastlock.current_profile[tx]; + _new = phy->fastlock.entry[tx][profile].alc_written; + + if (current_profile == 0) + curr = ad9361_spi_readf(phy->spi, REG_RX_FORCE_ALC + offs, + FORCE_ALC_WORD(~0)) << 1; + else + curr = phy->fastlock.entry[tx][current_profile - 1].alc_written; + + if ((curr >> 1) == (_new >> 1)) { + orig = phy->fastlock.entry[tx][profile].alc_orig; + + if ((orig >> 1) == (_new >> 1)) + phy->fastlock.entry[tx][profile].alc_written += 2; + else + phy->fastlock.entry[tx][profile].alc_written = orig; + + ad9361_fastlock_writeval(phy->spi, tx, profile, 0xF, + phy->fastlock.entry[tx][profile].alc_written, true); + } + + ad9361_fastlock_prepare(phy, tx, profile, true); + phy->fastlock.current_profile[tx] = profile + 1; + + return ad9361_spi_write(phy->spi, REG_RX_FAST_LOCK_SETUP + offs, + RX_FAST_LOCK_PROFILE(profile) | + (phy->pdata->trx_fastlock_pinctrl_en[tx] ? + RX_FAST_LOCK_PROFILE_PIN_SELECT : 0) | + RX_FAST_LOCK_MODE_ENABLE); +} + +/** + * Fastlock save. + * @param phy The AD9361 state structure. + * @param tx + * @param profile + * @param values + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_fastlock_save(struct ad9361_rf_phy *phy, bool tx, + uint32_t profile, uint8_t *values) +{ + int32_t i; + + dev_dbg(&phy->spi->dev, "%s: %s Profile %"PRIu32":", + __func__, tx ? "TX" : "RX", profile); + + for (i = 0; i < RX_FAST_LOCK_CONFIG_WORD_NUM; i++) + values[i] = ad9361_fastlock_readval(phy->spi, tx, profile, i); + + return 0; +} + +/** + * Multi Chip Sync (MCS) config. + * @param phy The AD9361 state structure. + * @param step MCS step. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_mcs(struct ad9361_rf_phy *phy, int32_t step) +{ + int32_t mcs_mask = MCS_RF_ENABLE | MCS_BBPLL_ENABLE | + MCS_DIGITAL_CLK_ENABLE | MCS_BB_ENABLE; + + dev_dbg(&phy->spi->dev, "%s: MCS step %"PRId32, __func__, step); + + switch (step) { + case 1: + /* REVIST: + * POWER_DOWN_TRX_SYNTH and MCS_RF_ENABLE somehow conflict + */ + ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2, + POWER_DOWN_TX_SYNTH | POWER_DOWN_RX_SYNTH, 0); + + ad9361_spi_writef(phy->spi, REG_MULTICHIP_SYNC_AND_TX_MON_CTRL, + mcs_mask, MCS_BB_ENABLE | MCS_BBPLL_ENABLE | MCS_RF_ENABLE); + ad9361_spi_writef(phy->spi, REG_CP_BLEED_CURRENT, + MCS_REFCLK_SCALE_EN, 1); + break; + case 2: + if(!phy->gpio_desc_sync) + break; + /* + * NOTE: This is not a regular GPIO - + * HDL ensures Multi-chip Synchronization SYNC_IN Pulse Timing + * relative to rising and falling edge of REF_CLK + */ + gpio_set_value(phy->gpio_desc_sync, 1); + gpio_set_value(phy->gpio_desc_sync, 0); + break; + case 3: + ad9361_spi_writef(phy->spi, REG_MULTICHIP_SYNC_AND_TX_MON_CTRL, + mcs_mask, MCS_BB_ENABLE | MCS_DIGITAL_CLK_ENABLE | MCS_RF_ENABLE); + break; + case 4: + if(!phy->gpio_desc_sync) + break; + gpio_set_value(phy->gpio_desc_sync, 1); + gpio_set_value(phy->gpio_desc_sync, 0); + break; + case 5: + ad9361_spi_writef(phy->spi, REG_MULTICHIP_SYNC_AND_TX_MON_CTRL, + mcs_mask, MCS_RF_ENABLE); + break; + } + + return 0; +} + +/** + * Clear state. + * @param phy The AD9361 state structure. + * @return None. + */ +void ad9361_clear_state(struct ad9361_rf_phy *phy) +{ + phy->current_table = NO_GAIN_TABLE; + phy->bypass_tx_fir = true; + phy->bypass_rx_fir = true; + phy->rate_governor = 1; + phy->rfdc_track_en = true; + phy->bbdc_track_en = true; + phy->quad_track_en = true; + phy->prev_ensm_state = 0; + phy->curr_ensm_state = 0; + phy->auto_cal_en = false; + phy->manual_tx_quad_cal_en = false; + phy->last_tx_quad_cal_freq = 0; + phy->flags = 0; + phy->current_rx_bw_Hz = 0; + phy->current_tx_bw_Hz = 0; + phy->rxbbf_div = 0; + phy->tx_fir_int = 0; + phy->tx_fir_ntaps = 0; + phy->rx_fir_dec = 0; + phy->rx_fir_ntaps = 0; + phy->ensm_pin_ctl_en = false; + phy->txmon_tdd_en = 0; + phy->current_tx_lo_freq = 0; + phy->current_rx_lo_freq = 0; + phy->current_tx_use_tdd_table = false; + phy->current_rx_use_tdd_table = false; + phy->cached_synth_pd[0] = 0; + phy->cached_synth_pd[1] = 0; + + memset(&phy->fastlock, 0, sizeof(phy->fastlock)); +} + +/** + * Determine the reference frequency value. + * @param refin_Hz Maximum allowed frequency. + * @param max Reference in frequency value. + * @return Reference frequency value. + */ +static uint32_t ad9361_ref_div_sel(uint32_t refin_Hz, uint32_t max) +{ + if (refin_Hz <= (max / 2)) + return 2 * refin_Hz; + else if (refin_Hz <= max) + return refin_Hz; + else if (refin_Hz <= (max * 2)) + return refin_Hz / 2; + else if (refin_Hz <= (max * 4)) + return refin_Hz / 4; + else + return 0; +} + +/** + * Setup the AD9361 device. + * @param phy The AD9361 state structure. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_setup(struct ad9361_rf_phy *phy) +{ + uint32_t refin_Hz, ref_freq, bbpll_freq; + struct spi_desc *spi = phy->spi; + struct ad9361_phy_platform_data *pd = phy->pdata; + int32_t ret; + uint32_t real_rx_bandwidth, real_tx_bandwidth; + bool tmp_use_ext_rx_lo = pd->use_ext_rx_lo; + bool tmp_use_ext_tx_lo = pd->use_ext_tx_lo; + + dev_dbg(dev, "%s", __func__); + + pd->rf_rx_bandwidth_Hz = ad9361_validate_rf_bw(phy, pd->rf_rx_bandwidth_Hz); + pd->rf_tx_bandwidth_Hz = ad9361_validate_rf_bw(phy, pd->rf_tx_bandwidth_Hz); + + real_rx_bandwidth = pd->rf_rx_bandwidth_Hz / 2; + real_tx_bandwidth = pd->rf_tx_bandwidth_Hz / 2; + + if (pd->fdd) { + pd->tdd_skip_vco_cal = false; + if (pd->ensm_pin_ctrl && pd->fdd_independent_mode) { + dev_warn(dev, + "%s: Either set ENSM PINCTRL or FDD Independent Mode", + __func__); + pd->ensm_pin_ctrl = false; + } + } + + ret = ad9361_auxdac_setup(phy, &pd->auxdac_ctrl); + if (ret < 0) + return ret; + + ret = ad9361_gpo_setup(phy, &pd->gpo_ctrl); + if (ret < 0) + return ret; + + if (pd->port_ctrl.pp_conf[2] & FDD_RX_RATE_2TX_RATE) + phy->rx_eq_2tx = true; + + ad9361_spi_write(spi, REG_CTRL, CTRL_ENABLE); + ad9361_spi_write(spi, REG_BANDGAP_CONFIG0, + MASTER_BIAS_TRIM(0x0E)); /* Enable Master Bias */ + ad9361_spi_write(spi, REG_BANDGAP_CONFIG1, + BANDGAP_TEMP_TRIM(0x0E)); /* Set Bandgap Trim */ + + ad9361_set_dcxo_tune(phy, pd->dcxo_coarse, pd->dcxo_fine); + + refin_Hz = phy->clk_refin->rate; + + ref_freq = ad9361_ref_div_sel(refin_Hz, MAX_BBPLL_FREF); + if (!ref_freq) + return -EINVAL; + + ad9361_spi_writef(spi, REG_REF_DIVIDE_CONFIG_1, RX_REF_RESET_BAR, 1); + ad9361_spi_writef(spi, REG_REF_DIVIDE_CONFIG_2, TX_REF_RESET_BAR, 1); + ad9361_spi_writef(spi, REG_REF_DIVIDE_CONFIG_2, + TX_REF_DOUBLER_FB_DELAY(~0), 3); /* FB DELAY */ + ad9361_spi_writef(spi, REG_REF_DIVIDE_CONFIG_2, + RX_REF_DOUBLER_FB_DELAY(~0), 3); /* FB DELAY */ + + ad9361_spi_write(spi, REG_CLOCK_ENABLE, + DIGITAL_POWER_UP | CLOCK_ENABLE_DFLT | BBPLL_ENABLE | + (pd->use_extclk ? XO_BYPASS : 0)); /* Enable Clocks */ + + ret = clk_set_rate(phy, phy->ref_clk_scale[BB_REFCLK], ref_freq); + if (ret < 0) { + dev_err(dev, "Failed to set BB ref clock rate (%"PRId32")", + ret); + return ret; + } + + ret = clk_prepare_enable(phy->clks[BB_REFCLK]); + if (ret < 0) { + dev_err(dev, "Failed to enable BB ref clock rate (%"PRId32")", + ret); + return ret; + } + + ad9361_spi_write(spi, REG_FRACT_BB_FREQ_WORD_2, 0x12); + ad9361_spi_write(spi, REG_FRACT_BB_FREQ_WORD_3, 0x34); + + ret = ad9361_set_trx_clock_chain(phy, pd->rx_path_clks, + pd->tx_path_clks); + if (ret < 0) + return ret; + + if (!pd->rx2tx2) { + pd->rx1tx1_mode_use_tx_num = + clamp_t(uint32_t, pd->rx1tx1_mode_use_tx_num, TX_1, TX_2); + pd->rx1tx1_mode_use_rx_num = + clamp_t(uint32_t, pd->rx1tx1_mode_use_rx_num, RX_1, RX_2); + + ad9361_en_dis_tx(phy, TX_1 | TX_2, pd->rx1tx1_mode_use_tx_num); + ad9361_en_dis_rx(phy, TX_1 | TX_2, pd->rx1tx1_mode_use_rx_num); + } else { + ad9361_en_dis_tx(phy, TX_1 | TX_2, TX_1 | TX_2); + ad9361_en_dis_rx(phy, RX_1 | RX_2, RX_1 | RX_2); + } + + ret = ad9361_rf_port_setup(phy, true, pd->rf_rx_input_sel, + pd->rf_tx_output_sel); + if (ret < 0) + return ret; + + ret = ad9361_pp_port_setup(phy, false); + if (ret < 0) + return ret; + + bbpll_freq = clk_get_rate(phy, phy->ref_clk_scale[BBPLL_CLK]); + ret = ad9361_auxadc_setup(phy, &pd->auxadc_ctrl, bbpll_freq); + if (ret < 0) + return ret; + + ret = ad9361_ctrl_outs_setup(phy, &pd->ctrl_outs_ctrl); + if (ret < 0) + return ret; + + ret = ad9361_set_ref_clk_cycles(phy, refin_Hz); + if (ret < 0) + return ret; + + ret = ad9361_setup_ext_lna(phy, &pd->elna_ctrl); + if (ret < 0) + return ret; + + /* + * This allows forcing a lower F_REF window + * (worse phase noise, better fractional spurs) + */ + pd->trx_synth_max_fref = clamp_t(uint32_t, pd->trx_synth_max_fref, + MIN_SYNTH_FREF, MAX_SYNTH_FREF); + + ref_freq = ad9361_ref_div_sel(refin_Hz, pd->trx_synth_max_fref); + if (!ref_freq) + return -EINVAL; + + ret = clk_set_rate(phy, phy->ref_clk_scale[RX_REFCLK], ref_freq); + if (ret < 0) { + dev_err(dev, "Failed to set RX Synth ref clock rate (%"PRId32")", ret); + return ret; + } + + ret = clk_set_rate(phy, phy->ref_clk_scale[TX_REFCLK], ref_freq); + if (ret < 0) { + dev_err(dev, "Failed to set TX Synth ref clock rate (%"PRId32")", ret); + return ret; + } + + ret = ad9361_txrx_synth_cp_calib(phy, ref_freq, false); /* RXCP */ + if (ret < 0) + return ret; + + ret = ad9361_txrx_synth_cp_calib(phy, ref_freq, true); /* TXCP */ + if (ret < 0) + return ret; + + phy->pdata->use_ext_rx_lo = 0; + phy->pdata->use_ext_tx_lo = 0; + + ret = clk_set_rate(phy, phy->ref_clk_scale[RX_RFPLL], + ad9361_to_clk(pd->rx_synth_freq)); + if (ret < 0) { + dev_err(dev, "Failed to set RX Synth rate (%"PRId32")", + ret); + return ret; + } + + ret = clk_prepare_enable(phy->clks[RX_REFCLK]); + if (ret < 0) { + dev_err(dev, "Failed to enable RX Synth ref clock (%"PRId32")", ret); + return ret; + } + + ret = clk_prepare_enable(phy->clks[RX_RFPLL]); + if (ret < 0) + return ret; + + /* Skip quad cal here we do it later again */ + phy->last_tx_quad_cal_freq = pd->tx_synth_freq; + ret = clk_set_rate(phy, phy->ref_clk_scale[TX_RFPLL], + ad9361_to_clk(pd->tx_synth_freq)); + if (ret < 0) { + dev_err(dev, "Failed to set TX Synth rate (%"PRId32")", + ret); + return ret; + } + + ret = clk_prepare_enable(phy->clks[TX_REFCLK]); + if (ret < 0) { + dev_err(dev, "Failed to enable TX Synth ref clock (%"PRId32")", ret); + return ret; + } + + ret = clk_prepare_enable(phy->clks[TX_RFPLL]); + if (ret < 0) + return ret; + + phy->pdata->use_ext_rx_lo = tmp_use_ext_rx_lo; + phy->pdata->use_ext_tx_lo = tmp_use_ext_tx_lo; + + ad9361_clk_mux_set_parent(phy->ref_clk_scale[RX_RFPLL], + pd->use_ext_rx_lo); + + ad9361_clk_mux_set_parent(phy->ref_clk_scale[TX_RFPLL], + pd->use_ext_tx_lo); + + ret = ad9361_load_mixer_gm_subtable(phy); + if (ret < 0) + return ret; + + ret = ad9361_gc_setup(phy, &pd->gain_ctrl); + if (ret < 0) + return ret; + + ret = ad9361_rx_bb_analog_filter_calib(phy, + real_rx_bandwidth, + bbpll_freq); + if (ret < 0) + return ret; + + ret = ad9361_tx_bb_analog_filter_calib(phy, + real_tx_bandwidth, + bbpll_freq); + if (ret < 0) + return ret; + + ret = ad9361_rx_tia_calib(phy, real_rx_bandwidth); + if (ret < 0) + return ret; + + ret = ad9361_tx_bb_second_filter_calib(phy, real_tx_bandwidth); + if (ret < 0) + return ret; + + ret = ad9361_rx_adc_setup(phy, + bbpll_freq, + clk_get_rate(phy, phy->ref_clk_scale[ADC_CLK])); + if (ret < 0) + return ret; + + ret = ad9361_bb_dc_offset_calib(phy); + if (ret < 0) + return ret; + + ret = ad9361_rf_dc_offset_calib(phy, + ad9361_from_clk(clk_get_rate(phy, phy->ref_clk_scale[RX_RFPLL]))); + if (ret < 0) + return ret; + + phy->current_rx_bw_Hz = pd->rf_rx_bandwidth_Hz; + phy->current_tx_bw_Hz = pd->rf_tx_bandwidth_Hz; + phy->last_tx_quad_cal_phase = ~0; + ret = ad9361_tx_quad_calib(phy, real_rx_bandwidth, real_tx_bandwidth, -1); + if (ret < 0) + return ret; + + ret = ad9361_tracking_control(phy, phy->bbdc_track_en, + phy->rfdc_track_en, phy->quad_track_en); + if (ret < 0) + return ret; + + ad9361_pp_port_setup(phy, true); + + ret = ad9361_set_ensm_mode(phy, pd->fdd, pd->ensm_pin_ctrl); + if (ret < 0) + return ret; + + ad9361_spi_writef(phy->spi, REG_TX_ATTEN_OFFSET, + MASK_CLR_ATTEN_UPDATE, 0); + + ret = ad9361_set_tx_atten(phy, pd->tx_atten, + pd->rx2tx2 ? true : pd->rx1tx1_mode_use_tx_num == 1, + pd->rx2tx2 ? true : pd->rx1tx1_mode_use_tx_num == 2, true); + if (ret < 0) + return ret; + + if (!pd->rx2tx2) { + ret = ad9361_set_tx_atten(phy, 89750, + pd->rx1tx1_mode_use_tx_num == 2, + pd->rx1tx1_mode_use_tx_num == 1, true); + if (ret < 0) + return ret; + } + + ret = ad9361_rssi_setup(phy, &pd->rssi_ctrl, false); + if (ret < 0) + return ret; + + ret = ad9361_clkout_control(phy, pd->ad9361_clkout_mode); + if (ret < 0) + return ret; + + ret = ad9361_txmon_setup(phy, &pd->txmon_ctrl); + if (ret < 0) + return ret; + + phy->curr_ensm_state = ad9361_spi_readf(spi, REG_STATE, ENSM_STATE(~0)); + ad9361_ensm_set_state(phy, pd->fdd ? ENSM_STATE_FDD : ENSM_STATE_RX, + pd->ensm_pin_ctrl); + + phy->auto_cal_en = true; + phy->cal_threshold_freq = 100000000ULL; /* 100 MHz */ + + return 0; + +} + +/** + * Perform the selected calibration + * @param phy The AD9361 state structure. + * @param cal The selected calibration. + * @param arg The argument of the calibration. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_do_calib_run(struct ad9361_rf_phy *phy, uint32_t cal, + int32_t arg) +{ + int32_t ret; + + dev_dbg(&phy->spi->dev, "%s: CAL %"PRIu32" ARG %"PRId32, __func__, cal, arg); + + ret = ad9361_tracking_control(phy, false, false, false); + if (ret < 0) + return ret; + + ad9361_ensm_force_state(phy, ENSM_STATE_ALERT); + + switch (cal) { + case TX_QUAD_CAL: + ret = ad9361_tx_quad_calib(phy, phy->current_rx_bw_Hz / 2, + phy->current_tx_bw_Hz / 2, arg); + break; + case RFDC_CAL: + ret = ad9361_rf_dc_offset_calib(phy, + ad9361_from_clk(clk_get_rate(phy, phy->ref_clk_scale[RX_RFPLL]))); + break; + default: + ret = -EINVAL; + break; + } + + ret = ad9361_tracking_control(phy, phy->bbdc_track_en, + phy->rfdc_track_en, phy->quad_track_en); + ad9361_ensm_restore_prev_state(phy); + + return ret; +} + +/** + * Set the RF bandwidth. + * @param phy The AD9361 state structure. + * @param rf_rx_bw The desired RX bandwidth [Hz]. + * @param rf_tx_bw The desired TX bandwidth [Hz]. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_update_rf_bandwidth(struct ad9361_rf_phy *phy, + uint32_t rf_rx_bw, uint32_t rf_tx_bw) +{ + int32_t ret; + + ret = ad9361_tracking_control(phy, false, false, false); + if (ret < 0) + return ret; + + ad9361_ensm_force_state(phy, ENSM_STATE_ALERT); + + ret = __ad9361_update_rf_bandwidth(phy, rf_rx_bw, rf_tx_bw); + if (ret < 0) + return ret; + + phy->current_rx_bw_Hz = rf_rx_bw; + phy->current_tx_bw_Hz = rf_tx_bw; + + if (phy->manual_tx_quad_cal_en == false) { + ret = ad9361_tx_quad_calib(phy, rf_rx_bw / 2, rf_tx_bw / 2, -1); + if (ret < 0) + return ret; + } + + ret = ad9361_tracking_control(phy, phy->bbdc_track_en, + phy->rfdc_track_en, phy->quad_track_en); + if (ret < 0) + return ret; + + ad9361_ensm_restore_prev_state(phy); + + return 0; +} + +/** + * Verify the FIR filter coefficients. + * @param phy The AD9361 state structure. + * @param dest Destination identifier (RX1,2 / TX1,2). + * @param ntaps Number of filter Taps. + * @param coef Pointer to filter coefficients. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_verify_fir_filter_coef(struct ad9361_rf_phy *phy, + enum fir_dest dest, + uint32_t ntaps, short *coef) +{ + struct spi_desc *spi = phy->spi; + uint32_t val, offs = 0, gain = 0, conf, sel, cnt; + int32_t ret = 0; + +#ifndef DEBUG + return 0; +#endif + dev_dbg(&phy->spi->dev, "%s: TAPS %"PRIu32", dest %d", + __func__, ntaps, dest); + + if (dest & FIR_IS_RX) { + gain = ad9361_spi_read(spi, REG_RX_FILTER_GAIN); + offs = REG_RX_FILTER_COEF_ADDR - REG_TX_FILTER_COEF_ADDR; + ad9361_spi_write(spi, REG_RX_FILTER_GAIN, 0); + } + + conf = ad9361_spi_read(spi, REG_TX_FILTER_CONF + offs); + + if ((dest & 3) == 3) { + sel = 1; + cnt = 2; + } else { + sel = (dest & 3); + cnt = 1; + } + + for (; cnt > 0; cnt--, sel++) { + + ad9361_spi_write(spi, REG_TX_FILTER_CONF + offs, + FIR_NUM_TAPS(ntaps / 16 - 1) | + FIR_SELECT(sel) | FIR_START_CLK); + for (val = 0; val < ntaps; val++) { + short tmp; + ad9361_spi_write(spi, REG_TX_FILTER_COEF_ADDR + offs, val); + + tmp = (ad9361_spi_read(spi, REG_TX_FILTER_COEF_READ_DATA_1 + offs) & 0xFF) | + (ad9361_spi_read(spi, REG_TX_FILTER_COEF_READ_DATA_2 + offs) << 8); + + if (tmp != coef[val]) { + dev_err(&phy->spi->dev,"%s%"PRIu32" read verify failed TAP%"PRIu32" %d =! %d", + (dest & FIR_IS_RX) ? "RX" : "TX", sel, + val, tmp, coef[val]); + ret = -EIO; + } + } + } + + if (dest & FIR_IS_RX) { + ad9361_spi_write(spi, REG_RX_FILTER_GAIN, gain); + } + + ad9361_spi_write(spi, REG_TX_FILTER_CONF + offs, conf); + + return ret; +} + +/** + * Load the FIR filter coefficients. + * @param phy The AD9361 state structure. + * @param dest Destination identifier (RX1,2 / TX1,2). + * @param gain_dB Gain option. + * @param ntaps Number of filter Taps. + * @param coef Pointer to filter coefficients. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_load_fir_filter_coef(struct ad9361_rf_phy *phy, + enum fir_dest dest, int32_t gain_dB, + uint32_t ntaps, int16_t *coef) +{ + struct spi_desc *spi = phy->spi; + uint32_t val, offs = 0, fir_conf = 0, fir_enable = 0; + int32_t ret; + + dev_dbg(&phy->spi->dev, "%s: TAPS %"PRIu32", gain %"PRId32", dest %d", + __func__, ntaps, gain_dB, dest); + + if (coef == NULL || !ntaps || ntaps > 128 || ntaps % 16) { + dev_err(&phy->spi->dev, + "%s: Invalid parameters: TAPS %"PRIu32", gain %"PRId32", dest 0x%X", + __func__, ntaps, gain_dB, dest); + + return -EINVAL; + } + + ad9361_ensm_force_state(phy, ENSM_STATE_ALERT); + + if (dest & FIR_IS_RX) { + val = 3 - (gain_dB + 12) / 6; + ad9361_spi_write(spi, REG_RX_FILTER_GAIN, val & 0x3); + offs = REG_RX_FILTER_COEF_ADDR - REG_TX_FILTER_COEF_ADDR; + phy->rx_fir_ntaps = ntaps; + fir_enable = ad9361_spi_readf(phy->spi, + REG_RX_ENABLE_FILTER_CTRL, RX_FIR_ENABLE_DECIMATION(~0)); + ad9361_spi_writef(phy->spi, REG_RX_ENABLE_FILTER_CTRL, + RX_FIR_ENABLE_DECIMATION(~0), + (phy->rx_fir_dec == 4) ? 3 : phy->rx_fir_dec); + } else { + if (gain_dB == -6) + fir_conf = TX_FIR_GAIN_6DB; + phy->tx_fir_ntaps = ntaps; + fir_enable = ad9361_spi_readf(phy->spi, + REG_TX_ENABLE_FILTER_CTRL, TX_FIR_ENABLE_INTERPOLATION(~0)); + ad9361_spi_writef(phy->spi, REG_TX_ENABLE_FILTER_CTRL, + TX_FIR_ENABLE_INTERPOLATION(~0), + (phy->tx_fir_int == 4) ? 3 : phy->tx_fir_int); + } + + val = ntaps / 16 - 1; + + fir_conf |= FIR_NUM_TAPS(val) | FIR_SELECT(dest) | FIR_START_CLK; + + ad9361_spi_write(spi, REG_TX_FILTER_CONF + offs, fir_conf); + + for (val = 0; val < ntaps; val++) { + ad9361_spi_write(spi, REG_TX_FILTER_COEF_ADDR + offs, val); + ad9361_spi_write(spi, REG_TX_FILTER_COEF_WRITE_DATA_1 + offs, + coef[val] & 0xFF); + ad9361_spi_write(spi, REG_TX_FILTER_COEF_WRITE_DATA_2 + offs, + coef[val] >> 8); + ad9361_spi_write(spi, REG_TX_FILTER_CONF + offs, + fir_conf | FIR_WRITE); + ad9361_spi_write(spi, REG_TX_FILTER_COEF_READ_DATA_2 + offs, 0); + ad9361_spi_write(spi, REG_TX_FILTER_COEF_READ_DATA_2 + offs, 0); + } + + ad9361_spi_write(spi, REG_TX_FILTER_CONF + offs, fir_conf); + fir_conf &= ~FIR_START_CLK; + ad9361_spi_write(spi, REG_TX_FILTER_CONF + offs, fir_conf); + + ret = ad9361_verify_fir_filter_coef(phy, dest, ntaps, coef); + + if (dest & FIR_IS_RX) + ad9361_spi_writef(phy->spi, REG_RX_ENABLE_FILTER_CTRL, + RX_FIR_ENABLE_DECIMATION(~0), fir_enable); + else + ad9361_spi_writef(phy->spi, REG_TX_ENABLE_FILTER_CTRL, + TX_FIR_ENABLE_INTERPOLATION(~0), fir_enable); + + ad9361_ensm_restore_prev_state(phy); + + return ret; +} + +/** + * Parse the FIR filter file/buffer. + * @param phy The AD9361 state structure. + * @param data Pointer to buffer. + * @param size Buffer size. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_parse_fir(struct ad9361_rf_phy *phy, + char *data, uint32_t size) +{ + char *line; + int32_t i = 0, ret, txc, rxc; + int32_t tx = -1, tx_gain = 0, tx_int = 0; + int32_t rx = -1, rx_gain = 0, rx_dec = 0; + int32_t rtx = -1, rrx = -1; + int16_t coef_tx[128]; + int16_t coef_rx[128]; + char *ptr = data; + + phy->filt_rx_bw_Hz = 0; + phy->filt_tx_bw_Hz = 0; + phy->filt_valid = false; + + while ((line = strsep(&ptr, "\n"))) { + if (line >= data + size) { + break; + } + + if (line[0] == '#') + continue; + + if (tx < 0) { +#ifdef WIN32 + ret = sscanf_s(line, "TX %d GAIN %d INT %d", + &tx, &tx_gain, &tx_int); +#else + ret = sscanf(line, "TX %"PRId32" GAIN %"PRId32" INT %"PRId32, + &tx, &tx_gain, &tx_int); +#endif + if (ret == 3) + continue; + else + tx = -1; + } + if (rx < 0) { +#ifdef WIN32 + ret = sscanf_s(line, "RX %d GAIN %d DEC %d", + &rx, &rx_gain, &rx_dec); +#else + ret = sscanf(line, "RX %"PRId32" GAIN %"PRId32" DEC %"PRId32, + &rx, &rx_gain, &rx_dec); +#endif + if (ret == 3) + continue; + else + tx = -1; + } + + if (rtx < 0) { +#ifdef WIN32 + ret = sscanf(line, "RTX %lu %lu %lu %lu %lu %lu", +#else + ret = sscanf(line, + "RTX %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32, +#endif + &phy->filt_tx_path_clks[0], + &phy->filt_tx_path_clks[1], + &phy->filt_tx_path_clks[2], + &phy->filt_tx_path_clks[3], + &phy->filt_tx_path_clks[4], + &phy->filt_tx_path_clks[5]); + if (ret == 6) { + rtx = 0; + continue; + } else { + rtx = -1; + } + } + + if (rrx < 0) { +#ifdef WIN32 + ret = sscanf(line, "RRX %lu %lu %lu %lu %lu %lu", +#else + ret = sscanf(line, + "RRX %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32, +#endif + &phy->filt_rx_path_clks[0], + &phy->filt_rx_path_clks[1], + &phy->filt_rx_path_clks[2], + &phy->filt_rx_path_clks[3], + &phy->filt_rx_path_clks[4], + &phy->filt_rx_path_clks[5]); + if (ret == 6) { + rrx = 0; + continue; + } else { + rrx = -1; + } + } + + if (!phy->filt_rx_bw_Hz) { +#ifdef WIN32 + ret = sscanf(line, "BWRX %d", &phy->filt_rx_bw_Hz); +#else + ret = sscanf(line, "BWRX %"PRId32, &phy->filt_rx_bw_Hz); +#endif + if (ret == 1) + continue; + else + phy->filt_rx_bw_Hz = 0; + } + + if (!phy->filt_tx_bw_Hz) { +#ifdef WIN32 + ret = sscanf(line, "BWTX %d", &phy->filt_tx_bw_Hz); +#else + ret = sscanf(line, "BWTX %"PRId32, &phy->filt_tx_bw_Hz); +#endif + if (ret == 1) + continue; + else + phy->filt_tx_bw_Hz = 0; + } + +#ifdef WIN32 + ret = sscanf_s(line, "%d,%d", &txc, &rxc); +#else + ret = sscanf(line, "%"PRId32",%"PRId32, &txc, &rxc); +#endif + if (ret == 1) { + coef_tx[i] = coef_rx[i] = (int16_t)txc; + i++; + continue; + } else if (ret == 2) { + coef_tx[i] = (int16_t)txc; + coef_rx[i] = (int16_t)rxc; + i++; + continue; + } + } + + switch (tx) { + case FIR_TX1: + case FIR_TX2: + case FIR_TX1_TX2: + phy->tx_fir_int = tx_int; + ret = ad9361_load_fir_filter_coef(phy, (enum fir_dest)tx, tx_gain, i, coef_tx); + break; + default: + ret = -EINVAL; + } + + switch (rx | FIR_IS_RX) { + case FIR_RX1: + case FIR_RX2: + case FIR_RX1_RX2: + phy->rx_fir_dec = rx_dec; + ret = ad9361_load_fir_filter_coef(phy, (enum fir_dest)(rx | FIR_IS_RX), + rx_gain, i, coef_rx); + break; + default: + ret = -EINVAL; + } + + if (ret < 0) + return ret; + + if (!(rrx | rtx)) + phy->filt_valid = true; + + return size; +} + +/** + * Validate FIR filter configuration - on pass enable. + * @param phy The AD9361 state structure. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_validate_enable_fir(struct ad9361_rf_phy *phy) +{ + int32_t ret; + uint32_t rx[6], tx[6]; + uint32_t max, min, valid; + + dev_dbg(dev, "%s: TX FIR EN=%d/TAPS%d/INT%d, RX FIR EN=%d/TAPS%d/DEC%d", + __func__, !phy->bypass_tx_fir, phy->tx_fir_ntaps, phy->tx_fir_int, + !phy->bypass_rx_fir, phy->rx_fir_ntaps, phy->rx_fir_dec); + + if (!phy->bypass_tx_fir) { + if (!(phy->tx_fir_int == 1 || phy->tx_fir_int == 2 || + phy->tx_fir_int == 4)) { + dev_err(dev, + "%s: Invalid: Interpolation %d in filter config", + __func__, phy->tx_fir_int); + return -EINVAL; + } + + + if (phy->tx_fir_int == 1 && phy->tx_fir_ntaps > 64) { + dev_err(dev, + "%s: Invalid: TAPS > 64 and Interpolation = 1", + __func__); + return -EINVAL; + } + } + + if (!phy->bypass_rx_fir) { + if (!(phy->rx_fir_dec == 1 || phy->rx_fir_dec == 2 || + phy->rx_fir_dec == 4)) { + dev_err(dev, + "%s: Invalid: Decimation %d in filter config", + __func__, phy->rx_fir_dec); + + return -EINVAL; + } + } + + if (!phy->filt_valid || phy->bypass_rx_fir || phy->bypass_tx_fir) { + ret = ad9361_calculate_rf_clock_chain(phy, + clk_get_rate(phy, phy->ref_clk_scale[TX_SAMPL_CLK]), + phy->rate_governor, rx, tx); + if (ret < 0) { + min = phy->rate_governor ? 1500000U : 1000000U; + dev_err(dev, + "%s: Calculating filter rates failed %"PRId32 + " using min frequency",__func__, ret); + ret = ad9361_calculate_rf_clock_chain(phy, min, + phy->rate_governor, rx, tx); + if (ret < 0) { + return ret; + } + } + valid = false; + } else { +#ifndef ALTERA_PLATFORM + memcpy(rx, phy->filt_rx_path_clks, sizeof(rx)); + memcpy(tx, phy->filt_tx_path_clks, sizeof(tx)); +#else + int32_t i; + uint32_t num; + num = sizeof(rx) / sizeof(rx[0]); + for (i = 0; i < num; i++) + rx[i] = phy->filt_rx_path_clks[i]; + num = sizeof(tx) / sizeof(rx[0]); + for (i = 0; i < num; i++) + tx[i] = phy->filt_tx_path_clks[i]; +#endif + valid = true; + + } + +#ifdef _DEBUG + dev_dbg(&phy->spi->dev, + "%s:RX %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32, + __func__, rx[BBPLL_FREQ], rx[ADC_FREQ], + rx[R2_FREQ], rx[R1_FREQ], + rx[CLKRF_FREQ], rx[RX_SAMPL_FREQ]); + + dev_dbg(&phy->spi->dev, + "%s:TX %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32, + __func__, tx[BBPLL_FREQ], tx[ADC_FREQ], + tx[R2_FREQ], tx[R1_FREQ], + tx[CLKRF_FREQ], tx[RX_SAMPL_FREQ]); +#endif + + if (!phy->bypass_tx_fir) { + max = (tx[DAC_FREQ] / tx[TX_SAMPL_FREQ]) * 16; + if (phy->tx_fir_ntaps > max) { + dev_err(dev, + "%s: Invalid: ratio DAC / TX_SAMPL * 16 > TAPS" + "(max %"PRIu32", adc %"PRIu32", tx %"PRIu32")", + __func__, max, tx[DAC_FREQ], tx[TX_SAMPL_FREQ]); + return -EINVAL; + } + } + + if (!phy->bypass_rx_fir) { + max = ((rx[ADC_FREQ] / ((rx[ADC_FREQ] == rx[R2_FREQ]) ? 1 : 2)) / + rx[RX_SAMPL_FREQ]) * 16; + if (phy->rx_fir_ntaps > max) { + dev_err(dev, + "%s: Invalid: ratio ADC/2 / RX_SAMPL * 16 > TAPS (max %"PRIu32")", + __func__, max); + return -EINVAL; + } + } + + ret = ad9361_set_trx_clock_chain(phy, rx, tx); + if (ret < 0) + return ret; + + /* See also: ad9361_set_trx_clock_chain() */ + if (!phy->pdata->dig_interface_tune_fir_disable && + phy->bypass_tx_fir && phy->bypass_rx_fir) + ad9361_dig_tune(phy, 0, RESTORE_DEFAULT); + + return ad9361_update_rf_bandwidth(phy, + valid ? phy->filt_rx_bw_Hz : phy->current_rx_bw_Hz, + valid ? phy->filt_tx_bw_Hz : phy->current_tx_bw_Hz); +} + +/* +* AD9361 Clocks +*/ + +/** +* Set the multiplier and the divider for the selected refclk_scale structure. +* @param priv The selected refclk_scale structure. +* @param mul The multiplier value. +* @param div The divider value. +* @return 0 in case of success, negative error code otherwise. +*/ +static inline int32_t ad9361_set_muldiv(struct refclk_scale *priv, uint32_t mul, + uint32_t div) +{ + priv->mult = mul; + priv->div = div; + return 0; +} + +/** + * Get the clk scaler for the selected refclk_scale structure. + * @param priv The selected refclk_scale structure. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_get_clk_scaler(struct refclk_scale *clk_priv) +{ + struct spi_desc *spi = clk_priv->spi; + uint32_t tmp, tmp1; + + switch (clk_priv->source) { + case BB_REFCLK: + tmp = ad9361_spi_read(spi, REG_CLOCK_CTRL); + tmp &= 0x3; + break; + case RX_REFCLK: + tmp = ad9361_spi_readf(spi, REG_REF_DIVIDE_CONFIG_1, + RX_REF_DIVIDER_MSB); + tmp1 = ad9361_spi_readf(spi, REG_REF_DIVIDE_CONFIG_2, + RX_REF_DIVIDER_LSB); + tmp = (tmp << 1) | tmp1; + break; + case TX_REFCLK: + tmp = ad9361_spi_readf(spi, REG_REF_DIVIDE_CONFIG_2, + TX_REF_DIVIDER(~0)); + break; + case ADC_CLK: + tmp = ad9361_spi_read(spi, REG_BBPLL); + return ad9361_set_muldiv(clk_priv, 1, 1 << (tmp & 0x7)); + case R2_CLK: + tmp = ad9361_spi_readf(spi, REG_RX_ENABLE_FILTER_CTRL, + DEC3_ENABLE_DECIMATION(~0)); + return ad9361_set_muldiv(clk_priv, 1, tmp + 1); + case R1_CLK: + tmp = ad9361_spi_readf(spi, REG_RX_ENABLE_FILTER_CTRL, RHB2_EN); + return ad9361_set_muldiv(clk_priv, 1, tmp + 1); + case CLKRF_CLK: + tmp = ad9361_spi_readf(spi, REG_RX_ENABLE_FILTER_CTRL, RHB1_EN); + return ad9361_set_muldiv(clk_priv, 1, tmp + 1); + case RX_SAMPL_CLK: + tmp = ad9361_spi_readf(spi, REG_RX_ENABLE_FILTER_CTRL, + RX_FIR_ENABLE_DECIMATION(~0)); + + if (!tmp) + tmp = 1; /* bypass filter */ + else + tmp = (1 << (tmp - 1)); + + return ad9361_set_muldiv(clk_priv, 1, tmp); + case DAC_CLK: + tmp = ad9361_spi_readf(spi, REG_BBPLL, BIT(3)); + return ad9361_set_muldiv(clk_priv, 1, tmp + 1); + case T2_CLK: + tmp = ad9361_spi_readf(spi, REG_TX_ENABLE_FILTER_CTRL, + THB3_ENABLE_INTERP(~0)); + return ad9361_set_muldiv(clk_priv, 1, tmp + 1); + case T1_CLK: + tmp = ad9361_spi_readf(spi, REG_TX_ENABLE_FILTER_CTRL, THB2_EN); + return ad9361_set_muldiv(clk_priv, 1, tmp + 1); + case CLKTF_CLK: + tmp = ad9361_spi_readf(spi, REG_TX_ENABLE_FILTER_CTRL, THB1_EN); + return ad9361_set_muldiv(clk_priv, 1, tmp + 1); + case TX_SAMPL_CLK: + tmp = ad9361_spi_readf(spi, REG_TX_ENABLE_FILTER_CTRL, + TX_FIR_ENABLE_INTERPOLATION(~0)); + + if (!tmp) + tmp = 1; /* bypass filter */ + else + tmp = (1 << (tmp - 1)); + + return ad9361_set_muldiv(clk_priv, 1, tmp); + default: + return -EINVAL; + } + + /* REFCLK Scaler */ + switch (tmp) { + case 0: + ad9361_set_muldiv(clk_priv, 1, 1); + break; + case 1: + ad9361_set_muldiv(clk_priv, 1, 2); + break; + case 2: + ad9361_set_muldiv(clk_priv, 1, 4); + break; + case 3: + ad9361_set_muldiv(clk_priv, 2, 1); + break; + default: + return -EINVAL; + + } + + return 0; +} + +/** + * Calculate the REFCLK Scaler for the selected refclk_scale structure. + * Note: REFCLK Scaler values - 00: x1; 01: x½; 10: x¼; 11: x2. + * @param clk_priv The selected refclk_scale structure. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_to_refclk_scaler(struct refclk_scale *clk_priv) +{ + /* REFCLK Scaler */ + switch (((clk_priv->mult & 0xF) << 4) | (clk_priv->div & 0xF)) { + case 0x11: + return 0; + case 0x12: + return 1; + case 0x14: + return 2; + case 0x21: + return 3; + default: + return -EINVAL; + } +}; + +/** + * Set clk scaler for the selected refclk_scale structure. + * @param clk_priv The selected refclk_scale structure. + * @param set Set true, the reference clock frequency will be scaled before + * it enters the BBPLL. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_set_clk_scaler(struct refclk_scale *clk_priv, bool set) +{ + struct spi_desc *spi = clk_priv->spi; + uint32_t tmp; + int32_t ret; + + switch (clk_priv->source) { + case BB_REFCLK: + ret = ad9361_to_refclk_scaler(clk_priv); + if (ret < 0) + return ret; + if (set) + return ad9361_spi_writef(spi, REG_CLOCK_CTRL, + REF_FREQ_SCALER(~0), ret); + break; + + case RX_REFCLK: + ret = ad9361_to_refclk_scaler(clk_priv); + if (ret < 0) + return ret; + if (set) { + tmp = ret; + ret = ad9361_spi_writef(spi, REG_REF_DIVIDE_CONFIG_1, + RX_REF_DIVIDER_MSB, tmp >> 1); + ret |= ad9361_spi_writef(spi, REG_REF_DIVIDE_CONFIG_2, + RX_REF_DIVIDER_LSB, tmp & 1); + return ret; + } + break; + case TX_REFCLK: + ret = ad9361_to_refclk_scaler(clk_priv); + if (ret < 0) + return ret; + if (set) + return ad9361_spi_writef(spi, REG_REF_DIVIDE_CONFIG_2, + TX_REF_DIVIDER(~0), ret); + break; + case ADC_CLK: + tmp = ilog2((uint8_t)clk_priv->div); + if (clk_priv->mult != 1 || tmp > 6 || tmp < 1) + return -EINVAL; + + if (set) + return ad9361_spi_writef(spi, REG_BBPLL, 0x7, tmp); + break; + case R2_CLK: + if (clk_priv->mult != 1 || clk_priv->div > 3 || clk_priv->div < 1) + return -EINVAL; + if (set) + return ad9361_spi_writef(spi, REG_RX_ENABLE_FILTER_CTRL, + DEC3_ENABLE_DECIMATION(~0), + clk_priv->div - 1); + break; + case R1_CLK: + if (clk_priv->mult != 1 || clk_priv->div > 2 || clk_priv->div < 1) + return -EINVAL; + if (set) + return ad9361_spi_writef(spi, REG_RX_ENABLE_FILTER_CTRL, + RHB2_EN, clk_priv->div - 1); + break; + case CLKRF_CLK: + if (clk_priv->mult != 1 || clk_priv->div > 2 || clk_priv->div < 1) + return -EINVAL; + if (set) + return ad9361_spi_writef(spi, REG_RX_ENABLE_FILTER_CTRL, + RHB1_EN, clk_priv->div - 1); + break; + case RX_SAMPL_CLK: + if (clk_priv->mult != 1 || clk_priv->div > 4 || + clk_priv->div < 1 || clk_priv->div == 3) + return -EINVAL; + + if (clk_priv->phy->bypass_rx_fir) + tmp = 0; + else + tmp = ilog2(clk_priv->div) + 1; + + if (set) + return ad9361_spi_writef(spi, REG_RX_ENABLE_FILTER_CTRL, + RX_FIR_ENABLE_DECIMATION(~0), tmp); + break; + case DAC_CLK: + if (clk_priv->mult != 1 || clk_priv->div > 2 || clk_priv->div < 1) + return -EINVAL; + if (set) + return ad9361_spi_writef(spi, REG_BBPLL, + BIT(3), clk_priv->div - 1); + break; + case T2_CLK: + if (clk_priv->mult != 1 || clk_priv->div > 3 || clk_priv->div < 1) + return -EINVAL; + if (set) + return ad9361_spi_writef(spi, REG_TX_ENABLE_FILTER_CTRL, + THB3_ENABLE_INTERP(~0), + clk_priv->div - 1); + break; + case T1_CLK: + if (clk_priv->mult != 1 || clk_priv->div > 2 || clk_priv->div < 1) + return -EINVAL; + if (set) + return ad9361_spi_writef(spi, REG_TX_ENABLE_FILTER_CTRL, + THB2_EN, clk_priv->div - 1); + break; + case CLKTF_CLK: + if (clk_priv->mult != 1 || clk_priv->div > 2 || clk_priv->div < 1) + return -EINVAL; + if (set) + return ad9361_spi_writef(spi, REG_TX_ENABLE_FILTER_CTRL, + THB1_EN, clk_priv->div - 1); + break; + case TX_SAMPL_CLK: + if (clk_priv->mult != 1 || clk_priv->div > 4 || + clk_priv->div < 1 || clk_priv->div == 3) + return -EINVAL; + + if (clk_priv->phy->bypass_tx_fir) + tmp = 0; + else + tmp = ilog2(clk_priv->div) + 1; + + if (set) + return ad9361_spi_writef(spi, REG_TX_ENABLE_FILTER_CTRL, + TX_FIR_ENABLE_INTERPOLATION(~0), tmp); + break; + default: + return -EINVAL; + } + + return 0; +} + +/** + * Recalculate the clock rate. + * @param clk_priv The refclk_scale structure. + * @param parent_rate The parent clock rate. + * @return The clock rate. + */ +uint32_t ad9361_clk_factor_recalc_rate(struct refclk_scale *clk_priv, + uint32_t parent_rate) +{ + uint64_t rate; + + ad9361_get_clk_scaler(clk_priv); + rate = (parent_rate * clk_priv->mult) / clk_priv->div; + + return (uint32_t)rate; +} + +/** + * Calculate the closest possible clock rate that can be set. + * @param clk_priv The refclk_scale structure. + * @param rate The clock rate. + * @param prate The parent clock rate. + * @return The closest possible clock rate that can be set. + */ +int32_t ad9361_clk_factor_round_rate(struct refclk_scale *clk_priv, + uint32_t rate, + uint32_t *prate) +{ + int32_t ret; + + if (rate >= *prate) { + clk_priv->mult = DIV_ROUND_CLOSEST(rate, *prate); + clk_priv->div = 1; + + } else { + clk_priv->div = DIV_ROUND_CLOSEST(*prate, rate); + clk_priv->mult = 1; + if (!clk_priv->div) { + dev_err(&clk_priv->spi->dev, "%s: divide by zero", + __func__); + clk_priv->div = 1; + } + } + + ret = ad9361_set_clk_scaler(clk_priv, false); + if (ret < 0) + return ret; + + return (*prate / clk_priv->div) * clk_priv->mult; +} + +/** + * Set the clock rate. + * @param clk_priv The refclk_scale structure. + * @param rate The clock rate. + * @param parent_rate The parent clock rate. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_clk_factor_set_rate(struct refclk_scale *clk_priv, uint32_t rate, + uint32_t parent_rate) +{ + dev_dbg(&clk_priv->spi->dev, "%s: Rate %"PRIu32" Hz Parent Rate %"PRIu32" Hz", + __func__, rate, parent_rate); + + if (rate >= parent_rate) { + clk_priv->mult = DIV_ROUND_CLOSEST(rate, parent_rate); + clk_priv->div = 1; + } else { + clk_priv->div = DIV_ROUND_CLOSEST(parent_rate, rate); + clk_priv->mult = 1; + if (!clk_priv->div) { + dev_err(&clk_priv->spi->dev, "%s: divide by zero", + __func__); + clk_priv->div = 1; + } + } + + return ad9361_set_clk_scaler(clk_priv, true); +} + +/* + * BBPLL + */ +/** + * Recalculate the clock rate. + * @param clk_priv The refclk_scale structure. + * @param parent_rate The parent clock rate. + * @return The clock rate. + */ +uint32_t ad9361_bbpll_recalc_rate(struct refclk_scale *clk_priv, + uint32_t parent_rate) +{ + uint64_t rate; + uint32_t fract, integer; + uint8_t buf[4]; + + ad9361_spi_readm(clk_priv->spi, REG_INTEGER_BB_FREQ_WORD, &buf[0], + REG_INTEGER_BB_FREQ_WORD - REG_FRACT_BB_FREQ_WORD_1 + 1); + + fract = (buf[3] << 16) | (buf[2] << 8) | buf[1]; + integer = buf[0]; + + rate = ((uint64_t)parent_rate * fract); + do_div(&rate, BBPLL_MODULUS); + rate += (uint64_t)parent_rate * integer; + + return (uint32_t)rate; +} + +/** + * Calculate the closest possible clock rate that can be set. + * @param clk_priv The refclk_scale structure. + * @param rate The clock rate. + * @param prate The parent clock rate. + * @return The closest possible clock rate that can be set. + */ +int32_t ad9361_bbpll_round_rate(struct refclk_scale *clk_priv, uint32_t rate, + uint32_t *prate) +{ + uint64_t tmp; + uint32_t fract, integer; + uint64_t temp; + + if (clk_priv) { + // Unused variable - fix compiler warning + } + + if (rate > MAX_BBPLL_FREQ) + return MAX_BBPLL_FREQ; + + if (rate < MIN_BBPLL_FREQ) + return MIN_BBPLL_FREQ; + + temp = rate; + tmp = do_div(&temp, *prate); + rate = temp; + tmp = tmp * BBPLL_MODULUS + (*prate >> 1); + do_div(&tmp, *prate); + + integer = rate; + fract = tmp; + + tmp = *prate * (uint64_t)fract; + do_div(&tmp, BBPLL_MODULUS); + tmp += *prate * integer; + + return tmp; +} + +/** + * Set the clock rate. + * @param clk_priv The refclk_scale structure. + * @param rate The clock rate. + * @param parent_rate The parent clock rate. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_bbpll_set_rate(struct refclk_scale *clk_priv, uint32_t rate, + uint32_t parent_rate) +{ + struct spi_desc *spi = clk_priv->spi; + uint64_t tmp; + uint32_t fract, integer; + int32_t icp_val; + uint8_t lf_defaults[3] = { 0x35, 0x5B, 0xE8 }; + uint64_t temp; + + dev_dbg(&spi->dev, "%s: Rate %"PRIu32" Hz Parent Rate %"PRIu32" Hz", + __func__, rate, parent_rate); + + /* + * Setup Loop Filter and CP Current + * Scale is 150uA @ (1280MHz BBPLL, 40MHz REFCLK) + */ + tmp = (rate >> 7) * 150ULL; + do_div(&tmp, (parent_rate >> 7) * 32UL); + + /* 25uA/LSB, Offset 25uA */ + icp_val = DIV_ROUND_CLOSEST((uint32_t)tmp, 25U) - 1; + + icp_val = clamp(icp_val, 1, 64); + + ad9361_spi_write(spi, REG_CP_CURRENT, icp_val); + ad9361_spi_writem(spi, REG_LOOP_FILTER_3, lf_defaults, + ARRAY_SIZE(lf_defaults)); + + /* Allow calibration to occur and set cal count to 1024 for max accuracy */ + ad9361_spi_write(spi, REG_VCO_CTRL, + FREQ_CAL_ENABLE | FREQ_CAL_COUNT_LENGTH(3)); + /* Set calibration clock to REFCLK/4 for more accuracy */ + ad9361_spi_write(spi, REG_SDM_CTRL, 0x10); + + /* Calculate and set BBPLL frequency word */ + temp = rate; + tmp = do_div(&temp, parent_rate); + rate = temp; + tmp = tmp *(uint64_t)BBPLL_MODULUS + (parent_rate >> 1); + do_div(&tmp, parent_rate); + + integer = rate; + fract = tmp; + + ad9361_spi_write(spi, REG_INTEGER_BB_FREQ_WORD, integer); + ad9361_spi_write(spi, REG_FRACT_BB_FREQ_WORD_3, fract); + ad9361_spi_write(spi, REG_FRACT_BB_FREQ_WORD_2, fract >> 8); + ad9361_spi_write(spi, REG_FRACT_BB_FREQ_WORD_1, fract >> 16); + + ad9361_spi_write(spi, REG_SDM_CTRL_1, + INIT_BB_FO_CAL | BBPLL_RESET_BAR); /* Start BBPLL Calibration */ + ad9361_spi_write(spi, REG_SDM_CTRL_1, + BBPLL_RESET_BAR); /* Clear BBPLL start calibration bit */ + + ad9361_spi_write(spi, REG_VCO_PROGRAM_1, + 0x86); /* Increase BBPLL KV and phase margin */ + ad9361_spi_write(spi, REG_VCO_PROGRAM_2, + 0x01); /* Increase BBPLL KV and phase margin */ + ad9361_spi_write(spi, REG_VCO_PROGRAM_2, + 0x05); /* Increase BBPLL KV and phase margin */ + + return ad9361_check_cal_done(clk_priv->phy, REG_CH_1_OVERFLOW, + BBPLL_LOCK, 1); +} + +/* + * RFPLL + */ + +/** + * Calculate the RFPLL frequency. + * @param parent_rate The parent clock rate. + * @param integer The integer value. + * @param fract The fractional value. + * @param vco_div The VCO divider. + * @return The RFPLL frequency. + */ +static uint64_t ad9361_calc_rfpll_int_freq(uint64_t parent_rate, + uint64_t integer, + uint64_t fract, uint32_t vco_div) +{ + uint64_t rate; + + rate = parent_rate * fract; + do_div(&rate, RFPLL_MODULUS); + rate += parent_rate * integer; + + return rate >> (vco_div + 1); +} + +/** + * Calculate the RFPLL dividers. + * @param phy The AD9361 state structure. + * @param clk_priv The refclk_scale structure. + * @param freq The RFPLL frequency. + * @param parent_rate The parent clock rate. + * @param integer The integer value. + * @param fract The fractional value. + * @param vco_div The VCO divider. + * @param vco_freq The VCO frequency. + * @return The RFPLL frequency. + */ +static int32_t ad9361_calc_rfpll_int_divder(struct ad9361_rf_phy *phy, + struct refclk_scale *clk_priv, uint64_t freq, + uint64_t parent_rate, uint32_t *integer, + uint32_t *fract, int32_t *vco_div, uint64_t *vco_freq) +{ + uint64_t tmp; + int32_t div, ret; + + ret = ad9361_validate_rfpll(phy, clk_priv->source == TX_RFPLL_INT, freq); + if (ret) + return ret; + + div = -1; + + while (freq <= MIN_VCO_FREQ_HZ) { + freq <<= 1; + div++; + } + + *vco_div = div; + *vco_freq = freq; + tmp = do_div(&freq, parent_rate); + tmp = tmp * RFPLL_MODULUS + (parent_rate >> 1); + do_div(&tmp, parent_rate); + *integer = freq; + *fract = tmp; + + return 0; +} + +/** + * Recalculate the clock rate. + * @param clk_priv The refclk_scale structure. + * @param parent_rate The parent clock rate. + * @return The clock rate. + */ +uint32_t ad9361_rfpll_int_recalc_rate(struct refclk_scale *clk_priv, + uint32_t parent_rate) +{ + struct ad9361_rf_phy *phy = clk_priv->phy; + uint32_t fract, integer; + uint8_t buf[5]; + uint32_t reg, div_mask, vco_div, profile; + + dev_dbg(&clk_priv->spi->dev, "%s: Parent Rate %"PRIu32" Hz", + __func__, parent_rate); + + switch (clk_priv->source) { + case RX_RFPLL_INT: + reg = REG_RX_FRACT_BYTE_2; + div_mask = RX_VCO_DIVIDER(~0); + profile = phy->fastlock.current_profile[0]; + break; + case TX_RFPLL_INT: + reg = REG_TX_FRACT_BYTE_2; + div_mask = TX_VCO_DIVIDER(~0); + profile = phy->fastlock.current_profile[1]; + break; + default: + return -EINVAL; + } + + if (profile) { + bool tx = clk_priv->source == TX_RFPLL_INT; + profile = profile - 1; + + buf[0] = ad9361_fastlock_readval(phy->spi, tx, profile, 4); + buf[1] = ad9361_fastlock_readval(phy->spi, tx, profile, 3); + buf[2] = ad9361_fastlock_readval(phy->spi, tx, profile, 2); + buf[3] = ad9361_fastlock_readval(phy->spi, tx, profile, 1); + buf[4] = ad9361_fastlock_readval(phy->spi, tx, profile, 0); + vco_div = ad9361_fastlock_readval(phy->spi, tx, profile, 12) & 0xF; + + } else { + ad9361_spi_readm(clk_priv->spi, reg, &buf[0], ARRAY_SIZE(buf)); + vco_div = ad9361_spi_readf(clk_priv->spi, REG_RFPLL_DIVIDERS, div_mask); + } + + fract = (SYNTH_FRACT_WORD(buf[0]) << 16) | (buf[1] << 8) | buf[2]; + integer = (SYNTH_INTEGER_WORD(buf[3]) << 8) | buf[4]; + + return ad9361_to_clk(ad9361_calc_rfpll_int_freq(parent_rate, integer, + fract, vco_div)); +} + +/** + * Calculate the closest possible clock rate that can be set. + * @param clk_priv The refclk_scale structure. + * @param rate The clock rate. + * @param prate The parent clock rate. + * @return The closest possible clock rate that can be set. + */ +int32_t ad9361_rfpll_int_round_rate(struct refclk_scale *clk_priv, + uint32_t rate, + uint32_t *prate) +{ + dev_dbg(&clk_priv->spi->dev, "%s: Rate %"PRIu32" Hz", __func__, rate); + + if (prate) { + // Unused variable - fix compiler warning + } + + if (clk_priv) { + // Unused variable - fix compiler warning + } + + if (ad9361_from_clk(rate) > MAX_CARRIER_FREQ_HZ || + ad9361_from_clk(rate) < ((clk_priv->source == TX_RFPLL_INT) ? + MIN_TX_CARRIER_FREQ_HZ : MIN_RX_CARRIER_FREQ_HZ)) + return -EINVAL; + + return rate; +} + +/** + * Set the clock rate. + * @param clk_priv The refclk_scale structure. + * @param rate The clock rate. + * @param parent_rate The parent clock rate. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_rfpll_int_set_rate(struct refclk_scale *clk_priv, uint32_t rate, + uint32_t parent_rate) +{ + struct ad9361_rf_phy *phy = clk_priv->phy; + uint64_t vco = 0; + uint8_t buf[5]; + uint32_t reg, div_mask, lock_reg, fract = 0, integer = 0; + int32_t vco_div, ret, fixup_other; + + dev_dbg(&clk_priv->spi->dev, + "%s: %s Rate %"PRIu32" Hz Parent Rate %"PRIu32" Hz", + __func__, clk_priv->source == TX_RFPLL_INT ? "TX" : "RX", + rate, parent_rate); + + ad9361_fastlock_prepare(phy, clk_priv->source == TX_RFPLL_INT, 0, false); + + ret = ad9361_calc_rfpll_int_divder(phy, clk_priv, ad9361_from_clk(rate), + parent_rate, + &integer, &fract, &vco_div, &vco); + if (ret < 0) + return ret; + + switch (clk_priv->source) { + case RX_RFPLL_INT: + reg = REG_RX_FRACT_BYTE_2; + lock_reg = REG_RX_CP_OVERRANGE_VCO_LOCK; + div_mask = RX_VCO_DIVIDER(~0); + phy->cached_rx_rfpll_div = vco_div; + phy->current_rx_lo_freq = rate; + break; + case TX_RFPLL_INT: + reg = REG_TX_FRACT_BYTE_2; + lock_reg = REG_TX_CP_OVERRANGE_VCO_LOCK; + div_mask = TX_VCO_DIVIDER(~0); + phy->cached_tx_rfpll_div = vco_div; + phy->current_tx_lo_freq = rate; + break; + default: + return -EINVAL; + + } + + /* Option to skip VCO cal in TDD mode when moving from TX/RX to Alert */ + if (phy->pdata->tdd_skip_vco_cal) + ad9361_trx_vco_cal_control(phy, clk_priv->source == TX_RFPLL_INT, + true); + + do { + fixup_other = 0; + ad9361_rfpll_vco_init(phy, div_mask == TX_VCO_DIVIDER(~0), + vco, parent_rate); + + buf[0] = SYNTH_FRACT_WORD(fract >> 16); + buf[1] = fract >> 8; + buf[2] = fract & 0xFF; + buf[3] = SYNTH_INTEGER_WORD(integer >> 8) | + (~SYNTH_INTEGER_WORD(~0) & + ad9361_spi_read(clk_priv->spi, reg - 3)); + buf[4] = integer & 0xFF; + + ad9361_spi_writem(clk_priv->spi, reg, buf, 5); + ad9361_spi_writef(clk_priv->spi, REG_RFPLL_DIVIDERS, div_mask, vco_div); + + ret = ad9361_check_cal_done(phy, lock_reg, VCO_LOCK, 1); + + /* In FDD mode with RX LO == TX LO frequency we use TDD tables to + * reduce VCO pulling + */ + + if (((phy->pdata->fdd && !phy->pdata->fdd_independent_mode) && + (phy->current_tx_lo_freq == phy->current_rx_lo_freq) && + (phy->current_tx_use_tdd_table != phy->current_rx_use_tdd_table)) || + ((phy->pdata->fdd && !phy->pdata->fdd_independent_mode) && + (phy->current_tx_lo_freq != phy->current_rx_lo_freq) && + (phy->current_tx_use_tdd_table || phy->current_rx_use_tdd_table))) { + unsigned long _rate; + + switch (clk_priv->source) { + case RX_RFPLL_INT: + reg = REG_TX_FRACT_BYTE_2; + lock_reg = REG_TX_CP_OVERRANGE_VCO_LOCK; + div_mask = TX_VCO_DIVIDER(~0); + _rate = phy->current_tx_lo_freq; + break; + case TX_RFPLL_INT: + reg = REG_RX_FRACT_BYTE_2; + lock_reg = REG_RX_CP_OVERRANGE_VCO_LOCK; + div_mask = RX_VCO_DIVIDER(~0); + _rate = phy->current_rx_lo_freq; + break; + default: + return -EINVAL; + + } + + if (phy->current_tx_lo_freq != phy->current_rx_lo_freq) { + ad9361_calc_rfpll_int_divder(phy, clk_priv, ad9361_from_clk(_rate), + parent_rate, &integer, &fract, &vco_div, &vco); + + ad9361_fastlock_prepare(phy, clk_priv->source == RX_RFPLL_INT, 0, false); + } + + fixup_other = 1; + } + + } while (fixup_other); + + if (phy->pdata->tdd_skip_vco_cal) + ad9361_trx_vco_cal_control(phy, clk_priv->source == TX_RFPLL_INT, + false); + + return ret; +} + +/** + * Recalculate the clock rate. + * @param clk_priv The refclk_scale structure. + * @return The clock rate. + */ +uint32_t ad9361_rfpll_dummy_recalc_rate(struct refclk_scale *clk_priv) +{ + struct ad9361_rf_phy *phy = clk_priv->phy; + + return phy->clks[clk_priv->source]->rate; +} + +/** + * Set the clock rate. + * @param clk_priv The refclk_scale structure. + * @param rate The clock rate. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_rfpll_dummy_set_rate(struct refclk_scale *clk_priv, + uint32_t rate) +{ + struct ad9361_rf_phy *phy = clk_priv->phy; + + phy->clks[clk_priv->source]->rate = rate; + + return 0; +} + +/** + * Recalculate the clock rate. + * @param clk_priv The refclk_scale structure. + * @return The clock rate. + */ +uint32_t ad9361_rfpll_recalc_rate(struct refclk_scale *clk_priv) +{ + struct ad9361_rf_phy *phy = clk_priv->phy; + uint32_t rate; + + switch (clk_priv->source) { + case RX_RFPLL: + if (phy->pdata->use_ext_rx_lo) { + if (phy->ad9361_rfpll_ext_recalc_rate) + rate = phy->ad9361_rfpll_ext_recalc_rate(clk_priv); + else + rate = ad9361_rfpll_dummy_recalc_rate(phy->ref_clk_scale[RX_RFPLL_DUMMY]); + } else { + rate = ad9361_rfpll_int_recalc_rate(phy->ref_clk_scale[RX_RFPLL_INT], + phy->clks[RX_REFCLK]->rate); + } + break; + case TX_RFPLL: + if (phy->pdata->use_ext_tx_lo) { + if (phy->ad9361_rfpll_ext_recalc_rate) + rate = phy->ad9361_rfpll_ext_recalc_rate(clk_priv); + else + rate = ad9361_rfpll_dummy_recalc_rate(phy->ref_clk_scale[TX_RFPLL_DUMMY]); + } else { + rate = ad9361_rfpll_int_recalc_rate(phy->ref_clk_scale[TX_RFPLL_INT], + phy->clks[TX_REFCLK]->rate); + } + break; + default: + rate = 0; + break; + } + + return rate; +} + +/** + * Calculate the closest possible clock rate that can be set. + * @param clk_priv The refclk_scale structure. + * @param rate The clock rate. + * @return The closest possible clock rate that can be set. + */ +int32_t ad9361_rfpll_round_rate(struct refclk_scale *clk_priv, uint32_t rate) +{ + struct ad9361_rf_phy *phy = clk_priv->phy; + int32_t round_rate; + + switch (clk_priv->source) { + case RX_RFPLL: + if (phy->pdata->use_ext_rx_lo) { + if (phy->ad9361_rfpll_ext_round_rate) + round_rate = phy->ad9361_rfpll_ext_round_rate(clk_priv, rate); + else + round_rate = rate; + } else { + round_rate = ad9361_rfpll_int_round_rate(phy->ref_clk_scale[RX_RFPLL_INT], rate, + &phy->clks[phy->ref_clk_scale[RX_RFPLL_INT]->parent_source]->rate); + } + break; + case TX_RFPLL: + if (phy->pdata->use_ext_tx_lo) { + if (phy->ad9361_rfpll_ext_round_rate) + round_rate = phy->ad9361_rfpll_ext_round_rate(clk_priv, rate); + else + round_rate = rate; + } else { + round_rate = ad9361_rfpll_int_round_rate(phy->ref_clk_scale[TX_RFPLL_INT], rate, + &phy->clks[phy->ref_clk_scale[TX_RFPLL_INT]->parent_source]->rate); + } + break; + default: + round_rate = 0; + break; + } + + return round_rate; +} + +/** + * Set the clock rate. + * @param clk_priv The refclk_scale structure. + * @param rate The clock rate. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_rfpll_set_rate(struct refclk_scale *clk_priv, uint32_t rate) +{ + struct ad9361_rf_phy *phy = clk_priv->phy; + int32_t ret; + + switch (clk_priv->source) { + case RX_RFPLL: + if (phy->pdata->use_ext_rx_lo) { + if (phy->ad9361_rfpll_ext_set_rate) + phy->ad9361_rfpll_ext_set_rate(clk_priv, rate); + else + ad9361_rfpll_dummy_set_rate(phy->ref_clk_scale[RX_RFPLL_DUMMY], rate); + } else { + ad9361_rfpll_int_set_rate(phy->ref_clk_scale[RX_RFPLL_INT], rate, + phy->clks[phy->ref_clk_scale[RX_RFPLL_INT]->parent_source]->rate); + } + /* Load Gain Table */ + ret = ad9361_load_gt(phy, ad9361_from_clk(rate), GT_RX1 + GT_RX2); + if (ret < 0) + return ret; + break; + case TX_RFPLL: + if (phy->pdata->use_ext_tx_lo) { + if (phy->ad9361_rfpll_ext_set_rate) + phy->ad9361_rfpll_ext_set_rate(clk_priv, rate); + else + ad9361_rfpll_dummy_set_rate(phy->ref_clk_scale[TX_RFPLL_DUMMY], rate); + } else { + ad9361_rfpll_int_set_rate(phy->ref_clk_scale[TX_RFPLL_INT], rate, + phy->clks[phy->ref_clk_scale[TX_RFPLL_INT]->parent_source]->rate); + } + /* For RX LO we typically have the tracking option enabled + * so for now do nothing here. + */ + if (phy->auto_cal_en && !phy->pdata->use_ext_tx_lo) + if ((diff_abs(phy->last_tx_quad_cal_freq, ad9361_from_clk(rate))) > + phy->cal_threshold_freq) { + ret = ad9361_do_calib_run(phy, TX_QUAD_CAL, -1); + if (ret < 0) + dev_err(&phy->spi->dev, + "%s: TX QUAD cal failed", __func__); + phy->last_tx_quad_cal_freq = ad9361_from_clk(rate); + } + break; + default: + break; + } + + return 0; +} + +/** + * Set clock mux parent. + * @param clk_priv The refclk_scale structure. + * @param index Index - Enable (1), disable (0) ext lo. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_clk_mux_set_parent(struct refclk_scale *clk_priv, uint8_t index) +{ + struct ad9361_rf_phy *phy = clk_priv->phy; + int32_t ret; + + dev_dbg(&clk_priv->spi->dev, "%s: index %d", __func__, index); + + ad9361_ensm_force_state(phy, ENSM_STATE_ALERT); + + ret = ad9361_trx_ext_lo_control(phy, clk_priv->source == TX_RFPLL, index == 1); + if (ret >= 0) + clk_priv->mult = index; + + ad9361_ensm_restore_prev_state(phy); + + return ret; +} + +/** + * Register and initialize a new clock. + * @param phy The AD9361 state structure. + * @param name The name of the new clock. + * @param parent_name The name of the parent clock. + * @param flags The flags. + * @param source The source of the new clock. + * @param parent_source The source of the parent clock. + * @return A struct clk for the new clock or a negative error code. + */ +static struct clk *ad9361_clk_register(struct ad9361_rf_phy *phy, + const char *name, + const char *parent_name, uint32_t flags, + uint32_t source, uint32_t parent_source) +{ + struct refclk_scale *clk_priv; + struct clk *clk; + + if (name) { + // Unused variable - fix compiler warning + } + if (parent_name) { + // Unused variable - fix compiler warning + } + if (flags) { + // Unused variable - fix compiler warning + } + + clk_priv = (struct refclk_scale *)malloc(sizeof(*clk_priv)); + if (!clk_priv) { + dev_err(&phy->spi->dev, + "ad9361_clk_register: could not allocate fixed factor clk"); + return (struct clk *)ERR_PTR(-ENOMEM); + } + + /* struct refclk_scale assignments */ + clk_priv->source = (enum ad9361_clocks)source; + clk_priv->parent_source = (enum ad9361_clocks)parent_source; + clk_priv->spi = phy->spi; + clk_priv->phy = phy; + + phy->ref_clk_scale[source] = clk_priv; + + clk = (struct clk *)malloc(sizeof(*clk)); + if (!clk) { + free(clk_priv); + return (struct clk *)ERR_PTR(-ENOMEM); + } + + switch (source) { + case TX_REFCLK: + clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clk_refin->rate); + break; + case RX_REFCLK: + clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clk_refin->rate); + break; + case BB_REFCLK: + clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clk_refin->rate); + break; + case BBPLL_CLK: + clk->rate = ad9361_bbpll_recalc_rate(clk_priv, phy->clks[BB_REFCLK]->rate); + break; + case ADC_CLK: + clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clks[BBPLL_CLK]->rate); + break; + case R2_CLK: + clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clks[ADC_CLK]->rate); + break; + case R1_CLK: + clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clks[R2_CLK]->rate); + break; + case CLKRF_CLK: + clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clks[R1_CLK]->rate); + break; + case RX_SAMPL_CLK: + clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clks[CLKRF_CLK]->rate); + break; + case DAC_CLK: + clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clks[ADC_CLK]->rate); + break; + case T2_CLK: + clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clks[DAC_CLK]->rate); + break; + case T1_CLK: + clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clks[T2_CLK]->rate); + break; + case CLKTF_CLK: + clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clks[T1_CLK]->rate); + break; + case TX_SAMPL_CLK: + clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clks[CLKTF_CLK]->rate); + break; + case RX_RFPLL_INT: + clk->rate = ad9361_rfpll_int_recalc_rate(clk_priv, phy->clks[RX_REFCLK]->rate); + break; + case TX_RFPLL_INT: + clk->rate = ad9361_rfpll_int_recalc_rate(clk_priv, phy->clks[TX_REFCLK]->rate); + break; + case RX_RFPLL_DUMMY: + clk->rate = phy->pdata->rx_synth_freq; + break; + case TX_RFPLL_DUMMY: + clk->rate = phy->pdata->tx_synth_freq; + break; + case RX_RFPLL: + clk->rate = ad9361_rfpll_recalc_rate(clk_priv); + break; + case TX_RFPLL: + clk->rate = ad9361_rfpll_recalc_rate(clk_priv); + } + + return clk; +} + +/** + * Register and initialize all the system clocks. + * @param phy The AD9361 state structure. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_register_clocks(struct ad9361_rf_phy *phy) +{ + uint32_t flags = CLK_GET_RATE_NOCACHE; + + /* Scaled Reference Clocks */ + phy->clks[TX_REFCLK] = ad9361_clk_register(phy, + "tx_refclk", "ad9361_ext_refclk", + flags | CLK_IGNORE_UNUSED, + TX_REFCLK, EXT_REF_CLK); + + phy->clks[RX_REFCLK] = ad9361_clk_register(phy, + "rx_refclk", "ad9361_ext_refclk", + flags | CLK_IGNORE_UNUSED, + RX_REFCLK, EXT_REF_CLK); + + phy->clks[BB_REFCLK] = ad9361_clk_register(phy, + "bb_refclk", "ad9361_ext_refclk", + flags | CLK_IGNORE_UNUSED, + BB_REFCLK, EXT_REF_CLK); + + /* Base Band PLL Clock */ + phy->clks[BBPLL_CLK] = ad9361_clk_register(phy, + "bbpll_clk", "bb_refclk", + flags | CLK_IGNORE_UNUSED, + BBPLL_CLK, BB_REFCLK); + + phy->clks[ADC_CLK] = ad9361_clk_register(phy, + "adc_clk", "bbpll_clk", + flags | CLK_IGNORE_UNUSED, + ADC_CLK, BBPLL_CLK); + + phy->clks[R2_CLK] = ad9361_clk_register(phy, + "r2_clk", "adc_clk", + flags | CLK_IGNORE_UNUSED, + R2_CLK, ADC_CLK); + + phy->clks[R1_CLK] = ad9361_clk_register(phy, + "r1_clk", "r2_clk", + flags | CLK_IGNORE_UNUSED, + R1_CLK, R2_CLK); + + phy->clks[CLKRF_CLK] = ad9361_clk_register(phy, + "clkrf_clk", "r1_clk", + flags | CLK_IGNORE_UNUSED, + CLKRF_CLK, R1_CLK); + + phy->clks[RX_SAMPL_CLK] = ad9361_clk_register(phy, + "rx_sampl_clk", "clkrf_clk", + flags | CLK_IGNORE_UNUSED, + RX_SAMPL_CLK, CLKRF_CLK); + + + phy->clks[DAC_CLK] = ad9361_clk_register(phy, + "dac_clk", "adc_clk", + flags | CLK_IGNORE_UNUSED, + DAC_CLK, ADC_CLK); + + phy->clks[T2_CLK] = ad9361_clk_register(phy, + "t2_clk", "dac_clk", + flags | CLK_IGNORE_UNUSED, + T2_CLK, DAC_CLK); + + phy->clks[T1_CLK] = ad9361_clk_register(phy, + "t1_clk", "t2_clk", + flags | CLK_IGNORE_UNUSED, + T1_CLK, T2_CLK); + + phy->clks[CLKTF_CLK] = ad9361_clk_register(phy, + "clktf_clk", "t1_clk", + flags | CLK_IGNORE_UNUSED, + CLKTF_CLK, T1_CLK); + + phy->clks[TX_SAMPL_CLK] = ad9361_clk_register(phy, + "tx_sampl_clk", "clktf_clk", + flags | CLK_IGNORE_UNUSED, + TX_SAMPL_CLK, CLKTF_CLK); + + phy->clks[RX_RFPLL_INT] = ad9361_clk_register(phy, + "rx_rfpll", "rx_refclk", + flags | CLK_IGNORE_UNUSED, + RX_RFPLL_INT, RX_REFCLK); + + phy->clks[TX_RFPLL_INT] = ad9361_clk_register(phy, + "tx_rfpll", "tx_refclk", + flags | CLK_IGNORE_UNUSED, + TX_RFPLL_INT, TX_REFCLK); + + phy->clks[RX_RFPLL_DUMMY] = ad9361_clk_register(phy, + "rx_rfpll_dummy", NULL, + flags | CLK_IGNORE_UNUSED, + RX_RFPLL_DUMMY, 0); + + phy->clks[TX_RFPLL_DUMMY] = ad9361_clk_register(phy, + "tx_rfpll_dummy", NULL, + flags | CLK_IGNORE_UNUSED, + TX_RFPLL_DUMMY, 0); + + phy->clks[RX_RFPLL] = ad9361_clk_register(phy, + "rx_rfpll", NULL, + flags | CLK_IGNORE_UNUSED, + RX_RFPLL, 0); + + phy->clks[TX_RFPLL] = ad9361_clk_register(phy, + "tx_rfpll", NULL, + flags | CLK_IGNORE_UNUSED, + TX_RFPLL, 0); + + return 0; +} + +/** + * Unregister all the system clocks. + * @param phy The AD9361 state structure. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_unregister_clocks(struct ad9361_rf_phy *phy) +{ + int32_t i; + + for (i = 0; i < NUM_AD9361_CLKS; i++) { + free(phy->clks[i]); + free(phy->ref_clk_scale[i]); + } + + return 0; +} + +/** + * Perform an RSSI gain step calibration. + * Note: Before running the function, provide a single tone within the channel + * bandwidth and monitor the received data. Adjust the tone amplitude until + * the received data is within a few dB of full scale but not overloading. + * @param phy The AD9361 state structure. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_rssi_gain_step_calib(struct ad9361_rf_phy *phy) +{ + uint32_t lna_error[4]; + uint32_t mixer_error[15]; + uint64_t lo_freq_hz; + uint8_t lo_index; + uint8_t i; + + lo_freq_hz = ad9361_from_clk(clk_get_rate(phy, + phy->ref_clk_scale[RX_RFPLL])); + if (lo_freq_hz < 1300000000ULL) + lo_index = 0; + else if (lo_freq_hz < 3300000000ULL) + lo_index = 1; + else if (lo_freq_hz < 4100000000ULL) + lo_index = 2; + else + lo_index = 3; + + /* Put the AD9361 into the Alert state. */ + ad9361_ensm_force_state(phy, ENSM_STATE_ALERT); + + /* Program the directly-addressable register values. */ + ad9361_spi_write(phy->spi, REG_MAX_MIXER_CALIBRATION_GAIN_INDEX, + MAX_MIXER_CALIBRATION_GAIN_INDEX(0x0F)); + ad9361_spi_write(phy->spi, REG_MEASURE_DURATION, + GAIN_CAL_MEAS_DURATION(0x0E)); + ad9361_spi_write(phy->spi, REG_SETTLE_TIME, + SETTLE_TIME(0x3F)); + ad9361_spi_write(phy->spi, REG_RSSI_CONFIG, + RSSI_MODE_SELECT(0x3) | DEFAULT_RSSI_MEAS_MODE); + ad9361_spi_write(phy->spi, REG_MEASURE_DURATION_01, + MEASUREMENT_DURATION_0(0x0E)); + ad9361_spi_write(phy->spi, REG_LNA_GAIN, + gain_step_calib_reg_val[lo_index][0]); + + /* Program the LNA gain step words into the internal table. */ + ad9361_spi_write(phy->spi, REG_CONFIG, + CALIB_TABLE_SELECT(0x3) | START_CALIB_TABLE_CLOCK); + for(i = 0; i < 4; i++) { + ad9361_spi_write(phy->spi, REG_WORD_ADDRESS, i); + ad9361_spi_write(phy->spi, REG_GAIN_DIFF_WORDERROR_WRITE, + gain_step_calib_reg_val[lo_index][i+1]); + ad9361_spi_write(phy->spi, REG_CONFIG, + CALIB_TABLE_SELECT(0x3) | WRITE_LNA_GAIN_DIFF | START_CALIB_TABLE_CLOCK); + udelay(3); //Wait for data to fully write to internal table + } + + ad9361_spi_write(phy->spi, REG_CONFIG, START_CALIB_TABLE_CLOCK); + ad9361_spi_write(phy->spi, REG_CONFIG, 0x00); + + /* Run and wait until the calibration completes. */ + ad9361_run_calibration(phy, RX_GAIN_STEP_CAL); + + /* Read the LNA and Mixer error terms into nonvolatile memory. */ + ad9361_spi_write(phy->spi, REG_CONFIG, CALIB_TABLE_SELECT(0x1) | READ_SELECT); + for(i = 0; i < 4; i++) { + ad9361_spi_write(phy->spi, REG_WORD_ADDRESS, i); + lna_error[i] = ad9361_spi_read(phy->spi, REG_GAIN_ERROR_READ); + } + ad9361_spi_write(phy->spi, REG_CONFIG, CALIB_TABLE_SELECT(0x1)); + for(i = 0; i < 15; i++) { + ad9361_spi_write(phy->spi, REG_WORD_ADDRESS, i); + mixer_error[i] = ad9361_spi_read(phy->spi, REG_GAIN_ERROR_READ); + } + ad9361_spi_write(phy->spi, REG_CONFIG, 0x00); + + /* Programming gain step errors into the AD9361 in the field */ + ad9361_spi_write(phy->spi, + REG_CONFIG, CALIB_TABLE_SELECT(0x3) | START_CALIB_TABLE_CLOCK); + for(i = 0; i < 4; i++) { + ad9361_spi_write(phy->spi, REG_WORD_ADDRESS, i); + ad9361_spi_write(phy->spi, REG_GAIN_DIFF_WORDERROR_WRITE, lna_error[i]); + ad9361_spi_write(phy->spi, REG_CONFIG, + CALIB_TABLE_SELECT(0x3) | WRITE_LNA_ERROR_TABLE | START_CALIB_TABLE_CLOCK); + } + ad9361_spi_write(phy->spi, REG_CONFIG, + CALIB_TABLE_SELECT(0x3) | START_CALIB_TABLE_CLOCK); + for(i = 0; i < 15; i++) { + ad9361_spi_write(phy->spi, REG_WORD_ADDRESS, i); + ad9361_spi_write(phy->spi, REG_GAIN_DIFF_WORDERROR_WRITE, mixer_error[i]); + ad9361_spi_write(phy->spi, REG_CONFIG, + CALIB_TABLE_SELECT(0x3) | WRITE_MIXER_ERROR_TABLE | START_CALIB_TABLE_CLOCK); + } + ad9361_spi_write(phy->spi, REG_CONFIG, 0x00); + + ad9361_ensm_restore_prev_state(phy); + + return 0; +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361/ad9361.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361/ad9361.h new file mode 100644 index 0000000..33eb527 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361/ad9361.h @@ -0,0 +1,3568 @@ +/***************************************************************************//** + * @file ad9361.h + * @brief Header file of AD9361 Driver. +******************************************************************************** + * Copyright 2014(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ +#ifndef IIO_FREQUENCY_AD9361_H_ +#define IIO_FREQUENCY_AD9361_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include +#include "common.h" +#include "../../../include/gpio.h" + +/******************************************************************************/ +/********************** Macros and Constants Definitions **********************/ +/******************************************************************************/ +#define REG_SPI_CONF 0x000 /* SPI Configuration */ +#define REG_MULTICHIP_SYNC_AND_TX_MON_CTRL 0x001 /* Multi-Chip Sync and Tx Mon Control */ +#define REG_TX_ENABLE_FILTER_CTRL 0x002 /* Tx Enable & Filter Control */ +#define REG_RX_ENABLE_FILTER_CTRL 0x003 /* Rx Enable & Filter Control */ +#define REG_INPUT_SELECT 0x004 /* Input Select */ +#define REG_RFPLL_DIVIDERS 0x005 /* RFPLL Dividers */ +#define REG_RX_CLOCK_DATA_DELAY 0x006 /* Rx Clock & Data Delay */ +#define REG_TX_CLOCK_DATA_DELAY 0x007 /* Tx Clock & Data Delay */ +#define REG_CLOCK_ENABLE 0x009 /* Clock Enable */ +#define REG_BBPLL 0x00A /* BBPLL */ +#define REG_TEMP_OFFSET 0x00B /* Offset */ +#define REG_START_TEMP_READING 0x00C /* Start Temp Reading */ +#define REG_TEMP_SENSE2 0x00D /* Temp Sense2 */ +#define REG_TEMPERATURE 0x00E /* Temperature */ +#define REG_TEMP_SENSOR_CONFIG 0x00F /* Temp Sensor Config */ +#define REG_PARALLEL_PORT_CONF_1 0x010 /* Parallel Port Configuration 1 */ +#define REG_PARALLEL_PORT_CONF_2 0x011 /* Parallel Port Configuration 2 */ +#define REG_PARALLEL_PORT_CONF_3 0x012 /* Parallel Port Configuration 3 */ +#define REG_ENSM_MODE 0x013 /* ENSM Mode */ +#define REG_ENSM_CONFIG_1 0x014 /* ENSM Config 1 */ +#define REG_ENSM_CONFIG_2 0x015 /* ENSM Config 2 */ +#define REG_CALIBRATION_CTRL 0x016 /* Calibration Control */ +#define REG_STATE 0x017 /* State */ +#define REG_AUXDAC_1_WORD 0x018 /* AuxDAC 1 Word */ +#define REG_AUXDAC_2_WORD 0x019 /* AuxDAC 2 Word */ +#define REG_AUXDAC_1_CONFIG 0x01A /* AuxDAC 1 Config */ +#define REG_AUXDAC_2_CONFIG 0x01B /* AuxDAC 2 Config */ +#define REG_AUXADC_CLOCK_DIVIDER 0x01C /* AuxADC Clock Divider */ +#define REG_AUXADC_CONFIG 0x01D /* Aux ADC Config */ +#define REG_AUXADC_WORD_MSB 0x01E /* AuxADC Word MSB */ +#define REG_AUXADC_LSB 0x01F /* AuxADC LSB */ +#define REG_AUTO_GPO 0x020 /* Auto GPO */ +#define REG_AGC_GAIN_LOCK_DELAY 0x021 /* AGC Gain Lock Delay */ +#define REG_AGC_ATTACK_DELAY 0x022 /* AGC Attack Delay */ +#define REG_AUXDAC_ENABLE_CTRL 0x023 /* AuxDAC Enable Control */ +#define REG_RX_LOAD_SYNTH_DELAY 0x024 /* RX Load Synth Delay */ +#define REG_TX_LOAD_SYNTH_DELAY 0x025 /* TX Load Synth Delay */ +#define REG_EXTERNAL_LNA_CTRL 0x026 /* External LNA control */ +#define REG_GPO_FORCE_AND_INIT 0x027 /* GPO Force and Init */ +#define REG_GPO0_RX_DELAY 0x028 /* GPO0 Rx delay */ +#define REG_GPO1_RX_DELAY 0x029 /* GPO1 Rx delay */ +#define REG_GPO2_RX_DELAY 0x02A /* GPO2 Rx delay */ +#define REG_GPO3_RX_DELAY 0x02B /* GPO3 Rx delay */ +#define REG_GPO0_TX_DELAY 0x02C /* GPO0 Tx Delay */ +#define REG_GPO1_TX_DELAY 0x02D /* GPO1 Tx Delay */ +#define REG_GPO2_TX_DELAY 0x02E /* GPO2 Tx Delay */ +#define REG_GPO3_TX_DELAY 0x02F /* GPO3 Tx Delay */ +#define REG_AUXDAC1_RX_DELAY 0x030 /* AuxDAC1 Rx Delay */ +#define REG_AUXDAC1_TX_DELAY 0x031 /* AuxDAC1 Tx Delay */ +#define REG_AUXDAC2_RX_DELAY 0x032 /* AuxDAC2 Rx Delay */ +#define REG_AUXDAC2_TX_DELAY 0x033 /* AuxDAC2 Tx Delay */ +#define REG_CTRL_OUTPUT_POINTER 0x035 /* Control Output Pointer */ +#define REG_CTRL_OUTPUT_ENABLE 0x036 /* Control Output Enable */ +#define REG_PRODUCT_ID 0x037 /* Product ID */ +#define REG_REFERENCE_CLOCK_CYCLES 0x03A /* Reference Clock Cycles */ +#define REG_DIGITAL_IO_CTRL 0x03B /* Digital I/O Control */ +#define REG_LVDS_BIAS_CTRL 0x03C /* LVDS Bias control */ +#define REG_LVDS_INVERT_CTRL1 0x03D /* LVDS Invert control1 */ +#define REG_LVDS_INVERT_CTRL2 0x03E /* LVDS Invert control2 */ +#define REG_SDM_CTRL_1 0x03F /* SDM Control 1 */ +#define REG_FRACT_BB_FREQ_WORD_1 0x041 /* Fractional BB Freq Word 1 */ +#define REG_FRACT_BB_FREQ_WORD_2 0x042 /* Fractional BB Freq Word 2 */ +#define REG_FRACT_BB_FREQ_WORD_3 0x043 /* Fractional BB Freq Word 3 */ +#define REG_INTEGER_BB_FREQ_WORD 0x044 /* Integer BB Freq Word */ +#define REG_CLOCK_CTRL 0x045 /* Clock Control */ +#define REG_CP_CURRENT 0x046 /* CP Current */ +#define REG_CP_BLEED_CURRENT 0x047 /* CP Bleed Current */ +#define REG_LOOP_FILTER_1 0x048 /* Loop Filter 1 */ +#define REG_LOOP_FILTER_2 0x049 /* Loop Filter 2 */ +#define REG_LOOP_FILTER_3 0x04A /* Loop Filter 3 */ +#define REG_VCO_CTRL 0x04B /* VCO Control */ +#define REG_VCO_PROGRAM_1 0x04C +#define REG_VCO_PROGRAM_2 0x04D +#define REG_SDM_CTRL 0x04E /* SDM Control */ +#define REG_RX_SYNTH_POWER_DOWN_OVERRIDE 0x050 /* Rx Synth Power Down Override */ +#define REG_TX_SYNTH_POWER_DOWN_OVERRIDE 0x051 /* TX Synth Power Down Override */ +#define REG_RX_ANALOG_POWER_DOWN_OVERRIDE_1 0x052 /* Rx Analog Power Down Override 1 */ +#define REG_RX_ANALOG_POWER_DOWN_OVERRIDE_2 0x053 /* Rx Analog Power Down Override 2 */ +#define REG_RX1_ADC_POWER_DOWN_OVERRIDE 0x054 /* Rx1 ADC Power Down Override */ +#define REG_RX2_ADC_POWER_DOWN_OVERRIDE 0x055 /* Rx2 ADC Power Down Override */ +#define REG_TX_ANALOG_POWER_DOWN_OVERRIDE_1 0x056 /* Tx Analog Power Down Override 1 */ +#define REG_ANALOG_POWER_DOWN_OVERRIDE 0x057 /* Analog Power Down Override */ +#define REG_MISC_POWER_DOWN_OVERRIDE 0x058 /* Misc Power Down Override */ +#define REG_CH_1_OVERFLOW 0x05E /* CH 1 Overflow */ +#define REG_CH_2_OVERFLOW 0x05F /* CH 2 Overflow */ +#define REG_TX_FILTER_COEF_ADDR 0x060 /* TX Filter Coefficient Address */ +#define REG_TX_FILTER_COEF_WRITE_DATA_1 0x061 /* TX Filter Coefficient Write Data 1 */ +#define REG_TX_FILTER_COEF_WRITE_DATA_2 0x062 /* TX Filter Coefficient Write Data 2 */ +#define REG_TX_FILTER_COEF_READ_DATA_1 0x063 /* TX Filter Coefficient Read Data 1 */ +#define REG_TX_FILTER_COEF_READ_DATA_2 0x064 /* TX Filter Coefficient Read Data 2 */ +#define REG_TX_FILTER_CONF 0x065 /* TX Filter Configuration */ +#define REG_TX_MON_LOW_GAIN 0x067 /* Tx Mon Low Gain */ +#define REG_TX_MON_HIGH_GAIN 0x068 /* Tx Mon High Gain */ +#define REG_TX_MON_DELAY 0x069 /* Tx Mon Delay */ +#define REG_TX_LEVEL_THRESH 0x06A /* Tx Level Threshold */ +#define REG_TX_RSSI1 0x06B /* TX RSSI1 */ +#define REG_TX_RSSI2 0x06C /* TX RSSI2 */ +#define REG_TX_RSSI_LSB 0x06D /* TX RSSI LSB */ +#define REG_TPM_MODE_ENABLE 0x06E /* TPM Mode Enable */ +#define REG_TX_MON_TEMP_GAIN_COEF 0x06F /* Temp Gain Coefficient */ +#define REG_TX_MON_1_CONFIG 0x070 /* Tx Mon 1 Config */ +#define REG_TX_MON_2_CONFIG 0x071 /* Tx Mon 2 Config */ +#define REG_TX1_ATTEN_0 0x073 /* Tx1 Atten 0 */ +#define REG_TX1_ATTEN_1 0x074 /* Tx1 Atten 1 */ +#define REG_TX2_ATTEN_0 0x075 /* Tx2 Atten 0 */ +#define REG_TX2_ATTEN_1 0x076 /* Tx2 Atten 1 */ +#define REG_TX_ATTEN_OFFSET 0x077 /* Tx Atten Offset */ +#define REG_TX_ATTEN_THRESH 0x078 /* Tx Atten Threshold */ +#define REG_TX1_DIG_ATTEN 0x079 /* Tx1 Dig Attenuation */ +#define REG_TX2_DIG_ATTEN 0x07C /* Tx2 Dig Attenuation */ +#define REG_TX1_SYMBOL_ATTEN 0x07F /* TX1 Symbol Attenuation */ +#define REG_TX2_SYMBOL_ATTEN 0x080 /* TX2 Symbol Attenuation */ +#define REG_TX_SYMBOL_ATTEN_CONFIG 0x081 /* TX Symbol Atten Config */ +#define REG_TX1_OUT_1_PHASE_CORR 0x08E /* Tx1 Out 1 Phase Corr */ +#define REG_TX1_OUT_1_GAIN_CORR 0x08F /* Tx1 Out 1 Gain Corr */ +#define REG_TX2_OUT_1_PHASE_CORR 0x090 /* Tx2 Out 1 Phase Corr */ +#define REG_TX2_OUT_1_GAIN_CORR 0x091 /* Tx2 Out 1 Gain Corr */ +#define REG_TX1_OUT_1_OFFSET_I 0x092 /* Tx1 Out 1 Offset I */ +#define REG_TX1_OUT_1_OFFSET_Q 0x093 /* Tx1 Out 1 Offset Q */ +#define REG_TX2_OUT_1_OFFSET_I 0x094 /* Tx2 Out 1 Offset I */ +#define REG_TX2_OUT_1_OFFSET_Q 0x095 /* Tx2 Out 1 Offset Q */ +#define REG_TX1_OUT_2_PHASE_CORR 0x096 /* Tx1 Out 2 Phase Corr */ +#define REG_TX1_OUT_2_GAIN_CORR 0x097 /* Tx1 Out 2 Gain Corr */ +#define REG_TX2_OUT_2_PHASE_CORR 0x098 /* Tx2 Out 2 Phase Corr */ +#define REG_TX2_OUT_2_GAIN_CORR 0x099 /* Tx2 Out 2 Gain Corr */ +#define REG_TX1_OUT_2_OFFSET_I 0x09A /* Tx1 Out 2 Offset I */ +#define REG_TX1_OUT_2_OFFSET_Q 0x09B /* Tx1 Out 2 Offset Q */ +#define REG_TX2_OUT_2_OFFSET_I 0x09C /* Tx2 Out 2 Offset I */ +#define REG_TX2_OUT_2_OFFSET_Q 0x09D /* Tx2 Out 2 Offset Q */ +#define REG_TX_FORCE_BITS 0x09F /* Force Bits */ +#define REG_QUAD_CAL_NCO_FREQ_PHASE_OFFSET 0x0A0 /* Quad Cal NCO Freq & Phase Offset */ +#define REG_QUAD_CAL_CTRL 0x0A1 /* Quad Cal Control */ +#define REG_KEXP_1 0x0A2 /* Kexp 1 */ +#define REG_KEXP_2 0x0A3 /* Kexp 2 */ +#define REG_QUAD_SETTLE_COUNT 0x0A4 /* QUAD Settle count */ +#define REG_MAG_FTEST_THRESH 0x0A5 /* Mag. Ftest Thresh */ +#define REG_MAG_FTEST_THRESH_2 0x0A6 /* Mag. Ftest Thresh 2 */ +#define REG_QUAD_CAL_STATUS_TX1 0x0A7 /* Quad cal status Tx1 */ +#define REG_QUAD_CAL_STATUS_TX2 0x0A8 /* Quad cal status Tx2 */ +#define REG_QUAD_CAL_COUNT 0x0A9 /* Quad cal Count */ +#define REG_TX_QUAD_FULL_LMT_GAIN 0x0AA /* Tx Quad Full/LMT Gain */ +#define REG_SQUARER_CONFIG 0x0AB /* Squarer Config */ +#define REG_TX_QUAD_CAL_ATTEN 0x0AC /* TX Quad Cal Atten */ +#define REG_THRESH_ACCUM 0x0AD /* Thresh Accum */ +#define REG_TX_QUAD_LPF_GAIN 0x0AE /* Tx Quad LPF Gain */ +#define REG_TXDAC_VDS_I 0x0B0 /* TxDAC Vds I */ +#define REG_TXDAC_VDS_Q 0x0B1 /* TxDAC Vds Q */ +#define REG_TXDAC_GN_I 0x0B2 /* TxDAC gn I */ +#define REG_TXDAC_GN_Q 0x0B3 /* TxDAC gn Q */ +#define REG_TXBBF_OPAMP_A 0x0C0 /* TxBBF OpAmp A */ +#define REG_TXBBF_OPAMP_B 0x0C1 /* TxBBF OpAmp B */ +#define REG_TX_BBF_R1 0x0C2 /* Tx BBF R1 */ +#define REG_TX_BBF_R2 0x0C3 /* Tx BBF R2 */ +#define REG_TX_BBF_R3 0x0C4 /* Tx BBF R3 */ +#define REG_TX_BBF_R4 0x0C5 /* Tx BBF R4 */ +#define REG_TX_BBF_RP 0x0C6 /* Tx BBF RP */ +#define REG_TX_BBF_C1 0x0C7 /* Tx BBF C1 */ +#define REG_TX_BBF_C2 0x0C8 /* Tx BBF C2 */ +#define REG_TX_BBF_CP 0x0C9 /* Tx BBF Cp */ +#define REG_TX_TUNE_CTRL 0x0CA /* Tx Tune Control */ +#define REG_TX_BBF_R2B 0x0CB /* Tx BBF R2b */ +#define REG_TX_BBF_TUNE 0x0CC /* Tx BBF Tune */ +#define REG_CONFIG0 0x0D0 /* Config0 */ +#define REG_RESISTOR 0x0D1 /* Resistor */ +#define REG_CAPACITOR 0x0D2 /* Capacitor */ +#define REG_LO_CM 0x0D3 /* LO CM */ +#define REG_TX_BBF_TUNE_DIVIDER 0x0D6 /* TX BBF Tune Divider */ +#define REG_TX_BBF_TUNE_MODE 0x0D7 /* TX BBF Tune Mode */ +#define REG_RX_FILTER_COEF_ADDR 0x0F0 /* Rx Filter Coeff Addr */ +#define REG_RX_FILTER_COEF_DATA_1 0x0F1 /* Rx Filter Coeff Data 1 */ +#define REG_RX_FILTER_COEF_DATA_2 0x0F2 /* Rx Filter Coeff Data 2 */ +#define REG_RX_FILTER_COEF_READ_DATA_1 0x0F3 /* Rx Filter Coeff Read Data 1 */ +#define REG_RX_FILTER_COEF_READ_DATA_2 0x0F4 /* Rx Filter Coeff Read Data 2 */ +#define REG_RX_FILTER_CONFIG 0x0F5 /* Rx Filter Config */ +#define REG_RX_FILTER_GAIN 0x0F6 /* Rx Filter Gain */ +#define REG_AGC_CONFIG_1 0x0FA /* AGC Config1 */ +#define REG_AGC_CONFIG_2 0x0FB /* AGC config2 */ +#define REG_AGC_CONFIG_3 0x0FC /* AGC Config3 */ +#define REG_MAX_LMT_FULL_GAIN 0x0FD /* Max LMT/Full Gain */ +#define REG_PEAK_WAIT_TIME 0x0FE /* Peak Wait Time */ +#define REG_DIGITAL_GAIN 0x100 /* Digital Gain */ +#define REG_AGC_LOCK_LEVEL 0x101 /* AGC Lock Level */ +#define REG_ADC_NOISE_CORRECTION_FACTOR 0x102 /* ADC noise Correction Factor */ +#define REG_GAIN_STP_CONFIG1 0x103 /* Gain Step Config1 */ +#define REG_ADC_SMALL_OVERLOAD_THRESH 0x104 /* ADC Small Overload Threshold */ +#define REG_ADC_LARGE_OVERLOAD_THRESH 0x105 /* ADC Large Overload Threshold */ +#define REG_GAIN_STP_CONFIG_2 0x106 /* Gain Step Config 2 */ +#define REG_SMALL_LMT_OVERLOAD_THRESH 0x107 /* Small LMT Overload Threshold */ +#define REG_LARGE_LMT_OVERLOAD_THRESH 0x108 /* Large LMT Overload Threshold */ +#define REG_RX1_MANUAL_LMT_FULL_GAIN 0x109 /* Rx1 Manual LMT/Full Gain */ +#define REG_RX1_MANUAL_LPF_GAIN 0x10A /* Rx1 Manual LPF gain */ +#define REG_RX1_MANUAL_DIGITALFORCED_GAIN 0x10B /* Rx1 Manual Digital/Forced Gain */ +#define REG_RX2_MANUAL_LMT_FULL_GAIN 0x10C /* Rx2 Manual LMT/Full Gain */ +#define REG_RX2_MANUAL_LPF_GAIN 0x10D /* Rx2 Manual LPF Gain */ +#define REG_RX2_MANUAL_DIGITALFORCED_GAIN 0x10E /* Rx2 Manual Digital/Forced Gain */ +#define REG_FAST_CONFIG_1 0x110 /* Config 1 */ +#define REG_FAST_CONFIG_2_SETTLING_DELAY 0x111 /* Config 2 & Settling Delay */ +#define REG_FAST_ENERGY_LOST_THRESH 0x112 /* Energy Lost Threshold */ +#define REG_FAST_STRONGER_SIGNAL_THRESH 0x113 /* Stronger Signal Threshold */ +#define REG_FAST_LOW_POWER_THRESH 0x114 /* Low Power Threshold */ +#define REG_FAST_STRONG_SIGNAL_FREEZE 0x115 /* Strong Signal Freeze */ +#define REG_FAST_FINAL_OVER_RANGE_AND_OPT_GAIN 0x116 /* Final Over Range and Opt Gain */ +#define REG_FAST_ENERGY_DETECT_COUNT 0x117 /* Energy Detect Count */ +#define REG_FAST_AGCLL_UPPER_LIMIT 0x118 /* AGCLL Upper Limit */ +#define REG_FAST_GAIN_LOCK_EXIT_COUNT 0x119 /* Gain Lock Exit Count */ +#define REG_FAST_INITIAL_LMT_GAIN_LIMIT 0x11A /* Initial LMT Gain Limit */ +#define REG_FAST_INCREMENT_TIME 0x11B /* Increment Time */ +#define REG_AGC_INNER_LOW_THRESH 0x120 /* AGC Inner Low Threshold */ +#define REG_LMT_OVERLOAD_COUNTERS 0x121 /* LMT Overload Counters */ +#define REG_ADC_OVERLOAD_COUNTERS 0x122 /* ADC Overload Counters */ +#define REG_GAIN_STP1 0x123 /* Gain Step1 */ +#define REG_GAIN_UPDATE_COUNTER1 0x124 /* Gain Update Counter1 */ +#define REG_GAIN_UPDATE_COUNTER2 0x125 /* Gain Update Counter2 */ +#define REG_DIGITAL_SAT_COUNTER 0x128 /* Digital Sat Counter */ +#define REG_OUTER_POWER_THRESHS 0x129 /* Outer Power Thresholds */ +#define REG_GAIN_STP_2 0x12A /* Gain Step 2 */ +#define REG_EXT_LNA_HIGH_GAIN 0x12C /* Ext LNA High Gain */ +#define REG_EXT_LNA_LOW_GAIN 0x12D /* Ext LNA Low Gain */ +#define REG_GAIN_TABLE_ADDRESS 0x130 /* Gain Table Address */ +#define REG_GAIN_TABLE_WRITE_DATA1 0x131 /* Gain Table Write Data1 */ +#define REG_GAIN_TABLE_WRITE_DATA2 0x132 /* Gain Table Write Data2 */ +#define REG_GAIN_TABLE_WRITE_DATA3 0x133 /* Gain Table Write Data 3 */ +#define REG_GAIN_TABLE_READ_DATA1 0x134 /* Gain Table Read Data 1 */ +#define REG_GAIN_TABLE_READ_DATA2 0x135 /* Gain Table Read Data 2 */ +#define REG_GAIN_TABLE_READ_DATA3 0x136 /* Gain Table Read Data 3 */ +#define REG_GAIN_TABLE_CONFIG 0x137 /* Gain Table Config */ +#define REG_GM_SUB_TABLE_ADDRESS 0x138 /* Gm Sub Table Address */ +#define REG_GM_SUB_TABLE_GAIN_WRITE 0x139 /* Gm Sub Table Gain Word Write */ +#define REG_GM_SUB_TABLE_BIAS_WRITE 0x13A /* Gm Sub Table Bias Word Write */ +#define REG_GM_SUB_TABLE_CTRL_WRITE 0x13B /* Gm Sub Table Control Word Write */ +#define REG_GM_SUB_TABLE_GAIN_READ 0x13C /* Gm Sub Table Gain Word Read */ +#define REG_GM_SUB_TABLE_BIAS_READ 0x13D /* Gm Sub Table Bias Word Read */ +#define REG_GM_SUB_TABLE_CTRL_READ 0x13E /* Gm Sub Table Control Word Read */ +#define REG_GM_SUB_TABLE_CONFIG 0x13F /* Gm Sub Table Config */ +#define REG_WORD_ADDRESS 0x140 /* Word Address */ +#define REG_GAIN_DIFF_WORDERROR_WRITE 0x141 /* Gain Diff Word/Error Write */ +#define REG_GAIN_ERROR_READ 0x142 /* Gain Error Read */ +#define REG_CONFIG 0x143 /* Config */ +#define REG_LNA_GAIN_DIFF_READ_BACK 0x144 /* LNA Gain Diff Read Back */ +#define REG_MAX_MIXER_CALIBRATION_GAIN_INDEX 0x145 /* Max Mixer Calibration Gain Index */ +#define REG_TEMP_GAIN_COEF 0x146 /* Temp Gain Coefficient */ +#define REG_SETTLE_TIME 0x147 /* Settle Time */ +#define REG_MEASURE_DURATION 0x148 /* Measure Duration */ +#define REG_CAL_TEMP_SENSOR_WORD 0x149 /* Cal Temp sensor word */ +#define REG_MEASURE_DURATION_01 0x150 /* Measure Duration 0&1 */ +#define REG_MEASURE_DURATION_23 0x151 /* Measure Duration 2&3 */ +#define REG_RSSI_WEIGHT_0 0x152 /* RSSI Weight 0 */ +#define REG_RSSI_WEIGHT_1 0x153 /* RSSI Weight 1 */ +#define REG_RSSI_WEIGHT_2 0x154 /* RSSI Weight 2 */ +#define REG_RSSI_WEIGHT_3 0x155 /* RSSI Weight 3 */ +#define REG_RSSI_DELAY 0x156 /* RSSI delay */ +#define REG_RSSI_WAIT_TIME 0x157 /* RSSI wait time */ +#define REG_RSSI_CONFIG 0x158 /* RSSI Config */ +#define REG_ADC_MEASURE_DURATION_01 0x159 /* ADC Measure Duration 0&1 */ +#define REG_ADC_WEIGHT_0 0x15A /* ADC Weight 0 */ +#define REG_ADC_WEIGHT_1 0x15B /* ADC Weight 1 */ +#define REG_DEC_POWER_MEASURE_DURATION_0 0x15C /* Dec Power Measure Duration 0 */ +#define REG_LNA_GAIN 0x15D /* LNA Gain */ +#define REG_CH1_ADC_POWER 0x160 /* CH1 ADC Power */ +#define REG_CH1_RX_FILTER_POWER 0x161 /* CH1 Rx filter Power */ +#define REG_CH2_ADC_POWER 0x162 /* CH2 ADC Power */ +#define REG_CH2_RX_FILTER_POWER 0x163 /* CH2 Rx filter Power */ +#define REG_RX_QUAD_CAL_LEVEL 0x168 /* Rx Quad Cal Level */ +#define REG_CALIBRATION_CONFIG_1 0x169 /* Calibration Config 1 */ +#define REG_CALIBRATION_CONFIG_2 0x16A /* Calibration config2 */ +#define REG_CALIBRATION_CONFIG_3 0x16B /* Calibration config3 */ +#define REG_CALIB_COUNT 0x16C /* Calib count */ +#define REG_SETTLE_COUNT 0x16D /* Settle count */ +#define REG_RX_QUAD_GAIN1 0x16E /* Rx Quad gain1 */ +#define REG_RX_QUAD_GAIN2 0x16F /* Rx Quad gain2 */ +#define REG_RX1_INPUT_A_PHASE_CORR 0x170 /* Rx1 Input A Phase Corr */ +#define REG_RX1_INPUT_A_GAIN_CORR 0x171 /* Rx1 Input A Gain Corr */ +#define REG_RX2_INPUT_A_PHASE_CORR 0x172 /* Rx2 Input A Phase Corr */ +#define REG_RX2_INPUT_A_GAIN_CORR 0x173 /* Rx2 Input A Gain Corr */ +#define REG_RX1_INPUT_A_Q_OFFSET 0x174 /* Rx1 Input A Q" Offset */ +#define REG_RX1_INPUT_A_OFFSETS 0x175 /* Rx1 Input A Offsets */ +#define REG_INPUT_A_OFFSETS_1 0x176 /* Input A Offsets 1 */ +#define REG_RX2_INPUT_A_OFFSETS 0x177 /* Rx2 Input A Offsets */ +#define REG_RX2_INPUT_A_I_OFFSET 0x178 /* Rx2 Input A "I" Offset */ +#define REG_RX1_INPUT_BC_PHASE_CORR 0x179 /* Rx1 Input B&C Phase Corr */ +#define REG_RX1_INPUT_BC_GAIN_CORR 0x17A /* Rx1 Input B&C Gain Corr */ +#define REG_RX2_INPUT_BC_PHASE_CORR 0x17B /* Rx2 Input B&C Phase Corr */ +#define REG_RX2_INPUT_BC_GAIN_CORR 0x17C /* Rx2 Input B&C Gain Corr */ +#define REG_RX1_INPUT_BC_Q_OFFSET 0x17D /* Rx1 Input B&C "Q" Offset */ +#define REG_RX1_INPUT_BC_OFFSETS 0x17E /* Rx1 Input B&C Offsets */ +#define REG_INPUT_BC_OFFSETS_1 0x17F /* Input B&C Offsets 1 */ +#define REG_RX2_INPUT_BC_OFFSETS 0x180 /* Rx2 Input B&C Offsets */ +#define REG_RX2_INPUT_BC_I_OFFSET 0x181 /* Rx2 Input B&C "I" Offset */ +#define REG_FORCE_BITS 0x182 /* Force Bits */ +#define REG_WAIT_COUNT 0x185 /* Wait Count */ +#define REG_RF_DC_OFFSET_COUNT 0x186 /* RF DC Offset Count */ +#define REG_RF_DC_OFFSET_CONFIG_1 0x187 /* RF DC Offset Config1 */ +#define REG_RF_DC_OFFSET_ATTEN 0x188 /* RF DC Offset Attenuation */ +#define REG_INVERT_BITS 0x189 /* Invert Bits */ +#define REG_DC_OFFSET_CONFIG2 0x18B /* DC Offset Config2 */ +#define REG_RF_CAL_GAIN_INDEX 0x18C /* RF Cal Gain Index */ +#define REG_SOI_THRESH 0x18D /* SOI Threshold */ +#define REG_BB_DC_OFFSET_SHIFT 0x190 /* BB DC Offset Shift */ +#define REG_BB_DC_OFFSET_FAST_SETTLE_SHIFT 0x191 /* BB DC Offset Fast Settle Shift */ +#define REG_BB_FAST_SETTLE_DUR 0x192 /* BB Fast Settle Dur */ +#define REG_BB_DC_OFFSET_COUNT 0x193 /* BB DC Offset Count */ +#define REG_BB_DC_OFFSET_ATTEN 0x194 /* BB DC Offset Attenuation */ +#define REG_RX1_BB_DC_WORD_I_MSB 0x19A /* RX1 BB DC word I MSB */ +#define REG_RX1_BB_DC_WORD_I_LSB 0x19B /* RX1 BB DC word I LSB */ +#define REG_RX1_BB_DC_WORD_Q_MSB 0x19C /* RX1 BB DC word Q MSB */ +#define REG_RX1_BB_DC_WORD_Q_LSB 0x19D /* RX1 BB DC word Q LSB */ +#define REG_RX2_BB_DC_WORD_I_MSB 0x19E /* RX2 BB DC word I MSB */ +#define REG_RX2_BB_DC_WORD_I_LSB 0x19F /* RX2 BB DC word I LSB */ +#define REG_RX2_BB_DC_WORD_Q_MSB 0x1A0 /* RX2 BB DC word Q MSB */ +#define REG_RX2_BB_DC_WORD_Q_LSB 0x1A1 /* RX2 BB DC word Q LSB */ +#define REG_BB_TRACK_CORR_WORD_I_MSB 0x1A2 /* BB Track corr word I MSB */ +#define REG_BB_TRACK_CORR_WORD_I_LSB 0x1A3 /* BB Track corr word I LSB */ +#define REG_BB_TRACK_CORR_WORD_Q_MSB 0x1A4 /* BB Track corr word Q MSB */ +#define REG_BB_TRACK_CORR_WORD_Q_LSB 0x1A5 /* BB Track corr word Q LSB */ +#define REG_RX1_RSSI_SYMBOL 0x1A7 /* Rx1 RSSI Symbol */ +#define REG_RX1_RSSI_PREAMBLE 0x1A8 /* Rx1 RSSI preamble */ +#define REG_RX2_RSSI_SYMBOL 0x1A9 /* Rx2 RSSI symbol */ +#define REG_RX2_RSSI_PREAMBLE 0x1AA /* Rx2 RSSI preamble */ +#define REG_SYMBOL_LSB 0x1AB /* Symbol LSB */ +#define REG_PREAMBLE_LSB 0x1AC /* Preamble LSB */ +#define REG_RX_PATH_GAIN_MSB 0x1AD /* Rx Path Gain */ +#define REG_RX_PATH_GAIN_LSB 0x1AE /* Rx Path Gain */ +#define REG_RX_DIFF_LNA_FORCE 0x1B0 /* Rx Diff LNA Force */ +#define REG_RX_LNA_BIAS_COARSE 0x1B1 /* Rx LNA Bias Coarse */ +#define REG_RX_LNA_BIAS_FINE_0 0x1B2 /* Rx LNA Bias Fine 0 */ +#define REG_RX_LNA_BIAS_FINE_1 0x1B3 /* Rx LNA Bias Fine 1 */ +#define REG_RX_MIX_GM_CONFIG 0x1C0 /* Rx Mix Gm Config */ +#define REG_RX1_MIX_GM_FORCE 0x1C1 /* Rx1 Mix Gm Force */ +#define REG_RX1_MIX_GM_BIAS_FORCE 0x1C2 /* Rx1 Mix Gm Bias (Force) */ +#define REG_RX2_MIX_GM_FORCE 0x1C3 /* Rx2 Mix Gm Force */ +#define REG_RX2_MIX_GM_BIAS_FORCE 0x1C4 /* Rx2 Mix Gm Bias (Force) */ +#define REG_INPUT_A_MSBS 0x1C8 /* Input A MSBs */ +#define REG_INPUT_A_RX1_I 0x1C9 /* Input A RX1 I */ +#define REG_INPUT_A_RX1_Q 0x1CA /* Input A RX1 Q */ +#define REG_INPUT_A_RX2_I 0x1CB /* Input A RX2 I */ +#define REG_INPUT_A_RX2_Q 0x1CC /* Input A RX2 Q */ +#define REG_INPUTS_BC_RX1_I 0x1CD /* Inputs B&C RX1 I */ +#define REG_BAND1_RX1_Q 0x1CE /* Band1 RX1 Q */ +#define REG_INPUTS_BC_RX2_I 0x1CF /* Inputs B&C RX2 I */ +#define REG_INPUTS_BC_RX2_Q 0x1D0 /* Inputs B&C RX2 Q */ +#define REG_INPUTS_BC_MSBS 0x1D1 /* Inputs B&C MSBs */ +#define REG_FORCE_OS_DAC 0x1D2 /* Force OS DAC */ +#define REG_RX_MIX_LO_CM 0x1D5 /* Rx Mix LO CM */ +#define REG_RX_CGB_SEG_ENABLE 0x1D6 /* Rx CGB Seg Enable */ +#define REG_RX_MIX_INPUTBIAS 0x1D7 /* Rx Mix Input/Bias */ +#define REG_RX_TIA_CONFIG 0x1DB /* Rx TIA Config */ +#define REG_TIA1_C_LSB 0x1DC /* TIA1 C LSB */ +#define REG_TIA1_C_MSB 0x1DD /* TIA1 C MSB */ +#define REG_TIA2_C_LSB 0x1DE /* TIA2 C LSB */ +#define REG_TIA2_C_MSB 0x1DF /* TIA2 C MSB */ +#define REG_RX1_BBF_R1A 0x1E0 /* Rx1 BBF R1A */ +#define REG_RX2_BBF_R1A 0x1E1 /* Rx2 BBF R1A */ +#define REG_RX1_TUNE_CTRL 0x1E2 /* Rx1 Tune Control */ +#define REG_RX2_TUNE_CTRL 0x1E3 /* Rx2 Tune Control */ +#define REG_RX1_BBF_R5 0x1E4 /* Rx1 BBF R5 */ +#define REG_RX2_BBF_R5 0x1E5 /* Rx2 BBF R5 */ +#define REG_RX_BBF_R2346 0x1E6 /* Rx BBF R2346 */ +#define REG_RX_BBF_C1_MSB 0x1E7 /* Rx BBF C1 MSB */ +#define REG_RX_BBF_C1_LSB 0x1E8 /* Rx BBF C1 LSB */ +#define REG_RX_BBF_C2_MSB 0x1E9 /* Rx BBF C2 MSB */ +#define REG_RX_BBF_C2_LSB 0x1EA /* Rx BBF C2 LSB */ +#define REG_RX_BBF_C3_MSB 0x1EB /* Rx BBF C3 MSB */ +#define REG_RX_BBF_C3_LSB 0x1EC /* Rx BBF C3 LSB */ +#define REG_RX_BBF_CC1_CTR 0x1ED /* Rx BBF CC1 Ctr */ +#define REG_RX_BBF_POW_RZ_BYTE0 0x1EE /* Rx BBF Pow Rz Byte0 */ +#define REG_RX_BBF_CC2_CTR 0x1EF /* Rx BBF CC2 Ctr */ +#define REG_RX_BBF_POW_RZ_BYTE1 0x1F0 /* Rx BBF Pow Rz Byte1 */ +#define REG_RX_BBF_CC3_CTR 0x1F1 /* Rx BBF CC3 Ctr */ +#define REG_RX_BBF_R5_TUNE 0x1F2 /* Rx BBF R5 Tune */ +#define REG_RX_BBF_TUNE 0x1F3 /* Rx BBF Tune */ +#define REG_RX1_BBF_MAN_GAIN 0x1F4 /* Rx1 BBF Man Gain */ +#define REG_RX2_BBF_MAN_GAIN 0x1F5 /* Rx2 BBF Man Gain */ +#define REG_RX_BBF_TUNE_DIVIDE 0x1F8 /* RX BBF Tune Divide */ +#define REG_RX_BBF_TUNE_CONFIG 0x1F9 /* RX BBF Tune Config */ +#define REG_POLE_GAIN 0x1FA /* Pole gain */ +#define REG_RX_BBBW_MHZ 0x1FB /* Rx BBBW MHz */ +#define REG_RX_BBBW_KHZ 0x1FC /* Rx BBBW kHz */ +#define REG_FB_DAC_CLK_DELAY1 0x201 /* FB DAC Clk Delay1 */ +#define REG_FB_DAC_CLK_DELAY2 0x202 /* FB DAC Clk Delay2 */ +#define REG_FLASH_SAMPLE_CLK_DELAY_3P 0x203 /* Flash Sample Clk Delay 3p */ +#define REG_FLASH_SAMPLE_CLK_DELAY_3N 0x204 /* Flash Sample Clk Delay 3n */ +#define REG_TEST_MUX_2I 0x205 /* Test MUX 2i */ +#define REG_TEST_MUX_2Q 0x206 /* Test MUX 2q */ +#define REG_INTEGRATOR_1_RESISTANCE 0x207 /* Integrator 1 Resistance */ +#define REG_INTEGRATOR_1_CAPACITANCE 0x208 /* Integrator 1 Capacitance */ +#define REG_INTEGRATOR_23_RESISTANCE 0x209 /* Integrator 23 Resistance */ +#define REG_INTEGRATOR_2_RESISTANCE 0x20A /* Integrator 2 Resistance */ +#define REG_INTEGRATOR_2_CAPACITANCE 0x20B /* Integrator 2 Capacitance */ +#define REG_INTEGRATOR_3_RESISTANCE 0x20C /* Integrator 3 Resistance */ +#define REG_INTEGRATOR_3_CAPACITANCE 0x20D /* Integrator 3 Capacitance */ +#define REG_INTEGRATOR_AMP_CC 0x20E /* Integrator Amp Cc */ +#define REG_INT_1_FB_DAC_NMOS_CURRENT_SOURCE 0x20F /* Int 1 FB DAC NMOS Current Source */ +#define REG_INT_1_FB_DAC_NMOS_CASOADE_BIAS_CURRENT 0x210 /* Int 1 FB DAC NMOS Casoade Bias Current */ +#define REG_INT_1_FB_DAC_PMOS_CURRENT_SOURCE 0x211 /* Int 1 FB DAC PMOS Current Source */ +#define REG_INT_2_FB_DAC_NMOS_CURRENT_SOURCE 0x212 /* Int 2 FB DAC NMOS Current Source */ +#define REG_INT_2_FB_DAC_NMOS_CASCODE_BIAS_CURRENT 0x213 /* Int 2 FB DAC NMOS Cascode Bias Current */ +#define REG_INT_2_FB_DAC_PMOS_CURRENT_SOURCE 0x214 /* Int 2 FB DAC PMOS Current Source */ +#define REG_INT_3_FB_DAC_NMOS_CURRENT_SOURCE 0x215 /* Int 3 FB DAC NMOS Current Source */ +#define REG_INT_3_FB_DAC_NMOS_CASCODE_BIAS_CURRENT 0x216 /* Int 3 FB DAC NMOS Cascode Bias Current */ +#define REG_INT_3_FB_DAC_PMOS_CURRENT_SOURCE 0x217 /* Int 3 FB DAC PMOS Current Source */ +#define REG_FB_DAC_BIAS_CURRENT 0x218 /* FB DAC Bias Current */ +#define REG_INT_1_1ST_STAGE_CURRENT 0x219 /* Int 1 1st Stage Current */ +#define REG_INT_1_1ST_STAGE_CASCODE_CURRENT 0x21A /* Int 1 1st Stage Cascode Current */ +#define REG_INT_1_2ND_STAGE_CURRENT 0x21B /* Int 1 2nd Stage Current */ +#define REG_INTEGRATOR_2_1ST_STAGE_CURRENT 0x21C /* Integrator 2 1st Stage Current */ +#define REG_INT_2_1ST_STAGE_CASCODE_CURRENT 0x21D /* Int 2 1st Stage Cascode Current */ +#define REG_INT_2_2ND_STAGE_CURRENT 0x21E /* Int 2 2nd Stage Current */ +#define REG_INT_3_1ST_STAGE_CURRENT 0x21F /* Int 3 1st Stage Current */ +#define REG_INT_3_1ST_STAGE_CASCODE_CURRENT 0x220 /* Int 3 1st Stage Cascode Current */ +#define REG_INT_3_2ND_STAGE_CURRENT 0x221 /* Int 3 2nd Stage Current */ +#define REG_FLASH_BIAS_CURRENT 0x222 /* Flash Bias Current */ +#define REG_FLASH_LADDER_BIAS 0x223 /* Flash Ladder Bias */ +#define REG_FLASH_LADDER_CASCODE_CURRENT 0x224 /* Flash Ladder Cascode Current */ +#define REG_FLASH_LADDER_BIAS2 0x225 /* Flash Ladder Bias2 */ +#define REG_RESET 0x226 /* Reset */ +#define REG_RX_PFD_CONFIG 0x230 /* RX PFD Config */ +#define REG_RX_INTEGER_BYTE_0 0x231 /* RX Integer Byte 0 */ +#define REG_RX_INTEGER_BYTE_1 0x232 /* RX Integer Byte 1 */ +#define REG_RX_FRACT_BYTE_0 0x233 /* RX Fractional Byte 0 */ +#define REG_RX_FRACT_BYTE_1 0x234 /* RX Fractional Byte 1 */ +#define REG_RX_FRACT_BYTE_2 0x235 /* RX Fractional Byte 2 */ +#define REG_RX_FORCE_ALC 0x236 /* RX Force ALC */ +#define REG_RX_FORCE_VCO_TUNE_0 0x237 /* RX Force VCO Tune 0 */ +#define REG_RX_FORCE_VCO_TUNE_1 0x238 /* RX Force VCO Tune 1 */ +#define REG_RX_ALC_VARACTOR 0x239 /* RX ALC/Varactor */ +#define REG_RX_VCO_OUTPUT 0x23A /* RX VCO Output */ +#define REG_RX_CP_CURRENT 0x23B /* RX CP Current */ +#define REG_RX_CP_OFFSET 0x23C /* RX CP Offset */ +#define REG_RX_CP_CONFIG 0x23D /* RX CP Config */ +#define REG_RX_LOOP_FILTER_1 0x23E /* RX Loop Filter 1 */ +#define REG_RX_LOOP_FILTER_2 0x23F /* RX Loop Filter 2 */ +#define REG_RX_LOOP_FILTER_3 0x240 /* RX Loop Filter 3 */ +#define REG_RX_DITHERCP_CAL 0x241 /* RX Dither/CP Cal */ +#define REG_RX_VCO_BIAS_1 0x242 /* RX VCO Bias 1 */ +#define REG_RX_CAL_STATUS 0x244 /* RX Cal Status */ +#define REG_RX_VCO_CAL_REF 0x245 /* RX VCO Cal Ref */ +#define REG_RX_VCO_PD_OVERRIDES 0x246 /* RX VCO Pd Overrides */ +#define REG_RX_CP_OVERRANGE_VCO_LOCK 0x247 /* RX CP Over Range/VCO Lock */ +#define REG_RX_VCO_LDO 0x248 /* RX VCO LDO */ +#define REG_RX_VCO_CAL 0x249 /* RX VCO Cal */ +#define REG_RX_LOCK_DETECT_CONFIG 0x24A /* RX Lock Detect Config */ +#define REG_RX_CP_LEVEL_DETECT 0x24B /* RX CP Level Detect */ +#define REG_RX_DSM_SETUP_0 0x24C /* RX DSM Setup 0 */ +#define REG_RX_DSM_SETUP_1 0x24D /* RX DSM Setup 1 */ +#define REG_RX_CORRECTION_WORD0 0x24E /* RX Correction Word0 */ +#define REG_RX_CORRECTION_WORD1 0x24F /* RX Correction Word1 */ +#define REG_RX_VCO_VARACTOR_CTRL_0 0x250 /* RX VCO Varactor Control 0 */ +#define REG_RX_VCO_VARACTOR_CTRL_1 0x251 /* RX VCO Varactor Control 1 */ +#define REG_RX_FAST_LOCK_SETUP 0x25A /* Rx Fast Lock Setup */ +#define REG_RX_FAST_LOCK_SETUP_INIT_DELAY 0x25B /* Rx Fast Lock Setup Init Delay */ +#define REG_RX_FAST_LOCK_PROGRAM_ADDR 0x25C /* Rx Fast Lock Program Addr */ +#define REG_RX_FAST_LOCK_PROGRAM_DATA 0x25D /* Rx Fast Lock Program Data */ +#define REG_RX_FAST_LOCK_PROGRAM_READ 0x25E /* Rx Fast Lock Program Read */ +#define REG_RX_FAST_LOCK_PROGRAM_CTRL 0x25F /* Rx Fast Lock Program Control */ +#define REG_RX_LO_GEN_POWER_MODE 0x261 /* Rx LO Gen Power Mode */ +#define REG_TX_PFD_CONFIG 0x270 /* TX PFD Config */ +#define REG_TX_INTEGER_BYTE_0 0x271 /* TX Integer Byte 0 */ +#define REG_TX_INTEGER_BYTE_1 0x272 /* TX Integer Byte 1 */ +#define REG_TX_FRACT_BYTE_0 0x273 /* TX Fractional Byte 0 */ +#define REG_TX_FRACT_BYTE_1 0x274 /* TX Fractional Byte 1 */ +#define REG_TX_FRACT_BYTE_2 0x275 /* TX Fractional Byte 2 */ +#define REG_TX_FORCE_ALC 0x276 /* TX Force ALC */ +#define REG_TX_FORCE_VCO_TUNE_0 0x277 /* TX Force VCO Tune 0 */ +#define REG_TX_FORCE_VCO_TUNE_1 0x278 /* TX Force VCO Tune 1 */ +#define REG_TX_ALCVARACT_OR 0x279 /* TX ALC/Varact or */ +#define REG_TX_VCO_OUTPUT 0x27A /* TX VCO Output */ +#define REG_TX_CP_CURRENT 0x27B /* TX CP Current */ +#define REG_TX_CP_OFFSET 0x27C /* TX CP Offset */ +#define REG_TX_CP_CONFIG 0x27D /* TX CP Config */ +#define REG_TX_LOOP_FILTER_1 0x27E /* TX Loop Filter 1 */ +#define REG_TX_LOOP_FILTER_2 0x27F /* TX Loop Filter 2 */ +#define REG_TX_LOOP_FILTER_3 0x280 /* TX Loop Filter 3 */ +#define REG_TX_DITHERCP_CAL 0x281 /* TX Dither/CP Cal */ +#define REG_TX_VCO_BIAS_1 0x282 /* TX VCO Bias 1 */ +#define REG_TX_VCO_BIAS_2 0x283 /* TX VCO Bias 2 */ +#define REG_TX_CAL_STATUS 0x284 /* TX Cal Status */ +#define REG_TX_VCO_CAL_REF 0x285 /* TX VCO Cal Ref */ +#define REG_TX_VCO_PD_OVERRIDES 0x286 /* TX VCO Pd Overrides */ +#define REG_TX_CP_OVERRANGE_VCO_LOCK 0x287 /* TX CP Over Range/VCO Lock */ +#define REG_TX_VCO_LDO 0x288 /* TX VCO LDO */ +#define REG_TX_VCO_CAL 0x289 /* TX VCO Cal */ +#define REG_TX_LOCK_DETECT_CONFIG 0x28A /* TX Lock Detect Config */ +#define REG_TX_CP_LEVEL_DETECT 0x28B /* TX CP Level Detect */ +#define REG_TX_DSM_SETUP_0 0x28C /* TX DSM Setup 0 */ +#define REG_TX_DSM_SETUP_1 0x28D /* TX DSM Setup 1 */ +#define REG_TX_CORRECTION_WORD0 0x28E /* TX Correction Word0 */ +#define REG_TX_CORRECTION_WORD1 0x28F /* TX Correction Word1 */ +#define REG_TX_VCO_VARACTOR_CTRL_0 0x290 /* TX VCO Varactor Control 0 */ +#define REG_TX_VCO_VARACTOR_CTRL_1 0x291 /* TX VCO Varactor Control 1 */ +#define REG_DCXO_COARSE_TUNE 0x292 /* DCXO Coarse Tune */ +#define REG_DCXO_FINE_TUNE_HIGH 0x293 /* DCXO Fine Tune2 */ +#define REG_DCXO_FINE_TUNE_LOW 0x294 /* DCXO Fine Tune1 */ +#define REG_DCXO_CONFIG 0x295 /* DCXO Config */ +#define REG_DCXO_TEMPCO_WRITE 0x296 /* DCXO Tempco Write */ +#define REG_DCXO_TEMPCO_READ 0x297 /* DCXO Tempco Read */ +#define REG_DCXO_TEMPCO_ADDR 0x298 /* DCXO Tempco Addr */ +#define REG_DELTA_T_READ 0x299 /* Delta T Read */ +#define REG_TX_FAST_LOCK_SETUP 0x29A /* Tx Fast Lock Setup */ +#define REG_TX_FAST_LOCK_SETUP_INIT_DELAY 0x29B /* Tx Fast Lock Setup Init Delay */ +#define REG_TX_FAST_LOCK_PROGRAM_ADDR 0x29C /* Tx Fast Lock Program Addr */ +#define REG_TX_FAST_LOCK_PROGRAM_DATA 0x29D /* Tx Fast Lock Program Data */ +#define REG_TX_FAST_LOCK_PROGRAM_READ 0x29E /* Tx Fast Lock Program Read */ +#define REG_TX_FAST_LOCK_PROGRAM_CTRL 0x29F /* Tx Fast Lock Program Ctrl */ +#define REG_TX_LO_GEN_POWER_MODE 0x2A1 /* Tx LO Gen Power Mode */ +#define REG_BANDGAP_CONFIG0 0x2A6 /* Bandgap Config0 */ +#define REG_BANDGAP_CONFIG1 0x2A8 /* Bandgap Config1 */ +#define REG_REF_DIVIDE_CONFIG_1 0x2AB /* Ref Divide Config 1 */ +#define REG_REF_DIVIDE_CONFIG_2 0x2AC /* Ref Divide Config 2 */ +#define REG_GAIN_RX1 0x2B0 /* Gain Rx1 */ +#define REG_LPF_GAIN_RX1 0x2B1 /* LPF Gain Rx1 */ +#define REG_DIG_GAIN_RX1 0x2B2 /* Dig gain Rx1 */ +#define REG_FAST_ATTACK_STATE 0x2B3 /* Fast Attack State */ +#define REG_SLOW_LOOP_STATE 0x2B4 /* Slow Loop State */ +#define REG_GAIN_RX2 0x2B5 /* Gain Rx2 */ +#define REG_LPF_GAIN_RX2 0x2B6 /* LPF Gain Rx2 */ +#define REG_DIG_GAIN_RX2 0x2B7 /* Dig Gain Rx2 */ +#define REG_OVRG_SIGS_RX1 0x2B8 /* Ovrg Sigs Rx1 */ +#define REG_OVRG_SIGS_RX2 0x2B9 /* Ovrg Sigs Rx2 */ +#define REG_CTRL 0x3DF /* Control */ +#define REG_BIST_CONFIG 0x3F4 /* BIST Config */ +#define REG_OBSERVE_CONFIG 0x3F5 /* Observe Config */ +#define REG_BIST_AND_DATA_PORT_TEST_CONFIG 0x3F6 /* BIST and Data Port Test Config */ +#define REG_DAC_TEST_0 0x3FC /* DAC Test 0 */ +#define REG_DAC_TEST_1 0x3FD /* DAC Test 1 */ +#define REG_DAC_TEST_2 0x3FE /* DAC Test 2 */ + +/* +* REG_SPI_CONF +*/ +#define SOFT_RESET (1 << 7) /* Soft Reset */ +#define WIRE3_SPI (1 << 6) /* 3-Wire SPI */ +#define LSB_FIRST (1 << 5) /* LSB First */ +#define _LSB_FIRST (1 << 2) /* LSB First */ +#define _WIRE3_SPI (1 << 1) /* 3-Wire SPI */ +#define _SOFT_RESET (1 << 0) /* Soft reset */ + +/* +* REG_MULTICHIP_SYNC_AND_TX_MON_CTRL +*/ +#define TX2_MONITOR_ENABLE (1 << 6) /* Tx2 Monitor Enable */ +#define TX1_MONITOR_ENABLE (1 << 5) /* Tx1 Monitor Enable */ +#define MCS_RF_ENABLE (1 << 3) /* MCS RF Enable */ +#define MCS_BBPLL_ENABLE (1 << 2) /* MCS BBPLL enable */ +#define MCS_DIGITAL_CLK_ENABLE (1 << 1) /* MCS Digital CLK Enable */ +#define MCS_BB_ENABLE (1 << 0) /* MCS BB Enable */ + +/* +* REG_TX_ENABLE_FILTER_CTRL +*/ +#define THB2_EN (1 << 3) /* THB2 Enable */ +#define THB1_EN (1 << 2) /* THB1 Enable */ +#define TX_CHANNEL_ENABLE(x) (((x) & 0x3) << 6) /* Tx channel Enable<1:0> */ +#define THB3_ENABLE_INTERP(x) (((x) & 0x3) << 4) /* THB3 Enable & Interp<1:0> */ +#define TX_FIR_ENABLE_INTERPOLATION(x) (((x) & 0x3) << 0) /* Tx FIR Enable & Interpolation<1:0> */ +#define TX_1 1 +#define TX_2 2 +#define TX_ENABLE 1 +#define TX_DISABLE 0 + +/* +* REG_RX_ENABLE_FILTER_CTRL +*/ +#define RHB2_EN (1 << 3) /* RHB2 Enable */ +#define RHB1_EN (1 << 2) /* RHB1 Enable */ +#define RX_CHANNEL_ENABLE(x) (((x) & 0x3) << 6) /* Rx channel Enable<1:0> */ +#define DEC3_ENABLE_DECIMATION(x) (((x) & 0x3) << 4) /* DEC3 Enable & Decimation<1:0> */ +#define RX_FIR_ENABLE_DECIMATION(x) (((x) & 0x3) << 0) /* Rx FIR Enable & Decimation<1:0> */ +#define RX_1 1 +#define RX_2 2 +#define RX_ENABLE 1 +#define RX_DISABLE 0 + +/* +* REG_INPUT_SELECT +*/ +#define TX_OUTPUT (1 << 6) /* TX Output */ +#define RX_INPUT(x) (((x) & 0x3F) << 0) /* RX Input <5:0> */ + +/* +* REG_RFPLL_DIVIDERS +*/ +#define TX_VCO_DIVIDER(x) (((x) & 0xF) << 4) /* TX VCO Divider<3:0> */ +#define RX_VCO_DIVIDER(x) (((x) & 0xF) << 0) /* RX VCO Divider<3:0> */ + +/* +* REG_RX_CLOCK_DATA_DELAY +*/ +#define DATA_CLK_DELAY(x) (((x) & 0xF) << 4) /* DATA_CLK Delay<3:0> */ +#define RX_DATA_DELAY(x) (((x) & 0xF) << 0) /* Rx Data Delay <3:0> */ + +/* +* REG_TX_CLOCK_DATA_DELAY +*/ +#define FB_CLK_DELAY(x) (((x) & 0xF) << 4) /* FB_CLK Delay<3:0> */ +#define TX_DATA_DELAY(x) (((x) & 0xF) << 0) /* Tx Data Delay <3:0> */ + +/* +* REG_CLOCK_ENABLE +*/ +#define XO_BYPASS (1 << 4) /* XO Bypass */ +#define DIGITAL_POWER_UP (1 << 2) /* Digital Power Up */ +#define CLOCK_ENABLE_DFLT (1 << 1) /* Set to 1 */ +#define BBPLL_ENABLE (1 << 0) /* BBPLL Enable */ + +/* +* REG_BBPLL +*/ +#define CLKOUT_ENABLE (1 << 4) /* CLKOUT Enable */ +#define DAC_CLK_DIV2 (1 << 3) /* DAC Clk div2 */ +#define CLKOUT_SELECT(x) (((x) & 0x7) << 5) /* CLKOUT Select<2:0> */ +#define BBPLL_DIVIDER(x) (((x) & 0x7) << 0) /* BBPLL Divider <2:0> */ + +/* +* REG_START_TEMP_READING +*/ +#define START_TEMP_READING (1 << 0) /* Start Temp Reading */ + +/* +* REG_TEMP_SENSE2 +*/ +#define TEMP_SENSE_PERIODIC_ENABLE (1 << 0) /* Temp Sense Periodic Enable */ +#define MEASUREMENT_TIME_INTERVAL(x) (((x) & 0x7F) << 1) /* Measurement Time Interval<6:0> */ + +/* +* REG_TEMP_SENSOR_CONFIG +*/ +#define TEMP_SENSOR_DECIMATION(x) (((x) & 0x7) << 0) /* Temp Sensor Decimation<2:0> */ + +/* +* REG_PARALLEL_PORT_CONF_1 +*/ +#define PP_TX_SWAP_IQ (1 << 7) /* PP Tx Swap IQ */ +#define PP_RX_SWAP_IQ (1 << 6) /* PP Rx Swap IQ */ +#define TX_CHANNEL_SWAP (1 << 5) /* Tx Channel swap */ +#define RX_CHANNEL_SWAP (1 << 4) /* Rx Channel swap */ +#define RX_FRAME_PULSE_MODE (1 << 3) /* Rx Frame Pulse Mode */ +#define R2T2_TIMING (1 << 2) /* 2R2T Timing */ +#define INVERT_DATA_BUS (1 << 1) /* Invert data bus */ +#define INVERT_DATA_CLK (1 << 0) /* Invert DATA CLK */ + +/* +* REG_PARALLEL_PORT_CONF_2 +*/ +#define FDD_ALT_WORD_ORDER (1 << 7) /* FDD Alt Word Order */ +#define INVERT_RX1 (1 << 6) /* Invert Rx1 */ +#define INVERT_RX2 (1 << 5) /* Invert Rx2 */ +#define INVERT_TX1 (1 << 4) /* Invert Tx1 */ +#define INVERT_TX2 (1 << 3) /* Invert Tx2 */ +#define INVERT_RX_FRAME (1 << 2) /* Invert Rx Frame */ +#define DELAY_RX_DATA(x) (((x) & 0x3) << 0) /* Delay Rx Data<1:0> */ + +/* +* REG_PARALLEL_PORT_CONF_3 +*/ +#define FDD_RX_RATE_2TX_RATE (1 << 7) /* FDD Rx Rate = 2*Tx Rate */ +#define SWAP_PORTS (1 << 6) /* Swap Ports */ +#define SINGLE_DATA_RATE (1 << 5) /* Single Data Rate */ +#define LVDS_MODE (1 << 4) /* LVDS Mode */ +#define HALF_DUPLEX_MODE (1 << 3) /* Half Duplex Mode */ +#define SINGLE_PORT_MODE (1 << 2) /* Single Port Mode */ +#define FULL_PORT (1 << 1) /* Full Port */ +#define FULL_DUPLEX_SWAP_BITS (1 << 0) /* Full Duplex Swap Bits */ + +/* +* REG_ENSM_MODE +*/ +#define FDD_MODE (1 << 0) /* FDD Mode */ + +/* +* REG_ENSM_CONFIG_1 +*/ +#define ENABLE_RX_DATA_PORT_FOR_CAL (1 << 7) /* Enable Rx Data Port for Cal */ +#define FORCE_RX_ON (1 << 6) /* Force Rx On */ +#define FORCE_TX_ON (1 << 5) /* Force Tx On */ +#define ENABLE_ENSM_PIN_CTRL (1 << 4) /* Enable ENSM Pin Control */ +#define LEVEL_MODE (1 << 3) /* Level Mode */ +#define FORCE_ALERT_STATE (1 << 2) /* Force Alert State */ +#define AUTO_GAIN_LOCK (1 << 1) /* Auto Gain Lock */ +#define TO_ALERT (1 << 0) /* To Alert */ + +/* +* REG_ENSM_CONFIG_2 +*/ +#define FDD_EXTERNAL_CTRL_ENABLE (1 << 7) /* FDD External Control Enable */ +#define POWER_DOWN_RX_SYNTH (1 << 6) /* Power Down Rx Synth */ +#define POWER_DOWN_TX_SYNTH (1 << 5) /* Power Down Tx Synth */ +#define TXNRX_SPI_CTRL (1 << 4) /* TXNRX SPI Control */ +#define SYNTH_ENABLE_PIN_CTRL_MODE (1 << 3) /* Synth Enable Pin Control Mode */ +#define DUAL_SYNTH_MODE (1 << 2) /* Dual Synth Mode */ +#define RX_SYNTH_READY_MASK (1 << 1) /* Rx Synth Ready Mask */ +#define TX_SYNTH_READY_MASK (1 << 0) /* Tx Synth Ready Mask */ + +/* +* REG_CALIBRATION_CTRL +*/ +#define RX_BB_TUNE_CAL (1 << 7) /* Rx BB Tune */ +#define TX_BB_TUNE_CAL (1 << 6) /* Tx BB Tune */ +#define RX_QUAD_CAL (1 << 5) /* Rx Quad Cal */ +#define TX_QUAD_CAL (1 << 4) /* Tx Quad Cal */ +#define RX_GAIN_STEP_CAL (1 << 3) /* Rx Gain Step Cal */ +#define TXMON_CAL (1 << 2) +#define RFDC_CAL (1 << 1) /* DC Cal RF Start */ +#define BBDC_CAL (1 << 0) /* DC cal BB Start */ + + +/* +* REG_STATE +*/ +#define CALIBRATION_SEQUENCE_STATE(x) (((x) & 0xF) << 4) /* Calibration Sequence State<3:0> */ +#define ENSM_STATE(x) (((x) & 0xF) << 0) /* ENSM State<3:0> */ +#define ENSM_STATE_SLEEP_WAIT 0x0 +#define ENSM_STATE_ALERT 0x5 +#define ENSM_STATE_TX 0x6 +#define ENSM_STATE_TX_FLUSH 0x7 +#define ENSM_STATE_RX 0x8 +#define ENSM_STATE_RX_FLUSH 0x9 +#define ENSM_STATE_FDD 0xA +#define ENSM_STATE_FDD_FLUSH 0xB +#define ENSM_STATE_INVALID 0xFF +#define ENSM_STATE_SLEEP 0x80 + +/* +* REG_AUXDAC_2_WORD +*/ +#define AUXDAC_2_WORD_MSB(x) (((x) & 0x3F) << 2) /* AuxDAC 2 Word<9:2> */ +#define AUXDAC_1_WORD(x) (((x) & 0x3) << 0) /* AuxDAC 1 Word <1:0> */ + +/* +* REG_AUXDAC_1_CONFIG +*/ +#define COMP_CTRL_1 (1 << 5) /* Comp Ctrl 1 */ +#define AUXDAC1_STP_FACTOR (1 << 4) /* AuxDAC1 Step Factor */ +#define AUXDAC_1_VREF(x) (((x) & 0x3) << 2) /* AuxDAC 1 Vref<1:0> */ +#define AUXDAC_1_WORD_LSB(x) (((x) & 0x3) << 0) /* AuxDAC 2 Word <1:0> */ + +/* +* REG_AUXDAC_2_CONFIG +*/ +#define COMP_CTRL_2 (1 << 5) /* Comp Ctrl 2 */ +#define AUXDAC2_STP_FACTOR (1 << 4) /* AuxDAC2 Step Factor */ +#define AUXDAC_2_VREF(x) (((x) & 0xF) << 2) /* AuxDAC 2 Vref<1:0> */ +#define AUXDAC_2_WORD_LSB(x) (((x) & 0x3) << 0) /* AuxDAC 2 Word <1:0> */ + +/* +* REG_AUXADC_CLOCK_DIVIDER +*/ +#define AUXADC_CLOCK_DIVIDER(x) (((x) & 0x3F) << 0) /* AuxADC Clock Divider<5:0> */ + +/* +* REG_AUXADC_CONFIG +*/ +#define AUXADC_POWER_DOWN (1 << 0) /* AuxADC Power Down */ +#define AUX_ADC_DECIMATION(x) (((x) & 0x7) << 1) /* Aux ADC Decimation<2:0> */ + +/* +* REG_AUXADC_LSB +*/ +#define AUXADC_WORD_LSB(x) (((x) & 0xF) << 0) /* AuxADC Word LSB<3:0> */ + +/* +* REG_AUTO_GPO +*/ +#define GPO_ENABLE_AUTO_RX(x) (((x) & 0xF) << 4) /* GPO Enable Auto Rx<3:0> */ +#define GPO_ENABLE_AUTO_TX(x) (((x) & 0xF) << 0) /* GPO Enable Auto Tx<3:0> */ + +/* +* REG_AGC_ATTACK_DELAY +*/ +#define INVERT_BYPASSED_LNA_POLARITY (1 << 6) /* Invert Bypassed LNA Polarity */ +#define AGC_ATTACK_DELAY(x) (((x) & 0x3F) << 0) /* AGC Attack Delay<5:0> */ + +/* +* REG_AUXDAC_ENABLE_CTRL +*/ +#define AUXDAC_MANUAL_BAR(x) (((x) & 0x3) << 6) /* AuxDac Manual Bar<1:0> */ +#define AUXDAC_AUTO_TX_BAR(x) (((x) & 0x3) << 4) /* AuxDAC Auto Tx Bar<1:0> */ +#define AUXDAC_AUTO_RX_BAR(x) (((x) & 0x3) << 2) /* AuxDAC Auto Rx Bar<1:0> */ +#define AUXDAC_INIT_BAR(x) (((x) & 0x3) << 0) /* AuxDAC Init Bar<1:0> */ + +/* +* REG_EXTERNAL_LNA_CTRL +*/ +#define AUXDAC_MANUAL_SELECT (1 << 7) /* AuxDAC Manual Select */ +#define EXTERNAL_LNA2_CTRL (1 << 6) /* External LNA2 control */ +#define EXTERNAL_LNA1_CTRL (1 << 5) /* External LNA1 control */ +#define GPO_MANUAL_SELECT (1 << 4) /* GPO manual select */ +#define OPEN(x) (((x) & 0xF) << 0) /* Open<3:0> */ + +/* +* REG_GPO_FORCE_AND_INIT +*/ +#define GPO_MANUAL_CTRL(x) (((x) & 0xF) << 4) /* GPO Manual Control<3:0> */ +#define GPO_INIT_STATE(x) (((x) & 0xF) << 0) /* GPO Init State<3:0> */ + +/* +* REG_CTRL_OUTPUT_ENABLE +*/ +#define EN_CTRL7 (1 << 7) /* En ctrl7 */ +#define EN_CTRL6 (1 << 6) /* En ctrl6 */ +#define EN_CTRL5 (1 << 5) /* En ctrl5 */ +#define EN_CTRL4 (1 << 4) /* En ctrl4 */ +#define EN_CTRL3 (1 << 3) /* En ctrl3 */ +#define EN_CTRL2 (1 << 2) /* En ctrl2 */ +#define EN_CTRL1 (1 << 1) /* En ctrl1 */ +#define EN_CTRL0 (1 << 0) /* En ctrl0 */ + +/* +* REG_PRODUCT_ID +*/ +#define PRODUCT_ID_MASK 0xF8 +#define PRODUCT_ID_9361 0x08 +#define REV_MASK 0x07 + +/* +* REG_REFERENCE_CLOCK_CYCLES +*/ +#define REFERENCE_CLOCK_CYCLES_PER_US(x) (((x) & 0x7F) << 0) /* Reference Clock Cycles per us<6:0> */ + +/* +* REG_DIGITAL_IO_CTRL +*/ +#define CLK_OUT_DRIVE (1 << 7) /* CLK Out Drive */ +#define DATACLK_DRIVE (1 << 6) /* DATACLK drive */ +#define DATA_PORT_DRIVE (1 << 2) /* Data Port Drive */ +#define DATACLK_SLEW(x) (((x) & 0x3) << 4) /* DATACLK slew <1:0> */ +#define DATA_PORT_SLEW(x) (((x) & 0x3) << 0) /* Data Port Slew<1:0> */ + +/* +* REG_LVDS_BIAS_CTRL +*/ +#define RX_ON_CHIP_TERM (1 << 5) /* Rx On Chip Term */ +#define LVDS_BYPASS_BIAS_R (1 << 4) /* Bypass Bias R */ +#define LVDS_TX_LO_VCM (1 << 3) /* LVDS Tx LO VCM */ +#define CLK_OUT_SLEW(x) (((x) & 0x3) << 6) /* CLK Out Slew<1:0> */ +#define LVDS_BIAS(x) (((x) & 0x7) << 0) /* LVDS Bias <2:0> */ + +/* +* REG_SDM_CTRL_1 +*/ +#define INIT_BB_FO_CAL (1 << 2) /* Init BB FO CAL */ +#define BBPLL_RESET_BAR (1 << 0) /* BBPLL Reset Bar */ + +/* +* REG_CLOCK_CTRL +*/ +#define REF_FREQ_SCALER(x) (((x) & 0x3) << 0) /* Ref Frequency Scaler */ + +/* +* REG_CP_CURRENT +*/ +#define CHARGE_PUMP_CURRENT(x) (((x) & 0x3F) << 0) /* Charge Pump Current<5:0> */ + +/* +* REG_CP_BLEED_CURRENT +*/ +#define MCS_REFCLK_SCALE_EN (1 << 7) /* MCS refclk Scale En */ + +/* +* REG_LOOP_FILTER_1 +*/ +#define C1_WORD(x) (((x) & 0x7) << 5) /* C1 Word<2:0> */ +#define R1_WORD(x) (((x) & 0x1F) << 0) /* R1 Word<4:0> */ + +/* +* REG_LOOP_FILTER_2 +*/ +#define R2_WORD (1 << 7) /* R2 Word<0> */ +#define C2_WORD(x) (((x) & 0x1F) << 2) /* C2 Word<4:0> */ +#define C1_WORD_LSB(x) (((x) & 0x3) << 0) /* C1 Word<4:3> */ + +/* +* REG_LOOP_FILTER_3 +*/ +#define BYPASS_C3 (1 << 7) /* Bypass C3 */ +#define BYPASS_R2 (1 << 6) /* Bypass R2 */ +#define C3_WORD(x) (((x) & 0xF) << 2) /* C3 Word<3:0> */ +#define R2_WORD_LSB(x) (((x) & 0x3) << 0) /* R2 Word<2:1> */ + +/* +* REG_VCO_CTRL +*/ +#define FREQ_CAL_ENABLE (1 << 7) /* Freq Cal Enable */ +#define FREQ_CAL_RESET (1 << 4) /* Freq Cal Reset */ +#define FREQ_CAL_COUNT_LENGTH(x) (((x) & 0x3) << 5) /* Freq Cal Count Length<1:0> */ + +/* +* REG_SDM_CTRL +*/ +#define CAL_CLOCK_DIV_4 (1 << 4) /* Cal Clock div 4 */ + +/* +* REG_RX_SYNTH_POWER_DOWN_OVERRIDE +*/ +#define RX_LO_POWER_DOWN (1 << 4) /* Rx LO Power Down */ +#define RX_SYNTH_VCO_ALC_POWER_DOWN (1 << 3) /* Rx Synth VCO ALC Power Down */ +#define RX_SYNTH_PTAT_POWER_DOWN (1 << 2) /* Rx Synth PTAT Power Down */ +#define RX_SYNTH_VCO_POWER_DOWN (1 << 1) /* Rx Synth VCO Power Down */ +#define RX_SYNTH_VCO_LDO_POWER_DOWN (1 << 0) /* Rx Synth VCO LDO Power Down */ + +/* +* REG_TX_SYNTH_POWER_DOWN_OVERRIDE +*/ +#define TX_LO_POWER_DOWN (1 << 4) /* Tx LO Power Down */ +#define TX_SYNTH_VCO_ALC_POWER_DOWN (1 << 3) /* Tx Synth VCO ALC Power Down */ +#define TX_SYNTH_PTAT_POWER_DOWN (1 << 2) /* Tx Synth PTAT Power Down */ +#define TX_SYNTH_VCO_POWER_DOWN (1 << 1) /* Tx Synth VCO Power Down */ +#define TX_SYNTH_VCO_LDO_POWER_DOWN (1 << 0) /* Tx Synth VCO LDO Power Down */ + +/* +* REG_RX_ANALOG_POWER_DOWN_OVERRIDE_1 +*/ +#define RX_OFFSET_DAC_CGIN_POWER_DOWN(x) (((x) & 0x3) << 6) /* Rx Offset DAC CGin Power Down<1:0> */ +#define RX_LMT_OVERLOAD_POWER_DOWN(x) (((x) & 0x3) << 4) /* Rx LMT Overload Power Down<1:0> */ +#define RX_MIXER_GM_POWER_DOWN(x) (((x) & 0x3) << 2) /* Rx Mixer Gm Power Down<1:0> */ +#define RX_CGB_POWER_DOWN(x) (((x) & 0x3) << 0) /* Rx CGB Power Down<1:0> */ + +/* +* REG_RX_ANALOG_POWER_DOWN_OVERRIDE_2 +*/ +#define RX_BBF_POWER_DOWN(x) (((x) & 0x3) << 6) /* Rx BBF Power Down<1:0> */ +#define RX_TIA_POWER_DOWN(x) (((x) & 0x3) << 4) /* Rx TIA Power Down<1:0> */ +#define RX_MIXER_POWER_DOWN(x) (((x) & 0x3) << 2) /* Rx Mixer Power Down<1:0> */ +#define RX_OFFSET_DAC_CGOUT_POWER_DOWN(x) (((x) & 0x3) << 0) /* Rx Offset DAC CGOut Power Down<1:0> */ + +/* +* REG_TX_ANALOG_POWER_DOWN_OVERRIDE_1 +*/ +#define TX_SECONDARY_FILTER_POWER_DOWN(x) (((x) & 0x3) << 6) /* Tx Secondary Filter Power Down<1:0> */ +#define TX_BBF_POWER_DOWN(x) (((x) & 0x3) << 4) /* Tx BBF Power Down<1:0> */ +#define TX_DAC_POWER_DOWN(x) (((x) & 0x3) << 2) /* Tx DAC Power Down<1:0> */ +#define TX_DAC_BIAS_POWER_DOWN(x) (((x) & 0x3) << 0) /* Tx DAC Bias Power Down<1:0> */ + +/* +* REG_ANALOG_POWER_DOWN_OVERRIDE +*/ +#define RX_EXT_VCO_BUFFER_POWER_DOWN (1 << 5) /* Rx Ext VCO Buffer Power Down */ +#define TX_EXT_VCO_BUFFER_POWER_DOWN (1 << 4) /* Tx Ext VCO Buffer Power Down */ +#define TX_MONITOR_POWER_DOWN(x) (((x) & 0x3) << 2) /* Tx Monitor Power Down<1:0> */ +#define TX_UPCONVERTER_POWER_DOWN(x) (((x) & 0x3) << 0) /* Tx Upconverter Power Down<1:0> */ + +/* +* REG_MISC_POWER_DOWN_OVERRIDE +*/ +#define RX_LNA_POWER_DOWN (1 << 6) /* Rx LNA Power Down */ +#define DCXO_POWER_DOWN (1 << 1) /* DCXO Power Down */ +#define MASTER_BIAS_POWER_DOWN (1 << 0) /* Master Bias Power Down */ +#define RX_CALIBRATION_POWER_DOWN(x) (((x) & 0x3) << 2) /* Rx Calibration Power Down<1:0> */ + +/* +* REG_CH_1_OVERFLOW +*/ +#define BBPLL_LOCK (1 << 7) /* BBPLL Lock */ +#define CH_1_INT3 (1 << 6) /* CH 1 INT3 */ +#define CH1_HB3 (1 << 5) /* CH1 HB3 */ +#define CH1_HB2 (1 << 4) /* CH1 HB2 */ +#define CH1_QEC (1 << 3) /* CH1 QEC */ +#define CH1_HB1 (1 << 2) /* CH1 HB1 */ +#define CH1_TFIR (1 << 1) /* CH1 TFIR */ +#define CH1_RFIR (1 << 0) /* CH1 RFIR */ + +/* +* REG_CH_2_OVERFLOW +*/ +#define CH2_INT3 (1 << 6) /* CH2 INT3 */ +#define CH2_HB3 (1 << 5) /* CH2 HB3 */ +#define CH2_HB2 (1 << 4) /* CH2 HB2 */ +#define CH2_QEC (1 << 3) /* CH2 QEC */ +#define CH2_HB1 (1 << 2) /* CH2 HB1 */ +#define CH2_TFIR (1 << 1) /* CH2 TFIR */ +#define CH2_RFIR (1 << 0) /* CH2 RFIR */ + +/* +* REG_TX_FILTER_CONF +*/ +#define TX_FIR_GAIN_6DB (1 << 0) /* Filter Gain */ +#define FIR_START_CLK (1 << 1) /* Start Tx/Rx Clock */ +#define FIR_WRITE (1 << 2) /* Write Tx/Rx */ +#define FIR_SELECT(x) (((x) & 0x3) << 3) /* Select Tx/Rx CH<1:0> */ +#define FIR_NUM_TAPS(x) (((x) & 0x7) << 5) /* Number of Taps<2:0> */ + +/* +* REG_TX_MON_LOW_GAIN +*/ +#define TX_MON_TRACK (1 << 5) /* Tx Mon Track */ +#define TX_MON_LOW_GAIN(x) (((x) & 0x1F) << 0) /* Tx Mon Low Gain<4:0> */ + +/* +* REG_TX_MON_HIGH_GAIN +*/ +#define TX_MON_HIGH_GAIN(x) (((x) & 0x1F) << 0) /* Tx Mon High Gain<4:0> */ + +/* +* REG_TX_LEVEL_THRESH +*/ +#define TX_LEVEL_THRESH(x) (((x) & 0x3F) << 2) /* Tx Level Threshold<5:0> */ +#define TX_MON_DELAY_COUNTER(x) (((x) & 0x3) << 0) /* Tx Mon Delay Counter<9:8> */ + +/* +* REG_TX_RSSI_LSB +*/ +#define TX_RSSI_2 (1 << 1) /* Tx RSSI 2<0> */ +#define TX_RSSI_1 (1 << 0) /* TX RSSI 1<0> */ + +/* +* REG_TPM_MODE_ENABLE +*/ +#define TX2_MON_ENABLE (1 << 7) /* Tx2 Monitor Enable */ +#define TX1_MON_ENABLE (1 << 5) /* Tx1 Monitor Enable */ +#define ONE_SHOT_MODE (1 << 6) /* One Shot Mode */ +#define TX_MON_DURATION(x) (((x) & 0xF) << 0) /* Tx Mon Duration<3:0> */ + +/* +* REG_TX_MON_1_CONFIG +*/ +#define TX_MON_1_LO_CM(x) (((x) & 0x3F) << 2) /* Tx Mon 1 LO CM<5:0> */ +#define TX_MON_1_GAIN(x) (((x) & 0x3) << 0) /* Tx Mon 1 Gain<1:0> */ + +/* +* REG_TX_MON_2_CONFIG +*/ +#define TX_MON_2_LO_CM(x) (((x) & 0x3F) << 2) /* Tx Mon 2 LO CM<5:0> */ +#define TX_MON_2_GAIN(x) (((x) & 0x3) << 0) /* Tx Mon 2 Gain<1:0> */ + +/* +* REG_TX1_ATTEN_1 +*/ +#define TX_1_ATTEN (1 << 0) /* Tx 1 Atten <8> */ + +/* +* REG_TX2_ATTEN_1 +*/ +#define TX_2_ATTEN (1 << 0) /* Tx 2 Atten <8> */ + +/* +* REG_TX_ATTEN_OFFSET +*/ +#define MASK_CLR_ATTEN_UPDATE (1 << 6) /* Mask Clr Atten Update */ +#define TX_ATTEN_OFFSET(x) (((x) & 0x3F) << 0) /* Tx Atten Offset<5:0> */ + +/* +* REG_TX1_DIG_ATTEN +*/ +#define SEL_TX1_TX2 (1 << 6) /* Sel Tx1 & Ttx2 */ + +/* +* REG_TX2_DIG_ATTEN +*/ +#define IMMEDIATELY_UPDATE_TPC_ATTEN (1 << 6) /* Immediately Update TPC Atten */ + +/* +* REG_TX1_SYMBOL_ATTEN +*/ +#define TX_1_SYMBOL_ATTEN(x) (((x) & 0x7F) << 0) /* Tx 1 Symbol Attenuation<6:0> */ + +/* +* REG_TX2_SYMBOL_ATTEN +*/ +#define TX_2_SYMBOL_ATTEN(x) (((x) & 0x7F) << 0) /* Tx 2 Symbol Attenuation<6:0> */ + +/* +* REG_TX_SYMBOL_ATTEN_CONFIG +*/ +#define USE_TX1_PIN_SYMBOL_ATTEN (1 << 3) /* Use Tx1 Pin & Symbol Atten */ +#define USE_CTRL_IN_FOR_SYMBOL_ATTEN (1 << 1) /* Use CTRL IN for symbol Atten */ +#define ENABLE_SYMBOL_ATTEN (1 << 0) /* Enable Symbol Atten */ + +/* +* REG_TX_FORCE_BITS +*/ +#define FORCE_OUT_2_TX2_OFFSET (1 << 7) /* Force Out 2 Tx2 Offset */ +#define FORCE_OUT_2_TX1_OFFSET (1 << 6) /* Force Out 2 Tx1 Offset */ +#define FORCE_OUT_2_TX2_PHASE_GAIN (1 << 5) /* Force Out 2 Tx2 Phase & Gain */ +#define FORCE_OUT_2_TX1_PHASE_GAIN (1 << 4) /* Force Out 2 Tx1 Phase & Gain */ +#define FORCE_OUT_1_TX2_OFFSET (1 << 3) /* Force Out 1 Tx2 Offset */ +#define FORCE_OUT_1_TX1_OFFSET (1 << 2) /* Force Out 1 Tx1 Offset */ +#define FORCE_OUT_1_TX2_PHASE_GAIN (1 << 1) /* Force Out 1 Tx2 Phase & Gain */ +#define FORCE_OUT_1_TX1_PHASE_GAIN (1 << 0) /* Force Out 1 Tx1 Phase & Gain */ + +/* +* REG_QUAD_CAL_NCO_FREQ_PHASE_OFFSET +*/ +#define RX_NCO_FREQ(x) (((x) & 0x3) << 5) /* Rx NCO Frequency<1:0> */ +#define RX_NCO_PHASE_OFFSET(x) (((x) & 0x1F) << 0) /* Rx NCO Phase Offset<4:0> */ + +/* +* REG_QUAD_CAL_CTRL +*/ +#define FREE_RUN_ENABLE (1 << 7) /* Free Run Enable */ +#define SETTLE_MAIN_ENABLE (1 << 6) /* Settle Main Enable */ +#define DC_OFFSET_ENABLE (1 << 5) /* DC Offset Enable */ +#define GAIN_ENABLE (1 << 4) /* Gain Enable */ +#define PHASE_ENABLE (1 << 3) /* Phase Enable */ +#define QUAD_CAL_SOFT_RESET (1 << 2) /* Quad Cal Soft Reset */ +#define M_DECIM(x) (((x) & 0x3) << 0) /* M<1:0> */ + +/* +* REG_KEXP_1 +*/ +#define KEXP_TX(x) (((x) & 0x3) << 6) /* Kexp Tx<1:0> */ +#define KEXP_TX_COMP(x) (((x) & 0x3) << 4) /* Kexp Tx_comp <1:0> */ +#define KEXP_DC_I(x) (((x) & 0x3) << 2) /* Kexp DC I <1:0> */ +#define KEXP_DC_Q(x) (((x) & 0x3) << 0) /* Kexp DC Q <1:0> */ + +/* +* REG_KEXP_2 +*/ +#define INVERT_I_DATA (1 << 5) /* Invert I data */ +#define INVERT_Q_DATA (1 << 4) /* Invert Q data */ +#define TX_NCO_FREQ(x) (((x) & 0x3) << 6) /* Tx NCO frequency<1:0> */ +#define KEXP_PHASE(x) (((x) & 0x3) << 2) /* Kexp Phase <1:0> */ +#define KEXP_AMP(x) (((x) & 0x3) << 0) /* Kexp Amp <1:0> */ + +/* +* REG_QUAD_CAL_STATUS_TX1 +*/ +#define TX1_LO_CONV (1 << 1) /* Tx1 LO Conv */ +#define TX1_SSB_CONV (1 << 0) /* Tx1 SSB Conv */ +#define TX1_CONVERGENCE_COUNT(x) (((x) & 0x3F) << 2) /* Tx1 Convergence Count<5:0> */ + +/* +* REG_QUAD_CAL_STATUS_TX2 +*/ +#define TX2_LO_CONV (1 << 1) /* Tx2 LO Conv */ +#define TX2_SSB_CONV (1 << 0) /* Tx2 SSB Conv */ +#define TX2_CONVERGENCE_COUNT(x) (((x) & 0x3F) << 2) /* Tx2 Convergence Count<5:0> */ + +/* +* REG_TX_QUAD_FULL_LMT_GAIN +*/ +#define RX_FULL_TABLELMT_TABLE_GAIN(x) (((x) & 0x7F) << 0) /* RX Full table/LMT table gain<6:0> */ + +/* +* REG_SQUARER_CONFIG +*/ +#define GM_STAGE_TIME_CON_OVERRIDE (1 << 5) /* Gm Stage Time Con Override */ +#define GM_STAGE_MV_HP_POLE (1 << 4) /* Gm Stage MV HP Pole */ +#define GM_STAGE_LOWER_CM (1 << 3) /* Gm Stage Lower CM */ +#define BYPASS_BIAS_R (1 << 0) /* Bypass Bias R */ +#define VBIAS_CTRL(x) (((x) & 0x3) << 1) /* Vbias Control<1:0> */ + +/* +* REG_THRESH_ACCUM +*/ +#define THRESH_ACCUMULATOR(x) (((x) & 0xF) << 0) /* Threshold Accumulator<3:0> */ + +/* +* REG_TX_QUAD_LPF_GAIN +*/ +#define RX_LPF_GAIN(x) (((x) & 0x1F) << 0) /* RX LPF gain<4:0> */ + +/* +* REG_TXDAC_VDS_I +*/ +#define TXDAC_VDS_I(x) (((x) & 0x3F) << 0) /* TxDAC Vds I<5:0> */ + +/* +* REG_TXDAC_VDS_Q +*/ +#define TXDAC_VDS_Q(x) (((x) & 0x3F) << 0) /* TxDAC Vds Q<5:0> */ + +/* +* REG_TXDAC_GN_I +*/ +#define TXDAC_GN_I(x) (((x) & 0x3F) << 0) /* txDAC_gn_I<5:0> */ + +/* +* REG_TXDAC_GN_Q +*/ +#define TXDAC_GN_Q(x) (((x) & 0x3F) << 0) /* txDAC_gn_Q<5:0> */ + +/* +* REG_TXBBF_OPAMP_A +*/ +#define OPAMPA_OUTPUT_BIAS(x) (((x) & 0x3) << 5) /* OpAmpA Output Bias<1:0> */ +#define OPAMPA_RZ(x) (((x) & 0x3) << 3) /* OpAmpA RZ<1:0> */ +#define OPAMP_A_CC(x) (((x) & 0x7) << 0) /* OpAmp A CC<2:0> */ + +/* +* REG_TXBBF_OPAMP_B +*/ +#define OPAMPB_OUTPUT_BIAS(x) (((x) & 0x3) << 5) /* OpAmpB Output Bias<1:0> */ +#define OPAMPB_RZ(x) (((x) & 0x3) << 3) /* OpAmpB RZ<1:0> */ +#define OPAMP_B_CC(x) (((x) & 0x7) << 0) /* OpAmp B CC<2:0> */ + +/* +* REG_TX_BBF_R1 +*/ +#define OVERRIDE_ENABLE (1 << 7) /* Override enable */ +#define R1(x) (((x) & 0x1F) << 0) /* R1<4:0> */ + +/* +* REG_TX_BBF_R2 +*/ +#define R2(x) (((x) & 0x1F) << 0) /* R2<4:0> */ + +/* +* REG_TX_BBF_R3 +*/ +#define R3(x) (((x) & 0x1F) << 0) /* R3<4:0> */ + +/* +* REG_TX_BBF_R4 +*/ +#define R4(x) (((x) & 0x1F) << 0) /* R4<4:0> */ + +/* +* REG_TX_BBF_RP +*/ +#define RP(x) (((x) & 0x1F) << 0) /* Rp<4:0> */ + +/* +* REG_TX_BBF_C1 +*/ +#define C1(x) (((x) & 0x3F) << 0) /* C1<5:0> */ + +/* +* REG_TX_BBF_C2 +*/ +#define C2(x) (((x) & 0x3F) << 0) /* C2<5:0> */ + +/* +* REG_TX_BBF_CP +*/ +#define CP(x) (((x) & 0x3F) << 0) /* Cp<5:0> */ + +/* +* REG_TX_TUNE_CTRL +*/ +#define PD_TUNE (1 << 2) /* PD Tune */ +#define TUNER_RESAMPLE (1 << 1) /* Tuner Resample */ +#define TUNER_RESAMPLE_PHASE (1 << 0) /* Tuner Resample Phase */ +#define TUNE_CTRL(x) (((x) & 0x3) << 5) /* Tune Control<1:0> */ + +/* +* REG_TX_BBF_R2B +*/ +#define TX_BBF_BYPASS_BIAS_R (1 << 7) /* Bypass Bias R */ +#define R2B_OVR (1 << 5) /* R2b Ovr */ +#define R2B(x) (((x) & 0x1F) << 0) /* R2b<4:0> */ + +/* +* REG_TX_BBF_TUNE +*/ +#define BBF1_COMP_I (1 << 3) /* BBF1 Comp I */ +#define BBF1_COMP_Q (1 << 2) /* BBF1 Comp Q */ +#define BBF2_COMP_I (1 << 1) /* BBF2 Comp I */ +#define BBF2_COMP_Q (1 << 0) /* BBF2 Comp Q */ + +/* +* REG_CONFIG0 +*/ +#define BIAS(x) (((x) & 0x3) << 6) /* Bias<1:0> */ +#define RGM(x) (((x) & 0x3) << 4) /* Rgm<1:0> */ +#define CC(x) (((x) & 0x3) << 2) /* Cc<1:0> */ +#define AMPBIAS(x) (((x) & 0x3) << 0) /* AmpBias<1:0> */ + +/* +* REG_RESISTOR +*/ +#define RESISTOR(x) (((x) & 0xF) << 0) /* Resistor<3:0> */ + +/* +* REG_CAPACITOR +*/ +#define CAPACITOR(x) (((x) & 0x3F) << 0) /* Capacitor<5:0> */ + +/* +* REG_LO_CM +*/ +#define LO_COMMON_MODE(x) (((x) & 0x3) << 5) /* LO Common Mode<1:0> */ + +/* +* REG_TX_BBF_TUNE_MODE +*/ +#define EVALTIME (1 << 4) /* EvalTime */ +#define TX_BBF_TUNE_DIVIDER (1 << 0) /* TX BBF Tune Divider<8> */ +#define TUNE_COMP_MASK(x) (((x) & 0x3) << 5) /* Tune Comp Mask<1:0> */ +#define TUNER_MODE(x) (((x) & 0x7) << 1) /* Tuner Mode<2:0> */ + +/* +* REG_RX_FILTER_CONFIG +*/ +#define WRITE_RX (1 << 2) /* Write Rx */ +#define START_RX_CLOCK (1 << 1) /* Start Rx Clock */ +#define NUMBER_OF_TAPS(x) (((x) & 0x7) << 5) /* Number of Taps */ +#define SELECT_RX_CH(x) (((x) & 0x3) << 3) /* Select Rx Ch<1:0> */ + +/* +* REG_RX_FILTER_GAIN +*/ +#define FILTER_GAIN(x) (((x) & 0x3) << 0) /* Filter gain<1:0> */ + +/* +* REG_AGC_CONFIG_1 +*/ +#define DEC_PWR_FOR_LOW_PWR (1 << 7) /* Dec Pwr for Low Pwr */ +#define DEC_PWR_FOR_LOCK_LEVEL (1 << 6) /* Dec Pwr for Lock Level */ +#define DEC_PWR_FOR_GAIN_LOCK_EXIT (1 << 5) /* Dec Pwr for Gain Lock Exit */ +#define SLOW_ATTACK_HYBRID_MODE (1 << 4) /* Slow Attack Hybrid Mode */ +#define RX2_GAIN_CTRL_SETUP(x) (((x) & 0x3) << 2) /* Rx 2 Gain Control Setup<1:0> */ +#define RX1_GAIN_CTRL_SETUP(x) (((x) & 0x3) << 0) /* Rx 1 Gain Control Setup<1:0> */ +#define RX_GAIN_CTL_MASK 0x03 +#define RX2_GAIN_CTRL_SHIFT 2 +#define RX1_GAIN_CTRL_SHIFT 0 +#define RX_GAIN_CTL_MGC 0x00 +#define RX_GAIN_CTL_AGC_FAST_ATK 0x01 +#define RX_GAIN_CTL_AGC_SLOW_ATK 0x02 +#define RX_GAIN_CTL_AGC_SLOW_ATK_HYBD 0x03 + +/* +* REG_AGC_CONFIG_2 +*/ +#define AGC_SOFT_RESET (1 << 7) /* Soft Reset */ +#define AGC_GAIN_UNLOCK_CTRL (1 << 6) /* Gain Unlock Control */ +#define AGC_USE_FULL_GAIN_TABLE (1 << 3) /* Use Full Gain Table */ +#define DIG_GAIN_EN (1 << 2) /* Enable Digital Gain */ +#define MAN_GAIN_CTRL_RX2 (1 << 1) /* Manual Gain Control Rx 2 */ +#define MAN_GAIN_CTRL_RX1 (1 << 0) /* Manual Gain Control Rx 1 */ + +/* +* REG_AGC_CONFIG_3 +*/ +#define INCDEC_LMT_GAIN (1 << 4) /* Inc/Dec LMT Gain */ +#define USE_AGC_FOR_LMTLPF_GAIN (1 << 3) /* Use AGC for LMT/LPF Gain */ +#define MANUAL_INCR_STEP_SIZE(x) (((x) & 0x7) << 5) /* Manual (CTRL_IN) Incr Gain Step Size<2:0> */ +#define ADC_OVERRANGE_SAMPLE_SIZE(x) (((x) & 0x7) << 0) /* ADC Overrange Sample Size<2:0> */ + +/* +* REG_MAX_LMT_FULL_GAIN +*/ +#define MAXIMUM_FULL_TABLELMT_TABLE_INDEX(x) (((x) & 0x7F) << 0) /* Maximum Full Table/LMT Table Index<6:0> */ + +/* +* REG_PEAK_WAIT_TIME +*/ +#define MANUAL_CTRL_IN_DECR_GAIN_STP_SIZE(x) (((x) & 0x7) << 5) /* Manual (CTRL_IN) Decr Gain Step Size<2:0> */ +#define PEAK_OVERLOAD_WAIT_TIME(x) (((x) & 0x1F) << 0) /* Peak Overload Wait Time<4:0> */ + +/* +* REG_DIGITAL_GAIN +*/ +#define DIG_GAIN_STP_SIZE(x) (((x) & 0x7) << 5) /* Dig Gain Step Size<2:0> */ +#define MAXIMUM_DIGITAL_GAIN(x) (((x) & 0x1F) << 0) /* Maximum Digital Gain<4:0> */ + +/* +* REG_AGC_LOCK_LEVEL +*/ +#define ENABLE_DIG_SAT_OVRG (1 << 7) /* Enable Dig Sat Ovrg */ +#define AGC_LOCK_LEVEL_FAST_AGC_INNER_HIGH_THRESH_SLOW(x) (((x) & 0x7F) << 0) /* AGC Lock Level (Fast)/ AGC Inner High Threshold (Slow) <6:0> */ + +/* +* REG_GAIN_STP_CONFIG1 +*/ +#define LMT_DETECTOR_SETTLING_TIME(x) (((x) & 0x7) << 5) /* LMT Detector Settling Time<2:0> */ +#define DEC_STP_SIZE_FOR_LARGE_LMT_OVERLOAD(x) (((x) & 0x7) << 2) /* Dec Step Size for: Large LMT Overload/ Full Table Case #3 <2:0> */ +#define ADC_NOISE_CORRECTION_FACTOR(x) (((x) & 0x3) << 0) /* ADC Noise Correction Factor<1:0> */ + +/* +* REG_GAIN_STP_CONFIG_2 +*/ +#define DECREMENT_STP_SIZE_FOR_SMALL_LPF_GAIN_CHANGE(x) (((x) & 0x7) << 4) /* Fast Attack Only. Decrement Step Size for: Small LPF Gain Change / Full Table Case #2 <2:0> */ +#define LARGE_LPF_GAIN_STEP(x) (((x) & 0xF) << 0) /* Decrement Step Size for: Large LPF Gain Change / Full Table Case #1<3:0> */ + +/* +* REG_SMALL_LMT_OVERLOAD_THRESH +*/ +#define FORCE_PD_RESET_RX2 (1 << 7) /* Force PD Reset Rx2 */ +#define FORCE_PD_RESET_RX1 (1 << 6) /* Force PD Reset Rx1 */ +#define SMALL_LMT_OVERLOAD_THRESH(x) (((x) & 0x3F) << 0) /* Small LMT Overload Threshold<5:0> */ + +/* +* REG_LARGE_LMT_OVERLOAD_THRESH +*/ +#define LARGE_LMT_OVERLOAD_THRESH(x) (((x) & 0x3F) << 0) /* Large LMT Overload Threshold<5:0> */ + +/* +* REG_RX1_MANUAL_LMT_FULL_GAIN +*/ +#define POWER_MEAS_IN_STATE_5_MSB (1 << 7) /* Power Meas in State 5 <3> */ +#define RX1_MANUAL_FULL_TABLE_LMT_TABLE_GAIN_INDEX(x) (((x) & 0x7F) << 0) /* Rx1 Manual Full table/LMT table Gain Index<6:0> */ +#define RX_FULL_TBL_IDX_MASK RX1_MANUAL_FULL_TABLE_LMT_TABLE_GAIN_INDEX(~0) + +/* +* REG_RX1_MANUAL_LPF_GAIN +*/ +#define POWER_MEAS_IN_STATE_5(x) (((x) & 0x7) << 5) /* Power Meas in State 5<2:0> */ +#define RX1_MANUAL_LPF_GAIN(x) (((x) & 0x1F) << 0) /* Rx1 Manual LPF Gain <4:0> */ +#define RX_LPF_IDX_MASK RX1_MANUAL_LPF_GAIN(~0) + +/* +* REG_RX1_MANUAL_DIGITALFORCED_GAIN +*/ +#define FORCE_RX1_DIGITAL_GAIN (1 << 5) /* Force Rx1 Digital Gain */ +#define RX1_MANUALFORCED_DIGITAL_GAIN(x) (((x) & 0x1F) << 0) /* Rx1 Manual/Forced Digital Gain<4:0> */ +#define RX_DIGITAL_IDX_MASK RX1_MANUALFORCED_DIGITAL_GAIN(~0) +/* +* REG_RX2_MANUAL_LMT_FULL_GAIN +*/ +#define RX2_MANUAL_FULL_TABLE_LMT_TABLE_GAIN_INDEX(x) (((x) & 0x7F) << 0) /* Rx2 Manual Full table/ LMT table Gain Index<6:0> */ + +/* +* REG_RX2_MANUAL_LPF_GAIN +*/ +#define RX2_MANUAL_LPF_GAIN(x) (((x) & 0x1F) << 0) /* Rx2 Manual LPF Gain<4:0> */ + +/* +* REG_RX2_MANUAL_DIGITALFORCED_GAIN +*/ +#define FORCE_RX2_DIGITAL_GAIN (1 << 5) /* Force Rx2 Digital Gain */ +#define RX2_MANUALFORCED_DIGITAL_GAIN(x) (((x) & 0x1F) << 0) /* Rx2 Manual/Forced Digital Gain<4:0> */ + +/* +* REG_FAST_CONFIG_1 +*/ +#define ENABLE_GAIN_INC_AFTER_GAIN_LOCK (1 << 7) /* Enable Gain Inc after Gain Lock */ +#define GOTO_OPT_GAIN_IF_ENERGY_LOST_OR_EN_AGC_HIGH (1 << 6) /* Goto Opt Gain if Energy Lost or EN_AGC High */ +#define GOTO_SET_GAIN_IF_EN_AGC_HIGH (1 << 5) /* Goto Set Gain if EN_AGC High */ +#define GOTO_SET_GAIN_IF_EXIT_RX_STATE (1 << 4) /* Goto Set Gain if Exit Rx State */ +#define DONT_UNLOCK_GAIN_IF_ENERGY_LOST (1 << 3) /* Don't Unlock Gain if Energy Lost */ +#define GOTO_OPTIMIZED_GAIN_IF_EXIT_RX_STATE (1 << 2) /* Goto Optimized Gain if Exit Rx State */ +#define DONT_UNLOCK_GAIN_IF_LG_ADC_OR_LMT_OVRG (1 << 1) /* Don't Unlock Gain If Lg ADC or LMT Ovrg */ +#define ENABLE_INCR_GAIN (1 << 0) /* Enable Incr Gain */ + +/* +* REG_FAST_CONFIG_2_SETTLING_DELAY +*/ +#define USE_LAST_LOCK_LEVEL_FOR_SET_GAIN (1 << 7) /* Use Last Lock Level for Set Gain */ +#define ENABLE_LMT_GAIN_INC_FOR_LOCK_LEVEL (1 << 6) /* Enable LMT Gain Inc for Lock Level */ +#define GOTO_MAX_GAIN_OR_OPT_GAIN_IF_EN_AGC_HIGH (1 << 5) /* Goto Max Gain or Opt Gain if EN_AGC High */ +#define SETTLING_DELAY(x) (((x) & 0x1F) << 0) /* Settling Delay<4:0> */ + +/* +* REG_FAST_ENERGY_LOST_THRESH +*/ +#define POST_LOCK_LEVEL_STP_SIZE_FOR_LPF_TABLE_FULL_TABLE(x) (((x) & 0x3) << 6) /* Post Lock Level Step Size for: LPF Table/ Full Table <1:0> */ +#define ENERGY_LOST_THRESH(x) (((x) & 0x3F) << 0) /* Energy lost threshold<5:0> */ + +/* +* REG_FAST_STRONGER_SIGNAL_THRESH +*/ +#define POST_LOCK_LEVEL_STP_FOR_LMT_TABLE(x) (((x) & 0x3) << 6) /* Post Lock Level Step for LMT Table <1:0> */ +#define STRONGER_SIGNAL_THRESH(x) (((x) & 0x3F) << 0) /* Stronger Signal Threshold<5:0> */ + +/* +* REG_FAST_LOW_POWER_THRESH +*/ +#define DONT_UNLOCK_GAIN_IF_ADC_OVRG (1 << 7) /* Don't unlock gain if ADC Ovrg */ +#define LOW_POWER_THRESH(x) (((x) & 0x7F) << 0) /* Low Power Threshold<6:0> */ + +/* +* REG_FAST_STRONG_SIGNAL_FREEZE +*/ +#define DONT_UNLOCK_GAIN_IF_STRONGER_SIGNAL (1 << 7) /* Don't unlock gain if Stronger Signal */ + +/* +* REG_FAST_FINAL_OVER_RANGE_AND_OPT_GAIN +*/ +#define FINAL_OVER_RANGE_COUNT(x) (((x) & 0x7) << 5) /* Final Over Range Count<2:0> */ +#define OPTIMIZE_GAIN_OFFSET(x) (((x) & 0xF) << 0) /* Optimize Gain Offset<3:0> */ + +/* +* REG_FAST_ENERGY_DETECT_COUNT +*/ +#define INCREMENT_GAIN_STP_LPFLMT(x) (((x) & 0x7) << 5) /* Increment Gain Step (LPF/LMT)<2:0> */ +#define ENERGY_DETECT_COUNT(x) (((x) & 0x1F) << 0) /* Energy Detect count<4:0> */ + +/* +* REG_FAST_AGCLL_UPPER_LIMIT +*/ +#define AGCLL_MAX_INCREASE(x) (((x) & 0x3F) << 0) /* AGCLL Max Increase<5:0> */ + +/* +* REG_FAST_GAIN_LOCK_EXIT_COUNT +*/ +#define GAIN_LOCK_EXIT_COUNT(x) (((x) & 0x3F) << 0) /* Gain Lock Exit Count<5:0> */ + +/* +* REG_FAST_INITIAL_LMT_GAIN_LIMIT +*/ +#define INITIAL_LMT_GAIN_LIMIT(x) (((x) & 0x7F) << 0) /* Initial LMT Gain Limit<6:0> */ + +/* +* REG_AGC_INNER_LOW_THRESH +*/ +#define PREVENT_GAIN_INC (1 << 7) /* Prevent Gain Inc */ +#define AGC_INNER_LOW_THRESH(x) (((x) & 0x7F) << 0) /* AGC Inner Low Threshold<6:0> */ + +/* +* REG_LMT_OVERLOAD_COUNTERS +*/ +#define LARGE_LMT_OVERLOAD_EXED_COUNTER(x) (((x) & 0xF) << 4) /* Large LMT Overload Exceeded Counter<3:0> */ +#define SMALL_LMT_OVERLOAD_EXED_COUNTER(x) (((x) & 0xF) << 0) /* Small LMT Overload Exceeded Counter<3:0> */ + +/* +* REG_ADC_OVERLOAD_COUNTERS +*/ +#define LARGE_ADC_OVERLOAD_EXED_COUNTER(x) (((x) & 0xF) << 4) /* Large ADC Overload Exceeded Counter<3:0> */ +#define SMALL_ADC_OVERLOAD_EXED_COUNTER(x) (((x) & 0xF) << 0) /* Small ADC Overload Exceeded Counter<3:0> */ + +/* +* REG_GAIN_STP1 +*/ +#define IMMED_GAIN_CHANGE_IF_LG_LMT_OVERLOAD (1 << 7) /* Immed. Gain Change if Lg LMT Overload */ +#define IMMED_GAIN_CHANGE_IF_LG_ADC_OVERLOAD (1 << 3) /* Immed. Gain Change if Lg ADC Overload */ +#define AGC_INNER_HIGH_THRESH_EXED_STP_SIZE(x) (((x) & 0x7) << 4) /* AGC Inner High Threshold Exceeded Step Size<2:0> */ +#define AGC_INNER_LOW_THRESH_EXED_STP_SIZE(x) (((x) & 0x7) << 0) /* AGC Inner Low Threshold Exceeded Step Size<2:0> */ + +/* +* REG_DIGITAL_SAT_COUNTER +*/ +#define DOUBLE_GAIN_COUNTER (1 << 5) /* Double Gain Counter */ +#define ENABLE_SYNC_FOR_GAIN_COUNTER (1 << 4) /* Enable Sync for Gain Counter */ +#define DIG_SATURATION_EXED_COUNTER(x) (((x) & 0xF) << 0) /* Dig Saturation Exceeded Counter<3:0> */ + +/* +* REG_OUTER_POWER_THRESHS +*/ +#define AGC_OUTER_HIGH_THRESH(x) (((x) & 0xF) << 4) /* AGC Outer High Threshold<3:0> */ +#define AGC_OUTER_LOW_THRESH(x) (((x) & 0xF) << 0) /* AGC Outer Low Threshold<3:0> */ + +/* +* REG_GAIN_STP_2 +*/ +#define AGC_OUTER_HIGH_THRESH_EXED_STP_SIZE(x) (((x) & 0xF) << 4) /* AGC outer High Threshold Exceeded Step Size<3:0> */ +#define AGC_OUTER_LOW_THRESH_EXED_STP_SIZE(x) (((x) & 0xF) << 0) /* AGC Outer Low Threshold Exceeded Step Size<3:0> */ + +/* +* REG_EXT_LNA_HIGH_GAIN +*/ +#define EXT_LNA_HIGH_GAIN(x) (((x) & 0x3F) << 0) /* Ext LNA High Gain<5:0> */ + +/* +* REG_EXT_LNA_LOW_GAIN +*/ +#define EXT_LNA_LOW_GAIN(x) (((x) & 0x3F) << 0) /* Ext LNA Low Gain<5:0> */ + +/* +* REG_GAIN_TABLE_ADDRESS +*/ +#define GAIN_TABLE_ADDRESS(x) (((x) & 0x7F) << 0) /* Gain Table Address<6:0> */ + +/* +* REG_GAIN_TABLE_WRITE_DATA1 +*/ +#define EXT_LNA_CTRL (1 << 7) /* Ext LNA Ctrl */ +#define LNA_GAIN(x) (((x) & 0x3) << 5) /* LNA Gain <1:0> */ +#define MIXER_GM_GAIN(x) (((x) & 0x1F) << 0) /* Mixer Gm Gain <4:0> */ + +/* +* REG_GAIN_TABLE_WRITE_DATA2 +*/ +#define TIA_GAIN (1 << 5) /* TIA Gain */ +#define LPF_GAIN(x) (((x) & 0x1F) << 0) /* LPF Gain <4:0> */ + +/* +* REG_GAIN_TABLE_WRITE_DATA_3 +*/ +#define RF_DC_CAL (1 << 5) /* RF DC Cal */ +#define DIGITAL_GAIN(x) (((x) & 0x1F) << 0) /* Digital Gain <4:0> */ + +/* +* REG_GAIN_TABLE_READ_DATA_1 +*/ +#define TO_LNA_GAIN(x) (((x) >> 5) & 0x3) /* LNA Gain <1:0> */ +#define TO_MIXER_GM_GAIN(x) (((x) >> 0) & 0x1F) /* Mixer Gm Gain <4:0> */ + +/* +* REG_GAIN_TABLE_READ_DATA_2 +*/ +#define TO_LPF_GAIN(x) (((x) >> 0) & 0x1F) /* LPF Gain <4:0> */ + +/* +* REG_GAIN_TABLE_READ_DATA_3 +*/ +#define TO_DIGITAL_GAIN(x) (((x) >> 0) & 0x1F) /* Digital Gain <4:0> */ + +/* +* REG_GAIN_TABLE_CONFIG +*/ +#define WRITE_GAIN_TABLE (1 << 2) /* Write Gain Table */ +#define START_GAIN_TABLE_CLOCK (1 << 1) /* Start Gain Table Clock */ +#define RECEIVER_SELECT(x) (((x) & 0x3) << 3) /* Receiver Select<1:0> */ +#define GT_RX1 1 +#define GT_RX2 2 + + +/* +* REG_GM_SUB_TABLE_GAIN_WRITE +*/ +#define GM_SUB_TABLE_GAIN_WRITE(x) (((x) & 0x7F) << 0) /* Gm Sub Table Gain Word Write<6:0> */ + +/* +* REG_GM_SUB_TABLE_BIAS_WRITE +*/ +#define GM_SUB_TABLE_BIAS_WRITE(x) (((x) & 0x1F) << 0) /* Gm Sub Table Bias Word Write<4:0> */ + +/* +* REG_GM_SUB_TABLE_CTRL_WRITE +*/ +#define GM_SUB_TABLE_CTRL_WRITE(x) (((x) & 0x3F) << 0) /* Gm Sub Table Control Word Write<5:0> */ + +/* +* REG_GM_SUB_TABLE_GAIN_READ +*/ +#define GM_SUB_TABLE_GAIN_READ(x) (((x) & 0x7F) << 0) /* Gm Sub Table Gain Word Read<6:0> */ + +/* +* REG_GM_SUB_TABLE_BIAS_READ +*/ +#define GM_SUB_TABLE_BIAS_READ(x) (((x) & 0x1F) << 0) /* Gm Sub Table Bias Word Read<4:0> */ + +/* +* REG_GM_SUB_TABLE_CTRL_READ +*/ +#define GM_SUB_TABLE_CTRL_READ(x) (((x) & 0x3F) << 0) /* Gm Sub Table Control Word Read<5:0> */ + +/* +* REG_GM_SUB_TABLE_CONFIG +*/ +#define WRITE_GM_SUB_TABLE (1 << 2) /* Write Gm Sub Table */ +#define START_GM_SUB_TABLE_CLOCK (1 << 1) /* Start Gm Sub Table Clock */ + +/* +* REG_GAIN_DIFF_WORDERROR_WRITE +*/ +#define CALIB_TABLE_GAIN_DIFFERROR_WORD(x) (((x) & 0x3F) << 0) /* Calib Table Gain Diff/Error Word<5:0> */ + +/* +* REG_GAIN_ERROR_READ +*/ +#define CALIB_TABLE_GAIN_ERROR(x) (((x) & 0x1F) << 0) /* Calib Table Gain Error<4:0> */ + +/* +* REG_CONFIG +*/ +#define READ_SELECT (1 << 4) /* Read Select */ +#define WRITE_MIXER_ERROR_TABLE (1 << 3) /* Write Mixer Error Table */ +#define WRITE_LNA_ERROR_TABLE (1 << 2) /* Write LNA Error Table */ +#define WRITE_LNA_GAIN_DIFF (1 << 1) /* Write LNA Gain Diff */ +#define START_CALIB_TABLE_CLOCK (1 << 0) /* Start Calib Table Clock */ +#define CALIB_TABLE_SELECT(x) (((x) & 0x3) << 5) /* Calib Table Select<1:0> */ + +/* +* REG_LNA_GAIN_DIFF_READ_BACK +*/ +#define LNA_CALIB_TABLE_GAIN_DIFFERENCE_WORD(x) (((x) & 0x3F) << 0) /* LNA Calib Table Gain Difference Word<5:0> */ + +/* +* REG_MAX_MIXER_CALIBRATION_GAIN_INDEX +*/ +#define MAX_MIXER_CALIBRATION_GAIN_INDEX(x) (((x) & 0x1F) << 0) /* Max Mixer Calibration Gain Index<4:0> */ + +/* +* REG_SETTLE_TIME +*/ +#define ENABLE_DIG_GAIN_CORR (1 << 7) /* Enable Dig Gain Corr */ +#define FORCE_TEMP_SENSOR_FOR_CAL (1 << 6) /* Force Temp Sensor for Cal */ +#define SETTLE_TIME(x) (((x) & 0x3F) << 0) /* Settle Time<5:0> */ + +/* +* REG_MEASURE_DURATION +*/ +#define GAIN_CAL_MEAS_DURATION(x) (((x) & 0xF) << 0) /* Gain Cal Meas Duration<3:0> */ + +/* +* REG_MEASURE_DURATION_01 +*/ +#define MEASUREMENT_DURATION_1(x) (((x) & 0xF) << 4) /* Measurement duration 1 <3:0> */ +#define MEASUREMENT_DURATION_0(x) (((x) & 0xF) << 0) /* Measurement duration 0 <3:0> */ + +/* +* REG_MEASURE_DURATION_23 +*/ +#define MEASUREMENT_DURATION_3(x) (((x) & 0xF) << 4) /* Measurement duration 3 <3:0> */ +#define MEASUREMENT_DURATION_2(x) (((x) & 0xF) << 0) /* Measurement duration 2 <3:0> */ + +/* +* REG_RSSI_CONFIG +*/ +#define START_RSSI_MEAS (1 << 5) /* Start RSSI Meas (Mode 4) */ +#define ENABLE_ADC_POWER_MEAS (1 << 1) /* Enable ADC Power Meas. */ +#define DEFAULT_RSSI_MEAS_MODE (1 << 0) /* Default RSSI Meas Mode */ +#define RFIR_FOR_RSSI_MEASUREMENT(x) (((x) & 0x3) << 6) /* RFIR for RSSI measurement<1:0> */ +#define RSSI_MODE_SELECT(x) (((x) & 0x7) << 2) /* RSSI Mode Select<2:0> */ + +/* +* REG_ADC_MEASURE_DURATION_01 +*/ +#define ADC_POWER_MEASUREMENT_DURATION_1(x) (((x) & 0xF) << 4) /* ADC Power Measurement Duration 1<3:0> */ +#define ADC_POWER_MEASUREMENT_DURATION_0(x) (((x) & 0xF) << 0) /* ADC Power Measurement Duration 0 <3:0> */ + +/* +* REG_DEC_POWER_MEASURE_DURATION_0 +*/ +#define USE_HB3_OUT_FOR_ADC_PWR_MEAS (1 << 7) /* Use HB3 Out for ADC Pwr Meas */ +#define USE_HB1_OUT_FOR_DEC_PWR_MEAS (1 << 6) /* Use HB1 Out for Dec pwr Meas */ +#define ENABLE_DEC_PWR_MEAS (1 << 5) /* Enable Dec Pwr Meas */ +#define DEFAULT_MODE_ADC_POWER (1 << 4) /* Default Mode ADC Power */ +#define DEC_POWER_MEASUREMENT_DURATION(x) (((x) & 0xF) << 0) /* Dec Power Measurement Duration <3:0> */ + +/* +* REG_LNA_GAIN +*/ +#define DB_GAIN_READBACK_CHANNEL (1 << 0) /* dB Gain Read-back Channel */ +#define MAX_LNA_GAIN(x) (((x) & 0x7F) << 1) /* Max LNA Gain<6:0> */ + +/* +* REG_RX_QUAD_CAL_LEVEL +*/ +#define RX_QUAD_CAL_LEVEL(x) (((x) & 0xF) << 0) /* Rx Quad Cal Level <3 :0> */ + +/* +* REG_CALIBRATION_CONFIG_1 +*/ +#define ENABLE_PHASE_CORR (1 << 7) /* Enable Phase Corr */ +#define ENABLE_GAIN_CORR (1 << 6) /* Enable Gain Corr */ +#define USE_SETTLE_COUNT_FOR_DC_CAL_WAIT (1 << 5) /* Use Settle Count for DC Cal Wait */ +#define FIXED_DC_CAL_WAIT_TIME (1 << 4) /* Fixed DC Cal Wait Time */ +#define FREE_RUN_MODE (1 << 3) /* Free Run Mode */ +#define ENABLE_CORR_WORD_DECIMATION (1 << 2) /* Enable Corr Word Decimation */ +#define ENABLE_TRACKING_MODE_CH2 (1 << 1) /* Enable Tracking Mode CH2 */ +#define ENABLE_TRACKING_MODE_CH1 (1 << 0) /* Enable Tracking Mode CH1 */ + +/* +* REG_CALIBRATION_CONFIG_2 +*/ +#define SOFT_RESET (1 << 7) /* Soft Reset */ +#define CALIBRATION_CONFIG2_DFLT (0x3 << 5) /* Must be 2'b11 */ +#define K_EXP_PHASE(x) (((x) & 0x1F) << 0) /* K exp Phase<4:0> */ + +/* +* REG_CALIBRATION_CONFIG_3 +*/ +#define PREVENT_POS_LOOP_GAIN (1 << 7) /* Prevent Pos Loop Gain */ +#define K_EXP_AMPLITUDE(x) (((x) & 0x1F) << 0) /* K exp Amplitude<4:0> */ + +/* +* REG_RX_QUAD_GAIN1 +*/ +#define RX_FULL_TABLELMT_TABLE_GAIN(x) (((x) & 0x7F) << 0) /* Rx Full table/LMT table gain<6:0> */ + +/* +* REG_RX_QUAD_GAIN2 +*/ +#define CORRECTION_WORD_DECIMATION_M(x) (((x) & 0x7) << 5) /* Correction Word Decimation M<2:0> */ +#define RX_LPF_GAIN(x) (((x) & 0x1F) << 0) /* Rx LPF gain<4:0> */ + +/* +* REG_RX1_INPUT_A_OFFSETS +*/ +#define RX1_INPUT_A_I_DC_OFFSET_LSB(x) (((x) & 0x3F) << 2) /* Rx1 Input A "I" DC Offset<5:0> */ +#define RX1_INPUT_A_Q_DC_OFFSET(x) (((x) & 0x3) << 0) /* Rx1 Input A "Q" DC Offset<9:8> */ + +/* +* REG_INPUT_A_OFFSETS_1 +*/ +#define RX2_INPUT_A_Q_DC_OFFSET_LSB(x) (((x) & 0xF) << 4) /* Rx2 Input A "Q" DC Offset<3:0> */ +#define RX1_INPUT_A_I_DC_OFFSET_MSB(x) (((x) & 0xF) << 0) /* Rx1 Input A "I" DC Offset<9:6> */ + +/* +* REG_RX2_INPUT_A_OFFSETS +*/ +#define RX2_INPUT_A_I_DC_OFFSET(x) (((x) & 0x3) << 6) /* Rx2 Input A "I" DC Offset<1:0> */ +#define RX2_INPUT_A_Q_DC_OFFSET_MSB(x) (((x) & 0x3F) << 0) /* Rx2 Input A "Q" DC Offset<9:4> */ + +/* +* REG_RX1_INPUT_BC_OFFSETS +*/ +#define RX1_INPUT_BC_I_DC_OFFSET_LSB(x) (((x) & 0x3F) << 2) /* Rx1 Input B&C "I" DC Offset<5:0> */ +#define RX1_INPUT_BC_Q_DC_OFFSET(x) (((x) & 0x3) << 0) /* Rx1 Input B&C "Q" DC Offset<9:8> */ + +/* +* REG_INPUT_BC_OFFSETS_1 +*/ +#define RX2_INPUT_BC_Q_DC_OFFSET_LSB(x) (((x) & 0xF) << 4) /* Rx2 Input B&C "Q" DC Offset<3:0> */ +#define RX1_INPUT_BC_I_DC_OFFSET_MSB(x) (((x) & 0xF) << 0) /* Rx1 Input B&C "I" DC Offset<9:6> */ + +/* +* REG_RX2_INPUT_BC_OFFSETS +*/ +#define RX2_INPUT_BC_I_DC_OFFSET(x) (((x) & 0x3) << 6) /* Rx2 Input B&C "I" DC Offset<1:0> */ +#define RX2_INPUT_BC_Q_DC_OFFSET_MSB(x) (((x) & 0x3F) << 0) /* Rx2 Input B&C "Q" DC Offset<9:4> */ + +/* +* REG_FORCE_BITS +*/ +#define RX2_INPUT_BC_FORCE_OFFSET (1 << 7) /* Rx2 Input B&C Force offset */ +#define RX1_INPUT_BC_FORCE_OFFSET (1 << 6) /* Rx1 Input B&C Force offset */ +#define RX2_INPUT_BC_FORCE_PHGAIN (1 << 5) /* Rx2 Input B&C Force Ph/Gain */ +#define RX1_INPUT_BC_FORCE_PHGAIN (1 << 4) /* Rx1 Input B&C Force Ph/Gain */ +#define RX2_INPUT_A_FORCE_OFFSET (1 << 3) /* Rx2 Input A Force offset */ +#define RX1_INPUT_A_FORCE_OFFSET (1 << 2) /* Rx1 Input A Force offset */ +#define RX2_INPUT_A_FORCE_PHGAIN (1 << 1) /* Rx2 Input A Force Ph/Gain */ +#define RX1_INPUT_A_FORCE_PHGAIN (1 << 0) /* Rx1 Input A Force Ph/Gain */ + +/* +* REG_RF_DC_OFFSET_CONFIG_1 +*/ +#define DAC_FS(x) (((x) & 0x3) << 4) /* DAC FS<1:0> */ +#define RF_DC_CALIBRATION_COUNT(x) (((x) & 0xF) << 0) /* RF DC Calibration Count<3:0> */ + +/* +* REG_RF_DC_OFFSET_ATTEN +*/ +#define RF_DC_OFFSET_TABLE_UPDATE_COUNT(x) (((x) & 0x7) << 5) /* RF DC Offset Table Update Count<2:0> */ +#define RF_DC_OFFSET_ATTEN(x) (((x) & 0x1F) << 0) /* RF DC Offset Attenuation<4:0> */ + +/* +* REG_INVERT_BITS +*/ +#define INVERT_RX2_RF_DC_CGIN_WORD (1 << 7) /* Invert Rx2 RF DC CGin Word */ +#define INVERT_RX1_RF_DC_CGIN_WORD (1 << 6) /* Invert Rx1 RF DC CGin Word */ +#define INVERT_RX2_RF_DC_CGOUT_WORD (1 << 5) /* Invert Rx2 RF DC CGout Word */ +#define INVERT_RX1_RF_DC_CGOUT_WORD (1 << 4) /* Invert Rx1 RF DC CGout Word */ + +/* +* REG_DC_OFFSET_CONFIG2 +*/ +#define USE_WAIT_COUNTER_FOR_RF_DC_INIT_CAL (1 << 7) /* Use Wait Counter for RF DC Init Cal */ +#define ENABLE_FAST_SETTLE_MODE (1 << 6) /* Enable Fast Settle Mode */ +#define ENABLE_BB_DC_OFFSET_TRACKING (1 << 5) /* Enable BB DC Offset Tracking */ +#define RESET_ACC_ON_GAIN_CHANGE (1 << 4) /* Reset Acc on Gain Change */ +#define ENABLE_RF_OFFSET_TRACKING (1 << 3) /* Enable RF Offset Tracking */ +#define DC_OFFSET_UPDATE(x) (((x) & 0x7) << 0) /* DC Offset Update<2:0> */ + +/* +* REG_RF_CAL_GAIN_INDEX +*/ +#define RF_MINIMUM_CALIBRATION_GAIN_INDEX(x) (((x) & 0x7F) << 0) /* RF Minimum Calibration Gain Index<6:0> */ + +/* +* REG_SOI_THRESH +*/ +#define RF_SOI_THRESH(x) (((x) & 0x7F) << 0) /* RF SOI Threshold<6:0> */ + +/* +* REG_BB_DC_OFFSET_SHIFT +*/ +#define INCREASE_COUNT_DURATION (1 << 7) /* Increase Count Duration */ +#define BB_TRACKING_DECIMATE(x) (((x) & 0x3) << 5) /* BB Tracking Decimate<1:0> */ +#define BB_DC_M_SHIFT(x) (((x) & 0x1F) << 0) /* BB DC M Shift<4:0> */ + +/* +* REG_BB_DC_OFFSET_FAST_SETTLE_SHIFT +*/ +#define READ_BACK_CH_SEL (1 << 7) /* Read Back CH Sel */ +#define UPDATE_TRACKING_WORD (1 << 6) /* Update Tracking Word */ +#define FORCE_RX_NULL (1 << 5) /* Force Rx Null */ +#define BB_DC_TRACKING_FAST_SETTLE_M_SHIFT(x) (((x) & 0x1F) << 0) /* BB DC Tracking Fast Settle M Shift<4:0> */ + +/* +* REG_BB_DC_OFFSET_ATTEN +*/ +#define BB_DC_OFFSET_ATTEN(x) (((x) & 0xF) << 0) /* BB DC Offset Atten<3:0> */ + +/* +* REG_RX1_BB_DC_WORD_I_MSB +*/ +#define RX1_BB_DC_OFFSET_CORRECTION_WORD_I(x) (((x) & 0x7F) << 0) /* RX1 BB DC Offset Correction word I<14:8> */ + +/* +* REG_RX1_BB_DC_WORD_Q_MSB +*/ +#define RX1_BB_DC_OFFSET_CORRECTION_WORD_Q(x) (((x) & 0x7F) << 0) /* RX1 BB DC Offset Correction word Q<14:8> */ + +/* +* REG_RX2_BB_DC_WORD_I_MSB +*/ +#define RX2_BB_DC_OFFSET_CORRECTION_WORD_I(x) (((x) & 0x7F) << 0) /* RX2 BB DC Offset Correction word I<14:8> */ + +/* +* REG_RX2_BB_DC_WORD_Q_MSB +*/ +#define RX2_BB_DC_OFFSET_CORRECTION_WORD_Q(x) (((x) & 0x7F) << 0) /* RX2 BB DC Offset Correction word Q<14:8> */ + +/* +* REG_BB_TRACK_CORR_WORD_I_MSB +*/ +#define RX1RX2_BB_DC_OFFSET_TRACKING_CORRECTION_WORD_I(x) (((x) & 0x7F) << 0) /* RX1/RX2 BB DC Offset Tracking correction word I<14:8> */ + +/* +* REG_BB_TRACK_CORR_WORD_Q_MSB +*/ +#define RX1RX2_BB_DC_OFFSET_TRACKING_CORRECTION_WORD_Q(x) (((x) & 0x7F) << 0) /* RX1/RX2 BB DC Offset Tracking correction word Q<14:8> */ + +/* +* REG_SYMBOL_LSB +*/ +#define RX2_RSSI_SYMBOL (1 << 1) /* Rx2 RSSI symbol <0> */ +#define RX1_RSSI_SYMBOL (1 << 0) /* Rx1 RSSI symbol <0> */ + +/* +* REG_PREAMBLE_LSB +*/ +#define RX2_RSSI_PREAMBLE (1 << 1) /* Rx2 RSSI preamble <0> */ +#define RX1_RSSI_PREAMBLE (1 << 0) /* Rx1 RSSI preamble <0> */ + +/* +* REG_RX1_RSSI_SYMBOL, REG_RX1_RSSI_PREAMBLE, +* REG_RX2_RSSI_SYMBOL, REG_RX2_RSSI_PREAMBLE +*/ +#define RSSI_LSB_SHIFT 1 +#define RSSI_LSB_MASK1 0x01 +#define RSSI_LSB_MASK2 0x02 + +/* +* REG_RX_PATH_GAIN_LSB +*/ +#define RX_PATH_GAIN (1 << 0) /* Rx Path Gain<0> */ + +/* +* REG_RX_DIFF_LNA_FORCE +*/ +#define FORCE_RX2_LNA_GAIN (1 << 7) /* Force Rx2 LNA Gain */ +#define RX2_LNA_BYPASS (1 << 6) /* Rx2 LNA Bypass */ +#define FORCE_RX1_LNA_GAIN (1 << 3) /* Force Rx1 LNA Gain */ +#define RX1_LNA_BYPASS (1 << 2) /* Rx1 LNA Bypass */ +#define RX2_LNA_GAIN(x) (((x) & 0x3) << 4) /* Rx2 LNA Gain<1:0> */ +#define RX1_LNA_GAIN(x) (((x) & 0x3) << 0) /* Rx1 LNA Gain<1:0> */ + +/* +* REG_RX_LNA_BIAS_COARSE +*/ +#define RX_LNA_BIAS_COARSE(x) (((x) & 0xF) << 0) /* Rx LNA Bias Coarse<3:0> */ + +/* +* REG_RX_LNA_BIAS_FINE_0 +*/ +#define RX_LNA_PCASCODE_BIAS(x) (((x) & 0x7) << 5) /* Rx LNA p-Cascode Bias<2:0> */ +#define RX_LNA_BIAS(x) (((x) & 0x1F) << 0) /* Rx LNA Bias<4:0> */ + +/* +* REG_RX_LNA_BIAS_FINE_1 +*/ +#define RX_LNA_P_CASCODE_BIAS_FINE(x) (((x) & 0x3) << 0) /* Rx LNA p- Cascode Bias Fine<4:3> */ + +/* +* REG_RX_MIX_GM_CONFIG +*/ +#define RX_MIX_GM_CM_OUT(x) (((x) & 0x7) << 5) /* Rx Mix Gm CM Out<2:0> */ +#define RX_MIX_GM_PLOAD(x) (((x) & 0x3) << 0) /* Rx Mix Gm pload <1:0> */ + +/* +* REG_RX1_MIX_GM_FORCE +*/ +#define FORCE_RX1_MIX_GM (1 << 6) /* Force Rx1 Mix Gm */ +#define RX1_MIX_GM_GAIN(x) (((x) & 0x3F) << 0) /* Rx1 Mix Gm Gain<5:0> */ + +/* +* REG_RX1_MIX_GM_BIAS_FORCE +*/ +#define RX1_MIX_GM_BIAS(x) (((x) & 0x1F) << 0) /* Rx1 Mix Gm Bias<4:0> */ + +/* +* REG_RX2_MIX_GM_FORCE +*/ +#define FORCE_RX2_MIX_GM (1 << 6) /* Force Rx2 Mix Gm */ +#define RX2_MIX_GM_GAIN(x) (((x) & 0x3F) << 0) /* Rx2 Mix Gm Gain<5:0> */ + +/* +* REG_RX2_MIX_GM_BIAS_FORCE +*/ +#define RX2_MIX_GM_BIAS(x) (((x) & 0x1F) << 0) /* Rx2 Mix Gm Bias<4:0> */ + +/* +* REG_INPUT_A_MSBS +*/ +#define INPUT_A_RX1_Q(x) (((x) & 0x3) << 6) /* Input A RX1 Q<9:8> */ +#define INPUT_A_RX1_I(x) (((x) & 0x3) << 4) /* Input A RX1 I<9:8> */ +#define INPUT_A_RX2_I(x) (((x) & 0x3) << 2) /* Input A RX2 I<9:8> */ +#define INPUT_A_RX2_Q(x) (((x) & 0x3) << 0) /* Input A RX2 Q<9:8> */ + +/* +* REG_INPUTS_BC_MSBS +*/ +#define INPUTS_BC_RX1_Q(x) (((x) & 0x3) << 6) /* Inputs B&C RX1 Q<9:8> */ +#define INPUTS_BC_RX1_I(x) (((x) & 0x3) << 4) /* Inputs B&C RX1 I<9:8> */ +#define INPUTS_BC_RX2_I(x) (((x) & 0x3) << 2) /* Inputs B&C RX2 I<9:8> */ +#define INPUTS_BC_RX2_Q(x) (((x) & 0x3) << 0) /* Inputs B&C RX2 Q<9:8> */ + +/* +* REG_FORCE_OS_DAC +*/ +#define FORCE_CGIN_DAC (1 << 2) /* Force CGin DAC */ + +/* +* REG_RX_MIX_LO_CM +*/ +#define RX_MIX_LO_CM(x) (((x) & 0x3F) << 0) /* Rx Mix LO CM<5:0> */ + +/* +* REG_RX_CGB_SEG_ENABLE +*/ +#define RX_CGB_SEG_ENABLE(x) (((x) & 0x3F) << 0) /* Rx CGB Seg Enable<5:0> */ + +/* +* REG_RX_MIX_INPUTBIAS +*/ +#define RX_CGB_INPUT_CM_SEL(x) (((x) & 0x3) << 4) /* Rx CGB Input CM Sel<1:0> */ +#define RX_CGB_BIAS(x) (((x) & 0xF) << 0) /* Rx CGB Bias<3:0> */ + +/* +* REG_RX_TIA_CONFIG +*/ +#define TIA2_OVERRIDE_C (1 << 3) /* TIA2 Override C */ +#define TIA2_OVERRIDE_R (1 << 2) /* TIA2 Override R */ +#define TIA1_OVERRIDE_C (1 << 1) /* TIA1 Override C */ +#define TIA1_OVERRIDE_R (1 << 0) /* TIA1 Override R */ +#define TIA_SEL_CC(x) (((x) & 0x7) << 5) /* TIA Sel CC<2:0> */ + +/* +* REG_TIA1_C_LSB +*/ +#define TIA1_RF(x) (((x) & 0x3) << 6) /* TIA1 RF<1:0> */ +#define TIA1_C_LSB(x) (((x) & 0x3F) << 0) /* TIA1 C LSB<5:0> */ + +/* +* REG_TIA1_C_MSB +*/ +#define TIA1_C_MSB(x) (((x) & 0x7F) << 0) /* TIA1 C MSB<6:0> */ + +/* +* REG_TIA2_C_LSB +*/ +#define TIA2_RF(x) (((x) & 0x3) << 6) /* TIA2 RF<1:0> */ +#define TIA2_C_LSB(x) (((x) & 0x3F) << 0) /* TIA2 C LSB<5:0> */ + +/* +* REG_TIA2_C_MSB +*/ +#define TIA2_C_MSB(x) (((x) & 0x7F) << 0) /* TIA2 C MSB<6:0> */ + +/* +* REG_RX1_BBF_R1A +*/ +#define FORCE_RX1_RESISTORS (1 << 7) /* Force Rx1 Resistors */ +#define RX1_BBF_R1A(x) (((x) & 0x3F) << 0) /* Rx1 BBF R1A<5:0> */ + +/* +* REG_RX2_BBF_R1A +*/ +#define FORCE_RX2_RESISTORS (1 << 7) /* Force Rx2 Resistors */ +#define RX2_BBF_R1A(x) (((x) & 0x3F) << 0) /* Rx2 BBF R1A<5:0> */ + +/* +* REG_RX1_TUNE_CTRL +*/ +#define RX1_TUNE_RESAMPLE_PHASE (1 << 2) /* Rx1 Tune Resample Phase */ +#define RX1_TUNE_RESAMPLE (1 << 1) /* Rx1 Tune Resample */ +#define RX1_PD_TUNE (1 << 0) /* Rx1 PD Tune */ + +/* +* REG_RX2_TUNE_CTRL +*/ +#define RX2_TUNE_RESAMPLE_PHASE (1 << 2) /* Rx2 Tune Resam ple Phase */ +#define RX2_TUNE_RESAMPLE (1 << 1) /* Rx2 Tune Resample */ +#define RX2_PD_TUNE (1 << 0) /* Rx2 PD Tune */ + +/* +* REG_RX_BBF_R2346 +*/ +#define TUNE_OVERRIDE (1 << 7) /* Tune Override */ +#define RX_BBF_R2346(x) (((x) & 0x7) << 0) /* Rx BBF R2346<2:0> */ + +/* +* REG_RX_BBF_C1_MSB +*/ +#define RX_BBF_C1_MSB(x) (((x) & 0x3F) << 0) /* Rx BBF C1 MSB<5:0> */ + +/* +* REG_RX_BBF_C1_LSB +*/ +#define RX_BBF_C1_LSB(x) (((x) & 0x7F) << 0) /* Rx BBF C1 LSB<6:0> */ + +/* +* REG_RX_BBF_C2_MSB +*/ +#define RX_BBF_C2_MSB(x) (((x) & 0x3F) << 0) /* Rx BBF C2 MSB<5:0> */ + +/* +* REG_RX_BBF_C2_LSB +*/ +#define RX_BBF_C2_LSB(x) (((x) & 0x7F) << 0) /* Rx BBF C2 LSB<6:0> */ + +/* +* REG_RX_BBF_C3_MSB +*/ +#define RX_BBF_C3_MSB(x) (((x) & 0x3F) << 0) /* Rx BBF C3 MSB<5:0> */ + +/* +* REG_RX_BBF_C3_LSB +*/ +#define RX_BBF_C3_LSB(x) (((x) & 0x7F) << 0) /* Rx BBF C3 LSB<6:0> */ + +/* +* REG_RX_BBF_CC1_CTR +*/ +#define RX_BBF_CC1_CTR(x) (((x) & 0x7F) << 0) /* Rx BBF CC1 Ctr<6:0> */ + +/* +* REG_RX_BBF_POW_RZ_BYTE0 +*/ +#define MUST_BE_ZERO (1 << 7) /* Must be zero */ +#define RX1_BBF_POW_CTR(x) (((x) & 0x3) << 5) /* Rx1 BBF Pow Ctr<1:0> */ +#define RX_BBF_RZ1_CTR(x) (((x) & 0x3) << 3) /* Rx BBF Rz1 Ctr<1:0> */ + +/* +* REG_RX_BBF_CC2_CTR +*/ +#define RX_BBF_CC2_CTR(x) (((x) & 0x7F) << 0) /* Rx BBF CC2 Ctr<6:0> */ + +/* +* REG_RX_BBF_POW_RZ_BYTE1 +*/ +#define RX_BBF_POW3_CTR(x) (((x) & 0x3) << 6) /* Rx BBF Pow3 Ctr<1:0> */ +#define RX_BBF_RZ3_CTR(x) (((x) & 0x3) << 4) /* Rx BBF RZ3 Ctr<1:0> */ +#define RX_BBF_POW2_CTR(x) (((x) & 0x3) << 2) /* Rx BBF Pow2 Ctr<1:0> */ +#define RX_BBF_RZ2_CTR(x) (((x) & 0x3) << 0) /* Rx BBF Rz2 Ctr<1:0> */ + +/* +* REG_RX_BBF_CC3_CTR +*/ +#define RX_BBF_CC3_CTR(x) (((x) & 0x7F) << 0) /* Rx BBF CC3 Ctr<6:0> */ + +/* +* REG_RX_BBF_TUNE +*/ +#define RXBBF_BYPASS_BIAS_R (1 << 7) /* RxBBF Bypass Bias R */ +#define RX_BBF_R5_TUNE (1 << 4) /* Rx BBF R5 Tune */ +#define RX1_BBF_TUNE_COMP_I (1 << 3) /* Rx1 BBF Tune Comp I */ +#define RX1_BBF_TUNE_COMP_Q (1 << 2) /* Rx1 BBF Tune Comp Q */ +#define RX2_BBF_TUNE_COMP_I (1 << 1) /* Rx2 BBF Tune Comp I */ +#define RX2_BBF_TUNE_COMP_Q (1 << 0) /* Rx2 BBF Tune Comp Q */ +#define RX_BBF_TUNE_CTR(x) (((x) & 0x3) << 5) /* Rx BBF Tune Ctr<1:0> */ + +/* +* REG_RX1_BBF_MAN_GAIN +*/ +#define RX1_BBF_FORCE_GAIN (1 << 5) /* Rx1 BBF Force Gain */ +#define RX1_BBF_BQ_GAIN(x) (((x) & 0x3) << 3) /* Rx1 BBF BQ Gain<1:0> */ +#define RX1_BBF_POLE_GAIN(x) (((x) & 0x7) << 0) /* Rx1 BBF Pole Gain<2:0> */ + +/* +* REG_RX2_BBF_MAN_GAIN +*/ +#define RX2_BBF_FORCE_GAIN (1 << 5) /* Rx2 BBF Force Gain */ +#define RX2_BBF_BQ_GAIN(x) (((x) & 0x3) << 3) /* Rx2 BBF BQ Gain<1:0> */ +#define RX2_BBF_POLE_GAIN(x) (((x) & 0x7) << 0) /* Rx2 BBF Pole Gain<2:0> */ + +/* +* REG_RX_BBF_TUNE_CONFIG +*/ +#define RX_TUNE_EVALTIME (1 << 4) /* Rx Tune Evaltime */ +#define RX_BBF_TUNE_DIVIDE (1 << 0) /* RX BBF Tune Divide<8> */ +#define TUNE_COMP_MASK(x) (((x) & 0x3) << 5) /* Tune Comp Mask <1:0> */ +#define RX_TUNE_MODE(x) (((x) & 0x7) << 1) /* Rx Tune Mode<2:0> */ + +/* +* REG_POLE_GAIN +*/ +#define POLE_GAIN_TUNE(x) (((x) & 0x3) << 0) /* Pole Gain Tune<1:0> */ + +/* +* REG_RX_BBBW_MHZ +*/ +#define RX_TUNE_BBBW_MHZ(x) (((x) & 0x1F) << 0) /* Rx Tune BBBW MHz<4::0> */ + +/* +* REG_RX_BBBW_KHZ +*/ +#define RX_TUNE_BBBW_KHZ(x) (((x) & 0x7F) << 0) /* Rx Tune BBBW kHz<6:0> */ + +/* +* REG_RX_PFD_CONFIG +*/ +#define BYPASS_LD_SYNTH (1 << 0) /* Bypass Ld Synth */ + +/* +* REG_RX_INTEGER_BYTE_1 +*/ +#define SYNTH_INTEGER_WORD(x) (((x) & 0x7) << 0) /* Synthesizer Integer Word<10:8> */ + +/* +* REG_RX_FRACT_BYTE_2 +*/ +#define SYNTH_FRACT_WORD(x) (((x) & 0x7F) << 0) /* Synthesizer Fractional Word <22:16> */ + +/* +* REG_RX_FORCE_VCO_TUNE_1 +*/ +#define VCO_CAL_OFFSET(x) (((x) & 0xF) << 3) /* VCO Cal Offset<3:0> */ + +/* +* REG_RX_ALC_VARACTOR +*/ +#define INIT_ALC_VALUE(x) (((x) & 0xF) << 4) /* Init ALC Value<3:0> */ +#define VCO_VARACTOR(x) (((x) & 0xF) << 0) /* VCO Varactor<3:0> */ + +/* +* REG_RX_VCO_OUTPUT +*/ +#define PORB_VCO_LOGIC (1 << 6) /* PORb VCO Logic */ +#define VCO_OUTPUT_LEVEL(x) (((x) & 0xF) << 0) /* VCO Output Level<3:0> */ + +/* +* REG_RX_CP_CURRENT +*/ +#define CHARGE_PUMP_CURRENT(x) (((x) & 0x3F) << 0) /* Charge Pump Current<5:0> */ + +/* +* REG_RX_CP_OFFSET +*/ +#define SYNTH_RECAL (1 << 7) /* Synth Re-Cal */ + +/* +* REG_RX_CP_CONFIG +*/ +#define HALF_VCO_CAL_CLK (1 << 7) /* Half Vco Cal Clk */ +#define CP_OFFSET_OFF (1 << 4) /* CP Offset Off */ +#define F_CPCAL (1 << 3) /* F Cpcal */ +#define CP_CAL_ENABLE (1 << 2) /* Cp Cal Enable */ + +/* +* REG_RX_LOOP_FILTER_1 +*/ +#define LOOP_FILTER_C2(x) (((x) & 0xF) << 4) /* Loop Filter C2<3:0> */ +#define LOOP_FILTER_C1(x) (((x) & 0xF) << 0) /* Loop Filter C1<3:0> */ + +/* +* REG_RX_LOOP_FILTER_2 +*/ +#define LOOP_FILTER_R1(x) (((x) & 0xF) << 4) /* Loop Filter R1<3:0> */ +#define LOOP_FILTER_C3(x) (((x) & 0xF) << 0) /* Loop Filter C3<3:0> */ + +/* +* REG_RX_LOOP_FILTER_3 +*/ +#define LOOP_FILTER_BYPASS_R3 (1 << 7) /* Loop Filter Bypass R3 */ +#define LOOP_FILTER_BYPASS_R1 (1 << 6) /* Loop Filter Bypass R1 */ +#define LOOP_FILTER_BYPASS_C2 (1 << 5) /* Loop Filter Bypass C2 */ +#define LOOP_FILTER_BYPASS_C1 (1 << 4) /* Loop Filter Bypass C1 */ +#define LOOP_FILTER_R3(x) (((x) & 0xF) << 0) /* Loop Filter R3<3:0> */ + +/* +* REG_RX_DITHERCP_CAL +*/ +#define FORCED_CP_CAL_WORD(x) (((x) & 0xF) << 0) /* Forced CP Cal Word<3:0> */ + +/* +* REG_RX_VCO_BIAS_1 +*/ +#define VCO_BIAS_TCF(x) (((x) & 0x3) << 3) /* VCO Bias Tcf<1:0> */ +#define VCO_BIAS_REF(x) (((x) & 0x7) << 0) /* VCO Bias Ref<2:0> */ + +/* +* REG_RX_CAL_STATUS +*/ +#define CP_CAL_VALID (1 << 7) /* CP Cal Valid */ +#define CP_CAL_DONE (1 << 5) /* CP Cal Done */ +#define VCO_CAL_BUSY (1 << 4) /* VCO Cal Busy */ +#define CP_CAL_WORD(x) (((x) & 0xF) << 0) /* CP Cal Word<3:0> */ + +/* +* REG_RX_VCO_CAL_REF +*/ +#define VCO_CAL_REF_TCF(x) (((x) & 0x7) << 0) /* VCO Cal Ref Tcf<2:0> */ + +/* +* REG_RX_VCO_PD_OVERRIDES +*/ +#define POWER_DOWN_VARACTOR_REF (1 << 3) /* Power Down Varactor Ref */ +#define PWR_DOWN_VARACT_REF_TCF (1 << 2) /* Pwr Down Varact Ref Tcf */ +#define POWER_DOWN_CAL_TCF (1 << 1) /* Power Down Cal Tcf */ +#define POWER_DOWN_VCO_BUFFFER (1 << 0) /* Power Down VCO Bufffer */ + +/* +* REG_RX_CP_OVERRANGE_VCO_LOCK +*/ +#define CP_OVRG_HIGH (1 << 7) /* CP Ovrg High */ +#define CP_OVRG_LOW (1 << 6) /* CP Ovrg Low */ +#define VCO_LOCK (1 << 1) /* Lock */ + +/* +* REG_RX_VCO_LDO +*/ +#define VCO_LDO_BYPASS (1 << 7) /* VCO LDO Bypass */ +#define VCO_LDO_INRUSH(x) (((x) & 0x3) << 5) /* VCO LDO Inrush<1:0> */ +#define VCO_LDO_SEL(x) (((x) & 0x7) << 2) /* VCO LDO Sel<2:0> */ +#define VCO_LDO_VDROP_SEL(x) (((x) & 0x3) << 0) /* VCO LDO Vdrop Sel<1:0> */ + +/* +* REG_RX_VCO_CAL +*/ +#define VCO_CAL_EN (1 << 7) /* VCO Cal En */ +#define VCO_CAL_ALC_WAIT(x) (((x) & 0x7) << 4) /* VCO Cal ALC Wait <2:0> */ +#define VCO_CAL_COUNT(x) (((x) & 0x3) << 2) /* VCO Cal Count <1:0> */ + +/* +* REG_RX_LOCK_DETECT_CONFIG +*/ +#define LOCK_DETECT_COUNT(x) (((x) & 0x3) << 2) /* Lock Detect Count<1:0> */ +#define LOCK_DETECT_MODE(x) (((x) & 0x3) << 0) /* Lock Detect Mode<1:0> */ + +/* +* REG_RX_CP_LEVEL_DETECT +*/ +#define CP_LEVEL_DETECT_POWER_DOWN (1 << 6) /* CP Level Detect Power Down */ +#define CP_LEVEL_THRESH_LOW(x) (((x) & 0x7) << 3) /* CP Level Threshold Low<2:0> */ +#define CP_LEVEL_THRESH_HIGH(x) (((x) & 0x7) << 0) /* CP Level Threshold High<2:0> */ + +/* +* REG_RX_DSM_SETUP_0 +*/ +#define DSM_PROG(x) (((x) & 0xF) << 0) /* DSM Prog<3:0> */ + +/* +* REG_RX_DSM_SETUP_1 +*/ +#define SIF_CLOCK (1 << 6) /* SIF clock */ +#define SIF_RESET_BAR (1 << 5) /* SIF Reset Bar */ +#define SIF_ADDR(x) (((x) & 0x1F) << 0) /* SIF Addr<4:0> */ + +/* +* REG_RX_CORRECTION_WORD0 +*/ +#define UPDATE_FREQ_WORD (1 << 7) /* Update Freq Word */ +#define READ_EFFECTIVE_TUNING_WORD (1 << 5) /* Read Effective Tuning Word */ +#define FREQ_CORRECTION_WORD_MSB(x) (((x) & 0x1F) << 0) /* Frequency Correction Word<11:7> */ + +/* +* REG_RX_CORRECTION_WORD1 +*/ +#define UPDATE_FREQ_WORD (1 << 7) /* Update Freq Word */ +#define FREQ_CORRECTION_WORD_LSB(x) (((x) & 0x7F) << 0) /* Frequency Correction Word<6:0> */ + +/* +* REG_RX_VCO_VARACTOR_CTRL_0 +*/ +#define VCO_VARACTOR_REFERENCE_TCF(x) (((x) & 0x7) << 4) /* VCO Varactor Reference Tcf<2:0> */ +#define VCO_VARACTOR_OFFSET(x) (((x) & 0xF) << 0) /* VCO Varactor Offset<3:0> */ + +/* +* REG_RX_VCO_VARACTOR_CTRL_1 +*/ +#define VCO_VARACTOR_REFERENCE(x) (((x) & 0xF) << 0) /* VCO Varactor Reference<3:0> */ + +/* +* REG_RX_FAST_LOCK_SETUP +*/ +#define RX_FAST_LOCK_LOAD_SYNTH (1 << 3) /* Rx Fast Lock Load Synth */ +#define RX_FAST_LOCK_PROFILE_INIT (1 << 2) /* Rx Fast Lock Profile Init */ +#define RX_FAST_LOCK_PROFILE_PIN_SELECT (1 << 1) /* Rx Fast Lock Profile Pin Select */ +#define RX_FAST_LOCK_MODE_ENABLE (1 << 0) /* Rx Fast Lock Mode Enable */ +#define RX_FAST_LOCK_PROFILE(x) (((x) & 0x7) << 5) /* Rx Fast Lock Profile<2:0> */ + +/* +* REG_RX_FAST_LOCK_PROGRAM_ADDR +*/ +#define RX_FAST_LOCK_PROFILE_ADDR(x) (((x) & 0x7) << 4) /* Rx Fast Lock Profile<2:0> */ +#define RX_FAST_LOCK_PROFILE_WORD(x) (((x) & 0xF) << 0) /* Configuration Word <3:0> */ + + +/* +* REG_RX_FAST_LOCK_PROGRAM_CTRL +*/ +#define RX_FAST_LOCK_PROGRAM_WRITE (1 << 1) /* Rx Fast Lock Program Write */ +#define RX_FAST_LOCK_PROGRAM_CLOCK_ENABLE (1 << 0) /* Rx Fast Lock Program Clock Enable */ + +#define RX_FAST_LOCK_CONFIG_WORD_NUM 16 + +/* +* REG_RX_LO_GEN_POWER_MODE +*/ +#define RX_LO_GEN_POWER_MODE(x) (((x) & 0x3) << 4) /* Power Mode<3:0> */ + +/* +* REG_TX_PFD_CONFIG +*/ +#define DIV_TEST_EN (1 << 5) /* Div Test En */ +#define PFD_CLK_EDGE (1 << 1) /* PFD Clk Edge */ +#define BYPASS_LD_SYNTH (1 << 0) /* Bypass Ld Synth */ +#define PFD_WIDTH(x) (((x) & 0x3) << 2) /* PFD Width <1:0> */ + +/* +* REG_TX_INTEGER_BYTE_1 +*/ +#define SDM_BYPASS (1 << 7) /* SDM Bypass */ +#define SDM_POWER_DOWN (1 << 6) /* SDM Power Down */ +#define SYNTH_INTEGER_WORD(x) (((x) & 0x7) << 0) /* Synthesizer Integer Word<10:8> */ + +/* +* REG_TX_FRACT_BYTE_2 +*/ +#define SYNTH_FRACT_WORD(x) (((x) & 0x7F) << 0) /* Synthesizer Fractional Word <22:16> */ + +/* +* REG_TX_FORCE_ALC +*/ +#define FORCE_ALC_ENABLE (1 << 7) /* Force ALC Enable */ +#define FORCE_ALC_WORD(x) (((x) & 0x7F) << 0) /* Force ALC Word<6:0> */ + +/* +* REG_TX_FORCE_VCO_TUNE_1 +*/ +#define BYPASS_LOAD_DELAY (1 << 7) /* Bypass Load Delay */ +#define FORCE_VCO_TUNE_ENABLE (1 << 1) /* Force VCO Tune Enable */ +#define FORCE_VCO_TUNE (1 << 0) /* Force VCO Tune */ +#define VCO_CAL_OFFSET(x) (((x) & 0xF) << 3) /* VCO Cal Offset<3:0> */ + +/* +* REG_TX_ALCVARACT_OR +*/ +#define INIT_ALC_VALUE(x) (((x) & 0xF) << 4) /* Init ALC Value<3:0> */ +#define VCO_VARACTOR(x) (((x) & 0xF) << 0) /* VCO Varactor<3:0> */ + +/* +* REG_TX_VCO_OUTPUT +*/ +#define PORB_VCO_LOGIC (1 << 6) /* PORb VCO Logic */ +#define VCO_OUTPUT_LEVEL(x) (((x) & 0xF) << 0) /* VCO Output Level<3:0> */ + +/* +* REG_TX_CP_CURRENT +*/ +#define TX_CP_CURRENT_DFLT (1 << 7) /* Set to 1 */ +#define VTUNE_FORCE (1 << 6) /* Vtune Force */ +#define CHARGE_PUMP_CURRENT(x) (((x) & 0x3F) << 0) /* Charge Pump Current<5:0> */ + +/* +* REG_TX_CP_OFFSET +*/ +#define SYNTH_RECAL (1 << 7) /* Synth Re-Cal */ +#define CHARGE_PUMP_OFFSET(x) (((x) & 0x3F) << 0) /* Charge Pump Offset<5:0> */ + +/* +* REG_TX_CP_CONFIG +*/ +#define HALF_VCO_CAL_CLK (1 << 7) /* Half Vco Cal Clk */ +#define DITHER_MODE (1 << 6) /* Dither Mode */ +#define CP_OFFSET_OFF (1 << 4) /* Cp Offset Off */ +#define F_CPCAL (1 << 3) /* F Cpcal */ +#define CP_CAL_ENABLE (1 << 2) /* Cp Cal Enable */ +#define CP_TEST(x) (((x) & 0x3) << 0) /* Cp Test <1:0> */ + +/* +* REG_TX_LOOP_FILTER_1 +*/ +#define LOOP_FILTER_C2(x) (((x) & 0xF) << 4) /* Loop Filter C2<3:0> */ +#define LOOP_FILTER_C1(x) (((x) & 0xF) << 0) /* Loop Filter C1<3:0> */ + +/* +* REG_TX_LOOP_FILTER_2 +*/ +#define LOOP_FILTER_R1(x) (((x) & 0xF) << 4) /* Loop Filter R1<3:0> */ +#define LOOP_FILTER_C3(x) (((x) & 0xF) << 0) /* Loop Filter C3<3:0> */ + +/* +* REG_TX_LOOP_FILTER_3 +*/ +#define LOOP_FILTER_BYPASS_R3 (1 << 7) /* Loop Filter Bypass R3 */ +#define LOOP_FILTER_BYPASS_R1 (1 << 6) /* Loop Filter Bypass R1 */ +#define LOOP_FILTER_BYPASS_C2 (1 << 5) /* Loop Filter Bypass C2 */ +#define LOOP_FILTER_BYPASS_C1 (1 << 4) /* Loop Filter Bypass C1 */ +#define LOOP_FILTER_R3(x) (((x) & 0xF) << 0) /* Loop Filter R3<3:0> */ + +/* +* REG_TX_DITHERCP_CAL +*/ +#define NUMBER_SDM_DITHER_BITS(x) (((x) & 0xF) << 4) /* Number SDM Dither Bits<3:0> */ +#define FORCED_CP_CAL_WORD(x) (((x) & 0xF) << 0) /* Forced CP Cal Word<3:0> */ + +/* +* REG_TX_VCO_BIAS_1 +*/ +#define MUST_BE_ZEROS(x) (((x) & 0x3) << 5) /* Must be zeros */ +#define VCO_BIAS_TCF(x) (((x) & 0x3) << 3) /* VCO Bias Tcf<1:0> */ +#define VCO_BIAS_REF(x) (((x) & 0x7) << 0) /* VCO Bias Ref<2:0> */ + +/* +* REG_TX_VCO_BIAS_2 +*/ +#define VCO_BYPASS_BIAS_DAC_R (1 << 7) /* VCO Bypass Bias DAC R */ +#define VCO_COMP_BYPASS_BIAS_R (1 << 4) /* VCO Comp Bypass Bias R */ +#define BYPASS_PRESCALE_R (1 << 3) /* Bypass Prescale R */ +#define LAST_ALC_ENABLE (1 << 2) /* Last ALC Enable */ +#define PRESCALE_BIAS(x) (((x) & 0x3) << 0) /* Prescale Bias <1:0> */ + +/* +* REG_TX_CAL_STATUS +*/ +#define CP_CAL_VALID (1 << 7) /* CP Cal Valid */ +#define COMP_OUT (1 << 6) /* Comp Out */ +#define CP_CAL_DONE (1 << 5) /* CP Cal Done */ +#define VCO_CAL_BUSY (1 << 4) /* VCO Cal Busy */ +#define CP_CAL_WORD(x) (((x) & 0xF) << 0) /* CP Cal Word<3:0> */ + +/* +* REG_TX_VCO_CAL_REF +*/ +#define VCO_CAL_REF_MONITOR (1 << 3) /* VCO Cal Ref Monitor */ +#define VCO_CAL_REF_TCF(x) (((x) & 0x7) << 0) /* VCO Cal Ref Tcf<2:0> */ + +/* +* REG_TX_VCO_PD_OVERRIDES +*/ +#define POWER_DOWN_VARACTOR_REF (1 << 3) /* Power Down Varactor Ref */ +#define POWER_DOWN_VARACT_REF_TCF (1 << 2) /* Power Down Varact Ref Tcf */ +#define POWER_DOWN_CAL_TCF (1 << 1) /* Power Down Cal Tcf */ +#define POWER_DOWN_VCO_BUFFFER (1 << 0) /* Power Down VCO Bufffer */ + +/* +* REG_TX_CP_OVERRANGE_VCO_LOCK +*/ +#define CP_OVRG_HIGH (1 << 7) /* CP Ovrg High */ +#define CP_OVRG_LOW (1 << 6) /* CP Ovrg Low */ +#define VCO_LOCK (1 << 1) /* Lock */ + +/* +* REG_TX_VCO_LDO +*/ +#define VCO_LDO_BYPASS (1 << 7) /* VCO LDO Bypass */ +#define VCO_LDO_INRUSH(x) (((x) & 0x3) << 5) /* VCO LDO Inrush<1:0> */ +#define VCO_LDO_VOUT_SEL(x) (((x) & 0x7) << 2) /* VCO LDO Vout Sel<2:0> */ +#define VCO_LDO_VDROP_SEL(x) (((x) & 0x3) << 0) /* VCO LDO Vdrop Sel<1:0> */ + +/* +* REG_TX_VCO_CAL +*/ +#define VCO_CAL_EN (1 << 7) /* VCO Cal En */ +#define VCO_CAL_ALC_WAIT(x) (((x) & 0x7) << 4) /* VCO Cal ALC Wait<2:0) */ +#define VCO_CAL_COUNT(x) (((x) & 0x3) << 2) /* VCO Cal Count<1:0> */ +#define FB_CLOCK_ADV(x) (((x) & 0x3) << 0) /* FB Clock Adv<1:0> */ + +/* +* REG_TX_LOCK_DETECT_CONFIG +*/ +#define LOCK_DETECT_COUNT(x) (((x) & 0x3) << 2) /* Lock Detect Count<1:0> */ +#define LOCK_DETECT_MODE(x) (((x) & 0x3) << 0) /* Lock Detect Mode<1:0> */ + +/* +* REG_TX_CP_LEVEL_DETECT +*/ +#define CP_LEVEL_DETECT_POWER_DOWN (1 << 6) /* CP Level Detect Power Down */ +#define CP_LEVEL_DETECT_THRESH_LOW(x) (((x) & 0x7) << 3) /* CP Level Detect Threshold Low<2:0> */ +#define CP_LEVEL_DETECT_THRESH_HIGH(x) (((x) & 0x7) << 0) /* CP Level Detect Threshold High<2:0> */ + +/* +* REG_TX_DSM_SETUP_0 +*/ +#define DSM_PROG(x) (((x) & 0xF) << 0) /* DSM Prog<3:0> */ + +/* +* REG_TX_DSM_SETUP_1 +*/ +#define SIF_CLOCK (1 << 6) /* SIF clock */ +#define SIF_RESET_BAR (1 << 5) /* SIF Reset Bar */ +#define SIF_ADDR(x) (((x) & 0x1F) << 0) /* SIF Addr<4:0> */ + +/* +* REG_TX_CORRECTION_WORD0 +*/ +#define UPDATE_FREQ_WORD (1 << 7) /* Update Freq Word */ +#define READ_EFFECTIVE_TUNING_WORD (1 << 5) /* Read Effective Tuning Word */ +#define FREQ_CORRECTION_WORD_MSB(x) (((x) & 0x1F) << 0) /* Frequency Correction Word<11:7> */ + +/* +* REG_TX_CORRECTION_WORD1 +*/ +#define UPDATE_FREQ_WORD (1 << 7) /* Update Freq Word */ +#define FREQ_CORRECTION_WORD_LSB(x) (((x) & 0x7F) << 0) /* Frequency Correction Word<6:0> */ + +/* +* REG_TX_VCO_VARACTOR_CTRL_0 +*/ +#define VCO_VARACTOR_REFERENCE_TCF(x) (((x) & 0x7) << 4) /* VCO Varactor Reference Tcf<2:0> */ +#define VCO_VARACTOR_OFFSET(x) (((x) & 0xF) << 0) /* VCO Varactor Offset<3:0> */ + +/* +* REG_TX_VCO_VARACTOR_CTRL_1 +*/ +#define VCO_VARACTOR_REFERENCE(x) (((x) & 0xF) << 0) /* VCO Varactor Reference<3:0> */ + +/* +* REG_DCXO_COARSE_TUNE +*/ +#define DCXO_TUNE_COARSE(x) (((x) & 0x3F) << 0) /* DCXO Tune Coarse<5:0> */ + +/* +* REG_DCXO_FINE_TUNE_LOW +*/ +#define DCXO_TUNE_FINE_LOW(x) (((x) & 0x1F) << 3) /* DCXO Tune Fine<4:0> */ + +/* +* REG_DCXO_FINE_TUNE_HIGH +*/ +#define DCXO_TUNE_FINE_HIGH(x) ((x) >> 5) /* DCXO Tune Fine<12:5> */ + +/* +* REG_DCXO_CONFIG +*/ +#define MUST_BE_ZERO (1 << 7) /* Must be zero */ +#define DCXO_RTAIL(x) (((x) & 0x7) << 4) /* DCXO Rtail<2:0> */ +#define DCXO_RD(x) (((x) & 0x3) << 2) /* DCXO Rd<1:0> */ + +/* +* REG_DCXO_TEMPCO_ADDR +*/ +#define DCXO_TEMPCO_EN (1 << 7) /* DCXO Tempco En */ +#define DCXO_TEMPCO_CLK (1 << 6) /* DCXO Tempco Clk */ +#define DCXO_TEMPERATURE_COEF_ADDRESS(x) (((x) & 0x3F) << 0) /* DCXO Temperature Coefficient Address<5:0> */ + +/* +* REG_TX_FAST_LOCK_SETUP +*/ +#define TX_FAST_LOCK_LOAD_SYNTH (1 << 3) /* Tx Fast Lock Load Synth */ +#define TX_FAST_LOCK_PROFILE_INIT (1 << 2) /* Tx Fast Lock Profile Init */ +#define TX_FAST_LOCK_PROFILE_PIN_SELECT (1 << 1) /* Tx Fast Lock Profile Pin Select */ +#define TX_FAST_LOCK_MODE_ENABLE (1 << 0) /* Tx Fast Lock Mode Enable */ +#define TX_FAST_LOCK_PROFILE(x) (((x) & 0x7) << 5) /* Tx Fast Lock Profile<2:0> */ + +/* +* REG_TX_FAST_LOCK_PROGRAM_CTRL +*/ +#define TX_FAST_LOCK_PROGRAM_WRITE (1 << 1) /* Tx Fast Lock Program Write */ +#define TX_FAST_LOCK_PROGRAM_CLOCK_ENABLE (1 << 0) /* Tx Fast Lock Program Clock Enable */ + +/* +* REG_TX_LO_GEN_POWER_MODE +*/ +#define TX_LO_GEN_POWER_MODE(x) (((x) & 0xF) << 4) /* Power Mode<3:0> */ + +/* +* REG_BANDGAP_CONFIG0 +*/ +#define POWER_DOWN_BANDGAP_REF (1 << 7) /* Power Down Bandgap Ref */ +#define MASTER_BIAS_FILTER_BYPASS (1 << 6) /* Master Bias Filter Bypass */ +#define MASTER_BIAS_REF_SEL (1 << 5) /* Master Bias Ref Sel */ +#define MASTER_BIAS_TRIM(x) (((x) & 0x1F) << 0) /* Master Bias Trim<4:0> */ + +/* +* REG_BANDGAP_CONFIG1 +*/ +#define VCO_LDO_FILTER_BYPASS (1 << 7) /* VCO LDO Filter Bypass */ +#define VCO_LDO_REF_SEL (1 << 6) /* VCO LDO Ref Sel */ +#define BANDGAP_REF_RESET (1 << 5) /* Bandgap Ref Reset */ +#define BANDGAP_TEMP_TRIM(x) (((x) & 0x1F) << 0) /* Bandgap Temp Trim<4:0> */ + +/* +* REG_REF_DIVIDE_CONFIG_1 +*/ +#define REF_DIVIDE_CONFIG_1_DFLT (1 << 2) /* Set to 1 */ +#define RX_REF_RESET_BAR (1 << 1) /* Rx Ref Reset Bar */ +#define RX_REF_DIVIDER_MSB (1 << 0) /* Rx Ref Divider<1> */ + +/* +* REG_REF_DIVIDE_CONFIG_2 +*/ +#define RX_REF_DIVIDER_LSB (1 << 7) /* Rx Ref Divider< 0> */ +#define TX_REF_RESET_BAR (1 << 4) /* Tx Ref Reset Bar */ +#define RX_REF_DOUBLER_FB_DELAY(x) (((x) & 0x3) << 5) /* Rx Ref Doubler FB Delay<1:0> */ +#define TX_REF_DIVIDER(x) (((x) & 0x3) << 2) /* Tx Ref Divider<1:0> */ +#define TX_REF_DOUBLER_FB_DELAY(x) (((x) & 0x3) << 0) /* Tx Ref Doubler FB Delay<1:0> */ + +/* +* REG_GAIN_RX1,2 +*/ +#define FULL_TABLE_GAIN_INDEX(x) (((x) & 0x7F) << 0) /* Full Table Gain Index Rx1/LMT Gain Rx1<6:0> */ + +/* +* REG_LPF_GAIN_RX1,2 +*/ +#define LPF_GAIN_RX(x) (((x) & 0x1F) << 0) /* LPF gain Rx1<4:0> */ + +/* +* REG_DIG_GAIN_RX1,2 +*/ +#define DIGITAL_GAIN_RX(x) (((x) & 0x1F) << 0) /* Digital gain Rx1<4:0> */ + +/* +* REG_FAST_ATTACK_STATE +*/ +#define FAST_ATTACK_STATE_RX2(x) (((x) & 0x7) << 4) /* Fast Attack State Rx2<2:0> */ +#define FAST_ATTACK_STATE_RX1(x) (((x) & 0x7) << 0) /* Fast Attack State Rx1<2:0> */ +#define FAST_ATK_MASK 0x7 +#define RX1_FAST_ATK_SHIFT 0 +#define RX2_FAST_ATK_SHIFT 4 +#define FAST_ATK_RESET 0 +#define FAST_ATK_PEAK_DETECT 1 +#define FAST_ATK_PWR_MEASURE 2 +#define FAST_ATK_FINAL_SETTELING 3 +#define FAST_ATK_FINAL_OVER 4 +#define FAST_ATK_GAIN_LOCKED 5 + +/* +* REG_SLOW_LOOP_STATE +*/ +#define SLOW_LOOP_STATE_RX2(x) (((x) & 0x7) << 4) /* Slow Loop State Rx2<2:0> */ +#define SLOW_LOOP_STATE_RX1(x) (((x) & 0x7) << 0) /* Slow Loop State Rx1<2:0> */ + + +/* +* REG_OVRG_SIGS_RX1,2 +*/ +#define GAIN_LOCK_1 (1 << 6) /* Gain Lock 1 */ +#define LOW_POWER_1 (1 << 5) /* Low Power 1 */ +#define LARGE_LMT_OL (1 << 4) /* Large LMT OL */ +#define SMALL_LMT_OL (1 << 3) /* Small LMT OL */ +#define LARGE_ADC_OL (1 << 2) /* Large ADC OL */ +#define SMALL_ADC_OL (1 << 1) /* Small ADC OL */ +#define DIG_SAT (1 << 0) /* Dig Sat */ +/* +* REG_CTRL +*/ +#define CTRL_ENABLE (1 << 0) /* Set to 1 */ + +/* +* REG_BIST_CONFIG +*/ +#define TONE_PRBS (1 << 1) /* Tone/ PRBS */ +#define BIST_ENABLE (1 << 0) /* BIST Enable */ +#define TONE_FREQ(x) (((x) & 0x3) << 6) /* Tone Frequency<1:0> */ +#define TONE_LEVEL(x) (((x) & 0x3) << 4) /* Tone Level<1:0> */ +#define BIST_CTRL_POINT(x) (((x) & 0x3) << 2) /* BIST Control Point <1:0> */ + +/* +* REG_OBSERVE_CONFIG +*/ +#define DATA_PORT_SP_HD_LOOP_TEST_OE (1 << 7) /* Data Port SP, HD Loop Test OE */ +#define RX_MASK (1 << 6) /* Rx Mask */ +#define CHANNEL (1 << 5) /* Channel */ +#define DATA_PORT_LOOP_TEST_ENABLE (1 << 0) /* Data Port Loop Test Enable */ +#define OBSERVATION_POINT(x) (((x) & 0xF) << 1) /* Observation Point<2:0> */ + +/* +* REG_BIST_AND_DATA_PORT_TEST_CONFIG +*/ +#define BIST_MASK_CHANNEL_2_Q_DATA (1 << 5) /* BIST Mask Channel 2 Q data */ +#define BIST_MASK_CHANNEL_2_I_DATA (1 << 4) /* BIST Mask Channel 2 I data */ +#define BIST_MASK_CHANNEL_1_Q_DATA (1 << 3) /* BIST Mask Channel 1 Q data */ +#define BIST_MASK_CHANNEL_1_I_DATA (1 << 2) /* BIST Mask Channel 1 I data */ +#define DATA_PORT_HILOW (1 << 1) /* Data Port Hi/Low */ +#define USE_DATA_PORT (1 << 0) /* Use Data Port */ +#define TEMP_SENSE_VBE_TEST(x) (((x) & 0x3) << 6) /* Temp Sense Vbe Test<1:0> */ + +/* +* REG_DAC_TEST_2 +*/ +#define DAC_TEST_ENABLE (1 << 7) /* DAC Test Enable */ +#define DAC_TEST_WORD(x) (((x) & 0x7F) << 0) /* DAC test Word <22:16> */ + +/* +* SPI Comm Helpers +*/ +#define AD_READ (0 << 15) +#define AD_WRITE (1 << 15) +#define AD_CNT(x) ((((x) - 1) & 0x7) << 12) +#define AD_ADDR(x) ((x) & 0x3FF) + + +/* +* AD9361 Limits +*/ + +#define RSSI_MULTIPLIER 100 +#define RSSI_RESOLUTION ((int) (0.25 * RSSI_MULTIPLIER)) +#define RSSI_MAX_WEIGHT 255 + +#define MAX_LMT_INDEX 40 +#define MAX_LPF_GAIN 24 +#define MAX_DIG_GAIN 31 + +#define MAX_BBPLL_FREF 70007000UL /* 70 MHz + 100ppm */ +#define MIN_BBPLL_FREQ 714928500UL /* 715 MHz - 100ppm */ +#define MAX_BBPLL_FREQ 1430143000UL /* 1430 MHz + 100ppm */ +#define MAX_BBPLL_DIV 64 +#define MIN_BBPLL_DIV 2 + +/* + * The ADC minimum and maximum operating output data rates + * are 25MHz and 640MHz respectively. + * For more information see here: https://ez.analog.com/docs/DOC-12763 + */ + +#define MIN_ADC_CLK 25000000UL /* 25 MHz */ +//#define MIN_ADC_CLK (MIN_BBPLL_FREQ / MAX_BBPLL_DIV) /* 11.17MHz */ +#define MAX_ADC_CLK 640000000UL /* 640 MHz */ +#define MAX_DAC_CLK (MAX_ADC_CLK / 2) + +/* Associated with outputs of stage */ +#define MAX_RX_HB1 245760000UL +#define MAX_RX_HB2 320000000UL +#define MAX_RX_HB3 640000000UL +/* Associated with inputs of stage */ +#define MAX_TX_HB1 160000000UL +#define MAX_TX_HB2 320000000UL +#define MAX_TX_HB3 320000000UL + +#define MAX_BASEBAND_RATE 61440000UL + +#define MAX_MBYTE_SPI 8 + +#define RFPLL_MODULUS 8388593UL +#define BBPLL_MODULUS 2088960UL + +#define MAX_SYNTH_FREF 80008000UL /* 80 MHz + 100ppm */ +#define MIN_SYNTH_FREF 9999000UL /* 10 MHz - 100ppm */ +#define MIN_VCO_FREQ_HZ 6000000000ULL +#define MAX_CARRIER_FREQ_HZ 6000000000ULL +#define MIN_RX_CARRIER_FREQ_HZ 70000000ULL +#define MIN_TX_CARRIER_FREQ_HZ 46875001ULL + +#define AD9363A_MAX_CARRIER_FREQ_HZ 3800000000ULL +#define AD9363A_MIN_CARRIER_FREQ_HZ 325000000ULL + +#define MAX_TX_ATTENUATION_DB 89750 + +/* +* Driver +*/ + +enum rx_gain_table_type { + RXGAIN_FULL_TBL, + RXGAIN_SPLIT_TBL, +}; + +enum rx_gain_table_name { + TBL_200_1300_MHZ, + TBL_1300_4000_MHZ, + TBL_4000_6000_MHZ, + RXGAIN_TBLS_END, +}; + +struct gain_table_info { + uint64_t start; + uint64_t end; + uint8_t max_index; + uint8_t split_table; + int8_t *abs_gain_tbl; + uint8_t (*tab)[3]; +}; + +enum fir_dest { + FIR_TX1 = 0x01, + FIR_TX2 = 0x02, + FIR_TX1_TX2 = 0x03, + FIR_RX1 = 0x81, + FIR_RX2 = 0x82, + FIR_RX1_RX2 = 0x83, + FIR_IS_RX = 0x80, +}; + +struct rf_gain_ctrl { + uint32_t ant; + uint8_t mode; +}; + +enum rf_gain_ctrl_mode { + RF_GAIN_MGC, + RF_GAIN_FASTATTACK_AGC, + RF_GAIN_SLOWATTACK_AGC, + RF_GAIN_HYBRID_AGC +}; + +enum f_agc_target_gain_index_type { + MAX_GAIN, + SET_GAIN, + OPTIMIZED_GAIN, + NO_GAIN_CHANGE, +}; + +struct gain_control { + enum rf_gain_ctrl_mode rx1_mode; + enum rf_gain_ctrl_mode rx2_mode; + + /* Common */ + uint8_t adc_ovr_sample_size; /* 1..8 Sum x samples, AGC_CONFIG_3 */ + uint8_t adc_small_overload_thresh; /* 0..255, 0x105 */ + uint8_t adc_large_overload_thresh; /* 0..255, 0x104 */ + + uint16_t lmt_overload_high_thresh; /* 16..800 mV, 0x107 */ + uint16_t lmt_overload_low_thresh; /* 16..800 mV, 0x108 */ + uint16_t dec_pow_measuremnt_duration; /* Samples, 0x15C */ + uint8_t low_power_thresh; /* -64..0 dBFS, 0x114 */ + bool use_rx_fir_out_for_dec_pwr_meas; /* clears 0x15C:6 USE_HB1_OUT_FOR_DEC_PWR_MEAS */ + + bool dig_gain_en; /* should be turned off, since ADI GT doesn't use dig gain */ + uint8_t max_dig_gain; /* 0..31 */ + + /* MGC */ + bool mgc_rx1_ctrl_inp_en; /* Enables Pin control on RX1 default SPI ctrl */ + bool mgc_rx2_ctrl_inp_en; /* Enables Pin control on RX2 default SPI ctrl */ + + uint8_t mgc_inc_gain_step; /* 1..8 */ + uint8_t mgc_dec_gain_step; /* 1..8 */ + uint8_t mgc_split_table_ctrl_inp_gain_mode; /* 0=AGC determine this, 1=only in LPF, 2=only in LMT */ + + /* AGC */ + uint8_t agc_attack_delay_extra_margin_us; /* 0..31 us */ + + uint8_t agc_outer_thresh_high; + uint8_t agc_outer_thresh_high_dec_steps; + uint8_t agc_inner_thresh_high; + uint8_t agc_inner_thresh_high_dec_steps; + uint8_t agc_inner_thresh_low; + uint8_t agc_inner_thresh_low_inc_steps; + uint8_t agc_outer_thresh_low; + uint8_t agc_outer_thresh_low_inc_steps; + + uint8_t adc_small_overload_exceed_counter; /* 0..15, 0x122 */ + uint8_t adc_large_overload_exceed_counter; /* 0..15, 0x122 */ + uint8_t adc_large_overload_inc_steps; /* 0..15, 0x106 */ + + bool adc_lmt_small_overload_prevent_gain_inc; /* 0x120 */ + + uint8_t lmt_overload_large_exceed_counter; /* 0..15, 0x121 */ + uint8_t lmt_overload_small_exceed_counter; /* 0..15, 0x121 */ + uint8_t lmt_overload_large_inc_steps; /* 0..7, 0x121 */ + + uint8_t dig_saturation_exceed_counter; /* 0..15, 0x128 */ + uint8_t dig_gain_step_size; /* 1..8, 0x100 */ + bool sync_for_gain_counter_en; /* 0x128:4 !Hybrid */ + + uint32_t gain_update_interval_us; /* in us */ + bool immed_gain_change_if_large_adc_overload; /* 0x123:3 */ + bool immed_gain_change_if_large_lmt_overload; /* 0x123:7 */ + + /* + * Fast AGC + */ + uint32_t f_agc_dec_pow_measuremnt_duration; /* Samples, 0x15C */ + uint32_t f_agc_state_wait_time_ns; /* 0x117 0..31 RX samples -> time_ns */ + /* Fast AGC - Low Power */ + bool f_agc_allow_agc_gain_increase; /* 0x110:1 */ + uint8_t f_agc_lp_thresh_increment_time; /* 0x11B RX samples */ + uint8_t f_agc_lp_thresh_increment_steps; /* 0x117 1..8 */ + + /* Fast AGC - Lock Level */ + uint8_t f_agc_lock_level; /* NOT USED: 0x101 0..-127 dBFS same as agc_inner_thresh_high */ + bool f_agc_lock_level_lmt_gain_increase_en; /* 0x111:6 */ + uint8_t f_agc_lock_level_gain_increase_upper_limit; /* 0x118 0..63 */ + /* Fast AGC - Peak Detectors and Final Settling */ + uint8_t f_agc_lpf_final_settling_steps; /* 0x112:6 0..3 (Post Lock Level Step)*/ + uint8_t f_agc_lmt_final_settling_steps; /* 0x113:6 0..3 (Post Lock Level Step)*/ + uint8_t f_agc_final_overrange_count; /* 0x116:5 0..7 */ + /* Fast AGC - Final Power Test */ + bool f_agc_gain_increase_after_gain_lock_en; /* 0x110:7 */ + /* Fast AGC - Unlocking the Gain */ + /* 0 = MAX Gain, 1 = Set Gain, 2 = Optimized Gain */ + enum f_agc_target_gain_index_type + f_agc_gain_index_type_after_exit_rx_mode; /* 0x110:[4,2] */ + bool f_agc_use_last_lock_level_for_set_gain_en; /* 0x111:7 */ + uint8_t f_agc_optimized_gain_offset; /*0x116 0..15 steps */ + bool f_agc_rst_gla_stronger_sig_thresh_exceeded_en; /* 0x110:~6 */ + uint8_t f_agc_rst_gla_stronger_sig_thresh_above_ll; /*0x113 0..63 dbFS */ + bool f_agc_rst_gla_engergy_lost_sig_thresh_exceeded_en; /* 0x110:6 */ + bool f_agc_rst_gla_engergy_lost_goto_optim_gain_en; /* 0x110:6 */ + uint8_t f_agc_rst_gla_engergy_lost_sig_thresh_below_ll; /* 0x112:6 */ + uint8_t f_agc_energy_lost_stronger_sig_gain_lock_exit_cnt; /* 0x119 0..63 RX samples */ + bool f_agc_rst_gla_large_adc_overload_en; /*0x110:~1 and 0x114:~7 */ + bool f_agc_rst_gla_large_lmt_overload_en; /*0x110:~1 */ + bool f_agc_rst_gla_en_agc_pulled_high_en; + /* 0 = Max Gain, 1 = Set Gain, 2 = Optimized Gain, 3 = No Gain Change */ + + enum f_agc_target_gain_index_type + f_agc_rst_gla_if_en_agc_pulled_high_mode; /* 0x0FB, 0x111 */ + uint8_t f_agc_power_measurement_duration_in_state5; /* 0x109, 0x10a RX samples 0..524288*/ + uint8_t f_agc_large_overload_inc_steps; /* 0x106 [D6:D4] 0..7 */ +}; + +struct auxdac_control { + uint16_t dac1_default_value; + uint16_t dac2_default_value; + + bool auxdac_manual_mode_en; + + bool dac1_in_rx_en; + bool dac1_in_tx_en; + bool dac1_in_alert_en; + + bool dac2_in_rx_en; + bool dac2_in_tx_en; + bool dac2_in_alert_en; + + uint8_t dac1_rx_delay_us; + uint8_t dac1_tx_delay_us; + uint8_t dac2_rx_delay_us; + uint8_t dac2_tx_delay_us; +}; + +enum rssi_restart_mode { + AGC_IN_FAST_ATTACK_MODE_LOCKS_THE_GAIN, + EN_AGC_PIN_IS_PULLED_HIGH, + ENTERS_RX_MODE, + GAIN_CHANGE_OCCURS, + SPI_WRITE_TO_REGISTER, + GAIN_CHANGE_OCCURS_OR_EN_AGC_PIN_PULLED_HIGH, +}; + +struct rssi_control { + enum rssi_restart_mode restart_mode; + bool rssi_unit_is_rx_samples; /* default unit is time */ + uint32_t rssi_delay; + uint32_t rssi_wait; + uint32_t rssi_duration; +}; + +struct rx_gain_info { + enum rx_gain_table_type tbl_type; + int32_t starting_gain_db; + int32_t max_gain_db; + int32_t gain_step_db; + int32_t max_idx; + int32_t idx_step_offset; +}; + +struct port_control { + uint8_t pp_conf[3]; + uint8_t rx_clk_data_delay; + uint8_t tx_clk_data_delay; + uint8_t digital_io_ctrl; + uint8_t lvds_bias_ctrl; + uint8_t lvds_invert[2]; +}; + +struct ctrl_outs_control { + uint8_t index; + uint8_t en_mask; +}; + +struct elna_control { + uint16_t gain_mdB; + uint16_t bypass_loss_mdB; + uint32_t settling_delay_ns; + bool elna_1_control_en; /* GPO0 */ + bool elna_2_control_en; /* GPO1 */ + bool elna_in_gaintable_all_index_en; +}; + +struct auxadc_control { + int8_t offset; + uint32_t temp_time_inteval_ms; + uint32_t temp_sensor_decimation; + bool periodic_temp_measuremnt; + uint32_t auxadc_clock_rate; + uint32_t auxadc_decimation; +}; + +struct gpo_control { + uint32_t gpo_manual_mode_enable_mask; + bool gpo_manual_mode_en; + bool gpo0_inactive_state_high_en; + bool gpo1_inactive_state_high_en; + bool gpo2_inactive_state_high_en; + bool gpo3_inactive_state_high_en; + bool gpo0_slave_rx_en; + bool gpo0_slave_tx_en; + bool gpo1_slave_rx_en; + bool gpo1_slave_tx_en; + bool gpo2_slave_rx_en; + bool gpo2_slave_tx_en; + bool gpo3_slave_rx_en; + bool gpo3_slave_tx_en; + uint8_t gpo0_rx_delay_us; + uint8_t gpo0_tx_delay_us; + uint8_t gpo1_rx_delay_us; + uint8_t gpo1_tx_delay_us; + uint8_t gpo2_rx_delay_us; + uint8_t gpo2_tx_delay_us; + uint8_t gpo3_rx_delay_us; + uint8_t gpo3_tx_delay_us; +}; + +struct tx_monitor_control { + bool tx_mon_track_en; + bool one_shot_mode_en; + uint32_t low_high_gain_threshold_mdB; + uint8_t low_gain_dB; + uint8_t high_gain_dB; + uint16_t tx_mon_delay; + uint16_t tx_mon_duration; + uint8_t tx1_mon_front_end_gain; + uint8_t tx2_mon_front_end_gain; + uint8_t tx1_mon_lo_cm; + uint8_t tx2_mon_lo_cm; +}; + +enum ad9361_pdata_rx_freq { + BBPLL_FREQ, + ADC_FREQ, + R2_FREQ, + R1_FREQ, + CLKRF_FREQ, + RX_SAMPL_FREQ, + NUM_RX_CLOCKS, +}; + +enum ad9361_pdata_tx_freq { + IGNORE, + DAC_FREQ, + T2_FREQ, + T1_FREQ, + CLKTF_FREQ, + TX_SAMPL_FREQ, + NUM_TX_CLOCKS, +}; + +enum ad9361_clkout { + CLKOUT_DISABLE, + BUFFERED_XTALN_DCXO, + ADC_CLK_DIV_2, + ADC_CLK_DIV_3, + ADC_CLK_DIV_4, + ADC_CLK_DIV_8, + ADC_CLK_DIV_16, +}; + +struct ad9361_phy_platform_data { + bool rx2tx2; + bool fdd; + bool fdd_independent_mode; + bool split_gt; + bool use_extclk; + bool ensm_pin_pulse_mode; + bool ensm_pin_ctrl; + bool debug_mode; + bool tdd_use_dual_synth; + bool tdd_skip_vco_cal; + bool use_ext_rx_lo; + bool use_ext_tx_lo; + bool rx1rx2_phase_inversion_en; + bool qec_tracking_slow_mode_en; + uint8_t dc_offset_update_events; + uint8_t dc_offset_attenuation_high; + uint8_t dc_offset_attenuation_low; + uint8_t rf_dc_offset_count_high; + uint8_t rf_dc_offset_count_low; + uint8_t dig_interface_tune_skipmode; + uint8_t dig_interface_tune_fir_disable; + uint8_t lo_powerdown_managed_en; + uint32_t dcxo_coarse; + uint32_t dcxo_fine; + uint32_t rf_rx_input_sel; + uint32_t rf_tx_output_sel; + uint32_t rx1tx1_mode_use_rx_num; + uint32_t rx1tx1_mode_use_tx_num; + uint32_t rx_path_clks[NUM_RX_CLOCKS]; + uint32_t tx_path_clks[NUM_TX_CLOCKS]; + uint32_t trx_synth_max_fref; + uint64_t rx_synth_freq; + uint64_t tx_synth_freq; + uint32_t rf_rx_bandwidth_Hz; + uint32_t rf_tx_bandwidth_Hz; + int32_t tx_atten; + bool update_tx_gain_via_alert; + uint32_t rx_fastlock_delay_ns; + uint32_t tx_fastlock_delay_ns; + bool trx_fastlock_pinctrl_en[2]; + + enum ad9361_clkout ad9361_clkout_mode; + + struct gain_control gain_ctrl; + struct rssi_control rssi_ctrl; + struct port_control port_ctrl; + struct ctrl_outs_control ctrl_outs_ctrl; + struct elna_control elna_ctrl; + struct auxadc_control auxadc_ctrl; + struct auxdac_control auxdac_ctrl; + struct gpo_control gpo_ctrl; + struct tx_monitor_control txmon_ctrl; +}; + +struct rf_rx_gain { + uint32_t ant; /* Antenna number to read gain */ + int32_t gain_db; /* gain value in dB */ + uint32_t fgt_lmt_index; /* Full Gain Table / LNA-MIXER-TIA gain index */ + uint32_t lmt_gain; /* LNA-MIXER-TIA gain in dB (Split GT mode only)*/ + uint32_t lpf_gain; /* Low pass filter gain in dB / index (Split GT mode only)*/ + uint32_t digital_gain; /* Digital gain in dB / index */ + /* Debug only */ + uint32_t lna_index; /* LNA Index (Split GT mode only) */ + uint32_t tia_index; /* TIA Index (Split GT mode only) */ + uint32_t mixer_index; /* MIXER Index (Split GT mode only) */ + +}; +struct rf_rssi { + uint32_t ant; /* Antenna number for which RSSI is reported */ + uint32_t symbol; /* Runtime RSSI */ + uint32_t preamble; /* Initial RSSI */ + int32_t multiplier; /* Multiplier to convert reported RSSI */ + uint8_t duration; /* Duration to be considered for measuring */ +}; + +struct SynthLUT { + uint16_t VCO_MHz; + uint8_t VCO_Output_Level; + uint8_t VCO_Varactor; + uint8_t VCO_Bias_Ref; + uint8_t VCO_Bias_Tcf; + uint8_t VCO_Cal_Offset; + uint8_t VCO_Varactor_Reference; + uint8_t Charge_Pump_Current; + uint8_t LF_C2; + uint8_t LF_C1; + uint8_t LF_R1; + uint8_t LF_C3; + uint8_t LF_R3; +}; + +enum { + LUT_FTDD_40, + LUT_FTDD_60, + LUT_FTDD_80, + LUT_FTDD_ENT, +}; + +enum ad9361_clocks { + BB_REFCLK, + RX_REFCLK, + TX_REFCLK, + BBPLL_CLK, + ADC_CLK, + R2_CLK, + R1_CLK, + CLKRF_CLK, + RX_SAMPL_CLK, + DAC_CLK, + T2_CLK, + T1_CLK, + CLKTF_CLK, + TX_SAMPL_CLK, + RX_RFPLL_INT, + TX_RFPLL_INT, + RX_RFPLL_DUMMY, + TX_RFPLL_DUMMY, + RX_RFPLL, + TX_RFPLL, + NUM_AD9361_CLKS, + EXT_REF_CLK, +}; + +struct ad9361_debugfs_entry { + struct ad9361_rf_phy *phy; + const char *propname; + void *out_value; + uint32_t val; + uint8_t size; + uint8_t cmd; +}; + +struct ad9361_fastlock_entry { +#define FASTLOOK_INIT 1 + uint8_t flags; + uint8_t alc_orig; + uint8_t alc_written; +}; + +struct ad9361_fastlock { + uint8_t save_profile; + uint8_t current_profile[2]; + struct ad9361_fastlock_entry entry[2][8]; +}; + +enum dig_tune_flags { + BE_VERBOSE = 1, + BE_MOREVERBOSE = 2, + DO_IDELAY = 4, + DO_ODELAY = 8, + SKIP_STORE_RESULT = 16, + RESTORE_DEFAULT = 32, +}; + +enum ad9361_bist_mode { + BIST_DISABLE, + BIST_INJ_TX, + BIST_INJ_RX, +}; + +enum synth_pd_ctrl { + LO_DONTCARE, + LO_OFF, + LO_ON, +}; + +enum dev_id { + ID_AD9361, + ID_AD9364, + ID_AD9363A +}; + +struct ad9361_rf_phy { + enum dev_id dev_sel; + struct spi_desc *spi; + struct gpio_desc *gpio_desc_resetb; + struct gpio_desc *gpio_desc_sync; + struct gpio_desc *gpio_desc_cal_sw1; + struct gpio_desc *gpio_desc_cal_sw2; +#ifndef AXI_ADC_NOT_PRESENT + struct axi_adc *rx_adc; + struct axi_dac *tx_dac; +#endif + struct clk *clk_refin; + struct clk *clks[NUM_AD9361_CLKS]; + struct refclk_scale *ref_clk_scale[NUM_AD9361_CLKS]; + uint32_t (*ad9361_rfpll_ext_recalc_rate)(struct refclk_scale *clk_priv); + int32_t (*ad9361_rfpll_ext_round_rate)(struct refclk_scale *clk_priv, + uint32_t rate); + int32_t (*ad9361_rfpll_ext_set_rate)(struct refclk_scale *clk_priv, + uint32_t rate); + struct ad9361_phy_platform_data *pdata; + uint8_t prev_ensm_state; + uint8_t curr_ensm_state; + uint8_t cached_rx_rfpll_div; + uint8_t cached_tx_rfpll_div; + uint8_t cached_synth_pd[2]; + int32_t tx_quad_lpf_tia_match; + uint32_t current_table; + struct gain_table_info *gt_info; + bool ensm_pin_ctl_en; + + bool auto_cal_en; + bool manual_tx_quad_cal_en; + uint64_t last_tx_quad_cal_freq; + uint32_t last_tx_quad_cal_phase; + uint64_t current_tx_lo_freq; + uint64_t current_rx_lo_freq; + bool current_tx_use_tdd_table; + bool current_rx_use_tdd_table; + uint32_t current_rx_path_clks[NUM_RX_CLOCKS]; + uint32_t current_tx_path_clks[NUM_TX_CLOCKS]; + uint32_t flags; + uint32_t cal_threshold_freq; + uint32_t current_rx_bw_Hz; + uint32_t current_tx_bw_Hz; + uint32_t rxbbf_div; + uint32_t rate_governor; + bool bypass_rx_fir; + bool bypass_tx_fir; + bool rx_eq_2tx; + bool filt_valid; + uint32_t filt_rx_path_clks[NUM_RX_CLOCKS]; + uint32_t filt_tx_path_clks[NUM_TX_CLOCKS]; + uint32_t filt_rx_bw_Hz; + uint32_t filt_tx_bw_Hz; + uint8_t tx_fir_int; + uint8_t tx_fir_ntaps; + uint8_t rx_fir_dec; + uint8_t rx_fir_ntaps; + uint8_t agc_mode[2]; + bool rfdc_track_en; + bool bbdc_track_en; + bool quad_track_en; + bool txmon_tdd_en; + uint16_t auxdac1_value; + uint16_t auxdac2_value; + uint32_t tx1_atten_cached; + uint32_t tx2_atten_cached; + struct ad9361_fastlock fastlock; + struct axiadc_converter *adc_conv; + struct axiadc_state *adc_state; + int32_t bist_loopback_mode; + int32_t bist_config; + enum ad9361_bist_mode bist_prbs_mode; + enum ad9361_bist_mode bist_tone_mode; + uint32_t bist_tone_freq_Hz; + uint32_t bist_tone_level_dB; + uint32_t bist_tone_mask; + bool bbpll_initialized; +}; + +struct refclk_scale { + struct spi_desc *spi; + struct ad9361_rf_phy *phy; + uint32_t mult; + uint32_t div; + enum ad9361_clocks source; + enum ad9361_clocks parent_source; +}; + +enum debugfs_cmd { + DBGFS_NONE, + DBGFS_INIT, + DBGFS_LOOPBACK, + DBGFS_BIST_PRBS, + DBGFS_BIST_TONE, + DBGFS_BIST_DT_ANALYSIS, + DBGFS_RXGAIN_1, + DBGFS_RXGAIN_2, +}; + +/******************************************************************************/ +/************************ Functions Declarations ******************************/ +/******************************************************************************/ +int32_t ad9361_spi_readm(struct spi_desc *spi, uint32_t reg, + uint8_t *rbuf, uint32_t num); +int32_t ad9361_spi_read(struct spi_desc *spi, uint32_t reg); +int32_t ad9361_spi_write(struct spi_desc *spi, + uint32_t reg, uint32_t val); +int32_t ad9361_reset(struct ad9361_rf_phy *phy); +int32_t ad9361_register_clocks(struct ad9361_rf_phy *phy); +int32_t ad9361_unregister_clocks(struct ad9361_rf_phy *phy); +uint32_t ad9361_gt(struct ad9361_rf_phy *phy); +int32_t ad9361_init_gain_tables(struct ad9361_rf_phy *phy); +int32_t ad9361_setup(struct ad9361_rf_phy *phy); +int32_t ad9361_post_setup(struct ad9361_rf_phy *phy); +int32_t ad9361_set_ensm_mode(struct ad9361_rf_phy *phy, bool fdd, bool pinctrl); +int32_t ad9361_ensm_set_state(struct ad9361_rf_phy *phy, uint8_t ensm_state, + bool pinctrl); +int32_t ad9361_set_rx_gain(struct ad9361_rf_phy *phy, + uint32_t rx_id, struct rf_rx_gain *rx_gain); +int32_t ad9361_get_rx_gain(struct ad9361_rf_phy *phy, + uint32_t rx_id, struct rf_rx_gain *rx_gain); +int32_t ad9361_update_rf_bandwidth(struct ad9361_rf_phy *phy, + uint32_t rf_rx_bw, uint32_t rf_tx_bw); +int32_t ad9361_calculate_rf_clock_chain(struct ad9361_rf_phy *phy, + uint32_t tx_sample_rate, + uint32_t rate_gov, + uint32_t *rx_path_clks, + uint32_t *tx_path_clks); +int32_t ad9361_set_trx_clock_chain(struct ad9361_rf_phy *phy, + uint32_t *rx_path_clks, + uint32_t *tx_path_clks); +int32_t ad9361_get_trx_clock_chain(struct ad9361_rf_phy *phy, + uint32_t *rx_path_clks, + uint32_t *tx_path_clks); +uint32_t ad9361_to_clk(uint64_t freq); +uint64_t ad9361_from_clk(uint32_t freq); +int32_t ad9361_read_rssi(struct ad9361_rf_phy *phy, struct rf_rssi *rssi); +int32_t ad9361_set_gain_ctrl_mode(struct ad9361_rf_phy *phy, + struct rf_gain_ctrl *gain_ctrl); +int32_t ad9361_load_fir_filter_coef(struct ad9361_rf_phy *phy, + enum fir_dest dest, int32_t gain_dB, + uint32_t ntaps, short *coef); +int32_t ad9361_validate_enable_fir(struct ad9361_rf_phy *phy); +int32_t ad9361_set_tx_atten(struct ad9361_rf_phy *phy, uint32_t atten_mdb, + bool tx1, bool tx2, bool immed); +int32_t ad9361_get_tx_atten(struct ad9361_rf_phy *phy, uint32_t tx_num); +uint32_t ad9361_clk_factor_recalc_rate(struct refclk_scale *clk_priv, + uint32_t parent_rate); +int32_t ad9361_clk_factor_round_rate(struct refclk_scale *clk_priv, + uint32_t rate, + uint32_t *prate); +int32_t ad9361_clk_factor_set_rate(struct refclk_scale *clk_priv, uint32_t rate, + uint32_t parent_rate); +uint32_t ad9361_bbpll_recalc_rate(struct refclk_scale *clk_priv, + uint32_t parent_rate); +int32_t ad9361_bbpll_round_rate(struct refclk_scale *clk_priv, uint32_t rate, + uint32_t *prate); +int32_t ad9361_bbpll_set_rate(struct refclk_scale *clk_priv, uint32_t rate, + uint32_t parent_rate); +uint32_t ad9361_rfpll_int_recalc_rate(struct refclk_scale *clk_priv, + uint32_t parent_rate); +int32_t ad9361_rfpll_int_round_rate(struct refclk_scale *clk_priv, + uint32_t rate, + uint32_t *prate); +int32_t ad9361_rfpll_int_set_rate(struct refclk_scale *clk_priv, uint32_t rate, + uint32_t parent_rate); +uint32_t ad9361_rfpll_dummy_recalc_rate(struct refclk_scale *clk_priv); +int32_t ad9361_rfpll_dummy_set_rate(struct refclk_scale *clk_priv, + uint32_t rate); +uint32_t ad9361_rfpll_recalc_rate(struct refclk_scale *clk_priv); +int32_t ad9361_rfpll_round_rate(struct refclk_scale *clk_priv, uint32_t rate); +int32_t ad9361_rfpll_set_rate(struct refclk_scale *clk_priv, uint32_t rate); +int32_t ad9361_clk_mux_set_parent(struct refclk_scale *clk_priv, uint8_t index); +int32_t ad9361_tracking_control(struct ad9361_rf_phy *phy, bool bbdc_track, + bool rfdc_track, bool rxquad_track); +int32_t ad9361_bist_loopback(struct ad9361_rf_phy *phy, int32_t mode); +void ad9361_get_bist_loopback(struct ad9361_rf_phy *phy, int32_t *mode); +int32_t ad9361_bist_prbs(struct ad9361_rf_phy *phy, enum ad9361_bist_mode mode); +void ad9361_get_bist_prbs(struct ad9361_rf_phy *phy, + enum ad9361_bist_mode *mode); +int32_t ad9361_bist_tone(struct ad9361_rf_phy *phy, + enum ad9361_bist_mode mode, uint32_t freq_Hz, + uint32_t level_dB, uint32_t mask); +void ad9361_get_bist_tone(struct ad9361_rf_phy *phy, + enum ad9361_bist_mode *mode, uint32_t *freq_Hz, + uint32_t *level_dB, uint32_t *mask); +int32_t ad9361_rf_port_setup(struct ad9361_rf_phy *phy, bool is_out, + uint32_t rx_inputs, uint32_t txb); +int32_t ad9361_mcs(struct ad9361_rf_phy *phy, int32_t step); +int32_t ad9361_do_calib_run(struct ad9361_rf_phy *phy, uint32_t cal, + int32_t arg); +int32_t ad9361_fastlock_store(struct ad9361_rf_phy *phy, bool tx, + uint32_t profile); +int32_t ad9361_fastlock_recall(struct ad9361_rf_phy *phy, bool tx, + uint32_t profile); +int32_t ad9361_fastlock_load(struct ad9361_rf_phy *phy, bool tx, + uint32_t profile, uint8_t *values); +int32_t ad9361_fastlock_save(struct ad9361_rf_phy *phy, bool tx, + uint32_t profile, uint8_t *values); +void ad9361_ensm_force_state(struct ad9361_rf_phy *phy, uint8_t ensm_state); +uint8_t ad9361_ensm_get_state(struct ad9361_rf_phy *phy); +void ad9361_ensm_restore_state(struct ad9361_rf_phy *phy, uint8_t ensm_state); +void ad9361_ensm_restore_prev_state(struct ad9361_rf_phy *phy); +int32_t ad9361_set_trx_clock_chain_freq(struct ad9361_rf_phy *phy, + uint32_t freq); +int32_t ad9361_find_opt(uint8_t *field, uint32_t size, uint32_t *ret_start); +int32_t ad9361_hdl_loopback(struct ad9361_rf_phy *phy, bool enable); +int32_t ad9361_dig_interface_timing_analysis(struct ad9361_rf_phy *phy, + char *buf, int32_t buflen); +int32_t ad9361_dig_tune(struct ad9361_rf_phy *phy, uint32_t max_freq, + enum dig_tune_flags flags); +int32_t ad9361_en_dis_tx(struct ad9361_rf_phy *phy, uint32_t tx_if, + uint32_t enable); +int32_t ad9361_en_dis_rx(struct ad9361_rf_phy *phy, uint32_t rx_if, + uint32_t enable); +int32_t ad9361_1rx1tx_channel_map(struct ad9361_rf_phy *phy, bool tx, + int32_t channel); +int32_t ad9361_rssi_gain_step_calib(struct ad9361_rf_phy *phy); +int32_t ad9361_set_dcxo_tune(struct ad9361_rf_phy *phy, + uint32_t coarse, uint32_t fine); +int32_t ad9361_tx_mute(struct ad9361_rf_phy *phy, uint32_t state); +uint32_t ad9361_validate_rf_bw(struct ad9361_rf_phy *phy, uint32_t bw); +int32_t ad9361_get_temp(struct ad9361_rf_phy *phy); +int ad9361_synth_lo_powerdown(struct ad9361_rf_phy *phy, + enum synth_pd_ctrl rx, + enum synth_pd_ctrl tx); +void ad9361_clear_state(struct ad9361_rf_phy *phy); +#endif diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361/ad9361_api.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361/ad9361_api.c new file mode 100644 index 0000000..1befc57 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361/ad9361_api.c @@ -0,0 +1,2157 @@ +/***************************************************************************//** + * @file ad9361_api.c + * @brief Implementation of AD9361 API Driver. + * @author DBogdan (dragos.bogdan@analog.com) +******************************************************************************** + * Copyright 2013(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include +#include "ad9361.h" +#include "ad9361_api.h" +#include "delay.h" +#include "spi.h" +#include "util.h" +#include "../../../config/app_config.h" + +/******************************************************************************/ +/************************ Constants Definitions *******************************/ +/******************************************************************************/ + +extern struct gain_table_info ad9361_adi_gt_info[]; + +/** + * Initialize the AD9361 part. + * @param ad9361_phy The AD9361 device structure. + * @param init_param The structure that contains the AD9361 initial parameters. + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_init (struct ad9361_rf_phy **ad9361_phy, + AD9361_InitParam *init_param) +{ + struct ad9361_rf_phy *phy; + int32_t ret = 0; + int32_t rev = 0; + int32_t i = 0; + + phy = (struct ad9361_rf_phy *)zmalloc(sizeof(*phy)); + if (!phy) { + return -ENOMEM; + } + + phy->clk_refin = (struct clk *)zmalloc(sizeof(*phy->clk_refin)); + if (!phy->clk_refin) { + return -ENOMEM; + } + + phy->pdata = (struct ad9361_phy_platform_data *)zmalloc(sizeof(*phy->pdata)); + if (!phy->pdata) { + return -ENOMEM; + } + + /* Device selection */ + phy->dev_sel = init_param->dev_sel; + + /* Reference Clock */ + phy->clk_refin->rate = init_param->reference_clk_rate; + + /* Base Configuration */ + phy->pdata->fdd = init_param->frequency_division_duplex_mode_enable; + phy->pdata->fdd_independent_mode = + init_param->frequency_division_duplex_independent_mode_enable; + phy->pdata->rx2tx2 = init_param->two_rx_two_tx_mode_enable; + phy->pdata->rx1tx1_mode_use_rx_num = init_param->one_rx_one_tx_mode_use_rx_num; + phy->pdata->rx1tx1_mode_use_tx_num = init_param->one_rx_one_tx_mode_use_tx_num; + phy->pdata->tdd_use_dual_synth = init_param->tdd_use_dual_synth_mode_enable; + phy->pdata->tdd_skip_vco_cal = init_param->tdd_skip_vco_cal_enable; + phy->pdata->rx_fastlock_delay_ns = init_param->rx_fastlock_delay_ns; + phy->pdata->tx_fastlock_delay_ns = init_param->tx_fastlock_delay_ns; + phy->pdata->trx_fastlock_pinctrl_en[0] = + init_param->rx_fastlock_pincontrol_enable; + phy->pdata->trx_fastlock_pinctrl_en[1] = + init_param->tx_fastlock_pincontrol_enable; + if (phy->dev_sel == ID_AD9363A) { + phy->pdata->use_ext_rx_lo = false; + phy->pdata->use_ext_tx_lo = false; + } else { + phy->pdata->use_ext_rx_lo = init_param->external_rx_lo_enable; + phy->pdata->use_ext_tx_lo = init_param->external_tx_lo_enable; + } + phy->pdata->dc_offset_update_events = + init_param->dc_offset_tracking_update_event_mask; + phy->pdata->dc_offset_attenuation_high = + init_param->dc_offset_attenuation_high_range; + phy->pdata->dc_offset_attenuation_low = + init_param->dc_offset_attenuation_low_range; + phy->pdata->rf_dc_offset_count_high = init_param->dc_offset_count_high_range; + phy->pdata->rf_dc_offset_count_low = init_param->dc_offset_count_low_range; + phy->pdata->split_gt = init_param->split_gain_table_mode_enable; + phy->pdata->trx_synth_max_fref = + init_param->trx_synthesizer_target_fref_overwrite_hz; + phy->pdata->qec_tracking_slow_mode_en = + init_param->qec_tracking_slow_mode_enable; + + /* ENSM Control */ + phy->pdata->ensm_pin_pulse_mode = init_param->ensm_enable_pin_pulse_mode_enable; + phy->pdata->ensm_pin_ctrl = init_param->ensm_enable_txnrx_control_enable; + + /* LO Control */ + phy->pdata->rx_synth_freq = init_param->rx_synthesizer_frequency_hz; + phy->pdata->tx_synth_freq = init_param->tx_synthesizer_frequency_hz; + phy->pdata->lo_powerdown_managed_en = + init_param->tx_lo_powerdown_managed_enable; + + /* Rate & BW Control */ + for(i = 0; i < 6; i++) { + phy->pdata->rx_path_clks[i] = init_param->rx_path_clock_frequencies[i]; + } + for(i = 0; i < 6; i++) { + phy->pdata->tx_path_clks[i] = init_param->tx_path_clock_frequencies[i]; + } + phy->pdata->rf_rx_bandwidth_Hz = init_param->rf_rx_bandwidth_hz; + phy->pdata->rf_tx_bandwidth_Hz = init_param->rf_tx_bandwidth_hz; + + /* RF Port Control */ + phy->pdata->rf_rx_input_sel = init_param->rx_rf_port_input_select; + phy->pdata->rf_tx_output_sel = init_param->tx_rf_port_input_select; + + /* TX Attenuation Control */ + phy->pdata->tx_atten = init_param->tx_attenuation_mdB; + phy->pdata->update_tx_gain_via_alert = + init_param->update_tx_gain_in_alert_enable; + + /* Reference Clock Control */ + switch (phy->dev_sel) { + case ID_AD9363A: + phy->pdata->use_extclk = true; + break; + default: + phy->pdata->use_extclk = init_param->xo_disable_use_ext_refclk_enable; + } + phy->pdata->dcxo_coarse = init_param->dcxo_coarse_and_fine_tune[0]; + phy->pdata->dcxo_fine = init_param->dcxo_coarse_and_fine_tune[1]; + phy->pdata->ad9361_clkout_mode = (enum ad9361_clkout) + init_param->clk_output_mode_select; + + /* Gain Control */ + phy->pdata->gain_ctrl.rx1_mode = (enum rf_gain_ctrl_mode) + init_param->gc_rx1_mode; + phy->pdata->gain_ctrl.rx2_mode = (enum rf_gain_ctrl_mode) + init_param->gc_rx2_mode; + phy->pdata->gain_ctrl.adc_large_overload_thresh = + init_param->gc_adc_large_overload_thresh; + phy->pdata->gain_ctrl.adc_ovr_sample_size = init_param->gc_adc_ovr_sample_size; + phy->pdata->gain_ctrl.adc_small_overload_thresh = + init_param->gc_adc_small_overload_thresh; + phy->pdata->gain_ctrl.dec_pow_measuremnt_duration = + init_param->gc_dec_pow_measurement_duration; + phy->pdata->gain_ctrl.dig_gain_en = init_param->gc_dig_gain_enable; + phy->pdata->gain_ctrl.lmt_overload_high_thresh = + init_param->gc_lmt_overload_high_thresh; + phy->pdata->gain_ctrl.lmt_overload_low_thresh = + init_param->gc_lmt_overload_low_thresh; + phy->pdata->gain_ctrl.low_power_thresh = init_param->gc_low_power_thresh; + phy->pdata->gain_ctrl.max_dig_gain = init_param->gc_max_dig_gain; + phy->pdata->gain_ctrl.use_rx_fir_out_for_dec_pwr_meas = + init_param->gc_use_rx_fir_out_for_dec_pwr_meas_enable; + + /* Gain MGC Control */ + phy->pdata->gain_ctrl.mgc_dec_gain_step = init_param->mgc_dec_gain_step; + phy->pdata->gain_ctrl.mgc_inc_gain_step = init_param->mgc_inc_gain_step; + phy->pdata->gain_ctrl.mgc_rx1_ctrl_inp_en = init_param->mgc_rx1_ctrl_inp_enable; + phy->pdata->gain_ctrl.mgc_rx2_ctrl_inp_en = init_param->mgc_rx2_ctrl_inp_enable; + phy->pdata->gain_ctrl.mgc_split_table_ctrl_inp_gain_mode = + init_param->mgc_split_table_ctrl_inp_gain_mode; + + /* Gain AGC Control */ + phy->pdata->gain_ctrl.adc_large_overload_exceed_counter = + init_param->agc_adc_large_overload_exceed_counter; + phy->pdata->gain_ctrl.adc_large_overload_inc_steps = + init_param->agc_adc_large_overload_inc_steps; + phy->pdata->gain_ctrl.adc_lmt_small_overload_prevent_gain_inc = + init_param->agc_adc_lmt_small_overload_prevent_gain_inc_enable; + phy->pdata->gain_ctrl.adc_small_overload_exceed_counter = + init_param->agc_adc_small_overload_exceed_counter; + phy->pdata->gain_ctrl.dig_gain_step_size = init_param->agc_dig_gain_step_size; + phy->pdata->gain_ctrl.dig_saturation_exceed_counter = + init_param->agc_dig_saturation_exceed_counter; + phy->pdata->gain_ctrl.gain_update_interval_us = + init_param->agc_gain_update_interval_us; + phy->pdata->gain_ctrl.immed_gain_change_if_large_adc_overload = + init_param->agc_immed_gain_change_if_large_adc_overload_enable; + phy->pdata->gain_ctrl.immed_gain_change_if_large_lmt_overload = + init_param->agc_immed_gain_change_if_large_lmt_overload_enable; + phy->pdata->gain_ctrl.agc_inner_thresh_high = init_param->agc_inner_thresh_high; + phy->pdata->gain_ctrl.agc_inner_thresh_high_dec_steps = + init_param->agc_inner_thresh_high_dec_steps; + phy->pdata->gain_ctrl.agc_inner_thresh_low = init_param->agc_inner_thresh_low; + phy->pdata->gain_ctrl.agc_inner_thresh_low_inc_steps = + init_param->agc_inner_thresh_low_inc_steps; + phy->pdata->gain_ctrl.lmt_overload_large_exceed_counter = + init_param->agc_lmt_overload_large_exceed_counter; + phy->pdata->gain_ctrl.lmt_overload_large_inc_steps = + init_param->agc_lmt_overload_large_inc_steps; + phy->pdata->gain_ctrl.lmt_overload_small_exceed_counter = + init_param->agc_lmt_overload_small_exceed_counter; + phy->pdata->gain_ctrl.agc_outer_thresh_high = init_param->agc_outer_thresh_high; + phy->pdata->gain_ctrl.agc_outer_thresh_high_dec_steps = + init_param->agc_outer_thresh_high_dec_steps; + phy->pdata->gain_ctrl.agc_outer_thresh_low = init_param->agc_outer_thresh_low; + phy->pdata->gain_ctrl.agc_outer_thresh_low_inc_steps = + init_param->agc_outer_thresh_low_inc_steps; + phy->pdata->gain_ctrl.agc_attack_delay_extra_margin_us = + init_param->agc_attack_delay_extra_margin_us; + phy->pdata->gain_ctrl.sync_for_gain_counter_en = + init_param->agc_sync_for_gain_counter_enable; + + /* Fast AGC */ + phy->pdata->gain_ctrl.f_agc_dec_pow_measuremnt_duration = + init_param->fagc_dec_pow_measuremnt_duration; + phy->pdata->gain_ctrl.f_agc_state_wait_time_ns = + init_param->fagc_state_wait_time_ns; + /* Fast AGC - Low Power */ + phy->pdata->gain_ctrl.f_agc_allow_agc_gain_increase = + init_param->fagc_allow_agc_gain_increase; + phy->pdata->gain_ctrl.f_agc_lp_thresh_increment_time = + init_param->fagc_lp_thresh_increment_time; + phy->pdata->gain_ctrl.f_agc_lp_thresh_increment_steps = + init_param->fagc_lp_thresh_increment_steps; + /* Fast AGC - Lock Level (Lock Level is set via slow AGC inner high threshold) */ + phy->pdata->gain_ctrl.f_agc_lock_level_lmt_gain_increase_en = + init_param->fagc_lock_level_lmt_gain_increase_en; + phy->pdata->gain_ctrl.f_agc_lock_level_gain_increase_upper_limit = + init_param->fagc_lock_level_gain_increase_upper_limit; + /* Fast AGC - Peak Detectors and Final Settling */ + phy->pdata->gain_ctrl.f_agc_lpf_final_settling_steps = + init_param->fagc_lpf_final_settling_steps; + phy->pdata->gain_ctrl.f_agc_lmt_final_settling_steps = + init_param->fagc_lmt_final_settling_steps; + phy->pdata->gain_ctrl.f_agc_final_overrange_count = + init_param->fagc_final_overrange_count; + /* Fast AGC - Final Power Test */ + phy->pdata->gain_ctrl.f_agc_gain_increase_after_gain_lock_en = + init_param->fagc_gain_increase_after_gain_lock_en; + /* Fast AGC - Unlocking the Gain */ + phy->pdata->gain_ctrl.f_agc_gain_index_type_after_exit_rx_mode = + (enum f_agc_target_gain_index_type) + init_param->fagc_gain_index_type_after_exit_rx_mode; + phy->pdata->gain_ctrl.f_agc_use_last_lock_level_for_set_gain_en = + init_param->fagc_use_last_lock_level_for_set_gain_en; + phy->pdata->gain_ctrl.f_agc_rst_gla_stronger_sig_thresh_exceeded_en = + init_param->fagc_rst_gla_stronger_sig_thresh_exceeded_en; + phy->pdata->gain_ctrl.f_agc_optimized_gain_offset = + init_param->fagc_optimized_gain_offset; + phy->pdata->gain_ctrl.f_agc_rst_gla_stronger_sig_thresh_above_ll = + init_param->fagc_rst_gla_stronger_sig_thresh_above_ll; + phy->pdata->gain_ctrl.f_agc_rst_gla_engergy_lost_sig_thresh_exceeded_en = + init_param->fagc_rst_gla_engergy_lost_sig_thresh_exceeded_en; + phy->pdata->gain_ctrl.f_agc_rst_gla_engergy_lost_goto_optim_gain_en = + init_param->fagc_rst_gla_engergy_lost_goto_optim_gain_en; + phy->pdata->gain_ctrl.f_agc_rst_gla_engergy_lost_sig_thresh_below_ll = + init_param->fagc_rst_gla_engergy_lost_sig_thresh_below_ll; + phy->pdata->gain_ctrl.f_agc_energy_lost_stronger_sig_gain_lock_exit_cnt = + init_param->fagc_energy_lost_stronger_sig_gain_lock_exit_cnt; + phy->pdata->gain_ctrl.f_agc_rst_gla_large_adc_overload_en = + init_param->fagc_rst_gla_large_adc_overload_en; + phy->pdata->gain_ctrl.f_agc_rst_gla_large_lmt_overload_en = + init_param->fagc_rst_gla_large_lmt_overload_en; + phy->pdata->gain_ctrl.f_agc_rst_gla_en_agc_pulled_high_en = + init_param->fagc_rst_gla_en_agc_pulled_high_en; + phy->pdata->gain_ctrl.f_agc_rst_gla_if_en_agc_pulled_high_mode = + (enum f_agc_target_gain_index_type) + init_param->fagc_rst_gla_if_en_agc_pulled_high_mode; + phy->pdata->gain_ctrl.f_agc_power_measurement_duration_in_state5 = + init_param->fagc_power_measurement_duration_in_state5; + phy->pdata->gain_ctrl.f_agc_large_overload_inc_steps = + init_param->fagc_large_overload_inc_steps; + + /* RSSI Control */ + phy->pdata->rssi_ctrl.rssi_delay = init_param->rssi_delay; + phy->pdata->rssi_ctrl.rssi_duration = init_param->rssi_duration; + phy->pdata->rssi_ctrl.restart_mode = (enum rssi_restart_mode) + init_param->rssi_restart_mode; + phy->pdata->rssi_ctrl.rssi_unit_is_rx_samples = + init_param->rssi_unit_is_rx_samples_enable; + phy->pdata->rssi_ctrl.rssi_wait = init_param->rssi_wait; + + /* Aux ADC Control */ + phy->pdata->auxadc_ctrl.auxadc_decimation = init_param->aux_adc_decimation; + phy->pdata->auxadc_ctrl.auxadc_clock_rate = init_param->aux_adc_rate; + + /* AuxDAC Control */ + phy->pdata->auxdac_ctrl.auxdac_manual_mode_en = + init_param->aux_dac_manual_mode_enable; + phy->pdata->auxdac_ctrl.dac1_default_value = + init_param->aux_dac1_default_value_mV; + phy->pdata->auxdac_ctrl.dac1_in_rx_en = + init_param->aux_dac1_active_in_rx_enable; + phy->pdata->auxdac_ctrl.dac1_in_tx_en = + init_param->aux_dac1_active_in_tx_enable; + phy->pdata->auxdac_ctrl.dac1_in_alert_en = + init_param->aux_dac1_active_in_alert_enable; + phy->pdata->auxdac_ctrl.dac1_rx_delay_us = init_param->aux_dac1_rx_delay_us; + phy->pdata->auxdac_ctrl.dac1_tx_delay_us = init_param->aux_dac1_tx_delay_us; + phy->pdata->auxdac_ctrl.dac2_default_value = + init_param->aux_dac2_default_value_mV; + phy->pdata->auxdac_ctrl.dac2_in_rx_en = + init_param->aux_dac2_active_in_rx_enable; + phy->pdata->auxdac_ctrl.dac2_in_tx_en = + init_param->aux_dac2_active_in_tx_enable; + phy->pdata->auxdac_ctrl.dac2_in_alert_en = + init_param->aux_dac2_active_in_alert_enable; + phy->pdata->auxdac_ctrl.dac2_rx_delay_us = init_param->aux_dac2_rx_delay_us; + phy->pdata->auxdac_ctrl.dac2_tx_delay_us = init_param->aux_dac2_tx_delay_us; + + /* Temperature Sensor Control */ + phy->pdata->auxadc_ctrl.temp_sensor_decimation = + init_param->temp_sense_decimation; + phy->pdata->auxadc_ctrl.temp_time_inteval_ms = + init_param->temp_sense_measurement_interval_ms; + phy->pdata->auxadc_ctrl.offset = init_param->temp_sense_offset_signed; + phy->pdata->auxadc_ctrl.periodic_temp_measuremnt = + init_param->temp_sense_periodic_measurement_enable; + + /* Control Out Setup */ + phy->pdata->ctrl_outs_ctrl.en_mask = init_param->ctrl_outs_enable_mask; + phy->pdata->ctrl_outs_ctrl.index = init_param->ctrl_outs_index; + + /* External LNA Control */ + phy->pdata->elna_ctrl.settling_delay_ns = init_param->elna_settling_delay_ns; + phy->pdata->elna_ctrl.gain_mdB = init_param->elna_gain_mdB; + phy->pdata->elna_ctrl.bypass_loss_mdB = init_param->elna_bypass_loss_mdB; + phy->pdata->elna_ctrl.elna_1_control_en = + init_param->elna_rx1_gpo0_control_enable; + phy->pdata->elna_ctrl.elna_2_control_en = + init_param->elna_rx2_gpo1_control_enable; + phy->pdata->elna_ctrl.elna_in_gaintable_all_index_en = + init_param->elna_gaintable_all_index_enable; + + /* Digital Interface Control */ + phy->pdata->dig_interface_tune_skipmode = + (init_param->digital_interface_tune_skip_mode); + phy->pdata->dig_interface_tune_fir_disable = + (init_param->digital_interface_tune_fir_disable); + phy->pdata->port_ctrl.pp_conf[0] = (init_param->pp_tx_swap_enable << 7); + phy->pdata->port_ctrl.pp_conf[0] |= (init_param->pp_rx_swap_enable << 6); + phy->pdata->port_ctrl.pp_conf[0] |= (init_param->tx_channel_swap_enable << 5); + phy->pdata->port_ctrl.pp_conf[0] |= (init_param->rx_channel_swap_enable << 4); + phy->pdata->port_ctrl.pp_conf[0] |= (init_param->rx_frame_pulse_mode_enable << + 3); + phy->pdata->port_ctrl.pp_conf[0] |= (init_param->two_t_two_r_timing_enable << + 2); + phy->pdata->port_ctrl.pp_conf[0] |= (init_param->invert_data_bus_enable << 1); + phy->pdata->port_ctrl.pp_conf[0] |= (init_param->invert_data_clk_enable << 0); + phy->pdata->port_ctrl.pp_conf[1] = (init_param->fdd_alt_word_order_enable << 7); + phy->pdata->port_ctrl.pp_conf[1] |= (init_param->invert_rx_frame_enable << 2); + phy->pdata->port_ctrl.pp_conf[2] = (init_param->fdd_rx_rate_2tx_enable << 7); + phy->pdata->port_ctrl.pp_conf[2] |= (init_param->swap_ports_enable << 6); + phy->pdata->port_ctrl.pp_conf[2] |= (init_param->single_data_rate_enable << 5); + phy->pdata->port_ctrl.pp_conf[2] |= (init_param->lvds_mode_enable << 4); + phy->pdata->port_ctrl.pp_conf[2] |= (init_param->half_duplex_mode_enable << 3); + phy->pdata->port_ctrl.pp_conf[2] |= (init_param->single_port_mode_enable << 2); + phy->pdata->port_ctrl.pp_conf[2] |= (init_param->full_port_enable << 1); + phy->pdata->port_ctrl.pp_conf[2] |= (init_param->full_duplex_swap_bits_enable << + 0); + phy->pdata->port_ctrl.pp_conf[1] |= (init_param->delay_rx_data & 0x3); + phy->pdata->port_ctrl.rx_clk_data_delay = DATA_CLK_DELAY( + init_param->rx_data_clock_delay); + phy->pdata->port_ctrl.rx_clk_data_delay |= RX_DATA_DELAY( + init_param->rx_data_delay); + phy->pdata->port_ctrl.tx_clk_data_delay = FB_CLK_DELAY( + init_param->tx_fb_clock_delay); + phy->pdata->port_ctrl.tx_clk_data_delay |= TX_DATA_DELAY( + init_param->tx_data_delay); + phy->pdata->port_ctrl.lvds_bias_ctrl = ((init_param->lvds_bias_mV - 75) / 75) & + 0x7; + phy->pdata->port_ctrl.lvds_bias_ctrl |= + (init_param->lvds_rx_onchip_termination_enable << 5); + phy->pdata->rx1rx2_phase_inversion_en = init_param->rx1rx2_phase_inversion_en; + + /* GPO Control */ + phy->pdata->gpo_ctrl.gpo_manual_mode_en = + init_param->gpo_manual_mode_enable; + phy->pdata->gpo_ctrl.gpo_manual_mode_enable_mask = + init_param->gpo_manual_mode_enable_mask; + phy->pdata->gpo_ctrl.gpo0_inactive_state_high_en = + init_param->gpo0_inactive_state_high_enable; + phy->pdata->gpo_ctrl.gpo1_inactive_state_high_en = + init_param->gpo1_inactive_state_high_enable; + phy->pdata->gpo_ctrl.gpo2_inactive_state_high_en = + init_param->gpo2_inactive_state_high_enable; + phy->pdata->gpo_ctrl.gpo3_inactive_state_high_en = + init_param->gpo3_inactive_state_high_enable; + + phy->pdata->gpo_ctrl.gpo0_slave_rx_en = init_param->gpo0_slave_rx_enable; + phy->pdata->gpo_ctrl.gpo0_slave_tx_en = init_param->gpo0_slave_tx_enable; + phy->pdata->gpo_ctrl.gpo1_slave_rx_en = init_param->gpo1_slave_rx_enable; + phy->pdata->gpo_ctrl.gpo1_slave_tx_en = init_param->gpo1_slave_tx_enable; + phy->pdata->gpo_ctrl.gpo2_slave_rx_en = init_param->gpo2_slave_rx_enable; + phy->pdata->gpo_ctrl.gpo2_slave_tx_en = init_param->gpo2_slave_tx_enable; + phy->pdata->gpo_ctrl.gpo3_slave_rx_en = init_param->gpo3_slave_rx_enable; + phy->pdata->gpo_ctrl.gpo3_slave_tx_en = init_param->gpo3_slave_tx_enable; + + phy->pdata->gpo_ctrl.gpo0_rx_delay_us = init_param->gpo0_rx_delay_us; + phy->pdata->gpo_ctrl.gpo0_tx_delay_us = init_param->gpo0_tx_delay_us; + phy->pdata->gpo_ctrl.gpo1_rx_delay_us = init_param->gpo1_rx_delay_us; + phy->pdata->gpo_ctrl.gpo1_tx_delay_us = init_param->gpo1_tx_delay_us; + phy->pdata->gpo_ctrl.gpo2_rx_delay_us = init_param->gpo2_rx_delay_us; + phy->pdata->gpo_ctrl.gpo2_tx_delay_us = init_param->gpo2_tx_delay_us; + phy->pdata->gpo_ctrl.gpo3_rx_delay_us = init_param->gpo3_rx_delay_us; + phy->pdata->gpo_ctrl.gpo3_tx_delay_us = init_param->gpo3_tx_delay_us; + + /* Tx Monitor Control */ + phy->pdata->txmon_ctrl.low_high_gain_threshold_mdB = + init_param->low_high_gain_threshold_mdB; + phy->pdata->txmon_ctrl.low_gain_dB = init_param->low_gain_dB; + phy->pdata->txmon_ctrl.high_gain_dB = init_param->high_gain_dB; + phy->pdata->txmon_ctrl.tx_mon_track_en = init_param->tx_mon_track_en; + phy->pdata->txmon_ctrl.one_shot_mode_en = init_param->one_shot_mode_en; + phy->pdata->txmon_ctrl.tx_mon_delay = init_param->tx_mon_delay; + phy->pdata->txmon_ctrl.tx_mon_duration = init_param->tx_mon_duration; + phy->pdata->txmon_ctrl.tx1_mon_front_end_gain = + init_param->tx1_mon_front_end_gain; + phy->pdata->txmon_ctrl.tx2_mon_front_end_gain = + init_param->tx2_mon_front_end_gain; + phy->pdata->txmon_ctrl.tx1_mon_lo_cm = init_param->tx1_mon_lo_cm; + phy->pdata->txmon_ctrl.tx2_mon_lo_cm = init_param->tx2_mon_lo_cm; + + phy->pdata->debug_mode = true; + + gpio_get(&phy->gpio_desc_resetb, &init_param->gpio_resetb); + /* Optional: next three GPIOs are used for MCS synchronization */ + gpio_get_optional(&phy->gpio_desc_sync, &init_param->gpio_sync); + gpio_get_optional(&phy->gpio_desc_cal_sw1, &init_param->gpio_cal_sw1); + gpio_get_optional(&phy->gpio_desc_cal_sw2, &init_param->gpio_cal_sw2); + + gpio_direction_output(phy->gpio_desc_resetb, 0); + + spi_init(&phy->spi, &init_param->spi_param); + + phy->pdata->port_ctrl.digital_io_ctrl = 0; + phy->pdata->port_ctrl.lvds_invert[0] = init_param->lvds_invert1_control; + phy->pdata->port_ctrl.lvds_invert[1] = init_param->lvds_invert2_control; + + if (AD9364_DEVICE) { + phy->pdata->rx2tx2 = false; + phy->pdata->rx1tx1_mode_use_rx_num = 1; + phy->pdata->rx1tx1_mode_use_tx_num = 1; + } + + phy->rx_eq_2tx = false; + + phy->current_table = -1; + phy->bypass_tx_fir = true; + phy->bypass_rx_fir = true; + phy->rate_governor = 1; + phy->rfdc_track_en = true; + phy->bbdc_track_en = true; + phy->quad_track_en = true; + + phy->gt_info = ad9361_adi_gt_info; + + phy->bist_loopback_mode = 0; + phy->bist_config = 0; + phy->bist_prbs_mode = BIST_DISABLE; + phy->bist_tone_mode = BIST_DISABLE; + phy->bist_tone_freq_Hz = 0; + phy->bist_tone_level_dB = 0; + phy->bist_tone_mask = 0; + + ad9361_reset(phy); + + ret = ad9361_spi_read(phy->spi, REG_PRODUCT_ID); + if ((ret & PRODUCT_ID_MASK) != PRODUCT_ID_9361) { + printf("%s : Unsupported PRODUCT_ID 0x%X", __func__, (unsigned int)ret); + ret = -ENODEV; + goto out; + } + rev = ret & REV_MASK; + + phy->ad9361_rfpll_ext_recalc_rate = init_param->ad9361_rfpll_ext_recalc_rate; + phy->ad9361_rfpll_ext_round_rate = init_param->ad9361_rfpll_ext_round_rate; + phy->ad9361_rfpll_ext_set_rate = init_param->ad9361_rfpll_ext_set_rate; + + ret = ad9361_register_clocks(phy); + if (ret < 0) + goto out; + + ret = ad9361_setup(phy); + if (ret < 0) + goto out_clk; + + printf("%s : AD936x Rev %d successfully initialized\n", __func__, (int)rev); + + *ad9361_phy = phy; + + return 0; + +out_clk: + ad9361_unregister_clocks(phy); +out: + free(phy->clk_refin); + free(phy->pdata); + free(phy); + printf("%s : AD936x initialization error\n", __func__); + + return -ENODEV; +} + +/** + * Free the allocated resources. + * @param phy The AD9361 current state structure. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_remove(struct ad9361_rf_phy *phy) +{ + ad9361_unregister_clocks(phy); + spi_remove(phy->spi); + gpio_remove(phy->gpio_desc_resetb); + gpio_remove(phy->gpio_desc_sync); + gpio_remove(phy->gpio_desc_cal_sw1); + gpio_remove(phy->gpio_desc_cal_sw2); + free(phy->clk_refin); + free(phy->pdata); + free(phy); + + return 0; +} + +/** + * Set the Enable State Machine (ENSM) mode. + * @param phy The AD9361 current state structure. + * @param mode The ENSM mode. + * Accepted values: + * ENSM_MODE_TX + * ENSM_MODE_RX + * ENSM_MODE_ALERT + * ENSM_MODE_FDD + * ENSM_MODE_WAIT + * ENSM_MODE_SLEEP + * ENSM_MODE_PINCTRL + * ENSM_MODE_PINCTRL_FDD_INDEP + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_set_en_state_machine_mode (struct ad9361_rf_phy *phy, + uint32_t mode) +{ + int32_t ret; + uint8_t ensm_state; + bool pinctrl = false; + + phy->pdata->fdd_independent_mode = false; + + switch (mode) { + case ENSM_MODE_TX: + ensm_state = ENSM_STATE_TX; + break; + case ENSM_MODE_RX: + ensm_state = ENSM_STATE_RX; + break; + case ENSM_MODE_ALERT: + ensm_state = ENSM_STATE_ALERT; + break; + case ENSM_MODE_FDD: + ensm_state = ENSM_STATE_FDD; + break; + case ENSM_MODE_WAIT: + ensm_state = ENSM_STATE_SLEEP_WAIT; + break; + case ENSM_MODE_SLEEP: + ensm_state = ENSM_STATE_SLEEP; + break; + case ENSM_MODE_PINCTRL: + ensm_state = ENSM_STATE_SLEEP_WAIT; + pinctrl = true; + break; + case ENSM_MODE_PINCTRL_FDD_INDEP: + ensm_state = ENSM_STATE_FDD; + phy->pdata->fdd_independent_mode = true; + break; + default: + return -EINVAL; + } + + ad9361_set_ensm_mode(phy, phy->pdata->fdd, pinctrl); + ret = ad9361_ensm_set_state(phy, ensm_state, pinctrl); + + return ret; +} + +/** + * Get the Enable State Machine (ENSM) mode. + * @param phy The AD9361 current state structure. + * @param mode A variable to store the selected ENSM mode. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_en_state_machine_mode (struct ad9361_rf_phy *phy, + uint32_t *mode) +{ + uint8_t ensm_state; + bool pinctrl = false; + int32_t ret; + + ensm_state = ad9361_spi_read(phy->spi, REG_STATE); + ensm_state &= ENSM_STATE(~0); + ret = ad9361_spi_read(phy->spi, REG_ENSM_CONFIG_1); + if ((ret & ENABLE_ENSM_PIN_CTRL) == ENABLE_ENSM_PIN_CTRL) + pinctrl = true; + + switch (ensm_state) { + case ENSM_STATE_TX: + *mode = ENSM_MODE_TX; + break; + case ENSM_STATE_RX: + *mode = ENSM_MODE_RX; + break; + case ENSM_STATE_ALERT: + *mode = ENSM_MODE_ALERT; + break; + case ENSM_STATE_FDD: + if (phy->pdata->fdd_independent_mode) + *mode = ENSM_MODE_PINCTRL_FDD_INDEP; + else + *mode = ENSM_MODE_FDD; + break; + case ENSM_STATE_SLEEP_WAIT: + if (pinctrl) + *mode = ENSM_MODE_PINCTRL; + else + *mode = ENSM_MODE_WAIT; + break; + case ENSM_STATE_SLEEP: + *mode = ENSM_MODE_SLEEP; + break; + default: + return -EINVAL; + } + + return 0; +} + +/** + * Set the receive RF gain for the selected channel. + * @param phy The AD9361 current state structure. + * @param ch The desired channel number (RX1, RX2). + * Accepted values in 2x2 mode: + * RX1 (0) + * RX2 (1) + * Accepted values in 1x1 mode: + * RX1 (0) + * @param gain_db The RF gain (dB). + * Example: + * 10 (10 dB) + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_rx_rf_gain (struct ad9361_rf_phy *phy, + uint8_t ch, int32_t gain_db) +{ + struct rf_rx_gain rx_gain = {0, 0, 0, 0, 0, 0, 0, 0, 0}; + int32_t ret = 0; + + rx_gain.gain_db = gain_db; + ret = ad9361_set_rx_gain(phy, + ad9361_1rx1tx_channel_map(phy, false, + ch + 1), &rx_gain); + + return ret; +} + +/** + * Get current receive RF gain for the selected channel. + * @param phy The AD9361 current state structure. + * @param ch The desired channel number (RX1, RX2). + * Accepted values in 2x2 mode: + * RX1 (0) + * RX2 (1) + * Accepted values in 1x1 mode: + * RX1 (0) + * @param gain_db A variable to store the RF gain (dB). + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_rx_rf_gain (struct ad9361_rf_phy *phy, + uint8_t ch, int32_t *gain_db) +{ + struct rf_rx_gain rx_gain = {0, 0, 0, 0, 0, 0, 0, 0, 0}; + int32_t ret = 0; + + ret = ad9361_get_rx_gain(phy, ad9361_1rx1tx_channel_map(phy, + false, ch + 1), &rx_gain); + + *gain_db = rx_gain.gain_db; + + return ret; +} + +/** + * Set the RX RF bandwidth. + * @param phy The AD9361 current state structure. + * @param bandwidth_hz The desired bandwidth (Hz). + * Example: + * 18000000 (18 MHz) + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_set_rx_rf_bandwidth (struct ad9361_rf_phy *phy, + uint32_t bandwidth_hz) +{ + int32_t ret = 0; + + bandwidth_hz = ad9361_validate_rf_bw(phy, bandwidth_hz); + + if (phy->current_rx_bw_Hz != bandwidth_hz) + ret = ad9361_update_rf_bandwidth(phy, bandwidth_hz, + phy->current_tx_bw_Hz); + else + ret = 0; + + return ret; +} + +/** + * Get the RX RF bandwidth. + * @param phy The AD9361 current state structure. + * @param bandwidth_hz A variable to store the bandwidth value (Hz). + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_rx_rf_bandwidth (struct ad9361_rf_phy *phy, + uint32_t *bandwidth_hz) +{ + *bandwidth_hz = phy->current_rx_bw_Hz; + + return 0; +} + +/** + * Set the RX sampling frequency. + * @param phy The AD9361 current state structure. + * @param sampling_freq_hz The desired frequency (Hz). + * Example: + * 30720000 (30.72 MHz) + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_set_rx_sampling_freq (struct ad9361_rf_phy *phy, + uint32_t sampling_freq_hz) +{ + int32_t ret; + uint32_t rx[6], tx[6]; + + ret = ad9361_calculate_rf_clock_chain(phy, sampling_freq_hz, + phy->rate_governor, rx, tx); + if (ret < 0) + return ret; + + ad9361_set_trx_clock_chain(phy, rx, tx); + + ret = ad9361_update_rf_bandwidth(phy, phy->current_rx_bw_Hz, + phy->current_tx_bw_Hz); + + return ret; +} + +/** + * Get current RX sampling frequency. + * @param phy The AD9361 current state structure. + * @param sampling_freq_hz A variable to store the frequency value (Hz). + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_rx_sampling_freq (struct ad9361_rf_phy *phy, + uint32_t *sampling_freq_hz) +{ + *sampling_freq_hz = (uint32_t)clk_get_rate(phy, + phy->ref_clk_scale[RX_SAMPL_CLK]); + + return 0; +} + +/** + * Set the RX LO frequency. + * @param phy The AD9361 current state structure. + * @param lo_freq_hz The desired frequency (Hz). + * Example: + * 2400000000 (2.4 GHz) + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_set_rx_lo_freq (struct ad9361_rf_phy *phy, + uint64_t lo_freq_hz) +{ + int32_t ret; + + ret = clk_set_rate(phy, phy->ref_clk_scale[RX_RFPLL], + ad9361_to_clk(lo_freq_hz)); + + return ret; +} + +/** + * Get current RX LO frequency. + * @param phy The AD9361 current state structure. + * @param lo_freq_hz A variable to store the frequency value (Hz). + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_rx_lo_freq (struct ad9361_rf_phy *phy, + uint64_t *lo_freq_hz) +{ + *lo_freq_hz = ad9361_from_clk(clk_get_rate(phy, + phy->ref_clk_scale[RX_RFPLL])); + + return 0; +} + +/** + * Switch between internal and external LO. + * @param phy The AD9361 state structure. + * @param int_ext The selected lo (INT_LO, EXT_LO). + * Accepted values: + * INT_LO + * EXT_LO + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_rx_lo_int_ext(struct ad9361_rf_phy *phy, uint8_t int_ext) +{ + if ((phy->dev_sel == ID_AD9363A) && (int_ext == EXT_LO)) { + printf("%s : EXT_LO is not supported by AD9363!\n", __func__); + return -1; + } + + phy->pdata->use_ext_rx_lo = int_ext; + + return ad9361_clk_mux_set_parent(phy->ref_clk_scale[RX_RFPLL], int_ext); +} + +/** + * Get the RSSI for the selected channel. + * @param phy The AD9361 current state structure. + * @param ch The desired channel (RX1, RX2). + * Accepted values in 2x2 mode: + * RX1 (0) + * RX2 (1) + * Accepted values in 1x1 mode: + * RX1 (0) + * @param rssi A variable to store the RSSI. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_rx_rssi (struct ad9361_rf_phy *phy, + uint8_t ch, struct rf_rssi *rssi) +{ + int32_t ret; + + rssi->ant = ad9361_1rx1tx_channel_map(phy, false, ch + 1); + rssi->duration = 1; + ret = ad9361_read_rssi(phy, rssi); + + return ret; +} + +/** + * Set the gain control mode for the selected channel. + * @param phy The AD9361 current state structure. + * @param ch The desired channel (RX1, RX2). + * Accepted values in 2x2 mode: + * RX1 (0) + * RX2 (1) + * Accepted values in 1x1 mode: + * RX1 (0) + * @param gc_mode The gain control mode (manual, fast_attack, slow_attack, + * hybrid). + * Accepted values: + * RF_GAIN_MGC (manual) + * RF_GAIN_FASTATTACK_AGC (fast_attack) + * RF_GAIN_SLOWATTACK_AGC (slow_attack) + * RF_GAIN_HYBRID_AGC (hybrid) + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_rx_gain_control_mode (struct ad9361_rf_phy *phy, + uint8_t ch, uint8_t gc_mode) +{ + struct rf_gain_ctrl gc = {0, 0}; + + gc.ant = ad9361_1rx1tx_channel_map(phy, false, ch + 1); + gc.mode = phy->agc_mode[ch] = gc_mode; + + ad9361_set_gain_ctrl_mode(phy, &gc); + + return 0; +} + +/** + * Get the gain control mode for the selected channel. + * @param phy The AD9361 current state structure. + * @param ch The desired channel (RX1, RX2). + * Accepted values: + * RX1 (0) + * RX2 (1) + * @param gc_mode A variable to store the gain control mode. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_rx_gain_control_mode (struct ad9361_rf_phy *phy, + uint8_t ch, uint8_t *gc_mode) +{ + *gc_mode = phy->agc_mode[ch]; + + return 0; +} + +/** + * Set the RX FIR filter configuration. + * @param phy The AD9361 current state structure. + * @param fir_cfg FIR filter configuration. + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_set_rx_fir_config (struct ad9361_rf_phy *phy, + AD9361_RXFIRConfig fir_cfg) +{ + int32_t ret; + + phy->rx_fir_dec = fir_cfg.rx_dec; + ret = ad9361_load_fir_filter_coef(phy, (enum fir_dest)(fir_cfg.rx | FIR_IS_RX), + fir_cfg.rx_gain, fir_cfg.rx_coef_size, fir_cfg.rx_coef); + + return ret; +} + +/** + * Get the RX FIR filter configuration. + * @param phy The AD9361 current state structure. + * @param rx_ch The selected RX channel (RX1, RX2). + * Accepted values: + * RX1 (0) + * RX2 (1) + * @param fir_cfg FIR filter configuration output file. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_rx_fir_config(struct ad9361_rf_phy *phy, uint8_t rx_ch, + AD9361_RXFIRConfig *fir_cfg) +{ + int32_t ret; + uint32_t fir_conf; + uint8_t index; + + rx_ch += 1; + + ret = ad9361_spi_read(phy->spi, REG_RX_FILTER_CONFIG); + if(ret < 0) + return ret; + fir_conf = ret; + + fir_cfg->rx_coef_size = (((fir_conf & FIR_NUM_TAPS(7)) >> 5) + 1) * 16; + + ret = ad9361_spi_read(phy->spi, REG_RX_FILTER_GAIN); + if(ret < 0) + return ret; + fir_cfg->rx_gain = -6 * (ret & FILTER_GAIN(3)) + 6; + fir_cfg->rx = rx_ch; + + fir_conf &= ~FIR_SELECT(3); + fir_conf |= FIR_SELECT(rx_ch) | FIR_START_CLK; + ad9361_spi_write(phy->spi, REG_RX_FILTER_CONFIG, fir_conf); + + for(index = 0; index < 128; index++) { + ad9361_spi_write(phy->spi, REG_RX_FILTER_COEF_ADDR, index); + ret = ad9361_spi_read(phy->spi, REG_RX_FILTER_COEF_READ_DATA_1); + if(ret < 0) + return ret; + fir_cfg->rx_coef[index] = ret; + ret = ad9361_spi_read(phy->spi, REG_RX_FILTER_COEF_READ_DATA_2); + if(ret < 0) + return ret; + fir_cfg->rx_coef[index] |= (ret << 8); + } + + fir_conf &= ~FIR_START_CLK; + ad9361_spi_write(phy->spi, REG_RX_FILTER_CONFIG, fir_conf); + + fir_cfg->rx_dec = phy->rx_fir_dec; + + return 0; +} + +/** + * Enable/disable the RX FIR filter. + * @param phy The AD9361 current state structure. + * @param en_dis The option (ENABLE, DISABLE). + * Accepted values: + * ENABLE (1) + * DISABLE (0) + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_set_rx_fir_en_dis (struct ad9361_rf_phy *phy, + uint8_t en_dis) +{ + int32_t ret = 0; + + if(phy->bypass_rx_fir == !en_dis) + return ret; + + phy->bypass_rx_fir = !en_dis; + ret = ad9361_validate_enable_fir(phy); + if (ret < 0) { + phy->bypass_rx_fir = true; + } + + return ret; +} + +/** + * Get the status of the RX FIR filter. + * @param phy The AD9361 current state structure. + * @param en_dis The enable/disable status buffer. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_rx_fir_en_dis (struct ad9361_rf_phy *phy, + uint8_t *en_dis) +{ + *en_dis = !phy->bypass_rx_fir; + + return 0; +} + +/** + * Enable/disable the RX RFDC Tracking. + * @param phy The AD9361 current state structure. + * @param en_dis The option (ENABLE, DISABLE). + * Accepted values: + * ENABLE (1) + * DISABLE (0) + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_rx_rfdc_track_en_dis (struct ad9361_rf_phy *phy, + uint8_t en_dis) +{ + int32_t ret = 0; + + if(phy->rfdc_track_en == en_dis) + return ret; + + phy->rfdc_track_en = en_dis; + ret = ad9361_tracking_control(phy, phy->bbdc_track_en, + phy->rfdc_track_en, phy->quad_track_en); + + return ret; +} + +/** + * Get the status of the RX RFDC Tracking. + * @param phy The AD9361 current state structure. + * @param en_dis The enable/disable status buffer. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_rx_rfdc_track_en_dis (struct ad9361_rf_phy *phy, + uint8_t *en_dis) +{ + *en_dis = phy->rfdc_track_en; + + return 0; +} + +/** + * Enable/disable the RX BasebandDC Tracking. + * @param phy The AD9361 current state structure. + * @param en_dis The option (ENABLE, DISABLE). + * Accepted values: + * ENABLE (1) + * DISABLE (0) + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_rx_bbdc_track_en_dis (struct ad9361_rf_phy *phy, + uint8_t en_dis) +{ + int32_t ret = 0; + + if(phy->bbdc_track_en == en_dis) + return ret; + + phy->bbdc_track_en = en_dis; + ret = ad9361_tracking_control(phy, phy->bbdc_track_en, + phy->rfdc_track_en, phy->quad_track_en); + + return ret; +} + +/** + * Get the status of the RX BasebandDC Tracking. + * @param phy The AD9361 current state structure. + * @param en_dis The enable/disable status buffer. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_rx_bbdc_track_en_dis (struct ad9361_rf_phy *phy, + uint8_t *en_dis) +{ + *en_dis = phy->bbdc_track_en; + + return 0; +} + +/** + * Enable/disable the RX Quadrature Tracking. + * @param phy The AD9361 current state structure. + * @param en_dis The option (ENABLE, DISABLE). + * Accepted values: + * ENABLE (1) + * DISABLE (0) + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_rx_quad_track_en_dis (struct ad9361_rf_phy *phy, + uint8_t en_dis) +{ + int32_t ret = 0; + + if(phy->quad_track_en == en_dis) + return ret; + + phy->quad_track_en = en_dis; + ret = ad9361_tracking_control(phy, phy->bbdc_track_en, + phy->rfdc_track_en, phy->quad_track_en); + + return ret; +} + +/** + * Get the status of the RX Quadrature Tracking. + * @param phy The AD9361 current state structure. + * @param en_dis The enable/disable status buffer. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_rx_quad_track_en_dis (struct ad9361_rf_phy *phy, + uint8_t *en_dis) +{ + *en_dis = phy->quad_track_en; + + return 0; +} + +/** + * Set the RX RF input port. + * @param phy The AD9361 current state structure. + * @param mode The RF port. + * Accepted values: + * A_BALANCED (0 - (RX1A_N & RX1A_P) and (RX2A_N & RX2A_P) enabled; balanced) + * B_BALANCED (1 - (RX1B_N & RX1B_P) and (RX2B_N & RX2B_P) enabled; balanced) + * C_BALANCED (2 - (RX1C_N & RX1C_P) and (RX2C_N & RX2C_P) enabled; balanced) + * A_N (3 - RX1A_N and RX2A_N enabled; unbalanced) + * A_P (4 - RX1A_P and RX2A_P enabled; unbalanced) + * B_N (5 - RX1B_N and RX2B_N enabled; unbalanced) + * B_P (6 - RX1B_P and RX2B_P enabled; unbalanced) + * C_N (7 - RX1C_N and RX2C_N enabled; unbalanced) + * C_P (8 - RX1C_P and RX2C_P enabled; unbalanced) + * TX_MON1 (9 - TX_MONITOR1) + * TX_MON2 (10 - TX_MONITOR2) + * TX_MON1_2 (11 - TX_MONITOR1 & TX_MONITOR2) + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_rx_rf_port_input (struct ad9361_rf_phy *phy, + uint32_t mode) +{ + int32_t ret; + + phy->pdata->rf_rx_input_sel = mode; + + ret = ad9361_rf_port_setup(phy, false, + phy->pdata->rf_rx_input_sel, + phy->pdata->rf_tx_output_sel); + + return ret; +} + +/** + * Get the selected RX RF input port. + * @param phy The AD9361 current state structure. + * @param mode The RF port. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_rx_rf_port_input (struct ad9361_rf_phy *phy, + uint32_t *mode) +{ + *mode = phy->pdata->rf_rx_input_sel; + + return 0; +} + +/** + * Store RX fastlock profile. + * To create a profile tune the synthesizer (ad9361_set_rx_lo_freq()) and then + * call this function specifying the target profile number. + * @param phy The AD9361 state structure. + * @param profile The profile number (0 - 7). + * Accepted values: + * 0 - 7 + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_rx_fastlock_store(struct ad9361_rf_phy *phy, uint32_t profile) +{ + return ad9361_fastlock_store(phy, 0, profile); +} + +/** + * Recall specified RX fastlock profile. + * When in fastlock pin select mode (init_param->rx_fastlock_pincontrol_enable), + * the function needs to be called before then the pin-control can be used. + * @param phy The AD9361 state structure. + * @param profile The profile number (0 - 7). + * Accepted values: + * 0 - 7 + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_rx_fastlock_recall(struct ad9361_rf_phy *phy, uint32_t profile) +{ + return ad9361_fastlock_recall(phy, 0, profile); +} + +/** + * Load RX fastlock profile. A previously saved profile can be loaded in any + * of the 8 available slots. + * @param phy The AD9361 state structure. + * @param profile The profile number (0 - 7). + * Accepted values: + * 0 - 7 + * @param values Fastlock profile program data. + * Example: + * val0,val1,val2,…,val15 + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_rx_fastlock_load(struct ad9361_rf_phy *phy, uint32_t profile, + uint8_t *values) +{ + return ad9361_fastlock_load(phy, 0, profile, values); +} + +/** + * Save RX fastlock profile. In order to use more than 8 Profiles, an existing + * profile can be read back and stored by the user application. + * @param phy The AD9361 state structure. + * @param profile The profile number (0 - 7). + * Accepted values: + * 0 - 7 + * @param values Fastlock profile program data. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_rx_fastlock_save(struct ad9361_rf_phy *phy, uint32_t profile, + uint8_t *values) +{ + return ad9361_fastlock_save(phy, 0, profile, values); +} + +/** + * Power down the RX Local Oscillator. + * @param phy The AD9361 state structure. + * @param option The option (ON, OFF). + * Accepted values: + * ON (0) + * OFF (1) + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_rx_lo_powerdown(struct ad9361_rf_phy *phy, uint8_t option) +{ + return ad9361_synth_lo_powerdown(phy, option ? LO_OFF : LO_ON, LO_DONTCARE); +} + +/** + * Get the RX Local Oscillator power status. + * @param phy The AD9361 state structure. + * @param option Store the option (ON, OFF). + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_rx_lo_power(struct ad9361_rf_phy *phy, uint8_t *option) +{ + *option = !(phy->cached_synth_pd[1] & RX_LO_POWER_DOWN) ? OFF : ON; + + return 0; +} + +/** + * Set the transmit attenuation for the selected channel. + * @param phy The AD9361 current state structure. + * @param ch The desired channel number (TX1, TX2). + * Accepted values in 2x2 mode: + * TX1 (0) + * TX2 (1) + * Accepted values in 1x1 mode: + * TX1 (0) + * @param attenuation_mdb The attenuation (mdB). + * Example: + * 10000 (10 dB) + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_tx_attenuation (struct ad9361_rf_phy *phy, + uint8_t ch, uint32_t attenuation_mdb) +{ + int32_t ret; + int32_t channel; + + channel = ad9361_1rx1tx_channel_map(phy, true, ch); + ret = ad9361_set_tx_atten(phy, attenuation_mdb, + channel == 0, channel == 1, + !phy->pdata->update_tx_gain_via_alert); + + return ret; +} + +/** + * Get current transmit attenuation for the selected channel. + * @param phy The AD9361 current state structure. + * @param ch The desired channel number (TX1, TX2). + * Accepted values in 2x2 mode: + * TX1 (0) + * TX2 (1) + * Accepted values in 1x1 mode: + * TX1 (0) + * @param attenuation_mdb A variable to store the attenuation value (mdB). + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_tx_attenuation (struct ad9361_rf_phy *phy, + uint8_t ch, uint32_t *attenuation_mdb) +{ + int32_t ret; + + ret = ad9361_get_tx_atten(phy, + ad9361_1rx1tx_channel_map(phy, true, + ch + 1)); + + if(ret < 0) + return ret; + *attenuation_mdb = ret; + + return 0; +} + +/** + * Set the TX RF bandwidth. + * @param phy The AD9361 current state structure. + * @param bandwidth_hz The desired bandwidth (Hz). + * Example: + * 18000000 (18 MHz) + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_set_tx_rf_bandwidth (struct ad9361_rf_phy *phy, + uint32_t bandwidth_hz) +{ + int32_t ret = 0; + + bandwidth_hz = ad9361_validate_rf_bw(phy, bandwidth_hz); + + if (phy->current_tx_bw_Hz != bandwidth_hz) + ret = ad9361_update_rf_bandwidth(phy, + phy->current_rx_bw_Hz, bandwidth_hz); + else + ret = 0; + + return ret; +} + +/** + * Get the TX RF bandwidth. + * @param phy The AD9361 current state structure. + * @param bandwidth_hz A variable to store the bandwidth value (Hz). + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_tx_rf_bandwidth (struct ad9361_rf_phy *phy, + uint32_t *bandwidth_hz) +{ + *bandwidth_hz = phy->current_tx_bw_Hz; + + return 0; +} + +/** + * Set the TX sampling frequency. + * @param phy The AD9361 current state structure. + * @param sampling_freq_hz The desired frequency (Hz). + * Example: + * 30720000 (30.72 MHz) + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_set_tx_sampling_freq (struct ad9361_rf_phy *phy, + uint32_t sampling_freq_hz) +{ + int32_t ret; + uint32_t rx[6], tx[6]; + + ret = ad9361_calculate_rf_clock_chain(phy, sampling_freq_hz, + phy->rate_governor, rx, tx); + if (ret < 0) + return ret; + + ad9361_set_trx_clock_chain(phy, rx, tx); + + ret = ad9361_update_rf_bandwidth(phy, phy->current_rx_bw_Hz, + phy->current_tx_bw_Hz); + + return ret; +} + +/** + * Get current TX sampling frequency. + * @param phy The AD9361 current state structure. + * @param sampling_freq_hz A variable to store the frequency value (Hz). + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_tx_sampling_freq (struct ad9361_rf_phy *phy, + uint32_t *sampling_freq_hz) +{ + *sampling_freq_hz = (uint32_t)clk_get_rate(phy, + phy->ref_clk_scale[TX_SAMPL_CLK]); + + return 0; +} + +/** + * Set the TX LO frequency. + * @param phy The AD9361 current state structure. + * @param lo_freq_hz The desired frequency (Hz). + * Example: + * 2400000000 (2.4 GHz) + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_set_tx_lo_freq (struct ad9361_rf_phy *phy, + uint64_t lo_freq_hz) +{ + int32_t ret; + + ret = clk_set_rate(phy, phy->ref_clk_scale[TX_RFPLL], + ad9361_to_clk(lo_freq_hz)); + + return ret; +} + +/** + * Get current TX LO frequency. + * @param phy The AD9361 current state structure. + * @param lo_freq_hz A variable to store the frequency value (Hz). + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_tx_lo_freq (struct ad9361_rf_phy *phy, + uint64_t *lo_freq_hz) +{ + *lo_freq_hz = ad9361_from_clk(clk_get_rate(phy, + phy->ref_clk_scale[TX_RFPLL])); + + return 0; +} + +/** + * Switch between internal and external LO. + * @param phy The AD9361 state structure. + * @param int_ext The selected lo (INT_LO, EXT_LO). + * Accepted values: + * INT_LO + * EXT_LO + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_tx_lo_int_ext(struct ad9361_rf_phy *phy, uint8_t int_ext) +{ + if ((phy->dev_sel == ID_AD9363A) && (int_ext == EXT_LO)) { + printf("%s : EXT_LO is not supported by AD9363!\n", __func__); + return -1; + } + + phy->pdata->use_ext_tx_lo = int_ext; + + return ad9361_clk_mux_set_parent(phy->ref_clk_scale[TX_RFPLL], int_ext); +} + +/** + * Set the TX FIR filter configuration. + * @param phy The AD9361 current state structure. + * @param fir_cfg FIR filter configuration. + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_set_tx_fir_config (struct ad9361_rf_phy *phy, + AD9361_TXFIRConfig fir_cfg) +{ + int32_t ret; + + phy->tx_fir_int = fir_cfg.tx_int; + ret = ad9361_load_fir_filter_coef(phy, (enum fir_dest)fir_cfg.tx, + fir_cfg.tx_gain, fir_cfg.tx_coef_size, fir_cfg.tx_coef); + + return ret; +} + +/** + * Get the TX FIR filter configuration. + * @param phy The AD9361 current state structure. + * @param tx_ch The selected TX channel (TX1, TX2). + * Accepted values: + * TX1 (0) + * TX2 (1) + * @param fir_cfg FIR filter configuration output file. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_tx_fir_config(struct ad9361_rf_phy *phy, uint8_t tx_ch, + AD9361_TXFIRConfig *fir_cfg) +{ + int32_t ret; + uint32_t fir_conf; + uint8_t index; + + tx_ch += 1; + + ret = ad9361_spi_read(phy->spi, REG_TX_FILTER_CONF); + if(ret < 0) + return ret; + fir_conf = ret; + fir_cfg->tx_coef_size = (((fir_conf & FIR_NUM_TAPS(7)) >> 5) + 1) * 16; + fir_cfg->tx_gain = -6 * (fir_conf & TX_FIR_GAIN_6DB); + fir_cfg->tx = tx_ch; + + fir_conf &= ~FIR_SELECT(3); + fir_conf |= FIR_SELECT(tx_ch) | FIR_START_CLK; + ad9361_spi_write(phy->spi, REG_TX_FILTER_CONF, fir_conf); + + for(index = 0; index < 128; index++) { + ad9361_spi_write(phy->spi, REG_TX_FILTER_COEF_ADDR, index); + ret = ad9361_spi_read(phy->spi, REG_TX_FILTER_COEF_READ_DATA_1); + if(ret < 0) + return ret; + fir_cfg->tx_coef[index] = ret; + ret = ad9361_spi_read(phy->spi, REG_TX_FILTER_COEF_READ_DATA_2); + if(ret < 0) + return ret; + fir_cfg->tx_coef[index] |= (ret << 8); + } + + fir_conf &= ~FIR_START_CLK; + ad9361_spi_write(phy->spi, REG_TX_FILTER_CONF, fir_conf); + + fir_cfg->tx_int = phy->tx_fir_int; + + return 0; +} + +/** + * Enable/disable the TX FIR filter. + * @param phy The AD9361 current state structure. + * @param en_dis The option (ENABLE, DISABLE). + * Accepted values: + * ENABLE (1) + * DISABLE (0) + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_set_tx_fir_en_dis (struct ad9361_rf_phy *phy, + uint8_t en_dis) +{ + int32_t ret = 0; + + if(phy->bypass_tx_fir == !en_dis) + return ret; + + phy->bypass_tx_fir = !en_dis; + ret = ad9361_validate_enable_fir(phy); + if (ret < 0) { + phy->bypass_tx_fir = true; + } + + return ret; +} + +/** + * Get the status of the TX FIR filter. + * @param phy The AD9361 current state structure. + * @param en_dis The enable/disable status buffer. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_tx_fir_en_dis (struct ad9361_rf_phy *phy, + uint8_t *en_dis) +{ + *en_dis = !phy->bypass_tx_fir; + + return 0; +} + +/** + * Get the TX RSSI for the selected channel (TX_MON should be enabled). + * @param phy The AD9361 current state structure. + * @param ch The desired channel (TX1, TX2). + * Accepted values: + * TX1 (0) + * TX2 (1) + * @param rssi_db_x_1000 A variable to store the RSSI. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_tx_rssi (struct ad9361_rf_phy *phy, + uint8_t ch, + uint32_t *rssi_db_x_1000) +{ + uint8_t reg_val_buf[3]; + uint32_t val; + int32_t ret; + + ret = ad9361_spi_readm(phy->spi, REG_TX_RSSI_LSB, + reg_val_buf, ARRAY_SIZE(reg_val_buf)); + if (ret < 0) { + return ret; + } + + switch (ch) { + case 0: + val = (reg_val_buf[2] << 1) | (reg_val_buf[0] & TX_RSSI_1); + break; + case 1: + val = (reg_val_buf[1] << 1) | ((reg_val_buf[0] & TX_RSSI_2) >> 1); + break; + default: + return -EINVAL; + } + + val *= RSSI_RESOLUTION; + + *rssi_db_x_1000 = ((val / RSSI_MULTIPLIER) * 1000) + + (val % RSSI_MULTIPLIER); + + return 0; +} + +/** + * Set the TX RF output port. + * @param phy The AD9361 current state structure. + * @param mode The RF port. + * Accepted values: + * TXA (0) + * TXB (1) + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_tx_rf_port_output (struct ad9361_rf_phy *phy, + uint32_t mode) +{ + int32_t ret; + + phy->pdata->rf_tx_output_sel = mode; + + ret = ad9361_rf_port_setup(phy, true, + phy->pdata->rf_rx_input_sel, + phy->pdata->rf_tx_output_sel); + + return ret; +} + +/** + * Get the selected TX RF output port. + * @param phy The AD9361 current state structure. + * @param mode The RF port. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_tx_rf_port_output (struct ad9361_rf_phy *phy, + uint32_t *mode) +{ + *mode = phy->pdata->rf_tx_output_sel; + + return 0; +} + +/** + * Enable/disable the auto calibration. + * @param phy The AD9361 current state structure. + * @param en_dis The option (ENABLE, DISABLE). + * Accepted values: + * ENABLE (1) + * DISABLE (0) + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_tx_auto_cal_en_dis (struct ad9361_rf_phy *phy, + uint8_t en_dis) +{ + if (en_dis == 0) + phy->auto_cal_en = 0; + else + phy->auto_cal_en = 1; + + return 0; +} + +/** + * Get the status of the auto calibration flag. + * @param phy The AD9361 current state structure. + * @param en_dis The enable/disable status buffer. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_tx_auto_cal_en_dis (struct ad9361_rf_phy *phy, + uint8_t *en_dis) +{ + *en_dis = phy->auto_cal_en; + + return 0; +} + +/** + * Store TX fastlock profile. + * To create a profile tune the synthesizer (ad9361_set_tx_lo_freq()) and then + * call this function specifying the target profile number. + * @param phy The AD9361 state structure. + * @param profile The profile number (0 - 7). + * Accepted values: + * 0 - 7 + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_tx_fastlock_store(struct ad9361_rf_phy *phy, uint32_t profile) +{ + return ad9361_fastlock_store(phy, 1, profile); +} + +/** + * Recall specified TX fastlock profile. + * When in fastlock pin select mode (init_param->tx_fastlock_pincontrol_enable), + * the function needs to be called before then the pin-control can be used. + * @param phy The AD9361 state structure. + * @param profile The profile number (0 - 7). + * Accepted values: + * 0 - 7 + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_tx_fastlock_recall(struct ad9361_rf_phy *phy, uint32_t profile) +{ + return ad9361_fastlock_recall(phy, 1, profile); +} + +/** + * Load TX fastlock profile. A previously saved profile can be loaded in any + * of the 8 available slots. + * @param phy The AD9361 state structure. + * @param profile The profile number (0 - 7). + * Accepted values: + * 0 - 7 + * @param values Fastlock profile program data. + * Example: + * val0,val1,val2,…,val15 + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_tx_fastlock_load(struct ad9361_rf_phy *phy, uint32_t profile, + uint8_t *values) +{ + return ad9361_fastlock_load(phy, 1, profile, values); +} + +/** + * Save TX fastlock profile. In order to use more than 8 Profiles, an existing + * profile can be read back and stored by the user application. + * @param phy The AD9361 state structure. + * @param profile The profile number (0 - 7). + * Accepted values: + * 0 - 7 + * @param values Fastlock profile program data. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_tx_fastlock_save(struct ad9361_rf_phy *phy, uint32_t profile, + uint8_t *values) +{ + return ad9361_fastlock_save(phy, 1, profile, values); +} + +/** + * Power down the TX Local Oscillator. + * @param phy The AD9361 state structure. + * @param option The option (ON, OFF). + * Accepted values: + * ON (0) + * OFF (1) + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_tx_lo_powerdown(struct ad9361_rf_phy *phy, uint8_t option) +{ + return ad9361_synth_lo_powerdown(phy, LO_DONTCARE, option ? LO_OFF : LO_ON); +} + +/** + * Get the TX Local Oscillator power status. + * @param phy The AD9361 state structure. + * @param option Store the option (ON, OFF). + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_tx_lo_power(struct ad9361_rf_phy *phy, uint8_t *option) +{ + *option = !(phy->cached_synth_pd[0] & TX_LO_POWER_DOWN) ? OFF : ON; + + return 0; +} + +/** + * Set the RX and TX path rates. + * @param phy The AD9361 state structure. + * @param rx_path_clks RX path rates buffer. + * @param tx_path_clks TX path rates buffer. + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_set_trx_path_clks(struct ad9361_rf_phy *phy, + uint32_t *rx_path_clks, + uint32_t *tx_path_clks) +{ + int32_t ret; + + ret = ad9361_set_trx_clock_chain(phy, rx_path_clks, tx_path_clks); + if (ret < 0) + return ret; + + ret = ad9361_update_rf_bandwidth(phy, phy->current_rx_bw_Hz, + phy->current_tx_bw_Hz); + + return ret; +} + +/** + * Get the RX and TX path rates. + * @param phy The AD9361 state structure. + * @param rx_path_clks RX path rates buffer. + * @param tx_path_clks TX path rates buffer. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_trx_path_clks(struct ad9361_rf_phy *phy, + uint32_t *rx_path_clks, + uint32_t *tx_path_clks) +{ + return ad9361_get_trx_clock_chain(phy, rx_path_clks, tx_path_clks); +} + +/** + * Set the number of channels mode. + * @param phy The AD9361 state structure. + * Note: This function also resets the device, some additional + * configurations might be necessary + * @param no_ch_mode Number of channels mode (MODE_1x1, MODE_2x2). + * Accepted values: + * MODE_1x1 (1) + * MODE_2x2 (2) + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_no_ch_mode(struct ad9361_rf_phy *phy, uint8_t no_ch_mode) +{ + switch (no_ch_mode) { + case 1: + phy->pdata->rx2tx2 = 0; + break; + case 2: + phy->pdata->rx2tx2 = 1; + break; + default: + return -EINVAL; + } + + ad9361_reset(phy); + ad9361_spi_write(phy->spi, REG_SPI_CONF, SOFT_RESET | _SOFT_RESET); + ad9361_spi_write(phy->spi, REG_SPI_CONF, 0x0); + + ad9361_clear_state(phy); + + phy->clks[TX_REFCLK]->rate = ad9361_clk_factor_recalc_rate( + phy->ref_clk_scale[TX_REFCLK], phy->clk_refin->rate); + phy->clks[TX_REFCLK]->rate = ad9361_clk_factor_recalc_rate( + phy->ref_clk_scale[TX_REFCLK], phy->clk_refin->rate); + phy->clks[RX_REFCLK]->rate = ad9361_clk_factor_recalc_rate( + phy->ref_clk_scale[RX_REFCLK], phy->clk_refin->rate); + phy->clks[BB_REFCLK]->rate = ad9361_clk_factor_recalc_rate( + phy->ref_clk_scale[BB_REFCLK], phy->clk_refin->rate); + phy->clks[BBPLL_CLK]->rate = ad9361_bbpll_recalc_rate( + phy->ref_clk_scale[BBPLL_CLK], phy->clks[BB_REFCLK]->rate); + phy->clks[ADC_CLK]->rate = ad9361_clk_factor_recalc_rate( + phy->ref_clk_scale[ADC_CLK], phy->clks[BBPLL_CLK]->rate); + phy->clks[R2_CLK]->rate = ad9361_clk_factor_recalc_rate( + phy->ref_clk_scale[R2_CLK], phy->clks[ADC_CLK]->rate); + phy->clks[R1_CLK]->rate = ad9361_clk_factor_recalc_rate( + phy->ref_clk_scale[R1_CLK], phy->clks[R2_CLK]->rate); + phy->clks[CLKRF_CLK]->rate = ad9361_clk_factor_recalc_rate( + phy->ref_clk_scale[CLKRF_CLK], phy->clks[R1_CLK]->rate); + phy->clks[RX_SAMPL_CLK]->rate = ad9361_clk_factor_recalc_rate( + phy->ref_clk_scale[RX_SAMPL_CLK], phy->clks[CLKRF_CLK]->rate); + phy->clks[DAC_CLK]->rate = ad9361_clk_factor_recalc_rate( + phy->ref_clk_scale[DAC_CLK], phy->clks[ADC_CLK]->rate); + phy->clks[T2_CLK]->rate = ad9361_clk_factor_recalc_rate( + phy->ref_clk_scale[T2_CLK], phy->clks[DAC_CLK]->rate); + phy->clks[T1_CLK]->rate = ad9361_clk_factor_recalc_rate( + phy->ref_clk_scale[T1_CLK], phy->clks[T2_CLK]->rate); + phy->clks[CLKTF_CLK]->rate = ad9361_clk_factor_recalc_rate( + phy->ref_clk_scale[CLKTF_CLK], phy->clks[T1_CLK]->rate); + phy->clks[TX_SAMPL_CLK]->rate = ad9361_clk_factor_recalc_rate( + phy->ref_clk_scale[TX_SAMPL_CLK], phy->clks[CLKTF_CLK]->rate); + phy->clks[RX_RFPLL_INT]->rate = ad9361_rfpll_int_recalc_rate( + phy->ref_clk_scale[RX_RFPLL_INT], phy->clks[RX_REFCLK]->rate); + phy->clks[TX_RFPLL_INT]->rate = ad9361_rfpll_int_recalc_rate( + phy->ref_clk_scale[TX_RFPLL_INT], phy->clks[TX_REFCLK]->rate); + phy->clks[RX_RFPLL_DUMMY]->rate = ad9361_rfpll_dummy_recalc_rate( + phy->ref_clk_scale[RX_RFPLL_DUMMY]); + phy->clks[TX_RFPLL_DUMMY]->rate = ad9361_rfpll_dummy_recalc_rate( + phy->ref_clk_scale[TX_RFPLL_DUMMY]); + phy->clks[RX_RFPLL]->rate = ad9361_rfpll_recalc_rate( + phy->ref_clk_scale[RX_RFPLL]); + phy->clks[TX_RFPLL]->rate = ad9361_rfpll_recalc_rate( + phy->ref_clk_scale[TX_RFPLL]); + + ad9361_setup(phy); +#ifndef AXI_ADC_NOT_PRESENT + /* platform specific wrapper to call ad9361_post_setup() */ + ad9361_post_setup(phy); +#endif + + return 0; +} + +/** + * Do multi chip synchronization. + * @param phy_master The AD9361 Master state structure. + * @param phy_slave The AD9361 Slave state structure. + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_do_mcs(struct ad9361_rf_phy *phy_master, + struct ad9361_rf_phy *phy_slave) +{ + uint32_t ensm_mode; + int32_t step; + int32_t reg; + + if ((phy_master->dev_sel == ID_AD9363A) || + (phy_slave->dev_sel == ID_AD9363A)) { + printf("%s : MCS is not supported by AD9363!\n", __func__); + return -1; + } + + reg = ad9361_spi_read(phy_master->spi, REG_RX_CLOCK_DATA_DELAY); + ad9361_spi_write(phy_slave->spi, REG_RX_CLOCK_DATA_DELAY, reg); + reg = ad9361_spi_read(phy_master->spi, REG_TX_CLOCK_DATA_DELAY); + ad9361_spi_write(phy_slave->spi, REG_TX_CLOCK_DATA_DELAY, reg); + + ad9361_get_en_state_machine_mode(phy_master, &ensm_mode); + + ad9361_set_en_state_machine_mode(phy_master, ENSM_MODE_ALERT); + ad9361_set_en_state_machine_mode(phy_slave, ENSM_MODE_ALERT); + + for (step = 0; step <= 5; step++) { + ad9361_mcs(phy_slave, step); + ad9361_mcs(phy_master, step); + mdelay(100); + } + + ad9361_set_en_state_machine_mode(phy_master, ensm_mode); + ad9361_set_en_state_machine_mode(phy_slave, ensm_mode); + + return 0; +} + +/** + * Enable/disable the TRX FIR filters. + * @param phy The AD9361 current state structure. + * @param en_dis The option (ENABLE, DISABLE). + * Accepted values: + * ENABLE (1) + * DISABLE (0) + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_set_trx_fir_en_dis (struct ad9361_rf_phy *phy, + uint8_t en_dis) +{ + int32_t ret = 0; + + if ((phy->bypass_rx_fir == phy->bypass_tx_fir) && + (phy->bypass_rx_fir == !en_dis)) + return ret; + + phy->bypass_rx_fir = !en_dis; + phy->bypass_tx_fir = !en_dis; + ret = ad9361_validate_enable_fir(phy); + if (ret < 0) { + phy->bypass_rx_fir = true; + phy->bypass_tx_fir = true; + } + + return ret; +} + +/** + * Set the OSR rate governor. + * @param phy The AD9361 current state structure. + * @param rate_gov OSR rate governor (highest, nominal). + * Accepted values: + * HIGHEST_OSR (0 - highest OSR) + * NOMINAL_OSR (1 - nominal) + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_trx_rate_gov (struct ad9361_rf_phy *phy, uint32_t rate_gov) +{ + if (rate_gov == 0) + phy->rate_governor = 0; + else + phy->rate_governor = 1; + + return 0; +} + +/** + * Get the OSR rate governor. + * @param phy The AD9361 current state structure. + * @param rate_gov Option buffer. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_trx_rate_gov (struct ad9361_rf_phy *phy, uint32_t *rate_gov) +{ + *rate_gov = phy->rate_governor; + + return 0; +} + +/** + * Perform the selected calibration. + * @param phy The AD9361 state structure. + * @param cal The selected calibration (TX_QUAD_CAL, RFDC_CAL). + * Accepted values: + * TX_QUAD_CAL + * RFDC_CAL + * @param arg For TX_QUAD_CAL - the optional RX phase value overwrite (set to zero). + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_do_calib(struct ad9361_rf_phy *phy, uint32_t cal, int32_t arg) +{ + return ad9361_do_calib_run(phy, cal, arg); +} + +/** + * Load and enable TRX FIR filters configurations. + * @param phy The AD9361 current state structure. + * @param rx_fir_cfg RX FIR filter configuration. + * @param tx_fir_cfg TX FIR filter configuration. + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_trx_load_enable_fir(struct ad9361_rf_phy *phy, + AD9361_RXFIRConfig rx_fir_cfg, + AD9361_TXFIRConfig tx_fir_cfg) +{ + int32_t rtx = -1, rrx = -1; + + phy->filt_rx_bw_Hz = 0; + phy->filt_tx_bw_Hz = 0; + phy->filt_valid = false; + + if (tx_fir_cfg.tx_path_clks[TX_SAMPL_FREQ]) { + memcpy(phy->filt_tx_path_clks, tx_fir_cfg.tx_path_clks, + sizeof(phy->filt_tx_path_clks)); + rtx = 0; + } + + if (rx_fir_cfg.rx_path_clks[RX_SAMPL_FREQ]) { + memcpy(phy->filt_rx_path_clks, rx_fir_cfg.rx_path_clks, + sizeof(phy->filt_rx_path_clks)); + rrx = 0; + } + + if (tx_fir_cfg.tx_bandwidth) { + phy->filt_tx_bw_Hz = tx_fir_cfg.tx_bandwidth; + } + + if (rx_fir_cfg.rx_bandwidth) { + phy->filt_rx_bw_Hz = rx_fir_cfg.rx_bandwidth; + } + + ad9361_set_tx_fir_config(phy, tx_fir_cfg); + ad9361_set_rx_fir_config(phy, rx_fir_cfg); + + if (!(rrx | rtx)) + phy->filt_valid = true; + + ad9361_set_trx_fir_en_dis(phy, 1); + + return 0; +} + +/** + * Do DCXO coarse tuning. + * @param phy The AD9361 current state structure. + * @param coarse The DCXO coarse tuning value. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_do_dcxo_tune_coarse(struct ad9361_rf_phy *phy, + uint32_t coarse) +{ + phy->pdata->dcxo_coarse = coarse; + + return ad9361_set_dcxo_tune(phy, phy->pdata->dcxo_coarse, + phy->pdata->dcxo_fine); +} + +/** + * Do DCXO fine tuning. + * @param phy The AD9361 current state structure. + * @param fine The DCXO fine tuning value. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_do_dcxo_tune_fine(struct ad9361_rf_phy *phy, + uint32_t fine) +{ + phy->pdata->dcxo_fine = fine; + + return ad9361_set_dcxo_tune(phy, phy->pdata->dcxo_coarse, + phy->pdata->dcxo_fine); +} + +/** + * Get the temperature. + * @param phy The AD9361 current state structure. + * @param temp The temperature (degrees C * 1000). + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_temperature(struct ad9361_rf_phy *phy, + int32_t *temp) +{ + *temp = ad9361_get_temp(phy); + return 0; +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361/ad9361_api.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361/ad9361_api.h new file mode 100644 index 0000000..6227bc8 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361/ad9361_api.h @@ -0,0 +1,552 @@ +/***************************************************************************//** + * @file ad9361_api.h + * @brief Header file of AD9361 API Driver. + * @author DBogdan (dragos.bogdan@analog.com) +******************************************************************************** + * Copyright 2013(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ +#ifndef AD9361_API_H_ +#define AD9361_API_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include "ad9361_util.h" +#include "gpio.h" +#include "spi.h" + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ +typedef struct { + /* Device selection */ + enum dev_id dev_sel; + /* Reference Clock */ + uint32_t reference_clk_rate; + /* Base Configuration */ + uint8_t two_rx_two_tx_mode_enable; /* adi,2rx-2tx-mode-enable */ + uint8_t one_rx_one_tx_mode_use_rx_num; /* adi,1rx-1tx-mode-use-rx-num */ + uint8_t one_rx_one_tx_mode_use_tx_num; /* adi,1rx-1tx-mode-use-tx-num */ + uint8_t frequency_division_duplex_mode_enable; /* adi,frequency-division-duplex-mode-enable */ + uint8_t frequency_division_duplex_independent_mode_enable; /* adi,frequency-division-duplex-independent-mode-enable */ + uint8_t tdd_use_dual_synth_mode_enable; /* adi,tdd-use-dual-synth-mode-enable */ + uint8_t tdd_skip_vco_cal_enable; /* adi,tdd-skip-vco-cal-enable */ + uint32_t tx_fastlock_delay_ns; /* adi,tx-fastlock-delay-ns */ + uint32_t rx_fastlock_delay_ns; /* adi,rx-fastlock-delay-ns */ + uint8_t rx_fastlock_pincontrol_enable; /* adi,rx-fastlock-pincontrol-enable */ + uint8_t tx_fastlock_pincontrol_enable; /* adi,tx-fastlock-pincontrol-enable */ + uint8_t external_rx_lo_enable; /* adi,external-rx-lo-enable */ + uint8_t external_tx_lo_enable; /* adi,external-tx-lo-enable */ + uint8_t dc_offset_tracking_update_event_mask; /* adi,dc-offset-tracking-update-event-mask */ + uint8_t dc_offset_attenuation_high_range; /* adi,dc-offset-attenuation-high-range */ + uint8_t dc_offset_attenuation_low_range; /* adi,dc-offset-attenuation-low-range */ + uint8_t dc_offset_count_high_range; /* adi,dc-offset-count-high-range */ + uint8_t dc_offset_count_low_range; /* adi,dc-offset-count-low-range */ + uint8_t split_gain_table_mode_enable; /* adi,split-gain-table-mode-enable */ + uint32_t trx_synthesizer_target_fref_overwrite_hz; /* adi,trx-synthesizer-target-fref-overwrite-hz */ + uint8_t qec_tracking_slow_mode_enable; /* adi,qec-tracking-slow-mode-enable */ + /* ENSM Control */ + uint8_t ensm_enable_pin_pulse_mode_enable; /* adi,ensm-enable-pin-pulse-mode-enable */ + uint8_t ensm_enable_txnrx_control_enable; /* adi,ensm-enable-txnrx-control-enable */ + /* LO Control */ + uint64_t rx_synthesizer_frequency_hz; /* adi,rx-synthesizer-frequency-hz */ + uint64_t tx_synthesizer_frequency_hz; /* adi,tx-synthesizer-frequency-hz */ + uint8_t tx_lo_powerdown_managed_enable; /* adi,tx-lo-powerdown-managed-enable */ + /* Rate & BW Control */ + uint32_t rx_path_clock_frequencies[6]; /* adi,rx-path-clock-frequencies */ + uint32_t tx_path_clock_frequencies[6]; /* adi,tx-path-clock-frequencies */ + uint32_t rf_rx_bandwidth_hz; /* adi,rf-rx-bandwidth-hz */ + uint32_t rf_tx_bandwidth_hz; /* adi,rf-tx-bandwidth-hz */ + /* RF Port Control */ + uint32_t rx_rf_port_input_select; /* adi,rx-rf-port-input-select */ + uint32_t tx_rf_port_input_select; /* adi,tx-rf-port-input-select */ + /* TX Attenuation Control */ + int32_t tx_attenuation_mdB; /* adi,tx-attenuation-mdB */ + uint8_t update_tx_gain_in_alert_enable; /* adi,update-tx-gain-in-alert-enable */ + /* Reference Clock Control */ + uint8_t xo_disable_use_ext_refclk_enable; /* adi,xo-disable-use-ext-refclk-enable */ + uint32_t dcxo_coarse_and_fine_tune[2]; /* adi,dcxo-coarse-and-fine-tune */ + uint32_t clk_output_mode_select; /* adi,clk-output-mode-select */ + /* Gain Control */ + uint8_t gc_rx1_mode; /* adi,gc-rx1-mode */ + uint8_t gc_rx2_mode; /* adi,gc-rx2-mode */ + uint8_t gc_adc_large_overload_thresh; /* adi,gc-adc-large-overload-thresh */ + uint8_t gc_adc_ovr_sample_size; /* adi,gc-adc-ovr-sample-size */ + uint8_t gc_adc_small_overload_thresh; /* adi,gc-adc-small-overload-thresh */ + uint16_t gc_dec_pow_measurement_duration; /* adi,gc-dec-pow-measurement-duration */ + uint8_t gc_dig_gain_enable; /* adi,gc-dig-gain-enable */ + uint16_t gc_lmt_overload_high_thresh; /* adi,gc-lmt-overload-high-thresh */ + uint16_t gc_lmt_overload_low_thresh; /* adi,gc-lmt-overload-low-thresh */ + uint8_t gc_low_power_thresh; /* adi,gc-low-power-thresh */ + uint8_t gc_max_dig_gain; /* adi,gc-max-dig-gain */ + uint8_t gc_use_rx_fir_out_for_dec_pwr_meas_enable; /* adi,gc-use-rx-fir-out-for-dec-pwr-meas-enable */ + /* Gain MGC Control */ + uint8_t mgc_dec_gain_step; /* adi,mgc-dec-gain-step */ + uint8_t mgc_inc_gain_step; /* adi,mgc-inc-gain-step */ + uint8_t mgc_rx1_ctrl_inp_enable; /* adi,mgc-rx1-ctrl-inp-enable */ + uint8_t mgc_rx2_ctrl_inp_enable; /* adi,mgc-rx2-ctrl-inp-enable */ + uint8_t mgc_split_table_ctrl_inp_gain_mode; /* adi,mgc-split-table-ctrl-inp-gain-mode */ + /* Gain AGC Control */ + uint8_t agc_adc_large_overload_exceed_counter; /* adi,agc-adc-large-overload-exceed-counter */ + uint8_t agc_adc_large_overload_inc_steps; /* adi,agc-adc-large-overload-inc-steps - Name is misleading should be dec-steps*/ + uint8_t agc_adc_lmt_small_overload_prevent_gain_inc_enable; /* adi,agc-adc-lmt-small-overload-prevent-gain-inc-enable */ + uint8_t agc_adc_small_overload_exceed_counter; /* adi,agc-adc-small-overload-exceed-counter */ + uint8_t agc_dig_gain_step_size; /* adi,agc-dig-gain-step-size */ + uint8_t agc_dig_saturation_exceed_counter; /* adi,agc-dig-saturation-exceed-counter */ + uint32_t agc_gain_update_interval_us; /* adi,agc-gain-update-interval-us */ + uint8_t agc_immed_gain_change_if_large_adc_overload_enable; /* adi,agc-immed-gain-change-if-large-adc-overload-enable */ + uint8_t agc_immed_gain_change_if_large_lmt_overload_enable; /* adi,agc-immed-gain-change-if-large-lmt-overload-enable */ + uint8_t agc_inner_thresh_high; /* adi,agc-inner-thresh-high */ + uint8_t agc_inner_thresh_high_dec_steps; /* adi,agc-inner-thresh-high-dec-steps */ + uint8_t agc_inner_thresh_low; /* adi,agc-inner-thresh-low */ + uint8_t agc_inner_thresh_low_inc_steps; /* adi,agc-inner-thresh-low-inc-steps */ + uint8_t agc_lmt_overload_large_exceed_counter; /* adi,agc-lmt-overload-large-exceed-counter */ + uint8_t agc_lmt_overload_large_inc_steps; /* adi,agc-lmt-overload-large-inc-steps */ + uint8_t agc_lmt_overload_small_exceed_counter; /* adi,agc-lmt-overload-small-exceed-counter */ + uint8_t agc_outer_thresh_high; /* adi,agc-outer-thresh-high */ + uint8_t agc_outer_thresh_high_dec_steps; /* adi,agc-outer-thresh-high-dec-steps */ + uint8_t agc_outer_thresh_low; /* adi,agc-outer-thresh-low */ + uint8_t agc_outer_thresh_low_inc_steps; /* adi,agc-outer-thresh-low-inc-steps */ + uint32_t agc_attack_delay_extra_margin_us; /* adi,agc-attack-delay-extra-margin-us */ + uint8_t agc_sync_for_gain_counter_enable; /* adi,agc-sync-for-gain-counter-enable */ + /* Fast AGC */ + uint32_t fagc_dec_pow_measuremnt_duration; /* adi,fagc-dec-pow-measurement-duration */ + uint32_t fagc_state_wait_time_ns; /* adi,fagc-state-wait-time-ns */ + /* Fast AGC - Low Power */ + uint8_t fagc_allow_agc_gain_increase; /* adi,fagc-allow-agc-gain-increase-enable */ + uint32_t fagc_lp_thresh_increment_time; /* adi,fagc-lp-thresh-increment-time */ + uint32_t fagc_lp_thresh_increment_steps; /* adi,fagc-lp-thresh-increment-steps */ + /* Fast AGC - Lock Level (Lock Level is set via slow AGC inner high threshold) */ + uint8_t fagc_lock_level_lmt_gain_increase_en; /* adi,fagc-lock-level-lmt-gain-increase-enable */ + uint32_t fagc_lock_level_gain_increase_upper_limit; /* adi,fagc-lock-level-gain-increase-upper-limit */ + /* Fast AGC - Peak Detectors and Final Settling */ + uint32_t fagc_lpf_final_settling_steps; /* adi,fagc-lpf-final-settling-steps */ + uint32_t fagc_lmt_final_settling_steps; /* adi,fagc-lmt-final-settling-steps */ + uint32_t fagc_final_overrange_count; /* adi,fagc-final-overrange-count */ + /* Fast AGC - Final Power Test */ + uint8_t fagc_gain_increase_after_gain_lock_en; /* adi,fagc-gain-increase-after-gain-lock-enable */ + /* Fast AGC - Unlocking the Gain */ + uint32_t fagc_gain_index_type_after_exit_rx_mode; /* adi,fagc-gain-index-type-after-exit-rx-mode */ + uint8_t fagc_use_last_lock_level_for_set_gain_en; /* adi,fagc-use-last-lock-level-for-set-gain-enable */ + uint8_t fagc_rst_gla_stronger_sig_thresh_exceeded_en; /* adi,fagc-rst-gla-stronger-sig-thresh-exceeded-enable */ + uint32_t fagc_optimized_gain_offset; /* adi,fagc-optimized-gain-offset */ + uint32_t fagc_rst_gla_stronger_sig_thresh_above_ll; /* adi,fagc-rst-gla-stronger-sig-thresh-above-ll */ + uint8_t fagc_rst_gla_engergy_lost_sig_thresh_exceeded_en; /* adi,fagc-rst-gla-engergy-lost-sig-thresh-exceeded-enable */ + uint8_t fagc_rst_gla_engergy_lost_goto_optim_gain_en; /* adi,fagc-rst-gla-engergy-lost-goto-optim-gain-enable */ + uint32_t fagc_rst_gla_engergy_lost_sig_thresh_below_ll; /* adi,fagc-rst-gla-engergy-lost-sig-thresh-below-ll */ + uint32_t fagc_energy_lost_stronger_sig_gain_lock_exit_cnt; /* adi,fagc-energy-lost-stronger-sig-gain-lock-exit-cnt */ + uint8_t fagc_rst_gla_large_adc_overload_en; /* adi,fagc-rst-gla-large-adc-overload-enable */ + uint8_t fagc_rst_gla_large_lmt_overload_en; /* adi,fagc-rst-gla-large-lmt-overload-enable */ + uint8_t fagc_rst_gla_en_agc_pulled_high_en; /* adi,fagc-rst-gla-en-agc-pulled-high-enable */ + uint32_t fagc_rst_gla_if_en_agc_pulled_high_mode; /* adi,fagc-rst-gla-if-en-agc-pulled-high-mode */ + uint32_t fagc_power_measurement_duration_in_state5; /* adi,fagc-power-measurement-duration-in-state5 */ + uint32_t fagc_large_overload_inc_steps; /* adi,fagc-adc-large-overload-inc-steps - Name is misleading should be dec-steps */ + /* RSSI Control */ + uint32_t rssi_delay; /* adi,rssi-delay */ + uint32_t rssi_duration; /* adi,rssi-duration */ + uint8_t rssi_restart_mode; /* adi,rssi-restart-mode */ + uint8_t rssi_unit_is_rx_samples_enable; /* adi,rssi-unit-is-rx-samples-enable */ + uint32_t rssi_wait; /* adi,rssi-wait */ + /* Aux ADC Control */ + uint32_t aux_adc_decimation; /* adi,aux-adc-decimation */ + uint32_t aux_adc_rate; /* adi,aux-adc-rate */ + /* AuxDAC Control */ + uint8_t aux_dac_manual_mode_enable; /* adi,aux-dac-manual-mode-enable */ + uint32_t aux_dac1_default_value_mV; /* adi,aux-dac1-default-value-mV */ + uint8_t aux_dac1_active_in_rx_enable; /* adi,aux-dac1-active-in-rx-enable */ + uint8_t aux_dac1_active_in_tx_enable; /* adi,aux-dac1-active-in-tx-enable */ + uint8_t aux_dac1_active_in_alert_enable; /* adi,aux-dac1-active-in-alert-enable */ + uint32_t aux_dac1_rx_delay_us; /* adi,aux-dac1-rx-delay-us */ + uint32_t aux_dac1_tx_delay_us; /* adi,aux-dac1-tx-delay-us */ + uint32_t aux_dac2_default_value_mV; /* adi,aux-dac2-default-value-mV */ + uint8_t aux_dac2_active_in_rx_enable; /* adi,aux-dac2-active-in-rx-enable */ + uint8_t aux_dac2_active_in_tx_enable; /* adi,aux-dac2-active-in-tx-enable */ + uint8_t aux_dac2_active_in_alert_enable; /* adi,aux-dac2-active-in-alert-enable */ + uint32_t aux_dac2_rx_delay_us; /* adi,aux-dac2-rx-delay-us */ + uint32_t aux_dac2_tx_delay_us; /* adi,aux-dac2-tx-delay-us */ + /* Temperature Sensor Control */ + uint32_t temp_sense_decimation; /* adi,temp-sense-decimation */ + uint16_t temp_sense_measurement_interval_ms; /* adi,temp-sense-measurement-interval-ms */ + int8_t temp_sense_offset_signed; /* adi,temp-sense-offset-signed */ + uint8_t temp_sense_periodic_measurement_enable; /* adi,temp-sense-periodic-measurement-enable */ + /* Control Out Setup */ + uint8_t ctrl_outs_enable_mask; /* adi,ctrl-outs-enable-mask */ + uint8_t ctrl_outs_index; /* adi,ctrl-outs-index */ + /* External LNA Control */ + uint32_t elna_settling_delay_ns; /* adi,elna-settling-delay-ns */ + uint32_t elna_gain_mdB; /* adi,elna-gain-mdB */ + uint32_t elna_bypass_loss_mdB; /* adi,elna-bypass-loss-mdB */ + uint8_t elna_rx1_gpo0_control_enable; /* adi,elna-rx1-gpo0-control-enable */ + uint8_t elna_rx2_gpo1_control_enable; /* adi,elna-rx2-gpo1-control-enable */ + uint8_t elna_gaintable_all_index_enable; /* adi,elna-gaintable-all-index-enable */ + /* Digital Interface Control */ + uint8_t digital_interface_tune_skip_mode; /* adi,digital-interface-tune-skip-mode */ + uint8_t digital_interface_tune_fir_disable; /* adi,digital-interface-tune-fir-disable */ + uint8_t pp_tx_swap_enable; /* adi,pp-tx-swap-enable */ + uint8_t pp_rx_swap_enable; /* adi,pp-rx-swap-enable */ + uint8_t tx_channel_swap_enable; /* adi,tx-channel-swap-enable */ + uint8_t rx_channel_swap_enable; /* adi,rx-channel-swap-enable */ + uint8_t rx_frame_pulse_mode_enable; /* adi,rx-frame-pulse-mode-enable */ + uint8_t two_t_two_r_timing_enable; /* adi,2t2r-timing-enable */ + uint8_t invert_data_bus_enable; /* adi,invert-data-bus-enable */ + uint8_t invert_data_clk_enable; /* adi,invert-data-clk-enable */ + uint8_t fdd_alt_word_order_enable; /* adi,fdd-alt-word-order-enable */ + uint8_t invert_rx_frame_enable; /* adi,invert-rx-frame-enable */ + uint8_t fdd_rx_rate_2tx_enable; /* adi,fdd-rx-rate-2tx-enable */ + uint8_t swap_ports_enable; /* adi,swap-ports-enable */ + uint8_t single_data_rate_enable; /* adi,single-data-rate-enable */ + uint8_t lvds_mode_enable; /* adi,lvds-mode-enable */ + uint8_t half_duplex_mode_enable; /* adi,half-duplex-mode-enable */ + uint8_t single_port_mode_enable; /* adi,single-port-mode-enable */ + uint8_t full_port_enable; /* adi,full-port-enable */ + uint8_t full_duplex_swap_bits_enable; /* adi,full-duplex-swap-bits-enable */ + uint32_t delay_rx_data; /* adi,delay-rx-data */ + uint32_t rx_data_clock_delay; /* adi,rx-data-clock-delay */ + uint32_t rx_data_delay; /* adi,rx-data-delay */ + uint32_t tx_fb_clock_delay; /* adi,tx-fb-clock-delay */ + uint32_t tx_data_delay; /* adi,tx-data-delay */ + uint32_t lvds_bias_mV; /* adi,lvds-bias-mV */ + uint8_t lvds_rx_onchip_termination_enable; /* adi,lvds-rx-onchip-termination-enable */ + uint8_t rx1rx2_phase_inversion_en; /* adi,rx1-rx2-phase-inversion-enable */ + uint8_t lvds_invert1_control; /* adi,lvds-invert1-control */ + uint8_t lvds_invert2_control; /* adi,lvds-invert2-control */ + /* GPO Control */ + uint8_t gpo_manual_mode_enable; /* adi,gpo-manual-mode-enable */ + uint32_t gpo_manual_mode_enable_mask; /* adi,gpo-manual-mode-enable-mask */ + uint8_t gpo0_inactive_state_high_enable; /* adi,gpo0-inactive-state-high-enable */ + uint8_t gpo1_inactive_state_high_enable; /* adi,gpo1-inactive-state-high-enable */ + uint8_t gpo2_inactive_state_high_enable; /* adi,gpo2-inactive-state-high-enable */ + uint8_t gpo3_inactive_state_high_enable; /* adi,gpo3-inactive-state-high-enable */ + uint8_t gpo0_slave_rx_enable; /* adi,gpo0-slave-rx-enable */ + uint8_t gpo0_slave_tx_enable; /* adi,gpo0-slave-tx-enable */ + uint8_t gpo1_slave_rx_enable; /* adi,gpo1-slave-rx-enable */ + uint8_t gpo1_slave_tx_enable; /* adi,gpo1-slave-tx-enable */ + uint8_t gpo2_slave_rx_enable; /* adi,gpo2-slave-rx-enable */ + uint8_t gpo2_slave_tx_enable; /* adi,gpo2-slave-tx-enable */ + uint8_t gpo3_slave_rx_enable; /* adi,gpo3-slave-rx-enable */ + uint8_t gpo3_slave_tx_enable; /* adi,gpo3-slave-tx-enable */ + uint8_t gpo0_rx_delay_us; /* adi,gpo0-rx-delay-us */ + uint8_t gpo0_tx_delay_us; /* adi,gpo0-tx-delay-us */ + uint8_t gpo1_rx_delay_us; /* adi,gpo1-rx-delay-us */ + uint8_t gpo1_tx_delay_us; /* adi,gpo1-tx-delay-us */ + uint8_t gpo2_rx_delay_us; /* adi,gpo2-rx-delay-us */ + uint8_t gpo2_tx_delay_us; /* adi,gpo2-tx-delay-us */ + uint8_t gpo3_rx_delay_us; /* adi,gpo3-rx-delay-us */ + uint8_t gpo3_tx_delay_us; /* adi,gpo3-tx-delay-us */ + /* Tx Monitor Control */ + uint32_t low_high_gain_threshold_mdB; /* adi,txmon-low-high-thresh */ + uint32_t low_gain_dB; /* adi,txmon-low-gain */ + uint32_t high_gain_dB; /* adi,txmon-high-gain */ + uint8_t tx_mon_track_en; /* adi,txmon-dc-tracking-enable */ + uint8_t one_shot_mode_en; /* adi,txmon-one-shot-mode-enable */ + uint32_t tx_mon_delay; /* adi,txmon-delay */ + uint32_t tx_mon_duration; /* adi,txmon-duration */ + uint32_t tx1_mon_front_end_gain; /* adi,txmon-1-front-end-gain */ + uint32_t tx2_mon_front_end_gain; /* adi,txmon-2-front-end-gain */ + uint32_t tx1_mon_lo_cm; /* adi,txmon-1-lo-cm */ + uint32_t tx2_mon_lo_cm; /* adi,txmon-2-lo-cm */ + /* GPIO definitions */ + struct gpio_init_param gpio_resetb; /* reset-gpios */ + /* MCS Sync */ + struct gpio_init_param gpio_sync; /* sync-gpios */ + struct gpio_init_param gpio_cal_sw1; /* cal-sw1-gpios */ + struct gpio_init_param gpio_cal_sw2; /* cal-sw2-gpios */ + + struct spi_init_param spi_param; + + /* External LO clocks */ + uint32_t (*ad9361_rfpll_ext_recalc_rate)(struct refclk_scale *clk_priv); + int32_t (*ad9361_rfpll_ext_round_rate)(struct refclk_scale *clk_priv, + uint32_t rate); + int32_t (*ad9361_rfpll_ext_set_rate)(struct refclk_scale *clk_priv, + uint32_t rate); + +} AD9361_InitParam; + +typedef struct { + uint32_t rx; /* 1, 2, 3(both) */ + int32_t rx_gain; /* -12, -6, 0, 6 */ + uint32_t rx_dec; /* 1, 2, 4 */ + int16_t rx_coef[128]; + uint8_t rx_coef_size; + uint32_t rx_path_clks[6]; + uint32_t rx_bandwidth; +} AD9361_RXFIRConfig; + +typedef struct { + uint32_t tx; /* 1, 2, 3(both) */ + int32_t tx_gain; /* -6, 0 */ + uint32_t tx_int; /* 1, 2, 4 */ + int16_t tx_coef[128]; + uint8_t tx_coef_size; + uint32_t tx_path_clks[6]; + uint32_t tx_bandwidth; +} AD9361_TXFIRConfig; + +enum ad9361_ensm_mode { + ENSM_MODE_TX, + ENSM_MODE_RX, + ENSM_MODE_ALERT, + ENSM_MODE_FDD, + ENSM_MODE_WAIT, + ENSM_MODE_SLEEP, + ENSM_MODE_PINCTRL, + ENSM_MODE_PINCTRL_FDD_INDEP, +}; + +#define ENABLE 1 +#define DISABLE 0 + +#define RX1 0 +#define RX2 1 + +#define TX1 0 +#define TX2 1 + +#define A_BALANCED 0 +#define B_BALANCED 1 +#define C_BALANCED 2 +#define A_N 3 +#define A_P 4 +#define B_N 5 +#define B_P 6 +#define C_N 7 +#define C_P 8 +#define TX_MON1 9 +#define TX_MON2 10 +#define TX_MON1_2 11 + +#define TXA 0 +#define TXB 1 + +#define MODE_1x1 1 +#define MODE_2x2 2 + +#define HIGHEST_OSR 0 +#define NOMINAL_OSR 1 + +#define INT_LO 0 +#define EXT_LO 1 + +#define ON 0 +#define OFF 1 + +/******************************************************************************/ +/************************ Functions Declarations ******************************/ +/******************************************************************************/ +/* Initialize the AD9361 part. */ +int32_t ad9361_init (struct ad9361_rf_phy **ad9361_phy, + AD9361_InitParam *init_param); +/* Free the allocated resources. */ +int32_t ad9361_remove(struct ad9361_rf_phy *phy); +/* Set the Enable State Machine (ENSM) mode. */ +int32_t ad9361_set_en_state_machine_mode (struct ad9361_rf_phy *phy, + uint32_t mode); +/* Get the Enable State Machine (ENSM) mode. */ +int32_t ad9361_get_en_state_machine_mode (struct ad9361_rf_phy *phy, + uint32_t *mode); +/* Set the receive RF gain for the selected channel. */ +int32_t ad9361_set_rx_rf_gain (struct ad9361_rf_phy *phy, uint8_t ch, + int32_t gain_db); +/* Get current receive RF gain for the selected channel. */ +int32_t ad9361_get_rx_rf_gain (struct ad9361_rf_phy *phy, uint8_t ch, + int32_t *gain_db); +/* Set the RX RF bandwidth. */ +int32_t ad9361_set_rx_rf_bandwidth (struct ad9361_rf_phy *phy, + uint32_t bandwidth_hz); +/* Get the RX RF bandwidth. */ +int32_t ad9361_get_rx_rf_bandwidth (struct ad9361_rf_phy *phy, + uint32_t *bandwidth_hz); +/* Set the RX sampling frequency. */ +int32_t ad9361_set_rx_sampling_freq (struct ad9361_rf_phy *phy, + uint32_t sampling_freq_hz); +/* Get current RX sampling frequency. */ +int32_t ad9361_get_rx_sampling_freq (struct ad9361_rf_phy *phy, + uint32_t *sampling_freq_hz); +/* Set the RX LO frequency. */ +int32_t ad9361_set_rx_lo_freq (struct ad9361_rf_phy *phy, uint64_t lo_freq_hz); +/* Get current RX LO frequency. */ +int32_t ad9361_get_rx_lo_freq (struct ad9361_rf_phy *phy, uint64_t *lo_freq_hz); +/* Switch between internal and external LO. */ +int32_t ad9361_set_rx_lo_int_ext(struct ad9361_rf_phy *phy, uint8_t int_ext); +/* Get the RSSI for the selected channel. */ +int32_t ad9361_get_rx_rssi (struct ad9361_rf_phy *phy, uint8_t ch, + struct rf_rssi *rssi); +/* Set the gain control mode for the selected channel. */ +int32_t ad9361_set_rx_gain_control_mode (struct ad9361_rf_phy *phy, uint8_t ch, + uint8_t gc_mode); +/* Get the gain control mode for the selected channel. */ +int32_t ad9361_get_rx_gain_control_mode (struct ad9361_rf_phy *phy, uint8_t ch, + uint8_t *gc_mode); +/* Set the RX FIR filter configuration. */ +int32_t ad9361_set_rx_fir_config (struct ad9361_rf_phy *phy, + AD9361_RXFIRConfig fir_cfg); +/* Get the RX FIR filter configuration. */ +int32_t ad9361_get_rx_fir_config(struct ad9361_rf_phy *phy, uint8_t rx_ch, + AD9361_RXFIRConfig *fir_cfg); +/* Enable/disable the RX FIR filter. */ +int32_t ad9361_set_rx_fir_en_dis (struct ad9361_rf_phy *phy, uint8_t en_dis); +/* Get the status of the RX FIR filter. */ +int32_t ad9361_get_rx_fir_en_dis (struct ad9361_rf_phy *phy, uint8_t *en_dis); +/* Enable/disable the RX RFDC Tracking. */ +int32_t ad9361_set_rx_rfdc_track_en_dis (struct ad9361_rf_phy *phy, + uint8_t en_dis); +/* Get the status of the RX RFDC Tracking. */ +int32_t ad9361_get_rx_rfdc_track_en_dis (struct ad9361_rf_phy *phy, + uint8_t *en_dis); +/* Enable/disable the RX BasebandDC Tracking. */ +int32_t ad9361_set_rx_bbdc_track_en_dis (struct ad9361_rf_phy *phy, + uint8_t en_dis); +/* Get the status of the RX BasebandDC Tracking. */ +int32_t ad9361_get_rx_bbdc_track_en_dis (struct ad9361_rf_phy *phy, + uint8_t *en_dis); +/* Enable/disable the RX Quadrature Tracking. */ +int32_t ad9361_set_rx_quad_track_en_dis (struct ad9361_rf_phy *phy, + uint8_t en_dis); +/* Get the status of the RX Quadrature Tracking. */ +int32_t ad9361_get_rx_quad_track_en_dis (struct ad9361_rf_phy *phy, + uint8_t *en_dis); +/* Set the RX RF input port. */ +int32_t ad9361_set_rx_rf_port_input (struct ad9361_rf_phy *phy, uint32_t mode); +/* Get the selected RX RF input port. */ +int32_t ad9361_get_rx_rf_port_input (struct ad9361_rf_phy *phy, uint32_t *mode); +/* Store RX fastlock profile. */ +int32_t ad9361_rx_fastlock_store(struct ad9361_rf_phy *phy, uint32_t profile); +/* Recall RX fastlock profile. */ +int32_t ad9361_rx_fastlock_recall(struct ad9361_rf_phy *phy, uint32_t profile); +/* Load RX fastlock profile. */ +int32_t ad9361_rx_fastlock_load(struct ad9361_rf_phy *phy, uint32_t profile, + uint8_t *values); +/* Save RX fastlock profile. */ +int32_t ad9361_rx_fastlock_save(struct ad9361_rf_phy *phy, uint32_t profile, + uint8_t *values); +/* Power down the RX Local Oscillator. */ +int32_t ad9361_rx_lo_powerdown(struct ad9361_rf_phy *phy, uint8_t option); +/* Get the RX Local Oscillator power status. */ +int32_t ad9361_get_rx_lo_power(struct ad9361_rf_phy *phy, uint8_t *option); +/* Set the transmit attenuation for the selected channel. */ +int32_t ad9361_set_tx_attenuation (struct ad9361_rf_phy *phy, uint8_t ch, + uint32_t attenuation_mdb); +/* Get current transmit attenuation for the selected channel. */ +int32_t ad9361_get_tx_attenuation (struct ad9361_rf_phy *phy, uint8_t ch, + uint32_t *attenuation_mdb); +/* Set the TX RF bandwidth. */ +int32_t ad9361_set_tx_rf_bandwidth (struct ad9361_rf_phy *phy, + uint32_t bandwidth_hz); +/* Get the TX RF bandwidth. */ +int32_t ad9361_get_tx_rf_bandwidth (struct ad9361_rf_phy *phy, + uint32_t *bandwidth_hz); +/* Set the TX sampling frequency. */ +int32_t ad9361_set_tx_sampling_freq (struct ad9361_rf_phy *phy, + uint32_t sampling_freq_hz); +/* Get current TX sampling frequency. */ +int32_t ad9361_get_tx_sampling_freq (struct ad9361_rf_phy *phy, + uint32_t *sampling_freq_hz); +/* Set the TX LO frequency. */ +int32_t ad9361_set_tx_lo_freq (struct ad9361_rf_phy *phy, uint64_t lo_freq_hz); +/* Get current TX LO frequency. */ +int32_t ad9361_get_tx_lo_freq (struct ad9361_rf_phy *phy, uint64_t *lo_freq_hz); +/* Switch between internal and external LO. */ +int32_t ad9361_set_tx_lo_int_ext(struct ad9361_rf_phy *phy, uint8_t int_ext); +/* Set the TX FIR filter configuration. */ +int32_t ad9361_set_tx_fir_config (struct ad9361_rf_phy *phy, + AD9361_TXFIRConfig fir_cfg); +/* Get the TX FIR filter configuration. */ +int32_t ad9361_get_tx_fir_config(struct ad9361_rf_phy *phy, uint8_t tx_ch, + AD9361_TXFIRConfig *fir_cfg); +/* Enable/disable the TX FIR filter. */ +int32_t ad9361_set_tx_fir_en_dis (struct ad9361_rf_phy *phy, uint8_t en_dis); +/* Get the status of the TX FIR filter. */ +int32_t ad9361_get_tx_fir_en_dis (struct ad9361_rf_phy *phy, uint8_t *en_dis); +/* Get the TX RSSI for the selected channel. */ +int32_t ad9361_get_tx_rssi (struct ad9361_rf_phy *phy, uint8_t ch, + uint32_t *rssi_db_x_1000); +/* Set the TX RF output port. */ +int32_t ad9361_set_tx_rf_port_output (struct ad9361_rf_phy *phy, uint32_t mode); +/* Get the selected TX RF output port. */ +int32_t ad9361_get_tx_rf_port_output (struct ad9361_rf_phy *phy, + uint32_t *mode); +/* Enable/disable the auto calibration. */ +int32_t ad9361_set_tx_auto_cal_en_dis (struct ad9361_rf_phy *phy, + uint8_t en_dis); +/* Get the status of the auto calibration flag. */ +int32_t ad9361_get_tx_auto_cal_en_dis (struct ad9361_rf_phy *phy, + uint8_t *en_dis); +/* Store TX fastlock profile. */ +int32_t ad9361_tx_fastlock_store(struct ad9361_rf_phy *phy, uint32_t profile); +/* Recall TX fastlock profile. */ +int32_t ad9361_tx_fastlock_recall(struct ad9361_rf_phy *phy, uint32_t profile); +/* Load TX fastlock profile. */ +int32_t ad9361_tx_fastlock_load(struct ad9361_rf_phy *phy, uint32_t profile, + uint8_t *values); +/* Save TX fastlock profile. */ +int32_t ad9361_tx_fastlock_save(struct ad9361_rf_phy *phy, uint32_t profile, + uint8_t *values); +/* Power down the TX Local Oscillator. */ +int32_t ad9361_tx_lo_powerdown(struct ad9361_rf_phy *phy, uint8_t option); +/* Get the TX Local Oscillator power status. */ +int32_t ad9361_get_tx_lo_power(struct ad9361_rf_phy *phy, uint8_t *option); +/* Set the RX and TX path rates. */ +int32_t ad9361_set_trx_path_clks(struct ad9361_rf_phy *phy, + uint32_t *rx_path_clks, uint32_t *tx_path_clks); +/* Get the RX and TX path rates. */ +int32_t ad9361_get_trx_path_clks(struct ad9361_rf_phy *phy, + uint32_t *rx_path_clks, uint32_t *tx_path_clks); +/* Set the number of channels mode. */ +int32_t ad9361_set_no_ch_mode(struct ad9361_rf_phy *phy, uint8_t no_ch_mode); +/* Do multi chip synchronization. */ +int32_t ad9361_do_mcs(struct ad9361_rf_phy *phy_master, + struct ad9361_rf_phy *phy_slave); +/* Enable/disable the TRX FIR filters. */ +int32_t ad9361_set_trx_fir_en_dis (struct ad9361_rf_phy *phy, uint8_t en_dis); +/* Set the OSR rate governor. */ +int32_t ad9361_set_trx_rate_gov (struct ad9361_rf_phy *phy, uint32_t rate_gov); +/* Get the OSR rate governor. */ +int32_t ad9361_get_trx_rate_gov (struct ad9361_rf_phy *phy, uint32_t *rate_gov); +/* Perform the selected calibration. */ +int32_t ad9361_do_calib(struct ad9361_rf_phy *phy, uint32_t cal, int32_t arg); +/* Load and enable TRX FIR filters configurations. */ +int32_t ad9361_trx_load_enable_fir(struct ad9361_rf_phy *phy, + AD9361_RXFIRConfig rx_fir_cfg, + AD9361_TXFIRConfig tx_fir_cfg); +/* Do DCXO coarse tuning. */ +int32_t ad9361_do_dcxo_tune_coarse(struct ad9361_rf_phy *phy, + uint32_t coarse); +/* Do DCXO fine tuning. */ +int32_t ad9361_do_dcxo_tune_fine(struct ad9361_rf_phy *phy, + uint32_t fine); +/* Get the temperature. */ +int32_t ad9361_get_temperature(struct ad9361_rf_phy *phy, + int32_t *temp); +#endif diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361/ad9361_conv.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361/ad9361_conv.c new file mode 100644 index 0000000..7ce74b7 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361/ad9361_conv.c @@ -0,0 +1,86 @@ + +/***************************************************************************//** + * @file ad9361_conv.c + * @brief Implementation of AD9361 Conv Driver. +******************************************************************************** + * Copyright 2014-2015(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include +#include +#include "ad9361.h" +#include "ad9361_util.h" +#include "delay.h" +#include "../../../config/app_config.h" + +/******************************************************************************/ +/********************** Macros and Constants Definitions **********************/ +/******************************************************************************/ + +/** + * HDL loopback enable/disable. + * @param phy The AD9361 state structure. + * @param enable Enable/disable option. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_hdl_loopback(struct ad9361_rf_phy *phy, bool enable) +{ + return -ENODEV; +} + +/** + * Digital tune. + * @param phy The AD9361 state structure. + * @param max_freq Maximum frequency. + * @param flags Flags: BE_VERBOSE, BE_MOREVERBOSE, DO_IDELAY, DO_ODELAY. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_dig_tune(struct ad9361_rf_phy *phy, uint32_t max_freq, + enum dig_tune_flags flags) +{ + return 0; +} + +/** +* Setup the AD9361 device. +* @param phy The AD9361 state structure. +* @return 0 in case of success, negative error code otherwise. +*/ +int32_t ad9361_post_setup(struct ad9361_rf_phy *phy) +{ + return 0; +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361/ad9361_util.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361/ad9361_util.c new file mode 100644 index 0000000..dac33d0 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361/ad9361_util.c @@ -0,0 +1,335 @@ +/***************************************************************************//** + * @file ad9361_util.c + * @brief AD9361 Implementation of Util Driver. + * @author DBogdan (dragos.bogdan@analog.com) +******************************************************************************** + * Copyright 2013(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include +#include "ad9361_util.h" +#include "../../../include/delay.h" + +/******************************************************************************/ +/*************************** Macros Definitions *******************************/ +/******************************************************************************/ +#define BITS_PER_LONG 32 + +/***************************************************************************//** + * @brief clk_prepare_enable +*******************************************************************************/ +int32_t clk_prepare_enable(struct clk *clk) +{ + if (clk) { + // Unused variable - fix compiler warning + } + + return 0; +} + +/***************************************************************************//** + * @brief clk_get_rate +*******************************************************************************/ +uint32_t clk_get_rate(struct ad9361_rf_phy *phy, + struct refclk_scale *clk_priv) +{ + uint32_t rate = 0; + uint32_t source; + + source = clk_priv->source; + + switch (source) { + case TX_REFCLK: + case RX_REFCLK: + case BB_REFCLK: + rate = ad9361_clk_factor_recalc_rate(clk_priv, + phy->clk_refin->rate); + break; + case TX_RFPLL_INT: + case RX_RFPLL_INT: + rate = ad9361_rfpll_int_recalc_rate(clk_priv, + phy->clks[clk_priv->parent_source]->rate); + break; + case RX_RFPLL_DUMMY: + case TX_RFPLL_DUMMY: + rate = ad9361_rfpll_dummy_recalc_rate(clk_priv); + break; + case TX_RFPLL: + case RX_RFPLL: + rate = ad9361_rfpll_recalc_rate(clk_priv); + break; + case BBPLL_CLK: + rate = ad9361_bbpll_recalc_rate(clk_priv, + phy->clks[clk_priv->parent_source]->rate); + break; + case ADC_CLK: + case R2_CLK: + case R1_CLK: + case CLKRF_CLK: + case RX_SAMPL_CLK: + case DAC_CLK: + case T2_CLK: + case T1_CLK: + case CLKTF_CLK: + case TX_SAMPL_CLK: + rate = ad9361_clk_factor_recalc_rate(clk_priv, + phy->clks[clk_priv->parent_source]->rate); + break; + default: + break; + } + + return rate; +} + +/***************************************************************************//** + * @brief clk_set_rate +*******************************************************************************/ +int32_t clk_set_rate(struct ad9361_rf_phy *phy, + struct refclk_scale *clk_priv, + uint32_t rate) +{ + uint32_t source; + int32_t i; + uint32_t round_rate; + + source = clk_priv->source; + if(phy->clks[source]->rate != rate) + { + switch (source) { + case TX_REFCLK: + case RX_REFCLK: + case BB_REFCLK: + round_rate = ad9361_clk_factor_round_rate(clk_priv, rate, + &phy->clk_refin->rate); + ad9361_clk_factor_set_rate(clk_priv, round_rate, + phy->clk_refin->rate); + phy->clks[source]->rate = ad9361_clk_factor_recalc_rate(clk_priv, + phy->clk_refin->rate); + break; + case TX_RFPLL_INT: + case RX_RFPLL_INT: + round_rate = ad9361_rfpll_int_round_rate(clk_priv, rate, + &phy->clks[clk_priv->parent_source]->rate); + ad9361_rfpll_int_set_rate(clk_priv, round_rate, + phy->clks[clk_priv->parent_source]->rate); + phy->clks[source]->rate = ad9361_rfpll_int_recalc_rate(clk_priv, + phy->clks[clk_priv->parent_source]->rate); + break; + case RX_RFPLL_DUMMY: + case TX_RFPLL_DUMMY: + ad9361_rfpll_dummy_set_rate(clk_priv, rate); + break; + case TX_RFPLL: + case RX_RFPLL: + round_rate = ad9361_rfpll_round_rate(clk_priv, rate); + ad9361_rfpll_set_rate(clk_priv, round_rate); + phy->clks[source]->rate = ad9361_rfpll_recalc_rate(clk_priv); + break; + case BBPLL_CLK: + round_rate = ad9361_bbpll_round_rate(clk_priv, rate, + &phy->clks[clk_priv->parent_source]->rate); + ad9361_bbpll_set_rate(clk_priv, round_rate, + phy->clks[clk_priv->parent_source]->rate); + phy->clks[source]->rate = ad9361_bbpll_recalc_rate(clk_priv, + phy->clks[clk_priv->parent_source]->rate); + phy->bbpll_initialized = true; + break; + case ADC_CLK: + case R2_CLK: + case R1_CLK: + case CLKRF_CLK: + case RX_SAMPL_CLK: + case DAC_CLK: + case T2_CLK: + case T1_CLK: + case CLKTF_CLK: + case TX_SAMPL_CLK: + round_rate = ad9361_clk_factor_round_rate(clk_priv, rate, + &phy->clks[clk_priv->parent_source]->rate); + ad9361_clk_factor_set_rate(clk_priv, round_rate, + phy->clks[clk_priv->parent_source]->rate); + phy->clks[source]->rate = ad9361_clk_factor_recalc_rate(clk_priv, + phy->clks[clk_priv->parent_source]->rate); + break; + default: + break; + } + for(i = BB_REFCLK; i < BBPLL_CLK; i++) + { + phy->clks[i]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[i], + phy->clk_refin->rate); + } + phy->clks[BBPLL_CLK]->rate = ad9361_bbpll_recalc_rate(phy->ref_clk_scale[BBPLL_CLK], + phy->clks[phy->ref_clk_scale[BBPLL_CLK]->parent_source]->rate); + for(i = ADC_CLK; i < RX_RFPLL_INT; i++) + { + phy->clks[i]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[i], + phy->clks[phy->ref_clk_scale[i]->parent_source]->rate); + } + for(i = RX_RFPLL_INT; i < RX_RFPLL_DUMMY; i++) + { + phy->clks[i]->rate = ad9361_rfpll_int_recalc_rate(phy->ref_clk_scale[i], + phy->clks[phy->ref_clk_scale[i]->parent_source]->rate); + } + for(i = RX_RFPLL_DUMMY; i < RX_RFPLL; i++) + { + phy->clks[i]->rate = ad9361_rfpll_dummy_recalc_rate(phy->ref_clk_scale[i]); + } + for(i = RX_RFPLL; i < NUM_AD9361_CLKS; i++) + { + phy->clks[i]->rate = ad9361_rfpll_recalc_rate(phy->ref_clk_scale[i]); + } + } else { + if ((source == BBPLL_CLK) && !phy->bbpll_initialized) { + round_rate = ad9361_bbpll_round_rate(clk_priv, rate, + &phy->clks[clk_priv->parent_source]->rate); + ad9361_bbpll_set_rate(clk_priv, round_rate, + phy->clks[clk_priv->parent_source]->rate); + phy->clks[source]->rate = ad9361_bbpll_recalc_rate(clk_priv, + phy->clks[clk_priv->parent_source]->rate); + phy->bbpll_initialized = true; + } + } + + return 0; +} + +/***************************************************************************//** + * @brief int_sqrt +*******************************************************************************/ +uint32_t int_sqrt(uint32_t x) +{ + uint32_t b, m, y = 0; + + if (x <= 1) + return x; + + m = 1UL << (BITS_PER_LONG - 2); + while (m != 0) { + b = y + m; + y >>= 1; + + if (x >= b) { + x -= b; + y += m; + } + m >>= 2; + } + + return y; +} + +/***************************************************************************//** + * @brief ilog2 +*******************************************************************************/ +int32_t ilog2(int32_t x) +{ + int32_t A = !(!(x >> 16)); + int32_t count = 0; + int32_t x_copy = x; + + count = count + (A << 4); + + x_copy = (((~A + 1) & (x >> 16)) + (~(~A + 1) & x)); + + A = !(!(x_copy >> 8)); + count = count + (A << 3); + x_copy = (((~A + 1) & (x_copy >> 8)) + (~(~A + 1) & x_copy)); + + A = !(!(x_copy >> 4)); + count = count + (A << 2); + x_copy = (((~A + 1) & (x_copy >> 4)) + (~(~A + 1) & x_copy)); + + A = !(!(x_copy >> 2)); + count = count + (A << 1); + x_copy = (((~A + 1) & (x_copy >> 2)) + (~(~A + 1) & x_copy)); + + A = !(!(x_copy >> 1)); + count = count + A; + + return count; +} + +/***************************************************************************//** + * @brief find_first_bit +*******************************************************************************/ +uint32_t find_first_bit(uint32_t word) +{ + int32_t num = 0; + + if ((word & 0xffff) == 0) { + num += 16; + word >>= 16; + } + if ((word & 0xff) == 0) { + num += 8; + word >>= 8; + } + if ((word & 0xf) == 0) { + num += 4; + word >>= 4; + } + if ((word & 0x3) == 0) { + num += 2; + word >>= 2; + } + if ((word & 0x1) == 0) + num += 1; + return num; +} + +/***************************************************************************//** + * @brief ERR_PTR +*******************************************************************************/ +void * ERR_PTR(long error) +{ + return (void *) error; +} + +/***************************************************************************//** + * @brief zmalloc +*******************************************************************************/ +void *zmalloc(size_t size) +{ + void *ptr = malloc(size); + if (ptr) + memset(ptr, 0, size); + mdelay(1); + + return ptr; +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361/ad9361_util.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361/ad9361_util.h new file mode 100644 index 0000000..85eb78f --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361/ad9361_util.h @@ -0,0 +1,120 @@ +/***************************************************************************//** + * @file ad9361_util.h + * @brief AD9361 Header file of Util driver. + * @author DBogdan (dragos.bogdan@analog.com) +******************************************************************************** + * Copyright 2013(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ +#ifndef __AD9361_UTIL_H__ +#define __AD9361_UTIL_H__ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include +#include +#include +#include +#include "ad9361.h" +#include "common.h" + +/******************************************************************************/ +/********************** Macros and Constants Definitions **********************/ +/******************************************************************************/ +#define CLK_IGNORE_UNUSED BIT(3) +#define CLK_GET_RATE_NOCACHE BIT(6) + +#if defined(HAVE_VERBOSE_MESSAGES) +#define dev_err(dev, format, ...) ({printf(format, ## __VA_ARGS__);printf("\n"); }) +#define dev_warn(dev, format, ...) ({printf(format, ## __VA_ARGS__);printf("\n"); }) +#if defined(HAVE_DEBUG_MESSAGES) +#define dev_dbg(dev, format, ...) ({printf(format, ## __VA_ARGS__);printf("\n"); }) +#else +#define dev_dbg(dev, format, ...) ({ if (0) printf(format, ## __VA_ARGS__); }) +#endif +#define printk(format, ...) printf(format, ## __VA_ARGS__) +#else +#define dev_err(dev, format, ...) ({ if (0) printf(format, ## __VA_ARGS__); }) +#define dev_warn(dev, format, ...) ({ if (0) printf(format, ## __VA_ARGS__); }) +#define dev_dbg(dev, format, ...) ({ if (0) printf(format, ## __VA_ARGS__); }) +#define printk(format, ...) ({ if (0) printf(format, ## __VA_ARGS__); }) +#endif + +struct device { +}; + +struct spi_device { + struct device dev; + uint8_t id_no; +}; + +struct axiadc_state { + struct ad9361_rf_phy *phy; + uint32_t pcore_version; +}; + +struct axiadc_chip_info { + char *name; + int32_t num_channels; +}; + +struct axiadc_converter { + struct axiadc_chip_info *chip_info; + uint32_t scratch_reg[16]; +}; + +#ifdef WIN32 +#include "basetsd.h" +typedef SSIZE_T ssize_t; +#define strsep(s, ct) 0 +#define snprintf(s, n, format, ...) 0 +#define __func__ __FUNCTION__ +#endif + +/******************************************************************************/ +/************************ Functions Declarations ******************************/ +/******************************************************************************/ +int32_t clk_prepare_enable(struct clk *clk); +uint32_t clk_get_rate(struct ad9361_rf_phy *phy, + struct refclk_scale *clk_priv); +int32_t clk_set_rate(struct ad9361_rf_phy *phy, + struct refclk_scale *clk_priv, + uint32_t rate); +uint32_t int_sqrt(uint32_t x); +int32_t ilog2(int32_t x); +uint32_t find_first_bit(uint32_t word); +void * ERR_PTR(long error); +void *zmalloc(size_t size); + +#endif diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361/common.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361/common.h new file mode 100644 index 0000000..7cc2627 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361/common.h @@ -0,0 +1,66 @@ +/***************************************************************************//** + * @file common.h + * @brief Header file of Common Driver. + * @author DBogdan (dragos.bogdan@analog.com) +******************************************************************************** + * Copyright 2013(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ +#ifndef __AD9361_COMMON_H_ +#define __AD9361_COMMON_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include +#include "../../../include/error.h" + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ +#if defined (__STDC__) && (__STDC_VERSION__ >= 199901L) +#include +#else +typedef enum { false, true } bool; +#endif + +struct clk { + const char *name; + uint32_t rate; +}; + +struct clk_hw { + struct clk *clk; +}; + +#endif // __AD9361_COMMON_H_ diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361_reg/ad9361_reg.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361_reg/ad9361_reg.c new file mode 100644 index 0000000..b6806e2 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361_reg/ad9361_reg.c @@ -0,0 +1,151 @@ +/***************************************************************************//** + * @file ad9361_reg.c + * @brief Implementation of AD9680 Driver. + * @author DBogdan (dragos.bogdan@analog.com) + ******************************************************************************** + * Copyright 2014-2016(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. + *******************************************************************************/ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include "ad9361_reg.h" + +#include +#include +#include "../../../include/error.h" + +/***************************************************************************//** + * @brief ad9361_reg_read + *******************************************************************************/ +int32_t ad9361_reg_read(struct ad9361_reg_dev *dev, + uint16_t reg_addr, + uint8_t *reg_data) +{ + uint8_t tx_buf[2]; + uint8_t rx_buf[1]; + int32_t ret; + + tx_buf[0] = reg_addr >> 8; + tx_buf[1] = reg_addr & 0xFF; + + + ret = spi_write_and_read_3wire(dev->spi_desc, + tx_buf, rx_buf, 2, 1); + + *reg_data = rx_buf[0]; + + return ret; +} + +/***************************************************************************//** + * @brief ad9361_reg_write + *******************************************************************************/ +int32_t ad9361_reg_write(struct ad9361_reg_dev *dev, + uint16_t reg_addr, + uint8_t reg_data) +{ + uint8_t buf[3]; + + int32_t ret; + + buf[0] = 0x80 | reg_addr >> 8; + buf[1] = reg_addr & 0xFF; + buf[2] = reg_data; + + ret = spi_write_and_read_3wire(dev->spi_desc, + buf, NULL, 3, 0); + return ret; +} + +/***************************************************************************//** + * @brief ad9361_reg_init + *******************************************************************************/ +int32_t ad9361_reg_init(struct ad9361_reg_dev **device, + const struct ad9361_reg_init_param *init_param) +{ + int ret = SUCCESS; + struct ad9361_reg_dev *dev; + + dev = (struct ad9361_reg_dev *)malloc(sizeof(*dev)); + if (!dev) + return FAILURE; + + /* SPI */ + ret = spi_init(&dev->spi_desc, init_param->spi_init); + ret |= gpio_get(&dev->gpio_desc_resetb, init_param->gpio_resetb); + + gpio_direction_output(dev->gpio_desc_resetb, 0); + + ad9361_reg_reset(dev); + + *device = dev; + + return ret; +} + +/** + * AD9361 Device Reset + * @param phy The AD9361 state structure. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_reg_reset(struct ad9361_reg_dev *dev) +{ + if (dev->gpio_desc_resetb) { + gpio_set_value(dev->gpio_desc_resetb, 0); + mdelay(10); + gpio_set_value(dev->gpio_desc_resetb, 1); + mdelay(10); + return 0; + } + return 0; +} + +/***************************************************************************//** + * @brief Free the resources allocated by ad9361_reg_setup(). + * + * @param dev - The device structure. + * + * @return SUCCESS in case of success, negative error code otherwise. +*******************************************************************************/ +int32_t ad9361_reg_remove(struct ad9361_reg_dev *dev) +{ + int32_t ret; + + ret = spi_remove(dev->spi_desc); + + free(dev); + + return ret; +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361_reg/ad9361_reg.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361_reg/ad9361_reg.h new file mode 100644 index 0000000..102df4b --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/ad9361_reg/ad9361_reg.h @@ -0,0 +1,104 @@ +/***************************************************************************//** + * @file ad9361_reg.h + * @brief Header file of ad9361_reg Driver. + * @author DBogdan (dragos.bogdan@analog.com) + ******************************************************************************** + * Copyright 2014-2016(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. + *******************************************************************************/ +#ifndef ad9361_reg_H_ +#define ad9361_reg_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include +#include "delay.h" +#include "spi.h" +#include "gpio.h" + +/******************************************************************************/ +/********************** Macros and Constants Definitions **********************/ +/******************************************************************************/ + + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ +struct ad9361_reg_dev { + /* SPI */ + struct spi_desc *spi_desc; + struct gpio_desc *gpio_desc_resetb; +}; + +struct ad9361_reg_init_param { + /* SPI */ + struct spi_init_param *spi_init; + struct gpio_init_param *gpio_resetb; +}; + +enum Operation_Type{ + AD9361_WRITE, + AD9361_READ, + AD9361_WAIT, + AD9361_WHEN +}; + +struct ad9361_reg_config { + int operation; + uint16_t val1; + uint16_t val2; +}; + + + +/******************************************************************************/ +/************************ Functions Declarations ******************************/ +/******************************************************************************/ + +int32_t ad9361_reg_read(struct ad9361_reg_dev *dev, + uint16_t reg_addr, + uint8_t *reg_data); + +int32_t ad9361_reg_write(struct ad9361_reg_dev *dev, + uint16_t reg_addr, + uint8_t reg_data); + +int32_t ad9361_reg_init(struct ad9361_reg_dev **device, + const struct ad9361_reg_init_param *init_param); + +int32_t ad9361_reg_reset(struct ad9361_reg_dev *dev); + +int32_t ad9361_reg_remove(struct ad9361_reg_dev *dev); + +#endif diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/cx9261a/cx9261a.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/cx9261a/cx9261a.c new file mode 100644 index 0000000..f0bfcde --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/cx9261a/cx9261a.c @@ -0,0 +1,147 @@ +/***************************************************************************//** + * @file cx9261a.c + * @brief Implementation of CX9261A Driver. + *******************************************************************************/ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include +#include +#include "../../../include/error.h" + +#include "cx9261a.h" + +/***************************************************************************//** + * @brief cx9261a_read + *******************************************************************************/ +int32_t cx9261a_read(struct cx9261a_dev *dev, + uint16_t reg_addr, + uint8_t *reg_data) +{ + uint8_t tx_buf[2]; + uint8_t rx_buf[1]; + int32_t ret; + + tx_buf[0] = reg_addr >> 8; + tx_buf[1] = reg_addr & 0xFF; + + + ret = spi_write_and_read_3wire(dev->spi_desc, + tx_buf, rx_buf, 2, 1); + + *reg_data = rx_buf[0]; + + return ret; +} + +/***************************************************************************//** + * @brief cx9261a_write + *******************************************************************************/ +int32_t cx9261a_write(struct cx9261a_dev *dev, + uint16_t reg_addr, + uint8_t reg_data) +{ + uint8_t buf[3]; + + int32_t ret; + + buf[0] = 0x80 | reg_addr >> 8; + buf[1] = reg_addr & 0xFF; + buf[2] = reg_data; + + ret = spi_write_and_read_3wire(dev->spi_desc, + buf, NULL, 3, 0); + return ret; +} + +/***************************************************************************//** + * @brief cx9261a_init + *******************************************************************************/ +int32_t cx9261a_init(struct cx9261a_dev **device, + const struct cx9261a_init_param *init_param) +{ + int ret = SUCCESS; + struct cx9261a_dev *dev; + + dev = (struct cx9261a_dev *)malloc(sizeof(*dev)); + if (!dev) + return FAILURE; + + /* SPI */ + ret = spi_init(&dev->spi_desc, init_param->spi_init); + ret |= gpio_get(&dev->gpio_desc_nrst, init_param->gpio_nrst); + ret |= gpio_get(&dev->gpio_desc_txnrx_trx1, init_param->gpio_txnrx_trx1); + ret |= gpio_get(&dev->gpio_desc_txnrx_trx2, init_param->gpio_txnrx_trx2); + ret |= gpio_get(&dev->gpio_desc_txnrx_rxfb, init_param->gpio_txnrx_rxfb); + ret |= gpio_get(&dev->gpio_desc_enable_trx1, init_param->gpio_enable_trx1); + ret |= gpio_get(&dev->gpio_desc_enable_trx2, init_param->gpio_enable_trx2); + ret |= gpio_get(&dev->gpio_desc_enable_rxfb, init_param->gpio_enable_rxfb); + ret |= gpio_get(&dev->gpio_desc_pl_rst, init_param->gpio_pl_rst); + ret |= gpio_get(&dev->gpio_desc_tr_sel, init_param->gpio_tr_sel); + ret |= gpio_get(&dev->gpio_desc_tdd_fdd_sel, init_param->gpio_tdd_fdd_sel); + ret |= gpio_get(&dev->gpio_desc_r1t1_r2t2_sel, init_param->gpio_r1t1_r2t2_sel); + ret |= gpio_get(&dev->gpio_desc_sp_dp_sel, init_param->gpio_sp_dp_sel); + ret |= gpio_get(&dev->gpio_desc_mclk_delay_ctrl0, init_param->gpio_mclk_delay_ctrl0); + ret |= gpio_get(&dev->gpio_desc_mclk_delay_ctrl1, init_param->gpio_mclk_delay_ctrl1); + ret |= gpio_get(&dev->gpio_desc_mclk_delay_ctrl2, init_param->gpio_mclk_delay_ctrl2); + ret |= gpio_get(&dev->gpio_desc_hop_flag, init_param->gpio_hop_flag); + + gpio_direction_output(dev->gpio_desc_nrst, 0); + gpio_direction_output(dev->gpio_desc_txnrx_trx1, 0); + gpio_direction_output(dev->gpio_desc_txnrx_trx2, 0); + gpio_direction_output(dev->gpio_desc_txnrx_rxfb, 0); + gpio_direction_output(dev->gpio_desc_enable_trx1, 0); + gpio_direction_output(dev->gpio_desc_enable_trx2, 0); + gpio_direction_output(dev->gpio_desc_enable_rxfb, 0); + gpio_direction_output(dev->gpio_desc_pl_rst, 0); + gpio_direction_output(dev->gpio_desc_tr_sel, 0); + gpio_direction_output(dev->gpio_desc_tdd_fdd_sel, 0); + gpio_direction_output(dev->gpio_desc_r1t1_r2t2_sel, 0); + gpio_direction_output(dev->gpio_desc_sp_dp_sel, 0); + gpio_direction_output(dev->gpio_desc_mclk_delay_ctrl0, 0); + gpio_direction_output(dev->gpio_desc_mclk_delay_ctrl1, 0); + gpio_direction_output(dev->gpio_desc_mclk_delay_ctrl2, 0); + gpio_direction_input(dev->gpio_desc_hop_flag); + + cx9261a_reset(dev); + + *device = dev; + + return ret; +} + +/** + * AD9361 Device Reset + * @param phy The AD9361 state structure. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t cx9261a_reset(struct cx9261a_dev *dev) +{ + if (dev->gpio_desc_nrst) { + gpio_set_value(dev->gpio_desc_nrst, 0); + mdelay(10); + gpio_set_value(dev->gpio_desc_nrst, 1); + mdelay(10); + return 0; + } + return 0; +} + +/***************************************************************************//** + * @brief Free the resources allocated by cx9261a_setup(). + * + * @param dev - The device structure. + * + * @return SUCCESS in case of success, negative error code otherwise. +*******************************************************************************/ +int32_t cx9261a_remove(struct cx9261a_dev *dev) +{ + int32_t ret; + + ret = spi_remove(dev->spi_desc); + + free(dev); + + return ret; +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/cx9261a/cx9261a.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/cx9261a/cx9261a.h new file mode 100644 index 0000000..ca38551 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/cx9261a/cx9261a.h @@ -0,0 +1,85 @@ +/***************************************************************************//** + * @file cx9261a.h + * @brief Header file of cx9261a Driver. + *******************************************************************************/ +#ifndef CX9261A_H_ +#define CX9261A_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include +#include "delay.h" +#include "spi.h" +#include "gpio.h" + +/******************************************************************************/ +/********************** Macros and Constants Definitions **********************/ +/******************************************************************************/ + + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ +struct cx9261a_dev { + /* SPI */ + struct spi_desc *spi_desc; + struct gpio_desc *gpio_desc_nrst; + struct gpio_desc *gpio_desc_txnrx_trx1; + struct gpio_desc *gpio_desc_txnrx_trx2; + struct gpio_desc *gpio_desc_txnrx_rxfb; + struct gpio_desc *gpio_desc_enable_trx1; + struct gpio_desc *gpio_desc_enable_trx2; + struct gpio_desc *gpio_desc_enable_rxfb; + struct gpio_desc *gpio_desc_pl_rst; + struct gpio_desc *gpio_desc_tr_sel; + struct gpio_desc *gpio_desc_tdd_fdd_sel; + struct gpio_desc *gpio_desc_r1t1_r2t2_sel; + struct gpio_desc *gpio_desc_sp_dp_sel; + struct gpio_desc *gpio_desc_mclk_delay_ctrl0; + struct gpio_desc *gpio_desc_mclk_delay_ctrl1; + struct gpio_desc *gpio_desc_mclk_delay_ctrl2; + struct gpio_desc *gpio_desc_hop_flag; +}; + +struct cx9261a_init_param { + /* SPI */ + struct spi_init_param *spi_init; + struct gpio_init_param *gpio_nrst; + struct gpio_init_param *gpio_txnrx_trx1; + struct gpio_init_param *gpio_txnrx_trx2; + struct gpio_init_param *gpio_txnrx_rxfb; + struct gpio_init_param *gpio_enable_trx1; + struct gpio_init_param *gpio_enable_trx2; + struct gpio_init_param *gpio_enable_rxfb; + struct gpio_init_param *gpio_pl_rst; + struct gpio_init_param *gpio_tr_sel; + struct gpio_init_param *gpio_tdd_fdd_sel; + struct gpio_init_param *gpio_r1t1_r2t2_sel; + struct gpio_init_param *gpio_sp_dp_sel; + struct gpio_init_param *gpio_mclk_delay_ctrl0; + struct gpio_init_param *gpio_mclk_delay_ctrl1; + struct gpio_init_param *gpio_mclk_delay_ctrl2; + struct gpio_init_param *gpio_hop_flag; +}; + +/******************************************************************************/ +/************************ Functions Declarations ******************************/ +/******************************************************************************/ + +int32_t cx9261a_read(struct cx9261a_dev *dev, + uint16_t reg_addr, + uint8_t *reg_data); + +int32_t cx9261a_write(struct cx9261a_dev *dev, + uint16_t reg_addr, + uint8_t reg_data); + +int32_t cx9261a_init(struct cx9261a_dev **device, + const struct cx9261a_init_param *init_param); + +int32_t cx9261a_reset(struct cx9261a_dev *dev); + +int32_t cx9261a_remove(struct cx9261a_dev *dev); + +#endif diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/cx9261a/cx9261adriver_c.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/cx9261a/cx9261adriver_c.h new file mode 100644 index 0000000..aac0d07 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/cx9261a/cx9261adriver_c.h @@ -0,0 +1,982 @@ +#ifndef __CX9261ADRIVER_C_H +#define __CX9261ADRIVER_C_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef __cplusplus +#include +#include +#include +#endif + +#define MAX_NUM_FIR 64 +#define MAX_MULTI_POINTS 64 + +typedef enum { + CX9261A_WKM_Rx, + CX9261A_WKM_Tx, + CX9261A_WKM_FDD +} CX9261A_WorkMode; + +/** + * @brief 收发数据通道数量 + * @note 此模式并不代表使用几个通道进行收发 + */ +typedef enum { + CX9261A_TRM_R1T1, + CX9261A_TRM_R2T2 +} CX9261A_TrMode; + +/** + * @brief 收发接口通道数量 + */ +typedef enum { + CX9261A_PORTM_Single, + CX9261A_PORTM_Dual +} CX9261A_PortMode; + +/** + * @brief 接口模式: + * CMOS CMOS接口模式, + * LVDS_S LVDS顺序模式, + * LVDS_E LVDS奇偶模式 + */ +typedef enum { + CX9261A_IOM_CMOS, + CX9261A_IOM_LVDS_S, + CX9261A_IOM_LVDS_E +} CX9261A_IOMode; + +/** + * @brief 本振来源 + */ +typedef enum { + CX9261A_LOSRC_Pll1 = 0b00001, + CX9261A_LOSRC_Pll2 = 0b00010, + CX9261A_LOSRC_Pll3 = 0b00100, + CX9261A_LOSRC_Pll4 = 0b01000, + CX9261A_LOSRC_Pll5 = 0b10000, + CX9261A_LOSRC_Off = 0 +} CX9261A_LoSource; + +/** + * @brief 接收通道 + */ +typedef enum { + CX9261A_RXCH_Rx1 = 1, + CX9261A_RXCH_Rx2, + CX9261A_RXCH_Rx3 +} CX9261A_RxChannel; + +/** + * @brief 发射通道 + */ +typedef enum { + CX9261A_TXCH_Tx1 = 1, + CX9261A_TXCH_Tx2 +} CX9261A_TxChannel; + +/** + * @brief 电源模式 + */ +typedef enum { + CX9261A_PWM_Shutdown = 0, + CX9261A_PWM_Sleep, + CX9261A_PWM_Alert, + CX9261A_PWM_Calib, + CX9261A_PWM_Receive, + CX9261A_PWM_Transmit, + CX9261A_PWM_Duplex, + CX9261A_PWM_Obs +} CX9261A_PowerMode; + +/** + * @brief NCO模式 + */ +typedef enum { + CX9261A_NCOM_Bypass = 0, + CX9261A_NCOM_Up, + CX9261A_NCOM_Down, + CX9261A_NCOM_BIST, + CX9261A_NCOM_Master, + CX9261A_NCOM_Slaver +} CX9261A_NCOMode; + +/** + * @brief 温补模式 DAC: DAC模式 Band: 跳子带模式 + */ +typedef enum { + CX9261A_TEMPCP_DAC = 0, + CX9261A_TEMPCP_Band +} CX9261A_TemperatureComp; + +/** + * @brief 校准方法 TST: 时域统计 Rx通道使用, Bisec: 二分法 Tx通道使用, MultiPoints: 多点校准 + */ +typedef enum { + CX9261A_CALM_TST = 0, + CX9261A_CALM_Bisec, + CX9261A_CALM_MultiPoints, + CX9261A_CALM_Tracking +} CX9261A_CalibMethod; + +/** + * @brief DC校准方法 Tracking: 跟踪校准 HighPass: 高通滤波器滤掉直流 + */ +typedef enum { + CX9261A_DCCALM_Tracking=0, + CX9261A_DCCALM_HighPass +} CX9261A_DCCalibMethod; + + +typedef struct { + CX9261A_LoSource first; + CX9261A_LoSource second; +} CX9261A_LoSourcePair; + +typedef struct { + CX9261A_LoSourcePair key; + uint16_t value; +} CX9261A_SrcMapEntry; + +/** + * @brief 接收通道本振来源映射表 + */ +static const CX9261A_SrcMapEntry CX9261A_rxSrcMap[] = { + { { CX9261A_LOSRC_Pll2, CX9261A_LOSRC_Pll2 }, 0b0010000 }, + { { CX9261A_LOSRC_Pll1, CX9261A_LOSRC_Pll1 }, 0b1101000 }, + { { CX9261A_LOSRC_Pll2, CX9261A_LOSRC_Pll1 }, 0b0100000 }, + { { CX9261A_LOSRC_Pll1, CX9261A_LOSRC_Pll2 }, 0b1000000 }, + { { CX9261A_LOSRC_Off, CX9261A_LOSRC_Pll1 }, 0b0101010 }, + { { CX9261A_LOSRC_Off, CX9261A_LOSRC_Pll2 }, 0b1010010 }, + { { CX9261A_LOSRC_Pll1, CX9261A_LOSRC_Off }, 0b1001001 }, + { { CX9261A_LOSRC_Pll2, CX9261A_LOSRC_Off }, 0b0110001 }, + { { CX9261A_LOSRC_Off, CX9261A_LOSRC_Off }, 0b0011011 } +}; +/** + * @brief 发射通道本振来源映射表 + */ +static const CX9261A_SrcMapEntry CX9261A_txSrcMap[] = { + { { CX9261A_LOSRC_Pll4, CX9261A_LOSRC_Pll4 }, 16 }, { { CX9261A_LOSRC_Pll3, CX9261A_LOSRC_Pll3 }, 104 }, + { { CX9261A_LOSRC_Pll4, CX9261A_LOSRC_Pll3 }, 32 }, { { CX9261A_LOSRC_Pll3, CX9261A_LOSRC_Pll4 }, 64 }, + { { CX9261A_LOSRC_Pll2, CX9261A_LOSRC_Pll2 }, 1560 }, { { CX9261A_LOSRC_Pll1, CX9261A_LOSRC_Pll1 }, 1944 }, + { { CX9261A_LOSRC_Pll2, CX9261A_LOSRC_Pll1 }, 1688 }, { { CX9261A_LOSRC_Pll1, CX9261A_LOSRC_Pll2 }, 1816 }, + { { CX9261A_LOSRC_Off, CX9261A_LOSRC_Pll3 }, 42 }, { { CX9261A_LOSRC_Off, CX9261A_LOSRC_Pll4 }, 82 }, + { { CX9261A_LOSRC_Off, CX9261A_LOSRC_Pll2 }, 794 }, { { CX9261A_LOSRC_Off, CX9261A_LOSRC_Pll1 }, 666 }, + { { CX9261A_LOSRC_Pll3, CX9261A_LOSRC_Off }, 73 }, { { CX9261A_LOSRC_Pll4, CX9261A_LOSRC_Off }, 49 }, + { { CX9261A_LOSRC_Pll2, CX9261A_LOSRC_Off }, 1177 }, { { CX9261A_LOSRC_Pll1, CX9261A_LOSRC_Off }, 1305 }, + { { CX9261A_LOSRC_Off, CX9261A_LOSRC_Off }, 27 }, { { CX9261A_LOSRC_Pll1, CX9261A_LOSRC_Pll3 }, 1320 }, + { { CX9261A_LOSRC_Pll1, CX9261A_LOSRC_Pll4 }, 1360 }, { { CX9261A_LOSRC_Pll2, CX9261A_LOSRC_Pll3 }, 1064 }, + { { CX9261A_LOSRC_Pll2, CX9261A_LOSRC_Pll4 }, 1104 }, { { CX9261A_LOSRC_Pll3, CX9261A_LOSRC_Pll1 }, 712 }, + { { CX9261A_LOSRC_Pll4, CX9261A_LOSRC_Pll1 }, 688 }, { { CX9261A_LOSRC_Pll3, CX9261A_LOSRC_Pll2 }, 584 }, + { { CX9261A_LOSRC_Pll4, CX9261A_LOSRC_Pll2 }, 560 } +}; +/** + * @brief 环路参数选择表 + */ +static const uint8_t CX9261A_coreTable[7][4][3] = { + { { 0x0C, 0x30, 0x08 }, { 0x0B, 0x30, 0x09 }, { 0x0C, 0x30, 0x05 }, { 0x0D, 0x30, 0x08 } }, + { { 0x0C, 0x30, 0x0C }, { 0x0B, 0x30, 0x0E }, { 0x0C, 0x30, 0x08 }, { 0x0D, 0x30, 0x0B } }, + { { 0x0C, 0x20, 0x10 }, { 0x0B, 0x20, 0x10 }, { 0x0C, 0x20, 0x0A }, { 0x0D, 0x20, 0x0D } }, + { { 0x0C, 0x20, 0x11 }, { 0x0B, 0x20, 0x13 }, { 0x0C, 0x20, 0x0C }, { 0x0D, 0x20, 0x0F } }, + { { 0x0D, 0x30, 0x14 }, { 0x0D, 0x22, 0x14 }, { 0x0D, 0x20, 0x0D }, { 0x0D, 0x15, 0x0C } }, + { { 0x0E, 0x30, 0x18 }, { 0x0E, 0x25, 0x16 }, { 0x0E, 0x25, 0x0F }, { 0x0E, 0x18, 0x0F } }, + { { 0x0F, 0x38, 0x1A }, { 0x0F, 0x32, 0x1C }, { 0x0F, 0x18, 0x12 }, { 0x0F, 0x18, 0x12 } } +}; + + +/** + * @brief AGC PDT模式的结构体 + */ +typedef struct +{ + int pdtN; + int pdtM; +} CX9261A_AGCPDT; + +/** + * @brief AGC LMT模式的结构体 + */ +typedef struct +{ + int count; /**< LMT统计次数阈值*/ + int threshold1; /**< LMT阈值第一档*/ + int threshold2; /**< LMT阈值第二档*/ + int threshold3; /**< LMT阈值第三档*/ + int threshold4; /**< LMT阈值第四档*/ +} CX9261A_AGCLMT; + +/** + * @brief AGC DIG模式的结构体 + */ +typedef struct +{ + int count; /**< DIG统计次数阈值*/ + int threshold1; /**< DIG阈值第一档*/ + int threshold2; /**< DIG阈值第二档*/ + int threshold3; /**< DIG阈值第三档*/ + int threshold4; /**< DIG阈值第四档*/ +} CX9261A_AGCDIG; + +/** + * @brief AGC模式的结构体 + */ +typedef struct +{ + int mode; /**< AGC模式选择*/ + CX9261A_AGCPDT pdt; /**< AGC PDT模式的结构体*/ + CX9261A_AGCLMT lmt; /**< AGC LMT模式的结构体*/ + CX9261A_AGCDIG dig; /**< AGC DIG模式的结构体*/ +} CX9261A_AGC; + +/** + * @brief 时钟相关参数结构体 + */ +typedef struct +{ + double ref; /**< 参考时钟频率,单位MHz*/ + double adc; /**< ADC频率,单位MHz*/ + int refDiv; /**< 参考时钟分频比*/ + bool bbUseIntDiv; /**< 是否使用整数分频模式*/ + bool refDoubler; /**< 是否使用参考时钟倍频器*/ +} CX9261A_Clock; + +/** + * @brief 校准相关参数结构体 + */ +typedef struct +{ + bool calibDC; /**< 是否开启DC Tracking*/ + CX9261A_DCCalibMethod dcCalibMethod; + int dcTrackingLength; /**< DC Tracking长度*/ + int dcHighPassFactor; /**< DC高通滤波系数,越大代表滤波器阶数越高,滤波范围越窄*/ + uint8_t rx1CalibPll; /**< rx1校准的pll选择,Pll1 | Pll2*/ + uint8_t rx2CalibPll; /**< rx2校准的pll选择,Pll1 | Pll2*/ + uint8_t rx3CalibPll; /**< rx3校准的pll选择,Pll5*/ + uint8_t tx1CalibPll; /**< tx1校准的pll选择,Pll1 | Pll2 | Pll3 | Pll4*/ + uint8_t tx2CalibPll; /**< tx2校准的pll选择,Pll1 | Pll2 | Pll3 | Pll4*/ + CX9261A_CalibMethod rxMethod; + CX9261A_CalibMethod txMethod; + double f0; /**< 校准音频率*/ + uint32_t numRxPoint; /**< Rx多点校准的频率个数*/ + uint32_t numTxPoint; /**< Tx多点校准的频率个数*/ + double rxMultiPoints[MAX_MULTI_POINTS]; /**< Rx多点校准的频率*/ + double txMultiPoints[MAX_MULTI_POINTS]; /**< Tx多点校准的频率*/ +} CX9261A_Calib; + +/** + * @brief 配置PLL来源相关参数结构体 + */ +typedef struct +{ + bool rxUseExt; /**< 接收通道是否使用外本振*/ + bool txUseExt; /**< 发射通道是否使用外本振*/ + CX9261A_LoSource rx1Source; /**< Rx1通道本振来源*/ + CX9261A_LoSource rx2Source; /**< Rx2通道本振来源*/ + CX9261A_LoSource rx3Source; /**< Rx3通道本振来源*/ + CX9261A_LoSource tx1Source; /**< Tx1通道本振来源*/ + CX9261A_LoSource tx2Source; /**< Tx2通道本振来源*/ +} CX9261A_PllSource; + +/** + * @brief 配置PLL频率相关参数结构体 + */ +typedef struct +{ + double pll1Freq; /**< PLL1本振时钟,单位MHz*/ + double pll2Freq; /**< PLL2本振时钟,单位MHz*/ + double pll3Freq; /**< PLL3本振时钟,单位MHz*/ + double pll4Freq; /**< PLL4本振时钟,单位MHz*/ + double pll5Freq; /**< PLL5本振时钟,单位MHz*/ + double rxExtFreq; /**< 接收通道使用外本振时,外本振频率,单位MHz*/ + double txExtFreq; /**< 发射通道使用外本振时,外本振频率,单位MHz*/ +} CX9261A_PllFreq; + +/** + * @brief 配置接口相关参数结构体 + */ +typedef struct +{ + CX9261A_WorkMode workMode; /**< 芯片工作模式*/ + CX9261A_TrMode trMode; /**< 收发数据通道模式*/ + CX9261A_PortMode portMode; /**< 收发接口通道模式*/ + CX9261A_IOMode ioMode; /**< 接口电平模式*/ +} CX9261A_Interface; + +/** + * @brief 配置TX NCO相关参数结构体 + */ +typedef struct +{ + bool txUseBist; /**< 是否开启BIST模式*/ + double txNCOFreq; /**< NCO频率*/ +} CX9261A_TxNCO; + +/** + * @brief 配置RX NCO相关参数结构体 + */ +typedef struct +{ + bool rxUseBist; /**< 是否开启BIST模式*/ + bool rxNCOBypass; /**< 是否Bypass NCO*/ + bool rxNCOUpward; /**< 开启上/下混频模式*/ + bool rxNCOMaster; /**< 使用Master/Slaver模式*/ + double rxNCOFreq; /**< NCO频率*/ +} CX9261A_RxNCO; + +/** + * @brief 配置接收通道相关参数结构体 + */ +typedef struct { + double bandwidth; /**< 接收通道1dB带宽,单位MHz*/ + int tia; /**< 接收通道TIA增益配置,推荐配置6*/ + int mixer; /**< 接收通道Mixer增益配置*/ +} CX9261A_RxConfig; + +/** + * @brief 配置发射通道相关参数结构体 + */ +typedef struct { + double bandwidth; /**< 发射通道1dB带宽,单位MHz*/ + int mixer; /**< 发射通道Mixer增益配置*/ +} CX9261A_TxConfig; + +/** + * @brief 配置滤波器相关参数结构体,0代表bypass + */ +typedef struct { + int fir; /**< FIR滤波器抽取/插值倍数*/ + int hbf1; /**< HBF1滤波器抽取/插值倍数*/ + int hbf2; /**< HBF2滤波器抽取/插值倍数*/ + int hbf3; /**< HBF3滤波器抽取/插值倍数*/ + int cic; /**< CIC滤波器抽取/插值倍数*/ +} CX9261A_Filters; + +/** + * @brief 配置电源模式结构体,1代表该模块上电 + */ +typedef struct { + bool dig_tx2; + bool dig_tx1; + bool dig_rx3; + bool dig_rx2; + bool dig_rx1; + bool fref1_rx; + bool auxadc2; + bool auxadc1; + bool auxdac; + bool temp_sensor; + bool bgr; + bool exlo; + bool tx2; + bool tx1; + bool rx3; + bool rx2; + bool rx1; + bool pll5_ldo; + bool pll5; + bool pll4_ldo; + bool pll4; + bool pll3_ldo; + bool pll3; + bool pll2_ldo; + bool pll2; + bool pll1_ldo; + bool pll1; + bool bbpll; +} CX9261A_Power; + +/** + * @brief 配置电源模式管理,包含模式选择,接收模式,发射模式,FDD模式 + */ +typedef struct { + CX9261A_PowerMode powerMode; + CX9261A_Power rxPower; + CX9261A_Power txPower; + CX9261A_Power fddPower; +} CX9261A_PowerManagement; + +/** + * @brief 储存窄带校准结果的结构体 + */ +typedef struct { + int bHigh; + int bLow; + int cHigh; + int cLow; + int dciHigh; + int dciLow; + int dcqHigh; + int dcqLow; +} CX9261A_CalibPredis; + +/** + * @brief CX9261A结构体 + */ +typedef struct { + CX9261A_TemperatureComp tempComp; /**< 温度补偿*/ + CX9261A_AGC agc; /**< AGC结构体*/ + CX9261A_Interface interface; /**< Interface结构体*/ + CX9261A_Clock clock; /**< Clock结构体*/ + CX9261A_PllSource pllSource; /**< PllSource结构体*/ + CX9261A_Calib calib; /**< Calib结构体*/ + CX9261A_PllFreq pllFreq; /**< PllFreq结构体*/ + CX9261A_TxNCO txNco; /**< TxNCO结构体*/ + CX9261A_RxNCO rxNco; /**< RxNCO结构体*/ + CX9261A_RxConfig rx1Config; /**< RxConfig结构体*/ + CX9261A_RxConfig rx2Config; /**< RxConfig结构体*/ + CX9261A_RxConfig rx3Config; /**< RxConfig结构体*/ + CX9261A_TxConfig tx1Config; /**< TxConfig结构体*/ + CX9261A_TxConfig tx2Config; /**< TxConfig结构体*/ + CX9261A_Filters rxFilters; /**< Filters结构体*/ + CX9261A_Filters txFilters; /**< Filters结构体*/ + CX9261A_PowerManagement powerManagement; /**< PowerManagement结构体*/ + uint32_t numRxFIR; + uint32_t numTxFIR; + double rxFIRCoe[MAX_NUM_FIR]; /**< Rx FIR滤波器系数*/ + double txFIRCoe[MAX_NUM_FIR]; /**< Tx FIR滤波器系数*/ +} CX9261A_CX9261AConfiguration; + +typedef struct { + int first; + int second; +}QecStatePair; + + +// 定义函数指针类型 +typedef void (*CX9261A_LoggingFunc)(const char* msg); +typedef void (*CX9261A_WriteSpiFunc)(int address, int value); +typedef int (*CX9261A_ReadSpiFunc)(int address); +typedef void (*CX9261A_PauseFunc)(double ms); +typedef void (*CX9261A_SetPinFunc)(int pin, int value); + +/** + * @brief 绑定日志函数 + * @param msg 要输出的内容 + */ +void CX9261A_setLoggingFunc(CX9261A_LoggingFunc func); +/** + * @brief 绑定SPI写入寄存器函数 + * @param address 要写入的寄存器地址 + * @param value 要写入的寄存器值 + */ +void CX9261A_setWriteSpiFunc(CX9261A_WriteSpiFunc func); +/** + * @brief 绑定等待函数 + * @param ms 等待时间,单位毫秒 + */ +void CX9261A_setReadSpiFunc(CX9261A_ReadSpiFunc func); +/** + * @brief 绑定SPI读取寄存器函数 + * @param address 要读取的寄存器地址 + * @return 返回的寄存器值 + */ +void CX9261A_setPauseFunc(CX9261A_PauseFunc func); +/** + * @brief 绑定配置FPGA对应寄存器函数 + * @param pin 寄存器地址 + * @param value 寄存器值 + */ +void CX9261A_setSetPinFunc(CX9261A_SetPinFunc func); + +/** + * @brief 初始化芯片状态,写入与具体配置无关的寄存器值 + */ +void CX9261A_initialChip(); +/** + * @brief 初始化电源配置,如果只自定义配置部分模式下的电源管理,在自定义配置之前需要先调用此函数 + */ +void CX9261A_initialPowerConfig(const CX9261A_CX9261AConfiguration *config); +/** + * @brief 初始化BBPLL相关寄存器,如果调用了initialChip,则无需单独调用此函数 + */ +void CX9261A_initialBBPLL(); +/** + * @brief 初始化PLL1相关寄存器,如果调用了initialChip,则无需单独调用此函数 + */ +void CX9261A_initialPLL1(); +/** + * @brief 初始化PLL2相关寄存器,如果调用了initialChip,则无需单独调用此函数 + */ +void CX9261A_initialPLL2(); +/** + * @brief 初始化PLL3相关寄存器,如果调用了initialChip,则无需单独调用此函数 + */ +void CX9261A_initialPLL3(); +/** + * @brief 初始化PLL4相关寄存器,如果调用了initialChip,则无需单独调用此函数 + */ +void CX9261A_initialPLL4(); +/** + * @brief 初始化PLL5相关寄存器,如果调用了initialChip,则无需单独调用此函数 + */ +void CX9261A_initialPLL5(); +/** + * @brief 配置受参考时钟频率影响的寄存器 + * @param ref 参考时钟频率,单位MHz + */ +void CX9261A_configRef(double ref); +/** + * @brief 配置时钟相关寄存器,包括ADC和BBPLL + * @param config CX9261A参数结构体 + */ +void CX9261A_configBBPLL(const CX9261A_CX9261AConfiguration *config); +/** + * @brief BBPLL AFC + * @return 是否锁定 + */ +bool CX9261A_afcBBPll(); +/** + * @brief 配置PLL1频率 + * @param lo 目标频率 + * @param clock 时钟结构体,使用其中的ref和refDoubler属性 + */ +void CX9261A_configPll1(double lo, const CX9261A_Clock *clock); +/** + * @brief Pll1 AFC + * @return 是否锁定 + */ +bool CX9261A_afcPll1(); +/** + * @brief 配置PLL2频率 + * @param lo 目标频率 + * @param clock 时钟结构体,使用其中的ref和refDoubler属性 + */ +void CX9261A_configPll2(double lo, const CX9261A_Clock *clock); +/** + * @brief Pll2 AFC + * @return 是否锁定 + */ +bool CX9261A_afcPll2(); +/** + * @brief 配置PLL3频率 + * @param lo 目标频率 + * @param clock 时钟结构体,使用其中的ref和refDoubler属性 + */ +void CX9261A_configPll3(double lo, const CX9261A_Clock *clock); +/** + * @brief Pll3 AFC + * @return 是否锁定 + */ +bool CX9261A_afcPll3(); +/** + * @brief 配置PLL4频率 + * @param lo 目标频率 + * @param clock 时钟结构体,使用其中的ref和refDoubler属性 + */ +void CX9261A_configPll4(double lo, const CX9261A_Clock *clock); +/** + * @brief Pll4 AFC + * @return 是否锁定 + */ +bool CX9261A_afcPll4(); +/** + * @brief 配置PLL5频率 + * @param lo 目标频率 + * @param clock 时钟结构体,使用其中的ref和refDoubler属性 + */ +void CX9261A_configPll5(double lo, const CX9261A_Clock *clock); +/** + * @brief Pll5 AFC + * @return 是否锁定 + */ +bool CX9261A_afcPll5(); +/** + * @brief 配置AGC模式 + * @param mode 模式0~13 + */ +void CX9261A_configAGCMode(int mode); +/** + * @brief 配置AGC PDT + * @param pdt AGCPDT结构体 + */ +void CX9261A_configAGCPDT(const CX9261A_AGCPDT *pdt); +/** + * @brief 配置AGC LMT + * @param lmt AGCLMT结构体 + */ +void CX9261A_configAGCLMT(const CX9261A_AGCLMT *lmt); +/** + * @brief 配置AGC DIG + * @param dig AGCDIG结构体 + */ +void CX9261A_configAGCDIG(const CX9261A_AGCDIG *dig); +/** + * @brief 配置AGC + * @param agc AGC结构体 + */ +void CX9261A_configAGC(const CX9261A_AGC *agc); +/** + * @brief 配置片内处理器,仅在使用需要处理器辅助的校准方法时需要开启处理器 + * @param on 开启/关闭 + */ +void CX9261A_configRISCV(bool on); +/** + * @brief 配置温补 + * @param mode 温补模式 DAC/Band + */ +void CX9261A_configTemperatureCompensation(CX9261A_TemperatureComp mode); +/** + * @brief ADC校准 + * @param adc ADC速率 + */ +void CX9261A_configADCCalib(double adc); +/** + * @brief Flash校准 + */ +void CX9261A_configFlashCalib(); +/** + * @brief QEC校准会改变部分寄存器配置,在进行校准之前需要保存这部分寄存器,校准后恢复 + * @return 包含寄存器地址和值的map + */ +void CX9261A_beforeQecState(QecStatePair* buff, size_t *len); +/** + * @brief 恢复QEC校准过程中改变的寄存器 + * @param state beforeQecState函数返回的map + */ +void CX9261A_restoreQecState(QecStatePair* buff, size_t len); +/** + * @brief 根据pll来源配置校准模式下的电源管理 + * @param pllSource PllSource结构体 + */ +void CX9261A_preparePowerForRxCalib(const CX9261A_PllSource *pllSource); +/** + * @brief 调整校准时Pll的配置 + * @param config CX9261A配置结构体 + * @param chan 要进行校准的通道 + */ +void CX9261A_preparePllForRxCalib(const CX9261A_CX9261AConfiguration *config, CX9261A_RxChannel chan); +/** + * @brief 等待RxTST窄带校准结束,可重写 + * @return 校准是否结束 + */ +bool CX9261A_waitRxTSTFinished(); +/** + * @brief RX时域统计校准 + * @param chan 校准的RX通道 + */ +void CX9261A_rxTSTCalib(CX9261A_RxChannel chan); +/** + * @brief RX延时线校准 + * @param fs ADC采样率 + * @param chan 校准的RX通道 + */ +void CX9261A_rxDelayCalib(double fs, CX9261A_RxChannel chan); +/** + * @brief DC Tracking + * @param config CX9261A配置结构体 + */ +void CX9261A_configDcCalib(const CX9261A_Calib *config); +void CX9261A_configDcCalibMethod(CX9261A_DCCalibMethod method); +void CX9261A_configDcTrackingLength(int length); +void CX9261A_configDcHighPassFactor(int factor); +/** + * @brief Rx校准 + * @param config CX9261A配置结构体 + * @param chan 要进行校准的通道 + */ +void CX9261A_rxCalib(const CX9261A_CX9261AConfiguration *config, CX9261A_RxChannel chan); +/** + * @brief 选择校准方法 + * @param calib 校准结构体 + */ +void CX9261A_configCalibMethod(const CX9261A_Calib *calib); +/** + * @brief 读取当前Rx chan通道的校准值 + * @param chan Rx通道 + * @return 校准值结构体 + */ +CX9261A_CalibPredis CX9261A_readRxCalibPredis(CX9261A_RxChannel chan); +/** + * @brief 储存校准值 + * @param index 第index组校准值 + * @param chan 接收通道 + * @param pll 使用的PLL + */ +void CX9261A_saveRxCalibPredis(unsigned char index, CX9261A_RxChannel chan, CX9261A_LoSource pll); +/** + * @brief 加载保存的校准值 + * @param index 第index组校准值 + * @param chan 指定加载校准值的通道 + * @param pll 使用的PLL + */ +void CX9261A_loadRxCalibPredis(unsigned char index, CX9261A_RxChannel chan, CX9261A_LoSource pll); +/** + * @brief Rx窄带多点校准 + * @param config CX9261A配置结构体 + * @param points 校准点 + * @param chan 接收通道 + */ +void CX9261A_configRxMultiPointsQec(const CX9261A_CX9261AConfiguration *config, CX9261A_RxChannel chan, const double *points, size_t len); +/** + * @brief 根据pll来源配置校准模式下的电源管理 + * @param pllSource PllSource结构体 + */ +void CX9261A_preparePowerForTxCalib(const CX9261A_PllSource *pllSource); +/** + * @brief 调整校准时Pll的配置 + * @param config CX9261A配置结构体 + */ +void CX9261A_preparePllForTxCalib(const CX9261A_CX9261AConfiguration *config); +/** + * @brief TX二分法QEC校准 + * @param f0 校准音频率 + * @param fs adc频率 + */ +void CX9261A_txBisectionQecCalib(double f0, double fs); +/** + * @brief TX时域统计法校准 + * @param config CX9261A配置结构体 + * @param chan 要校准的通道 + */ +void CX9261A_txTSTCalib(const CX9261A_CX9261AConfiguration *config, CX9261A_TxChannel chan); +/** + * @brief TX二分法DC校准 + * @param f0 校准音频率 + * @param fs adc频率 + */ +void CX9261A_txBisectionDcCalib(double f0, double fs); +/** + * @brief Tx多点校准 + * @param config CX9261A配置结构体 + * @param points 校准点 + */ +void CX9261A_configTxMultiPointsQec(const CX9261A_CX9261AConfiguration *config, const double *points, size_t len); +/** + * @brief Tx通道校准 + * @param config CX9261A配置结构体 + */ +void CX9261A_txCalib(const CX9261A_CX9261AConfiguration *config); +/** + * @brief 储存校准值 + * @param index 第index组校准值 + * @param chan 发射通道 + * @param pll pll + */ +void CX9261A_saveTxCalibPredis(unsigned char index, CX9261A_TxChannel chan, CX9261A_LoSource pll); +/** + * @brief 加载保存的校准值 + * @param index 第index组校准值 + * @param chan 指定加载校准值的通道 + * @param pll 指定加载校准值对应的PLL + */ +void CX9261A_loadTxCalibPredis(unsigned char index, CX9261A_TxChannel chan, CX9261A_LoSource pll); +/** + * @brief Qec校准顶层函数,内部根据配置选择使用对应方法进行校准 + * @param config CX9261A配置结构体 + */ +void CX9261A_configQecCalib(const CX9261A_CX9261AConfiguration *config); +/** + * @brief 配置TX NCO相关寄存器 + * @param nco TX NCO配置结构体 + * @param fs adc速率 + */ +void CX9261A_configTxNco(const CX9261A_TxNCO *nco, double fs); +/** + * @brief 配置TX NCO BIST模式 + * @param bist 开启/关闭BIST + */ +void CX9261A_configTxBIST(bool bist); +/** + * @brief 配置TX NCO频率 + * @param freq 混频频率 + * @param fs adc抽取后速率 + */ +void CX9261A_configTxNcoFreq(double freq, double fs); +/** + * @brief 配置RX NCO相关寄存器 + * @param nco RX NCO配置结构体 + * @param fs adc速率 + */ +void CX9261A_configRxNco(const CX9261A_RxNCO *nco, double fs); +/** + * @brief 配置Rx NCO模式 + * @param mode NCO模式:Bypass/BIST/Up/Down + */ +void CX9261A_configRxNcoMode(CX9261A_NCOMode mode); +/** + * @brief 配置Rx NCO模式 + * @param mode NCO模式:Master/Slaver + */ +void CX9261A_configRxNcoMS(CX9261A_NCOMode mode); +/** + * @brief 配置Rx NCO频率 + * @param freq 混频频率 + * @param fs adc速率 + */ +void CX9261A_configRxNcoFreq(double freq, double fs); +/** + * @brief 配置接口相关寄存器 + * @param interface Interface配置结构体 + */ +void CX9261A_configInterface(const CX9261A_Interface *interface); +/** + * @brief 配置PLL来源 + * @param pllSource PLlSource结构体 + */ +void CX9261A_configPllSource(const CX9261A_PllSource *pllSource); +/** + * @brief 切换电源模式 + * @param pm 电源模式 + */ +void CX9261A_configPowerMode(CX9261A_PowerMode pm); +/** + * @brief 配置电源管理 + * @param power 电源配置结构体 + * @param pm 电源模式 + */ +void CX9261A_configPowerPins(const CX9261A_Power *power, CX9261A_PowerMode pm); +/** + * @brief 配置参考倍频器 + * @param doubler 倍频器开关 + * @param ref 参考时钟频率,单位MHz + */ +void CX9261A_configRefDoubler(bool doubler, double ref); +/** + * @brief 初始化通道,调用initialChip函数会初始化RX1,RX2,RX3三个通道 + * @param chan 通道 RX1/RX2/RX3 + */ +void CX9261A_initialRxChannel(CX9261A_RxChannel chan); +/** + * @brief 配置Rx带宽 + * @note 只改变Rx带宽会导致通道增益改变,建议使用configRxChannel函数进行带宽调整 + * @param bandwidth 带宽,单位(MHz) + * @param chan 通道 Rx1/Rx2/Rx3 + */ +void CX9261A_configRxBandwidth(double bandwidth, CX9261A_RxChannel chan); +/** + * @brief 配置Rx TIA + * @note 只改变TIA会导致通道带宽改变,建议使用configRxChannel函数进行TIA调整 + * @param tia tia档位 + * @param chan 通道 Rx1/Rx2/Rx3 + */ +void CX9261A_configRxTIA(int tia, CX9261A_RxChannel chan); +/** + * @brief 配置Rx Mixer + * @param mixer mixer档位 + */ +void CX9261A_configRxMixer(int mixer); +/** + * @brief 配置Rx通道,包含TIA,Mixer以及Bandwidth + * @param config RxConfig结构体 + * @param chan Rx1/Rx2/Rx3 + */ +void CX9261A_configRxChannel(const CX9261A_RxConfig *config, CX9261A_RxChannel chan); +/** + * @brief 配置Rx FIR滤波器抽取倍数 + * @param fir 抽取倍数 0(Bypass)/1/2/3/4/8 + */ +void CX9261A_configRxFIR(int fir); +/** + * @brief 配置Rx HBF1滤波器抽取倍数 + * @param hbf 抽取倍数 0(Bypass)/2 + */ +void CX9261A_configRxHBF1(int hbf); +/** + * @brief 配置Rx HBF2滤波器抽取倍数 + * @param hbf 抽取倍数 0(Bypass)/2 + */ +void CX9261A_configRxHBF2(int hbf); +/** + * @brief 配置Rx HBF3滤波器抽取倍数 + * @param hbf 抽取倍数 0(Bypass)/2/3 + */ +void CX9261A_configRxHBF3(int hbf); +/** + * @brief 配置Rx CIC滤波器 + * @param cic 抽取倍数 0(Bypass)/20 + */ +void CX9261A_configRxCIC(int cic); +/** + * @brief 配置Rx滤波器 + * @param filters Filters结构体 + */ +void CX9261A_configRxFilters(const CX9261A_Filters *filters); +/** + * @brief 配置Rx FIR滤波器系数 + * @param coe 滤波器系数,长度为16的整倍数,最大128 + */ +void CX9261A_configRxFIRCoe(const double *coe, size_t len); +/** + * @brief 配置Tx带宽 + * @param bandwidth 带宽,单位(MHz) + * @param chan 通道 TX1/TX2 + */ +void CX9261A_configTxBandwidth(double bandwidth, CX9261A_TxChannel chan); +/** + * @brief 配置Tx Mixer + * @param mixer 混频器值 + * @param chan TX1/TX2 + */ +void CX9261A_configTxMixer(int mixer, CX9261A_TxChannel chan); +/** + * @brief 初始化Tx通道 + * @param chan 通道 TX1/TX2 + */ +void CX9261A_initialTxChannel(CX9261A_TxChannel chan); +/** + * @brief 根据Tx通道的本振频率,配置对应的参数 + * @param lo 本振频率,单位(MHz) + * @param chan 通道(Tx1/Tx2) + */ +void CX9261A_configTxChannelFrequency(double lo, CX9261A_TxChannel chan); +/** + * @brief 配置Tx通道,Mixer以及Bandwidth + * @param config TxConfig结构体 + * @param chan Tx1/Tx2 + */ +void CX9261A_configTxChannel(const CX9261A_TxConfig *config, CX9261A_TxChannel chan); +/** + * @brief 配置Tx FIR滤波器插值倍数 + * @param fir 插值倍数 0(Bypass)/1/2/3/4/8 + */ +void CX9261A_configTxFIR(int fir); +/** + * @brief 配置Tx HBF1滤波器插值倍数 + * @param hbf 插值倍数 0(Bypass)/2 + */ +void CX9261A_configTxHBF1(int hbf); +/** + * @brief 配置Tx HBF2滤波器插值倍数 + * @param hbf 插值倍数 0(Bypass)/2 + */ +void CX9261A_configTxHBF2(int hbf); +/** + * @brief 配置Tx HBF3滤波器插值倍数 + * @param hbf 插值倍数 0(Bypass)/2/3 + */ +void CX9261A_configTxHBF3(int hbf); +/** + * @brief 配置Tx CIC滤波器 + * @param cic 插值倍数 0(Bypass)/8/20/32/80 + */ +void CX9261A_configTxCIC(int cic); +/** + * @brief 配置Tx滤波器 + * @param filters Filters结构体 + */ +void CX9261A_configTxFilters(const CX9261A_Filters *filters); +/** + * @brief 配置Tx FIR滤波器系数 + * @param coe 滤波器系数,长度为16的整倍数,最大128 + */ +void CX9261A_configTxFIRCoe(const double *coe, size_t len); +/** + * @brief 读取当前Tx chan通道的校准值 + * @param chan Tx通道 + * @return 校准值结构体 + */ +CX9261A_CalibPredis CX9261A_readTxCalibPredis(CX9261A_TxChannel chan); +/** + * @brief 此函数为Demo板配套配置函数,用于配置FPGA接口,用户开发自己的程序时应重写对应功能 + * @param interface Interface结构体 + */ +void CX9261A_configFPGA(const CX9261A_Interface *interface); + +#ifdef __cplusplus +} +#endif + +#endif // __CX9261ADRIVER_C_H diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/cx9261a/lib/.project b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/cx9261a/lib/.project new file mode 100644 index 0000000..5b82956 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/cx9261a/lib/.project @@ -0,0 +1,26 @@ + + + link_test + Created by SDK v2019.1. standalone_ps7_cortexa9_0 - ps7_cortexa9_0 + + standalone_ps7_cortexa9_0 + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/cx9261a/lib/libcx9261a_c.a b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/drivers/rf-transceiver/cx9261a/lib/libcx9261a_c.a new file mode 100644 index 0000000000000000000000000000000000000000..c177de7bb35b35b82f1ba7edfee5e4199aaea362 GIT binary patch literal 149108 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

P9Npp7_yWMekp!AB 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 z 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μ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≶aD%T_vu&Qy@xq% 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 &f3eVgu$yszHW502_QU4zVK*Sw>Or(1 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�V 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 Ml24Jv#{P}}53yAzdH?_b literal 0 HcmV?d00001 diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/hwconfig.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/hwconfig.c new file mode 100644 index 0000000..75588b9 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/hwconfig.c @@ -0,0 +1,927 @@ +/*****************************************************************************/ +/** + * Description: hwconfig.h + * + + * Date: 2024-02-20 13:19 + * Last Edit Time: [2024-02-20 13:19] + * + * Revisions: Rev 0.0 + * History: + * [2023-05-07]: Initial edition. + * + * Copyright(c) 2024 Beijing Institute of Technology. + * + ******************************************************************************/ + +/***************************** Include Files *********************************/ +// Standard +#include +#include +// Xilinx +#include "xgpiops.h" +#include "xilinx_io.h" +#include "drivers/axi_core/spi_engine/spi_engine.h" +// App +#include "common.h" +#include "error.h" +#include "delay.h" +#include "system_monitor.h" +#include "hwconfig.h" +#include "config/app_config.h" +#include "dev_profile/dev_profile.h" + +#ifdef RFSOC +#include "xrfdc.h" +#endif + +/************************** Function Prototypes ******************************/ +/*********************** Driver Instance Definitions **************************/ +static struct hmc7044_dev *hmc7044_inst; +ad9528Device_t *ad9528_inst; +static struct lmk04828_dev *lmk04828_inst; +static struct lmx2594_dev *lmx2594_adc_inst; +static struct lmx2594_dev *lmx2594_dac_inst; +static struct ad9361_reg_dev *ad9361_1_reg_inst; +static struct ad9361_reg_dev *ad9361_2_reg_inst; +static struct cx9261a_dev *cx9261a_inst; +static struct ad9361_rf_phy *ad9361_phy; +static struct ad9653_dev *ad9653_inst; +static struct ad9694_dev *ad9694_inst; + +XGpioPs Gpio; +#ifdef RFSOC +XRFdc RFdc; +#endif + +/*****************************************************************************/ +/** + * @brief Hardware_SwitchFreq + * + * @param devSysMonitor_UpdateHardwareStatus(newHwState); + * @param freq + * + * @return None. + * + ******************************************************************************/ +int32_t Hardware_SwitchFreq(struct ad9361_reg_dev *dev, ad9361TxFreq freq) +{ + int32_t status = 0; + uint8_t value = 0; + int32_t time_out_cnt = 0; + + switch (freq) + { + case TX_FREQ_125: + { + //************************************************************ + // Setup Tx Frequency-Dependent Syntheisizer Registers + //************************************************************ + status = ad9361_reg_write(dev, 0x027A, 0x4A); // Set VCO Output level[3:0] + status = ad9361_reg_write(dev, 0x0279, 0xC1); // Set Init ALC Value[3:0] and VCO Varactor[3:0] + status = ad9361_reg_write(dev, 0x0282, 0x0E); // Set VCO Bias Tcf[1:0] and VCO Bias Ref[2:0] + status = ad9361_reg_write(dev, 0x0278, 0x78); // Set VCO Cal Offset[3:0] + status = ad9361_reg_write(dev, 0x0285, 0x00); // Set VCO Cal Ref Tcf[2:0] + status = ad9361_reg_write(dev, 0x0291, 0x0B); // Set VCO Varactor Reference[3:0] + status = ad9361_reg_write(dev, 0x0290, 0x70); // Set VCO Varactor Ref Tcf[2:0] and VCO Varactor Offset[3:0] + status = ad9361_reg_write(dev, 0x027B, 0x94); // Set Synth Loop Filter charge pump current (Icp) + status = ad9361_reg_write(dev, 0x027E, 0xF4); // Set Synth Loop Filter C2 and C1 + status = ad9361_reg_write(dev, 0x027F, 0xDF); // Set Synth Loop Filter R1 and C3 + status = ad9361_reg_write(dev, 0x0280, 0x0A); // Set Synth Loop Filter R3 + + //************************************************************ + // Write Tx Frequency Words + //************************************************************ + status = ad9361_reg_write(dev, 0x0273, 0x00); // Write Tx Synth Fractional Freq Word[7:0] + status = ad9361_reg_write(dev, 0x0274, 0x00); // Write Tx Synth Fractional Freq Word[15:8] + status = ad9361_reg_write(dev, 0x0275, 0x00); // Write Tx Synth Fractional Freq Word[22:16] + status = ad9361_reg_write(dev, 0x0272, 0x00); // Write Tx Synth Integer Freq Word[10:8] + status = ad9361_reg_write(dev, 0x0271, 0xA0); // Write Tx Synth Integer Freq Word[7:0] (starts VCO cal) + status = ad9361_reg_write(dev, 0x0005, 0x57); // Set LO divider setting + + //************************************************************ + // Tx Quadrature Calibration Settings + //************************************************************ + status = ad9361_reg_write(dev, 0x00A0, 0x75); // Set TxQuadcal NCO frequency + status = ad9361_reg_write(dev, 0x00A3, 0xC0); // Set TxQuadcal NCO frequency (Only update bits [7:6]) + status = ad9361_reg_write(dev, 0x00A1, 0x7B); // Tx Quad Cal Configuration, Phase and Gain Cal Enable + status = ad9361_reg_write(dev, 0x00A9, 0xFF); // Set Tx Quad Cal Count + status = ad9361_reg_write(dev, 0x00A2, 0x7F); // Set Tx Quad Cal Kexp + status = ad9361_reg_write(dev, 0x00A5, 0x01); // Set Tx Quad Cal Magnitude Threshhold + status = ad9361_reg_write(dev, 0x00A6, 0x01); // Set Tx Quad Cal Magnitude Threshhold + status = ad9361_reg_write(dev, 0x00AA, 0x22); // Set Tx Quad Cal Gain Table index + status = ad9361_reg_write(dev, 0x00A4, 0xF0); // Set Tx Quad Cal Settle Count + status = ad9361_reg_write(dev, 0x00AE, 0x00); // Set Tx Quad Cal LPF Gain index incase Split table mode used + + status = ad9361_reg_write(dev, 0x0169, 0xC0); // Disable Rx Quadrature Calibration before Running Tx Quadrature Calibration + status = ad9361_reg_write(dev, 0x0016, 0x10); // Start Tx Quad cal + + break; + } + case TX_FREQ_126: + { + //************************************************************ + // Setup Tx Frequency-Dependent Syntheisizer Registers + //************************************************************ + status = ad9361_reg_write(dev, 0x027A, 0x4A); // Set VCO Output level[3:0] + status = ad9361_reg_write(dev, 0x0279, 0xC1); // Set Init ALC Value[3:0] and VCO Varactor[3:0] + status = ad9361_reg_write(dev, 0x0282, 0x0E); // Set VCO Bias Tcf[1:0] and VCO Bias Ref[2:0] + status = ad9361_reg_write(dev, 0x0278, 0x78); // Set VCO Cal Offset[3:0] + status = ad9361_reg_write(dev, 0x0285, 0x00); // Set VCO Cal Ref Tcf[2:0] + status = ad9361_reg_write(dev, 0x0291, 0x0B); // Set VCO Varactor Reference[3:0] + status = ad9361_reg_write(dev, 0x0290, 0x70); // Set VCO Varactor Ref Tcf[2:0] and VCO Varactor Offset[3:0] + status = ad9361_reg_write(dev, 0x027B, 0x93); // Set Synth Loop Filter charge pump current (Icp) + status = ad9361_reg_write(dev, 0x027E, 0xF4); // Set Synth Loop Filter C2 and C1 + status = ad9361_reg_write(dev, 0x027F, 0xDF); // Set Synth Loop Filter R1 and C3 + status = ad9361_reg_write(dev, 0x0280, 0x0A); // Set Synth Loop Filter R3 + + //************************************************************ + // Write Tx Frequency Words + //************************************************************ + status = ad9361_reg_write(dev, 0x0273, 0x06); // Write Tx Synth Fractional Freq Word[7:0] + status = ad9361_reg_write(dev, 0x0274, 0xD7); // Write Tx Synth Fractional Freq Word[15:8] + status = ad9361_reg_write(dev, 0x0275, 0x23); // Write Tx Synth Fractional Freq Word[22:16] + status = ad9361_reg_write(dev, 0x0272, 0x00); // Write Tx Synth Integer Freq Word[10:8] + status = ad9361_reg_write(dev, 0x0271, 0xA1); // Write Tx Synth Integer Freq Word[7:0] (starts VCO cal) + status = ad9361_reg_write(dev, 0x0005, 0x57); // Set LO divider setting + + //************************************************************ + // Tx Quadrature Calibration Settings + //************************************************************ + status = ad9361_reg_write(dev, 0x00A0, 0x75); // Set TxQuadcal NCO frequency + status = ad9361_reg_write(dev, 0x00A3, 0xC0); // Set TxQuadcal NCO frequency (Only update bits [7:6]) + status = ad9361_reg_write(dev, 0x00A1, 0x7B); // Tx Quad Cal Configuration, Phase and Gain Cal Enable + status = ad9361_reg_write(dev, 0x00A9, 0xFF); // Set Tx Quad Cal Count + status = ad9361_reg_write(dev, 0x00A2, 0x7F); // Set Tx Quad Cal Kexp + status = ad9361_reg_write(dev, 0x00A5, 0x01); // Set Tx Quad Cal Magnitude Threshhold + status = ad9361_reg_write(dev, 0x00A6, 0x01); // Set Tx Quad Cal Magnitude Threshhold + status = ad9361_reg_write(dev, 0x00AA, 0x22); // Set Tx Quad Cal Gain Table index + status = ad9361_reg_write(dev, 0x00A4, 0xF0); // Set Tx Quad Cal Settle Count + status = ad9361_reg_write(dev, 0x00AE, 0x00); // Set Tx Quad Cal LPF Gain index incase Split table mode used + + status = ad9361_reg_write(dev, 0x0169, 0xC0); // Disable Rx Quadrature Calibration before Running Tx Quadrature Calibration + status = ad9361_reg_write(dev, 0x0016, 0x10); // Start Tx Quad cal + + break; + } + case TX_FREQ_4125: + { + //************************************************************ + // Setup Tx Frequency-Dependent Syntheisizer Registers + //************************************************************ + status = ad9361_reg_write(dev, 0x027A, 0x4A); // Set VCO Output level[3:0] + status = ad9361_reg_write(dev, 0x0279, 0xC1); // Set Init ALC Value[3:0] and VCO Varactor[3:0] + status = ad9361_reg_write(dev, 0x0282, 0x0E); // Set VCO Bias Tcf[1:0] and VCO Bias Ref[2:0] + status = ad9361_reg_write(dev, 0x0278, 0x78); // Set VCO Cal Offset[3:0] + status = ad9361_reg_write(dev, 0x0285, 0x00); // Set VCO Cal Ref Tcf[2:0] + status = ad9361_reg_write(dev, 0x0291, 0x0B); // Set VCO Varactor Reference[3:0] + status = ad9361_reg_write(dev, 0x0290, 0x70); // Set VCO Varactor Ref Tcf[2:0] and VCO Varactor Offset[3:0] + status = ad9361_reg_write(dev, 0x027B, 0x93); // Set Synth Loop Filter charge pump current (Icp) + status = ad9361_reg_write(dev, 0x027E, 0xF4); // Set Synth Loop Filter C2 and C1 + status = ad9361_reg_write(dev, 0x027F, 0xDF); // Set Synth Loop Filter R1 and C3 + status = ad9361_reg_write(dev, 0x0280, 0x0A); // Set Synth Loop Filter R3 + + //************************************************************ + // Write Rx Frequency Words + //************************************************************ + status = ad9361_reg_write(dev, 0x0273, 0x00); // Write Tx Synth Fractional Freq Word[7:0] + status = ad9361_reg_write(dev, 0x0274, 0x00); // Write Tx Synth Fractional Freq Word[15:8] + status = ad9361_reg_write(dev, 0x0275, 0x00); // Write Tx Synth Fractional Freq Word[22:16] + status = ad9361_reg_write(dev, 0x0272, 0x00); // Write Tx Synth Integer Freq Word[10:8] + status = ad9361_reg_write(dev, 0x0271, 0xA5); // Write Tx Synth Integer Freq Word[7:0] (starts VCO cal) + status = ad9361_reg_write(dev, 0x0005, 0x07); // Set LO divider setting + + //************************************************************ + // Tx Quadrature Calibration Settings + //************************************************************ + status = ad9361_reg_write(dev, 0x00A0, 0x75); // Set TxQuadcal NCO frequency + status = ad9361_reg_write(dev, 0x00A3, 0xC0); // Set TxQuadcal NCO frequency (Only update bits [7:6]) + status = ad9361_reg_write(dev, 0x00A1, 0x7B); // Tx Quad Cal Configuration, Phase and Gain Cal Enable + status = ad9361_reg_write(dev, 0x00A9, 0xFF); // Set Tx Quad Cal Count + status = ad9361_reg_write(dev, 0x00A2, 0x7F); // Set Tx Quad Cal Kexp + status = ad9361_reg_write(dev, 0x00A5, 0x01); // Set Tx Quad Cal Magnitude Threshhold + status = ad9361_reg_write(dev, 0x00A6, 0x01); // Set Tx Quad Cal Magnitude Threshhold + status = ad9361_reg_write(dev, 0x00AA, 0x28); // Set Tx Quad Cal Gain Table index + status = ad9361_reg_write(dev, 0x00A4, 0xF0); // Set Tx Quad Cal Settle Count + status = ad9361_reg_write(dev, 0x00AE, 0x04); // Set Tx Quad Cal LPF Gain index incase Split table mode used + + status = ad9361_reg_write(dev, 0x0169, 0xC0); // Disable Rx Quadrature Calibration before Running Tx Quadrature Calibration + status = ad9361_reg_write(dev, 0x0016, 0x10); // Start Tx Quad cal + + break; + } + + // Wait for cal to complete (Done when 0x016[4]==0) + time_out_cnt = 0; + do + { + status |= ad9361_reg_read(dev, 0x0016, &value); + mdelay(1); // Wait 1ms + time_out_cnt++; + } while ((value & 0x10) != 0x00 && time_out_cnt <= AD9361_TIMEOUT_TH); + } + + return status; +} + +// @ Get AD9361 - 1 temperature (Celsius Degree) +/** + * Read corresponding register to get AD9361 - 1 current temperature. + */ +int8_t Hardware_GetAD9361Temp_1(void) +{ + int8_t scaleTemp; + double temp; + + ad9361_reg_write(ad9361_1_reg_inst, 0x000B, -64); + ad9361_reg_write(ad9361_1_reg_inst, 0x001D, 0x01); // disable aux adc + ad9361_reg_read(ad9361_1_reg_inst, 0x000E, (uint8_t*)&scaleTemp); + ad9361_reg_write(ad9361_1_reg_inst, 0x001D, 0x00); // enable aux adc + + temp = ((double)scaleTemp + 64)/1.16; + return ((temp > INT8_MAX) ? INT8_MAX : (int8_t)temp); +} + +// @ Get AD9361 - 2 temperature (Celsius Degree) +/** + * Read corresponding register to get AD9361 - 2 current temperature. + */ +int8_t Hardware_GetAD9361Temp_2(void) +{ + int8_t scaleTemp; + double temp; + + ad9361_reg_write(ad9361_2_reg_inst, 0x000B, -64); + ad9361_reg_write(ad9361_2_reg_inst, 0x001D, 0x01); // disable aux adc + ad9361_reg_read(ad9361_2_reg_inst, 0x000E, (uint8_t*)&scaleTemp); + ad9361_reg_write(ad9361_2_reg_inst, 0x001D, 0x00); // enable aux adc + temp = ((double)scaleTemp + 64)/1.16; + return ((temp > INT8_MAX) ? INT8_MAX : (int8_t)temp); +} + +// @ Determine AD9361 lock state +/** + * Read corresponding register to determine AD9361(both) lock state. + */ +uint8_t Hardware_GetAD9361LockStatus(void) +{ + uint8_t ret = 0; + uint8_t regVal; + + // bbpll + ad9361_reg_read(ad9361_1_reg_inst, 0x005E, ®Val); // [D7] is lock indicator + ret |= ((regVal & 0x80) == 0x80) ? CLK_AD0_BBPLL_LOCK : 0; + ad9361_reg_read(ad9361_2_reg_inst, 0x005E, ®Val); // [D7] is lock indicator + ret |= ((regVal & 0x80) == 0x80) ? CLK_AD1_BBPLL_LOCK : 0; + + // rfpll + ad9361_reg_read(ad9361_1_reg_inst, 0x0287, ®Val); // [D1] is lock indicator + ret |= ((regVal & 0x02) == 0x02) ? CLK_AD0_RFPLL_LOCK : 0; + ad9361_reg_read(ad9361_2_reg_inst, 0x0287, ®Val); // [D1] is lock indicator + ret |= ((regVal & 0x02) == 0x02) ? CLK_AD1_RFPLL_LOCK : 0; + + return ret; +} + +// @ Determine HMC7044 lock state +/** + * Read corresponding register to determine clock lock state. + */ +uint8_t Hardware_GetHMC7044LockStatus(void) +{ + uint8_t ret = 0; + uint8_t regVal; + + // pll1 + hmc7044_read(hmc7044_inst, 0x007C, ®Val); // [D5] is lock indicator + ret |= ((regVal & 0xE0) != 0x00) ? CLK_SP_PLL0_LOCK : 0; + + // pll2 + hmc7044_read(hmc7044_inst, 0x007D, ®Val); // [D0] is lock indicator + ret |= ((regVal & 0x01) == 0x01) ? CLK_SP_PLL1_LOCK : 0; + + return ret; +} + +void CX9261A_Log(const char *msg) +{ + DBG_INFO("[CX9261A] %s\n", msg); +} +//void CX9261A_spiWrite(int address, int value) +//{ +// int32_t Status; +// +// Status = cx9261a_write(cx9261a_inst,address,value); +// if (Status != SUCCESS) +// { +// DBG_ERROR("HW: CX9261A_spiWrite() failed!"); +// } +// +//} +//int CX9261A_spiRead(int address) +//{ +// int32_t Status; +// uint8_t reg = 0; +// +// Status = cx9261a_read(cx9261a_inst,address,®); +// if (Status != SUCCESS) +// { +// DBG_ERROR("HW: CX9261A_spiRead() failed!"); +// } +// +// return reg; +//} +//void CX9261A_mdelay(double ms) +//{ +// mdelay((uint32_t)(ms)); +//} +//void CX9261A_setPin(int pin, int value) +//{ +// switch(pin){ +// case 0: +// gpio_set_value(cx9261a_inst->gpio_desc_nrst, value); +// break; +// case 1: +// gpio_set_value(cx9261a_inst->gpio_desc_enable_trx1, value); +// break; +// case 2: +// gpio_set_value(cx9261a_inst->gpio_desc_enable_trx2, value); +// break; +// case 3: +// gpio_set_value(cx9261a_inst->gpio_desc_enable_rxfb, value); +// break; +// case 4: +// gpio_set_value(cx9261a_inst->gpio_desc_txnrx_trx1, value); +// break; +// case 5: +// gpio_set_value(cx9261a_inst->gpio_desc_txnrx_trx2, value); +// break; +// case 6: +// gpio_set_value(cx9261a_inst->gpio_desc_txnrx_rxfb, value); +// break; +// case 34: +// gpio_set_value(cx9261a_inst->gpio_desc_pl_rst, value); +// break; +// case 35: +// gpio_set_value(cx9261a_inst->gpio_desc_tr_sel, value); +// break; +// case 36: +// gpio_set_value(cx9261a_inst->gpio_desc_tdd_fdd_sel, value); +// break; +// case 37: +// gpio_set_value(cx9261a_inst->gpio_desc_r1t1_r2t2_sel, value); +// break; +// case 38: +// gpio_set_value(cx9261a_inst->gpio_desc_sp_dp_sel, value); +// break; +// case 43: +// gpio_set_value(cx9261a_inst->gpio_desc_mclk_delay_ctrl0, value); +// break; +// case 44: +// gpio_set_value(cx9261a_inst->gpio_desc_mclk_delay_ctrl1, value); +// break; +// case 45: +// gpio_set_value(cx9261a_inst->gpio_desc_mclk_delay_ctrl2, value); +// break; +// default: +// break; +// } +// +// return; +//} + +void CX9261A_spiWrite(const int address, const int value) { + u8 rx_data1; + SpiPs_write_read1(address,value,(u8 *)&rx_data1); +} + +int CX9261A_spiRead(const int address) { + u8 rx_data1; + SpiPs_write_read2(address,0,(u8 *)&rx_data1); +return rx_data1; + +} + +void CX9261A_mdelay(const double ms) { + mdelay((uint32_t)(ms)); +} + +void CX9261A_setPin(const int pin, const int value) { + GpioSetPinDir(pin,GPIO_DIR_OUTPUT); + GpioWritePin(pin,value); +} + + +void cx9261a_trx_ch_pll_config(u8 pll_index) +{ + u8 rx_data; + u8 recv_buf[3]={0}; + u32 trx_ch_pll_data; + + if(pll_index==1){ + trx_ch_pll_data = 0x00077DCF; + }else if(pll_index==2){ + trx_ch_pll_data = 0x00077D0C; + } + + cx9261a_write(cx9261a_inst,trx_ch_pll_data >> 8, trx_ch_pll_data); +} + +/*****************************************************************************/ +/** + * @brief Hardware_Init + * + * @param none. + * + * @return None. + * + ******************************************************************************/ +int32_t Hardware_Init(void) +{ + int32_t Status; + + // gpio initialization + XGpioPs_Config *ConfigPtr; + ConfigPtr = XGpioPs_LookupConfig(AD9361_GPIO_DEVICE_ID); + Status = XGpioPs_CfgInitialize(&Gpio, ConfigPtr, ConfigPtr->BaseAddr); + if (Status != SUCCESS) + { + return FAILURE; + } + + SpiPsInit(SPI_DEVICE_ID); + GpioInit(GPIO_DEVICE_ID); + +#ifdef RFSOC + /**********************************************************/ + /************* RF Data Converter Configuration ************/ + /**********************************************************/ + // rf data converter initialization + XRFdc_Config *RFdc_ConfigPtr; + RFdc_ConfigPtr = XRFdc_LookupConfig(RFDC_DEVICE_ID); + Status = XRFdc_CfgInitialize(&RFdc, RFdc_ConfigPtr); + if (Status != XST_SUCCESS) + { + return XST_FAILURE; + } +#endif + +#ifdef HMC7044 + /**********************************************************/ + /****************** HMC7044 Configuration *****************/ + /**********************************************************/ + Status = hmc7044_init(&hmc7044_inst, &hmc7044_default_init_param); + if (Status != SUCCESS) + { + DBG_ERROR("HDWR: hmc7044_init() error: %" PRId32 "\n", Status); + return FAILURE; + } +#endif + +#ifdef AD9528 + /**********************************************************/ + /****************** AD9528 Configuration ******************/ + /**********************************************************/ + Status = AD9528_init(&ad9528_inst, &ad9528_default_init_param); + if (Status != SUCCESS) { + DBG_ERROR("HDWR: AD9528_init() error: %" PRId32 "\n", Status); + return FAILURE; + } + + Status = AD9528_config(ad9528_inst); + if (Status != SUCCESS) { + return FAILURE; + DBG_ERROR("HDWR: AD9528_config() error: %" PRId32 "\n", Status); + } +#endif + +#ifdef LMK04828 + /**********************************************************/ + /***************** LMK04828 Configuration *****************/ + /**********************************************************/ + Status = lmk04828_init(&lmk04828_inst, &lmk04828_default_init_param); + for(int i = 0; i < lmk04828_default_config_len; i++) + { + Status = lmk04828_write(lmk04828_inst, + lmk04828_default_config[i].addr, lmk04828_default_config[i].value); + if (lmk04828_default_config[i].addr == 0x0000) + { + mdelay(500); + } + } + + uint8_t Lmk04828Read; + + // check pll1 lock status + Status = lmk04828_read(lmk04828_inst, 0x0182, &Lmk04828Read); + if ((Lmk04828Read & 0x02) == 0x02) { + DBG_INFO("HDWR: [LMK04828] PLL1 Locked.\r\n"); + } + else { + DBG_ERROR("HDWR: [LMK04828] PLL1 NOT lock. It takes a while " + "for PLL1 to lock. Lock status is shown on LED.\r\n"); + } + + // check pll2 lock status + Status = lmk04828_read(lmk04828_inst, 0x0183, &Lmk04828Read); + if ((Lmk04828Read & 0x02) == 0x02) { + DBG_INFO("HDWR: [LMK04828] PLL2 Locked.\r\n"); + } + else { + DBG_INFO("HDWR: [LMK04828] PLL2 NOT lock!\r\n"); + } +#endif + +#ifdef LMX2594_ADC + /**********************************************************/ + /************* LMX2594 for ADC Configuration **************/ + /**********************************************************/ + Status = lmx2594_init(&lmx2594_adc_inst, &lmx2594_adc_init_param); + for(int i = 0; i < lmx2594_default_config_len; i++) + { + Status = lmx2594_write(lmx2594_adc_inst, + lmx2594_default_config[i].addr, lmx2594_default_config[i].value); + if (lmx2594_default_config[i].addr == 0) + { + mdelay(500); + } + } + + uint16_t Lmx2594AdcRead; + + // check vco lock status + Status = lmx2594_read(lmx2594_adc_inst, 110, &Lmx2594AdcRead); + if (((Lmx2594AdcRead >> 9) & 0x0003) == 0x0002) { + DBG_INFO("HDWR: [ADC LMX2594] Config Done.\r\n"); + } + else { + DBG_INFO("HDWR: [ADC LMX2594] not lock!\r\n"); + } + + // set mux out to lock status + Status = lmx2594_write(lmx2594_adc_inst, 0x00, 0x259C); + DBG_INFO("HDWR: [ADC LMX2594] SPI readback disabled, " + "lock status is shown on LED.\r\n"); +#endif + +#ifdef LMX2594_DAC + /**********************************************************/ + /************* LMX2594 for DAC Configuration **************/ + /**********************************************************/ + Status = lmx2594_init(&lmx2594_dac_inst, &lmx2594_dac_init_param); + for(int i = 0; i < lmx2594_default_config_len; i++) + { + Status = lmx2594_write(lmx2594_dac_inst, + lmx2594_default_config[i].addr, lmx2594_default_config[i].value); + if (lmx2594_default_config[i].addr == 0) + { + mdelay(500); + } + } + + uint16_t Lmx2594DacRead; + + // check vco lock status + Status = lmx2594_read(lmx2594_dac_inst, 110, &Lmx2594DacRead); + if (((Lmx2594DacRead >> 9) & 0x0003) == 0x0002) { + DBG_INFO("HDWR: [DAC LMX2594] Config Done.\r\n"); + } + else { + DBG_INFO("HDWR: [DAC LMX2594] not lock!\r\n"); + } + + // set mux out to lock status + Status = lmx2594_write(lmx2594_dac_inst, 0x00, 0x259C); + DBG_INFO("HDWR: [DAC LMX2594] SPI readback disabled, " + "lock status is shown on LED.\r\n"); +#endif + +#if defined(AD9361_API) || defined(AD9361_REG) + /**********************************************************/ + /***************** AD9361-1 Configuration *****************/ + /**********************************************************/ + XGpioPs_SetDirectionPin(&Gpio, AD9361_1_TXNRX, AD9361_GPIO_OUTPUT); + XGpioPs_SetDirectionPin(&Gpio, AD9361_1_ENABLE, AD9361_GPIO_OUTPUT); + XGpioPs_SetDirectionPin(&Gpio, AD9361_1_EN_AGC, AD9361_GPIO_OUTPUT); + XGpioPs_SetDirectionPin(&Gpio, AD9361_1_RESET_B, AD9361_GPIO_OUTPUT); + +#ifdef AD9361_REG + ad9361_reg_init(&ad9361_1_reg_inst, &ad9361_1_reg_default_init_param); + XGpioPs_WritePin(&Gpio, AD9361_1_ENABLE, 0); + ad9361_reg_config(ad9361_1_reg_inst); + XGpioPs_WritePin(&Gpio, AD9361_1_ENABLE, 1); +#endif +#ifdef AD9361_API + Status = ad9361_init(&ad9361_phy, &ad9361_default_init_param); + if (Status != XST_SUCCESS){ + DBG_ERROR("HDWR: ad9361_init() error: %" PRId32 "\n", Status); + return XST_FAILURE; + } + ad9361_set_tx_fir_config(ad9361_phy, tx_fir_config); + ad9361_set_rx_fir_config(ad9361_phy, rx_fir_config); +#endif + /**********************************************************/ + /***************** AD9361-2 Configuration *****************/ + /**********************************************************/ + XGpioPs_SetDirectionPin(&Gpio, AD9361_2_TXNRX, AD9361_GPIO_OUTPUT); + XGpioPs_SetDirectionPin(&Gpio, AD9361_2_ENABLE, AD9361_GPIO_OUTPUT); + XGpioPs_SetDirectionPin(&Gpio, AD9361_2_EN_AGC, AD9361_GPIO_OUTPUT); + XGpioPs_SetDirectionPin(&Gpio, AD9361_2_RESET_B, AD9361_GPIO_OUTPUT); + + ad9361_reg_init(&ad9361_2_reg_inst, &ad9361_2_reg_default_init_param); + XGpioPs_WritePin(&Gpio, AD9361_2_ENABLE, 0); + ad9361_reg_config(ad9361_2_reg_inst); + XGpioPs_WritePin(&Gpio, AD9361_2_ENABLE, 1); + + // uint8_t rd_back; + // Status = ad9361_reg_read(ad9361_1_reg_inst, 0x0002, &rd_back); + // Status = ad9361_reg_read(ad9361_2_reg_inst, 0x0002, &rd_back); + + /**********************************************************/ + /******* AD9361 Baseband Multichip Synchronization ********/ + /**********************************************************/ + XGpioPs_SetDirectionPin(&Gpio, AD9361_SYNC, AD9361_GPIO_OUTPUT); + XGpioPs_SetDirectionPin(&Gpio, AD9361_RX_FIFO_RST, AD9361_GPIO_OUTPUT); + + // SYNC BBPLL and DIGITAL SAMPLING CLOCKS + ad9361_reg_write(ad9361_1_reg_inst, 0x0001, 0x0D); // MCS RF Enable; MCS BBPLL Enable; MCS BB Enable + ad9361_reg_write(ad9361_2_reg_inst, 0x0001, 0x0D); + ad9361_reg_write(ad9361_1_reg_inst, 0x0047, 0x80); // MCS refclk Scale Enable + ad9361_reg_write(ad9361_2_reg_inst, 0x0047, 0x80); + XGpioPs_WritePin(&Gpio, AD9361_SYNC, 0); + mdelay(20); + XGpioPs_WritePin(&Gpio, AD9361_SYNC, 1); // Input a rising-edge pulse to the SYNC_IN pin + mdelay(500); + XGpioPs_WritePin(&Gpio, AD9361_SYNC, 0); + mdelay(2000); + ad9361_reg_write(ad9361_1_reg_inst, 0x0001, 0x0B); // MCS RF Enable; MCS Digital CLK Enable ; MCS BB Enable + ad9361_reg_write(ad9361_2_reg_inst, 0x0001, 0x0B); + XGpioPs_WritePin(&Gpio, AD9361_SYNC, 1); // Input another rising-edge pulse to the SYNC_IN pin + mdelay(500); + XGpioPs_WritePin(&Gpio, AD9361_SYNC, 0); + ad9361_reg_write(ad9361_1_reg_inst, 0x0001, 0x08); // clear MCS bits + ad9361_reg_write(ad9361_2_reg_inst, 0x0001, 0x08); + + XGpioPs_WritePin(&Gpio, AD9361_RX_FIFO_RST, 0); + mdelay(20); + XGpioPs_WritePin(&Gpio, AD9361_RX_FIFO_RST, 1); + mdelay(20); + XGpioPs_WritePin(&Gpio, AD9361_RX_FIFO_RST, 0); + + /**********************************************************/ + /********* AD9361 Multichip Rx Phase Calibration **********/ + /**********************************************************/ + XGpioPs_SetDirectionPin(&Gpio, AD9361_TX_SWITCH, AD9361_GPIO_OUTPUT); + XGpioPs_SetDirectionPin(&Gpio, AD9361_1_CAL_START, AD9361_GPIO_OUTPUT); + XGpioPs_SetDirectionPin(&Gpio, AD9361_1_CAL_DONE, AD9361_GPIO_INPUT); + XGpioPs_SetDirectionPin(&Gpio, AD9361_2_CAL_START, AD9361_GPIO_OUTPUT); + XGpioPs_SetDirectionPin(&Gpio, AD9361_2_CAL_DONE, AD9361_GPIO_INPUT); + + // switch to lower frequency + Status = Hardware_SwitchFreq(ad9361_1_reg_inst, TX_FREQ_126); + XGpioPs_WritePin(&Gpio, AD9361_TX_SWITCH, 1); + mdelay(20); + + // switch rx channel + Status = ad9361_reg_write(ad9361_1_reg_inst, 0x0004, 0x30); + Status = ad9361_reg_write(ad9361_2_reg_inst, 0x0004, 0x30); + + // enable dual ad9361 tx channels + Status = ad9361_reg_write(ad9361_1_reg_inst, 0x0002, 0xE4); // Setup Tx Digital Filters/ Channels + + // switch ad9361 tx channel + Status = ad9361_reg_write(ad9361_1_reg_inst, 0x0004, 0x70); // Select Rx input pin(A,B,C)/ Tx out pin (A,B) + mdelay(20); + + // begin to calibrate the 1st ad9361 + XGpioPs_WritePin(&Gpio, AD9361_1_CAL_START, 0); + XGpioPs_WritePin(&Gpio, AD9361_1_CAL_START, 1); + mdelay(1); + XGpioPs_WritePin(&Gpio, AD9361_1_CAL_START, 0); + + // read calibration status + int32_t time_out_cnt = 0; + do + { + mdelay(1); // Wait 1ms + time_out_cnt++; + } while (((XGpioPs_ReadPin(&Gpio, AD9361_1_CAL_DONE) == 0) && time_out_cnt <= AD9361_TIMEOUT_TH)); + + if (time_out_cnt == AD9361_TIMEOUT_TH) + { + return FAILURE; + } + + // begin to calibrate the 2nd ad9361 + XGpioPs_WritePin(&Gpio, AD9361_2_CAL_START, 1); + mdelay(1); + XGpioPs_WritePin(&Gpio, AD9361_2_CAL_START, 0); + + // read calibration status + time_out_cnt = 0; + do + { + mdelay(1); // Wait 1ms + time_out_cnt++; + } while (((XGpioPs_ReadPin(&Gpio, AD9361_2_CAL_DONE) == 0) && time_out_cnt <= AD9361_TIMEOUT_TH)); + + if (time_out_cnt == AD9361_TIMEOUT_TH) + { + return FAILURE; + } + + // enable single ad9361 tx channels + Status = ad9361_reg_write(ad9361_1_reg_inst, 0x0002, 0xA4); // Setup Tx Digital Filters/ Channels + + // switch ad9361 tx & rx channel + Status = ad9361_reg_write(ad9361_1_reg_inst, 0x0004, 0x03); + Status = ad9361_reg_write(ad9361_2_reg_inst, 0x0004, 0x03); + + // switch to higher frequency + Status = Hardware_SwitchFreq(ad9361_1_reg_inst, TX_FREQ_4125); + XGpioPs_WritePin(&Gpio, AD9361_TX_SWITCH, 0); + + Status = ad9361_reg_write(ad9361_1_reg_inst, 0x0109, 0x00); // Rx Gain + Status = ad9361_reg_write(ad9361_1_reg_inst, 0x010C, 0x00); // Rx Gain + Status = ad9361_reg_write(ad9361_2_reg_inst, 0x0109, 0x00); // Rx Gain + Status = ad9361_reg_write(ad9361_2_reg_inst, 0x010C, 0x00); // Rx Gain +#endif + +#ifdef AD9653 + /**********************************************************/ + /****************** AD9653 Configuration ******************/ + /**********************************************************/ + Status = ad9653_init(&ad9653_inst, &ad9653_default_init_param); + for(int i = 0; i < ad9653_default_config_len; i++){ + Status = ad9653_write(ad9653_inst, ad9653_default_config[i].addr, + ad9653_default_config[i].value); + if (ad9653_default_config[i].addr == 0x0000){ + mdelay(500); + } + } +#endif + +#ifdef AD9694 + /**********************************************************/ + /****************** AD9694 Configuration ******************/ + /**********************************************************/ + Status = ad9694_init(&ad9694_inst, &ad9694_default_init_param); + for(int i = 0; i < ad9694_default_config_len; i++){ + Status = ad9694_write(ad9694_inst, ad9694_default_config[i].addr, ad9694_default_config[i].value); + if (ad9694_default_config[i].addr == 0x0000){ + mdelay(500); + } + } + +// uint8_t reg0008; +// uint8_t reg056F; +// uint8_t reg0571; +// +// ad9694_read(ad9694_inst,0x008,®0008); +// ad9694_read(ad9694_inst,0x56F,®056F); +// ad9694_read(ad9694_inst,0x571,®0571); +#endif + +#ifdef CX9261A + /**********************************************************/ + /***************** CX9261A Configuration ******************/ + /**********************************************************/ + cx9261a_init(&cx9261a_inst, &cx9261a_default_init_param); + + // 缂佹垵鐣鹃崙鑺ユ殶 + CX9261A_setLoggingFunc(CX9261A_Log); + CX9261A_setWriteSpiFunc(CX9261A_spiWrite); + CX9261A_setReadSpiFunc(CX9261A_spiRead); + CX9261A_setPauseFunc(CX9261A_mdelay); + CX9261A_setSetPinFunc(CX9261A_setPin); + + // 瀵拷婵鍘ょ純顔垮П閻楋拷 + CX9261A_initialChip(); // 閺堬拷瀵拷婵娓剁憰浣界殶閻€劍顒濋崙鑺ユ殶 + int reg; + reg = CX9261A_spiRead(0x000); + CX9261A_initialPowerConfig(&cx9261a_configuration); + // 闁板秶鐤嗗〒鈺勊� + CX9261A_configTemperatureCompensation(cx9261a_configuration.tempComp); + // 娑撳秹娓剁憰浣藉殰鐎规矮绠熼悽鍨爱缁狅紕鎮婇弮璁圭礉鏉╂瑩鍎撮崚鍡曞敩閻礁褰叉禒銉ф阜閻o拷 + CX9261A_configPowerPins(&(cx9261a_configuration.powerManagement.rxPower), CX9261A_PWM_Receive); + CX9261A_configPowerPins(&(cx9261a_configuration.powerManagement.txPower), CX9261A_PWM_Transmit); + CX9261A_configPowerPins(&(cx9261a_configuration.powerManagement.fddPower), CX9261A_PWM_Duplex); + // 闁板秶鐤唒ll + CX9261A_configBBPLL(&cx9261a_configuration); + CX9261A_configPllSource(&(cx9261a_configuration.pllSource)); + CX9261A_configPll1(cx9261a_configuration.pllFreq.pll1Freq, &(cx9261a_configuration.clock)); + CX9261A_configPll2(cx9261a_configuration.pllFreq.pll2Freq, &(cx9261a_configuration.clock)); + // 闁板秶鐤哛x闁岸浜� + CX9261A_configRxChannel(&(cx9261a_configuration.rx1Config), CX9261A_RXCH_Rx1); + CX9261A_configRxChannel(&(cx9261a_configuration.rx2Config), CX9261A_RXCH_Rx2); + CX9261A_configRxChannel(&(cx9261a_configuration.rx3Config), CX9261A_RXCH_Rx3); + // 闁板秶鐤員x闁岸浜� + CX9261A_configTxChannelFrequency(cx9261a_configuration.pllFreq.pll1Freq, CX9261A_TXCH_Tx1); + CX9261A_configTxChannelFrequency(cx9261a_configuration.pllFreq.pll2Freq, CX9261A_TXCH_Tx2); + CX9261A_configTxChannel(&(cx9261a_configuration.tx1Config), CX9261A_TXCH_Tx1); + CX9261A_configTxChannel(&(cx9261a_configuration.tx2Config), CX9261A_TXCH_Tx2); + // 闁板秶鐤嗗銈嗗皾閸o拷 + CX9261A_configRxFilters(&(cx9261a_configuration.rxFilters)); + CX9261A_configTxFilters(&(cx9261a_configuration.txFilters)); + // 闁板秶鐤哊CO + CX9261A_configRxNco(&(cx9261a_configuration.rxNco), cx9261a_configuration.clock.adc); + int filters = max(cx9261a_configuration.rxFilters.fir, 1) * max(cx9261a_configuration.rxFilters.hbf3, 1) * + max(cx9261a_configuration.rxFilters.hbf2, 1) * + max(cx9261a_configuration.rxFilters.hbf1, 1) * max(cx9261a_configuration.rxFilters.cic, 1); + CX9261A_configTxNco(&(cx9261a_configuration.txNco), cx9261a_configuration.clock.adc / filters); + // 闁板秶鐤咥GC + CX9261A_configAGC(&(cx9261a_configuration.agc)); + // 閸掑洦宕查崚棰佺搐閻姵膩瀵拷 + CX9261A_configPowerMode(CX9261A_PWM_Sleep); + CX9261A_mdelay(1000); + // 閸掑洦宕查崚鐗堢墡閸戝棙膩瀵拷 + CX9261A_configPowerMode(CX9261A_PWM_Calib); + CX9261A_mdelay(100); + // 闁夸礁鐣綛BPLL + while (!CX9261A_afcBBPll()); + // 闁夸礁鐣綪LL1 + while(!CX9261A_afcPll1()); + // 闁夸礁鐣綪LL2 + while(!CX9261A_afcPll2()); + // 闁板秶鐤嗛幒銉ュ經 + CX9261A_configInterface(&(cx9261a_configuration.interface)); + // 闁板秶鐤咶PGA閹恒儱褰涢惄绋垮彠 濮濄倓璐熺粈杞扮伐閿涘奔绮庨柅鍌滄暏娴滃顶emo瀹搞儳鈻奸敍宀�鏁ら幋宄扮安閺嶈宓佺�圭偤妾幆鍛枌鏉╂稖顢戦柊宥囩枂 + CX9261A_configFPGA(&(cx9261a_configuration.interface)); + // 闁板秶鐤嗛弽鈥冲櫙 + CX9261A_configADCCalib(cx9261a_configuration.clock.adc); + CX9261A_configFlashCalib(); + CX9261A_configDcCalib(&(cx9261a_configuration.calib)); +// CX9261A_configQecCalib(&cx9261a_configuration); +// CX9261A_loadRxCalibPredis(0, CX9261A_RXCH_Rx1, CX9261A_LOSRC_Pll1); +// CX9261A_loadRxCalibPredis(0, CX9261A_RXCH_Rx2, CX9261A_LOSRC_Pll1); + // 閸掑洦宕查崚鏉夸紣娴f粍膩瀵拷 + CX9261A_configPowerMode(CX9261A_PWM_Receive); + // 鐠烘娊顣� + + uint8_t hop_flag = 0; + uint8_t hop_flag_new = 0; + double hop_freq; + +// while (true) +// { +// gpio_get_value(cx9261a_inst->gpio_desc_hop_flag, &hop_flag_new); +// if (hop_flag != hop_flag_new) +// { +// hop_flag = hop_flag_new; +// +// double hop_freq; +// double hop_freq1 = 800.25; +// int pll_index=1; +// int unlock_pll1_cnt=0,unlock_pll2_cnt=0; +// +// hop_freq = (rand()%(800-30))+30+(rand()%(101))/(double)100; +// +// if(hop_freq == hop_freq1){ +// if(pll_index == 2){ +// pll_index = 4; +// } +// else if(pll_index == 1){ +// pll_index = 3; +// } +// } +// +// if(hop_flag_new == 1) +// { +// if(pll_index == 1 || pll_index == 3) +// { +// //閸掑洦宕查懛纭僱l2閺夈儲绨� +// cx9261a_trx_ch_pll_config(2); +// //闁板秶鐤唒ll1閸欏倹鏆� +// CX9261A_configPll1(hop_freq,&(cx9261a_configuration.clock)); +// // unlock_pll1_cnt=PLL_config(1,hop_freq,ref)-1; +// while (!CX9261A_afcPll1()) { +// unlock_pll1_cnt++; +// } +// pll_index = 2; +// } +// else if(pll_index == 2 || pll_index == 4) +// { +// //閸掑洦宕查懛纭僱l1閺夈儲绨� +// cx9261a_trx_ch_pll_config(1); +// //闁板秶鐤唒ll2閸欏倹鏆� +// CX9261A_configPll2(hop_freq,&(cx9261a_configuration.clock)); +// // unlock_pll2_cnt=PLL_config(2,hop_freq,ref)-1; +// while (!CX9261A_afcPll2()) { +// unlock_pll2_cnt++; +// } +// pll_index = 1; +// } +// } +// hop_freq1 = hop_freq; +// if(unlock_pll2_cnt!=0 || unlock_pll1_cnt !=0){ +// DBG_ERROR("pll1 unlock num: %d; pll2 unlock num: %d;.\n", unlock_pll1_cnt, unlock_pll2_cnt); +// } +// } +// } +#endif + DBG_INFO("HDWR: configuration Done.\n"); + + return SUCCESS; +} + + diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/hwconfig.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/hwconfig.h new file mode 100644 index 0000000..b457b50 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/hwconfig.h @@ -0,0 +1,32 @@ +/*****************************************************************************/ +/** + * Description: hwconfig.h + * + + * Date: 2024-02-20 13:19 + * Last Edit Time: [2024-02-20 13:19] + * + * Revisions: Rev 0.0 + * History: + * [2023-05-07]: Initial edition. + * + * Copyright(c) 2024 Beijing Institute of Technology. + * + ******************************************************************************/ + +/***************************** Include Files *********************************/ +// Standard +#include +#include "config/app_config.h" +#include "drivers/rf-transceiver/ad9361_reg/ad9361_reg.h" +#include "drivers/rf-transceiver/ad9361/ad9361_api.h" + +/************************** Function Prototypes ******************************/ +int32_t Hardware_Init(void); +int32_t Hardware_SwitchFreq(struct ad9361_reg_dev *dev, ad9361TxFreq freq); +int8_t Hardware_GetAD9361Temp_1(void); +int8_t Hardware_GetAD9361Temp_2(void); +uint8_t Hardware_GetAD9361LockStatus(void); +uint8_t Hardware_GetHMC7044LockStatus(void); + + diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/axi_io.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/axi_io.h new file mode 100644 index 0000000..f804042 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/axi_io.h @@ -0,0 +1,59 @@ +/***************************************************************************//** + * @file axi_io.h + * @brief Header file of AXI IO. + * @author Antoniu Miclaus (antoniu.miclaus@analog.com) +******************************************************************************** + * Copyright 2019(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +#ifndef _AXI_IO_H_ +#define _AXI_IO_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ + +#include + +/******************************************************************************/ +/************************ Functions Declarations ******************************/ +/******************************************************************************/ + +/* AXI IO Read data */ +int32_t axi_io_read(uint32_t base, uint32_t offset, uint32_t *data); + +/* AXI IO Write data */ +int32_t axi_io_write(uint32_t base, uint32_t offset, uint32_t data); + +#endif // _AXI_IO_H_ diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/circ_queue.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/circ_queue.h new file mode 100644 index 0000000..312b125 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/circ_queue.h @@ -0,0 +1,100 @@ +/*****************************************************************************/ +/** + * @file circ_queue.h + * @author FanHao (hao_fan_bit_ee@163.com) + * @brief A simple circular FIFO bounded queue + * @version 0.1 + * @date 2023-02-23 + * @note Forked from https://github.com/chrismerck/rpa_queue.git + * @copyright Copyright(c) 2024 Beijing Institute of Technology.\n + * Lab of Communication and Network + * + ******************************************************************************/ + +/** + * @addtogroup Utility + * @{ + */ + +/** + * @defgroup CircularQueue + * @{ + */ + +#ifndef CIRC_QUEUE_H +#define CIRC_QUEUE_H + +#include +#include +#include + +/** + * @struct CIRC_QUEUE + * @brief Circular queue Instance + */ +typedef struct +{ + void **data; + volatile uint32_t nelts; ///< number of elements + uint32_t in; ///< next empty location + uint32_t out; ///< next filled location + uint32_t bounds; ///< max size of queue +} CIRC_QUEUE; + +/** + * @brief create a FIFO queue + * @param queue The new queue + * @param queue_capacity maximum size of the queue + */ +bool CircQueueCreate(CIRC_QUEUE **queue, uint32_t queue_capacity); + +/** + * @brief free queue memory -- call after destroy + */ +void CircQueueFree(CIRC_QUEUE *queue); + +/** + * @brief Detects when the CIRC_QUEUE is full. This utility function is expected + * to be called from within critical sections, and is not threadsafe. + */ +bool isCircQueueFull(CIRC_QUEUE *queue); + +/** + * @brief Detects when the CIRC_QUEUE is empty. This utility function is expected + * to be called from within critical sections, and is not threadsafe. + */ +bool isCircQueueEmpty(CIRC_QUEUE *queue); + +/** + * @brief push/add an object to the queue, blocking if the queue is already full + * + * @param queue the queue + * @param data the data + * @return True on a successful push + * @return False push failed + */ +bool CircQueuePush(CIRC_QUEUE *queue, void *data); + +/** + * @brief pop/get an object from the queue, blocking if the queue is already empty + * + * @param queue the queue + * @param data the data + * @return false pop failed + * @return true on a successful pop + */ +bool CircQueuePop(CIRC_QUEUE *queue, void **data); + +/** + * @brief returns the size of the queue. + * + * @param queue the queue + * @return the size of the queue + */ +uint32_t CircQueueSize(CIRC_QUEUE *queue); + +#endif /* CIRC_QUEUE_H */ + +/** @} CircularQueue*/ + +/** @} Utility*/ \ No newline at end of file diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/clist.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/clist.h new file mode 100644 index 0000000..47e6702 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/clist.h @@ -0,0 +1,138 @@ +/*****************************************************************************/ +/** + * @file clist.h + * @author FanHao (hao_fan_bit_ee@163.com) + * @brief A basic list data structure + * @version 0.1 + * @date 2023-02-23 + * @note Forked from https://github.com/clibs/list + * @copyright Copyright(c) 2024 Beijing Institute of Technology.\n + * Lab of Communication and Network + * + ******************************************************************************/ + +/** + * @addtogroup Utility + * @{ + */ + +/** + * @defgroup cList + * @{ + */ + +#ifndef __CLIST_H__ +#define __CLIST_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include + + /** + * @enum clist_direction_t + * @brief clist_t iterator direction. + */ + typedef enum + { + LIST_HEAD, + LIST_TAIL + } clist_direction_t; + + /** + * @struct clist_node_t + * @brief clist_t node struct. + */ + typedef struct clist_node + { + struct clist_node *prev; ///< previous node + struct clist_node *next; ///< next node + void *val; + } clist_node_t; + + /** + * @struct clist_t + * @brief clist_t struct. + */ + typedef struct + { + clist_node_t *head; ///< first node + clist_node_t *tail; ///< last node + uint32_t len; ///< list length + void (*free)(void *val); ///< node free function handle + int8_t (*compare)(void *a, void *b); ///< node compare function handle + } clist_t; + + /** + * @struct clist_iterator_t + * @brief clist_t iterator struct. + */ + typedef struct + { + clist_node_t *next; ///< next element + clist_direction_t direction; ///< iter direction + } clist_iterator_t; + + // Node prototypes. + + clist_node_t * + clist_node_new(void *val); + + // clist_t prototypes. + + clist_t * + clist_new(void); + + clist_node_t * + clist_rpush(clist_t *self, clist_node_t *node); + + clist_node_t * + clist_lpush(clist_t *self, clist_node_t *node); + + clist_node_t * + clist_find(clist_t *self, void *val); + + clist_node_t * + clist_at(clist_t *self, int index); + + clist_node_t * + clist_rpop(clist_t *self); + + clist_node_t * + clist_lpop(clist_t *self); + + void + clist_remove(clist_t *self, clist_node_t *node); + + void + clist_clear(clist_t *self); + + void + clist_destroy(clist_t *self); + + // clist_t iterator prototypes. + + clist_iterator_t * + clist_iterator_new(clist_t *list, clist_direction_t direction); + + clist_iterator_t * + clist_iterator_new_from_node(clist_node_t *node, clist_direction_t direction); + + clist_node_t * + clist_iterator_next(clist_iterator_t *self); + + void + clist_iterator_destroy(clist_iterator_t *self); + +#ifdef __cplusplus +} +#endif + +#endif /* __CLIBS_LIST_H__ */ + +/** @} cList*/ + +/** @} Utility*/ \ No newline at end of file diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/common.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/common.h new file mode 100644 index 0000000..c13de59 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/common.h @@ -0,0 +1,93 @@ +/*****************************************************************************/ +/** + * @file common.h + * @author FanHao (hao_fan_bit_ee@163.com) + * @brief Common macros + * @version 0.0 + * @date 2022-07-16 + * @copyright Copyright(c) 2024 Beijing Institute of Technology.\n + * Lab of Communication and Network + * + ******************************************************************************/ +/** + * @defgroup COMMON_MACRO + * @{ + */ + +/***************************** Include Files *********************************/ +#ifndef __COMMON_H_ +#define __COMMON_H_ +#include +#include +#include +#include +#include +#include "util.h" +#include "error.h" + +/************************** Constant Definitions *****************************/ +/***************** Macros (Inline Functions) Definitions *********************/ +#define __PRINT_BLOCK(block) \ + do \ + { \ + block \ + } while (0) + +#define ASSERT(exp) ((exp) ? (void)0 : AppAssert(__FILE__, __LINE__)) +#define _nop_() __asm__("nop") + +/* Stringify */ +#define STR(x) #x + +// #define _DEBUG_LEVEL 3 + +#if _DEBUG_LEVEL > 0 +#define DBG_PRINT(...) printf(__VA_ARGS__) +#endif +#if _DEBUG_LEVEL >= 1 +#define DBG_ERROR(...) __PRINT_BLOCK( \ + printf("in "__FILE__ \ + ":%d [error]\t", \ + __LINE__); \ + printf(__VA_ARGS__);) +#endif +#if _DEBUG_LEVEL >= 2 +#define DBG_WARNING(...) __PRINT_BLOCK( \ + printf("[warning]\t"); \ + printf(__VA_ARGS__);) +#endif +#if _DEBUG_LEVEL >= 3 +#define DBG_INFO(...) __PRINT_BLOCK( \ + printf("[Info]\t"); \ + printf(__VA_ARGS__);) +#endif + +#ifndef DBG_ERROR +#define DBG_ERROR(...) \ + { \ + } +#endif +#ifndef DBG_WARNING +#define DBG_WARNING(...) \ + { \ + } +#endif +#ifndef DBG_INFO +#define DBG_INFO(...) \ + { \ + } +#endif +#ifndef DBG_PRINT +#define DBG_PRINT(...) \ + { \ + } +#endif +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/************************** Function Prototypes ******************************/ +void AppAssert(const char *file, int line); +#endif /* __COMMON_H_ */ + +/** @} */ \ No newline at end of file diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/delay.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/delay.h new file mode 100644 index 0000000..b6907dc --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/delay.h @@ -0,0 +1,70 @@ +/***************************************************************************//** + * @file delay.h + * @brief Header file of Delay functions + * @author DBogdan (dragos.bogdan@analog.com) +******************************************************************************** + * Copyright 2019(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +#ifndef _DELAY_H_ +#define _DELAY_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ + +#include + +/** + * @struct time + * @brief Structure holding time data (seconds, microseconds). + */ +struct time { + unsigned int s, us; +}; + +/******************************************************************************/ +/************************ Functions Declarations ******************************/ +/******************************************************************************/ + +/* Generate microseconds delay. */ +void udelay(uint32_t usecs); + +/* Generate miliseconds delay. */ +void mdelay(uint32_t msecs); + +/* Get current time */ +struct time get_time(void); + +#endif // _DELAY_H_ diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/error.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/error.h new file mode 100644 index 0000000..bbd4b1b --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/error.h @@ -0,0 +1,56 @@ +/*****************************************************************************/ +/** + * @file common.h + * @author FanHao (hao_fan_bit_ee@163.com) + * @brief Common error numbers + * @version 0.0 + * @date 2022-06-18 + * @copyright Copyright(c) 2024 Beijing Institute of Technology.\n + * Lab of Communication and Network + * + ******************************************************************************/ + +/** + * @addtogroup COMMON_MACRO + * @{ + */ + +#ifndef SRC_ERROR_H_ +#define SRC_ERROR_H_ + +#include + +/******************************************************************************/ +/********************** Macros and Constants Definitions **********************/ +/******************************************************************************/ + +/** + * @name ERRORNO + * @{ + */ + +#ifdef SUCCESS +#undef SUCCESS +#endif +#define SUCCESS 0 +#ifdef FAILURE +#undef FAILURE +#endif +#define FAILURE -1 + +#define EPARAM -2 /*!< Data crc check failed */ +#define ECRC -3 /*!< Data crc check failed */ +#define EDINVAL -4 /*!< Device is invalid (sleep or power off) */ +#define EDCONF -5 /*!< Device confugure error */ +#define ETIMEOUT -6 /*!< Timeout error */ +#define EIGNORE -7 /*!< Received an unsupported command, and ignore it */ +#define EQUEUE -8 /*!< Queue overflow */ +#define EUNEXP -9 /*!< Encounter an unexpected error */ + +#define IS_ERR_VALUE(x) ((x) != SUCCESS) + +#endif // ERROR_H_ + +/** @} ERRORNO*/ + +/** @} COMMON_MACRO*/ \ No newline at end of file diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/global_timer.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/global_timer.h new file mode 100644 index 0000000..a58f41c --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/global_timer.h @@ -0,0 +1,95 @@ +#ifndef SRC_GLOBAL_TIMER_H_ +#define SRC_GLOBAL_TIMER_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ + +#include + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ + +/** + * @struct timer_init_param + * @brief Structure holding the parameters for timer initialization + */ +struct global_timer_init_param { + /** timer ID */ + uintptr_t base_addr; + /** timer ID */ + uint16_t id; + /** timer count frequency (Hz) */ + uint32_t freq_hz; + /** counter start value */ + uint64_t load_value; +}; + +/** + * @struct timer_desc + * @brief Structure holding timer descriptor + */ +struct global_timer_desc { + /** timer ID */ + uintptr_t base_addr; + /** timer ID */ + uint16_t id; + /** timer count frequency (Hz) */ + uint32_t freq_hz; + /** counter start value */ + uint64_t load_value; +}; + +/******************************************************************************/ +/************************ Functions Declarations ******************************/ +/******************************************************************************/ + +/* Initialize hardware timer and the handler structure associated with it. */ +int32_t global_timer_init(struct global_timer_desc **desc, + struct global_timer_init_param *param); + +/* Free the memory allocated by timer_setup(). */ +int32_t global_timer_remove(struct global_timer_desc *desc); + +/* Start a timer. */ +int32_t global_timer_start(struct global_timer_desc *desc); + +/* Stop a timer from counting. */ +int32_t global_timer_stop(struct global_timer_desc *desc); + +/* Get the value of the counter register for the timer. */ +int32_t global_timer_counter_get(struct global_timer_desc *desc, uint64_t *counter); + +/* Set the timer counter register value. */ +int32_t global_timer_counter_set(struct global_timer_desc *desc, uint64_t new_val); + +/* Get the timer clock frequency. */ +int32_t global_timer_count_clk_get(struct global_timer_desc *desc, uint32_t *freq_hz); + +/* Set the timer clock frequency. */ +int32_t global_timer_count_clk_set(struct global_timer_desc *desc, uint32_t freq_hz); + +/* Enable global timer interrupt. */ +int32_t global_timer_irq_enable(struct global_timer_desc *desc); + +/* Disable global timer interrupt. */ +int32_t global_timer_irq_disable(struct global_timer_desc *desc); + +/* Implement a acknowledgment global timer interrupt. */ +int32_t global_timer_irq_ack(struct global_timer_desc *desc); + +/* Set comparator register. */ +int32_t global_timer_compara_set(struct global_timer_desc *desc, uint64_t com_val); + +/* Set auto increment register . */ +int32_t global_timer_auto_inc_set(struct global_timer_desc *desc, uint32_t inc_val); + +/* Enable global timer auto increment mode. */ +int32_t global_timer_auto_inc_enable(struct global_timer_desc *desc); + +/* Disable global timer auto increment mode. */ +int32_t global_timer_auto_inc_disable(struct global_timer_desc *desc); + +#endif /* SRC_TIMER_H_ */ + diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/gpio.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/gpio.h new file mode 100644 index 0000000..5771659 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/gpio.h @@ -0,0 +1,188 @@ +/***************************************************************************//** + * @file gpio.h + * @brief Header file of GPIO Interface + * @author DBogdan (dragos.bogdan@analog.com) +******************************************************************************** + * Copyright 2019(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +#ifndef _GPIO_H_ +#define _GPIO_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ + +#include + +/******************************************************************************/ +/********************** Macros and Constants Definitions **********************/ +/******************************************************************************/ + +#define GPIO_OUT 0x01 +#define GPIO_IN 0x00 + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ + +/** + * @struct gpio_platform_ops + * @brief Structure holding gpio function pointers that point to the platform + * specific function + */ +struct gpio_platform_ops ; + +/** + * @enum gpio_pull_up + * @brief Enum that holds the possible pull up/ pull down resistor configuration. + */ +enum gpio_pull_up { + PULL_NONE, + /** Strong pull up */ + PULL_UP, + /** Strong pull down */ + PULL_DOWN, + PULL_UP_WEAK, + PULL_DOWN_WEAK +}; + +/** + * @struct gpio_init_param + * @brief Structure holding the parameters for GPIO initialization. + */ +struct gpio_init_param { + /** Port number */ + int32_t port; + /** GPIO number */ + int32_t number; + /** Pull up/down resistor configuration */ + enum gpio_pull_up pull; + /** GPIO platform specific functions */ + const struct gpio_platform_ops *platform_ops; + /** GPIO extra parameters (device specific) */ + void *extra; +}; + +/** + * @struct gpio_desc + * @brief Structure holding the GPIO descriptor. + */ +struct gpio_desc { + /** Port number */ + int32_t port; + /** GPIO number */ + int32_t number; + /** Pull up/down resistor configuration */ + enum gpio_pull_up pull; + /** GPIO platform specific functions */ + const struct gpio_platform_ops *platform_ops; + /** GPIO extra parameters (device specific) */ + void *extra; +}; + +/** + * @enum gpio_values + * @brief Enum that holds the possible output states of a GPIO. + */ +enum gpio_values { + /** GPIO logic low */ + GPIO_LOW, + /** GPIO logic high */ + GPIO_HIGH, + /** GPIO high impedance */ + GPIO_HIGH_Z +}; + +/** + * @struct gpio_platform_ops + * @brief Structure holding gpio function pointers that point to the platform + * specific function + */ +struct gpio_platform_ops { + /** gpio initialization function pointer */ + int32_t (*gpio_ops_get)(struct gpio_desc **, + const struct gpio_init_param *); + /** gpio optional descriptor function pointer */ + int32_t (*gpio_ops_get_optional)(struct gpio_desc **, + const struct gpio_init_param *); + /** gpio remove function pointer */ + int32_t (*gpio_ops_remove)(struct gpio_desc *); + /** gpio direction input function pointer */ + int32_t (*gpio_ops_direction_input)(struct gpio_desc *); + /** gpio direction output function pointer */ + int32_t (*gpio_ops_direction_output)(struct gpio_desc *, uint8_t); + /** gpio get direction function pointer */ + int32_t (*gpio_ops_get_direction)(struct gpio_desc *, uint8_t *); + /** gpio set value function pointer */ + int32_t (*gpio_ops_set_value)(struct gpio_desc *, uint8_t); + /** gpio get value function pointer */ + int32_t (*gpio_ops_get_value)(struct gpio_desc *, uint8_t *); +}; + +/******************************************************************************/ +/************************ Functions Declarations ******************************/ +/******************************************************************************/ + +/* Obtain the GPIO decriptor. */ +int32_t gpio_get(struct gpio_desc **desc, + const struct gpio_init_param *param); + +/* Obtain optional GPIO descriptor. */ +int32_t gpio_get_optional(struct gpio_desc **desc, + const struct gpio_init_param *param); + +/* Free the resources allocated by gpio_get(). */ +int32_t gpio_remove(struct gpio_desc *desc); + +/* Enable the input direction of the specified GPIO. */ +int32_t gpio_direction_input(struct gpio_desc *desc); + +/* Enable the output direction of the specified GPIO. */ +int32_t gpio_direction_output(struct gpio_desc *desc, + uint8_t value); + +/* Get the direction of the specified GPIO. */ +int32_t gpio_get_direction(struct gpio_desc *desc, + uint8_t *direction); + +/* Set the value of the specified GPIO. */ +int32_t gpio_set_value(struct gpio_desc *desc, + uint8_t value); + +/* Get the value of the specified GPIO. */ +int32_t gpio_get_value(struct gpio_desc *desc, + uint8_t *value); + +#endif // _GPIO_H_ diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/i2c.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/i2c.h new file mode 100644 index 0000000..e3ca53e --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/i2c.h @@ -0,0 +1,134 @@ +/***************************************************************************//** + * @file i2c.h + * @brief Header file of I2C Interface + * @author DBogdan (dragos.bogdan@analog.com) +******************************************************************************** + * Copyright 2019(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +#ifndef _I2C_H_ +#define _I2C_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ + +#include + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ + +/** + * @struct i2c_platform_ops + * @brief Structure holding I2C function pointers that point to the platform + * specific function + */ +struct i2c_platform_ops ; + +/** + * @struct i2c_init_param + * @brief Structure holding the parameters for I2C initialization. + */ +struct i2c_init_param { + /** Device ID */ + uint32_t device_id; + /** I2C maximum transfer speed supported */ + uint32_t max_speed_hz; + /** Slave address */ + uint8_t slave_address; + /** I2C platform specific functions */ + const struct i2c_platform_ops *platform_ops; + /** I2C extra parameters (device specific parameters) */ + void *extra; +}; + +/** + * @struct i2c_desc + * @brief Structure holding I2C descriptor + */ +struct i2c_desc { + /** Device ID */ + uint32_t device_id; + /** I2C maximum transfer speed supported */ + uint32_t max_speed_hz; + /** Slave address */ + uint8_t slave_address; + /** I2C platform specific functions */ + const struct i2c_platform_ops *platform_ops; + /** I2C extra parameters (device specific parameters) */ + void *extra; +}; + +/** + * @struct i2c_platform_ops + * @brief Structure holding i2c function pointers that point to the platform + * specific function + */ +struct i2c_platform_ops { + /** i2c initialization function pointer */ + int32_t (*i2c_ops_init)(struct i2c_desc **, + const struct i2c_init_param *); + /** i2c write function pointer */ + int32_t (*i2c_ops_write)(struct i2c_desc *, uint8_t *, uint8_t, uint8_t); + /** i2c write function pointer */ + int32_t (*i2c_ops_read)(struct i2c_desc *, uint8_t *, uint8_t, uint8_t); + /** i2c remove function pointer */ + int32_t (*i2c_ops_remove)(struct i2c_desc *); +}; + +/******************************************************************************/ +/************************ Functions Declarations ******************************/ +/******************************************************************************/ + +/* Initialize the I2C communication peripheral. */ +int32_t i2c_init(struct i2c_desc **desc, + const struct i2c_init_param *param); + +/* Free the resources allocated by i2c_init(). */ +int32_t i2c_remove(struct i2c_desc *desc); + +/* Write data to a slave device. */ +int32_t i2c_write(struct i2c_desc *desc, + uint8_t *data, + uint8_t bytes_number, + uint8_t stop_bit); + +/* Read data from a slave device. */ +int32_t i2c_read(struct i2c_desc *desc, + uint8_t *data, + uint8_t bytes_number, + uint8_t stop_bit); + +#endif // _I2C_H_ diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/irq.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/irq.h new file mode 100644 index 0000000..e8e0b6b --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/irq.h @@ -0,0 +1,235 @@ +/***************************************************************************//** + * @file irq.h + * @brief Header file of IRQ interface. + * @author Cristian Pop (cristian.pop@analog.com) +******************************************************************************** + * Copyright 2019(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +#ifndef _IRQ_H_ +#define _IRQ_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ + +#include + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ + +/** + * @enum irq_uart_event_e + * @brief Possible events for uart interrupt + * @todo replace this with enum irq_event + */ +enum irq_uart_event_e { + /** Write operation finalized */ + IRQ_WRITE_DONE, + /** Read operation finalized */ + IRQ_READ_DONE, + /** An error occurred */ + IRQ_ERROR +}; + +enum irq_event { + EVT_GPIO, + EVT_UART_TX_COMPLETE, + EVT_UART_RX_COMPLETE, + EVT_UART_ERROR, + EVT_RTC, + EVT_XINT, + EVT_TIM_ELAPSED +}; + +enum irq_trig_level { + IRQ_LEVEL_LOW, + IRQ_LEVEL_HIGH, + IRQ_EDGE_FALLING, + IRQ_EDGE_RISING, + IRQ_EDGE_BOTH +}; + +enum irq_peripheral { + GPIO_IRQ, + UART_IRQ, + RTC_IRQ, + TIM_IRQ +}; + +/** + * @struct irq_platform_ops + * @brief Structure holding IRQ function pointers that point to the platform + * specific function + */ +struct irq_platform_ops ; + +/** + * @struct irq_init_param + * @brief Structure holding the initial parameters for Interrupt Request. + */ +struct irq_init_param { + /** Interrupt request controller ID. */ + uint32_t irq_ctrl_id; + /** Platform specific IRQ platform ops structure. */ + const struct irq_platform_ops *platform_ops; + /** + * This is intended to store irq controller specific configurations, + * it should not be a reference to any peripheral descriptor. + */ + void *extra; +}; + +/** + * @struct irq_desc + * @brief Structure for Interrupt Request descriptor. + */ +struct irq_ctrl_desc { + /** Interrupt request controller ID. */ + uint32_t irq_ctrl_id; + /** Platform specific IRQ platform ops structure. */ + const struct irq_platform_ops *platform_ops; + /** + * This is intended to store irq controller specific configurations, + * it should not be a reference to any peripheral descriptor. + */ + void *extra; +}; + +/** + * @struct callback_desc + * @brief Structure describing a callback to be registered + * @todo: remove this, use struct irq_callback instead. + */ +struct callback_desc { + /** Callback to be called when the event an event occurs. */ + void (*callback)(void *context); + /** + * Callback to be called when the event an event occurs + * @param ctx - Same as \ref callback_desc.ctx + * @param event - Event that generated the callback + * @param extra - Platform specific data + */ + void (*legacy_callback)(void *ctx, uint32_t event, + void *extra); /* TODO: remove this. */ + /** Parameter to be passed when the callback is called */ + void *ctx; + /** Platform specific configuration for a callback */ + void *legacy_config; /* TODO: remove this. */ + /** Platform specific event that triggers the calling of the callback. */ + enum irq_event event; + /** Interrupt source peripheral specifier. */ + enum irq_peripheral peripheral; + /** This will be used to store HAL specific descriptors */ + void *handle; +}; + +/** + * @struct irq_platform_ops + * @brief Structure holding IRQ function pointers that point to the platform + * specific function + */ +struct irq_platform_ops { + /** Initialize a interrupt controller peripheral. */ + int32_t (*init)(struct irq_ctrl_desc **desc, + const struct irq_init_param *param); + /** Register a legacy_callback to handle the irq events */ + int32_t (*register_callback)(struct irq_ctrl_desc *desc, uint32_t irq_id, + struct callback_desc *callback); + /** Unregisters a generic IRQ handling function */ + int32_t (*unregister_callback)(struct irq_ctrl_desc *desc, + uint32_t irq_id, + struct callback_desc *callback); + /** Global interrupt enable */ + int32_t (*global_enable)(struct irq_ctrl_desc *desc); + /** Global interrupt disable */ + int32_t (*global_disable)(struct irq_ctrl_desc *desc); + /** Set interrupt trigger level. */ + int32_t (*trigger_level_set)(struct irq_ctrl_desc *desc, uint32_t irq_id, + enum irq_trig_level trig); + /** Enable specific interrupt */ + int32_t (*enable)(struct irq_ctrl_desc *desc, uint32_t irq_id); + /** Disable specific interrupt */ + int32_t (*disable)(struct irq_ctrl_desc *desc, uint32_t irq_id); + /** Set the priority level for a specific interrupt */ + int32_t (*set_priority)(struct irq_ctrl_desc *desc, uint32_t irq_id, + uint32_t priority_level); + /** IRQ remove function pointer */ + int32_t (*remove)(struct irq_ctrl_desc *desc); +}; + +/******************************************************************************/ +/************************ Functions Declarations ******************************/ +/******************************************************************************/ + +/* Initialize a interrupt controller peripheral. */ +int32_t irq_ctrl_init(struct irq_ctrl_desc **desc, + const struct irq_init_param *param); + +/* Free the resources allocated by irq_ctrl_init(). */ +int32_t irq_ctrl_remove(struct irq_ctrl_desc *desc); + +/* Register a callback to handle the irq events */ +int32_t irq_register_callback(struct irq_ctrl_desc *desc, + uint32_t irq_id, + struct callback_desc *callback_desc); + +/* Unregisters a generic IRQ handling function */ +int32_t irq_unregister_callback(struct irq_ctrl_desc *desc, + uint32_t irq_id, + struct callback_desc *callback_desc); + +/* Global interrupt enable */ +int32_t irq_global_enable(struct irq_ctrl_desc *desc); + +/* Global interrupt disable */ +int32_t irq_global_disable(struct irq_ctrl_desc *desc); + +/* Set interrupt trigger level. */ +int32_t irq_trigger_level_set(struct irq_ctrl_desc *desc, + uint32_t irq_id, + enum irq_trig_level trig); + +/* Enable specific interrupt */ +int32_t irq_enable(struct irq_ctrl_desc *desc, uint32_t irq_id); + +/* Disable specific interrupt */ +int32_t irq_disable(struct irq_ctrl_desc *desc, uint32_t irq_id); + +/** Set the priority level for a specific interrupt */ +int32_t irq_set_priority(struct irq_ctrl_desc *desc, + uint32_t irq_id, + uint32_t priority_level); +#endif // _IRQ_H_ diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/list.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/list.h new file mode 100644 index 0000000..d2e70f3 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/list.h @@ -0,0 +1,327 @@ +/***************************************************************************//** + * @file list.h + * @brief List library header + * @author Mihail Chindris (mihail.chindris@analog.com) +******************************************************************************** + * @copyright + * Copyright 2020(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +******************************************************************************** + * + * @section list_details Library description + * This library handles double linked lists and it expose inseart, + * read, get and delete functions. \n + * It also can be accesed using it member functions which wrapp function for + * usual list types.\n + * @subsection example Sample code + * @code{.c} + * // -- Use a generic list + * struct list_desc *list1; + * struct iterator *it; + * uint32_t a; + * // Create list list1 + * list_init(&list1, LIST_DEFAULT, NULL); + * // Add items to the list + * list_add_last(list1, 1); + * list_add_last(list1, 2); + * list_add_last(list1, 3); + * // Here the list will be: 1 -> 2 - > 3 + * + * list_read_last(list1, &a); + * printf("Last: %d\n", a); + * // 3 will be printed + * // Create an iterator on the end of the list + * iterator_init(&it, list1, 0); + * // Move the iterator backword with one position + * iterator_move(it, -1); + * // Read the data at the current position + * iterator_read(it, &a); + * printf("Current: %d\n", a); + * // 2 will be printed + * iterator_remove(it); + * list_remove(list1); + * + * // -- Use a popular list + * struct list_desc *stack; + * // Create a FIFO list + * list_init(&stack, LIST_STACK, NULL); + * // Put elements in the list + * stack->push(stack, 1); + * stack->push(stack, 2); + * stack->push(stack, 3); + * // Read from the stack + * stack->pop(stack, &a); + * printf("Last: %d\n", a); + * // 3 will be printed + * list_remove(stack); + * @endcode +*******************************************************************************/ + +#ifndef _LIST_H_ +#define _LIST_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ + +#include +#include + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ + +/** + * @struct list_desc + * @brief Structure storing the list and function wrapper for usual list types + * + * With this structure the funtionalities of usual list types + * ( \ref adapter_type ) can be accesed with the functions referenced in this + * structure. + * For example: + * @code{.c} + * my_queue->push(my_queue, my_data); + * @endcode + */ +struct list_desc; + +/** + * @struct list_iterator + * @brief Structure used to iterate through the list using Iterator functions. + */ +struct iterator; + +/** + * @brief Prototype of the compare function. + * + * The function used to compare the elements of the liste when doing + * operations on an ordered list. + * @param data1 - First element to be compared + * @param data2 - Second element to be compared + * @return + * - -1 - If data1 < data2 + * - 0 - If data1 == data2 + * - 1 - If data1 > data2 + */ +typedef int32_t (*f_cmp)(void *data1, void *data2); + +/** + * @name Generic functions + * Each function interacting with the list have one of the following formats.\n + * Aditionaly they may have one more parametere for specific functionalities.\n + * In the Iterator functions, the list reference is replaced by the iterator's + * one. + * @{ + */ + +/** + * @brief Add an element in the list. + * + * The element of the list is created and the data field is stored in it. + * @param list_desc - Reference to the list. Created by \ref list_init. + * @param data - Data to store in a list element + * @return + * - 0 : On success + * - -1 : Otherwise + */ +typedef int32_t (*f_add)(struct list_desc *list_desc, void *data); + +/** + * @brief Edit an element in the list. The content is replaced by new_data. + * @param list_desc - Reference to the list. Created by \ref list_init. + * @param new_data - New data to replace the old one + * @return + * - 0 : On success + * - -1 : Otherwise + */ +typedef int32_t (*f_edit)(struct list_desc *list_desc, void *new_data); + +/** + * @brief Read an element from the list. + * @param list_desc - Reference to the list. Created by \ref list_init. + * @param result - If not null, result is filled with: + * @param data - Content of the list element, NULL if some error occur. + * @return \n + * - 0 : On success + * - -1 : Otherwise + * @note If the content of an element can be 0 then the result must be checked + * to see if the functions has succeded + */ +typedef int32_t (*f_read)(struct list_desc *list_desc, void **data); + +/** + * @brief Read and remove an element from the list. + * @param list_desc - Reference to the list. Created by \ref list_init. + * @param result - If not null, result is filled with: + * @param data - Content of the list element, NULL if some error occur. + * @return + * - 0 : On success + * - -1 : Otherwise + */ +typedef int32_t (*f_get)(struct list_desc *list_desc, void **data); + +/** @} */ + +/** + * @enum adapter_type + * @brief Selects functionalities for functions in \ref list_desc + */ +enum adapter_type { + /** Default type is LIST_STACK */ + LIST_DEFAULT, + /** + * Functions for a FIFO list (First-in first-out). Elements are inserted + * in one end and extracted from the other end. + * - \e Push: Insert element + * - \e Pop: Get next element (Read and remove) + * - \e Top_next: Read next element + * - \e Back: Read first element + * - \e Swap: Edit the content of the next element + */ + LIST_QUEUE, + /** + * Functions for a LIFO list (Last-in first-out). Elements are inserted + * and extracted only from the same end. + * - \e Push: Insert element + * - \e Pop: Get top element (Read and remove) + * - \e Top_next: Read top element + * - \e Back: Read bottom element + * - \e Swap: Edit the content of the top element + */ + LIST_STACK, + /** + * Functions for ordered list. The order of element is determinated + * usinge the \ref f_cmp. + * - \e Push: Insert element + * - \e Pop: Get lowest element (Read and remove) + * - \e Top_next: Read lowest element + * - \e Back: Read the biggest element + * - \e Swap: Edit the lowest element + */ + LIST_PRIORITY_LIST +}; + +struct list_desc { + /** Refer to \ref adapter_type */ + f_add push; + /** Refer to \ref adapter_type */ + f_get pop; + /** Refer to \ref adapter_type */ + f_read top_next; + /** Refer to \ref adapter_type */ + f_read back; + /** Refer to \ref adapter_type */ + f_edit swap; + /** Structure storing the list internal parameters */ + void *priv_desc; +}; + +/******************************************************************************/ +/************************ Functions Declarations ******************************/ +/******************************************************************************/ + +int32_t list_init(struct list_desc **list_desc, + enum adapter_type type, + f_cmp comparator); +int32_t list_remove(struct list_desc *list_desc); +int32_t list_get_size(struct list_desc *list_desc, + uint32_t *out_size); + +/** + * @name Iterator functions + * An iterator is used to iterate through the list. For a list, any number of + * iterators can be created. All must be removed before removing a list. + * @{ + */ +int32_t iterator_init(struct iterator **iter, + struct list_desc *list_desc, + bool start); +int32_t iterator_remove(struct iterator *iter); +int32_t iterator_move(struct iterator *iter, int32_t steps); +int32_t iterator_move_to_idx(struct iterator *iter, int32_t idx); +int32_t iterator_find(struct iterator *iter, void *cmp_data); +int32_t iterator_insert(struct iterator *iter, void *data, + bool after); +int32_t iterator_edit(struct iterator *iter, void *new_data); +int32_t iterator_read(struct iterator *iter, void **data); +int32_t iterator_get(struct iterator *iter, void **data); +/** @}*/ + +/** + * @name Operations on the ends of the list + * These functions will operate on the first or last element of the list + * @{ + */ +int32_t list_add_first(struct list_desc *list_desc, void *data); +int32_t list_edit_first(struct list_desc *list_desc, + void *new_data); +int32_t list_read_first(struct list_desc *list_desc, void **data); +int32_t list_get_first(struct list_desc *list_desc, void **data); + +int32_t list_add_last(struct list_desc *list_desc, void *data); +int32_t list_edit_last(struct list_desc *list_desc, void *new_data); +int32_t list_read_last(struct list_desc *list_desc, void **data); +int32_t list_get_last(struct list_desc *list_desc, void **data); +/** @}*/ + +/** + * @name Operations by index + * These functions use an index to identify the element in the list. + * @{ + */ +int32_t list_add_idx(struct list_desc *list_desc, void *data, + uint32_t idx); +int32_t list_edit_idx(struct list_desc *list_desc, void *new_data, + uint32_t idx); +int32_t list_read_idx(struct list_desc *list_desc, void **data, + uint32_t idx); +int32_t list_get_idx(struct list_desc *list_desc, void **data, + uint32_t idx); +/** @}*/ + +/** + * @name Operations by comparation + * These functions use the specified \ref f_cmp at \ref list_init to identify + * the element this will operate on. + * @{ + */ +int32_t list_add_find(struct list_desc *list_desc, void *data); +int32_t list_edit_find(struct list_desc *list_desc, void *new_data, + void *cmp_data); +int32_t list_read_find(struct list_desc *list_desc, void **data, + void *cmp_data); +int32_t list_get_find(struct list_desc *list_desc, void **data, + void *cmp_data); +/** @}*/ + +#endif // _LIST_H_ diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/spi.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/spi.h new file mode 100644 index 0000000..b415fe2 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/spi.h @@ -0,0 +1,209 @@ +/***************************************************************************//** + * @file spi.h + * @brief Header file of SPI Interface + * @author DBogdan (dragos.bogdan@analog.com) +******************************************************************************** + * Copyright 2019(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +#ifndef _SPI_H_ +#define _SPI_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ + +#include + +/******************************************************************************/ +/********************** Macros and Constants Definitions **********************/ +/******************************************************************************/ + +#define SPI_CPHA 0x01 +#define SPI_CPOL 0x02 +#define SPI_3WIRE 0x04 + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ + +/** + * @enum spi_mode + * @brief SPI configuration for clock phase and polarity. + */ +enum spi_mode { + /** Data on rising, shift out on falling */ + SPI_MODE_0 = (0 | 0), + /** Data on falling, shift out on rising */ + SPI_MODE_1 = (0 | SPI_CPHA), + /** Data on rising, shift out on falling */ + SPI_MODE_2 = (SPI_CPOL | 0), + /** Data on falling, shift out on rising */ + SPI_MODE_3 = (SPI_CPOL | SPI_CPHA), + /** Data on rising, shift out on falling */ + SPI_MODE_4 = SPI_3WIRE +}; + +/** + * @enum spi_bit_order + * @brief SPI configuration for bit order (MSB/LSB). + */ +enum spi_bit_order { + /** Most-significant bit (MSB) first */ + SPI_BIT_ORDER_MSB_FIRST = 0, + /** Least-significant bit (LSB) first */ + SPI_BIT_ORDER_LSB_FIRST = 1, +}; + +/** + * @struct spi_msg_list + * @brief List item describing a SPI transfer + */ +struct spi_msg { + /** Buffer with data to send. If NULL, 0x00 will be sent */ + uint8_t *tx_buff; + /** Buffer where to store data. If NULL, incoming data won't be saved */ + uint8_t *rx_buff; + /** Length of buffers. Must have equal size. */ + uint32_t bytes_number; + /** If set, CS will be deasserted after the transfer */ + uint8_t cs_change; + /** + * Minimum delay (in us) between the CS de-assert event of the current message + * and the assert of the next one. + */ + uint32_t cs_change_delay; + /** Delay (in us) between the CS assert and the first SCLK edge. */ + uint32_t cs_delay_first; + /** Delay (in us) between the last SCLK edge and the CS deassert */ + uint32_t cs_delay_last; +}; + +/** + * @struct spi_platform_ops + * @brief Structure holding SPI function pointers that point to the platform + * specific function + */ +struct spi_platform_ops ; + +/** + * @struct spi_init_param + * @brief Structure holding the parameters for SPI initialization + */ +struct spi_init_param { + /** Device ID */ + uint32_t device_id; + /** maximum transfer speed */ + uint32_t max_speed_hz; + /** SPI chip select */ + uint8_t chip_select; + /** SPI mode */ + enum spi_mode mode; + /** SPI bit order */ + enum spi_bit_order bit_order; + const struct spi_platform_ops *platform_ops; + /** SPI extra parameters (device specific) */ + void *extra; + /** Parent of the device */ + struct spi_desc *parent; +}; + +/** + * @struct spi_desc + * @brief Structure holding SPI descriptor. + */ +struct spi_desc { + /** Device ID */ + uint32_t device_id; + /** maximum transfer speed */ + uint32_t max_speed_hz; + /** SPI chip select */ + uint8_t chip_select; + /** SPI mode */ + enum spi_mode mode; + /** SPI bit order */ + enum spi_bit_order bit_order; + const struct spi_platform_ops *platform_ops; + /** SPI extra parameters (device specific) */ + void *extra; + /** Parent of the device */ + struct spi_desc *parent; +}; + +/** + * @struct spi_platform_ops + * @brief Structure holding SPI function pointers that point to the platform + * specific function + */ +struct spi_platform_ops { + /** SPI initialization function pointer */ + int32_t (*init)(struct spi_desc **, const struct spi_init_param *); + /** SPI write/read function pointer */ + int32_t (*write_and_read)(struct spi_desc *, uint8_t *, uint16_t); + /** SPI write function pointer */ + int32_t (*write_and_read_3wire)(struct spi_desc *, uint8_t *,uint8_t *,uint16_t, uint16_t); + /** Iterate over the spi_msg array and send all messages at once */ + int32_t (*transfer)(struct spi_desc *, struct spi_msg *, uint32_t); + /** SPI remove function pointer */ + int32_t (*remove)(struct spi_desc *); +}; + +/******************************************************************************/ +/************************ Functions Declarations ******************************/ +/******************************************************************************/ + +/* Initialize the SPI communication peripheral. */ +int32_t spi_init(struct spi_desc **desc, + const struct spi_init_param *param); + +/* Free the resources allocated by spi_init(). */ +int32_t spi_remove(struct spi_desc *desc); + +/* Write and read data to/from SPI. */ +int32_t spi_write_and_read(struct spi_desc *desc, + uint8_t *data, + uint16_t bytes_number); +int32_t spi_write_and_read_3wire(struct spi_desc *desc, + uint8_t *write_data, + uint8_t *read_data, + uint16_t write_bytes_number, + uint16_t read_bytes_number); + +/* Iterate over the spi_msg array and send all messages at once */ +int32_t spi_transfer(struct spi_desc *desc, + struct spi_msg *msgs, + uint32_t len); + + +#endif // _SPI_H_ diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/timer.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/timer.h new file mode 100644 index 0000000..d1a9284 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/timer.h @@ -0,0 +1,159 @@ +/***************************************************************************//** +* @file timer.h +* @brief Timer control module header. +* @author Andrei Drimbarean (andrei.drimbarean@analog.com) +******************************************************************************** +* Copyright 2019(c) Analog Devices, Inc. +* +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* - Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* - 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. +* - Neither the name of Analog Devices, Inc. nor the names of its +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* - The use of this software may or may not infringe the patent rights +* of one or more patent holders. This license does not release you +* from the requirement that you obtain separate licenses from these +* patent holders to use this software. +* - Use of the software either in source or binary form, must be run +* on or directly connected to an Analog Devices Inc. component. +* +* THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR IMPLIED +* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, MERCHANTABILITY +* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +#ifndef _SRC_TIMER_H_ +#define _SRC_TIMER_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ + +#include + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ + +/** + * @struct timer_desc + * @brief Structure holding timer descriptor + */ +struct timer_desc { + /** timer ID */ + uint16_t id; + /** timer count frequency (Hz) */ + uint32_t freq_hz; + /** the number of ticks the timer counts until it resets */ + uint32_t ticks_count; + /** Timer platform operations */ + const struct timer_platform_ops *platform_ops; + /** timer extra parameters (device specific) */ + void *extra; +}; + +/** + * @struct timer_platform_ops + * @brief Structure holding timer function pointers that point to the platform + * specific function + */ +struct timer_platform_ops ; + +/** + * @struct timer_init_param + * @brief Structure holding the parameters for timer initialization + */ +struct timer_init_param { + /** timer ID */ + uint16_t id; + /** timer count frequency (Hz) */ + uint32_t freq_hz; + /** the number of ticks the timer counts until it resets */ + uint32_t ticks_count; + /** Timer platform operations */ + const struct timer_platform_ops *platform_ops; + /** timer extra parameters (device specific) */ + void *extra; +}; + +/** + * @struct timer_platform_ops + * @brief Structure holding timer function pointers that point to the platform + * specific function + */ +struct timer_platform_ops { + /** timer initialization function pointer */ + int32_t (*init)(struct timer_desc **, + const struct timer_init_param *); + /** timer start function pointer */ + int32_t (*start)(struct timer_desc *); + /** timer stop function pointer */ + int32_t (*stop)(struct timer_desc *); + /** timer get counter function pointer */ + int32_t (*counter_get)(struct timer_desc *, uint32_t *counter); + /** timer set counter function pointer */ + int32_t (*counter_set)(struct timer_desc *, uint32_t new_val); + /** timer get clock frequency function pointer */ + int32_t (*count_clk_get)(struct timer_desc *, uint32_t *freq_hz); + /** timer set clock frequency function pointer */ + int32_t (*count_clk_set)(struct timer_desc *, uint32_t freq_hz); + /** timer get elapsed time in nsec */ + int32_t (*get_elapsed_time_nsec)(struct timer_desc *, + uint64_t *elapsed_time); + /** timer remove function pointer */ + int32_t (*remove)(struct timer_desc *); +}; + +/******************************************************************************/ +/************************ Functions Declarations ******************************/ +/******************************************************************************/ + +/* Initialize hardware timer and the handler structure associated with it. */ +int32_t timer_init(struct timer_desc **desc, + struct timer_init_param *param); + +/* Free the memory allocated by timer_setup(). */ +int32_t timer_remove(struct timer_desc *desc); + +/* Start a timer. */ +int32_t timer_start(struct timer_desc *desc); + +/* Stop a timer from counting. */ +int32_t timer_stop(struct timer_desc *desc); + +/* Get the value of the counter register for the timer. */ +int32_t timer_counter_get(struct timer_desc *desc, + uint32_t *counter); + +/* Set the timer counter register value. */ +int32_t timer_counter_set(struct timer_desc *desc, + uint32_t new_val); + +/* Get the timer clock frequency. */ +int32_t timer_count_clk_get(struct timer_desc *desc, + uint32_t *freq_hz); + +/* Set the timer clock frequency. */ +int32_t timer_count_clk_set(struct timer_desc *desc, + uint32_t freq_hz); + +/* Get the elapsed time in nsec for the timer. */ +int32_t timer_get_elapsed_time_nsec(struct timer_desc *desc, + uint64_t *elapsed_time); + +#endif // _SRC_TIMER_H_ diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/trng.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/trng.h new file mode 100644 index 0000000..5698032 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/trng.h @@ -0,0 +1,115 @@ +/***************************************************************************//** + * @file trng.h + * @brief Header file of true random number generator + * @author Mihail Chindris (mihail.chindris@analog.com) +******************************************************************************** + * @copyright + * Copyright 2020(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +#ifndef _TRNG_H_ +#define _TRNG_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ + +#include + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ + +/** + * @struct trng_platform_ops + * @brief Structure holding TRNG function pointers that point to the platform + * specific function + */ +struct trng_platform_ops; + +/** + * @struct trng_desc + * @brief TRNG Descriptor + */ +struct trng_desc { + /** Platform ops */ + const struct trng_platform_ops *platform_ops; + /** Platform specific parameters */ + void *extra; +}; + +/** + * @struct trng_init_param + * @brief Init parameter for TRNG + */ +struct trng_init_param { + /** Device id */ + uint32_t dev_id; + /** Platform specific parameter */ + void *extra; + /** Platform ops */ + const struct trng_platform_ops *platform_ops; +}; + +/** + * @struct trng_platform_ops + * @brief Structure holding TRNG function pointers that point to the platform + * specific function + */ +struct trng_platform_ops { + /** TRNG initialization function pointer */ + int (*init)(struct trng_desc **, + const struct trng_init_param *); + /** Fill buffer with random numbers */ + int (*fill_buffer)(struct trng_desc *, uint8_t *, uint32_t); + /** TRNG remove function pointer */ + int (*remove)(struct trng_desc *); +}; + +/******************************************************************************/ +/************************ Functions Declarations ******************************/ +/******************************************************************************/ + +/* Initialize descriptor */ +int trng_init(struct trng_desc **desc, + const struct trng_init_param *param); + +/* Free resources allocated in descriptor */ +int trng_remove(struct trng_desc *desc); + +/* Fill buffer with random numbers */ +int trng_fill_buffer(struct trng_desc *desc, uint8_t *buff, + uint32_t len); + +#endif // _TRNG_H_ diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/util.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/util.h new file mode 100644 index 0000000..baad7b6 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/include/util.h @@ -0,0 +1,177 @@ +/*****************************************************************************/ +/** + * @file util.h + * @author FanHao (hao_fan_bit_ee@163.com) + * @brief Utility functions header file + * @version 0.0 + * @date 2023-05-06 + * + * @copyright Copyright(c) 2024 Beijing Institute of Technology.\n + * Lab of Communication and Network + * + ******************************************************************************/ + +/** + * @defgroup Utility + * @{ + */ + +#ifndef __UTIL_H_ +#define __UTIL_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include +#include +#include +/******************************************************************************/ +/********************** Macros and Constants Definitions **********************/ +/******************************************************************************/ +// #define __CRC_LSB +#define __BITSWAP_TABLE +#define __CRC_LSB +#define CRC16_INIT 0x0000 +#define CRC8_INIT 0x00 + +#define BIT(x) (1 << (x)) + +#define ARRAY_SIZE(x) \ + (sizeof(x) / sizeof((x)[0])) + +#define DIV_ROUND_UP(x, y) \ + (((x) + (y)-1) / (y)) +#define DIV_ROUND_CLOSEST(x, y) \ + (((x) + (y) / 2) / (y)) +#define DIV_ROUND_CLOSEST_ULL(x, y) \ + DIV_ROUND_CLOSEST(x, y) + +#define min(x, y) \ + (((x) < (y)) ? (x) : (y)) +#define min_t(type, x, y) \ + (type) min((type)(x), (type)(y)) + +#define max(x, y) \ + (((x) > (y)) ? (x) : (y)) +#define max_t(type, x, y) \ + (type) max((type)(x), (type)(y)) + +#define clamp(val, min_val, max_val) \ + (max(min((val), (max_val)), (min_val))) +#define clamp_t(type, val, min_val, max_val) \ + (type) clamp((type)(val), (type)(min_val), (type)(max_val)) + +#define swap(x, y) \ + { \ + typeof(x) _tmp_ = (x); \ + (x) = (y); \ + (y) = _tmp_; \ + } + +#define round_up(x, y) \ + (((x) + (y)-1) / (y)) + +#define BITS_PER_LONG 32 + +#define GENMASK(h, l) ({ \ + uint32_t t = (uint32_t)(~0UL); \ + t = t << (BITS_PER_LONG - (h - l + 1)); \ + t = t >> (BITS_PER_LONG - (h + 1)); \ + t; \ +}) + +#define bswap_constant_32(x) \ + ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \ + (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)) + +#define bswap_constant_16(x) ((((x) & (uint16_t)0xff00) >> 8) | \ + (((x) & (uint16_t)0x00ff) << 8)) + +#define bit_swap_constant_8(x) \ + ((((x) & 0x80) >> 7) | \ + (((x) & 0x40) >> 5) | \ + (((x) & 0x20) >> 3) | \ + (((x) & 0x10) >> 1) | \ + (((x) & 0x08) << 1) | \ + (((x) & 0x04) << 3) | \ + (((x) & 0x02) << 5) | \ + (((x) & 0x01) << 7)) + +#define U16_MAX ((uint16_t)~0U) +#define S16_MAX ((int16_t)(U16_MAX >> 1)) + +#define DIV_U64(x, y) (x / y) + +#define UNUSED_PARAM(x) ((void)x) + +#define shift_right(x, s) ((x) < 0 ? -(-(x) >> (s)) : (x) >> (s)) + +/******************************************************************************/ +/************************ Functions Declarations ******************************/ +/******************************************************************************/ + +void IO_Out32(uintptr_t addr, uint32_t value); +uint32_t IO_In32(uintptr_t addr); + +/* flip a byte left to right*/ +uint8_t byte_bit_swap(uint8_t byte); + +/* flip a 32bit word left to right */ +uint32_t word_bit_swap(uint32_t word); + +/* Returns the number of 1-bits in digit. */ +uint32_t count_ones(uint32_t word); + +/* convert a fixed number to a double. */ +double fix2double(int32_t val, uint8_t frac_bits); + +/* Find first set bit in word. */ +uint32_t find_first_set_bit(uint32_t word); +/* Find last set bit in word. */ +uint32_t find_last_set_bit(uint32_t word); +/* Locate the closest element in an array. */ +uint32_t find_closest(int32_t val, + const int32_t *array, + uint32_t size); +/* Shift the value and apply the specified mask. */ +uint32_t field_prep(uint32_t mask, uint32_t val); +/* Get a field specified by a mask from a word. */ +uint32_t field_get(uint32_t mask, uint32_t word); +/* Log base 2 of the given number. */ +int32_t log_base_2(uint32_t x); +/* Find greatest common divisor of the given two numbers. */ +uint32_t greatest_common_divisor(uint32_t a, + uint32_t b); +/* Calculate best rational approximation for a given fraction. */ +void rational_best_approximation(uint32_t given_numerator, + uint32_t given_denominator, + uint32_t max_numerator, + uint32_t max_denominator, + uint32_t *best_numerator, + uint32_t *best_denominator); +/* Calculate the number of set bits. */ +uint32_t hweight8(uint32_t word); +/* Calculate the quotient and the remainder of an integer division. */ +uint64_t do_div(uint64_t *n, + uint64_t base); +/* Unsigned 64bit divide with 64bit divisor and remainder */ +uint64_t div64_u64_rem(uint64_t dividend, uint64_t divisor, + uint64_t *remainder); +/* Unsigned 64bit divide with 32bit divisor with remainder */ +uint64_t div_u64_rem(uint64_t dividend, uint32_t divisor, uint32_t *remainder); +int64_t div_s64_rem(int64_t dividend, int32_t divisor, int32_t *remainder); +/* Unsigned 64bit divide with 32bit divisor */ +uint64_t div_u64(uint64_t dividend, uint32_t divisor); +int64_t div_s64(int64_t dividend, int32_t divisor); +/* Converts from string to int32_t */ +int32_t str_to_int32(const char *str); +/* Converts from string to uint32_t */ +uint32_t srt_to_uint32(const char *str); + +int crc16_gen(uint8_t *buffPtr, uint32_t dataLen, uint16_t poly, uint16_t *crcVal); +bool crc16_check(uint8_t *buffPtr, uint32_t dataLen, uint32_t crcOffset, uint16_t poly); +int crc8_gen(uint8_t *buffPtr, uint32_t dataLen, uint16_t poly, uint8_t *crcVal); +bool crc8_check(uint8_t *buffPtr, uint32_t dataLen, uint32_t crcOffset, uint16_t poly); +#endif // __UTIL_H_ + +/** @} */ \ No newline at end of file diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/main.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/main.c new file mode 100644 index 0000000..d8db2f6 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/main.c @@ -0,0 +1,524 @@ +/*****************************************************************************/ +/** + * Description: main.c + * + + * Date: 2023-05-07 13:19 + * Last Edit Time: [2023-05-07 20:32] + * + * Revisions: Rev 0.0 + * History: + * [2023-05-07]: Initial edition. + * + * Copyright(c) 2024 Beijing Institute of Technology. + * + ******************************************************************************/ + +/***************************** Include Files *********************************/ +// Standard +#include +#include +#include +// Xilinx +#include "xscugic.h" +#include "drivers/platform/xilinx_irq.h" +#include "drivers/platform/xilinx_timer.h" +// App +#include "common.h" +#include "error.h" +#include "timer.h" +#include "hwconfig.h" +#include "system_monitor.h" +#include "config/app_config.h" +#include "dev_profile/dev_profile.h" +//#include "systembus/systembus.h" +//#include "networking/phy_layer/phy_layer.h" +//#include "networking/link_layer/link_layer.h" +/************************** Function Prototypes ******************************/ +//int32_t SetupInterruptSystem(struct irq_ctrl_desc *descPtr); +//void Phy_IntrHandler(void *context); +//void PeriodicPull_Handler(void *callBackRef); +//void AppTimer_IntrHandler(void *context); +//void Systembus_RxIntrHandler(void *context); +//void Systembus_TxIntrHandler(void *context); + +/************************** Constant Definitions *****************************/ +//struct callback_desc phy_callback_desc = { +// .callback = Phy_IntrHandler, +// .ctx = NULL +//}; +// +//struct callback_desc periodicpull_callback_desc = { +// .callback = PeriodicPull_Handler, +// .ctx = NULL +//}; +// +// +//struct callback_desc sysbus_rx_callback_desc = { +// .callback = Systembus_RxIntrHandler, +// .ctx = NULL +//}; +// +//struct callback_desc sysbus_tx_callback_desc = { +// .callback = Systembus_TxIntrHandler, +// .ctx = NULL +//}; +// +//struct callback_desc apptimer_callback_desc = { +// .callback = AppTimer_IntrHandler, +// .ctx = NULL +//}; +// +//L2_CONFIG linkConfig = { +// .myType = L2_NODE_HEAD, +// .clustId = 1, +// .myId = 1, +// .headId = 1, +// .netScale = 2, +// .phyInstPtr = NULL, // physical layer reference +// .tmcInstPtr = NULL +//}; +// +//// L2_CONFIG linkConfig = { +//// .myType = L2_NODE_MEMBER, +//// .clustId = 1, +//// .myId = 2, +//// .headId = 1, +//// .netScale = 2, +//// .phyInstPtr = NULL, // physical layer reference +//// .tmcInstPtr = NULL +//// }; +// +//static PHY_CONFIG phyConfig = { +// .devId = PHY_DEVICE_ID, +// .baseAddr = PHY_BASEADDR, +// .antBramId = PHY_ANTCONFIG_BRAM_DEVICE_ID, +// .antInfoBramId = PHY_ANTINFO_BRAM_DEVICE_ID, +// .txBramId = PHY_TXBUFF_BRAM_DEVICE_ID, +// .rxBramId = PHY_RXBUFF_BRAM_DEVICE_ID, +// .hwSysmonId = PHY_HW_SYSMON_DEVICE_ID, +// .get_clk_state = Hardware_GetHMC7044LockStatus, +// .get_ad_lock_state = Hardware_GetAD9361LockStatus, +// .get_ad0_temp = Hardware_GetAD9361Temp_1, +// .get_ad1_temp = Hardware_GetAD9361Temp_2 +//}; +// +//TMC_CONFIG tmcConfig = { +// .baseAddr = TIME_CONTROLLER_BASEADDR, +// .reset = 10 +//}; +// +//static SYSBUS_CONFIG sysbusConfig = { +// .baseAddr = SYSBUS_BASE_ADDR, +// .txQueueMax = 16, +// .rxQueueMax = 16 +//}; +///*********************** Driver Instance Definitions **************************/ +//static struct irq_ctrl_desc *intcDescPtr; // General interrupt controller descriptor +//static struct timer_desc *logTmPtr; +//static SYSBUS *sysbusDescPtr; +//static PHY_INST *phyInstPtr; +//static TMC_INST *tmcInstPtr = NULL; +//L2_INST *linkInstPtr; +// +// +///****************************************************************************/ +///** +// * This function is responsible for initializing the SCUGIC instance, looking +// * for the device configuration based on the unique device ID, initializing a +// * SCUGIC instance, and initialize the interrupt at IRQ_F2P[0:0] +// * +// * @param intcDesc A pointer to INTC descriptor +// * +// * @return SUCCESS if initialize successful. +// * FAILURE if initialization failed. +// * +// *****************************************************************************/ +//int32_t SetupInterruptSystem(struct irq_ctrl_desc *descPtr) +//{ +// int32_t Status; +// struct xil_irq_desc *xil_dev; +// +// Status = irq_ctrl_init(&descPtr, &intc_init_param); +// if (Status != SUCCESS) +// { +// DBG_ERROR("Intc Init FAILED, ErrorCode: %" PRId32 ".\n", Status); +// return Status; +// } +// +// xil_dev = (struct xil_irq_desc *)(descPtr->extra); +// +// // Clear any pending interrupt flags +// uint32_t intrState = XScuGic_CPUReadReg((XScuGic *)(xil_dev->instance), XSCUGIC_INT_ACK_OFFSET); +// XScuGic_CPUWriteReg((XScuGic *)(xil_dev->instance), XSCUGIC_EOI_OFFSET, intrState); +// +// /* Initialize physical layer interrupt. */ +// Status = irq_trigger_level_set(descPtr, PHY_INTR_ID, IRQ_EDGE_RISING); +// if (Status != SUCCESS) +// { +// DBG_ERROR("PHY_INTR trigger level set FAILED, ErrorCode: %" PRId32 ".\n", Status); +// return Status; +// } +// // Set highest interrupt priority +// Status = irq_set_priority(descPtr, PHY_INTR_ID, 0x08); +// if (Status != SUCCESS) +// { +// DBG_ERROR("PHY_INTR priority level set FAILED, ErrorCode: %" PRId32 ".\n", Status); +// return Status; +// } +// // Connect the interrupt service routine isr1 to the interrupt controller +// Status = irq_register_callback(descPtr, PHY_INTR_ID, &phy_callback_desc); +// if (Status != SUCCESS) +// { +// DBG_ERROR("PHY_INTR register callback FAILED, ErrorCode: %" PRId32 ".\n", Status); +// return Status; +// } +// +// /* Initialize linklayer layer interrupt. */ +// Status = irq_trigger_level_set(descPtr, PERIODIC_5MS_INTR_ID, IRQ_EDGE_RISING); +// if (Status != SUCCESS) +// { +// DBG_ERROR("PERIODIC_5MS_INTR_ID trigger level set FAILED, ErrorCode: %" PRId32 ".\n", Status); +// return Status; +// } +// // Set highest interrupt priority +// Status = irq_set_priority(descPtr, PERIODIC_5MS_INTR_ID, 0x00); +// if (Status != SUCCESS) +// { +// DBG_ERROR("PERIODIC_5MS_INTR_ID priority level set FAILED, ErrorCode: %" PRId32 ".\n", Status); +// return Status; +// } +// // Connect the interrupt service routine isr1 to the interrupt controller +// Status = irq_register_callback(descPtr, PERIODIC_5MS_INTR_ID, &periodicpull_callback_desc); +// if (Status != SUCCESS) +// { +// DBG_ERROR("PHY_INTR register callback FAILED, ErrorCode: %" PRId32 ".\n", Status); +// return Status; +// } +// +// /* Initialize system bus interrupt. */ +// Status = irq_trigger_level_set(descPtr, SYSBUS_TX_INTR_ID, IRQ_EDGE_RISING); +// if (Status != SUCCESS) +// { +// DBG_ERROR("PHY_INTR trigger level set FAILED, ErrorCode: %" PRId32 ".\n", Status); +// return Status; +// } +// // Set interrupt priority +// Status = irq_set_priority(descPtr, SYSBUS_TX_INTR_ID, 0x10); +// if (Status != SUCCESS) +// { +// DBG_ERROR("PHY_INTR priority level set FAILED, ErrorCode: %" PRId32 ".\n", Status); +// return Status; +// } +// +// // Connect the interrupt service routine isr1 to the interrupt controller +// Status = irq_register_callback(descPtr, SYSBUS_TX_INTR_ID, &sysbus_tx_callback_desc); +// if (Status != SUCCESS) +// { +// DBG_ERROR("PHY_INTR register callback FAILED, ErrorCode: %" PRId32 ".\n", Status); +// return Status; +// } +// Status = irq_trigger_level_set(descPtr, SYSBUS_RX_INTR_ID, IRQ_EDGE_RISING); +// if (Status != SUCCESS) +// { +// DBG_ERROR("PHY_INTR trigger level set FAILED, ErrorCode: %" PRId32 ".\n", Status); +// return Status; +// } +// // Set interrupt priority +// Status = irq_set_priority(descPtr, SYSBUS_RX_INTR_ID, 0x18); +// if (Status != SUCCESS) +// { +// DBG_ERROR("PHY_INTR priority level set FAILED, ErrorCode: %" PRId32 ".\n", Status); +// return Status; +// } +// +// // Connect the interrupt service routine isr1 to the interrupt controller +// Status = irq_register_callback(descPtr, SYSBUS_RX_INTR_ID, &sysbus_rx_callback_desc); +// if (Status != SUCCESS) +// { +// DBG_ERROR("PHY_INTR register callback FAILED, ErrorCode: %" PRId32 ".\n", Status); +// return Status; +// } +// +// /* Initialize Scu timer interrupt. */ +// // Connect the interrupt service routine isr1 to the interrupt controller +// Status = irq_register_callback(descPtr, APPTIMER_INTR_ID, &apptimer_callback_desc); +// if (Status != SUCCESS) +// { +// DBG_ERROR("PHY_INTR register callback FAILED, ErrorCode: %" PRId32 ".\n", Status); +// return Status; +// } +// +// // Enable interrupts +// irq_enable(descPtr, PHY_INTR_ID); +// irq_enable(descPtr, PERIODIC_5MS_INTR_ID); +// irq_enable(descPtr, SYSBUS_RX_INTR_ID); +// irq_enable(descPtr, SYSBUS_TX_INTR_ID); +// irq_enable(descPtr, APPTIMER_INTR_ID); +// +// // Enable Exceptions +// Status = irq_global_enable(descPtr); +// if (Status != SUCCESS) +// { +// DBG_ERROR("IRQ global enable FAILED, ErrorCode: %" PRId32 ".\n", Status); +// return Status; +// } +// return SUCCESS; +//} // SetupInterruptSystem +// +///****************************************************************************/ +///** +// * This function is a interrupt service routine for IRQ_F2P[1:1], IRQ_F2P[1:1] +// * tells CPU that the data in OCM is ready! +// * +// * @param context +// * +// * @return None. +// * +// *****************************************************************************/ +//void Phy_IntrHandler(void *context) +//{ +// Phy_UpdateState(phyInstPtr); +//} // PhyIntr_Handler +// +// +///****************************************************************************/ +///** +// * Handler of periodic 5ms interrupt +// * +// * @param callBackRef is the linklayer instance pointer. +// * +// * @return None. +// * +// *****************************************************************************/ +//void PeriodicPull_Handler(void *callBackRef) +//{ +// int32_t Status; +// Status = Linklayer_Proc(linkInstPtr); +// if (Status != SUCCESS) +// { +// DBG_ERROR("Link layer runs failed, with error code: %" PRId32 "\n", Status); +// _nop_(); +// } +//} // PeriodicPull_Handler +// +///*****************************************************************************/ +///** +// * @brief Handler of System Bus RX done interrupt. +// * +// * @param context +// * +// * @return None. +// * +// ******************************************************************************/ +//void Systembus_RxIntrHandler(void *context) +//{ +// int32_t Status; +// Status = SystemBus_RxIntrHandler(sysbusDescPtr); +// if (Status != SUCCESS) +// { +// DBG_ERROR("SystemBus_RxIntrHandler exit with error code %" PRId32 ",\n", Status); +// } +//} +// +///*****************************************************************************/ +///** +// * @brief Handler of System Bus TX done interrupt. +// * +// * @param context +// * +// * @return None. +// * +// ******************************************************************************/ +//void Systembus_TxIntrHandler(void *context) +//{ +// int32_t Status; +// Status = SystemBus_TxIntrHandler(sysbusDescPtr); +// if (Status != SUCCESS) +// { +// DBG_ERROR("SystemBus_TxIntrHandler exit with error code %" PRId32 ",\n", Status); +// } +//} +// +///*****************************************************************************/ +///** +// * @brief Handler of AppTimer interrupt. +// * +// * @param context +// * +// * @return None. +// * +// ******************************************************************************/ +//void AppTimer_IntrHandler(void *context) +//{ +// struct xil_timer_desc *xdesc; +// xdesc = (struct xil_timer_desc *)(logTmPtr->extra); +//#ifdef XPAR_XSCUTIMER_NUM_INSTANCES +// if (XScuTimer_IsExpired((XScuTimer *)xdesc->instance)) +// { +// // Refresh log +// Phy_RefreshHwSate(phyInstPtr); +// if (TimeCtrl_GetIntCnt(tmcInstPtr) - phyInstPtr->lastLogTime >= PHY_LOG_EXPIRE) +// { +// Phy_UpdateLogging(phyInstPtr); +// } +// +// XScuTimer_ClearInterruptStatus((XScuTimer *)xdesc->instance); +// } +//#elif defined(XPAR_XTMRCTR_NUM_INSTANCES) +// if (XTmrCtr_IsExpired((XTmrCtr *)xdesc->instance, 0)) +// { +// // Refresh log +// Phy_RefreshHwSate(phyInstPtr); +// if (TimeCtrl_GetIntCnt(tmcInstPtr) - phyInstPtr->lastLogTime >= PHY_LOG_EXPIRE) +// { +// Phy_UpdateLogging(phyInstPtr); +// } +// +// XTmrCtr_ClearStats((XTmrCtr *)xdesc->instance); +// } +//#else +//#error No timer implementation! +//#endif +// else +// { +// DBG_WARNING("UNEXPECTED CALL of AppTimer_IntrHandler\n"); +// } +//} + +/*****************************************************************************/ +/** + * @brief Main function, initialize all the device needed, initialize + * interrupt system, halt in while(1) wait for event interrupt. + * + * @param none. + * + * @return + * + ******************************************************************************/ +int main(void) +{ + int32_t Status; + DBG_PRINT("Application Version: %2d.%2d.%4d.\n", _APP_VER_MAIN, _APP_VER_MINOR, _APP_VER_PATCH); + + /* System Monitor Initialize */ + Status = SysMonitor_Init(); + if (Status == SUCCESS) + { + DBG_PRINT("System monitor Init SUCCESS! \n"); + } + else + { + DBG_PRINT("System monitor Init FAILED! \n"); + return Status; + } + + /* Hardware Initialize */ + Status = Hardware_Init(); + if (Status == SUCCESS) + { + DBG_PRINT("Hardware Init SUCCESS! \n"); + } + else + { + DBG_PRINT("Hardware Init FAILED! \n"); + return Status; + } + +// /* Time controller Initialize */ +// Status = TimeCtrl_Init(&tmcInstPtr, &tmcConfig); +// if (Status == SUCCESS) +// { +// DBG_PRINT("TmcCtrl Init SUCCESS! \n"); +// } +// else +// { +// DBG_PRINT("TmcCtrl Init FAILED! \n"); +// return Status; +// } +// +// /* Physical Layer Initialize */ +// Status = Phy_Init(&phyInstPtr, &phyConfig); +// if (Status == SUCCESS) +// { +// DBG_PRINT("Physical layer Init SUCCESS! \n"); +// } +// else +// { +// DBG_PRINT("Physical layer FAILED! \n"); +// return Status; +// } +// +// /* Link Layer Initialize */ +// linkConfig.phyInstPtr = phyInstPtr; +// linkConfig.tmcInstPtr = tmcInstPtr; +// Status = Linklayer_Init(&linkInstPtr, &linkConfig); +// if (Status == SUCCESS) +// { +// DBG_PRINT("LinkLayer Init SUCCESS! \n"); +// } +// else +// { +// DBG_PRINT("Linklayer Init FAILED! \n"); +// return Status; +// } +// +// /* System Bus Initialize */ +// Status = SystemBus_Init(&sysbusDescPtr, &sysbusConfig); +// if (Status != SUCCESS) +// { +// DBG_ERROR("Systembus init FAILED, with error code: %" PRId32 "\n", Status); +// return Status; +// } +// +// /* System Monitor log timer Initialize */ +// Status = timer_init(&logTmPtr, &apptimer_init_param); +// if (Status != SUCCESS) +// { +// DBG_ERROR("APP Timer init FAILED, with error code: %" PRId32 "\n", Status); +// return Status; +// } +// +// /* interrupts Initialize */ +// Status = SetupInterruptSystem(intcDescPtr); +// if (Status == SUCCESS) +// { +// DBG_INFO("Interrupt system setup SUCCESS!\n"); +// } +// else +// { +// DBG_ERROR("Interrupt system setup FAILED, ErrorCode: %" PRId32 ".\n", Status); +// return Status; +// } +// +// // Start System Monitor logging. +// timer_start(logTmPtr); +// +// DBG_PRINT("Physical started...\n"); +// Phy_Start(phyInstPtr); +// +// // Linklayer_Start(linkInstPtr); + + while (true) + { +// // System bus handler +// if (SystemBus_IsReqPending(sysbusDescPtr)) +// { +// Status = SystemBus_HandleCmd(sysbusDescPtr); +// if (Status != SUCCESS) +// { +// DBG_ERROR("SystemBus_HandleCmd exit with ErrorCode: %" PRId32 ".\n", Status); +// } +// } +// if (SystemBus_IsRsqPending(sysbusDescPtr) && (!SystemBus_IsTxBusy(sysbusDescPtr))) +// { +// // FIXME: Required by 12 (CASC) +// udelay(4000); +// Status = SystemBus_MakeResp(sysbusDescPtr); +// if (Status != SUCCESS) +// { +// DBG_ERROR("SystemBus_HandleCmd exit with ErrorCode: %" PRId32 ".\n", Status); +// } +// } + } +} // main diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/system_monitor.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/system_monitor.c new file mode 100644 index 0000000..beddcc0 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/system_monitor.c @@ -0,0 +1,402 @@ +/** + * @addtogroup SystemMonitor + * @{ + */ + +/***************************** Include Files *********************************/ +#include +#include +#include +#include "common.h" +#include "system_monitor.h" +/************************** Constant Definitions *****************************/ +/** + * @name SystemMonitor_Private_Constants + * @{ + */ +#define BUFF_A 0x00 +#define BUFF_B 0x01 +#define BUFF_NONE 0xFF + +#define SYSINFO_PACK_LEN 54 // bytes +#define PAYLOAD_PACK_LEN 46 // bytes +#define SLOTLOG_PACK_LEN 88 // bytes +#define PRTCLLOG_PACK_ELN 24 // bytes +#define HWSTATE_PACK_ELN 12 // bytes +/** @} SystemMonitor_Private_Constants*/ +/************************** Data Type Definitions ****************************/ +/** + * @struct PINGPONG + * @brief PingPing Control + */ +typedef struct +{ + uint8_t latest; ///< latest available index + uint8_t rBusy; ///< current read busy index + uint8_t wBusy; ///< current write busy index +} PINGPONG; + +/** + * @struct SYSMONITOR + * @brief System Monitor struct holds all the system log info + */ +typedef struct +{ + PINGPONG recvCtrl; + PAYLOAD recv[2]; + PINGPONG sendCtrl; + PAYLOAD send[2]; + PINGPONG sysInfoCtrl; + SYSINFO sysInfo[2]; + PINGPONG linkLogCtrl; + LINKLOG linkLog[2]; + PINGPONG phyLogCtrl; + PHYLOG phyLog[2]; + PINGPONG hwStateCtrl; + HWSTATE hwState[2]; +} SYSMONITOR; + +/************************** Function Prototypes ******************************/ +static uint8_t GetWritable(PINGPONG ctrllor); +static uint8_t GetReadable(PINGPONG ctrllor); +/************************** Variable Definitions *****************************/ +static SYSMONITOR *sysMonitorInst; + +/*********************** Function Implementations ***************************/ +inline static uint8_t GetWritable(PINGPONG ctrllor) +{ + // Allow only one write. + ASSERT(ctrllor.wBusy == BUFF_NONE); + if (ctrllor.rBusy == BUFF_NONE) + return ((ctrllor.latest == BUFF_B) ? BUFF_A : BUFF_B); + else + return ((ctrllor.rBusy == BUFF_B) ? BUFF_A : BUFF_B); +} + +inline static uint8_t GetReadable(PINGPONG ctrllor) +{ + if (ctrllor.wBusy == BUFF_NONE || ctrllor.wBusy != ctrllor.latest) + return ((ctrllor.latest == BUFF_NONE) ? BUFF_A : BUFF_B); + else + return ((ctrllor.wBusy == BUFF_B) ? BUFF_A : BUFF_B); +} + +/** + * @brief Initialize System monitor + * + * @return SUCCESS + */ +int32_t SysMonitor_Init(void) +{ + sysMonitorInst = calloc(1, sizeof(SYSMONITOR)); + if (sysMonitorInst == NULL) + { + return ENOMEM; + } + memset(&(sysMonitorInst->recvCtrl), BUFF_NONE, sizeof(PINGPONG)); + memset(&(sysMonitorInst->sendCtrl), BUFF_NONE, sizeof(PINGPONG)); + memset(&(sysMonitorInst->sysInfoCtrl), BUFF_NONE, sizeof(PINGPONG)); + memset(&(sysMonitorInst->linkLogCtrl), BUFF_NONE, sizeof(PINGPONG)); + memset(&(sysMonitorInst->phyLogCtrl), BUFF_NONE, sizeof(PINGPONG)); + memset(&(sysMonitorInst->hwStateCtrl), BUFF_NONE, sizeof(PINGPONG)); + + sysMonitorInst->recv[0].dataId = DATA_ID_NONE; + sysMonitorInst->recv[1].dataId = DATA_ID_NONE; + sysMonitorInst->send[0].dataId = DATA_ID_NONE; + sysMonitorInst->send[1].dataId = DATA_ID_NONE; + return SUCCESS; +} + +/** + * @brief Destroy system monitor release all allocated memory + */ +void SysMonitor_Destroy(void) +{ + free(sysMonitorInst); +} + +/** + * @name SystemInfo_API + * @{ + */ + +/** + * @brief Update system info. + * + * @param info + */ +void SysMonitor_UpdateSysinfo(const SYSINFO *info) +{ + uint8_t buffIndx; + buffIndx = GetWritable(sysMonitorInst->sysInfoCtrl); + sysMonitorInst->sysInfoCtrl.wBusy = buffIndx; + memcpy(&(sysMonitorInst->sysInfo[buffIndx]), info, sizeof(SYSINFO)); + sysMonitorInst->sysInfoCtrl.latest = buffIndx; + sysMonitorInst->sysInfoCtrl.wBusy = BUFF_NONE; +} + +/** + * @brief Get system info. + * + * @param info + */ +void SysMonitor_GetSysinfo(SYSINFO *info) +{ + ASSERT(sysMonitorInst != NULL); + uint8_t buffIndx, buffIndex_old; + buffIndex_old = sysMonitorInst->sysInfoCtrl.rBusy; + if (buffIndex_old != BUFF_NONE) + buffIndx = buffIndex_old; // Keep it busy + else + buffIndx = GetReadable(sysMonitorInst->sysInfoCtrl); + sysMonitorInst->sysInfoCtrl.rBusy = buffIndx; + memcpy(info, &(sysMonitorInst->sysInfo[buffIndx]), sizeof(SYSINFO)); + sysMonitorInst->sysInfoCtrl.rBusy = buffIndex_old; +} +/** @} SystemInfo_API*/ + +/** + * @name ServicePayload_API + * @{ + */ + +/** + * @brief Update send payload by + * + * @param buff + * @param len + */ +void SysMonitor_UpdateSendPayload(const uint8_t *buff, size_t len) +{ + ASSERT(sysMonitorInst != NULL); + if (len > PAYLOAD_LEN_MAX) + { + return; + } + uint8_t buffIndx; + buffIndx = GetWritable(sysMonitorInst->sendCtrl); + sysMonitorInst->sendCtrl.wBusy = buffIndx; + memcpy(sysMonitorInst->send[buffIndx].data, buff, len); + sysMonitorInst->send[buffIndx].dataId++; + sysMonitorInst->sendCtrl.latest = buffIndx; + sysMonitorInst->sendCtrl.wBusy = BUFF_NONE; +} + +/** + * @brief Get the latest send payload. + * + * @param buff + * @param id + * @param len + */ +void SysMonitor_GetSendPayload(uint8_t *buff, uint32_t *id, size_t len) +{ + ASSERT(sysMonitorInst != NULL); + ASSERT(buff != NULL); + ASSERT(id != NULL); + if (len > PAYLOAD_LEN_MAX) + { + return; + } + uint8_t buffIndx, buffIndex_old; + buffIndex_old = sysMonitorInst->sendCtrl.rBusy; + if (buffIndex_old != BUFF_NONE) + buffIndx = buffIndex_old; // Keep it busy + else + buffIndx = GetReadable(sysMonitorInst->sendCtrl); + sysMonitorInst->sendCtrl.rBusy = buffIndx; + if (sysMonitorInst->send[buffIndx].dataId != DATA_ID_NONE) + { + memcpy(buff, sysMonitorInst->send[buffIndx].data, len); + } + *id = sysMonitorInst->send[buffIndx].dataId; + sysMonitorInst->sendCtrl.rBusy = buffIndex_old; +} + +/** + * @brief Update receive payload. + * + * @param buff + * @param id + * @param len + */ +void SysMonitor_UpdateRecvPayload(const uint8_t *buff, uint32_t id, size_t len) +{ + ASSERT(sysMonitorInst != NULL); + ASSERT(buff != NULL); + if (id == DATA_ID_NONE || len > PAYLOAD_LEN_MAX) + { + return; + } + uint8_t buffIndx; + buffIndx = GetWritable(sysMonitorInst->recvCtrl); + sysMonitorInst->recvCtrl.wBusy = buffIndx; + memcpy(sysMonitorInst->recv[buffIndx].data, buff, len); + sysMonitorInst->recv[buffIndx].dataId = id; + sysMonitorInst->recvCtrl.latest = buffIndx; + sysMonitorInst->recvCtrl.wBusy = BUFF_NONE; +} + +/** + * @brief Get the latest receive payload. + * + * @param buff + * @param id + * @param len + */ +void SysMonitor_GetRecvPayload(uint8_t *buff, uint32_t *id, size_t len) +{ + ASSERT(sysMonitorInst != NULL); + ASSERT(buff != NULL); + ASSERT(id != NULL); + if (len > PAYLOAD_LEN_MAX) + { + return; + } + uint8_t buffIndx, buffIndex_old; + buffIndex_old = sysMonitorInst->recvCtrl.rBusy; + if (buffIndex_old != BUFF_NONE) + buffIndx = buffIndex_old; // Keep it busy + else + buffIndx = GetReadable(sysMonitorInst->recvCtrl); + sysMonitorInst->recvCtrl.rBusy = buffIndx; + if (sysMonitorInst->recv[buffIndx].dataId != DATA_ID_NONE) + { + memcpy(buff, sysMonitorInst->recv[buffIndx].data, len); + } + *id = sysMonitorInst->recv[buffIndx].dataId; + sysMonitorInst->recvCtrl.rBusy = buffIndex_old; +} +/** @} ServicePayload_API*/ + +/** + * @name Linklayer_Log_API + * @{ + */ + +/** + * @brief Update link layer log + * + * @param log + */ +void SysMonitor_UpdateLinklayerLog(const LINKLOG *log) +{ + ASSERT(sysMonitorInst != NULL); + ASSERT(log != NULL); + uint8_t buffIndx; + buffIndx = GetWritable(sysMonitorInst->linkLogCtrl); + sysMonitorInst->linkLogCtrl.wBusy = buffIndx; + memcpy(&(sysMonitorInst->linkLog[buffIndx]), log, sizeof(LINKLOG)); + sysMonitorInst->linkLogCtrl.latest = buffIndx; + sysMonitorInst->linkLogCtrl.wBusy = BUFF_NONE; +} + +/** + * @brief Get the latest link layer log + * + * @param log + */ +void SysMonitor_GetLinklayerLog(LINKLOG *log) +{ + ASSERT(sysMonitorInst != NULL); + ASSERT(log != NULL); + uint8_t buffIndx, buffIndex_old; + buffIndex_old = sysMonitorInst->linkLogCtrl.rBusy; + if (buffIndex_old != BUFF_NONE) + buffIndx = buffIndex_old; // Keep it busy + else + buffIndx = GetReadable(sysMonitorInst->linkLogCtrl); + sysMonitorInst->linkLogCtrl.rBusy = buffIndx; + memcpy(log, &(sysMonitorInst->linkLog[buffIndx]), sizeof(LINKLOG)); + sysMonitorInst->linkLogCtrl.rBusy = buffIndex_old; +} +/** @} Linklayer_Log_API*/ + +/** + * @name PhysicalLayer_Log_API + * @{ + */ + +/** + * @brief Update physical layer log + * + * @param log + */ +void SysMonitor_UpdatePhysicalLayerLog(const PHYLOG *log) +{ + ASSERT(sysMonitorInst != NULL); + ASSERT(log != NULL); + uint8_t buffIndx; + buffIndx = GetWritable(sysMonitorInst->phyLogCtrl); + sysMonitorInst->phyLogCtrl.wBusy = buffIndx; + memcpy(&(sysMonitorInst->phyLog[buffIndx]), log, sizeof(PHYLOG)); + sysMonitorInst->phyLogCtrl.latest = buffIndx; + sysMonitorInst->phyLogCtrl.wBusy = BUFF_NONE; +} + +/** + * @brief Get the latest physical layer log + * + * @param log + */ +void SysMonitor_GetPhysicalLayerLog(PHYLOG *log) +{ + ASSERT(sysMonitorInst != NULL); + ASSERT(log != NULL); + uint8_t buffIndx, buffIndex_old; + buffIndex_old = sysMonitorInst->phyLogCtrl.rBusy; + if (buffIndex_old != BUFF_NONE) + buffIndx = buffIndex_old; // Keep it busy + else + buffIndx = GetReadable(sysMonitorInst->phyLogCtrl); + sysMonitorInst->phyLogCtrl.rBusy = buffIndx; + memcpy(log, &(sysMonitorInst->phyLog[buffIndx]), sizeof(PHYLOG)); + sysMonitorInst->phyLogCtrl.rBusy = buffIndex_old; +} +/** @} */ + +/** + * @name HardwareState_Log_API + * @{ + */ + +/** + * @brief Log hardware running status. + * + * @param state + */ +void SysMonitor_UpdateHardwareStatus(const HWSTATE *state) +{ + ASSERT(sysMonitorInst != NULL); + ASSERT(state != NULL); + uint8_t buffIndx; + buffIndx = GetWritable(sysMonitorInst->hwStateCtrl); + sysMonitorInst->hwStateCtrl.wBusy = buffIndx; + memcpy(&(sysMonitorInst->hwState[buffIndx]), state, sizeof(HWSTATE)); + sysMonitorInst->hwStateCtrl.latest = buffIndx; + sysMonitorInst->hwStateCtrl.wBusy = BUFF_NONE; +} + +/** + * @brief Get the latest hardware running status. + * + * @param state + */ +void SysMonitor_GetHardwareStatus(HWSTATE *state) +{ + ASSERT(sysMonitorInst != NULL); + ASSERT(state != NULL); + uint8_t buffIndx, buffIndex_old; + buffIndex_old = sysMonitorInst->hwStateCtrl.rBusy; + if (buffIndex_old != BUFF_NONE) + buffIndx = buffIndex_old; // Keep it busy + else + buffIndx = GetReadable(sysMonitorInst->hwStateCtrl); + sysMonitorInst->hwStateCtrl.rBusy = buffIndx; + memcpy(state, &(sysMonitorInst->hwState[buffIndx]), sizeof(HWSTATE)); + sysMonitorInst->hwStateCtrl.rBusy = buffIndex_old; +} + +/** @} HardwareState_Log_API */ + +/** @} SystemMonitor*/ \ No newline at end of file diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/system_monitor.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/system_monitor.h new file mode 100644 index 0000000..f5be369 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/system_monitor.h @@ -0,0 +1,241 @@ +/** + * @defgroup SystemMonitor + * @{ + */ + +#ifndef __SYSTEM_MONITOR_H_ +#define __SYSTEM_MONITOR_H_ + +/***************************** Include Files *********************************/ +#include +#include +#include "util.h" + +/************************* Constant Definitions ******************************/ +/** + * @name SystemMonitor_Constants + * @{ + */ +#define PAYLOAD_LEN_MAX 256 +#define PAYLOAD_LEN_FLJ_RX 168 +#define PAYLOAD_LEN_FLJ_TX 66 +#define PAYLOAD_LEN_LJ_RX 66 +#define PAYLOAD_LEN_LJ_TX 168 + +#define DATA_ID_NONE UINT32_MAX +#define DATA_ID_TEST 0x02030203 + +#define VOLTAGE_LSB 0.01 // volt +#define VOLTAGE_MAX 2.55 // volt + +#define VOLT_PS_STD 1.0 +#define VOLT_PSAUX_STD 1.8 +#define VOLT_DDR_STD 1.5 +#define VOLT_PL_STD 1.0 +#define VOLT_PLAUX_STD 1.8 +#define VOLT_BRAM_STD 1.0 + +#define VOLT_PS_OK BIT(0) +#define VOLT_PSAUX_OK BIT(1) +#define VOLT_DDR_OK BIT(2) +#define VOLT_PL_OK BIT(3) +#define VOLT_PLAUX_OK BIT(4) +#define VOLT_BRAM_OK BIT(5) + +#define VOLT_ANT_PROC_OK BIT(0) +#define VOLT_ANT_RF_OK BIT(2) +#define VOLT_ANT_NIL_OK BIT(4) +#define ANT_STATE_OK BIT(6) + +// Xilinx Recommended +// #define IS_PS_VALID(x) (x <= 1.05 && x >= 0.95) +// #define IS_PSAUX_VALID(x) (x <= 1.89 && x >= 1.71) +// #define IS_DDR_VALID(x) (x <= 1.89 && x >= 1.14) +// #define IS_PL_VALID(x) (x <= 1.03 && x >= 0.97) +// #define IS_PLAUX_VALID(x) (x <= 1.89 && x >= 1.71) +// #define IS_BRAM_VALID(x) (x <= 1.03 && x >= 0.97) +#define IS_PS_VALID(x) (x <= 1.1 && x >= 0.9) +#define IS_PSAUX_VALID(x) (x <= 2.0 && x >= 1.6) +#define IS_DDR_VALID(x) (x <= 1.7 && x >= 1.3) +#define IS_PL_VALID(x) (x <= 1.1 && x >= 0.9) +#define IS_PLAUX_VALID(x) (x <= 2.0 && x >= 1.6) +#define IS_BRAM_VALID(x) (x <= 1.1 && x >= 0.9) + +#define CLK_SP_PLL0_LOCK BIT(0) +#define CLK_SP_PLL1_LOCK BIT(2) + +#define CLK_AD0_BBPLL_LOCK BIT(0) +#define CLK_AD0_RFPLL_LOCK BIT(2) +#define CLK_AD1_BBPLL_LOCK BIT(4) +#define CLK_AD1_RFPLL_LOCK BIT(6) + +#define CLK_ANT_BBPLL_LOCK BIT(0) +#define CLK_ANT_RFPLL0_LOCK BIT(2) +#define CLK_ANT_RFPLL1_LOCK BIT(4) + +#define TEMPERATURE_LSB 1.0 // Celsius degree +#define TEMPERATURE_MAX 127.0 // Celsius degree +#define TEMPERATURE_MIN -128.0 // Celsius degree +/** @} SystemMonitor_Constants */ + +/************************* Data Type Definitions ****************************/ + +/** + * @struct SYSINFO + * @brief System Infomation log + */ +typedef struct __attribute__((packed)) +{ + uint32_t psBDS; ///< -- unit: ms, 0~604799999 + uint16_t pBDS; ///< -- unit: week, 0~0~8191 + uint32_t psGPS; ///< -- unit: ms, 0~604799999 + uint16_t pGPS; ///< -- unit: week, 0~0~8191 + uint8_t tmInd; ///< Time validation indicator \n + // -- [0] GPS\n + // -- [1] reserve.\n + // -- [2] UTC\n + // -- [3] BDS\n + float tmFly; ///< Time of flying uint: s + uint8_t stFly; ///< Status of flying \n + // [7:6] departure status 11 - departed/00 - not departed \n + // [5:4] ready status 11 - ready/00 - not ready \n + // [3:2] attitude status 11 - attitude ok/00 - attitude not ok \n + // [1:0] attitude adjust status 11 - adjusted/00 - not adjusted + float posX; ///< coord X Position in launch coordinate system + float posY; ///< coord Y Position in launch coordinate system + float posZ; ///< coord Z Position in launch coordinate system + float vX; ///< Speed X in launch coordinate system + float vY; ///< Speed Y in launch coordinate system + float vZ; ///< Speed Z in launch coordinate system + float pitch; ///< Pitch Attitude in launch inertia coordinate system + float yaw; ///< Yaw Attitude in launch inertia coordinate system + float roll; ///< Roll Attitude in launch inertia coordinate system +} SYSINFO; + +/** + * @struct SYSBUSLOG + * @brief System Info holds the system bus log + */ +typedef struct __attribute__((packed)) +{ + // SystemBus log + + uint8_t lastConfig[2]; ///< Last valid config \n + // byte 0: 0x11 FLJ, 0x00 LJ \n + // byte 1: 0x66 Full Power, 0x88 Low Power + uint32_t pollCnt; ///< number of poll commands + uint32_t svcCnt; ///< number of service commands + uint32_t remoteCnt; ///< number of remote check commands +} SYSBUSLOG; + +/** + * @struct LINKLOG + * @brief Linklayer Log struct holds the Linklayer log + */ +typedef struct __attribute__((packed)) +{ + // Network log + uint32_t tuNum; ///< current time unite number + uint8_t slotNum; ///< current slot number + uint8_t nodeType; ///< current node type + uint8_t cxnStatus; ///< current connection status + uint8_t linkStage; ///< current linklayer working stage + uint32_t recxnCnt; ///< reconnect trial count + uint8_t beamNo; ///< current working beam number + uint8_t beamWidth; ///< current working beam width + uint16_t beamAz; ///< current working beam angle Az + uint16_t beamEl; ///< current working beam angle Ez + uint16_t advTxCnt; ///< adv frame send count + uint16_t advRecvCnt; ///< adv frame received count + uint16_t advCorrectCnt; ///< adv frame received correctly count + uint32_t cfmTxCnt; ///< cfm frame send count + uint32_t cfmRecvCnt; ///< cfm frame received count + uint32_t cfmCorrectCnt; ///< adv frame received correctly count + uint32_t svcTxCnt; ///< svc frame send count + uint32_t svcRecvCnt; ///< svc frame received count + uint32_t svcCorrectCnt; ///< svc frame received correctly count + int32_t timeDev; ///< last time deviation result + uint32_t sendId; ///< last send data id + uint32_t recvId; ///< last received data id + uint8_t reserve[4]; ///< reserved data +} LINKLOG; + +/** + * @struct PHYLOG + * @brief Physical Layer Log struct holds the Linklayer log + */ +typedef struct __attribute__((packed)) +{ + uint32_t acqCnt; ///< aquisition count + uint32_t fsyncCnt; ///< frame sync count + uint32_t decodeCnt; ///< decode count + uint32_t crcCnt; ///< crc valid count + double doppler; ///< last doppler estimation result + uint64_t recvStamp; ///< last frame arrival time + double acqPAPR; ///< last acquisition peak-average ration + uint8_t acqPhase; ///< last acquisition phase estimation + uint8_t acqFreqCh; ///< last acquisition frequency deviation estimation + double fsyncPAPR; ///< last frame sync peak-average ration + uint8_t fsyncFreqCh; ///< last frame sync frequency estimation + uint16_t deintlvAmpEst; ///< de-interleave amplitude estimation + uint8_t antMode; ///< current antenna working mode + uint8_t reserve[3]; ///< reserved data +} PHYLOG; + +/** + * @struct HWSTATE + * @brief Hardware Status Log struct holds all hardware status + */ +typedef struct __attribute__((packed)) +{ + bool isSpOk; ///< signal processor state + bool isAntOk; ///< antenna state + // -- Signal processor status + uint8_t spFpgaVolt; ///< Signal processor voltage state indicator + uint8_t spClkState; ///< Signal processor clock state indicator + uint8_t spAdLockState; ///< Signal processor adc lock state indicator + uint8_t spAdTemp_0; ///< Signal processor adc0 temperature + uint8_t spAdTemp_1; ///< Signal processor adc1 temperature + uint8_t spFpgaTemp; ///< Signal processor FPGA temperature + // -- Antenna status + uint8_t antVolt; ///< Antenna voltage state indicator + uint8_t antTemp; ///< Antenna rf temperature + uint8_t antPll; ///< Antenna rf lock state indicator + uint8_t antMgc0; ///< Antenna rf mgc0~1 indicator + uint8_t antMgc1; ///< Antenna rf mgc2~3 indicator + uint8_t phyId; + uint8_t reserve[1]; +} HWSTATE; + +/** + * @struct PAYLOAD + * @brief Payload struct holds the service payload + */ +typedef struct +{ + uint32_t dataId; + uint8_t data[PAYLOAD_LEN_MAX]; +} PAYLOAD; + +/************************** Function Prototypes ******************************/ +int32_t SysMonitor_Init(void); +void SysMonitor_Destroy(void); + +void SysMonitor_UpdateSysinfo(const SYSINFO *info); +void SysMonitor_GetSysinfo(SYSINFO *info); + +void SysMonitor_UpdateSendPayload(const uint8_t *buff, size_t len); +void SysMonitor_UpdateRecvPayload(const uint8_t *buff, uint32_t id, size_t len); +void SysMonitor_GetSendPayload(uint8_t *buff, uint32_t *id, size_t len); +void SysMonitor_GetRecvPayload(uint8_t *buff, uint32_t *id, size_t len); + +void SysMonitor_UpdateLinklayerLog(const LINKLOG *log); +void SysMonitor_UpdatePhysicalLayerLog(const PHYLOG *log); +void SysMonitor_UpdateHardwareStatus(const HWSTATE *state); +void SysMonitor_GetLinklayerLog(LINKLOG *log); +void SysMonitor_GetPhysicalLayerLog(PHYLOG *log); +void SysMonitor_GetHardwareStatus(HWSTATE *state); + +#endif //__SYSTEM_MONITOR_H_ + +/** @} */ \ No newline at end of file diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/util/circ_queue.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/util/circ_queue.c new file mode 100644 index 0000000..11270f3 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/util/circ_queue.c @@ -0,0 +1,134 @@ +/*****************************************************************************/ +/** + * @file circ_queue.c + * @author FanHao (hao_fan_bit_ee@163.com) + * @brief A simple circular FIFO bounded queue + * @version 0.1 + * @date 2023-02-23 + * @note Forked from https://github.com/chrismerck/rpa_queue.git + * @copyright Copyright(c) 2024 Beijing Institute of Technology.\n + * Lab of Communication and Network + * + ******************************************************************************/ + +/** + * @addtogroup Utility + * @{ + */ + +/** + * @addtogroup CircularQueue + * @{ + */ +#include "circ_queue.h" +#include "common.h" +#include +#include +#include + +inline bool isCircQueueFull(CIRC_QUEUE *queue) +{ + ASSERT(queue != NULL); + return ((queue)->nelts == (queue)->bounds); +} + +inline bool isCircQueueEmpty(CIRC_QUEUE *queue) +{ + ASSERT(queue != NULL); + return ((queue)->nelts == 0); +} + +void CircQueueFree(CIRC_QUEUE *queue) +{ + ASSERT(queue != NULL); + if (queue->data) + free(queue->data); + free(queue); +} + +/** + * @brief Initialize the CIRC_QUEUE. + */ +bool CircQueueCreate(CIRC_QUEUE **q, uint32_t queueCapacity) +{ + ASSERT(q != NULL); + CIRC_QUEUE *queue; + queue = malloc(sizeof(CIRC_QUEUE)); + if (!queue) + { + return false; + } + *q = queue; + memset(queue, 0, sizeof(CIRC_QUEUE)); + + /* Set all the data in the queue to NULL */ + queue->data = malloc(queueCapacity * sizeof(void *)); + if (queue->data == NULL) + { + goto error; + } + queue->bounds = queueCapacity; + queue->nelts = 0; + queue->in = 0; + queue->out = 0; + return true; +error: + free(queue); + return false; +} + +/** + * @brief Push new data onto the queue. + */ +bool CircQueuePush(CIRC_QUEUE *queue, void *data) +{ + ASSERT(queue != NULL); + + if (isCircQueueFull(queue)) + { + return false; + } + + queue->data[queue->in] = data; + queue->in++; + if (queue->in >= queue->bounds) + { + queue->in -= queue->bounds; + } + queue->nelts++; + + return true; +} + +/** + * @brief Return current number of element in queue. + */ +inline uint32_t CircQueueSize(CIRC_QUEUE *queue) +{ + return queue->nelts; +} + +/** + * @brief Retrieves the next item from the queue. + */ +bool CircQueuePop(CIRC_QUEUE *queue, void **data) +{ + ASSERT(queue != NULL); + + if (isCircQueueEmpty(queue)) + { + return false; + } + + *data = queue->data[queue->out]; + queue->nelts--; + + queue->out++; + if (queue->out >= queue->bounds) + { + queue->out -= queue->bounds; + } + return true; +} +/** @} CircularQueue*/ +/** @} Utility*/ \ No newline at end of file diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/util/clist.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/util/clist.c new file mode 100644 index 0000000..2ed467e --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/util/clist.c @@ -0,0 +1,353 @@ +/*****************************************************************************/ +/** + * @file clist.c + * @author FanHao (hao_fan_bit_ee@163.com) + * @brief A basic list data structure + * @version 0.1 + * @date 2023-02-23 + * @note Forked from https://github.com/clibs/list + * @copyright Copyright(c) 2024 Beijing Institute of Technology.\n + * Lab of Communication and Network + * + ******************************************************************************/ + +/** + * @addtogroup Utility + * @{ + */ + +/** + * @addtogroup cList + * @{ + */ +#ifndef LIST_MALLOC +#define LIST_MALLOC malloc +#endif + +#ifndef LIST_FREE +#define LIST_FREE free +#endif + +#include +#include "clist.h" + +/** + * @brief a new clist_node_t. NULL on failure. + */ +clist_node_t * +clist_node_new(void *val) +{ + clist_node_t *self; + if (!(self = LIST_MALLOC(sizeof(clist_node_t)))) + return NULL; + self->prev = NULL; + self->next = NULL; + self->val = val; + return self; +} + +/** + * @brief Allocate a new clist_t. NULL on failure. + */ +clist_t * +clist_new(void) +{ + clist_t *self; + if (!(self = LIST_MALLOC(sizeof(clist_t)))) + return NULL; + self->head = NULL; + self->tail = NULL; + self->free = NULL; + self->compare = NULL; + self->len = 0; + return self; +} + +/** + * @brief Remove all the node in list. + * @param self: Pointer to the list + */ +void clist_clear(clist_t *self) +{ + clist_node_t *next; + clist_node_t *curr = self->head; + + while (curr != NULL) + { + next = curr->next; + if (self->free) + self->free(curr->val); + LIST_FREE(curr); + curr = next; + } + self->len = 0; + self->head = NULL; +} + +/** + * @brief Free the list. + * @param self: Pointer to the list + */ +void clist_destroy(clist_t *self) +{ + clist_clear(self); + LIST_FREE(self); +} + +/** + * @brief Append the given node to the list and return the node, NULL on failure. + * @param self: Pointer to the list for popping node + * @param node: the node to push + */ +clist_node_t * +clist_rpush(clist_t *self, clist_node_t *node) +{ + if (!node) + return NULL; + + if (self->len) + { + node->prev = self->tail; + node->next = NULL; + self->tail->next = node; + self->tail = node; + } + else + { + self->head = self->tail = node; + node->prev = node->next = NULL; + } + + ++self->len; + return node; +} + +/** + * @brief Return / detach the last node in the list, or NULL. + * @param self: Pointer to the list for popping node + */ + +clist_node_t * +clist_rpop(clist_t *self) +{ + if (!self->len) + return NULL; + + clist_node_t *node = self->tail; + + if (--self->len) + { + (self->tail = node->prev)->next = NULL; + } + else + { + self->tail = self->head = NULL; + } + + node->next = node->prev = NULL; + return node; +} + +/** + * @brief Return / detach the first node in the list, or NULL. + * @param self: Pointer to the list for popping node + */ + +clist_node_t * +clist_lpop(clist_t *self) +{ + if (!self->len) + return NULL; + + clist_node_t *node = self->head; + + if (--self->len) + { + (self->head = node->next)->prev = NULL; + } + else + { + self->head = self->tail = NULL; + } + + node->next = node->prev = NULL; + return node; +} + +/** + * @brief Prepend the given node to the list and return the node, NULL on failure. + * @param self: Pointer to the list for pushing node + * @param node: the node to push + */ + +clist_node_t * +clist_lpush(clist_t *self, clist_node_t *node) +{ + if (!node) + return NULL; + + if (self->len) + { + node->next = self->head; + node->prev = NULL; + self->head->prev = node; + self->head = node; + } + else + { + self->head = self->tail = node; + node->prev = node->next = NULL; + } + + ++self->len; + return node; +} + +/** + * @brief Return the node associated to val or NULL. + * @param self: Pointer to the list for finding given value + * @param val: Value to find + */ + +clist_node_t * +clist_find(clist_t *self, void *val) +{ + clist_iterator_t *it = clist_iterator_new(self, LIST_HEAD); + clist_node_t *node; + + while ((node = clist_iterator_next(it))) + { + if (self->compare) + { + if (self->compare(val, node->val) == 0) + { + clist_iterator_destroy(it); + return node; + } + } + else + { + if (val == node->val) + { + clist_iterator_destroy(it); + return node; + } + } + } + + clist_iterator_destroy(it); + return NULL; +} + +/** + * @brief Return the node at the given index or NULL. + * @param self: Pointer to the list for finding given index + * @param index: the index of node in the list + */ + +clist_node_t * +clist_at(clist_t *self, int index) +{ + clist_direction_t direction = LIST_HEAD; + + if (index < 0) + { + direction = LIST_TAIL; + index = ~index; + } + + if ((unsigned)index < self->len) + { + clist_iterator_t *it = clist_iterator_new(self, direction); + clist_node_t *node = clist_iterator_next(it); + while (index--) + node = clist_iterator_next(it); + clist_iterator_destroy(it); + return node; + } + + return NULL; +} + +/** + * @brief Remove the given node from the list, freeing it and it's value. + * @param self: Pointer to the list to delete a node + * @param node: Pointer the node to be deleted + */ + +void clist_remove(clist_t *self, clist_node_t *node) +{ + node->prev + ? (node->prev->next = node->next) + : (self->head = node->next); + + node->next + ? (node->next->prev = node->prev) + : (self->tail = node->prev); + + if (self->free) + self->free(node->val); + + LIST_FREE(node); + --self->len; +} + +/** + * @brief Allocate a new clist_iterator_t. NULL on failure.\n + * Accepts a direction, which may be LIST_HEAD or LIST_TAIL. + */ + +clist_iterator_t * +clist_iterator_new(clist_t *list, clist_direction_t direction) +{ + clist_node_t *node = direction == LIST_HEAD + ? list->head + : list->tail; + return clist_iterator_new_from_node(node, direction); +} + +/** + * @brief Allocate a new clist_iterator_t with the given start + * node. NULL on failure. + */ + +clist_iterator_t * +clist_iterator_new_from_node(clist_node_t *node, clist_direction_t direction) +{ + clist_iterator_t *self; + if (!(self = LIST_MALLOC(sizeof(clist_iterator_t)))) + return NULL; + self->next = node; + self->direction = direction; + return self; +} + +/** + * @brief Return the next clist_node_t or NULL when no more + * nodes remain in the list. + */ + +clist_node_t * +clist_iterator_next(clist_iterator_t *self) +{ + clist_node_t *curr = self->next; + if (curr) + { + self->next = self->direction == LIST_HEAD + ? curr->next + : curr->prev; + } + return curr; +} + +/** + * @brief Free the list iterator. + */ + +void clist_iterator_destroy(clist_iterator_t *self) +{ + LIST_FREE(self); + self = NULL; +} +/** @} cList*/ +/** @} Utility*/ \ No newline at end of file diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/util/common.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/util/common.c new file mode 100644 index 0000000..e42bf68 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/util/common.c @@ -0,0 +1,51 @@ +/*****************************************************************************/ +/** + * Description: Common functions + * + * Date: 2021-11-16 00:08 + * LastEditors: Fan Hao + * LastEditTime: 2022-07-16 17:13 + * Revisions: Rev 0.1 + * History: + * [2022-07-16]: New branch for Dijian. + * [2021-11-16]: Initial version. + * + * Copyright(c) 2024 Beijing Institute of Technology. + * Lab of Communication and Networking + * + ******************************************************************************/ +/** + * @addtogroup COMMON_MACRO + * @{ + */ +/***************************** Include Files *********************************/ +#include "common.h" +#include "error.h" +#include "util.h" + +/************************** Constant Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/**************************** Type Definitions *******************************/ + +/************************** Variable Definitions *****************************/ + +/*****************************************************************************/ +/** + * @brief Throw an assert. + * + * @param file indicate the filename of the assertion + * @param line indicate which line the assertion occurs + * + * @return NONE. + * + ******************************************************************************/ +void AppAssert(const char *file, int line) +{ + DBG_PRINT("in %s:%d [assert] .\n", file, line); + while (1) + ; +} + +/** @} */ \ No newline at end of file diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/util/crc.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/util/crc.c new file mode 100644 index 0000000..5d88b23 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/util/crc.c @@ -0,0 +1,444 @@ +/*****************************************************************************/ +/** + * Description: CRC implementation file + * + * Author: Hao Fan + * Date: 2021-11-16 00:50 + * LastEditors: Hao Fan + * LastEditTime: 2022-07-17 20:53 + * Revisions: Rev 0.0 + * History: + * [2022-07-17]: Add CRC16_0x8005 CRC8_0x31. + * [2022-01-06]: Initial verion (Basic CRC-16). + * + * Copyright(c) 2024 Beijing Institute of Technology. + * Lab of Communication and Networking + * + ******************************************************************************/ +/** + * @addtogroup Utility + * @{ + */ +#include "util.h" +#include "error.h" + +#ifdef __CRC_LSB +// CRC-16-CCITT G(X)=X16+X12+X5+1 +// Reversed +static const uint16_t crc16_1021_lsb_lut[256] = +{ + 0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF, + 0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7, + 0x1081, 0x0108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E, + 0x9CC9, 0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876, + 0x2102, 0x308B, 0x0210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD, + 0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5, + 0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, 0x54B5, 0x453C, + 0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974, + 0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB, + 0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3, + 0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A, + 0xDECD, 0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72, + 0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9, + 0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1, + 0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x0738, + 0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70, + 0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7, + 0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF, + 0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036, + 0x18C1, 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E, + 0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5, + 0x2942, 0x38CB, 0x0A50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD, + 0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134, + 0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C, + 0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3, + 0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, 0x3EFB, + 0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232, + 0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A, + 0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1, + 0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9, + 0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330, + 0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0x0F78 +}; + +// CRC-16-MAXIM G(X)=X16+X15+X2+1 +const uint16_t crc16_8005_lsb_lut[256] = +{ + 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, + 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, + 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, + 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, + 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, + 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, + 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, + 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, + 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, + 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, + 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, + 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, + 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, + 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, + 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, + 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, + 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, + 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, + 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, + 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, + 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, + 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, + 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, + 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, + 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, + 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, + 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, + 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, + 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, + 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, + 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, + 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 +}; + +// CRC-8-MAXIM G(X)=X8+X5+X4+1 +const uint8_t crc8_31_lsb_lut[256] = +{ + 0x00, 0x5E, 0xBC, 0xE2, 0x61, 0x3F, 0xDD, 0x83, + 0xC2, 0x9C, 0x7E, 0x20, 0xA3, 0xFD, 0x1F, 0x41, + 0x9D, 0xC3, 0x21, 0x7F, 0xFC, 0xA2, 0x40, 0x1E, + 0x5F, 0x01, 0xE3, 0xBD, 0x3E, 0x60, 0x82, 0xDC, + 0x23, 0x7D, 0x9F, 0xC1, 0x42, 0x1C, 0xFE, 0xA0, + 0xE1, 0xBF, 0x5D, 0x03, 0x80, 0xDE, 0x3C, 0x62, + 0xBE, 0xE0, 0x02, 0x5C, 0xDF, 0x81, 0x63, 0x3D, + 0x7C, 0x22, 0xC0, 0x9E, 0x1D, 0x43, 0xA1, 0xFF, + 0x46, 0x18, 0xFA, 0xA4, 0x27, 0x79, 0x9B, 0xC5, + 0x84, 0xDA, 0x38, 0x66, 0xE5, 0xBB, 0x59, 0x07, + 0xDB, 0x85, 0x67, 0x39, 0xBA, 0xE4, 0x06, 0x58, + 0x19, 0x47, 0xA5, 0xFB, 0x78, 0x26, 0xC4, 0x9A, + 0x65, 0x3B, 0xD9, 0x87, 0x04, 0x5A, 0xB8, 0xE6, + 0xA7, 0xF9, 0x1B, 0x45, 0xC6, 0x98, 0x7A, 0x24, + 0xF8, 0xA6, 0x44, 0x1A, 0x99, 0xC7, 0x25, 0x7B, + 0x3A, 0x64, 0x86, 0xD8, 0x5B, 0x05, 0xE7, 0xB9, + 0x8C, 0xD2, 0x30, 0x6E, 0xED, 0xB3, 0x51, 0x0F, + 0x4E, 0x10, 0xF2, 0xAC, 0x2F, 0x71, 0x93, 0xCD, + 0x11, 0x4F, 0xAD, 0xF3, 0x70, 0x2E, 0xCC, 0x92, + 0xD3, 0x8D, 0x6F, 0x31, 0xB2, 0xEC, 0x0E, 0x50, + 0xAF, 0xF1, 0x13, 0x4D, 0xCE, 0x90, 0x72, 0x2C, + 0x6D, 0x33, 0xD1, 0x8F, 0x0C, 0x52, 0xB0, 0xEE, + 0x32, 0x6C, 0x8E, 0xD0, 0x53, 0x0D, 0xEF, 0xB1, + 0xF0, 0xAE, 0x4C, 0x12, 0x91, 0xCF, 0x2D, 0x73, + 0xCA, 0x94, 0x76, 0x28, 0xAB, 0xF5, 0x17, 0x49, + 0x08, 0x56, 0xB4, 0xEA, 0x69, 0x37, 0xD5, 0x8B, + 0x57, 0x09, 0xEB, 0xB5, 0x36, 0x68, 0x8A, 0xD4, + 0x95, 0xCB, 0x29, 0x77, 0xF4, 0xAA, 0x48, 0x16, + 0xE9, 0xB7, 0x55, 0x0B, 0x88, 0xD6, 0x34, 0x6A, + 0x2B, 0x75, 0x97, 0xC9, 0x4A, 0x14, 0xF6, 0xA8, + 0x74, 0x2A, 0xC8, 0x96, 0x15, 0x4B, 0xA9, 0xF7, + 0xB6, 0xE8, 0x0A, 0x54, 0xD7, 0x89, 0x6B, 0x35 +}; +#else +// CRC-16-CCITT G(X)=X16+X12+X5+1 +static const uint16_t crc16_1021_msb_lut[256] = +{ + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, + 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, + 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, + 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, + 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, + 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, + 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, + 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, + 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, + 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, + 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, + 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, + 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, + 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, + 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, + 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, + 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, + 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, + 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, + 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, + 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, + 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, + 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 +}; + +// CRC-16-MAXIM G(X)=X16+X15+X2+1 +const uint16_t crc16_8005_msb_lut[256] = +{ + 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011, + 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022, + 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072, + 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041, + 0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2, + 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1, + 0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1, + 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082, + 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192, + 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1, + 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1, + 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2, + 0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151, + 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162, + 0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132, + 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101, + 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312, + 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321, + 0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371, + 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342, + 0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1, + 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2, + 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2, + 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381, + 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291, + 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2, + 0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2, + 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1, + 0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252, + 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261, + 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231, + 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202 +}; + +// CRC-8-MAXIM G(X)=X8+X5+X4+1 +const uint8_t crc8_31_msb_lut[256] = +{ + 0x00, 0x31, 0x62, 0x53, 0xC4, 0xF5, 0xA6, 0x97, + 0xB9, 0x88, 0xDB, 0xEA, 0x7D, 0x4C, 0x1F, 0x2E, + 0x43, 0x72, 0x21, 0x10, 0x87, 0xB6, 0xE5, 0xD4, + 0xFA, 0xCB, 0x98, 0xA9, 0x3E, 0x0F, 0x5C, 0x6D, + 0x86, 0xB7, 0xE4, 0xD5, 0x42, 0x73, 0x20, 0x11, + 0x3F, 0x0E, 0x5D, 0x6C, 0xFB, 0xCA, 0x99, 0xA8, + 0xC5, 0xF4, 0xA7, 0x96, 0x01, 0x30, 0x63, 0x52, + 0x7C, 0x4D, 0x1E, 0x2F, 0xB8, 0x89, 0xDA, 0xEB, + 0x3D, 0x0C, 0x5F, 0x6E, 0xF9, 0xC8, 0x9B, 0xAA, + 0x84, 0xB5, 0xE6, 0xD7, 0x40, 0x71, 0x22, 0x13, + 0x7E, 0x4F, 0x1C, 0x2D, 0xBA, 0x8B, 0xD8, 0xE9, + 0xC7, 0xF6, 0xA5, 0x94, 0x03, 0x32, 0x61, 0x50, + 0xBB, 0x8A, 0xD9, 0xE8, 0x7F, 0x4E, 0x1D, 0x2C, + 0x02, 0x33, 0x60, 0x51, 0xC6, 0xF7, 0xA4, 0x95, + 0xF8, 0xC9, 0x9A, 0xAB, 0x3C, 0x0D, 0x5E, 0x6F, + 0x41, 0x70, 0x23, 0x12, 0x85, 0xB4, 0xE7, 0xD6, + 0x7A, 0x4B, 0x18, 0x29, 0xBE, 0x8F, 0xDC, 0xED, + 0xC3, 0xF2, 0xA1, 0x90, 0x07, 0x36, 0x65, 0x54, + 0x39, 0x08, 0x5B, 0x6A, 0xFD, 0xCC, 0x9F, 0xAE, + 0x80, 0xB1, 0xE2, 0xD3, 0x44, 0x75, 0x26, 0x17, + 0xFC, 0xCD, 0x9E, 0xAF, 0x38, 0x09, 0x5A, 0x6B, + 0x45, 0x74, 0x27, 0x16, 0x81, 0xB0, 0xE3, 0xD2, + 0xBF, 0x8E, 0xDD, 0xEC, 0x7B, 0x4A, 0x19, 0x28, + 0x06, 0x37, 0x64, 0x55, 0xC2, 0xF3, 0xA0, 0x91, + 0x47, 0x76, 0x25, 0x14, 0x83, 0xB2, 0xE1, 0xD0, + 0xFE, 0xCF, 0x9C, 0xAD, 0x3A, 0x0B, 0x58, 0x69, + 0x04, 0x35, 0x66, 0x57, 0xC0, 0xF1, 0xA2, 0x93, + 0xBD, 0x8C, 0xDF, 0xEE, 0x79, 0x48, 0x1B, 0x2A, + 0xC1, 0xF0, 0xA3, 0x92, 0x05, 0x34, 0x67, 0x56, + 0x78, 0x49, 0x1A, 0x2B, 0xBC, 0x8D, 0xDE, 0xEF, + 0x82, 0xB3, 0xE0, 0xD1, 0x46, 0x77, 0x24, 0x15, + 0x3B, 0x0A, 0x59, 0x68, 0xFF, 0xCE, 0x9D, 0xAC +}; +#endif + +/* CRC lookup table algorithm +#define CRC_BIT_NUM 8u/16u/32u/64u +#define BITS_IN_BYTE 8u +uint8_t dataBuff[ARRAY_SIZE] = { 0x32, 0x33, …}; +uint8_t/uint16_t/uint32_t crc_val = 0x0; +// LSB +for(int i = 0; i < sizeof(dataBuff); i++) +{ + crc_val = (crc_val >> BITS_IN_BYTE) ^ crc_lsb_lut[(uint8)(crc_val^dataBuff[i])]; +} +// MSB +for(int i = 0; i < sizeof(dataBuff); i++) +{ + crc_val = (crc_val << BITS_IN_BYTE) ^ crc_msb_lut[(uint8)((crc_val>>8)^dataBuff[i])]; +} +*/ + +/*****************************************************************************/ +/** + * @brief CRC8 generator + * + * @param buffPtr data buffer pointer. + * @param dataLen data length + * @param poly polynomials of crc + * @param crcVal a pointer to store generated crc value + * + * @return SUCCESS + * FAILURE + * + ******************************************************************************/ +int crc8_gen(uint8_t *buffPtr, uint32_t dataLen, uint16_t poly, uint8_t *crcVal) +{ + uint8_t crc = CRC8_INIT; + const uint8_t *crc_lut = NULL; + + switch (poly) + { +#ifdef __CRC_LSB + case 0x31: + crc_lut = crc8_31_lsb_lut; + break; + default: + return FAILURE; +#else + case 0x31: + crc_lut = crc8_31_msb_lut; + break; + default: + return FAILURE; +#endif + } + + for (int i = 0; i < dataLen; i++) + { + crc = crc_lut[(uint8_t)(crc ^ buffPtr[i])]; + } + *crcVal = crc; + return SUCCESS; +} + +/*****************************************************************************/ +/** + * @brief CRC16 generator + * + * @param buffPtr data buffer pointer. + * @param dataLen data length + * @param poly polynomials of crc + * @param crcVal a pointer to store generated crc value + * + * @return SUCCESS + * FAILURE + * + ******************************************************************************/ +int crc16_gen(uint8_t *buffPtr, uint32_t dataLen, uint16_t poly, uint16_t *crcVal) +{ + uint16_t crc = CRC16_INIT; + const uint16_t *crc_lut = NULL; + switch (poly) + { +#ifdef __CRC_LSB + case 0x8005: + crc_lut = crc16_8005_lsb_lut; + break; + case 0x1021: + crc_lut = crc16_1021_lsb_lut; + break; + default: + return FAILURE; +#else + case 0x8005: + crc_lut = crc16_8005_msb_lut; + break; + case 0x1021: + crc_lut = crc16_1021_msb_lut; + break; + default: + return FAILURE; +#endif + } + for (int i = 0; i < dataLen; i++) + { +#ifdef __CRC_LSB + crc = (crc >> 8) ^ crc_lut[(uint8_t)(crc ^ buffPtr[i])]; +#else + crc = (crc << 8) ^ crc_lut[(uint8_t)((crc >> 8) ^ buffPtr[i])]; +#endif + } + *crcVal = crc; + return SUCCESS; +} + +/*****************************************************************************/ +/** + * @brief CRC8 compare cheker + * + * @param buffPtr data buffer pointer. + * @param dataLen data length + * @param crcOffset byte offset of crc stored position. + * @param poly polynomials of crc + * + * @return true + * false + * + ******************************************************************************/ +bool crc8_check(uint8_t *buffPtr, uint32_t dataLen, uint32_t crcOffset, uint16_t poly) +{ + int Status = SUCCESS; + uint8_t crc_comp; + uint8_t crc; + if (dataLen == 0) + { + return false; + } + crc_comp = (uint8_t)(*(buffPtr + crcOffset)); + Status = crc8_gen(buffPtr, dataLen, poly, &crc); + if (Status == SUCCESS) + { + if (crc == crc_comp) + { + return true; + } + else + { + return false; + } + } + else + { + return false; + } +} + +/*****************************************************************************/ +/** + * @brief CRC16 compare cheker + * + * @param buffPtr data buffer pointer. + * @param dataLen data length + * @param crcOffset byte offset of crc stored position. + * @param poly polynomials of crc + * + * @return true + * fasle + * + ******************************************************************************/ +bool crc16_check(uint8_t *buffPtr, uint32_t dataLen, uint32_t crcOffset, uint16_t poly) +{ + int Status = SUCCESS; + uint16_t crc_comp; + uint16_t crc; + if (dataLen == 0) + { + return false; + } + crc_comp = (uint16_t)(*(buffPtr + crcOffset)); + crc_comp |= (uint16_t)((*(buffPtr + crcOffset + 1)) << 8); + Status = crc16_gen(buffPtr, dataLen, poly, &crc); + if (Status == SUCCESS) + { + if (crc == crc_comp) + { + return true; + } + else + { + return false; + } + } + else + { + return false; + } +} + +/** @}*/ \ No newline at end of file diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/util/list.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/util/list.c new file mode 100644 index 0000000..46a57ba --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/util/list.c @@ -0,0 +1,886 @@ +/******************************************************************************* + * @file list.c + * @brief List library implementation + * @author Mihail Chindris (mihail.chindris@analog.com) +******************************************************************************** + * Copyright 2020(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, 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. +*******************************************************************************/ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ + +#include "list.h" +#include "error.h" +#include + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ + +/** + * @struct list_elem + * @brief Format of each element of the list + */ +struct list_elem +{ + /** User data */ + void *data; + /** Reference to previous element */ + struct list_elem *prev; + /** Reference to next element */ + struct list_elem *next; +}; + +/** + * @struct list_iterator + * @brief Structure used to iterate through the list + */ +struct iterator +{ + /** List reference */ + struct _list_desc *list; + /** Current element reference */ + struct list_elem *elem; +}; + +/** + * @struct _list_desc + * @brief List descriptor + */ +struct _list_desc +{ + /** Reference to first element in the list */ + struct list_elem *first; + /** Reference to last element in the list*/ + struct list_elem *last; + /** Number of elements in the list */ + uint32_t nb_elements; + /** Function used to compare elements */ + f_cmp comparator; + /** Number of current active iterators */ + uint32_t nb_iterators; + /** Internal list iterator */ + struct iterator l_it; +}; + +/** @brief Default function used to compare element in the list ( \ref f_cmp) */ +static int32_t default_comparator(void *data1, void *data2) +{ + return (int32_t)((int32_t *)data1 - (int32_t *)data2); +} + +/** + * @brief Creates a new list elements an configure its value + * @param data - To set list_elem.data + * @param prev - To set list_elem.prev + * @param next - To set list_elem.next + * @return Address of the new element or NULL if allocation fails. + */ +static inline struct list_elem *create_element(void *data, + struct list_elem *prev, + struct list_elem *next) +{ + struct list_elem *elem; + + elem = (struct list_elem *)calloc(1, sizeof(*elem)); + if (!elem) + return NULL; + elem->data = data; + elem->prev = prev; + elem->next = next; + + return (elem); +} + +/** + * @brief Updates the necesary link on the list elements to add or remove one + * @param prev - Low element + * @param elem - Middle element + * @param next - High element + */ +static inline void update_links(struct list_elem *prev, struct list_elem *elem, + struct list_elem *next) +{ + if (prev) + prev->next = elem ? elem : next; + if (elem) + { + elem->prev = prev; + elem->next = next; + } + if (next) + next->prev = elem ? elem : prev; +} + +/** + * @brief Update according to modification the list descriptor + * @param list - List reference + * @param new_first - New first element + * @param new_last - New last element + */ +static inline void update_desc(struct _list_desc *list, + struct list_elem *new_first, + struct list_elem *new_last) +{ + if (new_first == list->first) + { + list->last = new_last; + if (new_first == NULL || new_last == NULL) + list->first = new_last; + } + else + { /* if (new_last == list->last) */ + list->first = new_first; + if (new_last == NULL || new_first == NULL) + list->last = new_first; + } +} + +/** + * @brief Set the adapter functions acording to the adapter type + * @param ad - Reference of the adapter + * @param type - Type of the adapter + */ +static inline void set_adapter(struct list_desc *ad, enum adapter_type type) +{ + switch (type) + { + case LIST_PRIORITY_LIST: + ad->push = list_add_find; + ad->pop = list_get_first; + ad->top_next = list_read_first; + ad->back = list_read_last; + ad->swap = list_edit_first; + break; + case LIST_QUEUE: + ad->push = list_add_last; + ad->pop = list_get_first; + ad->top_next = list_read_first; + ad->back = list_read_last; + ad->swap = list_edit_first; + break; + case LIST_DEFAULT: + case LIST_STACK: + default: + ad->push = list_add_last; + ad->pop = list_get_last; + ad->top_next = list_read_last; + ad->back = list_read_first; + ad->swap = list_edit_last; + break; + } +} + +/** + * @brief Create a new empty list + * @param list_desc - Where to store the reference of the new created list + * @param type - Type of adapter to use. + * @param comparator - Used to compare item when using an ordered list or when + * using the \em find functions. + * @return + * - \ref SUCCESS : On success + * - \ref FAILURE : Otherwise + */ +int32_t list_init(struct list_desc **list_desc, enum adapter_type type, + f_cmp comparator) +{ + struct list_desc *l_desc; + struct _list_desc *list; + + if (!list_desc) + return FAILURE; + l_desc = (struct list_desc *)calloc(1, sizeof(*l_desc)); + if (!l_desc) + return FAILURE; + list = (struct _list_desc *)calloc(1, sizeof(*list)); + if (!list) + { + free(l_desc); + return FAILURE; + } + + *list_desc = l_desc; + l_desc->priv_desc = list; + list->comparator = comparator ? comparator : default_comparator; + + /* Configure wrapper */ + set_adapter(l_desc, type); + list->l_it.list = list; + + return SUCCESS; +} + +/** + * @brief Remove the created list. + * + * All its elements will be cleared and the data inside each will be lost. If + * not all iterators have been removed, the list will not be removed. + * @param list_desc - Reference to the list + * @return + * - \ref SUCCESS : On success + * - \ref FAILURE : Otherwise + */ +int32_t list_remove(struct list_desc *list_desc) +{ + void *data; + struct _list_desc *list; + + if (!list_desc) + return FAILURE; + + list = list_desc->priv_desc; + if (list->nb_iterators != 0) + return FAILURE; + + /* Remove all the elements */ + while (SUCCESS == list_get_first(list_desc, &data)) + ; + free(list_desc->priv_desc); + free(list_desc); + + return SUCCESS; +} + +/** + * @brief Get the number of elements inside the list + * @param list_desc - List reference + * @param out_size - Where to store the number of elements + * @return + * - \ref SUCCESS : On success + * - \ref FAILURE : Otherwise + */ +int32_t list_get_size(struct list_desc *list_desc, uint32_t *out_size) +{ + struct _list_desc *list; + + if (!list_desc || !out_size) + return FAILURE; + + list = list_desc->priv_desc; + *out_size = list->nb_elements; + + return SUCCESS; +} + +/** @brief Add element at the begining of the list. Refer to \ref f_add */ +int32_t list_add_first(struct list_desc *list_desc, void *data) +{ + struct list_elem *prev; + struct list_elem *next; + struct list_elem *elem; + struct _list_desc *list; + + if (!list_desc) + return FAILURE; + + list = list_desc->priv_desc; + + prev = NULL; + next = list->first; + elem = create_element(data, prev, next); + if (!elem) + return FAILURE; + + update_links(prev, elem, next); + + update_desc(list, elem, list->last); + + list->nb_elements++; + + return SUCCESS; +} + +/** @brief Add element at the end of the list. Refer to \ref f_add */ +int32_t list_add_last(struct list_desc *list_desc, void *data) +{ + struct list_elem *prev; + struct list_elem *next; + struct list_elem *elem; + struct _list_desc *list; + + if (!list_desc) + return FAILURE; + list = list_desc->priv_desc; + + prev = list->last; + next = NULL; + elem = create_element(data, prev, next); + if (!elem) + return FAILURE; + + update_links(prev, elem, next); + + update_desc(list, list->first, elem); + + list->nb_elements++; + + return SUCCESS; +} + +/** @brief Add element at the specified idx. Refer to \ref f_add */ +int32_t list_add_idx(struct list_desc *list_desc, void *data, uint32_t idx) +{ + struct _list_desc *list; + + if (!list_desc) + return FAILURE; + list = list_desc->priv_desc; + + /* If there are no elements the creation of an iterator will fail */ + if (list->nb_elements == 0 || idx == 0) + return list_add_first(list_desc, data); + if (list->nb_elements == idx) + return list_add_last(list_desc, data); + + list->l_it.elem = list->first; + if (SUCCESS != iterator_move(&(list->l_it), idx)) + return FAILURE; + + return iterator_insert(&(list->l_it), data, 0); +} + +/** @brief Add element in ascending order. Refer to \ref f_add */ +int32_t list_add_find(struct list_desc *list_desc, void *data) +{ + struct list_elem *elem; + struct _list_desc *list; + + if (!list_desc) + return FAILURE; + list = list_desc->priv_desc; + + /* Based on place iterator */ + elem = list->first; + while (elem) + { + if (0 < list->comparator(elem->data, data)) + break; + elem = elem->next; + } + if (elem == NULL) + { + list->l_it.elem = list->last; + return iterator_insert(&(list->l_it), data, 1); + } + else + { + list->l_it.elem = elem; + return iterator_insert(&(list->l_it), data, 0); + } +} + +/** @brief Edit the first element of the list. Refer to \ref f_edit */ +int32_t list_edit_first(struct list_desc *list_desc, void *new_data) +{ + struct _list_desc *list; + + if (!list_desc) + return FAILURE; + + list = list_desc->priv_desc; + list->first->data = new_data; + + return SUCCESS; +} + +/** @brief Edit the last element of the list. Refer to \ref f_edit */ +int32_t list_edit_last(struct list_desc *list_desc, void *new_data) +{ + struct _list_desc *list; + + if (!list_desc) + return FAILURE; + + list = list_desc->priv_desc; + list->last->data = new_data; + + return SUCCESS; +} + +/** @brief Edit the element at the specified idx. Refer to \ref f_edit */ +int32_t list_edit_idx(struct list_desc *list_desc, void *new_data, uint32_t idx) +{ + struct _list_desc *list; + + if (!list_desc) + return FAILURE; + list = list_desc->priv_desc; + + list->l_it.elem = list->first; + if (SUCCESS != iterator_move(&(list->l_it), idx)) + return FAILURE; + + return iterator_edit(&(list->l_it), new_data); +} + +/** @brief Edit the element which match with cmp_data. Refer to \ref f_edit */ +int32_t list_edit_find(struct list_desc *list_desc, void *new_data, + void *cmp_data) +{ + struct _list_desc *list; + + if (!list_desc) + return FAILURE; + list = list_desc->priv_desc; + + list->l_it.elem = list->first; + if (SUCCESS != iterator_find(&(list->l_it), cmp_data)) + return FAILURE; + + return iterator_edit(&(list->l_it), new_data); +} + +/** @brief Read the first element of the list. Refer to \ref f_read */ +int32_t list_read_first(struct list_desc *list_desc, void **data) +{ + struct _list_desc *list; + + if (!list_desc || !data) + return FAILURE; + + *data = NULL; + list = list_desc->priv_desc; + if (!list->first) + return FAILURE; + + *data = list->first->data; + + return SUCCESS; +} + +/** @brief Read the last element of the list. Refer to \ref f_read */ +int32_t list_read_last(struct list_desc *list_desc, void **data) +{ + struct _list_desc *list; + + if (!list_desc || !data) + return FAILURE; + + *data = NULL; + list = list_desc->priv_desc; + if (!list->last) + return FAILURE; + + *data = list->last->data; + + return SUCCESS; +} + +/** @brief Read the element at the specified idx. Refer to \ref f_read */ +int32_t list_read_idx(struct list_desc *list_desc, void **data, uint32_t idx) +{ + struct _list_desc *list; + + if (!list_desc || !data) + return FAILURE; + + *data = NULL; + list = list_desc->priv_desc; + if (!list) + return FAILURE; + + if (idx >= list->nb_elements) + return FAILURE; + + list->l_it.elem = list->first; + if (SUCCESS != iterator_move(&(list->l_it), idx)) + return FAILURE; + + return iterator_read(&(list->l_it), data); +} + +/** @brief Read the element which match with cmp_data. Refer to \ref f_read */ +int32_t list_read_find(struct list_desc *list_desc, void **data, + void *cmp_data) +{ + struct _list_desc *list; + + if (!list_desc || !data) + return FAILURE; + + *data = NULL; + list = list_desc->priv_desc; + if (!list) + return FAILURE; + + list = list_desc->priv_desc; + list->l_it.elem = list->first; + if (SUCCESS != iterator_find(&(list->l_it), cmp_data)) + return FAILURE; + + return iterator_read(&(list->l_it), data); +} + +/** @brief Read and delete the first element of the list. Refer to \ref f_get */ +int32_t list_get_first(struct list_desc *list_desc, void **data) +{ + struct list_elem *prev; + struct list_elem *next; + struct list_elem *elem; + struct _list_desc *list; + + if (!list_desc || !data) + return FAILURE; + + *data = NULL; + list = list_desc->priv_desc; + if (!list->nb_elements) + return FAILURE; + + elem = list->first; + prev = elem->prev; + next = elem->next; + + update_links(prev, NULL, next); + update_desc(list, next, list->last); + list->nb_elements--; + + *data = elem->data; + free(elem); + + return SUCCESS; +} + +/** @brief Read and delete the last element of the list. Refer to \ref f_get */ +int32_t list_get_last(struct list_desc *list_desc, void **data) +{ + struct list_elem *prev; + struct list_elem *next; + struct list_elem *elem; + struct _list_desc *list; + + if (!list_desc || !data) + return FAILURE; + + *data = NULL; + list = list_desc->priv_desc; + if (!list->nb_elements) + return FAILURE; + + elem = list->last; + prev = elem->prev; + next = elem->next; + + update_links(prev, NULL, next); + update_desc(list, list->first, prev); + list->nb_elements--; + + *data = elem->data; + free(elem); + + return SUCCESS; +} + +/** @brief Read and delete the element at idx. Refer to \ref f_get */ +int32_t list_get_idx(struct list_desc *list_desc, void **data, uint32_t idx) +{ + struct _list_desc *list; + + if (!list_desc || !data) + return FAILURE; + + *data = NULL; + list = list_desc->priv_desc; + list->l_it.elem = list->first; + if (SUCCESS != iterator_move(&(list->l_it), idx)) + return FAILURE; + + return iterator_get(&(list->l_it), data); +} + +/** + * @brief Read and delete the element which match with cmp_data. + * Refer to \ref f_get + */ +int32_t list_get_find(struct list_desc *list_desc, void **data, void *cmp_data) +{ + struct _list_desc *list; + + if (!list_desc || !data) + return FAILURE; + + *data = NULL; + list = list_desc->priv_desc; + list->l_it.elem = list->first; + if (SUCCESS != iterator_find(&(list->l_it), cmp_data)) + return FAILURE; + + return iterator_get(&(list->l_it), data); +} + +/** + * @brief Create a new iterator + * @param iter - Where to store the reference for the new iterator + * @param list_desc - Reference of the list the iterator will be used for + * @param start - If it is true the iterator will be positioned at the first + * element of the list, else it will be positioned at the last. + * @return + * - \ref SUCCESS : On success + * - \ref FAILURE : Otherwise + */ +int32_t iterator_init(struct iterator **iter, struct list_desc *list_desc, + bool start) +{ + struct iterator *it; + + if (!list_desc) + return FAILURE; + + it = (struct iterator *)calloc(1, sizeof(*it)); + if (!it) + return FAILURE; + it->list = list_desc->priv_desc; + it->list->nb_iterators++; + it->elem = start ? it->list->first : it->list->last; + *iter = it; + + return SUCCESS; +} + +/** + * @brief Remove the created iterator + * @param iter - Reference of the iterator + * @return + * - \ref SUCCESS : On success + * - \ref FAILURE : Otherwise + */ +int32_t iterator_remove(struct iterator *iter) +{ + struct iterator *it = iter; + + if (!it) + return FAILURE; + + it->list->nb_iterators--; + free(it); + + return SUCCESS; +} + +/** + * @brief Move the position of the iteration through the list. + * + * If the required position is outside the list, the call will fail and the + * iterator will keep its position. + * @param iter - Reference of the iterator + * @param steps - Number of positions to be move. If positive, it will be moved + * forward, otherwise backwords. + * @return + * - \ref SUCCESS : On success + * - \ref FAILURE : Otherwise + */ +int32_t iterator_move(struct iterator *iter, int32_t steps) +{ + struct iterator *it = iter; + struct list_elem *elem; + int32_t dir = (steps < 0) ? -1 : 1; + + if (!it) + return FAILURE; + + steps = abs(steps); + elem = it->elem; + while (steps > 0 && elem) + { + elem = dir > 0 ? elem->next : elem->prev; + steps--; + } + if (!elem) + return FAILURE; + + it->elem = elem; + + return SUCCESS; +} + +/** + * @brief Move the position of the iterator at the specified index of the list. + * + * @param iter - Reference of the iterator + * @param idx - Position in the list. If negative start counting backwords + * @return + * - \ref SUCCESS : On success + * - \ref FAILURE : Otherwise + */ +int32_t iterator_move_to_idx(struct iterator *iter, int32_t idx) +{ + struct list_elem *elem; + int32_t dir = (idx < 0) ? -1 : 1; + + if (!iter) + return FAILURE; + + idx = abs(idx); + elem = dir > 0 ? iter->list->first : iter->list->last; + while (idx > 0 && elem) + { + elem = dir > 0 ? elem->next : elem->prev; + idx--; + } + if (!elem) + return FAILURE; + + iter->elem = elem; + + return SUCCESS; +} + +/** + * @brief Place the iterator where cmp_data if found. + * @param iter - Reference to the iterator + * @param cmp_data - Data to be found + * @return + * - \ref SUCCESS : On success + * - \ref FAILURE : Otherwise + */ +int32_t iterator_find(struct iterator *iter, void *cmp_data) +{ + struct iterator *it = iter; + struct list_elem *elem; + + if (!it) + return FAILURE; + + elem = it->list->first; + while (elem) + { + if (0 == it->list->comparator(elem->data, cmp_data)) + { + it->elem = elem; + return SUCCESS; + } + elem = elem->next; + } + + return FAILURE; +} + +/** + * @brief Replace the data at the current position. Refer to \ref f_edit + */ +int32_t iterator_edit(struct iterator *iter, void *new_data) +{ + struct iterator *it = iter; + + if (!it) + return FAILURE; + + it->elem->data = new_data; + + return SUCCESS; +} + +/** + * @brief Read and remove the data at the current position. Refer to \ref f_get. + * + * If the current item is the last one, the iterator will be moved to the + * previous one. + */ +int32_t iterator_get(struct iterator *iter, void **data) +{ + struct iterator *it = iter; + struct list_elem *next; + + if (!it || !it->elem || !data) + return FAILURE; + + update_links(it->elem->prev, NULL, it->elem->next); + if (it->elem == it->list->first) + update_desc(it->list, it->elem->next, it->list->last); + else if (it->elem == it->list->last) + update_desc(it->list, it->list->first, it->elem->prev); + it->list->nb_elements--; + + *data = it->elem->data; + if (it->elem == it->list->last) + next = it->elem->prev; + else + next = it->elem->next; + free(it->elem); + it->elem = next; + + return SUCCESS; +} + +/** + * @brief Read the data at the current position. Refer to \ref f_read + */ +int32_t iterator_read(struct iterator *iter, void **data) +{ + struct iterator *it = iter; + + if (!it || !it->elem || !data) + return FAILURE; + + *data = it->elem->data; + + return SUCCESS; +} + +/** + * @brief Insert an item in the list. Refer to \ref f_add + * @param iter + * @param data + * @param after - If true, the item will be inserted after the current position. + * Otherwise it will be inserted before. + */ +int32_t iterator_insert(struct iterator *iter, void *data, bool after) +{ + struct iterator *it = iter; + struct list_elem *elem; + struct list_desc list_desc; + + if (!it) + return FAILURE; + + list_desc.priv_desc = iter->list; + if (after && it->elem == it->list->last) + return list_add_last(&list_desc, data); + if (!after && it->elem == it->list->first) + return list_add_first(&list_desc, data); + + if (after) + elem = create_element(data, it->elem, it->elem->next); + else + elem = create_element(data, it->elem->prev, it->elem); + if (!elem) + return FAILURE; + + update_links(elem->prev, elem, elem->next); + + it->list->nb_elements++; + + return SUCCESS; +} diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/util/tables.txt b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/util/tables.txt new file mode 100644 index 0000000..b3bff8c --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/util/tables.txt @@ -0,0 +1,231 @@ +Generating CRC table for crc8-31-lsb + +0x00, 0x5E, 0xBC, 0xE2, 0x61, 0x3F, 0xDD, 0x83, +0xC2, 0x9C, 0x7E, 0x20, 0xA3, 0xFD, 0x1F, 0x41, +0x9D, 0xC3, 0x21, 0x7F, 0xFC, 0xA2, 0x40, 0x1E, +0x5F, 0x01, 0xE3, 0xBD, 0x3E, 0x60, 0x82, 0xDC, +0x23, 0x7D, 0x9F, 0xC1, 0x42, 0x1C, 0xFE, 0xA0, +0xE1, 0xBF, 0x5D, 0x03, 0x80, 0xDE, 0x3C, 0x62, +0xBE, 0xE0, 0x02, 0x5C, 0xDF, 0x81, 0x63, 0x3D, +0x7C, 0x22, 0xC0, 0x9E, 0x1D, 0x43, 0xA1, 0xFF, +0x46, 0x18, 0xFA, 0xA4, 0x27, 0x79, 0x9B, 0xC5, +0x84, 0xDA, 0x38, 0x66, 0xE5, 0xBB, 0x59, 0x07, +0xDB, 0x85, 0x67, 0x39, 0xBA, 0xE4, 0x06, 0x58, +0x19, 0x47, 0xA5, 0xFB, 0x78, 0x26, 0xC4, 0x9A, +0x65, 0x3B, 0xD9, 0x87, 0x04, 0x5A, 0xB8, 0xE6, +0xA7, 0xF9, 0x1B, 0x45, 0xC6, 0x98, 0x7A, 0x24, +0xF8, 0xA6, 0x44, 0x1A, 0x99, 0xC7, 0x25, 0x7B, +0x3A, 0x64, 0x86, 0xD8, 0x5B, 0x05, 0xE7, 0xB9, +0x8C, 0xD2, 0x30, 0x6E, 0xED, 0xB3, 0x51, 0x0F, +0x4E, 0x10, 0xF2, 0xAC, 0x2F, 0x71, 0x93, 0xCD, +0x11, 0x4F, 0xAD, 0xF3, 0x70, 0x2E, 0xCC, 0x92, +0xD3, 0x8D, 0x6F, 0x31, 0xB2, 0xEC, 0x0E, 0x50, +0xAF, 0xF1, 0x13, 0x4D, 0xCE, 0x90, 0x72, 0x2C, +0x6D, 0x33, 0xD1, 0x8F, 0x0C, 0x52, 0xB0, 0xEE, +0x32, 0x6C, 0x8E, 0xD0, 0x53, 0x0D, 0xEF, 0xB1, +0xF0, 0xAE, 0x4C, 0x12, 0x91, 0xCF, 0x2D, 0x73, +0xCA, 0x94, 0x76, 0x28, 0xAB, 0xF5, 0x17, 0x49, +0x08, 0x56, 0xB4, 0xEA, 0x69, 0x37, 0xD5, 0x8B, +0x57, 0x09, 0xEB, 0xB5, 0x36, 0x68, 0x8A, 0xD4, +0x95, 0xCB, 0x29, 0x77, 0xF4, 0xAA, 0x48, 0x16, +0xE9, 0xB7, 0x55, 0x0B, 0x88, 0xD6, 0x34, 0x6A, +0x2B, 0x75, 0x97, 0xC9, 0x4A, 0x14, 0xF6, 0xA8, +0x74, 0x2A, 0xC8, 0x96, 0x15, 0x4B, 0xA9, 0xF7, +0xB6, 0xE8, 0x0A, 0x54, 0xD7, 0x89, 0x6B, 0x35, + + +Generating CRC table for crc8-31-msb + +0x00, 0x31, 0x62, 0x53, 0xC4, 0xF5, 0xA6, 0x97, +0xB9, 0x88, 0xDB, 0xEA, 0x7D, 0x4C, 0x1F, 0x2E, +0x43, 0x72, 0x21, 0x10, 0x87, 0xB6, 0xE5, 0xD4, +0xFA, 0xCB, 0x98, 0xA9, 0x3E, 0x0F, 0x5C, 0x6D, +0x86, 0xB7, 0xE4, 0xD5, 0x42, 0x73, 0x20, 0x11, +0x3F, 0x0E, 0x5D, 0x6C, 0xFB, 0xCA, 0x99, 0xA8, +0xC5, 0xF4, 0xA7, 0x96, 0x01, 0x30, 0x63, 0x52, +0x7C, 0x4D, 0x1E, 0x2F, 0xB8, 0x89, 0xDA, 0xEB, +0x3D, 0x0C, 0x5F, 0x6E, 0xF9, 0xC8, 0x9B, 0xAA, +0x84, 0xB5, 0xE6, 0xD7, 0x40, 0x71, 0x22, 0x13, +0x7E, 0x4F, 0x1C, 0x2D, 0xBA, 0x8B, 0xD8, 0xE9, +0xC7, 0xF6, 0xA5, 0x94, 0x03, 0x32, 0x61, 0x50, +0xBB, 0x8A, 0xD9, 0xE8, 0x7F, 0x4E, 0x1D, 0x2C, +0x02, 0x33, 0x60, 0x51, 0xC6, 0xF7, 0xA4, 0x95, +0xF8, 0xC9, 0x9A, 0xAB, 0x3C, 0x0D, 0x5E, 0x6F, +0x41, 0x70, 0x23, 0x12, 0x85, 0xB4, 0xE7, 0xD6, +0x7A, 0x4B, 0x18, 0x29, 0xBE, 0x8F, 0xDC, 0xED, +0xC3, 0xF2, 0xA1, 0x90, 0x07, 0x36, 0x65, 0x54, +0x39, 0x08, 0x5B, 0x6A, 0xFD, 0xCC, 0x9F, 0xAE, +0x80, 0xB1, 0xE2, 0xD3, 0x44, 0x75, 0x26, 0x17, +0xFC, 0xCD, 0x9E, 0xAF, 0x38, 0x09, 0x5A, 0x6B, +0x45, 0x74, 0x27, 0x16, 0x81, 0xB0, 0xE3, 0xD2, +0xBF, 0x8E, 0xDD, 0xEC, 0x7B, 0x4A, 0x19, 0x28, +0x06, 0x37, 0x64, 0x55, 0xC2, 0xF3, 0xA0, 0x91, +0x47, 0x76, 0x25, 0x14, 0x83, 0xB2, 0xE1, 0xD0, +0xFE, 0xCF, 0x9C, 0xAD, 0x3A, 0x0B, 0x58, 0x69, +0x04, 0x35, 0x66, 0x57, 0xC0, 0xF1, 0xA2, 0x93, +0xBD, 0x8C, 0xDF, 0xEE, 0x79, 0x48, 0x1B, 0x2A, +0xC1, 0xF0, 0xA3, 0x92, 0x05, 0x34, 0x67, 0x56, +0x78, 0x49, 0x1A, 0x2B, 0xBC, 0x8D, 0xDE, 0xEF, +0x82, 0xB3, 0xE0, 0xD1, 0x46, 0x77, 0x24, 0x15, +0x3B, 0x0A, 0x59, 0x68, 0xFF, 0xCE, 0x9D, 0xAC, + + +Generating CRC table for crc16-8005-lsb + +0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, +0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, +0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, +0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, +0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, +0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, +0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, +0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, +0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, +0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, +0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, +0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, +0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, +0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, +0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, +0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, +0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, +0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, +0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, +0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, +0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, +0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, +0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, +0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, +0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, +0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, +0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, +0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, +0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, +0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, +0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, +0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040, + + +Generating CRC table for crc16-8005-msb + +0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011, +0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022, +0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072, +0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041, +0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2, +0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1, +0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1, +0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082, +0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192, +0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1, +0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1, +0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2, +0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151, +0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162, +0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132, +0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101, +0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312, +0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321, +0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371, +0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342, +0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1, +0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2, +0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2, +0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381, +0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291, +0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2, +0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2, +0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1, +0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252, +0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261, +0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231, +0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202, + + +Generating CRC table for crc16-1021-lsb + +0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF, +0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7, +0x1081, 0x0108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E, +0x9CC9, 0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876, +0x2102, 0x308B, 0x0210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD, +0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5, +0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, 0x54B5, 0x453C, +0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974, +0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB, +0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3, +0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A, +0xDECD, 0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72, +0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9, +0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1, +0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x0738, +0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70, +0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7, +0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF, +0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036, +0x18C1, 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E, +0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5, +0x2942, 0x38CB, 0x0A50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD, +0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134, +0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C, +0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3, +0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, 0x3EFB, +0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232, +0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A, +0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1, +0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9, +0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330, +0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0x0F78, + + +Generating CRC table for crc16-1021-msb + +0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, +0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, +0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, +0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, +0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, +0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, +0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, +0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, +0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, +0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, +0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, +0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, +0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, +0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, +0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, +0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, +0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, +0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, +0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, +0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, +0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, +0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, +0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, +0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, +0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, +0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, +0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, +0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, +0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, +0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, +0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, +0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0, + + + +#define CRC_BIT_NUM 8u/16u/32u/64u +#define BITS_IN_BYTE 8u +uint8_t dataBuff[ARRAY_SIZE] = { 0x32, 0x33, ...; +uint8_t/uint16_t/uint32_t crc_val = 0x0; +// LSB +for(int i = 0; i < sizeof(dataBuff); i++) +{ + crc_val = (crc_val >> BITS_IN_BYTE) ^ crc_lsb_lut[(uint8)(crc_val^dataBuff[i])]; +} +// MSB +for(int i = 0; i < sizeof(dataBuff); i++) +{ + crc_val = (crc_val << BITS_IN_BYTE) ^ crc_msb_lut[(uint8)((crc_val>>8)^dataBuff[i])]; +} \ No newline at end of file diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/util/util.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/util/util.c new file mode 100644 index 0000000..87c8961 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/util/util.c @@ -0,0 +1,403 @@ +/*****************************************************************************/ +/** + * @file util.c + * @author FanHao (hao_fan_bit_ee@163.com) + * @brief Utility functions implementation file + * @version 0.0 + * @date 2023-05-06 + * + * @copyright Copyright(c) 2024 Beijing Institute of Technology.\n + * Lab of Communication and Network + * + ******************************************************************************/ + +/***************************** Include Files **********************************/ + +/** + * @addtogroup Utility + * @{ + */ + +#include +#include +// #include +#include "util.h" +#include "error.h" + +/************************** Functions Implementation **************************/ +#ifdef __BITSWAP_TABLE +static uint8_t byte_flipLR[256] = +{ + 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, + 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, + 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, + 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, + 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, + 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, + 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, + 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, + 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, + 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, + 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, + 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, + 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, + 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, + 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, + 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, + 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, + 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, + 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, + 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, + 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, + 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, + 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, + 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, + 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, + 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, + 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF +}; +#endif + +/** + * @brief Write a 32bit word to specific memory address + */ +inline void IO_Out32(uintptr_t addr, uint32_t value) +{ + volatile uint32_t *localAddr = (volatile uint32_t *)addr; + *localAddr = value; +} + +/** + * @brief Read a 32bit word from specific memory address + */ +inline uint32_t IO_In32(uintptr_t addr) +{ + return *(volatile uint32_t *)addr; +} + +/** + * @brief Swap a byte word bit-wise + */ +uint8_t byte_bit_swap(uint8_t byte) +{ +#ifdef __BITSWAP_TABLE + return byte_flipLR[byte]; +#else + return bit_swap_constant_8(byte); +#endif +} + +/** + * @brief Swap 32bit word bit-wise + */ +uint32_t word_bit_swap(uint32_t word) +{ +#ifdef __BITSWAP_TABLE + uint32_t rv_word = (byte_flipLR[word & 0xff] << 24) | + (byte_flipLR[(word >> 8) & 0xff] << 16) | + (byte_flipLR[(word >> 16) & 0xff] << 8) | + (byte_flipLR[(word >> 24) & 0xff]); + return rv_word; +#else + word = (((word & 0xaaaaaaaa) >> 1) | ((word & 0x55555555) << 1)); + word = (((word & 0xcccccccc) >> 2) | ((word & 0x33333333) << 2)); + word = (((word & 0xf0f0f0f0) >> 4) | ((word & 0x0f0f0f0f) << 4)); + word = (((word & 0xff00ff00) >> 8) | ((word & 0x00ff00ff) << 8)); + return ((word >> 16) | (word << 16)); +#endif +} + +/** + * @brief Count ones in an 32bit word + */ +uint32_t count_ones(uint32_t word) +{ +#ifdef __GNUC__ + return __builtin_popcount(word); +#else + uint32_t one_count = 0; + while (word) + { + if (word & 0x00000001) + one_count++; + word >>= 1; + } + return one_count; +#endif +} + +/** + * @brief Convert fix ;oint number to float number + */ +double fix2double(int32_t val, uint8_t frac_bits) +{ + return (((double)val) / (BIT(frac_bits))); +} + +/** + * @brief Find first set bit in word. + */ +uint32_t find_first_set_bit(uint32_t word) +{ +#ifdef __GNUC__ + if (word != 0) + { + return __builtin_ctz(word); + } + else + { + return 32; + } +#else + uint32_t first_set_bit = 0; + + while (word) + { + if (word & 0x1) + return first_set_bit; + word >>= 1; + first_set_bit++; + } + + return 32; +#endif +} + +/** + * @brief Find last set bit in word. + */ +uint32_t find_last_set_bit(uint32_t word) +{ +#ifdef __GNUC__ + if (word != 0) + { + return (31 - __builtin_clz(word)); + } + else + { + return 32; + } +#else + uint32_t bit = 0; + uint32_t last_set_bit = 32; + + while (word) + { + if (word & 0x1) + last_set_bit = bit; + word >>= 1; + bit++; + } + + return last_set_bit; +#endif +} + +/** + * @brief Locate the closest element in an array. + */ +uint32_t find_closest(int32_t val, + const int32_t *array, + uint32_t size) +{ + int32_t diff = abs(array[0] - val); + uint32_t ret = 0; + uint32_t i; + + for (i = 1; i < size; i++) + { + if (abs(array[i] - val) < diff) + { + diff = abs(array[i] - val); + ret = i; + } + } + + return ret; +} + +/** + * @brief Shift the value and apply the specified mask. + */ +uint32_t field_prep(uint32_t mask, uint32_t val) +{ + return (val << find_first_set_bit(mask)) & mask; +} + +/** + * @brief Get a field specified by a mask from a word. + */ +uint32_t field_get(uint32_t mask, uint32_t word) +{ + return (word & mask) >> find_first_set_bit(mask); +} + +/** + * @brief Log base 2 of the given number. + */ +int32_t log_base_2(uint32_t x) +{ + return find_last_set_bit(x); +} + +/** + * @brief Find greatest common divisor of the given two numbers. + */ +uint32_t greatest_common_divisor(uint32_t a, + uint32_t b) +{ + uint32_t div; + uint32_t common_div = 1; + + if ((a == 0) || (b == 0)) + return max(a, b); + + for (div = 1; (div <= a) && (div <= b); div++) + if (!(a % div) && !(b % div)) + common_div = div; + + return common_div; +} + +/** + * @brief Calculate best rational approximation for a given fraction. + */ +void rational_best_approximation(uint32_t given_numerator, + uint32_t given_denominator, + uint32_t max_numerator, + uint32_t max_denominator, + uint32_t *best_numerator, + uint32_t *best_denominator) +{ + uint32_t gcd; + + gcd = greatest_common_divisor(given_numerator, given_denominator); + + *best_numerator = given_numerator / gcd; + *best_denominator = given_denominator / gcd; + + if ((*best_numerator > max_numerator) || + (*best_denominator > max_denominator)) + { + *best_numerator = 0; + *best_denominator = 0; + } +} + +/** + * @brief Calculate the number of set bits. + */ +uint32_t hweight8(uint32_t word) +{ + uint32_t count = 0; + + while (word) + { + if (word & 0x1) + count++; + word >>= 1; + } + + return count; +} + +/** + * @brief Calculate the quotient and the remainder of an integer division. + */ +uint64_t do_div(uint64_t *n, + uint64_t base) +{ + uint64_t mod = 0; + + mod = *n % base; + *n = *n / base; + + return mod; +} + +/** + * @brief Unsigned 64bit divide with 64bit divisor and remainder + */ +uint64_t div64_u64_rem(uint64_t dividend, uint64_t divisor, uint64_t *remainder) +{ + *remainder = dividend % divisor; + + return dividend / divisor; +} + +/** + * @brief Unsigned 64bit divide with 32bit divisor with remainder + */ +uint64_t div_u64_rem(uint64_t dividend, uint32_t divisor, uint32_t *remainder) +{ + *remainder = do_div(÷nd, divisor); + + return dividend; +} + +/** + * @brief Signed 64bit divide with 32bit divisor with remainder + */ +int64_t div_s64_rem(int64_t dividend, int32_t divisor, int32_t *remainder) +{ + *remainder = dividend % divisor; + return dividend / divisor; +} + +/** + * @brief Unsigned 64bit divide with 32bit divisor + */ +uint64_t div_u64(uint64_t dividend, uint32_t divisor) +{ + uint32_t remainder; + + return div_u64_rem(dividend, divisor, &remainder); +} + +/** + * @brief Signed 64bit divide with 32bit divisor + */ +int64_t div_s64(int64_t dividend, int32_t divisor) +{ + int32_t remainder; + return div_s64_rem(dividend, divisor, &remainder); +} + +/** + * @brief Converts from string to int32_t + * @param *str + * @return int32_t + */ +int32_t str_to_int32(const char *str) +{ + char *end; + int32_t value = strtol(str, &end, 0); + + if (end == str) + return FAILURE; + else + return value; +} + +/** + * @brief Converts from string to uint32_t + * @param *str + * @return uint32_t + */ +uint32_t srt_to_uint32(const char *str) +{ + char *end; + uint32_t value = strtoul(str, &end, 0); + + if (end == str) + return FAILURE; + else + return value; +} + +/** @} */ \ No newline at end of file diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/xilinx_io.c b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/xilinx_io.c new file mode 100644 index 0000000..da948cd --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/xilinx_io.c @@ -0,0 +1,160 @@ + +#include "xilinx_io.h" + +static XSpiPs SpiInstance; +static XGpioPs Gpio; /* The driver instance for GPIO Device. */ +static int spi_select_value=0; + +int SpiPsInit(u16 SpiDeviceId){ + int Status; + XSpiPs_Config *SpiConfig; + /* + * Initialize the SPI driver so that it's ready to use + */ + SpiConfig = XSpiPs_LookupConfig(SpiDeviceId); + if (NULL == SpiConfig) { + return XST_FAILURE; + } + Status = XSpiPs_CfgInitialize((&SpiInstance), SpiConfig, + SpiConfig->BaseAddress); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + + /* + * Perform a self-test to check hardware build + */ + Status = XSpiPs_SelfTest((&SpiInstance)); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + /* + * The SPI device is a slave by default and the clock phase + * have to be set according to its master. In this example, CPOL is set + * to quiescent high and CPHA is set to 1. + */ + + XSpiPs_SetOptions((&SpiInstance), (XSPIPS_CR_CPHA_MASK) | + XSPIPS_MASTER_OPTION | + XSPIPS_FORCE_SSELECT_OPTION); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + XSpiPs_SetClkPrescaler((&SpiInstance), XSPIPS_CLK_PRESCALE_8); + /* + * Set the Rx FIFO Threshold to the Max Data + */ +// XSpiPs_SetRXWatermark((&SpiInstance),MAX_DATA); + /* + * Enable the device. + */ + XSpiPs_Enable((&SpiInstance)); + +return XST_SUCCESS; +} + +int cx9261a_write_cx(int addr , int value) +{ + Xil_Out32(AXI4_LITE1_SPI_BASEADDR+addr*4,value); + return 1; +} + +int cx9261a_read_cx(int addr) +{ + return Xil_In32(AXI4_LITE1_SPI_BASEADDR+addr*4); +} + +//int SpiPs_write_read(u8 cmd, u16 addr,u8 data,u8 * rx_data){ +int SpiPs_write_read(u8 * data,u8 * rx_data){ + int Status; + u8 rx_buffer[3]; + + if(spi_select_value == 0){ + rx_buffer[0]= *data; + rx_buffer[1]= *(data+1); + rx_buffer[2]= *(data+2); + if(rx_buffer[0] >= 128){ + cx9261a_write_cx((rx_buffer[0]-128)*256+rx_buffer[1],rx_buffer[2]); + }else{ + *rx_data=cx9261a_read_cx(rx_buffer[0]*256+rx_buffer[1]); + } + }else{ + + XSpiPs_SetSlaveSelect((&SpiInstance),0x00); + + Status = XSpiPs_PolledTransfer((&SpiInstance),data,rx_buffer,3); + + *rx_data = rx_buffer[2]; + + } + +return 0; +} + +int SpiPs_write_read1(u16 addr,u8 data,u8 * rx_data){ + u8 rx_data1; + u16 temp; + u8 rx_buffer[3]; + + temp= addr&0x00ff; + rx_buffer[0]= temp/256+128; + rx_buffer[1]= addr&0x00ff; + rx_buffer[2]= data; + + if(spi_select_value == 0){ + cx9261a_write_cx(addr,data); + }else{ + + SpiPs_write_read((u8 *)&rx_buffer,&rx_data1); + *rx_data = rx_data1; + } + +return 0; +} +int SpiPs_write_read2(u16 addr,u8 data,u8 * rx_data){ + u8 rx_data1; + u16 temp; + u8 rx_buffer[3]; + + temp= addr&0x00ff; + rx_buffer[0]= temp/256; + rx_buffer[1]= addr&0x00ff; + rx_buffer[2]= data; + + if(spi_select_value == 0){ + *rx_data=cx9261a_read_cx(addr); + }else{ + SpiPs_write_read((u8 *)&rx_buffer,&rx_data1); + *rx_data = rx_data1; + } + +return 0; +} +int GpioInit(u16 DeviceId) +{ + int Status; + XGpioPs_Config *ConfigPtr; + /* Initialize the GPIO driver. */ + ConfigPtr = XGpioPs_LookupConfig(DeviceId); + Status = XGpioPs_CfgInitialize(&Gpio, ConfigPtr, + ConfigPtr->BaseAddr); + if (Status != XST_SUCCESS) { + return XST_FAILURE; + } + return XST_SUCCESS; +} + +int GpioSetPinDir(u32 Pin,u32 Direction){ + XGpioPs_SetDirectionPin((&Gpio),GPIO_EMIO_OFFSET + Pin,Direction); + return 0; +} +int GpioWritePin(u32 Pin,u32 data){ + XGpioPs_SetOutputEnablePin(&Gpio, GPIO_EMIO_OFFSET + Pin, 1); + XGpioPs_WritePin((&Gpio),GPIO_EMIO_OFFSET + Pin,data); + return 0; +} + +int GpioReadPin(u32 Pin){ + return XGpioPs_ReadPin((&Gpio),GPIO_EMIO_OFFSET + Pin); +} + diff --git a/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/xilinx_io.h b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/xilinx_io.h new file mode 100644 index 0000000..91a84d8 --- /dev/null +++ b/cx9261a_demo_cmos_20240513/vitis_classic_autobuild/source_cx9261a_api_lib_c/xilinx_io.h @@ -0,0 +1,92 @@ +/* + * xilinx_io.h + * + * Created on: 2021Äê9ÔÂ6ÈÕ + * Author: cxlab + */ + +#ifndef SRC_XILINX_IO_H_ +#define SRC_XILINX_IO_H_ + + +#include "xspips.h" /* SPI device driver */ +#include "xgpiops.h" +#include "xparameters.h" + +//#define DBGLOG +#define MAX_DATA 100 +#define SPI_WRITE 0x80 +#define SPI_READ 0x00 +#define GPIO_DIR_INPUT 0x00 +#define GPIO_DIR_OUTPUT 0x01 +#define GPIO_EMIO_OFFSET 54 + +#define TX_EN 0x08 +#define RX_EN 0x07 +#define GPIO_1 0x06 +#define GPIO_0 0x05 + +#define NRST 0 +#define ENABLE_TRX1 1 +#define ENABLE_TRX2 2 +#define ENABLE_TRX3 3 +#define TXNRX_TRX1 4 +#define TXNRX_TRX2 5 +#define TXNRX_RX3 6 + +#define aresetn_0 33 +#define rst 34 +#define tr_sel 35 +#define tdd_fdd_sel 36 +#define r1t1_r2t2_sel 37 +#define sp_dp_sel 38 +#define aresetn_1 39 +#define aresetn_2 40 +#define hop 41 +#define cx3e04_rstn 42 +#define spi_select 46 +//#define debug_mode 63 + +#define INTER_SIG3 0x03//dma_axis_rx_rstn +#define INTER_SIG2 0x02//frame_pattern +#define INTER_SIG1 0x01//mode_sel_async +#define INTER_SIG0 0x00//rst_async + +#define SPI_DEVICE_ID XPAR_XQSPIPS_0_DEVICE_ID +#define GPIO_DEVICE_ID XPAR_PS7_GPIO_0_DEVICE_ID + +#define AXI_DMA_DEVCIE_ID0 XPAR_AXI_DMA_0_DEVICE_ID +#define AXI_DMA_DEVCIE_ID1 XPAR_AXI_DMA_1_DEVICE_ID +#define AXI_DMA_DEVCIE_ID2 XPAR_AXI_DMA_2_DEVICE_ID + +#define BROM_BASEADDR XPAR_M08_AXI_0_BASEADDR + +#define AXI4_LITE0_SPI_BASEADDR XPAR_AXI4_LITE_SPI_0_BASEADDR +#define AXI4_LITE1_SPI_BASEADDR XPAR_AXI4_LITE_SPI_1_BASEADDR +#define BRAM_BASEADDR XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR +#define BRAM_BASEADDR_1 XPAR_AXI_BRAM_CTRL_1_S_AXI_BASEADDR +#define PACKER_BASEADDR0 XPAR_AXIS_ADC_DATA_PACKER_0_S00_AXI_BASEADDR +#define PACKER_BASEADDR1 XPAR_AXIS_ADC_DATA_PACKER_1_S00_AXI_BASEADDR +#define PACKER_BASEADDR2 XPAR_AXIS_ADC_DATA_PACKER_2_S00_AXI_BASEADDR +#define WAVELENGTH 8192 +#define TX_BUFFER_BASE XPAR_MIG_7SERIES_0_BASEADDR +0x2000 +#define WAVE_LENGTH 8192*4 + +#define GPIO_INT_ID XPAR_FABRIC_GPIO_0_VEC_ID //PL ¶Ë AXI GPIO ÖжÏ ID +#define AXI_GPIO_ID XPAR_AXI_GPIO_0_DEVICE_ID //PL ¶Ë AXI GPIO Æ÷¼þ ID +#define SCUGIC_ID XPAR_SCUGIC_0_DEVICE_ID //ÖжϿØÖÆÆ÷ ID + +#define KEY_MASK XGPIO_IR_CH1_MASK //ͨµÀ 1 µÄ붨Òå + + +int SpiPsInit(u16 SpiDeviceId); +//int SpiPs_write_read(u8 cmd, u16 addr,u8 data,u8 * rx_data); +int SpiPs_write_read(u8 * data,u8 * rx_data); +int SpiPs_write_read1(u16 addr,u8 data,u8 * rx_data); +int SpiPs_write_read2(u16 addr,u8 data,u8 * rx_data); +int GpioInit(u16 DeviceId); +int GpioSetPinDir(u32 Pin,u32 Direction); +int GpioWritePin(u32 Pin,u32 data); +int GpioReadPin(u32 Pin); + +#endif