From 0792eac2172bc73dd7ba64721e4c5f7bbe462b21 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Fri, 28 Jan 2022 13:35:47 +0100 Subject: [PATCH] iio: adc: cf_axi_adc_core: Support for EXT SYNC This patch adds a new device attributes 'sync_start_enable' and 'sync_start_enable_available' reading the later returns the available modes which depend on HDL core synthesis parameters. The options are explained below. Reading 'sync_start_enable' returns either 'arm' while waiting for the external synchronization signal or 'disarm' otherwise. - arm: Setting this bit will arm the trigger mechanism sensitive to an external sync signal. Once the external sync signal goes high it synchronizes channels within a DAC, and across multiple instances. This bit has an effect only the EXT_SYNC synthesis parameter is set. This bit self clears. - disarm: Setting this bit will disarm the trigger mechanism sensitive to an external sync signal. This bit has an effect only the EXT_SYNC synthesis parameter is set. This bit self clears. - trigger_manual: Setting this bit will issue an external sync event if it is hooked up inside the fabric. This bit has an effect only the EXT_SYNC synthesis parameter is set. This bit self clears. Signed-off-by: Michael Hennerich --- drivers/iio/adc/cf_axi_adc.h | 9 ++++ drivers/iio/adc/cf_axi_adc_core.c | 78 ++++++++++++++++++++++++++----- 2 files changed, 75 insertions(+), 12 deletions(-) diff --git a/drivers/iio/adc/cf_axi_adc.h b/drivers/iio/adc/cf_axi_adc.h index 41aa81323872f1..0e145a677f44c3 100644 --- a/drivers/iio/adc/cf_axi_adc.h +++ b/drivers/iio/adc/cf_axi_adc.h @@ -26,6 +26,7 @@ #define ADI_CMOS_OR_LVDS_N (1 << 7) #define ADI_PPS_RECEIVER_ENABLE (1 << 8) #define ADI_SCALECORRECTION_ONLY (1 << 9) +#define ADI_EXT_SYNC (1 << 12) #define ADI_REG_RSTN 0x0040 #define ADI_RSTN (1 << 0) @@ -37,6 +38,11 @@ #define ADI_DDR_EDGESEL (1 << 1) #define ADI_PIN_MODE (1 << 0) +#define ADI_REG_CNTRL_2 0x0048 +#define ADI_EXT_SYNC_ARM (1 << 1) +#define ADI_EXT_SYNC_DISARM (1 << 2) +#define ADI_MANUAL_SYNC_REQUEST (1 << 8) + #define ADI_REG_CLK_FREQ 0x0054 #define ADI_CLK_FREQ(x) (((x) & 0xFFFFFFFF) << 0) #define ADI_TO_CLK_FREQ(x) (((x) >> 0) & 0xFFFFFFFF) @@ -65,6 +71,9 @@ #define ADI_DELAY_RDATA(x) (((x) & 0x1F) << 0) #define ADI_TO_DELAY_RDATA(x) (((x) >> 0) & 0x1F) +#define ADI_REG_SYNC_STATUS 0x0068 +#define ADI_ADC_SYNC_STATUS (1 << 0) + #define ADI_REG_DRP_CNTRL 0x0070 #define ADI_DRP_SEL (1 << 29) #define ADI_DRP_RWN (1 << 28) diff --git a/drivers/iio/adc/cf_axi_adc_core.c b/drivers/iio/adc/cf_axi_adc_core.c index 30d036d1790c8e..7f743ee54b9a5e 100644 --- a/drivers/iio/adc/cf_axi_adc_core.c +++ b/drivers/iio/adc/cf_axi_adc_core.c @@ -60,6 +60,7 @@ struct axiadc_state { unsigned int have_slave_channels; bool additional_channel; bool dp_disable; + bool ext_sync_avail; struct iio_chan_spec channels[AXIADC_MAX_CHANNEL]; }; @@ -400,35 +401,85 @@ static ssize_t axiadc_sampling_frequency_available(struct device *dev, return ret; } -static ssize_t axiadc_sync_start(struct device *dev, +static const char * const axiadc_sync_ctrls[] = { + "arm", "disarm", "trigger_manual", +}; + +static ssize_t axiadc_sync_start_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct axiadc_state *st = iio_priv(indio_dev); - bool state; - u32 reg; int ret; - ret = strtobool(buf, &state); + ret = sysfs_match_string(axiadc_sync_ctrls, buf); if (ret < 0) return ret; - if (state) { - mutex_lock(&indio_dev->mlock); + mutex_lock(&indio_dev->mlock); + if (st->ext_sync_avail) { + switch (ret) { + case 0: + axiadc_write(st, ADI_REG_CNTRL_2, ADI_EXT_SYNC_ARM); + break; + case 1: + axiadc_write(st, ADI_REG_CNTRL_2, ADI_EXT_SYNC_DISARM); + break; + case 2: + axiadc_write(st, ADI_REG_CNTRL_2, ADI_MANUAL_SYNC_REQUEST); + break; + default: + ret = -EINVAL; + } + } else if (ret == 0) { + u32 reg; + reg = axiadc_read(st, ADI_REG_CNTRL); axiadc_write(st, ADI_REG_CNTRL, reg | ADI_SYNC); - mutex_unlock(&indio_dev->mlock); } + mutex_unlock(&indio_dev->mlock); - return len; + return ret < 0 ? ret : len; } -static IIO_DEVICE_ATTR(sync_start_enable, 0200, - NULL, - axiadc_sync_start, +static ssize_t axiadc_sync_start_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + struct axiadc_state *st = iio_priv(indio_dev); + u32 reg; + + switch ((u32)this_attr->address) { + case 0: + reg = axiadc_read(st, ADI_REG_SYNC_STATUS); + + return sprintf(buf, "%s\n", reg & ADI_ADC_SYNC_STATUS ? + axiadc_sync_ctrls[0] : axiadc_sync_ctrls[1]); + case 1: + if (st->ext_sync_avail) + return sprintf(buf, "arm disarm trigger_manual\n"); + else + return sprintf(buf, "arm\n"); + default: + return -EINVAL; + } + + return -EINVAL; +} + +static IIO_DEVICE_ATTR(sync_start_enable, 0644, + axiadc_sync_start_show, + axiadc_sync_start_store, 0); +static IIO_DEVICE_ATTR(sync_start_enable_available, 0444, + axiadc_sync_start_show, + NULL, + 1); + static IIO_DEVICE_ATTR(in_voltage_sampling_frequency_available, S_IRUGO, axiadc_sampling_frequency_available, NULL, @@ -436,6 +487,7 @@ static IIO_DEVICE_ATTR(in_voltage_sampling_frequency_available, S_IRUGO, static struct attribute *axiadc_attributes[] = { &iio_dev_attr_sync_start_enable.dev_attr.attr, + &iio_dev_attr_sync_start_enable_available.dev_attr.attr, &iio_dev_attr_in_voltage_sampling_frequency_available.dev_attr.attr, NULL, }; @@ -1020,7 +1072,7 @@ static int axiadc_probe(struct platform_device *pdev) struct resource *mem; struct axiadc_spidev axiadc_spidev; struct axiadc_converter *conv; - unsigned int skip = 1; + unsigned int config, skip = 1; int ret; dev_dbg(&pdev->dev, "Device Tree Probing \'%s\'\n", @@ -1076,6 +1128,8 @@ static int axiadc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, indio_dev); + config = axiadc_read(st, ADI_REG_CONFIG); + st->ext_sync_avail = !!(config & ADI_EXT_SYNC); st->dp_disable = false; /* FIXME: resolve later which reg & bit to read for this */ conv = to_converter(st->dev_spi);