I'm interfacing with an Analog Devices AD9850 DDS IC via SPI on a Xilinx Zynq-7020 SoC running embedded Linux (Yocto). The SPI interface is via an AXI SPI IP core - this only supports up to 32 bits per transaction, whereas the AD9850 requires 40 bits, so I'm only using the SPI peripheral to generate the clock and data lines, and I want to use a GPIO line to manually control the CS line separately.
I've put together an IIO driver to control the SPI peripheral and the (optional) GPIO line. As it currently stands, data and clock (from the SPI peripheral) are working fine (viewed on a logic analyser), however the CS line (from the GPIO peripheral) isn't doing what I expect it to.
I think that this is most likely due to the GPIO peripheral not being mapped correctly to the IIO driver via the Linux device tree, as the other GPIO peripherals are working OK with standalone drivers. Could someone advise on how I should go about mapping the "fq_update" GPIO peripheral under the DDS entry, please?
Here is an extract from my device tree:
&amba_pl {
gpio-dds-cs {
compatible = "adi,fq_update";
dds_cs {
label = "dds_cs";
gpios = <&dds_chip_select_gpio 0 1>;
default-state = "off";
};
};
};
&dds_spi {
num-cs = <1>;
status = "okay";
dds: ad9850@0 {
label = "dds";
compatible = "ad9850";
reg = <0x0>;
spi-max-frequency = <1000000>;
};
};
Here is an extract from my IIO driver:
struct ad9850_state {
struct spi_device *spi;
struct gpio_desc *fq_update;
uint32_t cache[AD9850_NUM_CHANNELS];
struct mutex lock;
/* DMA (thus cache coherency maintenance) may require the transfer buffers to live in their own cache lines. */
u8 data[AD9850_DATA_SIZE_BYTES] ____cacheline_aligned;
};
static int ad9850_channel_write(struct ad9850_state *priv, int channel, u32 val) {
int ret;
priv->cache[channel] = val;
memset(priv->data, 0, AD9850_DATA_SIZE_BYTES);
memcpy(priv->data, (u8 *)&priv->cache[channel], sizeof(priv->cache[channel]));
gpiod_set_value(priv->fq_update, 1);
gpiod_set_value(priv->fq_update, 0);
ret = spi_write(priv->spi, &priv->data, AD9850_DATA_SIZE_BYTES);
gpiod_set_value(priv->fq_update, 1);
gpiod_set_value(priv->fq_update, 0);
if (ret < 0) {
return ret;
}
return 0;
}
static int ad9850_probe(struct spi_device *spi) {
*************
/* The FQ_UPDATE pin can be controlled by the driver or externally. */
priv->fq_update = devm_gpiod_get_optional(&spi->dev, "adi,fq_update", GPIOD_OUT_LOW);
if (IS_ERR(priv->fq_update)) {
return PTR_ERR(priv->fq_update);
}
*************
}
I seem to have managed to solve this, as follows.
I've removed the "gpio-dds-cs" entry under "&amba_pl", and instead referenced the GPIO directly from the DDS entry.
&dds_spi {
num-cs = <1>;
status = "okay";
dds: ad9850@0 {
label = "dds";
compatible = "ad9850";
reg = <0x0>;
spi-max-frequency = <1000000>;
fq_update-gpios = <&dds_chip_select_gpio 0 1>;
};
};
This meant updating the GPIO name in the IIO driver - I also changed this from being "optional", as it is required for operation of the device.
#define AD9850_GPIO_NAME "fq_update"
priv->fq_update = devm_gpiod_get(&spi->dev, AD9850_GPIO_NAME, GPIOD_OUT_LOW);
if (IS_ERR(priv->fq_update)) {
#if (PRINT_TO_KERNEL_LOG == true)
printk(KERN_INFO "GPIO setup for IIO device %s failed!\n", AD9850_DRV_NAME);
#endif
return PTR_ERR(priv->fq_update);
}