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
556559static 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+
717740static 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 ) ;
24952521static 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
24982526static 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+
26182674static 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
32323288static 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+
36343693static 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
36733733int 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+
41354259static 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+
41654305static void adrv9002_clk_disable (void * data )
41664306{
41674307 struct clk * clk = data ;
@@ -4266,6 +4406,8 @@ ADRV9002_DPD_COEFICCIENTS(1, 7);
42664406static BIN_ATTR (stream_config , 0222 , NULL, adrv9002_stream_bin_write , ADRV9002_STREAM_BINARY_SZ ) ;
42674407static 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
42704412static 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,
0 commit comments