Skip to content

Commit ac7c986

Browse files
committed
iio: adc: adrv9002: add warmboot support
Warmboot allows to bypass the Initial calibrations and instead write a set of known-good calibration coefficients directly to memory. This improves significantly boot and profile loading times. The way this is done is by adding a new binary attribute called 'warmboot_coefficients' and another device attribute called 'warmboot_coefficients_file'. The typical workflow looks like: 1. cat warmboot_coefficients > /lib/firmware/current_profile_coeffs.bin 2. echo current_profile_coeffs.bin > warmboot_coefficients_file 3. cat current_profile.json > profile_config So, looking at the above commands, one first needs to save the coefficients for the desired profile (note that profile needs to be loaded with warmboot disabled first so the initial calibrations can run once). Then, life goes on and we might change to another profile but when moving back to the same profile (now with warmboot enabled) one can run 2 and 3 commands to load the profile. When setting up the device, we see if warmboot is enabled and then we make use of the firmware API to request the coefficients to load on the device. Things are done like this mainly to save memory as these coefficients buffers can be up to 1MB big and the main device object is already quite big. Like this, we allocate memory for the coefficients only when needed and free it as soon as we are done with it. While at it, make sure to directly include 'mutex.h'. Signed-off-by: Nuno Sa <nuno.sa@analog.com>
1 parent cb3cc1d commit ac7c986

File tree

2 files changed

+176
-5
lines changed

2 files changed

+176
-5
lines changed

drivers/iio/adc/navassa/adrv9002.c

Lines changed: 162 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
#include <linux/iopoll.h>
1818
#include <linux/kernel.h>
1919
#include <linux/module.h>
20+
#include <linux/mutex.h>
2021
#include <linux/of_device.h>
2122
#include <linux/slab.h>
2223
#include <linux/spi/spi.h>
2324
#include <linux/sysfs.h>
25+
#include <linux/string.h>
2426
#include <linux/units.h>
2527

2628
#include "adrv9002.h"
@@ -551,6 +553,7 @@ enum {
551553
ADRV9002_HOP_1_TRIGGER,
552554
ADRV9002_HOP_2_TRIGGER,
553555
ADRV9002_INIT_CALS_RUN,
556+
ADRV9002_WARMBOOT_SEL,
554557
};
555558

556559
static const char * const adrv9002_hop_table[ADRV9002_FH_TABLES_NR + 1] = {
@@ -608,6 +611,9 @@ static ssize_t adrv9002_attr_show(struct device *dev, struct device_attribute *a
608611
case ADRV9002_INIT_CALS_RUN:
609612
ret = sysfs_emit(buf, "%s\n", adrv9002_init_cals_modes[phy->run_cals]);
610613
break;
614+
case ADRV9002_WARMBOOT_SEL:
615+
ret = sysfs_emit(buf, "%s\n", phy->warm_boot.coeffs_name);
616+
break;
611617
default:
612618
ret = -EINVAL;
613619
}
@@ -714,6 +720,23 @@ static int adrv9002_init_cals_set(struct adrv9002_rf_phy *phy, const char *buf)
714720
return adrv9002_phy_rerun_cals(phy, &cals, 0);
715721
}
716722

723+
static int adrv9002_warm_boot_name_save(struct adrv9002_rf_phy *phy, const char *buf)
724+
{
725+
int ret;
726+
char *p;
727+
728+
ret = strscpy(phy->warm_boot.coeffs_name, buf, sizeof(phy->warm_boot.coeffs_name));
729+
if (ret < 0)
730+
return ret;
731+
732+
/* Strip trailing newline */
733+
p = phy->warm_boot.coeffs_name + strlen(phy->warm_boot.coeffs_name);
734+
if (*--p == '\n')
735+
*p = '\0';
736+
737+
return 0;
738+
}
739+
717740
static int adrv9002_fh_set(const struct adrv9002_rf_phy *phy, const char *buf, u64 address)
718741
{
719742
int ret;
@@ -763,6 +786,9 @@ static ssize_t adrv9002_attr_store(struct device *dev, struct device_attribute *
763786
case ADRV9002_INIT_CALS_RUN:
764787
ret = adrv9002_init_cals_set(phy, buf);
765788
break;
789+
case ADRV9002_WARMBOOT_SEL:
790+
ret = adrv9002_warm_boot_name_save(phy, buf);
791+
break;
766792
default:
767793
ret = adrv9002_fh_set(phy, buf, iio_attr->address);
768794
}
@@ -2494,6 +2520,8 @@ static IIO_DEVICE_ATTR(frequency_hopping_hop2_signal_trigger, 0200, NULL, adrv90
24942520
ADRV9002_HOP_2_TRIGGER);
24952521
static IIO_DEVICE_ATTR(initial_calibrations, 0600, adrv9002_attr_show, adrv9002_attr_store,
24962522
ADRV9002_INIT_CALS_RUN);
2523+
static IIO_DEVICE_ATTR(warmboot_coefficients_file, 0600, adrv9002_attr_show, adrv9002_attr_store,
2524+
ADRV9002_WARMBOOT_SEL);
24972525

24982526
static struct attribute *adrv9002_sysfs_attrs[] = {
24992527
&iio_const_attr_initial_calibrations_available.dev_attr.attr,
@@ -2503,6 +2531,7 @@ static struct attribute *adrv9002_sysfs_attrs[] = {
25032531
&iio_dev_attr_frequency_hopping_hop1_signal_trigger.dev_attr.attr,
25042532
&iio_dev_attr_frequency_hopping_hop2_signal_trigger.dev_attr.attr,
25052533
&iio_dev_attr_initial_calibrations.dev_attr.attr,
2534+
&iio_dev_attr_warmboot_coefficients_file.dev_attr.attr,
25062535
NULL
25072536
};
25082537

@@ -2615,6 +2644,33 @@ static int adrv9002_dgpio_config(const struct adrv9002_rf_phy *phy)
26152644
return 0;
26162645
}
26172646

2647+
static int adrv9002_init_cals_handle(struct adrv9002_rf_phy *phy)
2648+
{
2649+
const struct firmware *fw;
2650+
int ret;
2651+
u8 errors;
2652+
2653+
if (!phy->curr_profile->sysConfig.warmBootEnable || phy->warm_boot.coeffs_name[0] == '\0')
2654+
goto run_cals;
2655+
2656+
dev_dbg(&phy->spi->dev, "Requesting warmboot coefficients: \"%s\"n",
2657+
phy->warm_boot.coeffs_name);
2658+
2659+
ret = request_firmware(&fw, phy->warm_boot.coeffs_name, &phy->spi->dev);
2660+
if (ret)
2661+
return ret;
2662+
2663+
ret = api_call(phy, adi_adrv9001_cals_InitCals_WarmBoot_Coefficients_UniqueArray_Set,
2664+
fw->data, phy->init_cals.chanInitCalMask[0],
2665+
phy->init_cals.chanInitCalMask[1]);
2666+
release_firmware(fw);
2667+
if (ret)
2668+
return ret;
2669+
2670+
run_cals:
2671+
return api_call(phy, adi_adrv9001_cals_InitCals_Run, &phy->init_cals, 60000, &errors);
2672+
}
2673+
26182674
static int adrv9001_rx_path_config(struct adrv9002_rf_phy *phy,
26192675
const adi_adrv9001_ChannelState_e state)
26202676
{
@@ -3231,7 +3287,6 @@ static struct adi_adrv9001_SpiSettings adrv9002_spi = {
32313287

32323288
static int adrv9002_setup(struct adrv9002_rf_phy *phy)
32333289
{
3234-
u8 init_cals_error = 0;
32353290
int ret;
32363291
adi_adrv9001_ChannelState_e init_state;
32373292

@@ -3278,8 +3333,7 @@ static int adrv9002_setup(struct adrv9002_rf_phy *phy)
32783333
return ret;
32793334
}
32803335

3281-
ret = api_call(phy, adi_adrv9001_cals_InitCals_Run, &phy->init_cals,
3282-
60000, &init_cals_error);
3336+
ret = adrv9002_init_cals_handle(phy);
32833337
if (ret)
32843338
return ret;
32853339

@@ -3631,6 +3685,11 @@ static const char *const rx_gain_type[] = {
36313685
"Compensated"
36323686
};
36333687

3688+
static const char *const warm_boot[] = {
3689+
"Disabled",
3690+
"Enabled"
3691+
};
3692+
36343693
static void adrv9002_fill_profile_read(struct adrv9002_rf_phy *phy)
36353694
{
36363695
struct adi_adrv9001_DeviceSysConfig *sys = &phy->curr_profile->sysConfig;
@@ -3656,6 +3715,7 @@ static void adrv9002_fill_profile_read(struct adrv9002_rf_phy *phy)
36563715
"Duplex Mode: %s\n"
36573716
"FH enable: %d\n"
36583717
"MCS mode: %s\n"
3718+
"WarmBoot: %s\n"
36593719
"SSI interface: %s\n", clks->deviceClock_kHz * 1000,
36603720
clks->clkPllVcoFreq_daHz * 10ULL, clks->armPowerSavingClkDiv,
36613721
lo_maps[clks->rx1LoSelect], lo_maps[clks->rx2LoSelect],
@@ -3667,7 +3727,7 @@ static void adrv9002_fill_profile_read(struct adrv9002_rf_phy *phy)
36673727
rx_gain_type[rx_cfg[ADRV9002_CHANN_2].profile.gainTableType],
36683728
rx->rxInitChannelMask, tx->txInitChannelMask,
36693729
duplex[sys->duplexMode], sys->fhModeOn, mcs[sys->mcsMode],
3670-
ssi[phy->ssi_type]);
3730+
warm_boot[sys->warmBootEnable], ssi[phy->ssi_type]);
36713731
}
36723732

36733733
int adrv9002_init(struct adrv9002_rf_phy *phy, struct adi_adrv9001_Init *profile)
@@ -4132,6 +4192,70 @@ static ssize_t adrv9002_dpd_tx_coeficcients_write(struct adrv9002_rf_phy *phy, c
41324192
return ret ? ret : count;
41334193
}
41344194

4195+
static ssize_t adrv9002_init_cals_bin_read(struct file *filp, struct kobject *kobj,
4196+
struct bin_attribute *bin_attr, char *buf, loff_t off,
4197+
size_t count)
4198+
{
4199+
struct iio_dev *indio_dev = dev_to_iio_dev(kobj_to_dev(kobj));
4200+
struct adrv9002_rf_phy *phy = iio_priv(indio_dev);
4201+
off_t curr_off = off;
4202+
int ret = 0, len;
4203+
4204+
mutex_lock(&phy->lock);
4205+
if (!off) {
4206+
struct adi_adrv9001_Warmboot_CalNumbers cals;
4207+
4208+
if (phy->warm_boot.cals) {
4209+
/*
4210+
* Someone stop reading the coefficients in the middle of it or we have
4211+
* concurrent cals! As we cannot know which one is it just error out...
4212+
*/
4213+
ret = -EBUSY;
4214+
goto out_unlock;
4215+
}
4216+
4217+
ret = api_call(phy, adi_adrv9001_cals_InitCals_WarmBoot_UniqueEnabledCals_Get,
4218+
&cals, phy->init_cals.chanInitCalMask[0],
4219+
phy->init_cals.chanInitCalMask[1]);
4220+
if (ret)
4221+
goto out_unlock;
4222+
4223+
/*
4224+
* These coefficients buffers are huge and adrv9002_rf_phy is already quite big.
4225+
* That's why we are going with this trouble to allocate + free the memory every
4226+
* time one wants to save the current coefficients.
4227+
*/
4228+
phy->warm_boot.cals = devm_kzalloc(&phy->spi->dev, cals.warmbootMemoryNumBytes,
4229+
GFP_KERNEL);
4230+
if (!phy->warm_boot.cals)
4231+
return -ENOMEM;
4232+
4233+
ret = api_call(phy,
4234+
adi_adrv9001_cals_InitCals_WarmBoot_Coefficients_UniqueArray_Get,
4235+
phy->warm_boot.cals, phy->init_cals.chanInitCalMask[0],
4236+
phy->init_cals.chanInitCalMask[1]);
4237+
if (ret)
4238+
goto out_unlock;
4239+
4240+
phy->warm_boot.size = cals.warmbootMemoryNumBytes;
4241+
}
4242+
4243+
len = memory_read_from_buffer(buf, count, &off, phy->warm_boot.cals, phy->warm_boot.size);
4244+
if (curr_off + count >= phy->warm_boot.size && phy->warm_boot.cals) {
4245+
/*
4246+
* We are done with it. Even if userspace tries to read more,
4247+
* @memory_read_from_buffer() will return 0 (EOF) without trying to copy into the
4248+
* buffer.
4249+
*/
4250+
dev_dbg(&phy->spi->dev, "Freeing memory(%u)...\n", phy->warm_boot.size);
4251+
devm_kfree(&phy->spi->dev, phy->warm_boot.cals);
4252+
phy->warm_boot.cals = NULL;
4253+
}
4254+
out_unlock:
4255+
mutex_unlock(&phy->lock);
4256+
return ret ? ret : len;
4257+
}
4258+
41354259
static int adrv9002_profile_load(struct adrv9002_rf_phy *phy)
41364260
{
41374261
int ret;
@@ -4162,6 +4286,22 @@ static int adrv9002_profile_load(struct adrv9002_rf_phy *phy)
41624286
return ret;
41634287
}
41644288

4289+
static int adrv9002_init_cals_coeffs_name_get(struct adrv9002_rf_phy *phy)
4290+
{
4291+
const char *init_cals;
4292+
4293+
/* Ignore if the profile has no warmboot */
4294+
if (!phy->profile.sysConfig.warmBootEnable)
4295+
return 0;
4296+
4297+
if (phy->ssi_type == ADI_ADRV9001_SSI_TYPE_CMOS)
4298+
init_cals = phy->chip->cmos_cals;
4299+
else
4300+
init_cals = phy->chip->lvds_cals;
4301+
4302+
return strscpy(phy->warm_boot.coeffs_name, init_cals, sizeof(phy->warm_boot.coeffs_name));
4303+
}
4304+
41654305
static void adrv9002_clk_disable(void *data)
41664306
{
41674307
struct clk *clk = data;
@@ -4266,6 +4406,8 @@ ADRV9002_DPD_COEFICCIENTS(1, 7);
42664406
static BIN_ATTR(stream_config, 0222, NULL, adrv9002_stream_bin_write, ADRV9002_STREAM_BINARY_SZ);
42674407
static BIN_ATTR(profile_config, 0644, adrv9002_profile_bin_read, adrv9002_profile_bin_write,
42684408
ADRV9002_PROFILE_MAX_SZ);
4409+
static BIN_ATTR(warmboot_coefficients, 0400, adrv9002_init_cals_bin_read, NULL,
4410+
ADRV9002_INIT_CALS_COEFFS_MAX);
42694411

42704412
static int adrv9002_iio_channels_get(struct adrv9002_rf_phy *phy)
42714413
{
@@ -4389,6 +4531,10 @@ int adrv9002_post_init(struct adrv9002_rf_phy *phy)
43894531
if (ret)
43904532
return ret;
43914533

4534+
ret = adrv9002_init_cals_coeffs_name_get(phy);
4535+
if (ret < 0)
4536+
return ret;
4537+
43924538
ret = adrv9002_init(phy, &phy->profile);
43934539
if (ret)
43944540
return ret;
@@ -4469,6 +4615,10 @@ int adrv9002_post_init(struct adrv9002_rf_phy *phy)
44694615
}
44704616
}
44714617

4618+
ret = device_create_bin_file(&indio_dev->dev, &bin_attr_warmboot_coefficients);
4619+
if (ret)
4620+
return ret;
4621+
44724622
api_call(phy, adi_adrv9001_ApiVersion_Get, &api_version);
44734623
api_call(phy, adi_adrv9001_arm_Version, &arm_version);
44744624
api_call(phy, adi_adrv9001_SiliconVersion_Get, &silicon_version);
@@ -4493,6 +4643,8 @@ static const struct adrv9002_chip_info adrv9002_info[] = {
44934643
.num_channels = ARRAY_SIZE(adrv9002_phy_chan),
44944644
.cmos_profile = "Navassa_CMOS_profile.json",
44954645
.lvd_profile = "Navassa_LVDS_profile.json",
4646+
.cmos_cals = "Navassa_CMOS_init_cals.bin",
4647+
.lvds_cals = "Navassa_LVDS_init_cals.bin",
44964648
.name = "adrv9002-phy",
44974649
.n_tx = ADRV9002_CHANN_MAX,
44984650
},
@@ -4501,6 +4653,8 @@ static const struct adrv9002_chip_info adrv9002_info[] = {
45014653
.num_channels = ARRAY_SIZE(adrv9002_phy_chan),
45024654
.cmos_profile = "Navassa_CMOS_profile.json",
45034655
.lvd_profile = "Navassa_LVDS_profile.json",
4656+
.cmos_cals = "Navassa_CMOS_init_cals.bin",
4657+
.lvds_cals = "Navassa_LVDS_init_cals.bin",
45044658
.name = "adrv9002-phy",
45054659
.n_tx = ADRV9002_CHANN_MAX,
45064660
.rx2tx2 = true,
@@ -4510,6 +4664,8 @@ static const struct adrv9002_chip_info adrv9002_info[] = {
45104664
.num_channels = ARRAY_SIZE(adrv9003_phy_chan),
45114665
.cmos_profile = "Navassa_CMOS_profile_adrv9003.json",
45124666
.lvd_profile = "Navassa_LVDS_profile_adrv9003.json",
4667+
.cmos_cals = "Navassa_CMOS_init_cals_adrv9003.bin",
4668+
.lvds_cals = "Navassa_LVDS_init_cals_adrv9003.bin",
45134669
.name = "adrv9003-phy",
45144670
.n_tx = 1,
45154671
},
@@ -4518,6 +4674,8 @@ static const struct adrv9002_chip_info adrv9002_info[] = {
45184674
.num_channels = ARRAY_SIZE(adrv9003_phy_chan),
45194675
.cmos_profile = "Navassa_CMOS_profile_adrv9003.json",
45204676
.lvd_profile = "Navassa_LVDS_profile_adrv9003.json",
4677+
.cmos_cals = "Navassa_CMOS_init_cals_adrv9003.bin",
4678+
.lvds_cals = "Navassa_LVDS_init_cals_adrv9003.bin",
45214679
.name = "adrv9003-phy",
45224680
.n_tx = 1,
45234681
.rx2tx2 = true,

drivers/iio/adc/navassa/adrv9002.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#ifndef IIO_TRX_ADRV9002_H_
1111
#define IIO_TRX_ADRV9002_H_
1212

13+
#include <linux/limits.h>
1314
#include <linux/clk-provider.h>
1415
#include <linux/clk/clkscale.h>
1516
#include <linux/delay.h>
@@ -25,6 +26,7 @@
2526
#include "adi_adrv9001_rx_gaincontrol_types.h"
2627
#include "adi_adrv9001_rxSettings_types.h"
2728
#include "adi_adrv9001_ssi_types.h"
29+
#include "adi_adrv9001_types.h"
2830
#include "linux_platform.h"
2931

3032
struct iio_chan_spec;
@@ -38,6 +40,8 @@ struct iio_chan_spec;
3840
#define ADRV9002_RX_MAX_GAIN_IDX ADI_ADRV9001_RX_GAIN_INDEX_MAX
3941
#define ADRV9002_DPD_MAX_REGIONS 8
4042
#define ADRV9002_DPD_FH_MAX_REGIONS (ADRV9002_DPD_MAX_REGIONS - 1)
43+
#define ADRV9002_INIT_CALS_COEFFS_MAX \
44+
(ADI_ADRV9001_WB_MAX_NUM_UNIQUE_CALS * ADI_ADRV9001_WB_MAX_NUM_COEFF)
4145

4246
enum {
4347
ADRV9002_CHANN_1,
@@ -217,6 +221,8 @@ struct adrv9002_chip_info {
217221
const struct iio_chan_spec *channels;
218222
const char *cmos_profile;
219223
const char *lvd_profile;
224+
const char *cmos_cals;
225+
const char *lvds_cals;
220226
const char *name;
221227
u32 num_channels;
222228
u32 n_tx;
@@ -229,21 +235,28 @@ struct adrv9002_ext_lo {
229235
u16 divider;
230236
};
231237

238+
struct adrv9002_warm_boot {
239+
char coeffs_name[NAME_MAX];
240+
u32 size;
241+
u8 *cals;
242+
};
243+
232244
struct adrv9002_rf_phy {
233245
const struct adrv9002_chip_info *chip;
234246
struct spi_device *spi;
235247
struct iio_dev *indio_dev;
236248
struct gpio_desc *reset_gpio;
237249
struct gpio_desc *ssi_sync;
238250
struct iio_chan_spec *iio_chan;
251+
struct adrv9002_warm_boot warm_boot;
239252
/* Protect against concurrent accesses to the device */
240253
struct mutex lock;
241254
struct clk *clks[NUM_ADRV9002_CLKS];
242255
struct adrv9002_clock clk_priv[NUM_ADRV9002_CLKS];
243256
struct clk_onecell_data clk_data;
244257
/* each LO controls two ports (at least) */
245258
struct adrv9002_ext_lo ext_los[ADRV9002_CHANN_MAX];
246-
char profile_buf[350];
259+
char profile_buf[400];
247260
size_t profile_len;
248261
char *bin_attr_buf;
249262
u8 *stream_buf;

0 commit comments

Comments
 (0)