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); diff --git a/drivers/dac/ad3552r/ad3552r.c b/drivers/dac/ad3552r/ad3552r.c index 7b3826e06da..81cf057f83f 100644 --- a/drivers/dac/ad3552r/ad3552r.c +++ b/drivers/dac/ad3552r/ad3552r.c @@ -1389,6 +1389,31 @@ 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; + + /* + * 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); @@ -1417,6 +1442,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 +1576,171 @@ 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; + + /* 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, + 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; + + /* 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; +} + /** * @brief Write data samples to dac * @param desc - The device structure. @@ -1551,7 +1753,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 +1765,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 +1785,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; 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, 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