Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wip/bl/ad35xxr qspi axi #2407

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions drivers/axi_core/axi_dac_core/axi_dac_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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.
Expand Down
11 changes: 10 additions & 1 deletion drivers/axi_core/axi_dac_core/axi_dac_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand Down
241 changes: 207 additions & 34 deletions drivers/dac/ad3552r/ad3552r.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;

spectrum70 marked this conversation as resolved.
Show resolved Hide resolved
/* 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.
Expand All @@ -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,
Expand All @@ -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) {
Expand All @@ -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;
}
Expand Down
7 changes: 7 additions & 0 deletions drivers/dac/ad3552r/ad3552r.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions projects/ad3552r_fmcz/src/common/common_data.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion projects/ad3552r_fmcz/src/platform/xilinx/parameters.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Loading