/*
 * AD9361 Agile RF Transceiver
 *
 * Copyright 2013-2017 Analog Devices Inc.
 *
 * Licensed under the GPL-2.
 */

#include <linux/module.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/string.h>
#include <linux/uaccess.h>

#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>

#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>

#include "ad9361.h"

#if IS_ENABLED(CONFIG_CF_AXI_ADC)
#include "cf_axi_adc.h"

static void ad9361_set_intf_delay(struct ad9361_rf_phy *phy, bool tx,
				  unsigned int clock_delay,
				  unsigned int data_delay, bool clock_changed)
{
	if (clock_changed)
		ad9361_ensm_force_state(phy, ENSM_STATE_ALERT);
	ad9361_spi_write(phy->spi,
			REG_RX_CLOCK_DATA_DELAY + (tx ? 1 : 0),
			RX_DATA_DELAY(data_delay) |
			DATA_CLK_DELAY(clock_delay));
	if (clock_changed)
		ad9361_ensm_force_state(phy, ENSM_STATE_FDD);
}

static unsigned int ad9361_num_phy_chan(struct axiadc_converter *conv)
{
	if (conv->chip_info->num_channels > 4)
		return 4;
	return conv->chip_info->num_channels;
}

static int ad9361_check_pn(struct axiadc_converter *conv, bool tx,
			   unsigned int delay)
{
	struct axiadc_state *st = iio_priv(conv->indio_dev);
	unsigned int num_chan = ad9361_num_phy_chan(conv);
	unsigned int chan;

	for (chan = 0; chan < num_chan; chan++)
		axiadc_write(st, ADI_REG_CHAN_STATUS(chan),
			     ADI_PN_ERR | ADI_PN_OOS);
	mdelay(delay);

	if (!tx && !(axiadc_read(st, ADI_REG_STATUS) & ADI_STATUS))
		return 1;

	for (chan = 0; chan < num_chan; chan++) {
		if (axiadc_read(st, ADI_REG_CHAN_STATUS(chan)))
			return 1;
	}

	return 0;
}

ssize_t ad9361_dig_interface_timing_analysis(struct ad9361_rf_phy *phy,
						   char *buf, unsigned buflen)
{
	struct axiadc_converter *conv = spi_get_drvdata(phy->spi);
	struct ad9361_dig_tune_data data;
	int i, j, len = 0;
	int ret;
	u8 field[16][16];
	u8 rx;

	if (!conv)
		return -ENODEV;

	ret = ad9361_get_dig_tune_data(phy, &data);
	if (ret < 0)
		return ret;

	dev_dbg(&phy->spi->dev, "%s:\n", __func__);

	rx = ad9361_spi_read(phy->spi, REG_RX_CLOCK_DATA_DELAY);

	/* Mute TX, we don't want to transmit the PRBS */
	ad9361_tx_mute(phy, 1);

	ad9361_ensm_mode_disable_pinctrl(phy);

	ad9361_bist_loopback(phy, 0);
	ad9361_bist_prbs(phy, BIST_INJ_RX);

	for (i = 0; i < 16; i++) {
		for (j = 0; j < 16; j++) {
			ad9361_set_intf_delay(phy, false, i, j, j == 0);
			field[j][i] = ad9361_check_pn(conv, false, 1);
		}
	}

	ad9361_ensm_force_state(phy, ENSM_STATE_ALERT);
	ad9361_spi_write(phy->spi, REG_RX_CLOCK_DATA_DELAY, rx);
	ad9361_bist_loopback(phy, data.bist_loopback_mode);
	ad9361_write_bist_reg(phy, data.bist_config);

	ad9361_ensm_mode_restore_pinctrl(phy);
	ad9361_ensm_restore_state(phy, data.ensm_state);

	ad9361_tx_mute(phy, 0);

	len += snprintf(buf + len, buflen, "CLK: %lu Hz 'o' = PASS\n",
		       clk_get_rate(phy->clks[RX_SAMPL_CLK]));
	len += snprintf(buf + len, buflen, "DC");
	for (i = 0; i < 16; i++)
		len += snprintf(buf + len, buflen, "%x:", i);
	len += snprintf(buf + len, buflen, "\n");

	for (i = 0; i < 16; i++) {
		len += snprintf(buf + len, buflen, "%x:", i);
		for (j = 0; j < 16; j++) {
			len += snprintf(buf + len, buflen, "%c ",
					(field[i][j] ? '.' : 'o'));
		}
		len += snprintf(buf + len, buflen, "\n");
	}
	len += snprintf(buf + len, buflen, "\n");

	return len;
}
EXPORT_SYMBOL(ad9361_dig_interface_timing_analysis);

static ssize_t samples_pps_read(struct iio_dev *indio_dev,
				    uintptr_t private,
				    const struct iio_chan_spec *chan, char *buf)
{
	struct axiadc_converter *conv = iio_device_get_drvdata(indio_dev);
	struct axiadc_state *st = iio_priv(conv->indio_dev);
	u32 config, val, mode;

	config = axiadc_read(st, ADI_REG_CONFIG);

	if (!(config & ADI_PPS_RECEIVER_ENABLE))
		return -ENODEV;

	val = axiadc_read(st, ADI_REG_CLOCKS_PER_PPS_STATUS);
	if (val & ADI_CLOCKS_PER_PPS_STAT_INVAL)
		return -ETIMEDOUT;

	mode = axiadc_read(st, ADI_REG_CNTRL);

	/*
	 * Counts DATA_CLK cycles therefore needs to be corrected
	 * for 2rx2tx mode or for LVDS vs. CMOS mode.
	 */

	val = axiadc_read(st, ADI_REG_CLOCKS_PER_PPS);

	if (!(mode & ADI_R1_MODE))
		val /= 2;

	if (!(config & ADI_CMOS_OR_LVDS_N))
		val /= 2;

	return sprintf(buf, "%u\n", val);
}

/*
 * Returns the number of samples during a 1PPS (Pulse Per Second) interval.
 */

static struct iio_chan_spec_ext_info axiadc_ext_info[] = {
	{
		.name = "samples_pps",
		.read = samples_pps_read,
		.shared = IIO_SHARED_BY_TYPE,
	},
	{},
};

#define AIM_CHAN(_chan, _si, _bits, _sign)			\
	{ .type = IIO_VOLTAGE,						\
	  .indexed = 1,							\
	  .channel = _chan,						\
	  .info_mask_separate = BIT(IIO_CHAN_INFO_CALIBSCALE) |		\
			BIT(IIO_CHAN_INFO_CALIBBIAS) |			\
			BIT(IIO_CHAN_INFO_CALIBPHASE),			\
	  .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
	  .ext_info = axiadc_ext_info,					\
	  .scan_index = _si,						\
	  .scan_type = {						\
		.sign = _sign,						\
		.realbits = _bits,					\
		.storagebits = 16,					\
		.shift = 0,						\
	  },								\
	}

#define AIM_MC_CHAN(_chan, _si, _bits, _sign)			\
	{ .type = IIO_VOLTAGE,						\
	  .indexed = 1,							\
	  .channel = _chan,						\
	  .scan_index = _si,						\
	  .scan_type = {						\
		.sign = _sign,						\
		.realbits = _bits,					\
		.storagebits = 16,					\
		.shift = 0,						\
	  },								\
	}


static const unsigned long ad9361_2x2_available_scan_masks[] = {
	0x01, 0x02, 0x04, 0x08, 0x03, 0x0C, /* 1 & 2 chan */
	0x10, 0x20, 0x40, 0x80, 0x30, 0xC0, /* 1 & 2 chan */
	0x33, 0xCC, 0xC3, 0x3C, 0x0F, 0xF0, /* 4 chan */
	0xFF,				   /* 8 chan */
	0x00,
};

static const unsigned long ad9361_available_scan_masks[] = {
	0x01, 0x02, 0x04, 0x08, 0x03, 0x0C, 0x0F,
	0x00,
};

static const struct axiadc_chip_info axiadc_chip_info_tbl[] = {
	[ID_AD9361] = {
		.name = "AD9361",
		.max_rate = 61440000UL,
		.max_testmode = 0,
		.num_channels = 4,
		.scan_masks = ad9361_available_scan_masks,
		.channel[0] = AIM_CHAN(0, 0, 12, 'S'),
		.channel[1] = AIM_CHAN(1, 1, 12, 'S'),
		.channel[2] = AIM_CHAN(2, 2, 12, 'S'),
		.channel[3] = AIM_CHAN(3, 3, 12, 'S'),
	},
	[ID_AD9361_2] = { /* MCS/MIMO 2x AD9361 */
		.name = "AD9361-2",
		.max_rate = 61440000UL,
		.max_testmode = 0,
		.num_channels = 8,
		.num_shadow_slave_channels = 4,
		.scan_masks = ad9361_2x2_available_scan_masks,
		.channel[0] = AIM_CHAN(0, 0, 12, 'S'),
		.channel[1] = AIM_CHAN(1, 1, 12, 'S'),
		.channel[2] = AIM_CHAN(2, 2, 12, 'S'),
		.channel[3] = AIM_CHAN(3, 3, 12, 'S'),
		.channel[4] = AIM_MC_CHAN(4, 4, 12, 'S'),
		.channel[5] = AIM_MC_CHAN(5, 5, 12, 'S'),
		.channel[6] = AIM_MC_CHAN(6, 6, 12, 'S'),
		.channel[7] = AIM_MC_CHAN(7, 7, 12, 'S'),
	},
	[ID_AD9364] = {
		.name = "AD9364",
		.max_rate = 61440000UL,
		.max_testmode = 0,
		.num_channels = 2,
		.channel[0] = AIM_CHAN(0, 0, 12, 'S'),
		.channel[1] = AIM_CHAN(1, 1, 12, 'S'),
	},

};

static int ad9361_read_raw(struct iio_dev *indio_dev,
			   struct iio_chan_spec const *chan,
			   int *val,
			   int *val2,
			   long m)
{
	struct axiadc_converter *conv = iio_device_get_drvdata(indio_dev);

	switch (m) {
	case IIO_CHAN_INFO_SAMP_FREQ:
		if (!conv->clk)
			return -ENODEV;

		*val = conv->adc_clk = clk_get_rate(conv->clk);

		return IIO_VAL_INT;

	}
	return -EINVAL;
}

static int ad9361_write_raw(struct iio_dev *indio_dev,
			       struct iio_chan_spec const *chan,
			       int val,
			       int val2,
			       long mask)
{
	struct axiadc_converter *conv = iio_device_get_drvdata(indio_dev);
	unsigned long r_clk;
	int ret;

	switch (mask) {
	case IIO_CHAN_INFO_SAMP_FREQ:
		if (!conv->clk)
			return -ENODEV;

		if (chan->extend_name)
			return -ENODEV;

		r_clk = clk_round_rate(conv->clk, val);
		if (r_clk < 0 || r_clk > conv->chip_info->max_rate) {
			dev_warn(&conv->spi->dev,
				"Error setting ADC sample rate %ld", r_clk);
			return -EINVAL;
		}

		ret = clk_set_rate(conv->clk, r_clk);
		if (ret < 0)
			return ret;

		return 0;
		break;
	default:
		return -EINVAL;
	}

	return 0;
}

int ad9361_hdl_loopback(struct ad9361_rf_phy *phy, bool enable)
{
	struct axiadc_converter *conv = spi_get_drvdata(phy->spi);
	struct axiadc_state *st;
	unsigned reg, addr, chan, version;

	if (!conv)
		return -ENODEV;

	st = iio_priv(conv->indio_dev);
	version = axiadc_read(st, 0x4000);

	/* Still there but implemented a bit different */
	if (ADI_AXI_PCORE_VER_MAJOR(version) > 7)
		addr = 0x4418;
	else
		addr = 0x4414;

	for (chan = 0; chan < conv->chip_info->num_channels; chan++) {
		reg = axiadc_read(st, addr + (chan) * 0x40);

		if (ADI_AXI_PCORE_VER_MAJOR(version) > 7) {
			if (enable) {
				if (reg != 0x8) {
					conv->scratch_reg[chan] = reg;
					reg = 0x8;
				}
			} else if (reg == 0x8) {
				reg = conv->scratch_reg[chan];
			}
		} else {
		/* DAC_LB_ENB If set enables loopback of receive data */
			if (enable)
				reg |= BIT(1);
			else
				reg &= ~BIT(1);
		}
		axiadc_write(st, addr + (chan) * 0x40, reg);
	}

	return 0;
}
EXPORT_SYMBOL(ad9361_hdl_loopback);

static int ad9361_iodelay_set(struct axiadc_state *st, unsigned lane,
			      unsigned val, bool tx)
{
	if (tx) {
		if (ADI_AXI_PCORE_VER_MAJOR(st->pcore_version) > 8)
			axiadc_write(st, 0x4000 + ADI_REG_DELAY(lane), val);
		else
			return -ENODEV;
	} else {
		axiadc_idelay_set(st, lane, val);
	}

	return 0;
}

static int ad9361_midscale_iodelay(struct ad9361_rf_phy *phy, bool tx)
{
	struct axiadc_converter *conv = spi_get_drvdata(phy->spi);
	struct axiadc_state *st = iio_priv(conv->indio_dev);
	int ret = 0, i;

	for (i = 0; i < 7; i++)
		ret |= ad9361_iodelay_set(st, i, 15, tx);

	return 0;
}

static int ad9361_dig_tune_iodelay(struct ad9361_rf_phy *phy, bool tx)
{
	struct axiadc_converter *conv = spi_get_drvdata(phy->spi);
	struct axiadc_state *st = iio_priv(conv->indio_dev);
	int i, j;
	u32 s0, c0;
	u8 field[32];

	for (i = 0; i < 7; i++) {
		for (j = 0; j < 32; j++) {
			ad9361_iodelay_set(st, i, j, tx);
			mdelay(1);
			field[j] = ad9361_check_pn(conv, tx, 10);
		}

		c0 = ad9361_find_opt(&field[0], 32, &s0);
		ad9361_iodelay_set(st, i, s0 + c0 / 2, tx);

		dev_info(&phy->spi->dev,
			 "%s Lane %d, window cnt %d , start %d, IODELAY set to %d\n",
			 tx ? "TX" :"RX",  i , c0, s0, s0 + c0 / 2);

	}

	return 0;
}

static void ad9361_dig_tune_verbose_print(struct ad9361_rf_phy *phy,
					  u8 field[][16], bool tx,
					  int sel_clk, int sel_data)
{
	int i, j;
	char c;

	pr_info("SAMPL CLK: %lu tuning: %s\n",
	        clk_get_rate(phy->clks[RX_SAMPL_CLK]), tx ? "TX" : "RX");
	pr_info("  ");
	for (i = 0; i < 16; i++)
		pr_cont("%x:", i);
	pr_cont("\n");

	for (i = 0; i < 2; i++) {
		pr_info("%x:", i);
		for (j = 0; j < 16; j++) {
			if (field[i][j])
			    c = '#';
			else if ((i == 0 && j == sel_data) ||
				 (i == 1 && j == sel_clk))
			    c = 'O';
			else
			    c = 'o';
			pr_cont("%c ", c);
		}
		pr_cont("\n");
	}
}

static int ad9361_dig_tune_delay(struct ad9361_rf_phy *phy,
				 unsigned long max_freq,
				 enum dig_tune_flags flags, bool tx)
{
	// static const unsigned int rates[3] = {25000000U, 40000000U, 61440000U};
	static const unsigned int rates[3] = {25000000U, 40000000U, 40000000U};
	struct axiadc_converter *conv = spi_get_drvdata(phy->spi);
	unsigned int s0, s1, c0, c1;
	unsigned int i, j, r;
	bool half_data_rate;
	u8 field[2][16];

	if (ad9361_uses_lvds_mode(phy) || !ad9361_uses_rx2tx2(phy))
		half_data_rate = false;
	else
		half_data_rate = true;

	memset(field, 0, 32);
	for (r = 0; r < (max_freq ? ARRAY_SIZE(rates) : 1); r++) {
		if (max_freq)
			ad9361_set_trx_clock_chain_freq(phy,
				half_data_rate ? rates[r] / 2 : rates[r]);

		for (i = 0; i < 2; i++) {
			for (j = 0; j < 16; j++) {
				/*
				 * i == 0: clock delay = 0, data delay from 0 to 15
				 * i == 1: clock delay = 15, data delay from 15 to 0
				 */
				ad9361_set_intf_delay(phy, tx, i ? 15 : 0,
						      i ? 15 - j : j, j == 0);
				field[i][j] |= ad9361_check_pn(conv, tx, 4);
			}
		}

		if ((flags & BE_MOREVERBOSE) && max_freq) {
			ad9361_dig_tune_verbose_print(phy, field, tx, -1, -1);
		}
	}

	c0 = ad9361_find_opt(&field[0][0], 16, &s0);
	c1 = ad9361_find_opt(&field[1][0], 16, &s1);

	if (!c0 && !c1) {
		ad9361_dig_tune_verbose_print(phy, field, tx, -1, -1);
		dev_err(&phy->spi->dev, "%s: Tuning %s FAILED!", __func__,
			tx ? "TX" : "RX");
		return -EIO;
	} else if (flags & BE_VERBOSE) {
		ad9361_dig_tune_verbose_print(phy, field, tx,
					      c1 > c0 ? (s1 + c1 / 2) : -1,
					      c1 > c0 ? -1 : (s0 + c0 / 2));
	}

	if (c1 > c0)
		ad9361_set_intf_delay(phy, tx, s1 + c1 / 2, 0, true);
	else
		ad9361_set_intf_delay(phy, tx, 0, s0 + c0 / 2, true);

	return 0;
}

static int ad9361_dig_tune_rx(struct ad9361_rf_phy *phy, unsigned long max_freq,
			      enum dig_tune_flags flags)
{
	struct axiadc_converter *conv = spi_get_drvdata(phy->spi);
	struct axiadc_state *st = iio_priv(conv->indio_dev);
	int ret;

	ad9361_bist_loopback(phy, 0);
	ad9361_bist_prbs(phy, BIST_INJ_RX);

	ret = ad9361_dig_tune_delay(phy, max_freq, flags, false);
	if (flags & DO_IDELAY)
		ad9361_dig_tune_iodelay(phy, false);

	axiadc_write(st, ADI_REG_RSTN, ADI_MMCM_RSTN);
	axiadc_write(st, ADI_REG_RSTN, ADI_RSTN | ADI_MMCM_RSTN);

	return ret;
}

static int ad9361_dig_tune_tx(struct ad9361_rf_phy *phy, unsigned long max_freq,
			      enum dig_tune_flags flags)
{
	struct axiadc_converter *conv = spi_get_drvdata(phy->spi);
	struct axiadc_state *st = iio_priv(conv->indio_dev);
	u32 saved_dsel[4], saved_chan_ctrl6[4], saved_chan_ctrl0[4];
	unsigned int chan, num_chan;
	unsigned int hdl_dac_version;
	u32 tmp, saved = 0;
	int ret;

	num_chan = ad9361_num_phy_chan(conv);
	hdl_dac_version = axiadc_read(st, 0x4000);

	ad9361_bist_prbs(phy, BIST_DISABLE);
	ad9361_bist_loopback(phy, 1);
	axiadc_write(st, 0x4000 + ADI_REG_RSTN, ADI_RSTN | ADI_MMCM_RSTN);

	for (chan = 0; chan < num_chan; chan++) {
		saved_chan_ctrl0[chan] = axiadc_read(st, ADI_REG_CHAN_CNTRL(chan));
		axiadc_write(st, ADI_REG_CHAN_CNTRL(chan),
			ADI_FORMAT_SIGNEXT | ADI_FORMAT_ENABLE |
			ADI_ENABLE | ADI_IQCOR_ENB);
		axiadc_set_pnsel(st, chan, ADC_PN_CUSTOM);
		saved_chan_ctrl6[chan] = axiadc_read(st, 0x4414 + (chan) * 0x40);
		if (ADI_AXI_PCORE_VER_MAJOR(hdl_dac_version) > 7) {
			saved_dsel[chan] = axiadc_read(st, 0x4418 + (chan) * 0x40);
			axiadc_write(st, 0x4418 + (chan) * 0x40, 9);
			axiadc_write(st, 0x4414 + (chan) * 0x40, 0); /* !IQCOR_ENB */
			axiadc_write(st, 0x4044, 1);
		} else {
			axiadc_write(st, 0x4414 + (chan) * 0x40, 1); /* DAC_PN_ENB */
		}
	}
	if (ADI_AXI_PCORE_VER_MAJOR(hdl_dac_version) < 8) {
		saved = tmp = axiadc_read(st, 0x4048);
		tmp &= ~0xF;
		tmp |= 1;
		axiadc_write(st, 0x4048, tmp);
	}

	ret = ad9361_dig_tune_delay(phy, max_freq, flags, true);
	if (flags & DO_ODELAY)
		ad9361_dig_tune_iodelay(phy, true);

	if (ADI_AXI_PCORE_VER_MAJOR(hdl_dac_version) < 8)
		axiadc_write(st, 0x4048, saved);

	for (chan = 0; chan < num_chan; chan++) {
		axiadc_write(st, ADI_REG_CHAN_CNTRL(chan),
			     saved_chan_ctrl0[chan]);
		axiadc_set_pnsel(st, chan, ADC_PN9);
		if (ADI_AXI_PCORE_VER_MAJOR(hdl_dac_version) > 7) {
			axiadc_write(st, 0x4418 + chan * 0x40,
				     saved_dsel[chan]);
			axiadc_write(st, 0x4044, 1);
		}

		axiadc_write(st, 0x4414 + chan * 0x40, saved_chan_ctrl6[chan]);
	}

	return ret;
}

int ad9361_dig_tune(struct ad9361_rf_phy *phy, unsigned long max_freq,
		    enum dig_tune_flags flags)
{
	struct axiadc_converter *conv = spi_get_drvdata(phy->spi);
	struct ad9361_dig_tune_data data;
	struct axiadc_state *st;
	bool restore = false;
	int ret = 0;

	if (!conv)
		return -ENODEV;

	ret = ad9361_get_dig_tune_data(phy, &data);
	if (ret < 0)
		return ret;

	dev_dbg(&phy->spi->dev, "%s: freq %lu flags 0x%X\n", __func__,
		max_freq, flags);

	st = iio_priv(conv->indio_dev);

	if ((data.skip_mode == SKIP_ALL) ||
	    (flags & RESTORE_DEFAULT)) {
		/* skip completely and use defaults */
		restore = true;
	} else {
		/* Mute TX, we don't want to transmit the PRBS */
		ad9361_tx_mute(phy, 1);

		ad9361_ensm_mode_disable_pinctrl(phy);

		if (flags & DO_IDELAY)
			ad9361_midscale_iodelay(phy, false);

		if (flags & DO_ODELAY)
			ad9361_midscale_iodelay(phy, true);

		ret = ad9361_dig_tune_rx(phy, max_freq, flags);
		if (ret == 0 && (data.skip_mode == TUNE_RX_TX))
			ret = ad9361_dig_tune_tx(phy, max_freq, flags);

		ad9361_bist_loopback(phy, data.bist_loopback_mode);
		ad9361_write_bist_reg(phy, data.bist_config);

		if (ret == -EIO)
			restore = true;
		if (!max_freq)
			ret = 0;
	}

	if (restore) {
		ad9361_ensm_force_state(phy, ENSM_STATE_ALERT);
		ad9361_write_clock_data_delays(phy);
	} else if (!(flags & SKIP_STORE_RESULT)) {
		ad9361_read_clock_data_delays(phy);
	}

	ad9361_ensm_mode_restore_pinctrl(phy);
	ad9361_ensm_restore_state(phy, data.ensm_state);

	axiadc_write(st, ADI_REG_RSTN, ADI_MMCM_RSTN);
	axiadc_write(st, ADI_REG_RSTN, ADI_RSTN | ADI_MMCM_RSTN);

	ad9361_tx_mute(phy, 0);

	return ret;
}
EXPORT_SYMBOL(ad9361_dig_tune);

static int ad9361_post_setup(struct iio_dev *indio_dev)
{
	struct axiadc_state *st = iio_priv(indio_dev);
	struct axiadc_converter *conv = iio_device_get_drvdata(indio_dev);
	struct ad9361_rf_phy *phy = conv->phy;
	bool rx2tx2 = ad9361_uses_rx2tx2(phy);
	unsigned tmp, num_chan, flags;
	int i, ret;

	num_chan = ad9361_num_phy_chan(conv);

	conv->indio_dev = indio_dev;
	axiadc_write(st, ADI_REG_CNTRL, rx2tx2 ? 0 : ADI_R1_MODE);
	tmp = axiadc_read(st, 0x4048);

	if (!rx2tx2) {
		axiadc_write(st, 0x4048, tmp | BIT(5)); /* R1_MODE */
		axiadc_write(st, 0x404c,
			     ad9361_uses_lvds_mode(phy) ? 1 : 0); /* RATE */
	} else {
		tmp &= ~BIT(5);
		axiadc_write(st, 0x4048, tmp);
		axiadc_write(st, 0x404c,
			     ad9361_uses_lvds_mode(phy) ? 3 : 1); /* RATE */
	}

	for (i = 0; i < num_chan; i++) {
		axiadc_write(st, ADI_REG_CHAN_CNTRL_1(i),
			     ADI_DCFILT_OFFSET(0));
		axiadc_write(st, ADI_REG_CHAN_CNTRL_2(i),
			     (i & 1) ? 0x00004000 : 0x40000000);
		axiadc_write(st, ADI_REG_CHAN_CNTRL(i),
			     ADI_FORMAT_SIGNEXT | ADI_FORMAT_ENABLE |
			     ADI_ENABLE | ADI_IQCOR_ENB);
	}

	flags = 0;

	ret = ad9361_dig_tune(phy, (axiadc_read(st, ADI_AXI_REG_ID)) ?
		0 : 61440000, flags);
	if (ret < 0)
		goto error;

	if (flags & (DO_IDELAY | DO_ODELAY)) {
		ret = ad9361_dig_tune(phy, (axiadc_read(st, ADI_AXI_REG_ID)) ?
			0 : 61440000, flags & BE_VERBOSE);
		if (ret < 0)
			goto error;
	}

	ret = ad9361_set_trx_clock_chain_default(phy);

	ad9361_ensm_force_state(phy, ENSM_STATE_ALERT);
	ad9361_ensm_restore_prev_state(phy);

	return 0;

error:
	spi_set_drvdata(phy->spi, NULL);
	return ret;
}

int ad9361_register_axi_converter(struct ad9361_rf_phy *phy)
{
	struct axiadc_converter *conv;
	struct spi_device *spi = phy->spi;
	int ret;

	conv = devm_kzalloc(&spi->dev, sizeof(*conv), GFP_KERNEL);
	if (conv == NULL)
		return -ENOMEM;

	conv->id = ad9361_spi_read(spi, REG_PRODUCT_ID) & PRODUCT_ID_MASK;
	if (conv->id != PRODUCT_ID_9361) {
		dev_err(&spi->dev, "Unrecognized CHIP_ID 0x%X\n", conv->id);
  		ret = -ENODEV;
  		goto out;
	}

	conv->chip_info = &axiadc_chip_info_tbl[
		(spi_get_device_id(spi)->driver_data == ID_AD9361_2) ?
		ID_AD9361_2 : ad9361_uses_rx2tx2(phy) ? ID_AD9361 : ID_AD9364];
	conv->write_raw = ad9361_write_raw;
	conv->read_raw = ad9361_read_raw;
	conv->post_setup = ad9361_post_setup;
	conv->spi = spi;
	conv->phy = phy;

	conv->clk = phy->clks[RX_SAMPL_CLK];
	conv->adc_clk = clk_get_rate(conv->clk);

	spi_set_drvdata(spi, conv); /* Take care here */

	return 0;
out:
	spi_set_drvdata(spi, NULL);
	return ret;
}
EXPORT_SYMBOL(ad9361_register_axi_converter);

struct ad9361_rf_phy* ad9361_spi_to_phy(struct spi_device *spi)
{
	struct axiadc_converter *conv = spi_get_drvdata(spi);
	return conv->phy;
}
EXPORT_SYMBOL(ad9361_spi_to_phy);

#else  /* CONFIG_CF_AXI_ADC */

int ad9361_dig_tune(struct ad9361_rf_phy *phy, unsigned long max_freq,
			   enum dig_tune_flags flags)
{
	return -ENODEV;
}
EXPORT_SYMBOL(ad9361_dig_tune);

ssize_t ad9361_dig_interface_timing_analysis(struct ad9361_rf_phy *phy,
						   char *buf, unsigned buflen)
{
	return 0;
}
EXPORT_SYMBOL(ad9361_dig_interface_timing_analysis);

int ad9361_hdl_loopback(struct ad9361_rf_phy *phy, bool enable)
{
	return -ENODEV;
}
EXPORT_SYMBOL(ad9361_hdl_loopback);

int ad9361_register_axi_converter(struct ad9361_rf_phy *phy)
{
	struct spi_device *spi = phy->spi;
	spi_set_drvdata(spi, phy); /* Take care here */

	return 0;
}
EXPORT_SYMBOL(ad9361_register_axi_converter);

struct ad9361_rf_phy* ad9361_spi_to_phy(struct spi_device *spi)
{
	return spi_get_drvdata(spi);
}
EXPORT_SYMBOL(ad9361_spi_to_phy);

#endif /* CONFIG_CF_AXI_ADC */
