Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+117
View File
@@ -0,0 +1,117 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Frequency
# Direct Digital Synthesis drivers (DDS)
# Clock Distribution device drivers
# Phase-Locked Loop (PLL) frequency synthesizers
#
# When adding new entries keep the list in alphabetical order
menu "Frequency Synthesizers DDS/PLL"
menu "Clock Generator/Distribution"
config AD9523
tristate "Analog Devices AD9523 Low Jitter Clock Generator"
depends on SPI
help
Say yes here to build support for Analog Devices AD9523 Low Jitter
Clock Generator. The driver provides direct access via sysfs.
To compile this driver as a module, choose M here: the
module will be called ad9523.
endmenu
#
# Phase-Locked Loop (PLL) frequency synthesizers
#
menu "Phase-Locked Loop (PLL) frequency synthesizers"
config ADF4350
tristate "Analog Devices ADF4350/ADF4351 Wideband Synthesizers"
depends on SPI
help
Say yes here to build support for Analog Devices ADF4350/ADF4351
Wideband Synthesizers. The driver provides direct access via sysfs.
To compile this driver as a module, choose M here: the
module will be called adf4350.
config ADF4371
tristate "Analog Devices ADF4371/ADF4372 Wideband Synthesizers"
depends on SPI
select REGMAP_SPI
help
Say yes here to build support for Analog Devices ADF4371 and ADF4372
Wideband Synthesizers. The driver provides direct access via sysfs.
To compile this driver as a module, choose M here: the
module will be called adf4371.
config ADF4377
tristate "Analog Devices ADF4377 Microwave Wideband Synthesizer"
depends on SPI && COMMON_CLK
select REGMAP_SPI
help
Say yes here to build support for Analog Devices ADF4377 Microwave
Wideband Synthesizer.
To compile this driver as a module, choose M here: the
module will be called adf4377.
config ADMFM2000
tristate "Analog Devices ADMFM2000 Dual Microwave Down Converter"
depends on GPIOLIB
help
Say yes here to build support for Analog Devices ADMFM2000 Dual
Microwave Down Converter.
To compile this driver as a module, choose M here: the
module will be called admfm2000.
config ADMV1013
tristate "Analog Devices ADMV1013 Microwave Upconverter"
depends on SPI && COMMON_CLK
help
Say yes here to build support for Analog Devices ADMV1013
24 GHz to 44 GHz, Wideband, Microwave Upconverter.
To compile this driver as a module, choose M here: the
module will be called admv1013.
config ADMV1014
tristate "Analog Devices ADMV1014 Microwave Downconverter"
depends on SPI && COMMON_CLK && 64BIT
help
Say yes here to build support for Analog Devices ADMV1014
24 GHz to 44 GHz, Wideband, Microwave Downconverter.
To compile this driver as a module, choose M here: the
module will be called admv1014.
config ADMV4420
tristate "Analog Devices ADMV4420 K Band Downconverter"
depends on SPI
select REGMAP_SPI
help
Say yes here to build support for Analog Devices K Band
Downconverter with integrated Fractional-N PLL and VCO.
To compile this driver as a module, choose M here: the
module will be called admv4420.
config ADRF6780
tristate "Analog Devices ADRF6780 Microwave Upconverter"
depends on SPI
depends on COMMON_CLK
help
Say yes here to build support for Analog Devices ADRF6780
5.9 GHz to 23.6 GHz, Wideband, Microwave Upconverter.
To compile this driver as a module, choose M here: the
module will be called adrf6780.
endmenu
endmenu
@@ -0,0 +1,15 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile iio/frequency
#
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AD9523) += ad9523.o
obj-$(CONFIG_ADF4350) += adf4350.o
obj-$(CONFIG_ADF4371) += adf4371.o
obj-$(CONFIG_ADF4377) += adf4377.o
obj-$(CONFIG_ADMFM2000) += admfm2000.o
obj-$(CONFIG_ADMV1013) += admv1013.o
obj-$(CONFIG_ADMV1014) += admv1014.o
obj-$(CONFIG_ADMV4420) += admv4420.o
obj-$(CONFIG_ADRF6780) += adrf6780.o
File diff suppressed because it is too large Load Diff
+712
View File
@@ -0,0 +1,712 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* ADF4350/ADF4351 SPI Wideband Synthesizer driver
*
* Copyright 2012-2013 Analog Devices Inc.
*/
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/spi/spi.h>
#include <linux/regulator/consumer.h>
#include <linux/err.h>
#include <linux/gcd.h>
#include <linux/gpio/consumer.h>
#include <asm/div64.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/frequency/adf4350.h>
enum {
ADF4350_FREQ,
ADF4350_FREQ_REFIN,
ADF4350_FREQ_RESOLUTION,
ADF4350_PWRDOWN,
};
struct adf4350_state {
struct spi_device *spi;
struct gpio_desc *lock_detect_gpiod;
struct adf4350_platform_data *pdata;
struct clk *clk;
struct clk *clkout;
const char *clk_out_name;
struct clk_hw hw;
unsigned long clkin;
unsigned long chspc; /* Channel Spacing */
unsigned long fpfd; /* Phase Frequency Detector */
unsigned long min_out_freq;
unsigned r0_fract;
unsigned r0_int;
unsigned r1_mod;
unsigned r4_rf_div_sel;
unsigned long regs[6];
unsigned long regs_hw[6];
unsigned long long freq_req;
/*
* Lock to protect the state of the device from potential concurrent
* writes. The device is configured via a sequence of SPI writes,
* and this lock is meant to prevent the start of another sequence
* before another one has finished.
*/
struct mutex lock;
/*
* DMA (thus cache coherency maintenance) may require that
* transfer buffers live in their own cache lines.
*/
__be32 val __aligned(IIO_DMA_MINALIGN);
};
#define to_adf4350_state(_hw) container_of(_hw, struct adf4350_state, hw)
static struct adf4350_platform_data default_pdata = {
.channel_spacing = 10000,
.r2_user_settings = ADF4350_REG2_PD_POLARITY_POS |
ADF4350_REG2_CHARGE_PUMP_CURR_uA(2500),
.r3_user_settings = ADF4350_REG3_12BIT_CLKDIV_MODE(0),
.r4_user_settings = ADF4350_REG4_OUTPUT_PWR(3) |
ADF4350_REG4_MUTE_TILL_LOCK_EN,
};
static int adf4350_sync_config(struct adf4350_state *st)
{
int ret, i, doublebuf = 0;
for (i = ADF4350_REG5; i >= ADF4350_REG0; i--) {
if ((st->regs_hw[i] != st->regs[i]) ||
((i == ADF4350_REG0) && doublebuf)) {
switch (i) {
case ADF4350_REG1:
case ADF4350_REG4:
doublebuf = 1;
break;
}
st->val = cpu_to_be32(st->regs[i] | i);
ret = spi_write(st->spi, &st->val, 4);
if (ret < 0)
return ret;
st->regs_hw[i] = st->regs[i];
dev_dbg(&st->spi->dev, "[%d] 0x%X\n",
i, (u32)st->regs[i] | i);
}
}
return 0;
}
static int adf4350_reg_access(struct iio_dev *indio_dev,
unsigned reg, unsigned writeval,
unsigned *readval)
{
struct adf4350_state *st = iio_priv(indio_dev);
int ret;
if (reg > ADF4350_REG5)
return -EINVAL;
mutex_lock(&st->lock);
if (readval == NULL) {
st->regs[reg] = writeval & ~(BIT(0) | BIT(1) | BIT(2));
ret = adf4350_sync_config(st);
} else {
*readval = st->regs_hw[reg];
ret = 0;
}
mutex_unlock(&st->lock);
return ret;
}
static int adf4350_tune_r_cnt(struct adf4350_state *st, unsigned short r_cnt)
{
struct adf4350_platform_data *pdata = st->pdata;
do {
r_cnt++;
st->fpfd = (st->clkin * (pdata->ref_doubler_en ? 2 : 1)) /
(r_cnt * (pdata->ref_div2_en ? 2 : 1));
} while (st->fpfd > ADF4350_MAX_FREQ_PFD);
return r_cnt;
}
static int adf4350_set_freq(struct adf4350_state *st, unsigned long long freq)
{
struct adf4350_platform_data *pdata = st->pdata;
u64 tmp;
u32 div_gcd, prescaler, chspc;
u16 mdiv, r_cnt = 0;
u8 band_sel_div;
if (freq > ADF4350_MAX_OUT_FREQ || freq < st->min_out_freq)
return -EINVAL;
st->r4_rf_div_sel = 0;
/*
* NOTE: This iteratively shifts freq by a power of 2
* (st->r4_rf_div_sel) to meet or exceed ADF4350_MIN_VCO_FREQ.
* A constant-time approach using fls_long() was attempted but
* deemed more complex without meaningful benefit for init code.
*/
while (freq < ADF4350_MIN_VCO_FREQ) {
freq <<= 1;
st->r4_rf_div_sel++;
}
if (freq > ADF4350_MAX_FREQ_45_PRESC) {
prescaler = ADF4350_REG1_PRESCALER;
mdiv = 75;
} else {
prescaler = 0;
mdiv = 23;
}
/*
* Allow a predefined reference division factor
* if not set, compute our own
*/
if (pdata->ref_div_factor)
r_cnt = pdata->ref_div_factor - 1;
chspc = st->chspc;
do {
do {
do {
r_cnt = adf4350_tune_r_cnt(st, r_cnt);
st->r1_mod = st->fpfd / chspc;
if (r_cnt > ADF4350_MAX_R_CNT) {
/* try higher spacing values */
chspc++;
r_cnt = 0;
}
} while ((st->r1_mod > ADF4350_MAX_MODULUS) && r_cnt);
} while (r_cnt == 0);
tmp = freq * (u64)st->r1_mod + (st->fpfd >> 1);
do_div(tmp, st->fpfd); /* Div round closest (n + d/2)/d */
st->r0_fract = do_div(tmp, st->r1_mod);
st->r0_int = tmp;
} while (mdiv > st->r0_int);
band_sel_div = DIV_ROUND_UP(st->fpfd, ADF4350_MAX_BANDSEL_CLK);
if (st->r0_fract && st->r1_mod) {
div_gcd = gcd(st->r1_mod, st->r0_fract);
st->r1_mod /= div_gcd;
st->r0_fract /= div_gcd;
} else {
st->r0_fract = 0;
st->r1_mod = 1;
}
dev_dbg(&st->spi->dev, "VCO: %llu Hz, PFD %lu Hz\n"
"REF_DIV %d, R0_INT %d, R0_FRACT %d\n"
"R1_MOD %d, RF_DIV %d\nPRESCALER %s, BAND_SEL_DIV %d\n",
freq, st->fpfd, r_cnt, st->r0_int, st->r0_fract, st->r1_mod,
1 << st->r4_rf_div_sel, prescaler ? "8/9" : "4/5",
band_sel_div);
st->regs[ADF4350_REG0] = ADF4350_REG0_INT(st->r0_int) |
ADF4350_REG0_FRACT(st->r0_fract);
st->regs[ADF4350_REG1] = ADF4350_REG1_PHASE(1) |
ADF4350_REG1_MOD(st->r1_mod) |
prescaler;
st->regs[ADF4350_REG2] =
ADF4350_REG2_10BIT_R_CNT(r_cnt) |
ADF4350_REG2_DOUBLE_BUFF_EN |
(pdata->ref_doubler_en ? ADF4350_REG2_RMULT2_EN : 0) |
(pdata->ref_div2_en ? ADF4350_REG2_RDIV2_EN : 0) |
(pdata->r2_user_settings & (ADF4350_REG2_PD_POLARITY_POS |
ADF4350_REG2_LDP_6ns | ADF4350_REG2_LDF_INT_N |
ADF4350_REG2_CHARGE_PUMP_CURR_uA(5000) |
ADF4350_REG2_MUXOUT(0x7) | ADF4350_REG2_NOISE_MODE(0x3)));
st->regs[ADF4350_REG3] = pdata->r3_user_settings &
(ADF4350_REG3_12BIT_CLKDIV(0xFFF) |
ADF4350_REG3_12BIT_CLKDIV_MODE(0x3) |
ADF4350_REG3_12BIT_CSR_EN |
ADF4351_REG3_CHARGE_CANCELLATION_EN |
ADF4351_REG3_ANTI_BACKLASH_3ns_EN |
ADF4351_REG3_BAND_SEL_CLOCK_MODE_HIGH);
st->regs[ADF4350_REG4] =
ADF4350_REG4_FEEDBACK_FUND |
ADF4350_REG4_RF_DIV_SEL(st->r4_rf_div_sel) |
ADF4350_REG4_8BIT_BAND_SEL_CLKDIV(band_sel_div) |
ADF4350_REG4_RF_OUT_EN |
(pdata->r4_user_settings &
(ADF4350_REG4_OUTPUT_PWR(0x3) |
ADF4350_REG4_AUX_OUTPUT_PWR(0x3) |
ADF4350_REG4_AUX_OUTPUT_EN |
ADF4350_REG4_AUX_OUTPUT_FUND |
ADF4350_REG4_MUTE_TILL_LOCK_EN));
st->regs[ADF4350_REG5] = ADF4350_REG5_LD_PIN_MODE_DIGITAL;
st->freq_req = freq;
return adf4350_sync_config(st);
}
static ssize_t adf4350_write(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
const char *buf, size_t len)
{
struct adf4350_state *st = iio_priv(indio_dev);
unsigned long long readin;
unsigned long tmp;
int ret;
ret = kstrtoull(buf, 10, &readin);
if (ret)
return ret;
mutex_lock(&st->lock);
switch ((u32)private) {
case ADF4350_FREQ:
ret = adf4350_set_freq(st, readin);
break;
case ADF4350_FREQ_REFIN:
if (readin > ADF4350_MAX_FREQ_REFIN) {
ret = -EINVAL;
break;
}
if (st->clk) {
tmp = clk_round_rate(st->clk, readin);
if (tmp != readin) {
ret = -EINVAL;
break;
}
ret = clk_set_rate(st->clk, tmp);
if (ret < 0)
break;
}
st->clkin = readin;
ret = adf4350_set_freq(st, st->freq_req);
break;
case ADF4350_FREQ_RESOLUTION:
if (readin == 0)
ret = -EINVAL;
else
st->chspc = readin;
break;
case ADF4350_PWRDOWN:
if (readin)
st->regs[ADF4350_REG2] |= ADF4350_REG2_POWER_DOWN_EN;
else
st->regs[ADF4350_REG2] &= ~ADF4350_REG2_POWER_DOWN_EN;
adf4350_sync_config(st);
break;
default:
ret = -EINVAL;
}
mutex_unlock(&st->lock);
return ret ? ret : len;
}
static ssize_t adf4350_read(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
char *buf)
{
struct adf4350_state *st = iio_priv(indio_dev);
unsigned long long val;
int ret = 0;
mutex_lock(&st->lock);
switch ((u32)private) {
case ADF4350_FREQ:
val = (u64)((st->r0_int * st->r1_mod) + st->r0_fract) *
(u64)st->fpfd;
do_div(val, st->r1_mod * (1 << st->r4_rf_div_sel));
/* PLL unlocked? return error */
if (st->lock_detect_gpiod)
if (!gpiod_get_value(st->lock_detect_gpiod)) {
dev_dbg(&st->spi->dev, "PLL un-locked\n");
ret = -EBUSY;
}
break;
case ADF4350_FREQ_REFIN:
if (st->clk)
st->clkin = clk_get_rate(st->clk);
val = st->clkin;
break;
case ADF4350_FREQ_RESOLUTION:
val = st->chspc;
break;
case ADF4350_PWRDOWN:
val = !!(st->regs[ADF4350_REG2] & ADF4350_REG2_POWER_DOWN_EN);
break;
default:
ret = -EINVAL;
val = 0;
}
mutex_unlock(&st->lock);
return ret < 0 ? ret : sprintf(buf, "%llu\n", val);
}
#define _ADF4350_EXT_INFO(_name, _ident) { \
.name = _name, \
.read = adf4350_read, \
.write = adf4350_write, \
.private = _ident, \
.shared = IIO_SEPARATE, \
}
static const struct iio_chan_spec_ext_info adf4350_ext_info[] = {
/* Ideally we use IIO_CHAN_INFO_FREQUENCY, but there are
* values > 2^32 in order to support the entire frequency range
* in Hz. Using scale is a bit ugly.
*/
_ADF4350_EXT_INFO("frequency", ADF4350_FREQ),
_ADF4350_EXT_INFO("frequency_resolution", ADF4350_FREQ_RESOLUTION),
_ADF4350_EXT_INFO("refin_frequency", ADF4350_FREQ_REFIN),
_ADF4350_EXT_INFO("powerdown", ADF4350_PWRDOWN),
{ }
};
static const struct iio_chan_spec adf4350_chan = {
.type = IIO_ALTVOLTAGE,
.indexed = 1,
.output = 1,
.ext_info = adf4350_ext_info,
};
static const struct iio_info adf4350_info = {
.debugfs_reg_access = &adf4350_reg_access,
};
static void adf4350_clk_del_provider(void *data)
{
struct adf4350_state *st = data;
of_clk_del_provider(st->spi->dev.of_node);
}
static unsigned long adf4350_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct adf4350_state *st = to_adf4350_state(hw);
unsigned long long tmp;
tmp = (u64)(st->r0_int * st->r1_mod + st->r0_fract) * st->fpfd;
do_div(tmp, st->r1_mod * (1 << st->r4_rf_div_sel));
return tmp;
}
static int adf4350_clk_set_rate(struct clk_hw *hw,
unsigned long rate,
unsigned long parent_rate)
{
struct adf4350_state *st = to_adf4350_state(hw);
if (parent_rate == 0 || parent_rate > ADF4350_MAX_FREQ_REFIN)
return -EINVAL;
st->clkin = parent_rate;
return adf4350_set_freq(st, rate);
}
static int adf4350_clk_prepare(struct clk_hw *hw)
{
struct adf4350_state *st = to_adf4350_state(hw);
st->regs[ADF4350_REG2] &= ~ADF4350_REG2_POWER_DOWN_EN;
return adf4350_sync_config(st);
}
static void adf4350_clk_unprepare(struct clk_hw *hw)
{
struct adf4350_state *st = to_adf4350_state(hw);
st->regs[ADF4350_REG2] |= ADF4350_REG2_POWER_DOWN_EN;
adf4350_sync_config(st);
}
static int adf4350_clk_is_enabled(struct clk_hw *hw)
{
struct adf4350_state *st = to_adf4350_state(hw);
return (st->regs[ADF4350_REG2] & ADF4350_REG2_POWER_DOWN_EN);
}
static const struct clk_ops adf4350_clk_ops = {
.recalc_rate = adf4350_clk_recalc_rate,
.set_rate = adf4350_clk_set_rate,
.prepare = adf4350_clk_prepare,
.unprepare = adf4350_clk_unprepare,
.is_enabled = adf4350_clk_is_enabled,
};
static int adf4350_clk_register(struct adf4350_state *st)
{
struct spi_device *spi = st->spi;
struct clk_init_data init;
struct clk *clk;
const char *parent_name;
int ret;
if (!device_property_present(&spi->dev, "#clock-cells"))
return 0;
if (device_property_read_string(&spi->dev, "clock-output-names", &init.name)) {
init.name = devm_kasprintf(&spi->dev, GFP_KERNEL, "%s-clk",
fwnode_get_name(dev_fwnode(&spi->dev)));
if (!init.name)
return -ENOMEM;
}
parent_name = of_clk_get_parent_name(spi->dev.of_node, 0);
if (!parent_name)
return -EINVAL;
init.ops = &adf4350_clk_ops;
init.parent_names = &parent_name;
init.num_parents = 1;
init.flags = CLK_SET_RATE_PARENT;
st->hw.init = &init;
clk = devm_clk_register(&spi->dev, &st->hw);
if (IS_ERR(clk))
return PTR_ERR(clk);
ret = of_clk_add_provider(spi->dev.of_node, of_clk_src_simple_get, clk);
if (ret)
return ret;
st->clkout = clk;
return devm_add_action_or_reset(&spi->dev, adf4350_clk_del_provider, st);
}
static struct adf4350_platform_data *adf4350_parse_dt(struct device *dev)
{
struct adf4350_platform_data *pdata;
unsigned int tmp;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return NULL;
snprintf(pdata->name, sizeof(pdata->name), "%pfw", dev_fwnode(dev));
tmp = 10000;
device_property_read_u32(dev, "adi,channel-spacing", &tmp);
pdata->channel_spacing = tmp;
tmp = 0;
device_property_read_u32(dev, "adi,power-up-frequency", &tmp);
pdata->power_up_frequency = tmp;
tmp = 0;
device_property_read_u32(dev, "adi,reference-div-factor", &tmp);
pdata->ref_div_factor = tmp;
pdata->ref_doubler_en = device_property_read_bool(dev, "adi,reference-doubler-enable");
pdata->ref_div2_en = device_property_read_bool(dev, "adi,reference-div2-enable");
/* r2_user_settings */
pdata->r2_user_settings = 0;
if (device_property_read_bool(dev, "adi,phase-detector-polarity-positive-enable"))
pdata->r2_user_settings |= ADF4350_REG2_PD_POLARITY_POS;
if (device_property_read_bool(dev, "adi,lock-detect-precision-6ns-enable"))
pdata->r2_user_settings |= ADF4350_REG2_LDP_6ns;
if (device_property_read_bool(dev, "adi,lock-detect-function-integer-n-enable"))
pdata->r2_user_settings |= ADF4350_REG2_LDF_INT_N;
tmp = 2500;
device_property_read_u32(dev, "adi,charge-pump-current", &tmp);
pdata->r2_user_settings |= ADF4350_REG2_CHARGE_PUMP_CURR_uA(tmp);
tmp = 0;
device_property_read_u32(dev, "adi,muxout-select", &tmp);
pdata->r2_user_settings |= ADF4350_REG2_MUXOUT(tmp);
if (device_property_read_bool(dev, "adi,low-spur-mode-enable"))
pdata->r2_user_settings |= ADF4350_REG2_NOISE_MODE(0x3);
/* r3_user_settings */
pdata->r3_user_settings = 0;
if (device_property_read_bool(dev, "adi,cycle-slip-reduction-enable"))
pdata->r3_user_settings |= ADF4350_REG3_12BIT_CSR_EN;
if (device_property_read_bool(dev, "adi,charge-cancellation-enable"))
pdata->r3_user_settings |= ADF4351_REG3_CHARGE_CANCELLATION_EN;
if (device_property_read_bool(dev, "adi,anti-backlash-3ns-enable"))
pdata->r3_user_settings |= ADF4351_REG3_ANTI_BACKLASH_3ns_EN;
if (device_property_read_bool(dev, "adi,band-select-clock-mode-high-enable"))
pdata->r3_user_settings |= ADF4351_REG3_BAND_SEL_CLOCK_MODE_HIGH;
tmp = 0;
device_property_read_u32(dev, "adi,12bit-clk-divider", &tmp);
pdata->r3_user_settings |= ADF4350_REG3_12BIT_CLKDIV(tmp);
tmp = 0;
device_property_read_u32(dev, "adi,clk-divider-mode", &tmp);
pdata->r3_user_settings |= ADF4350_REG3_12BIT_CLKDIV_MODE(tmp);
/* r4_user_settings */
pdata->r4_user_settings = 0;
if (device_property_read_bool(dev, "adi,aux-output-enable"))
pdata->r4_user_settings |= ADF4350_REG4_AUX_OUTPUT_EN;
if (device_property_read_bool(dev, "adi,aux-output-fundamental-enable"))
pdata->r4_user_settings |= ADF4350_REG4_AUX_OUTPUT_FUND;
if (device_property_read_bool(dev, "adi,mute-till-lock-enable"))
pdata->r4_user_settings |= ADF4350_REG4_MUTE_TILL_LOCK_EN;
tmp = 0;
device_property_read_u32(dev, "adi,output-power", &tmp);
pdata->r4_user_settings |= ADF4350_REG4_OUTPUT_PWR(tmp);
tmp = 0;
device_property_read_u32(dev, "adi,aux-output-power", &tmp);
pdata->r4_user_settings |= ADF4350_REG4_AUX_OUTPUT_PWR(tmp);
return pdata;
}
static void adf4350_power_down(void *data)
{
struct iio_dev *indio_dev = data;
struct adf4350_state *st = iio_priv(indio_dev);
st->regs[ADF4350_REG2] |= ADF4350_REG2_POWER_DOWN_EN;
adf4350_sync_config(st);
}
static int adf4350_probe(struct spi_device *spi)
{
struct adf4350_platform_data *pdata;
struct iio_dev *indio_dev;
struct adf4350_state *st;
struct clk *clk = NULL;
int ret;
if (dev_fwnode(&spi->dev)) {
pdata = adf4350_parse_dt(&spi->dev);
if (pdata == NULL)
return -ENOMEM;
} else {
pdata = dev_get_platdata(&spi->dev);
}
if (!pdata) {
dev_warn(&spi->dev, "no platform data? using default\n");
pdata = &default_pdata;
}
if (!pdata->clkin) {
clk = devm_clk_get_enabled(&spi->dev, "clkin");
if (IS_ERR(clk))
return PTR_ERR(clk);
}
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
if (indio_dev == NULL)
return -ENOMEM;
st = iio_priv(indio_dev);
ret = devm_regulator_get_enable(&spi->dev, "vcc");
if (ret)
return ret;
st->spi = spi;
st->pdata = pdata;
indio_dev->name = (pdata->name[0] != 0) ? pdata->name :
spi_get_device_id(spi)->name;
indio_dev->info = &adf4350_info;
indio_dev->modes = INDIO_DIRECT_MODE;
mutex_init(&st->lock);
st->chspc = pdata->channel_spacing;
if (clk) {
st->clk = clk;
st->clkin = clk_get_rate(clk);
} else {
st->clkin = pdata->clkin;
}
st->min_out_freq = spi_get_device_id(spi)->driver_data == 4351 ?
ADF4351_MIN_OUT_FREQ : ADF4350_MIN_OUT_FREQ;
memset(st->regs_hw, 0xFF, sizeof(st->regs_hw));
st->lock_detect_gpiod = devm_gpiod_get_optional(&spi->dev, NULL,
GPIOD_IN);
if (IS_ERR(st->lock_detect_gpiod))
return PTR_ERR(st->lock_detect_gpiod);
if (pdata->power_up_frequency) {
ret = adf4350_set_freq(st, pdata->power_up_frequency);
if (ret)
return ret;
}
ret = adf4350_clk_register(st);
if (ret)
return ret;
if (!st->clkout) {
indio_dev->channels = &adf4350_chan;
indio_dev->num_channels = 1;
}
ret = devm_add_action_or_reset(&spi->dev, adf4350_power_down, indio_dev);
if (ret)
return ret;
return devm_iio_device_register(&spi->dev, indio_dev);
}
static const struct of_device_id adf4350_of_match[] = {
{ .compatible = "adi,adf4350", },
{ .compatible = "adi,adf4351", },
{ }
};
MODULE_DEVICE_TABLE(of, adf4350_of_match);
static const struct spi_device_id adf4350_id[] = {
{"adf4350", 4350},
{"adf4351", 4351},
{ }
};
MODULE_DEVICE_TABLE(spi, adf4350_id);
static struct spi_driver adf4350_driver = {
.driver = {
.name = "adf4350",
.of_match_table = adf4350_of_match,
},
.probe = adf4350_probe,
.id_table = adf4350_id,
};
module_spi_driver(adf4350_driver);
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Analog Devices ADF4350/ADF4351 PLL");
MODULE_LICENSE("GPL v2");
+652
View File
@@ -0,0 +1,652 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Analog Devices ADF4371 SPI Wideband Synthesizer driver
*
* Copyright 2019 Analog Devices Inc.
*/
#include "linux/dev_printk.h"
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gcd.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/sysfs.h>
#include <linux/spi/spi.h>
#include <linux/iio/iio.h>
/* Registers address macro */
#define ADF4371_REG(x) (x)
/* ADF4371_REG0 */
#define ADF4371_ADDR_ASC_MSK BIT(2)
#define ADF4371_ADDR_ASC(x) FIELD_PREP(ADF4371_ADDR_ASC_MSK, x)
#define ADF4371_ADDR_ASC_R_MSK BIT(5)
#define ADF4371_ADDR_ASC_R(x) FIELD_PREP(ADF4371_ADDR_ASC_R_MSK, x)
#define ADF4371_RESET_CMD 0x81
/* ADF4371_REG17 */
#define ADF4371_FRAC2WORD_L_MSK GENMASK(7, 1)
#define ADF4371_FRAC2WORD_L(x) FIELD_PREP(ADF4371_FRAC2WORD_L_MSK, x)
#define ADF4371_FRAC1WORD_MSK BIT(0)
#define ADF4371_FRAC1WORD(x) FIELD_PREP(ADF4371_FRAC1WORD_MSK, x)
/* ADF4371_REG18 */
#define ADF4371_FRAC2WORD_H_MSK GENMASK(6, 0)
#define ADF4371_FRAC2WORD_H(x) FIELD_PREP(ADF4371_FRAC2WORD_H_MSK, x)
/* ADF4371_REG1A */
#define ADF4371_MOD2WORD_MSK GENMASK(5, 0)
#define ADF4371_MOD2WORD(x) FIELD_PREP(ADF4371_MOD2WORD_MSK, x)
/* ADF4371_REG22 */
#define ADF4371_REFIN_MODE_MASK BIT(6)
#define ADF4371_REFIN_MODE(x) FIELD_PREP(ADF4371_REFIN_MODE_MASK, x)
#define ADF4371_REF_DOUB_MASK BIT(5)
#define ADF4371_REF_DOUB(x) FIELD_PREP(ADF4371_REF_DOUB_MASK, x)\
/* ADF4371_REG24 */
#define ADF4371_RF_DIV_SEL_MSK GENMASK(6, 4)
#define ADF4371_RF_DIV_SEL(x) FIELD_PREP(ADF4371_RF_DIV_SEL_MSK, x)
/* ADF4371_REG25 */
#define ADF4371_MUTE_LD_MSK BIT(7)
#define ADF4371_MUTE_LD(x) FIELD_PREP(ADF4371_MUTE_LD_MSK, x)
/* ADF4371_REG32 */
#define ADF4371_TIMEOUT_MSK GENMASK(1, 0)
#define ADF4371_TIMEOUT(x) FIELD_PREP(ADF4371_TIMEOUT_MSK, x)
/* ADF4371_REG34 */
#define ADF4371_VCO_ALC_TOUT_MSK GENMASK(4, 0)
#define ADF4371_VCO_ALC_TOUT(x) FIELD_PREP(ADF4371_VCO_ALC_TOUT_MSK, x)
/* Specifications */
#define ADF4371_MIN_VCO_FREQ 4000000000ULL /* 4000 MHz */
#define ADF4371_MAX_VCO_FREQ 8000000000ULL /* 8000 MHz */
#define ADF4371_MAX_OUT_RF8_FREQ ADF4371_MAX_VCO_FREQ /* Hz */
#define ADF4371_MIN_OUT_RF8_FREQ (ADF4371_MIN_VCO_FREQ / 64) /* Hz */
#define ADF4371_MAX_OUT_RF16_FREQ (ADF4371_MAX_VCO_FREQ * 2) /* Hz */
#define ADF4371_MIN_OUT_RF16_FREQ (ADF4371_MIN_VCO_FREQ * 2) /* Hz */
#define ADF4371_MAX_OUT_RF32_FREQ (ADF4371_MAX_VCO_FREQ * 4) /* Hz */
#define ADF4371_MIN_OUT_RF32_FREQ (ADF4371_MIN_VCO_FREQ * 4) /* Hz */
#define ADF4371_MAX_FREQ_PFD 250000000UL /* Hz */
#define ADF4371_MAX_FREQ_REFIN 600000000UL /* Hz */
#define ADF4371_MAX_FREQ_REFIN_SE 500000000UL /* Hz */
#define ADF4371_MIN_CLKIN_DOUB_FREQ 10000000ULL /* Hz */
#define ADF4371_MAX_CLKIN_DOUB_FREQ 125000000ULL /* Hz */
/* MOD1 is a 24-bit primary modulus with fixed value of 2^25 */
#define ADF4371_MODULUS1 33554432ULL
/* MOD2 is the programmable, 14-bit auxiliary fractional modulus */
#define ADF4371_MAX_MODULUS2 BIT(14)
#define ADF4371_CHECK_RANGE(freq, range) \
((freq > ADF4371_MAX_ ## range) || (freq < ADF4371_MIN_ ## range))
enum {
ADF4371_FREQ,
ADF4371_POWER_DOWN,
ADF4371_CHANNEL_NAME
};
enum {
ADF4371_CH_RF8,
ADF4371_CH_RFAUX8,
ADF4371_CH_RF16,
ADF4371_CH_RF32
};
enum adf4371_variant {
ADF4371,
ADF4372
};
struct adf4371_pwrdown {
unsigned int reg;
unsigned int bit;
};
static const char * const adf4371_ch_names[] = {
"RF8x", "RFAUX8x", "RF16x", "RF32x"
};
static const struct adf4371_pwrdown adf4371_pwrdown_ch[4] = {
[ADF4371_CH_RF8] = { ADF4371_REG(0x25), 2 },
[ADF4371_CH_RFAUX8] = { ADF4371_REG(0x72), 3 },
[ADF4371_CH_RF16] = { ADF4371_REG(0x25), 3 },
[ADF4371_CH_RF32] = { ADF4371_REG(0x25), 4 },
};
static const struct reg_sequence adf4371_reg_defaults[] = {
{ ADF4371_REG(0x0), 0x18 },
{ ADF4371_REG(0x12), 0x40 },
{ ADF4371_REG(0x1E), 0x48 },
{ ADF4371_REG(0x20), 0x14 },
{ ADF4371_REG(0x22), 0x00 },
{ ADF4371_REG(0x23), 0x00 },
{ ADF4371_REG(0x24), 0x80 },
{ ADF4371_REG(0x25), 0x07 },
{ ADF4371_REG(0x27), 0xC5 },
{ ADF4371_REG(0x28), 0x83 },
{ ADF4371_REG(0x2C), 0x44 },
{ ADF4371_REG(0x2D), 0x11 },
{ ADF4371_REG(0x2E), 0x12 },
{ ADF4371_REG(0x2F), 0x94 },
{ ADF4371_REG(0x32), 0x04 },
{ ADF4371_REG(0x35), 0xFA },
{ ADF4371_REG(0x36), 0x30 },
{ ADF4371_REG(0x39), 0x07 },
{ ADF4371_REG(0x3A), 0x55 },
{ ADF4371_REG(0x3E), 0x0C },
{ ADF4371_REG(0x3F), 0x80 },
{ ADF4371_REG(0x40), 0x50 },
{ ADF4371_REG(0x41), 0x28 },
{ ADF4371_REG(0x47), 0xC0 },
{ ADF4371_REG(0x52), 0xF4 },
{ ADF4371_REG(0x70), 0x03 },
{ ADF4371_REG(0x71), 0x60 },
{ ADF4371_REG(0x72), 0x32 },
};
static const struct regmap_config adf4371_regmap_config = {
.reg_bits = 16,
.val_bits = 8,
.read_flag_mask = BIT(7),
};
struct adf4371_chip_info {
const char *name;
unsigned int num_channels;
const struct iio_chan_spec *channels;
};
struct adf4371_state {
struct spi_device *spi;
struct regmap *regmap;
/*
* Lock for accessing device registers. Some operations require
* multiple consecutive R/W operations, during which the device
* shouldn't be interrupted. The buffers are also shared across
* all operations so need to be protected on stand alone reads and
* writes.
*/
struct mutex lock;
const struct adf4371_chip_info *chip_info;
unsigned long clkin_freq;
unsigned long fpfd;
unsigned int integer;
unsigned int fract1;
unsigned int fract2;
unsigned int mod2;
unsigned int rf_div_sel;
unsigned int ref_div_factor;
bool ref_diff_en;
u8 buf[10] __aligned(IIO_DMA_MINALIGN);
};
static unsigned long long adf4371_pll_fract_n_get_rate(struct adf4371_state *st,
u32 channel)
{
unsigned long long val, tmp;
unsigned int ref_div_sel;
val = (((u64)st->integer * ADF4371_MODULUS1) + st->fract1) * st->fpfd;
tmp = (u64)st->fract2 * st->fpfd;
do_div(tmp, st->mod2);
val += tmp + ADF4371_MODULUS1 / 2;
if (channel == ADF4371_CH_RF8 || channel == ADF4371_CH_RFAUX8)
ref_div_sel = st->rf_div_sel;
else
ref_div_sel = 0;
do_div(val, ADF4371_MODULUS1 * (1 << ref_div_sel));
if (channel == ADF4371_CH_RF16)
val <<= 1;
else if (channel == ADF4371_CH_RF32)
val <<= 2;
return val;
}
static void adf4371_pll_fract_n_compute(unsigned long long vco,
unsigned long long pfd,
unsigned int *integer,
unsigned int *fract1,
unsigned int *fract2,
unsigned int *mod2)
{
unsigned long long tmp;
u32 gcd_div;
tmp = do_div(vco, pfd);
tmp = tmp * ADF4371_MODULUS1;
*fract2 = do_div(tmp, pfd);
*integer = vco;
*fract1 = tmp;
*mod2 = pfd;
while (*mod2 > ADF4371_MAX_MODULUS2) {
*mod2 >>= 1;
*fract2 >>= 1;
}
gcd_div = gcd(*fract2, *mod2);
*mod2 /= gcd_div;
*fract2 /= gcd_div;
}
static int adf4371_set_freq(struct adf4371_state *st, unsigned long long freq,
unsigned int channel)
{
u32 cp_bleed;
u8 int_mode = 0;
int ret;
switch (channel) {
case ADF4371_CH_RF8:
case ADF4371_CH_RFAUX8:
if (ADF4371_CHECK_RANGE(freq, OUT_RF8_FREQ))
return -EINVAL;
st->rf_div_sel = 0;
while (freq < ADF4371_MIN_VCO_FREQ) {
freq <<= 1;
st->rf_div_sel++;
}
break;
case ADF4371_CH_RF16:
/* ADF4371 RF16 8000...16000 MHz */
if (ADF4371_CHECK_RANGE(freq, OUT_RF16_FREQ))
return -EINVAL;
freq >>= 1;
break;
case ADF4371_CH_RF32:
/* ADF4371 RF32 16000...32000 MHz */
if (ADF4371_CHECK_RANGE(freq, OUT_RF32_FREQ))
return -EINVAL;
freq >>= 2;
break;
default:
return -EINVAL;
}
adf4371_pll_fract_n_compute(freq, st->fpfd, &st->integer, &st->fract1,
&st->fract2, &st->mod2);
st->buf[0] = st->integer >> 8;
st->buf[1] = 0x40; /* REG12 default */
st->buf[2] = 0x00;
st->buf[3] = st->fract1 & 0xFF;
st->buf[4] = st->fract1 >> 8;
st->buf[5] = st->fract1 >> 16;
st->buf[6] = ADF4371_FRAC2WORD_L(st->fract2 & 0x7F) |
ADF4371_FRAC1WORD(st->fract1 >> 24);
st->buf[7] = ADF4371_FRAC2WORD_H(st->fract2 >> 7);
st->buf[8] = st->mod2 & 0xFF;
st->buf[9] = ADF4371_MOD2WORD(st->mod2 >> 8);
ret = regmap_bulk_write(st->regmap, ADF4371_REG(0x11), st->buf, 10);
if (ret < 0)
return ret;
/*
* The R counter allows the input reference frequency to be
* divided down to produce the reference clock to the PFD
*/
ret = regmap_write(st->regmap, ADF4371_REG(0x1F), st->ref_div_factor);
if (ret < 0)
return ret;
ret = regmap_update_bits(st->regmap, ADF4371_REG(0x24),
ADF4371_RF_DIV_SEL_MSK,
ADF4371_RF_DIV_SEL(st->rf_div_sel));
if (ret < 0)
return ret;
cp_bleed = DIV_ROUND_UP(400 * 1750, st->integer * 375);
cp_bleed = clamp(cp_bleed, 1U, 255U);
ret = regmap_write(st->regmap, ADF4371_REG(0x26), cp_bleed);
if (ret < 0)
return ret;
/*
* Set to 1 when in INT mode (when FRAC1 = FRAC2 = 0),
* and set to 0 when in FRAC mode.
*/
if (st->fract1 == 0 && st->fract2 == 0)
int_mode = 0x01;
ret = regmap_write(st->regmap, ADF4371_REG(0x2B), int_mode);
if (ret < 0)
return ret;
return regmap_write(st->regmap, ADF4371_REG(0x10), st->integer & 0xFF);
}
static ssize_t adf4371_read(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
char *buf)
{
struct adf4371_state *st = iio_priv(indio_dev);
unsigned long long val = 0;
unsigned int readval, reg, bit;
int ret;
switch ((u32)private) {
case ADF4371_FREQ:
val = adf4371_pll_fract_n_get_rate(st, chan->channel);
ret = regmap_read(st->regmap, ADF4371_REG(0x7C), &readval);
if (ret < 0)
break;
if (readval == 0x00) {
dev_dbg(&st->spi->dev, "PLL un-locked\n");
ret = -EBUSY;
}
break;
case ADF4371_POWER_DOWN:
reg = adf4371_pwrdown_ch[chan->channel].reg;
bit = adf4371_pwrdown_ch[chan->channel].bit;
ret = regmap_read(st->regmap, reg, &readval);
if (ret < 0)
break;
val = !(readval & BIT(bit));
break;
case ADF4371_CHANNEL_NAME:
return sprintf(buf, "%s\n", adf4371_ch_names[chan->channel]);
default:
ret = -EINVAL;
val = 0;
break;
}
return ret < 0 ? ret : sprintf(buf, "%llu\n", val);
}
static ssize_t adf4371_write(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
const char *buf, size_t len)
{
struct adf4371_state *st = iio_priv(indio_dev);
unsigned long long freq;
bool power_down;
unsigned int bit, readval, reg;
int ret;
mutex_lock(&st->lock);
switch ((u32)private) {
case ADF4371_FREQ:
ret = kstrtoull(buf, 10, &freq);
if (ret)
break;
ret = adf4371_set_freq(st, freq, chan->channel);
break;
case ADF4371_POWER_DOWN:
ret = kstrtobool(buf, &power_down);
if (ret)
break;
reg = adf4371_pwrdown_ch[chan->channel].reg;
bit = adf4371_pwrdown_ch[chan->channel].bit;
ret = regmap_read(st->regmap, reg, &readval);
if (ret < 0)
break;
readval &= ~BIT(bit);
readval |= (!power_down << bit);
ret = regmap_write(st->regmap, reg, readval);
break;
default:
ret = -EINVAL;
break;
}
mutex_unlock(&st->lock);
return ret ? ret : len;
}
#define _ADF4371_EXT_INFO(_name, _ident) { \
.name = _name, \
.read = adf4371_read, \
.write = adf4371_write, \
.private = _ident, \
.shared = IIO_SEPARATE, \
}
static const struct iio_chan_spec_ext_info adf4371_ext_info[] = {
/*
* Ideally we use IIO_CHAN_INFO_FREQUENCY, but there are
* values > 2^32 in order to support the entire frequency range
* in Hz. Using scale is a bit ugly.
*/
_ADF4371_EXT_INFO("frequency", ADF4371_FREQ),
_ADF4371_EXT_INFO("powerdown", ADF4371_POWER_DOWN),
_ADF4371_EXT_INFO("name", ADF4371_CHANNEL_NAME),
{ }
};
#define ADF4371_CHANNEL(index) { \
.type = IIO_ALTVOLTAGE, \
.output = 1, \
.channel = index, \
.ext_info = adf4371_ext_info, \
.indexed = 1, \
}
static const struct iio_chan_spec adf4371_chan[] = {
ADF4371_CHANNEL(ADF4371_CH_RF8),
ADF4371_CHANNEL(ADF4371_CH_RFAUX8),
ADF4371_CHANNEL(ADF4371_CH_RF16),
ADF4371_CHANNEL(ADF4371_CH_RF32),
};
static const struct adf4371_chip_info adf4371_chip_info = {
.name = "adf4371",
.channels = adf4371_chan,
.num_channels = 4,
};
static const struct adf4371_chip_info adf4372_chip_info = {
.name = "adf4372",
.channels = adf4371_chan,
.num_channels = 3,
};
static int adf4371_reg_access(struct iio_dev *indio_dev,
unsigned int reg,
unsigned int writeval,
unsigned int *readval)
{
struct adf4371_state *st = iio_priv(indio_dev);
if (readval)
return regmap_read(st->regmap, reg, readval);
else
return regmap_write(st->regmap, reg, writeval);
}
static const struct iio_info adf4371_info = {
.debugfs_reg_access = &adf4371_reg_access,
};
static int adf4371_setup(struct adf4371_state *st)
{
unsigned int synth_timeout = 2, timeout = 1, vco_alc_timeout = 1;
unsigned int vco_band_div, tmp, ref_doubler_en = 0;
int ret;
/* Perform a software reset */
ret = regmap_write(st->regmap, ADF4371_REG(0x0), ADF4371_RESET_CMD);
if (ret < 0)
return ret;
ret = regmap_multi_reg_write(st->regmap, adf4371_reg_defaults,
ARRAY_SIZE(adf4371_reg_defaults));
if (ret < 0)
return ret;
/* Mute to Lock Detect */
if (device_property_read_bool(&st->spi->dev, "adi,mute-till-lock-en")) {
ret = regmap_update_bits(st->regmap, ADF4371_REG(0x25),
ADF4371_MUTE_LD_MSK,
ADF4371_MUTE_LD(1));
if (ret < 0)
return ret;
}
/* Set address in ascending order, so the bulk_write() will work */
ret = regmap_update_bits(st->regmap, ADF4371_REG(0x0),
ADF4371_ADDR_ASC_MSK | ADF4371_ADDR_ASC_R_MSK,
ADF4371_ADDR_ASC(1) | ADF4371_ADDR_ASC_R(1));
if (ret < 0)
return ret;
if ((st->ref_diff_en && st->clkin_freq > ADF4371_MAX_FREQ_REFIN) ||
(!st->ref_diff_en && st->clkin_freq > ADF4371_MAX_FREQ_REFIN_SE))
return -EINVAL;
if (st->clkin_freq < ADF4371_MAX_CLKIN_DOUB_FREQ &&
st->clkin_freq > ADF4371_MIN_CLKIN_DOUB_FREQ)
ref_doubler_en = 1;
ret = regmap_update_bits(st->regmap, ADF4371_REG(0x22),
ADF4371_REF_DOUB_MASK |
ADF4371_REFIN_MODE_MASK,
ADF4371_REF_DOUB(ref_doubler_en) |
ADF4371_REFIN_MODE(st->ref_diff_en));
if (ret < 0)
return ret;
/*
* Calculate and maximize PFD frequency
* fPFD = REFIN × ((1 + D)/(R × (1 + T)))
* Where D is the REFIN doubler bit, T is the reference divide by 2,
* R is the reference division factor
* TODO: it is assumed D and T equal 0.
*/
do {
st->ref_div_factor++;
st->fpfd = st->clkin_freq * (1 + ref_doubler_en) /
st->ref_div_factor;
} while (st->fpfd > ADF4371_MAX_FREQ_PFD);
/* Calculate Timeouts */
vco_band_div = DIV_ROUND_UP(st->fpfd, 2400000U);
tmp = DIV_ROUND_CLOSEST(st->fpfd, 1000000U);
do {
timeout++;
if (timeout > 1023) {
timeout = 2;
synth_timeout++;
}
} while (synth_timeout * 1024 + timeout <= 20 * tmp);
do {
vco_alc_timeout++;
} while (vco_alc_timeout * 1024 - timeout <= 50 * tmp);
st->buf[0] = vco_band_div;
st->buf[1] = timeout & 0xFF;
st->buf[2] = ADF4371_TIMEOUT(timeout >> 8) | 0x04;
st->buf[3] = synth_timeout;
st->buf[4] = ADF4371_VCO_ALC_TOUT(vco_alc_timeout);
return regmap_bulk_write(st->regmap, ADF4371_REG(0x30), st->buf, 5);
}
static int adf4371_probe(struct spi_device *spi)
{
struct iio_dev *indio_dev;
struct adf4371_state *st;
struct regmap *regmap;
struct clk *clkin;
int ret;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
regmap = devm_regmap_init_spi(spi, &adf4371_regmap_config);
if (IS_ERR(regmap))
return dev_err_probe(&spi->dev, PTR_ERR(regmap),
"Error initializing spi regmap\n");
st = iio_priv(indio_dev);
st->spi = spi;
st->regmap = regmap;
mutex_init(&st->lock);
st->chip_info = spi_get_device_match_data(spi);
if (!st->chip_info)
return -ENODEV;
indio_dev->name = st->chip_info->name;
indio_dev->info = &adf4371_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = st->chip_info->channels;
indio_dev->num_channels = st->chip_info->num_channels;
st->ref_diff_en = false;
clkin = devm_clk_get_enabled(&spi->dev, "clkin");
if (IS_ERR(clkin)) {
clkin = devm_clk_get_enabled(&spi->dev, "clkin-diff");
if (IS_ERR(clkin))
return dev_err_probe(&spi->dev, PTR_ERR(clkin),
"Failed to get clkin/clkin-diff\n");
st->ref_diff_en = true;
}
st->clkin_freq = clk_get_rate(clkin);
ret = adf4371_setup(st);
if (ret < 0)
return dev_err_probe(&spi->dev, ret, "ADF4371 setup failed\n");
return devm_iio_device_register(&spi->dev, indio_dev);
}
static const struct spi_device_id adf4371_id_table[] = {
{ "adf4371", (kernel_ulong_t)&adf4371_chip_info },
{ "adf4372", (kernel_ulong_t)&adf4372_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, adf4371_id_table);
static const struct of_device_id adf4371_of_match[] = {
{ .compatible = "adi,adf4371", .data = &adf4371_chip_info },
{ .compatible = "adi,adf4372", .data = &adf4372_chip_info},
{ }
};
MODULE_DEVICE_TABLE(of, adf4371_of_match);
static struct spi_driver adf4371_driver = {
.driver = {
.name = "adf4371",
.of_match_table = adf4371_of_match,
},
.probe = adf4371_probe,
.id_table = adf4371_id_table,
};
module_spi_driver(adf4371_driver);
MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
MODULE_DESCRIPTION("Analog Devices ADF4371 SPI PLL");
MODULE_LICENSE("GPL");
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,269 @@
// SPDX-License-Identifier: GPL-2.0
/*
* ADMFM2000 Dual Microwave Down Converter
*
* Copyright 2024 Analog Devices Inc.
*/
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/iio/iio.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#define ADMFM2000_MIXER_MODE 0
#define ADMFM2000_DIRECT_IF_MODE 1
#define ADMFM2000_DSA_GPIOS 5
#define ADMFM2000_MODE_GPIOS 2
#define ADMFM2000_MAX_GAIN 0
#define ADMFM2000_MIN_GAIN -31000
#define ADMFM2000_DEFAULT_GAIN -0x20
struct admfm2000_state {
struct mutex lock; /* protect sensor state */
struct gpio_desc *sw1_ch[2];
struct gpio_desc *sw2_ch[2];
struct gpio_desc *dsa1_gpios[5];
struct gpio_desc *dsa2_gpios[5];
u32 gain[2];
};
static int admfm2000_mode(struct iio_dev *indio_dev, u32 chan, u32 mode)
{
struct admfm2000_state *st = iio_priv(indio_dev);
int i;
switch (mode) {
case ADMFM2000_MIXER_MODE:
for (i = 0; i < ADMFM2000_MODE_GPIOS; i++) {
gpiod_set_value_cansleep(st->sw1_ch[i], (chan == 0) ? 1 : 0);
gpiod_set_value_cansleep(st->sw2_ch[i], (chan == 0) ? 0 : 1);
}
return 0;
case ADMFM2000_DIRECT_IF_MODE:
for (i = 0; i < ADMFM2000_MODE_GPIOS; i++) {
gpiod_set_value_cansleep(st->sw1_ch[i], (chan == 0) ? 0 : 1);
gpiod_set_value_cansleep(st->sw2_ch[i], (chan == 0) ? 1 : 0);
}
return 0;
default:
return -EINVAL;
}
}
static int admfm2000_attenuation(struct iio_dev *indio_dev, u32 chan, u32 value)
{
struct admfm2000_state *st = iio_priv(indio_dev);
int i;
switch (chan) {
case 0:
for (i = 0; i < ADMFM2000_DSA_GPIOS; i++)
gpiod_set_value_cansleep(st->dsa1_gpios[i], value & (1 << i));
return 0;
case 1:
for (i = 0; i < ADMFM2000_DSA_GPIOS; i++)
gpiod_set_value_cansleep(st->dsa2_gpios[i], value & (1 << i));
return 0;
default:
return -EINVAL;
}
}
static int admfm2000_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
struct admfm2000_state *st = iio_priv(indio_dev);
int gain;
switch (mask) {
case IIO_CHAN_INFO_HARDWAREGAIN:
mutex_lock(&st->lock);
gain = ~(st->gain[chan->channel]) * -1000;
*val = gain / 1000;
*val2 = (gain % 1000) * 1000;
mutex_unlock(&st->lock);
return IIO_VAL_INT_PLUS_MICRO_DB;
default:
return -EINVAL;
}
}
static int admfm2000_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val,
int val2, long mask)
{
struct admfm2000_state *st = iio_priv(indio_dev);
int gain, ret;
if (val < 0)
gain = (val * 1000) - (val2 / 1000);
else
gain = (val * 1000) + (val2 / 1000);
if (gain > ADMFM2000_MAX_GAIN || gain < ADMFM2000_MIN_GAIN)
return -EINVAL;
switch (mask) {
case IIO_CHAN_INFO_HARDWAREGAIN:
mutex_lock(&st->lock);
st->gain[chan->channel] = ~((abs(gain) / 1000) & 0x1F);
ret = admfm2000_attenuation(indio_dev, chan->channel,
st->gain[chan->channel]);
mutex_unlock(&st->lock);
return ret;
default:
return -EINVAL;
}
}
static int admfm2000_write_raw_get_fmt(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
long mask)
{
switch (mask) {
case IIO_CHAN_INFO_HARDWAREGAIN:
return IIO_VAL_INT_PLUS_MICRO_DB;
default:
return -EINVAL;
}
}
static const struct iio_info admfm2000_info = {
.read_raw = &admfm2000_read_raw,
.write_raw = &admfm2000_write_raw,
.write_raw_get_fmt = &admfm2000_write_raw_get_fmt,
};
#define ADMFM2000_CHAN(_channel) { \
.type = IIO_VOLTAGE, \
.output = 1, \
.indexed = 1, \
.channel = _channel, \
.info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN), \
}
static const struct iio_chan_spec admfm2000_channels[] = {
ADMFM2000_CHAN(0),
ADMFM2000_CHAN(1),
};
static int admfm2000_channel_config(struct admfm2000_state *st,
struct iio_dev *indio_dev)
{
struct platform_device *pdev = to_platform_device(indio_dev->dev.parent);
struct device *dev = &pdev->dev;
struct gpio_desc **dsa;
struct gpio_desc **sw;
int ret, i;
bool mode;
u32 reg;
device_for_each_child_node_scoped(dev, child) {
ret = fwnode_property_read_u32(child, "reg", &reg);
if (ret)
return dev_err_probe(dev, ret,
"Failed to get reg property\n");
if (reg >= indio_dev->num_channels)
return dev_err_probe(dev, -EINVAL, "reg bigger than: %d\n",
indio_dev->num_channels);
if (fwnode_property_present(child, "adi,mixer-mode"))
mode = ADMFM2000_MIXER_MODE;
else
mode = ADMFM2000_DIRECT_IF_MODE;
switch (reg) {
case 0:
sw = st->sw1_ch;
dsa = st->dsa1_gpios;
break;
case 1:
sw = st->sw2_ch;
dsa = st->dsa2_gpios;
break;
default:
return -EINVAL;
}
for (i = 0; i < ADMFM2000_MODE_GPIOS; i++) {
sw[i] = devm_fwnode_gpiod_get_index(dev, child, "switch",
i, GPIOD_OUT_LOW, NULL);
if (IS_ERR(sw[i]))
return dev_err_probe(dev, PTR_ERR(sw[i]),
"Failed to get gpios\n");
}
for (i = 0; i < ADMFM2000_DSA_GPIOS; i++) {
dsa[i] = devm_fwnode_gpiod_get_index(dev, child,
"attenuation", i,
GPIOD_OUT_LOW, NULL);
if (IS_ERR(dsa[i]))
return dev_err_probe(dev, PTR_ERR(dsa[i]),
"Failed to get gpios\n");
}
ret = admfm2000_mode(indio_dev, reg, mode);
if (ret)
return ret;
}
return 0;
}
static int admfm2000_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct admfm2000_state *st;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
st = iio_priv(indio_dev);
indio_dev->name = "admfm2000";
indio_dev->num_channels = ARRAY_SIZE(admfm2000_channels);
indio_dev->channels = admfm2000_channels;
indio_dev->info = &admfm2000_info;
indio_dev->modes = INDIO_DIRECT_MODE;
st->gain[0] = ADMFM2000_DEFAULT_GAIN;
st->gain[1] = ADMFM2000_DEFAULT_GAIN;
mutex_init(&st->lock);
ret = admfm2000_channel_config(st, indio_dev);
if (ret)
return ret;
return devm_iio_device_register(dev, indio_dev);
}
static const struct of_device_id admfm2000_of_match[] = {
{ .compatible = "adi,admfm2000" },
{ }
};
MODULE_DEVICE_TABLE(of, admfm2000_of_match);
static struct platform_driver admfm2000_driver = {
.driver = {
.name = "admfm2000",
.of_match_table = admfm2000_of_match,
},
.probe = admfm2000_probe,
};
module_platform_driver(admfm2000_driver);
MODULE_AUTHOR("Kim Seer Paller <kimseer.paller@analog.com>");
MODULE_DESCRIPTION("ADMFM2000 Dual Microwave Down Converter");
MODULE_LICENSE("GPL");
@@ -0,0 +1,645 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* ADMV1013 driver
*
* Copyright 2021 Analog Devices Inc.
*/
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/iio/iio.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#include <linux/units.h>
#include <linux/unaligned.h>
/* ADMV1013 Register Map */
#define ADMV1013_REG_SPI_CONTROL 0x00
#define ADMV1013_REG_ALARM 0x01
#define ADMV1013_REG_ALARM_MASKS 0x02
#define ADMV1013_REG_ENABLE 0x03
#define ADMV1013_REG_LO_AMP_I 0x05
#define ADMV1013_REG_LO_AMP_Q 0x06
#define ADMV1013_REG_OFFSET_ADJUST_I 0x07
#define ADMV1013_REG_OFFSET_ADJUST_Q 0x08
#define ADMV1013_REG_QUAD 0x09
#define ADMV1013_REG_VVA_TEMP_COMP 0x0A
/* ADMV1013_REG_SPI_CONTROL Map */
#define ADMV1013_PARITY_EN_MSK BIT(15)
#define ADMV1013_SPI_SOFT_RESET_MSK BIT(14)
#define ADMV1013_CHIP_ID_MSK GENMASK(11, 4)
#define ADMV1013_CHIP_ID 0xA
#define ADMV1013_REVISION_ID_MSK GENMASK(3, 0)
/* ADMV1013_REG_ALARM Map */
#define ADMV1013_PARITY_ERROR_MSK BIT(15)
#define ADMV1013_TOO_FEW_ERRORS_MSK BIT(14)
#define ADMV1013_TOO_MANY_ERRORS_MSK BIT(13)
#define ADMV1013_ADDRESS_RANGE_ERROR_MSK BIT(12)
/* ADMV1013_REG_ENABLE Map */
#define ADMV1013_VGA_PD_MSK BIT(15)
#define ADMV1013_MIXER_PD_MSK BIT(14)
#define ADMV1013_QUAD_PD_MSK GENMASK(13, 11)
#define ADMV1013_BG_PD_MSK BIT(10)
#define ADMV1013_MIXER_IF_EN_MSK BIT(7)
#define ADMV1013_DET_EN_MSK BIT(5)
/* ADMV1013_REG_LO_AMP Map */
#define ADMV1013_LOAMP_PH_ADJ_FINE_MSK GENMASK(13, 7)
#define ADMV1013_MIXER_VGATE_MSK GENMASK(6, 0)
/* ADMV1013_REG_OFFSET_ADJUST Map */
#define ADMV1013_MIXER_OFF_ADJ_P_MSK GENMASK(15, 9)
#define ADMV1013_MIXER_OFF_ADJ_N_MSK GENMASK(8, 2)
/* ADMV1013_REG_QUAD Map */
#define ADMV1013_QUAD_SE_MODE_MSK GENMASK(9, 6)
#define ADMV1013_QUAD_FILTERS_MSK GENMASK(3, 0)
/* ADMV1013_REG_VVA_TEMP_COMP Map */
#define ADMV1013_VVA_TEMP_COMP_MSK GENMASK(15, 0)
/* ADMV1013 Miscellaneous Defines */
#define ADMV1013_READ BIT(7)
#define ADMV1013_REG_ADDR_READ_MSK GENMASK(6, 1)
#define ADMV1013_REG_ADDR_WRITE_MSK GENMASK(22, 17)
#define ADMV1013_REG_DATA_MSK GENMASK(16, 1)
enum {
ADMV1013_IQ_MODE,
ADMV1013_IF_MODE
};
enum {
ADMV1013_RFMOD_I_CALIBPHASE,
ADMV1013_RFMOD_Q_CALIBPHASE,
};
enum {
ADMV1013_SE_MODE_POS,
ADMV1013_SE_MODE_NEG,
ADMV1013_SE_MODE_DIFF,
};
struct admv1013_state {
struct spi_device *spi;
struct clk *clkin;
/* Protect against concurrent accesses to the device and to data */
struct mutex lock;
struct notifier_block nb;
unsigned int input_mode;
unsigned int quad_se_mode;
bool det_en;
u8 data[3] __aligned(IIO_DMA_MINALIGN);
};
static int __admv1013_spi_read(struct admv1013_state *st, unsigned int reg,
unsigned int *val)
{
int ret;
struct spi_transfer t = {0};
st->data[0] = ADMV1013_READ | FIELD_PREP(ADMV1013_REG_ADDR_READ_MSK, reg);
st->data[1] = 0x0;
st->data[2] = 0x0;
t.rx_buf = &st->data[0];
t.tx_buf = &st->data[0];
t.len = 3;
ret = spi_sync_transfer(st->spi, &t, 1);
if (ret)
return ret;
*val = FIELD_GET(ADMV1013_REG_DATA_MSK, get_unaligned_be24(&st->data[0]));
return ret;
}
static int admv1013_spi_read(struct admv1013_state *st, unsigned int reg,
unsigned int *val)
{
int ret;
mutex_lock(&st->lock);
ret = __admv1013_spi_read(st, reg, val);
mutex_unlock(&st->lock);
return ret;
}
static int __admv1013_spi_write(struct admv1013_state *st,
unsigned int reg,
unsigned int val)
{
put_unaligned_be24(FIELD_PREP(ADMV1013_REG_DATA_MSK, val) |
FIELD_PREP(ADMV1013_REG_ADDR_WRITE_MSK, reg), &st->data[0]);
return spi_write(st->spi, &st->data[0], 3);
}
static int admv1013_spi_write(struct admv1013_state *st, unsigned int reg,
unsigned int val)
{
int ret;
mutex_lock(&st->lock);
ret = __admv1013_spi_write(st, reg, val);
mutex_unlock(&st->lock);
return ret;
}
static int __admv1013_spi_update_bits(struct admv1013_state *st, unsigned int reg,
unsigned int mask, unsigned int val)
{
int ret;
unsigned int data, temp;
ret = __admv1013_spi_read(st, reg, &data);
if (ret)
return ret;
temp = (data & ~mask) | (val & mask);
return __admv1013_spi_write(st, reg, temp);
}
static int admv1013_spi_update_bits(struct admv1013_state *st, unsigned int reg,
unsigned int mask, unsigned int val)
{
int ret;
mutex_lock(&st->lock);
ret = __admv1013_spi_update_bits(st, reg, mask, val);
mutex_unlock(&st->lock);
return ret;
}
static int admv1013_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long info)
{
struct admv1013_state *st = iio_priv(indio_dev);
unsigned int data, addr;
int ret;
switch (info) {
case IIO_CHAN_INFO_CALIBBIAS:
switch (chan->channel) {
case IIO_MOD_I:
addr = ADMV1013_REG_OFFSET_ADJUST_I;
break;
case IIO_MOD_Q:
addr = ADMV1013_REG_OFFSET_ADJUST_Q;
break;
default:
return -EINVAL;
}
ret = admv1013_spi_read(st, addr, &data);
if (ret)
return ret;
if (!chan->channel)
*val = FIELD_GET(ADMV1013_MIXER_OFF_ADJ_P_MSK, data);
else
*val = FIELD_GET(ADMV1013_MIXER_OFF_ADJ_N_MSK, data);
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static int admv1013_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long info)
{
struct admv1013_state *st = iio_priv(indio_dev);
unsigned int addr, data, msk;
switch (info) {
case IIO_CHAN_INFO_CALIBBIAS:
switch (chan->channel2) {
case IIO_MOD_I:
addr = ADMV1013_REG_OFFSET_ADJUST_I;
break;
case IIO_MOD_Q:
addr = ADMV1013_REG_OFFSET_ADJUST_Q;
break;
default:
return -EINVAL;
}
if (!chan->channel) {
msk = ADMV1013_MIXER_OFF_ADJ_P_MSK;
data = FIELD_PREP(ADMV1013_MIXER_OFF_ADJ_P_MSK, val);
} else {
msk = ADMV1013_MIXER_OFF_ADJ_N_MSK;
data = FIELD_PREP(ADMV1013_MIXER_OFF_ADJ_N_MSK, val);
}
return admv1013_spi_update_bits(st, addr, msk, data);
default:
return -EINVAL;
}
}
static ssize_t admv1013_read(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
char *buf)
{
struct admv1013_state *st = iio_priv(indio_dev);
unsigned int data, addr;
int ret;
switch ((u32)private) {
case ADMV1013_RFMOD_I_CALIBPHASE:
addr = ADMV1013_REG_LO_AMP_I;
break;
case ADMV1013_RFMOD_Q_CALIBPHASE:
addr = ADMV1013_REG_LO_AMP_Q;
break;
default:
return -EINVAL;
}
ret = admv1013_spi_read(st, addr, &data);
if (ret)
return ret;
data = FIELD_GET(ADMV1013_LOAMP_PH_ADJ_FINE_MSK, data);
return sysfs_emit(buf, "%u\n", data);
}
static ssize_t admv1013_write(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
const char *buf, size_t len)
{
struct admv1013_state *st = iio_priv(indio_dev);
unsigned int data;
int ret;
ret = kstrtou32(buf, 10, &data);
if (ret)
return ret;
data = FIELD_PREP(ADMV1013_LOAMP_PH_ADJ_FINE_MSK, data);
switch ((u32)private) {
case ADMV1013_RFMOD_I_CALIBPHASE:
ret = admv1013_spi_update_bits(st, ADMV1013_REG_LO_AMP_I,
ADMV1013_LOAMP_PH_ADJ_FINE_MSK,
data);
if (ret)
return ret;
break;
case ADMV1013_RFMOD_Q_CALIBPHASE:
ret = admv1013_spi_update_bits(st, ADMV1013_REG_LO_AMP_Q,
ADMV1013_LOAMP_PH_ADJ_FINE_MSK,
data);
if (ret)
return ret;
break;
default:
return -EINVAL;
}
return len;
}
static int admv1013_update_quad_filters(struct admv1013_state *st)
{
unsigned int filt_raw;
u64 rate = clk_get_rate(st->clkin);
if (rate >= (5400 * HZ_PER_MHZ) && rate <= (7000 * HZ_PER_MHZ))
filt_raw = 15;
else if (rate >= (5400 * HZ_PER_MHZ) && rate <= (8000 * HZ_PER_MHZ))
filt_raw = 10;
else if (rate >= (6600 * HZ_PER_MHZ) && rate <= (9200 * HZ_PER_MHZ))
filt_raw = 5;
else
filt_raw = 0;
return __admv1013_spi_update_bits(st, ADMV1013_REG_QUAD,
ADMV1013_QUAD_FILTERS_MSK,
FIELD_PREP(ADMV1013_QUAD_FILTERS_MSK, filt_raw));
}
static int admv1013_update_mixer_vgate(struct admv1013_state *st, int vcm)
{
unsigned int mixer_vgate;
if (vcm <= 1800000)
mixer_vgate = (2389 * vcm / 1000000 + 8100) / 100;
else if (vcm > 1800000 && vcm <= 2600000)
mixer_vgate = (2375 * vcm / 1000000 + 125) / 100;
else
return -EINVAL;
return __admv1013_spi_update_bits(st, ADMV1013_REG_LO_AMP_I,
ADMV1013_MIXER_VGATE_MSK,
FIELD_PREP(ADMV1013_MIXER_VGATE_MSK, mixer_vgate));
}
static int admv1013_reg_access(struct iio_dev *indio_dev,
unsigned int reg,
unsigned int write_val,
unsigned int *read_val)
{
struct admv1013_state *st = iio_priv(indio_dev);
if (read_val)
return admv1013_spi_read(st, reg, read_val);
else
return admv1013_spi_write(st, reg, write_val);
}
static const struct iio_info admv1013_info = {
.read_raw = admv1013_read_raw,
.write_raw = admv1013_write_raw,
.debugfs_reg_access = &admv1013_reg_access,
};
static const char * const admv1013_vcc_regs[] = {
"vcc-drv", "vcc2-drv", "vcc-vva", "vcc-amp1", "vcc-amp2",
"vcc-env", "vcc-bg", "vcc-bg2", "vcc-mixer", "vcc-quad"
};
static int admv1013_freq_change(struct notifier_block *nb, unsigned long action, void *data)
{
struct admv1013_state *st = container_of(nb, struct admv1013_state, nb);
int ret;
if (action == POST_RATE_CHANGE) {
mutex_lock(&st->lock);
ret = notifier_from_errno(admv1013_update_quad_filters(st));
mutex_unlock(&st->lock);
return ret;
}
return NOTIFY_OK;
}
#define _ADMV1013_EXT_INFO(_name, _shared, _ident) { \
.name = _name, \
.read = admv1013_read, \
.write = admv1013_write, \
.private = _ident, \
.shared = _shared, \
}
static const struct iio_chan_spec_ext_info admv1013_ext_info[] = {
_ADMV1013_EXT_INFO("i_calibphase", IIO_SEPARATE, ADMV1013_RFMOD_I_CALIBPHASE),
_ADMV1013_EXT_INFO("q_calibphase", IIO_SEPARATE, ADMV1013_RFMOD_Q_CALIBPHASE),
{ }
};
#define ADMV1013_CHAN_PHASE(_channel, _channel2, _admv1013_ext_info) { \
.type = IIO_ALTVOLTAGE, \
.output = 0, \
.indexed = 1, \
.channel2 = _channel2, \
.channel = _channel, \
.differential = 1, \
.ext_info = _admv1013_ext_info, \
}
#define ADMV1013_CHAN_CALIB(_channel, rf_comp) { \
.type = IIO_ALTVOLTAGE, \
.output = 0, \
.indexed = 1, \
.channel = _channel, \
.channel2 = IIO_MOD_##rf_comp, \
.info_mask_separate = BIT(IIO_CHAN_INFO_CALIBBIAS), \
}
static const struct iio_chan_spec admv1013_channels[] = {
ADMV1013_CHAN_PHASE(0, 1, admv1013_ext_info),
ADMV1013_CHAN_CALIB(0, I),
ADMV1013_CHAN_CALIB(0, Q),
ADMV1013_CHAN_CALIB(1, I),
ADMV1013_CHAN_CALIB(1, Q),
};
static int admv1013_init(struct admv1013_state *st, int vcm_uv)
{
int ret;
unsigned int data;
struct device *dev = &st->spi->dev;
/* Perform a software reset */
ret = __admv1013_spi_update_bits(st, ADMV1013_REG_SPI_CONTROL,
ADMV1013_SPI_SOFT_RESET_MSK,
FIELD_PREP(ADMV1013_SPI_SOFT_RESET_MSK, 1));
if (ret)
return ret;
ret = __admv1013_spi_update_bits(st, ADMV1013_REG_SPI_CONTROL,
ADMV1013_SPI_SOFT_RESET_MSK,
FIELD_PREP(ADMV1013_SPI_SOFT_RESET_MSK, 0));
if (ret)
return ret;
ret = __admv1013_spi_read(st, ADMV1013_REG_SPI_CONTROL, &data);
if (ret)
return ret;
data = FIELD_GET(ADMV1013_CHIP_ID_MSK, data);
if (data != ADMV1013_CHIP_ID)
return dev_err_probe(dev, -EINVAL, "Invalid Chip ID.\n");
ret = __admv1013_spi_write(st, ADMV1013_REG_VVA_TEMP_COMP, 0xE700);
if (ret)
return ret;
switch (st->quad_se_mode) {
case ADMV1013_SE_MODE_POS:
data = 6;
break;
case ADMV1013_SE_MODE_NEG:
data = 9;
break;
case ADMV1013_SE_MODE_DIFF:
data = 12;
break;
default:
return -EINVAL;
}
ret = __admv1013_spi_update_bits(st, ADMV1013_REG_QUAD,
ADMV1013_QUAD_SE_MODE_MSK,
FIELD_PREP(ADMV1013_QUAD_SE_MODE_MSK, data));
if (ret)
return ret;
ret = admv1013_update_mixer_vgate(st, vcm_uv);
if (ret)
return ret;
ret = admv1013_update_quad_filters(st);
if (ret)
return ret;
return __admv1013_spi_update_bits(st, ADMV1013_REG_ENABLE,
ADMV1013_DET_EN_MSK |
ADMV1013_MIXER_IF_EN_MSK,
st->det_en |
st->input_mode);
}
static void admv1013_powerdown(void *data)
{
unsigned int enable_reg, enable_reg_msk;
/* Disable all components in the Enable Register */
enable_reg_msk = ADMV1013_VGA_PD_MSK |
ADMV1013_MIXER_PD_MSK |
ADMV1013_QUAD_PD_MSK |
ADMV1013_BG_PD_MSK |
ADMV1013_MIXER_IF_EN_MSK |
ADMV1013_DET_EN_MSK;
enable_reg = FIELD_PREP(ADMV1013_VGA_PD_MSK, 1) |
FIELD_PREP(ADMV1013_MIXER_PD_MSK, 1) |
FIELD_PREP(ADMV1013_QUAD_PD_MSK, 7) |
FIELD_PREP(ADMV1013_BG_PD_MSK, 1) |
FIELD_PREP(ADMV1013_MIXER_IF_EN_MSK, 0) |
FIELD_PREP(ADMV1013_DET_EN_MSK, 0);
admv1013_spi_update_bits(data, ADMV1013_REG_ENABLE, enable_reg_msk, enable_reg);
}
static const char * const admv1013_input_modes[] = {
[ADMV1013_IQ_MODE] = "iq",
[ADMV1013_IF_MODE] = "if",
};
static const char * const admv1013_quad_se_modes[] = {
[ADMV1013_SE_MODE_POS] = "se-pos",
[ADMV1013_SE_MODE_NEG] = "se-neg",
[ADMV1013_SE_MODE_DIFF] = "diff",
};
static int admv1013_properties_parse(struct admv1013_state *st)
{
int ret;
struct device *dev = &st->spi->dev;
st->det_en = device_property_read_bool(dev, "adi,detector-enable");
ret = device_property_match_property_string(dev, "adi,input-mode",
admv1013_input_modes,
ARRAY_SIZE(admv1013_input_modes));
st->input_mode = ret >= 0 ? ret : ADMV1013_IQ_MODE;
ret = device_property_match_property_string(dev, "adi,quad-se-mode",
admv1013_quad_se_modes,
ARRAY_SIZE(admv1013_quad_se_modes));
st->quad_se_mode = ret >= 0 ? ret : ADMV1013_SE_MODE_DIFF;
ret = devm_regulator_bulk_get_enable(dev,
ARRAY_SIZE(admv1013_vcc_regs),
admv1013_vcc_regs);
if (ret) {
dev_err_probe(dev, ret,
"Failed to request VCC regulators\n");
return ret;
}
return 0;
}
static int admv1013_probe(struct spi_device *spi)
{
struct iio_dev *indio_dev;
struct admv1013_state *st;
struct device *dev = &spi->dev;
int ret, vcm_uv;
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
st = iio_priv(indio_dev);
indio_dev->info = &admv1013_info;
indio_dev->name = "admv1013";
indio_dev->channels = admv1013_channels;
indio_dev->num_channels = ARRAY_SIZE(admv1013_channels);
st->spi = spi;
ret = admv1013_properties_parse(st);
if (ret)
return ret;
ret = devm_regulator_get_enable_read_voltage(dev, "vcm");
if (ret < 0)
return dev_err_probe(dev, ret,
"failed to get the common-mode voltage\n");
vcm_uv = ret;
st->clkin = devm_clk_get_enabled(dev, "lo_in");
if (IS_ERR(st->clkin))
return dev_err_probe(dev, PTR_ERR(st->clkin),
"failed to get the LO input clock\n");
st->nb.notifier_call = admv1013_freq_change;
ret = devm_clk_notifier_register(dev, st->clkin, &st->nb);
if (ret)
return ret;
mutex_init(&st->lock);
ret = admv1013_init(st, vcm_uv);
if (ret)
return dev_err_probe(dev, ret, "admv1013 init failed\n");
ret = devm_add_action_or_reset(dev, admv1013_powerdown, st);
if (ret)
return ret;
return devm_iio_device_register(dev, indio_dev);
}
static const struct spi_device_id admv1013_id[] = {
{ "admv1013", 0 },
{ }
};
MODULE_DEVICE_TABLE(spi, admv1013_id);
static const struct of_device_id admv1013_of_match[] = {
{ .compatible = "adi,admv1013" },
{ }
};
MODULE_DEVICE_TABLE(of, admv1013_of_match);
static struct spi_driver admv1013_driver = {
.driver = {
.name = "admv1013",
.of_match_table = admv1013_of_match,
},
.probe = admv1013_probe,
.id_table = admv1013_id,
};
module_spi_driver(admv1013_driver);
MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com");
MODULE_DESCRIPTION("Analog Devices ADMV1013");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,807 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* ADMV1014 driver
*
* Copyright 2022 Analog Devices Inc.
*/
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/device.h>
#include <linux/iio/iio.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#include <linux/units.h>
#include <linux/unaligned.h>
/* ADMV1014 Register Map */
#define ADMV1014_REG_SPI_CONTROL 0x00
#define ADMV1014_REG_ALARM 0x01
#define ADMV1014_REG_ALARM_MASKS 0x02
#define ADMV1014_REG_ENABLE 0x03
#define ADMV1014_REG_QUAD 0x04
#define ADMV1014_REG_LO_AMP_PHASE_ADJUST1 0x05
#define ADMV1014_REG_MIXER 0x07
#define ADMV1014_REG_IF_AMP 0x08
#define ADMV1014_REG_IF_AMP_BB_AMP 0x09
#define ADMV1014_REG_BB_AMP_AGC 0x0A
#define ADMV1014_REG_VVA_TEMP_COMP 0x0B
/* ADMV1014_REG_SPI_CONTROL Map */
#define ADMV1014_PARITY_EN_MSK BIT(15)
#define ADMV1014_SPI_SOFT_RESET_MSK BIT(14)
#define ADMV1014_CHIP_ID_MSK GENMASK(11, 4)
#define ADMV1014_CHIP_ID 0x9
#define ADMV1014_REVISION_ID_MSK GENMASK(3, 0)
/* ADMV1014_REG_ALARM Map */
#define ADMV1014_PARITY_ERROR_MSK BIT(15)
#define ADMV1014_TOO_FEW_ERRORS_MSK BIT(14)
#define ADMV1014_TOO_MANY_ERRORS_MSK BIT(13)
#define ADMV1014_ADDRESS_RANGE_ERROR_MSK BIT(12)
/* ADMV1014_REG_ENABLE Map */
#define ADMV1014_IBIAS_PD_MSK BIT(14)
#define ADMV1014_P1DB_COMPENSATION_MSK GENMASK(13, 12)
#define ADMV1014_IF_AMP_PD_MSK BIT(11)
#define ADMV1014_QUAD_BG_PD_MSK BIT(9)
#define ADMV1014_BB_AMP_PD_MSK BIT(8)
#define ADMV1014_QUAD_IBIAS_PD_MSK BIT(7)
#define ADMV1014_DET_EN_MSK BIT(6)
#define ADMV1014_BG_PD_MSK BIT(5)
/* ADMV1014_REG_QUAD Map */
#define ADMV1014_QUAD_SE_MODE_MSK GENMASK(9, 6)
#define ADMV1014_QUAD_FILTERS_MSK GENMASK(3, 0)
/* ADMV1014_REG_LO_AMP_PHASE_ADJUST1 Map */
#define ADMV1014_LOAMP_PH_ADJ_I_FINE_MSK GENMASK(15, 9)
#define ADMV1014_LOAMP_PH_ADJ_Q_FINE_MSK GENMASK(8, 2)
/* ADMV1014_REG_MIXER Map */
#define ADMV1014_MIXER_VGATE_MSK GENMASK(15, 9)
#define ADMV1014_DET_PROG_MSK GENMASK(6, 0)
/* ADMV1014_REG_IF_AMP Map */
#define ADMV1014_IF_AMP_COARSE_GAIN_I_MSK GENMASK(11, 8)
#define ADMV1014_IF_AMP_FINE_GAIN_Q_MSK GENMASK(7, 4)
#define ADMV1014_IF_AMP_FINE_GAIN_I_MSK GENMASK(3, 0)
/* ADMV1014_REG_IF_AMP_BB_AMP Map */
#define ADMV1014_IF_AMP_COARSE_GAIN_Q_MSK GENMASK(15, 12)
#define ADMV1014_BB_AMP_OFFSET_Q_MSK GENMASK(9, 5)
#define ADMV1014_BB_AMP_OFFSET_I_MSK GENMASK(4, 0)
/* ADMV1014_REG_BB_AMP_AGC Map */
#define ADMV1014_BB_AMP_REF_GEN_MSK GENMASK(6, 3)
#define ADMV1014_BB_AMP_GAIN_CTRL_MSK GENMASK(2, 1)
#define ADMV1014_BB_SWITCH_HIGH_LOW_CM_MSK BIT(0)
/* ADMV1014_REG_VVA_TEMP_COMP Map */
#define ADMV1014_VVA_TEMP_COMP_MSK GENMASK(15, 0)
/* ADMV1014 Miscellaneous Defines */
#define ADMV1014_READ BIT(7)
#define ADMV1014_REG_ADDR_READ_MSK GENMASK(6, 1)
#define ADMV1014_REG_ADDR_WRITE_MSK GENMASK(22, 17)
#define ADMV1014_REG_DATA_MSK GENMASK(16, 1)
#define ADMV1014_NUM_REGULATORS 9
enum {
ADMV1014_IQ_MODE,
ADMV1014_IF_MODE,
};
enum {
ADMV1014_SE_MODE_POS = 6,
ADMV1014_SE_MODE_NEG = 9,
ADMV1014_SE_MODE_DIFF = 12,
};
enum {
ADMV1014_CALIBSCALE_COARSE,
ADMV1014_CALIBSCALE_FINE,
};
static const int detector_table[] = {0, 1, 2, 4, 8, 16, 32, 64};
static const char * const input_mode_names[] = { "iq", "if" };
static const char * const quad_se_mode_names[] = { "se-pos", "se-neg", "diff" };
struct admv1014_state {
struct spi_device *spi;
struct clk *clkin;
struct notifier_block nb;
/* Protect against concurrent accesses to the device and to data*/
struct mutex lock;
struct regulator_bulk_data regulators[ADMV1014_NUM_REGULATORS];
unsigned int input_mode;
unsigned int quad_se_mode;
unsigned int p1db_comp;
bool det_en;
u8 data[3] __aligned(IIO_DMA_MINALIGN);
};
static const int mixer_vgate_table[] = {106, 107, 108, 110, 111, 112, 113, 114,
117, 118, 119, 120, 122, 123, 44, 45};
static int __admv1014_spi_read(struct admv1014_state *st, unsigned int reg,
unsigned int *val)
{
struct spi_transfer t = {};
int ret;
st->data[0] = ADMV1014_READ | FIELD_PREP(ADMV1014_REG_ADDR_READ_MSK, reg);
st->data[1] = 0;
st->data[2] = 0;
t.rx_buf = &st->data[0];
t.tx_buf = &st->data[0];
t.len = sizeof(st->data);
ret = spi_sync_transfer(st->spi, &t, 1);
if (ret)
return ret;
*val = FIELD_GET(ADMV1014_REG_DATA_MSK, get_unaligned_be24(&st->data[0]));
return ret;
}
static int admv1014_spi_read(struct admv1014_state *st, unsigned int reg,
unsigned int *val)
{
int ret;
mutex_lock(&st->lock);
ret = __admv1014_spi_read(st, reg, val);
mutex_unlock(&st->lock);
return ret;
}
static int __admv1014_spi_write(struct admv1014_state *st,
unsigned int reg,
unsigned int val)
{
put_unaligned_be24(FIELD_PREP(ADMV1014_REG_DATA_MSK, val) |
FIELD_PREP(ADMV1014_REG_ADDR_WRITE_MSK, reg), &st->data[0]);
return spi_write(st->spi, &st->data[0], 3);
}
static int admv1014_spi_write(struct admv1014_state *st, unsigned int reg,
unsigned int val)
{
int ret;
mutex_lock(&st->lock);
ret = __admv1014_spi_write(st, reg, val);
mutex_unlock(&st->lock);
return ret;
}
static int __admv1014_spi_update_bits(struct admv1014_state *st, unsigned int reg,
unsigned int mask, unsigned int val)
{
unsigned int data, temp;
int ret;
ret = __admv1014_spi_read(st, reg, &data);
if (ret)
return ret;
temp = (data & ~mask) | (val & mask);
return __admv1014_spi_write(st, reg, temp);
}
static int admv1014_spi_update_bits(struct admv1014_state *st, unsigned int reg,
unsigned int mask, unsigned int val)
{
int ret;
mutex_lock(&st->lock);
ret = __admv1014_spi_update_bits(st, reg, mask, val);
mutex_unlock(&st->lock);
return ret;
}
static int admv1014_update_quad_filters(struct admv1014_state *st)
{
unsigned int filt_raw;
u64 rate = clk_get_rate(st->clkin);
if (rate >= (5400 * HZ_PER_MHZ) && rate <= (7000 * HZ_PER_MHZ))
filt_raw = 15;
else if (rate > (7000 * HZ_PER_MHZ) && rate <= (8000 * HZ_PER_MHZ))
filt_raw = 10;
else if (rate > (8000 * HZ_PER_MHZ) && rate <= (9200 * HZ_PER_MHZ))
filt_raw = 5;
else
filt_raw = 0;
return __admv1014_spi_update_bits(st, ADMV1014_REG_QUAD,
ADMV1014_QUAD_FILTERS_MSK,
FIELD_PREP(ADMV1014_QUAD_FILTERS_MSK, filt_raw));
}
static int admv1014_update_vcm_settings(struct admv1014_state *st)
{
unsigned int i, vcm_mv, vcm_comp, bb_sw_hl_cm;
int ret;
vcm_mv = regulator_get_voltage(st->regulators[0].consumer) / 1000;
for (i = 0; i < ARRAY_SIZE(mixer_vgate_table); i++) {
vcm_comp = 1050 + mult_frac(i, 450, 8);
if (vcm_mv != vcm_comp)
continue;
ret = __admv1014_spi_update_bits(st, ADMV1014_REG_MIXER,
ADMV1014_MIXER_VGATE_MSK,
FIELD_PREP(ADMV1014_MIXER_VGATE_MSK,
mixer_vgate_table[i]));
if (ret)
return ret;
bb_sw_hl_cm = ~(i / 8);
bb_sw_hl_cm = FIELD_PREP(ADMV1014_BB_SWITCH_HIGH_LOW_CM_MSK, bb_sw_hl_cm);
return __admv1014_spi_update_bits(st, ADMV1014_REG_BB_AMP_AGC,
ADMV1014_BB_AMP_REF_GEN_MSK |
ADMV1014_BB_SWITCH_HIGH_LOW_CM_MSK,
FIELD_PREP(ADMV1014_BB_AMP_REF_GEN_MSK, i) |
bb_sw_hl_cm);
}
return -EINVAL;
}
static int admv1014_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long info)
{
struct admv1014_state *st = iio_priv(indio_dev);
unsigned int data;
int ret;
switch (info) {
case IIO_CHAN_INFO_OFFSET:
ret = admv1014_spi_read(st, ADMV1014_REG_IF_AMP_BB_AMP, &data);
if (ret)
return ret;
if (chan->channel2 == IIO_MOD_I)
*val = FIELD_GET(ADMV1014_BB_AMP_OFFSET_I_MSK, data);
else
*val = FIELD_GET(ADMV1014_BB_AMP_OFFSET_Q_MSK, data);
return IIO_VAL_INT;
case IIO_CHAN_INFO_PHASE:
ret = admv1014_spi_read(st, ADMV1014_REG_LO_AMP_PHASE_ADJUST1, &data);
if (ret)
return ret;
if (chan->channel2 == IIO_MOD_I)
*val = FIELD_GET(ADMV1014_LOAMP_PH_ADJ_I_FINE_MSK, data);
else
*val = FIELD_GET(ADMV1014_LOAMP_PH_ADJ_Q_FINE_MSK, data);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
ret = admv1014_spi_read(st, ADMV1014_REG_MIXER, &data);
if (ret)
return ret;
*val = FIELD_GET(ADMV1014_DET_PROG_MSK, data);
return IIO_VAL_INT;
case IIO_CHAN_INFO_CALIBSCALE:
ret = admv1014_spi_read(st, ADMV1014_REG_BB_AMP_AGC, &data);
if (ret)
return ret;
*val = FIELD_GET(ADMV1014_BB_AMP_GAIN_CTRL_MSK, data);
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static int admv1014_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long info)
{
int data;
unsigned int msk;
struct admv1014_state *st = iio_priv(indio_dev);
switch (info) {
case IIO_CHAN_INFO_OFFSET:
if (chan->channel2 == IIO_MOD_I) {
msk = ADMV1014_BB_AMP_OFFSET_I_MSK;
data = FIELD_PREP(ADMV1014_BB_AMP_OFFSET_I_MSK, val);
} else {
msk = ADMV1014_BB_AMP_OFFSET_Q_MSK;
data = FIELD_PREP(ADMV1014_BB_AMP_OFFSET_Q_MSK, val);
}
return admv1014_spi_update_bits(st, ADMV1014_REG_IF_AMP_BB_AMP, msk, data);
case IIO_CHAN_INFO_PHASE:
if (chan->channel2 == IIO_MOD_I) {
msk = ADMV1014_LOAMP_PH_ADJ_I_FINE_MSK;
data = FIELD_PREP(ADMV1014_LOAMP_PH_ADJ_I_FINE_MSK, val);
} else {
msk = ADMV1014_LOAMP_PH_ADJ_Q_FINE_MSK;
data = FIELD_PREP(ADMV1014_LOAMP_PH_ADJ_Q_FINE_MSK, val);
}
return admv1014_spi_update_bits(st, ADMV1014_REG_LO_AMP_PHASE_ADJUST1, msk, data);
case IIO_CHAN_INFO_SCALE:
return admv1014_spi_update_bits(st, ADMV1014_REG_MIXER,
ADMV1014_DET_PROG_MSK,
FIELD_PREP(ADMV1014_DET_PROG_MSK, val));
case IIO_CHAN_INFO_CALIBSCALE:
return admv1014_spi_update_bits(st, ADMV1014_REG_BB_AMP_AGC,
ADMV1014_BB_AMP_GAIN_CTRL_MSK,
FIELD_PREP(ADMV1014_BB_AMP_GAIN_CTRL_MSK, val));
default:
return -EINVAL;
}
}
static ssize_t admv1014_read(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
char *buf)
{
struct admv1014_state *st = iio_priv(indio_dev);
unsigned int data;
int ret;
switch (private) {
case ADMV1014_CALIBSCALE_COARSE:
if (chan->channel2 == IIO_MOD_I) {
ret = admv1014_spi_read(st, ADMV1014_REG_IF_AMP, &data);
if (ret)
return ret;
data = FIELD_GET(ADMV1014_IF_AMP_COARSE_GAIN_I_MSK, data);
} else {
ret = admv1014_spi_read(st, ADMV1014_REG_IF_AMP_BB_AMP, &data);
if (ret)
return ret;
data = FIELD_GET(ADMV1014_IF_AMP_COARSE_GAIN_Q_MSK, data);
}
break;
case ADMV1014_CALIBSCALE_FINE:
ret = admv1014_spi_read(st, ADMV1014_REG_IF_AMP, &data);
if (ret)
return ret;
if (chan->channel2 == IIO_MOD_I)
data = FIELD_GET(ADMV1014_IF_AMP_FINE_GAIN_I_MSK, data);
else
data = FIELD_GET(ADMV1014_IF_AMP_FINE_GAIN_Q_MSK, data);
break;
default:
return -EINVAL;
}
return sysfs_emit(buf, "%u\n", data);
}
static ssize_t admv1014_write(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
const char *buf, size_t len)
{
struct admv1014_state *st = iio_priv(indio_dev);
unsigned int data, addr, msk;
int ret;
ret = kstrtouint(buf, 10, &data);
if (ret)
return ret;
switch (private) {
case ADMV1014_CALIBSCALE_COARSE:
if (chan->channel2 == IIO_MOD_I) {
addr = ADMV1014_REG_IF_AMP;
msk = ADMV1014_IF_AMP_COARSE_GAIN_I_MSK;
data = FIELD_PREP(ADMV1014_IF_AMP_COARSE_GAIN_I_MSK, data);
} else {
addr = ADMV1014_REG_IF_AMP_BB_AMP;
msk = ADMV1014_IF_AMP_COARSE_GAIN_Q_MSK;
data = FIELD_PREP(ADMV1014_IF_AMP_COARSE_GAIN_Q_MSK, data);
}
break;
case ADMV1014_CALIBSCALE_FINE:
addr = ADMV1014_REG_IF_AMP;
if (chan->channel2 == IIO_MOD_I) {
msk = ADMV1014_IF_AMP_FINE_GAIN_I_MSK;
data = FIELD_PREP(ADMV1014_IF_AMP_FINE_GAIN_I_MSK, data);
} else {
msk = ADMV1014_IF_AMP_FINE_GAIN_Q_MSK;
data = FIELD_PREP(ADMV1014_IF_AMP_FINE_GAIN_Q_MSK, data);
}
break;
default:
return -EINVAL;
}
ret = admv1014_spi_update_bits(st, addr, msk, data);
return ret ? ret : len;
}
static int admv1014_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals, int *type, int *length,
long info)
{
switch (info) {
case IIO_CHAN_INFO_SCALE:
*vals = detector_table;
*type = IIO_VAL_INT;
*length = ARRAY_SIZE(detector_table);
return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
}
static int admv1014_reg_access(struct iio_dev *indio_dev,
unsigned int reg,
unsigned int write_val,
unsigned int *read_val)
{
struct admv1014_state *st = iio_priv(indio_dev);
if (read_val)
return admv1014_spi_read(st, reg, read_val);
else
return admv1014_spi_write(st, reg, write_val);
}
static const struct iio_info admv1014_info = {
.read_raw = admv1014_read_raw,
.write_raw = admv1014_write_raw,
.read_avail = &admv1014_read_avail,
.debugfs_reg_access = &admv1014_reg_access,
};
static const char * const admv1014_reg_name[] = {
"vcm", "vcc-if-bb", "vcc-vga", "vcc-vva", "vcc-lna-3p3",
"vcc-lna-1p5", "vcc-bg", "vcc-quad", "vcc-mixer"
};
static int admv1014_freq_change(struct notifier_block *nb, unsigned long action, void *data)
{
struct admv1014_state *st = container_of(nb, struct admv1014_state, nb);
int ret;
if (action == POST_RATE_CHANGE) {
mutex_lock(&st->lock);
ret = notifier_from_errno(admv1014_update_quad_filters(st));
mutex_unlock(&st->lock);
return ret;
}
return NOTIFY_OK;
}
#define _ADMV1014_EXT_INFO(_name, _shared, _ident) { \
.name = _name, \
.read = admv1014_read, \
.write = admv1014_write, \
.private = _ident, \
.shared = _shared, \
}
static const struct iio_chan_spec_ext_info admv1014_ext_info[] = {
_ADMV1014_EXT_INFO("calibscale_coarse", IIO_SEPARATE, ADMV1014_CALIBSCALE_COARSE),
_ADMV1014_EXT_INFO("calibscale_fine", IIO_SEPARATE, ADMV1014_CALIBSCALE_FINE),
{ }
};
#define ADMV1014_CHAN_IQ(_channel, rf_comp) { \
.type = IIO_ALTVOLTAGE, \
.modified = 1, \
.output = 0, \
.indexed = 1, \
.channel2 = IIO_MOD_##rf_comp, \
.channel = _channel, \
.info_mask_separate = BIT(IIO_CHAN_INFO_PHASE) | \
BIT(IIO_CHAN_INFO_OFFSET), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBSCALE), \
}
#define ADMV1014_CHAN_IF(_channel, rf_comp) { \
.type = IIO_ALTVOLTAGE, \
.modified = 1, \
.output = 0, \
.indexed = 1, \
.channel2 = IIO_MOD_##rf_comp, \
.channel = _channel, \
.info_mask_separate = BIT(IIO_CHAN_INFO_PHASE) | \
BIT(IIO_CHAN_INFO_OFFSET), \
}
#define ADMV1014_CHAN_POWER(_channel) { \
.type = IIO_POWER, \
.output = 0, \
.indexed = 1, \
.channel = _channel, \
.info_mask_separate = BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \
}
#define ADMV1014_CHAN_CALIBSCALE(_channel, rf_comp, _admv1014_ext_info) { \
.type = IIO_ALTVOLTAGE, \
.modified = 1, \
.output = 0, \
.indexed = 1, \
.channel2 = IIO_MOD_##rf_comp, \
.channel = _channel, \
.ext_info = _admv1014_ext_info, \
}
static const struct iio_chan_spec admv1014_channels_iq[] = {
ADMV1014_CHAN_IQ(0, I),
ADMV1014_CHAN_IQ(0, Q),
ADMV1014_CHAN_POWER(0),
};
static const struct iio_chan_spec admv1014_channels_if[] = {
ADMV1014_CHAN_IF(0, I),
ADMV1014_CHAN_IF(0, Q),
ADMV1014_CHAN_CALIBSCALE(0, I, admv1014_ext_info),
ADMV1014_CHAN_CALIBSCALE(0, Q, admv1014_ext_info),
ADMV1014_CHAN_POWER(0),
};
static void admv1014_clk_disable(void *data)
{
clk_disable_unprepare(data);
}
static void admv1014_reg_disable(void *data)
{
regulator_bulk_disable(ADMV1014_NUM_REGULATORS, data);
}
static void admv1014_powerdown(void *data)
{
unsigned int enable_reg, enable_reg_msk;
/* Disable all components in the Enable Register */
enable_reg_msk = ADMV1014_IBIAS_PD_MSK |
ADMV1014_IF_AMP_PD_MSK |
ADMV1014_QUAD_BG_PD_MSK |
ADMV1014_BB_AMP_PD_MSK |
ADMV1014_QUAD_IBIAS_PD_MSK |
ADMV1014_BG_PD_MSK;
enable_reg = FIELD_PREP(ADMV1014_IBIAS_PD_MSK, 1) |
FIELD_PREP(ADMV1014_IF_AMP_PD_MSK, 1) |
FIELD_PREP(ADMV1014_QUAD_BG_PD_MSK, 1) |
FIELD_PREP(ADMV1014_BB_AMP_PD_MSK, 1) |
FIELD_PREP(ADMV1014_QUAD_IBIAS_PD_MSK, 1) |
FIELD_PREP(ADMV1014_BG_PD_MSK, 1);
admv1014_spi_update_bits(data, ADMV1014_REG_ENABLE,
enable_reg_msk, enable_reg);
}
static int admv1014_init(struct admv1014_state *st)
{
unsigned int chip_id, enable_reg, enable_reg_msk;
struct spi_device *spi = st->spi;
struct device *dev = &spi->dev;
int ret;
ret = regulator_bulk_enable(ADMV1014_NUM_REGULATORS, st->regulators);
if (ret)
return dev_err_probe(dev, ret, "Failed to enable regulators");
ret = devm_add_action_or_reset(dev, admv1014_reg_disable, st->regulators);
if (ret)
return ret;
ret = clk_prepare_enable(st->clkin);
if (ret)
return ret;
ret = devm_add_action_or_reset(dev, admv1014_clk_disable, st->clkin);
if (ret)
return ret;
st->nb.notifier_call = admv1014_freq_change;
ret = devm_clk_notifier_register(dev, st->clkin, &st->nb);
if (ret)
return ret;
ret = devm_add_action_or_reset(dev, admv1014_powerdown, st);
if (ret)
return ret;
/* Perform a software reset */
ret = __admv1014_spi_update_bits(st, ADMV1014_REG_SPI_CONTROL,
ADMV1014_SPI_SOFT_RESET_MSK,
FIELD_PREP(ADMV1014_SPI_SOFT_RESET_MSK, 1));
if (ret)
return dev_err_probe(dev, ret,
"ADMV1014 SPI software reset failed.\n");
ret = __admv1014_spi_update_bits(st, ADMV1014_REG_SPI_CONTROL,
ADMV1014_SPI_SOFT_RESET_MSK,
FIELD_PREP(ADMV1014_SPI_SOFT_RESET_MSK, 0));
if (ret)
return dev_err_probe(dev, ret,
"ADMV1014 SPI software reset disable failed.\n");
ret = __admv1014_spi_write(st, ADMV1014_REG_VVA_TEMP_COMP, 0x727C);
if (ret)
return dev_err_probe(dev, ret,
"Writing default Temperature Compensation value failed.\n");
ret = __admv1014_spi_read(st, ADMV1014_REG_SPI_CONTROL, &chip_id);
if (ret)
return ret;
chip_id = FIELD_GET(ADMV1014_CHIP_ID_MSK, chip_id);
if (chip_id != ADMV1014_CHIP_ID)
return dev_err_probe(dev, -EINVAL, "Invalid Chip ID.\n");
ret = __admv1014_spi_update_bits(st, ADMV1014_REG_QUAD,
ADMV1014_QUAD_SE_MODE_MSK,
FIELD_PREP(ADMV1014_QUAD_SE_MODE_MSK,
st->quad_se_mode));
if (ret)
return dev_err_probe(dev, ret,
"Writing Quad SE Mode failed.\n");
ret = admv1014_update_quad_filters(st);
if (ret)
return dev_err_probe(dev, ret,
"Update Quad Filters failed.\n");
ret = admv1014_update_vcm_settings(st);
if (ret)
return dev_err_probe(dev, ret,
"Update VCM Settings failed.\n");
enable_reg_msk = ADMV1014_P1DB_COMPENSATION_MSK |
ADMV1014_IF_AMP_PD_MSK |
ADMV1014_BB_AMP_PD_MSK |
ADMV1014_DET_EN_MSK;
enable_reg = FIELD_PREP(ADMV1014_P1DB_COMPENSATION_MSK, st->p1db_comp ? 3 : 0) |
FIELD_PREP(ADMV1014_IF_AMP_PD_MSK,
(st->input_mode == ADMV1014_IF_MODE) ? 0 : 1) |
FIELD_PREP(ADMV1014_BB_AMP_PD_MSK,
(st->input_mode == ADMV1014_IF_MODE) ? 1 : 0) |
FIELD_PREP(ADMV1014_DET_EN_MSK, st->det_en);
return __admv1014_spi_update_bits(st, ADMV1014_REG_ENABLE, enable_reg_msk, enable_reg);
}
static int admv1014_properties_parse(struct admv1014_state *st)
{
unsigned int i;
struct spi_device *spi = st->spi;
struct device *dev = &spi->dev;
int ret;
st->det_en = device_property_read_bool(dev, "adi,detector-enable");
st->p1db_comp = device_property_read_bool(dev, "adi,p1db-compensation-enable");
ret = device_property_match_property_string(dev, "adi,input-mode",
input_mode_names,
ARRAY_SIZE(input_mode_names));
if (ret >= 0)
st->input_mode = ret;
else
st->input_mode = ADMV1014_IQ_MODE;
ret = device_property_match_property_string(dev, "adi,quad-se-mode",
quad_se_mode_names,
ARRAY_SIZE(quad_se_mode_names));
if (ret >= 0)
st->quad_se_mode = ADMV1014_SE_MODE_POS + (ret * 3);
else
st->quad_se_mode = ADMV1014_SE_MODE_POS;
for (i = 0; i < ADMV1014_NUM_REGULATORS; ++i)
st->regulators[i].supply = admv1014_reg_name[i];
ret = devm_regulator_bulk_get(dev, ADMV1014_NUM_REGULATORS,
st->regulators);
if (ret)
return dev_err_probe(dev, ret, "Failed to request regulators");
st->clkin = devm_clk_get(dev, "lo_in");
if (IS_ERR(st->clkin))
return dev_err_probe(dev, PTR_ERR(st->clkin),
"failed to get the LO input clock\n");
return 0;
}
static int admv1014_probe(struct spi_device *spi)
{
struct iio_dev *indio_dev;
struct admv1014_state *st;
struct device *dev = &spi->dev;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
st = iio_priv(indio_dev);
ret = admv1014_properties_parse(st);
if (ret)
return ret;
indio_dev->info = &admv1014_info;
indio_dev->name = "admv1014";
if (st->input_mode == ADMV1014_IQ_MODE) {
indio_dev->channels = admv1014_channels_iq;
indio_dev->num_channels = ARRAY_SIZE(admv1014_channels_iq);
} else {
indio_dev->channels = admv1014_channels_if;
indio_dev->num_channels = ARRAY_SIZE(admv1014_channels_if);
}
st->spi = spi;
mutex_init(&st->lock);
ret = admv1014_init(st);
if (ret)
return ret;
return devm_iio_device_register(dev, indio_dev);
}
static const struct spi_device_id admv1014_id[] = {
{ "admv1014", 0 },
{ }
};
MODULE_DEVICE_TABLE(spi, admv1014_id);
static const struct of_device_id admv1014_of_match[] = {
{ .compatible = "adi,admv1014" },
{ }
};
MODULE_DEVICE_TABLE(of, admv1014_of_match);
static struct spi_driver admv1014_driver = {
.driver = {
.name = "admv1014",
.of_match_table = admv1014_of_match,
},
.probe = admv1014_probe,
.id_table = admv1014_id,
};
module_spi_driver(admv1014_driver);
MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com");
MODULE_DESCRIPTION("Analog Devices ADMV1014");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,395 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
/*
* ADMV4420
*
* Copyright 2021 Analog Devices Inc.
*/
#include <linux/bitfield.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include <linux/units.h>
#include <linux/unaligned.h>
/* ADMV4420 Register Map */
#define ADMV4420_SPI_CONFIG_1 0x00
#define ADMV4420_SPI_CONFIG_2 0x01
#define ADMV4420_CHIPTYPE 0x03
#define ADMV4420_PRODUCT_ID_L 0x04
#define ADMV4420_PRODUCT_ID_H 0x05
#define ADMV4420_SCRATCHPAD 0x0A
#define ADMV4420_SPI_REV 0x0B
#define ADMV4420_ENABLES 0x103
#define ADMV4420_SDO_LEVEL 0x108
#define ADMV4420_INT_L 0x200
#define ADMV4420_INT_H 0x201
#define ADMV4420_FRAC_L 0x202
#define ADMV4420_FRAC_M 0x203
#define ADMV4420_FRAC_H 0x204
#define ADMV4420_MOD_L 0x208
#define ADMV4420_MOD_M 0x209
#define ADMV4420_MOD_H 0x20A
#define ADMV4420_R_DIV_L 0x20C
#define ADMV4420_R_DIV_H 0x20D
#define ADMV4420_REFERENCE 0x20E
#define ADMV4420_VCO_DATA_READBACK1 0x211
#define ADMV4420_VCO_DATA_READBACK2 0x212
#define ADMV4420_PLL_MUX_SEL 0x213
#define ADMV4420_LOCK_DETECT 0x214
#define ADMV4420_BAND_SELECT 0x215
#define ADMV4420_VCO_ALC_TIMEOUT 0x216
#define ADMV4420_VCO_MANUAL 0x217
#define ADMV4420_ALC 0x219
#define ADMV4420_VCO_TIMEOUT1 0x21C
#define ADMV4420_VCO_TIMEOUT2 0x21D
#define ADMV4420_VCO_BAND_DIV 0x21E
#define ADMV4420_VCO_READBACK_SEL 0x21F
#define ADMV4420_AUTOCAL 0x226
#define ADMV4420_CP_STATE 0x22C
#define ADMV4420_CP_BLEED_EN 0x22D
#define ADMV4420_CP_CURRENT 0x22E
#define ADMV4420_CP_BLEED 0x22F
#define ADMV4420_SPI_CONFIG_1_SDOACTIVE (BIT(4) | BIT(3))
#define ADMV4420_SPI_CONFIG_1_ENDIAN (BIT(5) | BIT(2))
#define ADMV4420_SPI_CONFIG_1_SOFTRESET (BIT(7) | BIT(1))
#define ADMV4420_REFERENCE_DIVIDE_BY_2_MASK BIT(0)
#define ADMV4420_REFERENCE_MODE_MASK BIT(1)
#define ADMV4420_REFERENCE_DOUBLER_MASK BIT(2)
#define ADMV4420_REF_DIVIDER_MAX_VAL GENMASK(9, 0)
#define ADMV4420_N_COUNTER_INT_MAX GENMASK(15, 0)
#define ADMV4420_N_COUNTER_FRAC_MAX GENMASK(23, 0)
#define ADMV4420_N_COUNTER_MOD_MAX GENMASK(23, 0)
#define ENABLE_PLL BIT(6)
#define ENABLE_LO BIT(5)
#define ENABLE_VCO BIT(3)
#define ENABLE_IFAMP BIT(2)
#define ENABLE_MIXER BIT(1)
#define ENABLE_LNA BIT(0)
#define ADMV4420_SCRATCH_PAD_VAL_1 0xAD
#define ADMV4420_SCRATCH_PAD_VAL_2 0xEA
#define ADMV4420_REF_FREQ_HZ 50000000
#define MAX_N_COUNTER 655360UL
#define MAX_R_DIVIDER 1024
#define ADMV4420_DEFAULT_LO_FREQ_HZ 16750000000ULL
enum admv4420_mux_sel {
ADMV4420_LOW = 0,
ADMV4420_LOCK_DTCT = 1,
ADMV4420_R_COUNTER_PER_2 = 4,
ADMV4420_N_CONUTER_PER_2 = 5,
ADMV4420_HIGH = 8,
};
struct admv4420_reference_block {
bool doubler_en;
bool divide_by_2_en;
bool ref_single_ended;
u32 divider;
};
struct admv4420_n_counter {
u32 int_val;
u32 frac_val;
u32 mod_val;
u32 n_counter;
};
struct admv4420_state {
struct spi_device *spi;
struct regmap *regmap;
u64 vco_freq_hz;
u64 lo_freq_hz;
struct admv4420_reference_block ref_block;
struct admv4420_n_counter n_counter;
enum admv4420_mux_sel mux_sel;
struct mutex lock;
u8 transf_buf[4] __aligned(IIO_DMA_MINALIGN);
};
static const struct regmap_config admv4420_regmap_config = {
.reg_bits = 16,
.val_bits = 8,
.read_flag_mask = BIT(7),
};
static int admv4420_reg_access(struct iio_dev *indio_dev,
u32 reg, u32 writeval,
u32 *readval)
{
struct admv4420_state *st = iio_priv(indio_dev);
if (readval)
return regmap_read(st->regmap, reg, readval);
else
return regmap_write(st->regmap, reg, writeval);
}
static int admv4420_set_n_counter(struct admv4420_state *st, u32 int_val,
u32 frac_val, u32 mod_val)
{
int ret;
put_unaligned_le32(frac_val, st->transf_buf);
ret = regmap_bulk_write(st->regmap, ADMV4420_FRAC_L, st->transf_buf, 3);
if (ret)
return ret;
put_unaligned_le32(mod_val, st->transf_buf);
ret = regmap_bulk_write(st->regmap, ADMV4420_MOD_L, st->transf_buf, 3);
if (ret)
return ret;
put_unaligned_le32(int_val, st->transf_buf);
return regmap_bulk_write(st->regmap, ADMV4420_INT_L, st->transf_buf, 2);
}
static int admv4420_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long info)
{
struct admv4420_state *st = iio_priv(indio_dev);
switch (info) {
case IIO_CHAN_INFO_FREQUENCY:
*val = div_u64_rem(st->lo_freq_hz, MICRO, val2);
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
}
static const struct iio_info admv4420_info = {
.read_raw = admv4420_read_raw,
.debugfs_reg_access = &admv4420_reg_access,
};
static const struct iio_chan_spec admv4420_channels[] = {
{
.type = IIO_ALTVOLTAGE,
.output = 0,
.indexed = 1,
.channel = 0,
.info_mask_separate = BIT(IIO_CHAN_INFO_FREQUENCY),
},
};
static void admv4420_fw_parse(struct admv4420_state *st)
{
struct device *dev = &st->spi->dev;
u32 tmp;
int ret;
ret = device_property_read_u32(dev, "adi,lo-freq-khz", &tmp);
if (!ret)
st->lo_freq_hz = (u64)tmp * KILO;
st->ref_block.ref_single_ended = device_property_read_bool(dev,
"adi,ref-ext-single-ended-en");
}
static inline uint64_t admv4420_calc_pfd_vco(struct admv4420_state *st)
{
return div_u64(st->vco_freq_hz * 10, st->n_counter.n_counter);
}
static inline uint32_t admv4420_calc_pfd_ref(struct admv4420_state *st)
{
uint32_t tmp;
u8 doubler, divide_by_2;
doubler = st->ref_block.doubler_en ? 2 : 1;
divide_by_2 = st->ref_block.divide_by_2_en ? 2 : 1;
tmp = ADMV4420_REF_FREQ_HZ * doubler;
return (tmp / (st->ref_block.divider * divide_by_2));
}
static int admv4420_calc_parameters(struct admv4420_state *st)
{
u64 pfd_ref, pfd_vco;
bool sol_found = false;
st->ref_block.doubler_en = false;
st->ref_block.divide_by_2_en = false;
st->vco_freq_hz = div_u64(st->lo_freq_hz, 2);
for (st->ref_block.divider = 1; st->ref_block.divider < MAX_R_DIVIDER;
st->ref_block.divider++) {
pfd_ref = admv4420_calc_pfd_ref(st);
for (st->n_counter.n_counter = 1; st->n_counter.n_counter < MAX_N_COUNTER;
st->n_counter.n_counter++) {
pfd_vco = admv4420_calc_pfd_vco(st);
if (pfd_ref == pfd_vco) {
sol_found = true;
break;
}
}
if (sol_found)
break;
st->n_counter.n_counter = 1;
}
if (!sol_found)
return -EINVAL;
st->n_counter.int_val = div_u64_rem(st->n_counter.n_counter, 10, &st->n_counter.frac_val);
st->n_counter.mod_val = 10;
return 0;
}
static int admv4420_setup(struct iio_dev *indio_dev)
{
struct admv4420_state *st = iio_priv(indio_dev);
struct device *dev = indio_dev->dev.parent;
u32 val;
int ret;
ret = regmap_write(st->regmap, ADMV4420_SPI_CONFIG_1,
ADMV4420_SPI_CONFIG_1_SOFTRESET);
if (ret)
return ret;
ret = regmap_write(st->regmap, ADMV4420_SPI_CONFIG_1,
ADMV4420_SPI_CONFIG_1_SDOACTIVE |
ADMV4420_SPI_CONFIG_1_ENDIAN);
if (ret)
return ret;
ret = regmap_write(st->regmap,
ADMV4420_SCRATCHPAD,
ADMV4420_SCRATCH_PAD_VAL_1);
if (ret)
return ret;
ret = regmap_read(st->regmap, ADMV4420_SCRATCHPAD, &val);
if (ret)
return ret;
if (val != ADMV4420_SCRATCH_PAD_VAL_1)
return dev_err_probe(dev, -EIO,
"Failed ADMV4420 to read/write scratchpad %x\n", val);
ret = regmap_write(st->regmap,
ADMV4420_SCRATCHPAD,
ADMV4420_SCRATCH_PAD_VAL_2);
if (ret)
return ret;
ret = regmap_read(st->regmap, ADMV4420_SCRATCHPAD, &val);
if (ret)
return ret;
if (val != ADMV4420_SCRATCH_PAD_VAL_2)
return dev_err_probe(dev, -EIO,
"Failed to read/write scratchpad %x\n", val);
st->mux_sel = ADMV4420_LOCK_DTCT;
st->lo_freq_hz = ADMV4420_DEFAULT_LO_FREQ_HZ;
admv4420_fw_parse(st);
ret = admv4420_calc_parameters(st);
if (ret)
return dev_err_probe(dev, ret,
"Failed calc parameters for %llu\n",
st->vco_freq_hz);
ret = regmap_write(st->regmap, ADMV4420_R_DIV_L,
FIELD_GET(0xFF, st->ref_block.divider));
if (ret)
return ret;
ret = regmap_write(st->regmap, ADMV4420_R_DIV_H,
FIELD_GET(0xFF00, st->ref_block.divider));
if (ret)
return ret;
ret = regmap_write(st->regmap, ADMV4420_REFERENCE,
st->ref_block.divide_by_2_en |
FIELD_PREP(ADMV4420_REFERENCE_MODE_MASK, st->ref_block.ref_single_ended) |
FIELD_PREP(ADMV4420_REFERENCE_DOUBLER_MASK, st->ref_block.doubler_en));
if (ret)
return ret;
ret = admv4420_set_n_counter(st, st->n_counter.int_val,
st->n_counter.frac_val,
st->n_counter.mod_val);
if (ret)
return ret;
ret = regmap_write(st->regmap, ADMV4420_PLL_MUX_SEL, st->mux_sel);
if (ret)
return ret;
return regmap_write(st->regmap, ADMV4420_ENABLES,
ENABLE_PLL | ENABLE_LO | ENABLE_VCO |
ENABLE_IFAMP | ENABLE_MIXER | ENABLE_LNA);
}
static int admv4420_probe(struct spi_device *spi)
{
struct device *dev = &spi->dev;
struct iio_dev *indio_dev;
struct admv4420_state *st;
struct regmap *regmap;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
regmap = devm_regmap_init_spi(spi, &admv4420_regmap_config);
if (IS_ERR(regmap))
return dev_err_probe(dev, PTR_ERR(regmap),
"Failed to initializing spi regmap\n");
st = iio_priv(indio_dev);
st->spi = spi;
st->regmap = regmap;
indio_dev->name = "admv4420";
indio_dev->info = &admv4420_info;
indio_dev->channels = admv4420_channels;
indio_dev->num_channels = ARRAY_SIZE(admv4420_channels);
ret = admv4420_setup(indio_dev);
if (ret)
return dev_err_probe(dev, ret, "Setup ADMV4420 failed\n");
return devm_iio_device_register(dev, indio_dev);
}
static const struct of_device_id admv4420_of_match[] = {
{ .compatible = "adi,admv4420" },
{ }
};
MODULE_DEVICE_TABLE(of, admv4420_of_match);
static struct spi_driver admv4420_driver = {
.driver = {
.name = "admv4420",
.of_match_table = admv4420_of_match,
},
.probe = admv4420_probe,
};
module_spi_driver(admv4420_driver);
MODULE_AUTHOR("Cristian Pop <cristian.pop@analog.com>");
MODULE_DESCRIPTION("Analog Devices ADMV44200 K Band Downconverter");
MODULE_LICENSE("Dual BSD/GPL");
@@ -0,0 +1,512 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* ADRF6780 driver
*
* Copyright 2021 Analog Devices Inc.
*/
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/iio/iio.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/unaligned.h>
/* ADRF6780 Register Map */
#define ADRF6780_REG_CONTROL 0x00
#define ADRF6780_REG_ALARM_READBACK 0x01
#define ADRF6780_REG_ALARM_MASKS 0x02
#define ADRF6780_REG_ENABLE 0x03
#define ADRF6780_REG_LINEARIZE 0x04
#define ADRF6780_REG_LO_PATH 0x05
#define ADRF6780_REG_ADC_CONTROL 0x06
#define ADRF6780_REG_ADC_OUTPUT 0x0C
/* ADRF6780_REG_CONTROL Map */
#define ADRF6780_PARITY_EN_MSK BIT(15)
#define ADRF6780_SOFT_RESET_MSK BIT(14)
#define ADRF6780_CHIP_ID_MSK GENMASK(11, 4)
#define ADRF6780_CHIP_ID 0xA
#define ADRF6780_CHIP_REVISION_MSK GENMASK(3, 0)
/* ADRF6780_REG_ALARM_READBACK Map */
#define ADRF6780_PARITY_ERROR_MSK BIT(15)
#define ADRF6780_TOO_FEW_ERRORS_MSK BIT(14)
#define ADRF6780_TOO_MANY_ERRORS_MSK BIT(13)
#define ADRF6780_ADDRESS_RANGE_ERROR_MSK BIT(12)
/* ADRF6780_REG_ENABLE Map */
#define ADRF6780_VGA_BUFFER_EN_MSK BIT(8)
#define ADRF6780_DETECTOR_EN_MSK BIT(7)
#define ADRF6780_LO_BUFFER_EN_MSK BIT(6)
#define ADRF6780_IF_MODE_EN_MSK BIT(5)
#define ADRF6780_IQ_MODE_EN_MSK BIT(4)
#define ADRF6780_LO_X2_EN_MSK BIT(3)
#define ADRF6780_LO_PPF_EN_MSK BIT(2)
#define ADRF6780_LO_EN_MSK BIT(1)
#define ADRF6780_UC_BIAS_EN_MSK BIT(0)
/* ADRF6780_REG_LINEARIZE Map */
#define ADRF6780_RDAC_LINEARIZE_MSK GENMASK(7, 0)
/* ADRF6780_REG_LO_PATH Map */
#define ADRF6780_LO_SIDEBAND_MSK BIT(10)
#define ADRF6780_Q_PATH_PHASE_ACCURACY_MSK GENMASK(7, 4)
#define ADRF6780_I_PATH_PHASE_ACCURACY_MSK GENMASK(3, 0)
/* ADRF6780_REG_ADC_CONTROL Map */
#define ADRF6780_VDET_OUTPUT_SELECT_MSK BIT(3)
#define ADRF6780_ADC_START_MSK BIT(2)
#define ADRF6780_ADC_EN_MSK BIT(1)
#define ADRF6780_ADC_CLOCK_EN_MSK BIT(0)
/* ADRF6780_REG_ADC_OUTPUT Map */
#define ADRF6780_ADC_STATUS_MSK BIT(8)
#define ADRF6780_ADC_VALUE_MSK GENMASK(7, 0)
struct adrf6780_state {
struct spi_device *spi;
struct clk *clkin;
/* Protect against concurrent accesses to the device */
struct mutex lock;
bool vga_buff_en;
bool lo_buff_en;
bool if_mode_en;
bool iq_mode_en;
bool lo_x2_en;
bool lo_ppf_en;
bool lo_en;
bool uc_bias_en;
bool lo_sideband;
bool vdet_out_en;
u8 data[3] __aligned(IIO_DMA_MINALIGN);
};
static int __adrf6780_spi_read(struct adrf6780_state *st, unsigned int reg,
unsigned int *val)
{
int ret;
struct spi_transfer t = {0};
st->data[0] = 0x80 | (reg << 1);
st->data[1] = 0x0;
st->data[2] = 0x0;
t.rx_buf = &st->data[0];
t.tx_buf = &st->data[0];
t.len = 3;
ret = spi_sync_transfer(st->spi, &t, 1);
if (ret)
return ret;
*val = (get_unaligned_be24(&st->data[0]) >> 1) & GENMASK(15, 0);
return ret;
}
static int adrf6780_spi_read(struct adrf6780_state *st, unsigned int reg,
unsigned int *val)
{
int ret;
mutex_lock(&st->lock);
ret = __adrf6780_spi_read(st, reg, val);
mutex_unlock(&st->lock);
return ret;
}
static int __adrf6780_spi_write(struct adrf6780_state *st,
unsigned int reg,
unsigned int val)
{
put_unaligned_be24((val << 1) | (reg << 17), &st->data[0]);
return spi_write(st->spi, &st->data[0], 3);
}
static int adrf6780_spi_write(struct adrf6780_state *st, unsigned int reg,
unsigned int val)
{
int ret;
mutex_lock(&st->lock);
ret = __adrf6780_spi_write(st, reg, val);
mutex_unlock(&st->lock);
return ret;
}
static int __adrf6780_spi_update_bits(struct adrf6780_state *st,
unsigned int reg, unsigned int mask,
unsigned int val)
{
int ret;
unsigned int data, temp;
ret = __adrf6780_spi_read(st, reg, &data);
if (ret)
return ret;
temp = (data & ~mask) | (val & mask);
return __adrf6780_spi_write(st, reg, temp);
}
static int adrf6780_spi_update_bits(struct adrf6780_state *st, unsigned int reg,
unsigned int mask, unsigned int val)
{
int ret;
mutex_lock(&st->lock);
ret = __adrf6780_spi_update_bits(st, reg, mask, val);
mutex_unlock(&st->lock);
return ret;
}
static int adrf6780_read_adc_raw(struct adrf6780_state *st, unsigned int *read_val)
{
int ret;
mutex_lock(&st->lock);
ret = __adrf6780_spi_update_bits(st, ADRF6780_REG_ADC_CONTROL,
ADRF6780_ADC_EN_MSK |
ADRF6780_ADC_CLOCK_EN_MSK |
ADRF6780_ADC_START_MSK,
FIELD_PREP(ADRF6780_ADC_EN_MSK, 1) |
FIELD_PREP(ADRF6780_ADC_CLOCK_EN_MSK, 1) |
FIELD_PREP(ADRF6780_ADC_START_MSK, 1));
if (ret)
goto exit;
/*
* Per ADRF6780 datasheet (Rev. D, page 23, ADC section),
* wait approximately 200 us for the ADC to be ready.
*/
fsleep(200);
ret = __adrf6780_spi_read(st, ADRF6780_REG_ADC_OUTPUT, read_val);
if (ret)
goto exit;
if (!(*read_val & ADRF6780_ADC_STATUS_MSK)) {
ret = -EINVAL;
goto exit;
}
ret = __adrf6780_spi_update_bits(st, ADRF6780_REG_ADC_CONTROL,
ADRF6780_ADC_START_MSK,
FIELD_PREP(ADRF6780_ADC_START_MSK, 0));
if (ret)
goto exit;
ret = __adrf6780_spi_read(st, ADRF6780_REG_ADC_OUTPUT, read_val);
exit:
mutex_unlock(&st->lock);
return ret;
}
static int adrf6780_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long info)
{
struct adrf6780_state *dev = iio_priv(indio_dev);
unsigned int data;
int ret;
switch (info) {
case IIO_CHAN_INFO_RAW:
ret = adrf6780_read_adc_raw(dev, &data);
if (ret)
return ret;
*val = data & ADRF6780_ADC_VALUE_MSK;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
ret = adrf6780_spi_read(dev, ADRF6780_REG_LINEARIZE, &data);
if (ret)
return ret;
*val = data & ADRF6780_RDAC_LINEARIZE_MSK;
return IIO_VAL_INT;
case IIO_CHAN_INFO_PHASE:
ret = adrf6780_spi_read(dev, ADRF6780_REG_LO_PATH, &data);
if (ret)
return ret;
switch (chan->channel2) {
case IIO_MOD_I:
*val = data & ADRF6780_I_PATH_PHASE_ACCURACY_MSK;
return IIO_VAL_INT;
case IIO_MOD_Q:
*val = FIELD_GET(ADRF6780_Q_PATH_PHASE_ACCURACY_MSK,
data);
return IIO_VAL_INT;
default:
return -EINVAL;
}
default:
return -EINVAL;
}
}
static int adrf6780_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long info)
{
struct adrf6780_state *st = iio_priv(indio_dev);
switch (info) {
case IIO_CHAN_INFO_SCALE:
return adrf6780_spi_write(st, ADRF6780_REG_LINEARIZE, val);
case IIO_CHAN_INFO_PHASE:
switch (chan->channel2) {
case IIO_MOD_I:
return adrf6780_spi_update_bits(st,
ADRF6780_REG_LO_PATH,
ADRF6780_I_PATH_PHASE_ACCURACY_MSK,
FIELD_PREP(ADRF6780_I_PATH_PHASE_ACCURACY_MSK, val));
case IIO_MOD_Q:
return adrf6780_spi_update_bits(st,
ADRF6780_REG_LO_PATH,
ADRF6780_Q_PATH_PHASE_ACCURACY_MSK,
FIELD_PREP(ADRF6780_Q_PATH_PHASE_ACCURACY_MSK, val));
default:
return -EINVAL;
}
default:
return -EINVAL;
}
}
static int adrf6780_reg_access(struct iio_dev *indio_dev,
unsigned int reg,
unsigned int write_val,
unsigned int *read_val)
{
struct adrf6780_state *st = iio_priv(indio_dev);
if (read_val)
return adrf6780_spi_read(st, reg, read_val);
else
return adrf6780_spi_write(st, reg, write_val);
}
static const struct iio_info adrf6780_info = {
.read_raw = adrf6780_read_raw,
.write_raw = adrf6780_write_raw,
.debugfs_reg_access = &adrf6780_reg_access,
};
#define ADRF6780_CHAN_ADC(_channel) { \
.type = IIO_ALTVOLTAGE, \
.output = 0, \
.indexed = 1, \
.channel = _channel, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \
}
#define ADRF6780_CHAN_RDAC(_channel) { \
.type = IIO_ALTVOLTAGE, \
.output = 1, \
.indexed = 1, \
.channel = _channel, \
.info_mask_separate = BIT(IIO_CHAN_INFO_SCALE) \
}
#define ADRF6780_CHAN_IQ_PHASE(_channel, rf_comp) { \
.type = IIO_ALTVOLTAGE, \
.modified = 1, \
.output = 1, \
.indexed = 1, \
.channel2 = IIO_MOD_##rf_comp, \
.channel = _channel, \
.info_mask_separate = BIT(IIO_CHAN_INFO_PHASE) \
}
static const struct iio_chan_spec adrf6780_channels[] = {
ADRF6780_CHAN_ADC(0),
ADRF6780_CHAN_RDAC(0),
ADRF6780_CHAN_IQ_PHASE(0, I),
ADRF6780_CHAN_IQ_PHASE(0, Q),
};
static int adrf6780_reset(struct adrf6780_state *st)
{
int ret;
struct device *dev = &st->spi->dev;
ret = __adrf6780_spi_update_bits(st, ADRF6780_REG_CONTROL,
ADRF6780_SOFT_RESET_MSK,
FIELD_PREP(ADRF6780_SOFT_RESET_MSK, 1));
if (ret)
return dev_err_probe(dev, ret,
"ADRF6780 SPI software reset failed.\n");
ret = __adrf6780_spi_update_bits(st, ADRF6780_REG_CONTROL,
ADRF6780_SOFT_RESET_MSK,
FIELD_PREP(ADRF6780_SOFT_RESET_MSK, 0));
if (ret)
return dev_err_probe(dev, ret,
"ADRF6780 SPI software reset disable failed.\n");
return 0;
}
static int adrf6780_init(struct adrf6780_state *st)
{
int ret;
unsigned int chip_id, enable_reg, enable_reg_msk;
struct device *dev = &st->spi->dev;
/* Perform a software reset */
ret = adrf6780_reset(st);
if (ret)
return ret;
ret = __adrf6780_spi_read(st, ADRF6780_REG_CONTROL, &chip_id);
if (ret)
return ret;
chip_id = FIELD_GET(ADRF6780_CHIP_ID_MSK, chip_id);
if (chip_id != ADRF6780_CHIP_ID)
return dev_err_probe(dev, -EINVAL,
"ADRF6780 Invalid Chip ID.\n");
enable_reg_msk = ADRF6780_VGA_BUFFER_EN_MSK |
ADRF6780_DETECTOR_EN_MSK |
ADRF6780_LO_BUFFER_EN_MSK |
ADRF6780_IF_MODE_EN_MSK |
ADRF6780_IQ_MODE_EN_MSK |
ADRF6780_LO_X2_EN_MSK |
ADRF6780_LO_PPF_EN_MSK |
ADRF6780_LO_EN_MSK |
ADRF6780_UC_BIAS_EN_MSK;
enable_reg = FIELD_PREP(ADRF6780_VGA_BUFFER_EN_MSK, st->vga_buff_en) |
FIELD_PREP(ADRF6780_DETECTOR_EN_MSK, 1) |
FIELD_PREP(ADRF6780_LO_BUFFER_EN_MSK, st->lo_buff_en) |
FIELD_PREP(ADRF6780_IF_MODE_EN_MSK, st->if_mode_en) |
FIELD_PREP(ADRF6780_IQ_MODE_EN_MSK, st->iq_mode_en) |
FIELD_PREP(ADRF6780_LO_X2_EN_MSK, st->lo_x2_en) |
FIELD_PREP(ADRF6780_LO_PPF_EN_MSK, st->lo_ppf_en) |
FIELD_PREP(ADRF6780_LO_EN_MSK, st->lo_en) |
FIELD_PREP(ADRF6780_UC_BIAS_EN_MSK, st->uc_bias_en);
ret = __adrf6780_spi_update_bits(st, ADRF6780_REG_ENABLE,
enable_reg_msk, enable_reg);
if (ret)
return ret;
ret = __adrf6780_spi_update_bits(st, ADRF6780_REG_LO_PATH,
ADRF6780_LO_SIDEBAND_MSK,
FIELD_PREP(ADRF6780_LO_SIDEBAND_MSK, st->lo_sideband));
if (ret)
return ret;
return __adrf6780_spi_update_bits(st, ADRF6780_REG_ADC_CONTROL,
ADRF6780_VDET_OUTPUT_SELECT_MSK,
FIELD_PREP(ADRF6780_VDET_OUTPUT_SELECT_MSK, st->vdet_out_en));
}
static void adrf6780_properties_parse(struct adrf6780_state *st)
{
struct device *dev = &st->spi->dev;
st->vga_buff_en = device_property_read_bool(dev, "adi,vga-buff-en");
st->lo_buff_en = device_property_read_bool(dev, "adi,lo-buff-en");
st->if_mode_en = device_property_read_bool(dev, "adi,if-mode-en");
st->iq_mode_en = device_property_read_bool(dev, "adi,iq-mode-en");
st->lo_x2_en = device_property_read_bool(dev, "adi,lo-x2-en");
st->lo_ppf_en = device_property_read_bool(dev, "adi,lo-ppf-en");
st->lo_en = device_property_read_bool(dev, "adi,lo-en");
st->uc_bias_en = device_property_read_bool(dev, "adi,uc-bias-en");
st->lo_sideband = device_property_read_bool(dev, "adi,lo-sideband");
st->vdet_out_en = device_property_read_bool(dev, "adi,vdet-out-en");
}
static void adrf6780_powerdown(void *data)
{
/* Disable all components in the Enable Register */
adrf6780_spi_write(data, ADRF6780_REG_ENABLE, 0x0);
}
static int adrf6780_probe(struct spi_device *spi)
{
struct iio_dev *indio_dev;
struct adrf6780_state *st;
struct device *dev = &spi->dev;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
st = iio_priv(indio_dev);
indio_dev->info = &adrf6780_info;
indio_dev->name = "adrf6780";
indio_dev->channels = adrf6780_channels;
indio_dev->num_channels = ARRAY_SIZE(adrf6780_channels);
st->spi = spi;
adrf6780_properties_parse(st);
st->clkin = devm_clk_get_enabled(dev, "lo_in");
if (IS_ERR(st->clkin))
return dev_err_probe(dev, PTR_ERR(st->clkin),
"failed to get the LO input clock\n");
mutex_init(&st->lock);
ret = adrf6780_init(st);
if (ret)
return ret;
ret = devm_add_action_or_reset(dev, adrf6780_powerdown, st);
if (ret)
return ret;
return devm_iio_device_register(dev, indio_dev);
}
static const struct spi_device_id adrf6780_id[] = {
{ "adrf6780", 0 },
{ }
};
MODULE_DEVICE_TABLE(spi, adrf6780_id);
static const struct of_device_id adrf6780_of_match[] = {
{ .compatible = "adi,adrf6780" },
{ }
};
MODULE_DEVICE_TABLE(of, adrf6780_of_match);
static struct spi_driver adrf6780_driver = {
.driver = {
.name = "adrf6780",
.of_match_table = adrf6780_of_match,
},
.probe = adrf6780_probe,
.id_table = adrf6780_id,
};
module_spi_driver(adrf6780_driver);
MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com");
MODULE_DESCRIPTION("Analog Devices ADRF6780");
MODULE_LICENSE("GPL v2");