From 1c80533e896840071d6e5468302648e9815911c8 Mon Sep 17 00:00:00 2001 From: Angelo Dureghello Date: Fri, 20 Dec 2024 20:32:37 +0100 Subject: [PATCH 1/6] projects: ad3552r_fmcz: add support for ad354xr Add support for ad354xr using last updated HDL. Signed-off-by: Angelo Dureghello --- projects/ad3552r_fmcz/src/platform/xilinx/parameters.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/ad3552r_fmcz/src/platform/xilinx/parameters.h b/projects/ad3552r_fmcz/src/platform/xilinx/parameters.h index f5883f396ca..09071732a7e 100644 --- a/projects/ad3552r_fmcz/src/platform/xilinx/parameters.h +++ b/projects/ad3552r_fmcz/src/platform/xilinx/parameters.h @@ -90,7 +90,7 @@ #define UART_IRQ_ID XPAR_XUARTPS_1_INTR -#define TX_CORE_BASEADDR XPAR_AXI_AD3552R_DAC_BASEADDR +#define TX_CORE_BASEADDR XPAR_AXI_AD35XXR_DAC_BASEADDR #define TX_DMA_BASEADDR XPAR_AXI_DAC_DMA_BASEADDR #define TX_CLKGEN_BASEADDR XPAR_AXI_CLKGEN_BASEADDR From aa1d9027983943496582edd6b1976a7f290e0623 Mon Sep 17 00:00:00 2001 From: Angelo Dureghello Date: Fri, 20 Dec 2024 20:41:15 +0100 Subject: [PATCH 2/6] projects: ad3552r_fmcz: fix default drive strength Fix default drive strength. Signed-off-by: Angelo Dureghello --- projects/ad3552r_fmcz/src/common/common_data.c | 1 + 1 file changed, 1 insertion(+) diff --git a/projects/ad3552r_fmcz/src/common/common_data.c b/projects/ad3552r_fmcz/src/common/common_data.c index 385c7a0fb95..291b7aa2e37 100644 --- a/projects/ad3552r_fmcz/src/common/common_data.c +++ b/projects/ad3552r_fmcz/src/common/common_data.c @@ -120,6 +120,7 @@ struct ad3552r_init_param default_ad3552r_param = { }, .ldac_gpio_param_optional = &gpio_ldac_param, .reset_gpio_param_optional = &gpio_reset_param, + .sdo_drive_strength = 1, .channels = { [0] = { .en = 1, From 78b3fde9c7716bc63829fd16e67ed345fe79d490 Mon Sep 17 00:00:00 2001 From: Angelo Dureghello Date: Fri, 20 Dec 2024 20:22:34 +0100 Subject: [PATCH 3/6] drivers: axi_core: add IO mode setup Add support to change axi IO mode between simple SPI, DUAL or QUAD. Signed-off-by: Angelo Dureghello --- drivers/axi_core/axi_dac_core/axi_dac_core.c | 15 +++++++++++++++ drivers/axi_core/axi_dac_core/axi_dac_core.h | 11 ++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/axi_core/axi_dac_core/axi_dac_core.c b/drivers/axi_core/axi_dac_core/axi_dac_core.c index 481f7215c94..c5db4a9ce6b 100644 --- a/drivers/axi_core/axi_dac_core/axi_dac_core.c +++ b/drivers/axi_core/axi_dac_core/axi_dac_core.c @@ -86,6 +86,7 @@ #define AXI_DAC_ADDRESS(x) ((x << 24) & 0xff000000) #define AXI_DAC_STREAM NO_OS_BIT(1) #define AXI_DAC_TRANSFER_DATA NO_OS_BIT(0) +#define AXI_DAC_IO_MODE(x) ((x << 2) & 0x0000000c) #define AXI_DAC_STREAM_ENABLE (AXI_DAC_STREAM | \ AXI_DAC_TRANSFER_DATA) @@ -525,6 +526,20 @@ int32_t axi_dac_set_ddr(struct axi_dac *dac, bool enable) return 0; } +/** + * @brief AXI DAC Set IO mode + * @param dac - The device structure. + * @param mode - enum axi_io_mode. + * @return Returns 0 in case of success or negative error code otherwise. + */ +int32_t axi_dac_set_io_mode(struct axi_dac *dac, enum axi_io_mode mode) +{ + axi_dac_update_bits(dac, AXI_DAC_REG_CUSTOM_CTRL, + AXI_DAC_IO_MODE(0x03), AXI_DAC_IO_MODE(mode)); + + return 0; +} + /** * @brief AXI DAC Set data stream mode. * @param dac - The device structure. diff --git a/drivers/axi_core/axi_dac_core/axi_dac_core.h b/drivers/axi_core/axi_dac_core/axi_dac_core.h index 47c5c89b6f3..21970763fba 100644 --- a/drivers/axi_core/axi_dac_core/axi_dac_core.h +++ b/drivers/axi_core/axi_dac_core/axi_dac_core.h @@ -41,11 +41,17 @@ /******************************************************************************/ /*************************** Types Declarations *******************************/ /******************************************************************************/ -enum { +enum axi_iface { AXI_DAC_BUS_TYPE_NONE, AXI_DAC_BUS_TYPE_QSPI, }; +enum axi_io_mode { + AXI_DAC_IO_MODE_SPI, + AXI_DAC_IO_MODE_DSPI, + AXI_DAC_IO_MODE_QSPI, +}; + /** * @struct axi_dac * @brief AXI DAC Device Descriptor. @@ -194,6 +200,9 @@ int32_t axi_dac_bus_write(struct axi_dac *dac, /** AXI DAC Set DDR (bus double-data-rate) mode */ int32_t axi_dac_set_ddr(struct axi_dac *dac, bool enable); +/** AXI DAC set IO mode */ +int32_t axi_dac_set_io_mode(struct axi_dac *dac, + enum axi_io_mode mode); /** AXI DAC Set data stream mode */ int32_t axi_dac_set_data_stream(struct axi_dac *dac, bool enable); From 3c0f071d1bbf6d05dd8a74b4fe7af1d58a54fa1f Mon Sep 17 00:00:00 2001 From: Angelo Dureghello Date: Fri, 20 Dec 2024 20:27:49 +0100 Subject: [PATCH 4/6] drivers: ad3552r: add support for ad354xr in high speed mode Add high speed support for ad354xr using last updated HDL: commit a62f97280e52f923f7a3ec1bae4f799be8420a6d Author: caosjr Signed-off-by: Angelo Dureghello --- drivers/dac/ad3552r/ad3552r.c | 201 ++++++++++++++++++++++++++++------ drivers/dac/ad3552r/ad3552r.h | 7 ++ 2 files changed, 174 insertions(+), 34 deletions(-) diff --git a/drivers/dac/ad3552r/ad3552r.c b/drivers/dac/ad3552r/ad3552r.c index 7b3826e06da..cb9f7eb47ed 100644 --- a/drivers/dac/ad3552r/ad3552r.c +++ b/drivers/dac/ad3552r/ad3552r.c @@ -1417,6 +1417,18 @@ int32_t ad3552r_init(struct ad3552r_desc **desc, } ldesc->chip_id = param->chip_id; ldesc->is_simultaneous = param->is_simultaneous; + switch (ldesc->chip_id) { + case AD3541R_ID: + case AD3542R_ID: + ldesc->num_spi_data_lanes = 2; + break; + case AD3551R_ID: + case AD3552R_ID: + default: + ldesc->num_spi_data_lanes = 4; + break; + } + err = ad3552r_configure_device(ldesc, param); if (NO_OS_IS_ERR_VALUE(err)) { err = -ENODEV; @@ -1539,6 +1551,156 @@ int32_t ad3552r_set_asynchronous(struct ad3552r_desc *desc, uint8_t enable) return 0; } +static int ad3552r_hs_set_target_io_mode_hs(struct ad3552r_desc *desc) +{ + int mode_target, val; + + /* + * Best access for secondary reg area, QSPI where possible, + * else as DSPI. + */ + mode_target = (desc->num_spi_data_lanes == 4) ? + AD3552R_QUAD_SPI : AD3552R_DUAL_SPI; + + val = no_os_field_prep(AD3552R_MASK_MULTI_IO_MODE, mode_target); + + /* + * Better to not use update here, since generally we are already + * set as DDR mode, and it's not possible to read in DDR mode. + */ + return ad3552r_write_reg(desc, AD3552R_REG_ADDR_TRANSFER_REGISTER, + val | AD3552R_MASK_STREAM_LENGTH_KEEP_VALUE); +} + +static int ad3552r_hs_set_bus_io_mode_hs(struct ad3552r_desc *desc) +{ + int bus_mode; + + bus_mode = (desc->num_spi_data_lanes == 4) ? + AXI_DAC_IO_MODE_QSPI : AXI_DAC_IO_MODE_DSPI; + + return axi_dac_set_io_mode(desc->ad3552r_core_ip, bus_mode); +} + +/* + * NOTE: this sequence must be strictly repsected, since, axi side cannot read + * in ddr mode (_update can't be used), and can access primary region in + * SDR mode only. + */ +static int ad3552r_hs_buffer_preenable(struct ad3552r_desc *desc) +{ + int ret, loop_len; + + /* Need to keep loop len. */ + ret = _ad3552r_update_reg_field(desc, + AD3552R_REG_ADDR_TRANSFER_REGISTER, + AD3552R_MASK_STREAM_LENGTH_KEEP_VALUE, + 1); + if (ret) + return ret; + + loop_len = 4; + ret = ad3552r_write_reg(desc, AD3552R_REG_ADDR_STREAM_MODE, loop_len); + if (ret) + return ret; + + /* Set target, then axi bus into DDR mode. */ + ret = _ad3552r_update_reg_field(desc, + AD3552R_REG_ADDR_INTERFACE_CONFIG_D, + AD3552R_MASK_SPI_CONFIG_DDR, 1); + if (ret) + return ret; + + ret = axi_dac_set_ddr(desc->ad3552r_core_ip, true); + if (ret) + goto exit_err_ddr; + + /* Set high speed, DSPI or QSPI, depending on the model. */ + ret = ad3552r_hs_set_target_io_mode_hs(desc); + if (ret) + goto exit_err_ddr; + + ret = ad3552r_hs_set_bus_io_mode_hs(desc); + if (ret) + goto exit_err_io_mode; + + /* Set up now only rest of backend registers */ + ret = axi_dac_data_transfer_addr(desc->ad3552r_core_ip, + AD3552R_REG_ADDR_CH_DAC_16B(1)); + if (ret) + goto exit_err_io_mode; + + ret = axi_dac_data_format_set(desc->ad3552r_core_ip, 16); + if (ret) + goto exit_err_io_mode; + + ret = axi_dac_set_data_stream(desc->ad3552r_core_ip, true); + if (ret) + goto exit_err_io_mode; + + return 0; + + /* Unwrapping possible error cases. */ + +exit_err_io_mode: + /* Back to simple SPI */ + ad3552r_write_reg(desc, AD3552R_REG_ADDR_TRANSFER_REGISTER, + AD3552R_MASK_STREAM_LENGTH_KEEP_VALUE); + + axi_dac_set_io_mode(desc->ad3552r_core_ip, AXI_DAC_IO_MODE_SPI); + +exit_err_ddr: + /* Set target, then axi bus into DDR mode. */ + _ad3552r_update_reg_field(desc, AD3552R_REG_ADDR_INTERFACE_CONFIG_D, + AD3552R_MASK_SPI_CONFIG_DDR, 0); + + axi_dac_set_ddr(desc->ad3552r_core_ip, false); + + return ret; +} + +static int ad3552r_hs_buffer_postdisable(struct ad3552r_desc *desc) +{ + int ret; + + ret = axi_dac_set_data_stream(desc->ad3552r_core_ip, false); + if (ret) + return ret; + + /* + * Set us to simple SPI, even if still in ddr, so to be able + * to write in primary region. + */ + ret = axi_dac_set_io_mode(desc->ad3552r_core_ip, AXI_DAC_IO_MODE_SPI); + if (ret) + return ret; + + /* + * Back to SDR + * (in DDR we cannot read, whatever the mode is, so not using update). + */ + ret = ad3552r_write_reg(desc, AD3552R_REG_ADDR_INTERFACE_CONFIG_D, + no_os_field_prep( + AD3552R_MASK_SDO_DRIVE_STRENGTH, 1)); + + ret = axi_dac_set_ddr(desc->ad3552r_core_ip, false); + if (ret) + return ret; + + /* + * Back to simple SPI for secondary region too now, + * so to be able to dump/read registers there too if needed. + */ + ret = _ad3552r_update_reg_field(desc, + AD3552R_REG_ADDR_TRANSFER_REGISTER, + AD3552R_MASK_MULTI_IO_MODE, + AD3552R_SPI); + if (ret) + return ret; + + return 0; +} + /** * @brief Write data samples to dac * @param desc - The device structure. @@ -1551,7 +1713,7 @@ int32_t ad3552r_set_asynchronous(struct ad3552r_desc *desc, uint8_t enable) int32_t ad3552r_axi_write_data(struct ad3552r_desc *desc, uint32_t *buf, uint16_t samples, bool cyclic, int cyclic_secs) { - int ret, loop_len; + int ret; struct axi_dma_transfer write_transfer = { .size = samples * AD3552R_BYTES_PER_SAMPLE, .transfer_done = 0, @@ -1563,39 +1725,15 @@ int32_t ad3552r_axi_write_data(struct ad3552r_desc *desc, uint32_t *buf, if (!desc->axi) return -EINVAL; - ret = _ad3552r_update_reg_field(desc, AD3552R_REG_ADDR_INTERFACE_CONFIG_D, - AD3552R_MASK_SPI_CONFIG_DDR, - AD3552R_MASK_SPI_CONFIG_DDR); + ret = ad3552r_hs_buffer_preenable(desc); if (ret) return ret; - ret = axi_dac_set_ddr(desc->ad3552r_core_ip, true); - if (ret) - goto exit_err_ddr; - - loop_len = 4; - ret = ad3552r_write_reg(desc, AD3552R_REG_ADDR_STREAM_MODE, loop_len); - if (ret) - goto exit_err_ddr; - - ret = axi_dac_data_transfer_addr(desc->ad3552r_core_ip, - AD3552R_REG_ADDR_CH_DAC_16B(1)); - if (ret) - goto exit_err_ddr; - - ret = axi_dac_data_format_set(desc->ad3552r_core_ip, 16); - if (ret) - goto exit_err_ddr; - - ret = axi_dac_set_data_stream(desc->ad3552r_core_ip, true); - if (ret) - goto exit_err_ddr; - /* Need to wait for voltage stabilization */ ret = axi_dmac_transfer_start(desc->dmac_ip, &write_transfer); if (ret) { pr_err("axi_dmac_transfer_start() failed!\n"); - goto exit_err_ddr; + goto exit_err; } if (cyclic) { @@ -1607,13 +1745,8 @@ int32_t ad3552r_axi_write_data(struct ad3552r_desc *desc, uint32_t *buf, } else axi_dmac_transfer_wait_completion(desc->dmac_ip, 10000); - ret = axi_dac_set_data_stream(desc->ad3552r_core_ip, false); - -exit_err_ddr: - _ad3552r_update_reg_field(desc, AD3552R_REG_ADDR_INTERFACE_CONFIG_D, - AD3552R_MASK_SPI_CONFIG_DDR, 0); - - axi_dac_set_ddr(desc->ad3552r_core_ip, false); +exit_err: + ad3552r_hs_buffer_postdisable(desc); return ret; } diff --git a/drivers/dac/ad3552r/ad3552r.h b/drivers/dac/ad3552r/ad3552r.h index 6a2776ff36f..7bf65d57b89 100644 --- a/drivers/dac/ad3552r/ad3552r.h +++ b/drivers/dac/ad3552r/ad3552r.h @@ -179,6 +179,12 @@ enum ad3552r_id { AD3552R_ID }; +enum ad3552r_io_mode { + AD3552R_SPI, + AD3552R_DUAL_SPI, + AD3552R_QUAD_SPI, +}; + enum ad3552r_ch_vref_select { /* Internal source with Vref I/O floating */ AD3552R_INTERNAL_VREF_PIN_FLOATING, @@ -399,6 +405,7 @@ struct ad3552r_desc { uint8_t axi_xfer_size; uint8_t crc_table[NO_OS_CRC8_TABLE_SIZE]; uint8_t chip_id; + uint8_t num_spi_data_lanes; uint8_t crc_en : 1; uint8_t is_simultaneous : 1; uint8_t single_transfer : 1; From 639b12deb0ec8eac04b4b0309e4cfcc6c9ea0e41 Mon Sep 17 00:00:00 2001 From: Angelo Dureghello Date: Fri, 20 Dec 2024 20:49:35 +0100 Subject: [PATCH 5/6] drivers: ad3552r: add reset flag cleanup Cleanup reset error flag, so that the error status is clean and ready for new errors. Signed-off-by: Angelo Dureghello --- drivers/dac/ad3552r/ad3552r.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/dac/ad3552r/ad3552r.c b/drivers/dac/ad3552r/ad3552r.c index cb9f7eb47ed..3ac4bb013e5 100644 --- a/drivers/dac/ad3552r/ad3552r.c +++ b/drivers/dac/ad3552r/ad3552r.c @@ -1389,6 +1389,12 @@ int32_t ad3552r_init(struct ad3552r_desc **desc, } } + /* Clean reset flags. */ + err = ad3552r_write_reg(ldesc, AD3552R_REG_ADDR_ERR_STATUS, + AD3552R_MASK_RESET_STATUS); + if (err) + return err; + err = ad3552r_check_scratch_pad(ldesc); if (NO_OS_IS_ERR_VALUE(err)) { pr_err("Scratch pad test failed: %"PRIi32"\n", err); From b6c691374a48b82765e6cbe5471489a916be8752 Mon Sep 17 00:00:00 2001 From: Angelo Dureghello Date: Fri, 20 Dec 2024 20:53:24 +0100 Subject: [PATCH 6/6] drivers: ad3552r: set instruction mode as default mode Setting instruction mode for initial configuration operartions, as suggested as safer from HDL team. Streaming mode is actually enabled only for DMA data streaming. Signed-off-by: Angelo Dureghello --- drivers/dac/ad3552r/ad3552r.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/dac/ad3552r/ad3552r.c b/drivers/dac/ad3552r/ad3552r.c index 3ac4bb013e5..81cf057f83f 100644 --- a/drivers/dac/ad3552r/ad3552r.c +++ b/drivers/dac/ad3552r/ad3552r.c @@ -1395,6 +1395,25 @@ int32_t ad3552r_init(struct ad3552r_desc **desc, if (err) return err; + /* + * FPGA HDL keeps QSPI pin low for ad355xr, so for the whole family + * SPI "multi IO" mode is set appropriately for each working mode. + * + * Whatever is the SPI IO mode, reading in DDR is never possible. + * R/W as D/QSPI is also possible for the secondary region. + * + * When not streaming, so in configuration mode or raw sammple R/W, + * staying instruction mode, simple SPI SDR. + * + * When streaming, setting streamign mode and best high speed mode. + */ + err = _ad3552r_update_reg_field(ldesc, + AD3552R_REG_ADDR_INTERFACE_CONFIG_B, + AD3552R_MASK_SINGLE_INST, + AD3552R_MASK_SINGLE_INST); + if (err < 0) + return err; + err = ad3552r_check_scratch_pad(ldesc); if (NO_OS_IS_ERR_VALUE(err)) { pr_err("Scratch pad test failed: %"PRIi32"\n", err); @@ -1597,6 +1616,13 @@ static int ad3552r_hs_buffer_preenable(struct ad3552r_desc *desc) { int ret, loop_len; + /* Set target into streaming mode. */ + ret = _ad3552r_update_reg_field(desc, + AD3552R_REG_ADDR_INTERFACE_CONFIG_B, + AD3552R_MASK_SINGLE_INST, 0); + if (ret) + return ret; + /* Need to keep loop len. */ ret = _ad3552r_update_reg_field(desc, AD3552R_REG_ADDR_TRANSFER_REGISTER, @@ -1704,6 +1730,14 @@ static int ad3552r_hs_buffer_postdisable(struct ad3552r_desc *desc) if (ret) return ret; + /* Back to single instruction mode, disabling loop. */ + ret = _ad3552r_update_reg_field(desc, + AD3552R_REG_ADDR_INTERFACE_CONFIG_B, + AD3552R_MASK_SINGLE_INST, + AD3552R_MASK_SINGLE_INST); + if (ret) + return ret; + return 0; }