diff --git a/drivers/iio/adc/adrv902x/adrv9025.c b/drivers/iio/adc/adrv902x/adrv9025.c index 124e29ada05cbf..decf81e39861df 100644 --- a/drivers/iio/adc/adrv902x/adrv9025.c +++ b/drivers/iio/adc/adrv902x/adrv9025.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -44,6 +45,10 @@ enum adrv9025_iio_dev_attr { ADRV9025_INIT_CAL, + ADRV9025_CAL_MASK, + ADRV9025_DPD_TX_MASK, + ADRV9025_DPD_RESET, + ADRV9025_DPD_CONFIG_SET, adrv9025_JESD204_FSM_ERROR, adrv9025_JESD204_FSM_PAUSED, adrv9025_JESD204_FSM_STATE, @@ -133,7 +138,6 @@ static int adrv9025_RxLinkSamplingRateFind(adi_adrv9025_Device_t *device, adi_adrv9025_FramerSel_e framerSel, u32 *iqRate_kHz) { - int recoveryAction = ADI_COMMON_ACT_NO_ACTION; adi_adrv9025_AdcSampleXbarSel_e conv = ADI_ADRV9025_ADC_RX1_Q; u32 framerIndex = 0; @@ -164,7 +168,7 @@ static int adrv9025_RxLinkSamplingRateFind(adi_adrv9025_Device_t *device, if (adrv9025Init->dataInterface.framer[framerIndex].jesd204M < 1) { *iqRate_kHz = 0; - return recoveryAction; + return ADI_COMMON_ACT_NO_ACTION; } conv = adrv9025Init->dataInterface.framer[framerIndex] @@ -223,7 +227,7 @@ static int adrv9025_RxLinkSamplingRateFind(adi_adrv9025_Device_t *device, ADI_ERROR_RETURN(device->common.error.newAction); } - return recoveryAction; + return ADI_COMMON_ACT_NO_ACTION; } static int adrv9025_TxLinkSamplingRateFind(adi_adrv9025_Device_t *device, @@ -231,7 +235,6 @@ static int adrv9025_TxLinkSamplingRateFind(adi_adrv9025_Device_t *device, adi_adrv9025_DeframerSel_e deframerSel, u32 *iqRate_kHz) { - int recoveryAction = ADI_COMMON_ACT_NO_ACTION; u32 deframerIndex = 0; /* Check device pointer is not null */ @@ -259,7 +262,7 @@ static int adrv9025_TxLinkSamplingRateFind(adi_adrv9025_Device_t *device, if (adrv9025Init->dataInterface.deframer[deframerIndex].jesd204M < 1) { *iqRate_kHz = 0; - return recoveryAction; + return ADI_COMMON_ACT_NO_ACTION; } //Use samplerate of DAC set to use deframer output 0. @@ -295,7 +298,7 @@ static int adrv9025_TxLinkSamplingRateFind(adi_adrv9025_Device_t *device, .profile.txInputRate_kHz; } - return recoveryAction; + return ADI_COMMON_ACT_NO_ACTION; } static void adrv9025_shutdown(struct adrv9025_rf_phy *phy) @@ -343,8 +346,9 @@ static ssize_t adrv9025_phy_store(struct device *dev, 60000; /*60 seconds timeout*/ u8 initCalsError = 0; - phy->cal_mask.channelMask = - phy->adrv9025PostMcsInitInst.initCals.channelMask; + if (!phy->cal_mask.channelMask) + phy->cal_mask.channelMask = + phy->adrv9025PostMcsInitInst.initCals.channelMask; /* Run Init Cals */ ret = adi_adrv9025_InitCalsRun(phy->madDevice, @@ -361,6 +365,53 @@ static ssize_t adrv9025_phy_store(struct device *dev, ret = adrv9025_dev_err(phy); } break; + case ADRV9025_CAL_MASK: + ret = kstrtou64(buf, 0, &val); + if (ret) + break; + + if (val <= 0x0F) + phy->cal_mask.channelMask = val; + else + ret = -EINVAL; + break; + case ADRV9025_DPD_TX_MASK: + ret = kstrtou64(buf, 0, &val); + if (ret) + break; + + if (val <= 0x0F) + phy->dpdTxChannel = (u8)val; + else + ret = -EINVAL; + break; + case ADRV9025_DPD_RESET: + ret = kstrtobool(buf, &enable); + if (ret) + break; + + if (enable) { + ret = adi_adrv9025_DpdReset(phy->madDevice, + phy->dpdTxChannel, + ADI_ADRV9025_DPD_RESET_FULL); + if (ret) + ret = adrv9025_dev_err(phy); + } + break; + case ADRV9025_DPD_CONFIG_SET: + ret = kstrtobool(buf, &enable); + if (ret) + break; + + phy->dpdTrackingConfig->txChannelMask = phy->dpdTxChannel; + + if (enable) { + ret = adi_adrv9025_DpdTrackingConfigSet(phy->madDevice, + phy->dpdTrackingConfig); + if (ret) + ret = adrv9025_dev_err(phy); + } + break; case adrv9025_JESD204_FSM_RESUME: if (!phy->jdev) { ret = -ENOTSUPP; @@ -417,8 +468,15 @@ static ssize_t adrv9025_phy_show(struct device *dev, val = this_attr->address >> 8; if (val) - ret = sprintf(buf, "%d\n", - !!(phy->cal_mask.calMask & val)); + ret = sysfs_emit(buf, "%d\n", + !!(phy->cal_mask.calMask & val)); + break; + case ADRV9025_CAL_MASK: + ret = sysfs_emit(buf, "0x%x\n", + phy->cal_mask.channelMask); + break; + case ADRV9025_DPD_TX_MASK: + ret = sysfs_emit(buf, "0x%x\n", phy->dpdTxChannel); break; case adrv9025_JESD204_FSM_ERROR: if (!phy->jdev) { @@ -443,7 +501,7 @@ static ssize_t adrv9025_phy_show(struct device *dev, break; } } - ret = sprintf(buf, "%d\n", err); + ret = sysfs_emit(buf, "%d\n", err); break; case adrv9025_JESD204_FSM_PAUSED: if (!phy->jdev) { @@ -471,7 +529,7 @@ static ssize_t adrv9025_phy_show(struct device *dev, break; } } - ret = sprintf(buf, "%d\n", paused); + ret = sysfs_emit(buf, "%d\n", paused); break; case adrv9025_JESD204_FSM_STATE: if (!phy->jdev) { @@ -492,7 +550,7 @@ static ssize_t adrv9025_phy_show(struct device *dev, * just get the first link state; we're assuming that all 3 are in sync * and that adrv9025_JESD204_FSM_PAUSED was called before */ - ret = sprintf(buf, "%s\n", jesd204_link_get_state_str(links[0])); + ret = sysfs_emit(buf, "%s\n", jesd204_link_get_state_str(links[0])); break; case adrv9025_JESD204_FSM_CTRL: if (!phy->jdev) { @@ -500,7 +558,7 @@ static ssize_t adrv9025_phy_show(struct device *dev, break; } - ret = sprintf(buf, "%d\n", phy->is_initialized); + ret = sysfs_emit(buf, "%d\n", phy->is_initialized); break; default: ret = -EINVAL; @@ -531,6 +589,22 @@ static IIO_DEVICE_ATTR(calibrate_tx_lol_ext_en, 0644, ADRV9025_INIT_CAL | (ADI_ADRV9025_TX_LO_LEAKAGE_EXTERNAL << 8)); +static IIO_DEVICE_ATTR(calibrate_ext_path_delay_en, 0644, + adrv9025_phy_show, adrv9025_phy_store, + ADRV9025_INIT_CAL | (ADI_ADRV9025_EXTERNAL_PATH_DELAY << 8)); + +static IIO_DEVICE_ATTR(calibrate_mask, 0644, adrv9025_phy_show, + adrv9025_phy_store, ADRV9025_CAL_MASK); + +static IIO_DEVICE_ATTR(dpd_tx_mask, 0644, adrv9025_phy_show, + adrv9025_phy_store, ADRV9025_DPD_TX_MASK); + +static IIO_DEVICE_ATTR(dpd_reset, 0200, NULL, + adrv9025_phy_store, ADRV9025_DPD_RESET); + +static IIO_DEVICE_ATTR(dpd_tracking_config_set, 0200, NULL, + adrv9025_phy_store, ADRV9025_DPD_CONFIG_SET); + static IIO_DEVICE_ATTR(jesd204_fsm_error, 0444, adrv9025_phy_show, NULL, @@ -562,6 +636,11 @@ static struct attribute *adrv9025_phy_attributes[] = { &iio_dev_attr_calibrate_tx_qec_en.dev_attr.attr, &iio_dev_attr_calibrate_tx_lol_en.dev_attr.attr, &iio_dev_attr_calibrate_tx_lol_ext_en.dev_attr.attr, + &iio_dev_attr_calibrate_ext_path_delay_en.dev_attr.attr, + &iio_dev_attr_calibrate_mask.dev_attr.attr, + &iio_dev_attr_dpd_tx_mask.dev_attr.attr, + &iio_dev_attr_dpd_reset.dev_attr.attr, + &iio_dev_attr_dpd_tracking_config_set.dev_attr.attr, &iio_dev_attr_jesd204_fsm_error.dev_attr.attr, &iio_dev_attr_jesd204_fsm_state.dev_attr.attr, &iio_dev_attr_jesd204_fsm_paused.dev_attr.attr, @@ -636,8 +715,7 @@ static ssize_t adrv9025_phy_lo_read(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, char *buf) { struct adrv9025_rf_phy *phy = iio_priv(indio_dev); - - u64 val; + u64 val = 0; int ret; mutex_lock(&phy->lock); @@ -654,7 +732,7 @@ static ssize_t adrv9025_phy_lo_read(struct iio_dev *indio_dev, } mutex_unlock(&phy->lock); - return ret ? ret : sprintf(buf, "%llu\n", val); + return ret ? ret : sysfs_emit(buf, "%llu\n", val); } #define _ADRV9025_EXT_LO_INFO(_name, _ident) \ @@ -826,8 +904,8 @@ static ssize_t adrv9025_phy_rx_read(struct iio_dev *indio_dev, ret = adi_adrv9025_RxDecPowerGet(phy->madDevice, rxchan, &dec_pwr_mdb); if (ret == 0) - ret = sprintf(buf, "%u.%02u dB\n", dec_pwr_mdb / 1000, - dec_pwr_mdb % 1000); + ret = sysfs_emit(buf, "%u.%02u dB\n", dec_pwr_mdb / 1000, + dec_pwr_mdb % 1000); else ret = adrv9025_dev_err(phy); @@ -836,29 +914,29 @@ static ssize_t adrv9025_phy_rx_read(struct iio_dev *indio_dev, tmask = ADI_ADRV9025_TRACK_RX1_QEC << chan->channel; ret = adi_adrv9025_TrackingCalsEnableGet(phy->madDevice, &mask); if (ret == 0) - ret = sprintf(buf, "%d\n", !!(tmask & mask)); + ret = sysfs_emit(buf, "%d\n", !!(tmask & mask)); break; case RX_HD2: tmask = ADI_ADRV9025_TRACK_RX1_HD2 << chan->channel; ret = adi_adrv9025_TrackingCalsEnableGet(phy->madDevice, &mask); if (ret == 0) - ret = sprintf(buf, "%d\n", !!(tmask & mask)); + ret = sysfs_emit(buf, "%d\n", !!(tmask & mask)); break; case RX_DIG_DC: ret = adi_adrv9025_DigDcOffsetEnableGet(phy->madDevice, &mask16); if (ret == 0) - ret = sprintf(buf, "%d\n", - !!((ADI_ADRV9025_MSHIFT_DC_OFFSET_RX_CH0 << chan->channel) & mask16)); + ret = sysfs_emit(buf, "%d\n", + !!((ADI_ADRV9025_MSHIFT_DC_OFFSET_RX_CH0 << chan->channel) & mask16)); else ret = adrv9025_dev_err(phy); break; case RX_RF_BANDWIDTH: - ret = sprintf(buf, "%u\n", - phy->deviceInitStruct.rx.rxChannelCfg[chan->channel].profile.rfBandwidth_kHz * - 1000); + ret = sysfs_emit(buf, "%u\n", + phy->deviceInitStruct.rx.rxChannelCfg[chan->channel].profile.rfBandwidth_kHz * + 1000); break; default: ret = -EINVAL; @@ -898,6 +976,11 @@ static ssize_t adrv9025_phy_tx_read(struct iio_dev *indio_dev, ret = adi_adrv9025_TrackingCalsEnableGet(phy->madDevice, &mask); val = !!(tmask & mask); break; + case TX_DPD: + tmask = ADI_ADRV9025_TRACK_TX1_DPD << chan->channel; + ret = adi_adrv9025_TrackingCalsEnableGet(phy->madDevice, &mask); + val = !!(tmask & mask); + break; case TX_RF_BANDWIDTH: val = phy->deviceInitStruct.tx.txChannelCfg[chan->channel] .profile.rfBandwidth_kHz * @@ -910,7 +993,7 @@ static ssize_t adrv9025_phy_tx_read(struct iio_dev *indio_dev, mutex_unlock(&phy->lock); if (ret == 0) - ret = sprintf(buf, "%d\n", val); + ret = sysfs_emit(buf, "%d\n", val); else return adrv9025_dev_err(phy); @@ -958,6 +1041,15 @@ static ssize_t adrv9025_phy_tx_write(struct iio_dev *indio_dev, if (ret) ret = adrv9025_dev_err(phy); break; + case TX_DPD: + mask = ADI_ADRV9025_TRACK_TX1_DPD << chan->channel; + + ret = adi_adrv9025_TrackingCalsEnableSet(phy->madDevice, mask, + enable ? ADI_ADRV9025_TRACKING_CAL_ENABLE : + ADI_ADRV9025_TRACKING_CAL_DISABLE); + if (ret) + ret = adrv9025_dev_err(phy); + break; default: ret = -EINVAL; } @@ -1094,6 +1186,7 @@ static struct iio_chan_spec_ext_info adrv9025_phy_tx_ext_info[] = { _ADRV9025_EXT_TX_INFO("quadrature_tracking_en", TX_QEC), _ADRV9025_EXT_TX_INFO("lo_leakage_tracking_en", TX_LOL), _ADRV9025_EXT_TX_INFO("rf_bandwidth", TX_RF_BANDWIDTH), + _ADRV9025_EXT_TX_INFO("dpd_en", TX_DPD), {}, }; static int adrv9025_gainindex_to_gain(struct adrv9025_rf_phy *phy, int channel, @@ -1176,17 +1269,47 @@ static int adrv9025_phy_read_raw(struct iio_dev *indio_dev, } else { adi_adrv9025_RxGain_t rxGain; + chan_no = chan->channel; + + if (chan_no > CHAN_RX4) { + /* For OBS channels, determine which specific channel is enabled */ + ret = adi_adrv9025_RxTxEnableGet(phy->madDevice, &rxchan, &txchan); + if (ret) { + ret = adrv9025_dev_err(phy); + break; + } + + if (chan_no == CHAN_OBS_RX1) { + if ((rxchan & ADI_ADRV9025_ORX1) && !(rxchan & ADI_ADRV9025_ORX2)) { + chan_no = CHAN_OBS_RX1; + } else if (!(rxchan & ADI_ADRV9025_ORX1) && (rxchan & ADI_ADRV9025_ORX2)) { + chan_no = CHAN_OBS_RX2; + } else { + ret = -EINVAL; + break; + } + } else if (chan_no == CHAN_OBS_RX2) { + if ((rxchan & ADI_ADRV9025_ORX3) && !(rxchan & ADI_ADRV9025_ORX4)) { + chan_no = CHAN_OBS_RX3; + } else if (!(rxchan & ADI_ADRV9025_ORX3) && (rxchan & ADI_ADRV9025_ORX4)) { + chan_no = CHAN_OBS_RX4; + } else { + ret = -EINVAL; + break; + } + } + } ret = adi_adrv9025_RxGainGet( - phy->madDevice, 1 << chan->channel, &rxGain); + phy->madDevice, 1 << chan_no, &rxGain); if (ret) { ret = adrv9025_dev_err(phy); break; } - ret = adrv9025_gainindex_to_gain(phy, chan->channel, - rxGain.gainIndex, val, - val2); + adrv9025_gainindex_to_gain(phy, chan->channel, + rxGain.gainIndex, val, + val2); } ret = IIO_VAL_INT_PLUS_MICRO_DB; break; @@ -1257,6 +1380,8 @@ static int adrv9025_phy_write_raw(struct iio_dev *indio_dev, chan_no += 1; if (val) { rxchan |= (ADI_ADRV9025_RX1 << chan_no); + if (chan_no >= CHAN_OBS_RX1) + rxchan &= ~(ADI_ADRV9025_RX1 << (chan_no + 1)); } else { if (chan_no < CHAN_OBS_RX1) { rxchan &= ~(ADI_ADRV9025_RX1 << chan_no); @@ -1293,14 +1418,44 @@ static int adrv9025_phy_write_raw(struct iio_dev *indio_dev, } else { adi_adrv9025_RxGain_t rxGain; + chan_no = chan->channel; - ret = adrv9025_gain_to_gainindex(phy, chan->channel, + if (chan_no > CHAN_RX4) { + ret = adi_adrv9025_RxTxEnableGet(phy->madDevice, &rxchan, + &txchan); + if (ret) { + ret = adrv9025_dev_err(phy); + goto out; + } + + if (chan_no == CHAN_OBS_RX1) { + if (rxchan & ADI_ADRV9025_ORX1 && !(rxchan & ADI_ADRV9025_ORX2)) { + chan_no = CHAN_OBS_RX1; + } else if (!(rxchan & ADI_ADRV9025_ORX1) && (rxchan & ADI_ADRV9025_ORX2)) { + chan_no = CHAN_OBS_RX2; + } else { + ret = -EINVAL; + goto out; + } + } else if (chan_no == CHAN_OBS_RX2) { + if (rxchan & ADI_ADRV9025_ORX3 && !(rxchan & ADI_ADRV9025_ORX4)) { + chan_no = CHAN_OBS_RX3; + } else if (!(rxchan & ADI_ADRV9025_ORX3) && (rxchan & ADI_ADRV9025_ORX4)) { + chan_no = CHAN_OBS_RX4; + } else { + ret = -EINVAL; + goto out; + } + } + } + + ret = adrv9025_gain_to_gainindex(phy, chan_no, val, val2, &code); if (ret < 0) break; rxGain.gainIndex = code; - rxGain.rxChannelMask = 1 << chan->channel; + rxGain.rxChannelMask = 1 << chan_no; ret = adi_adrv9025_RxGainSet(phy->madDevice, &rxGain, 1); @@ -1482,8 +1637,12 @@ static ssize_t adrv9025_rx_qec_status_read(struct adrv9025_rf_phy *phy, if (ret) return adrv9025_dev_err(phy); - return sprintf(buf, "err %d %% %d perf %d iter cnt %d update cnt %d\n", rxQecStatus.errorCode, rxQecStatus.percentComplete, - rxQecStatus.selfcheckIrrDb, rxQecStatus.iterCount, rxQecStatus.updateCount); + return scnprintf(buf, PAGE_SIZE, "err %d %% %d perf %d iter cnt %d update cnt %d\n", + rxQecStatus.errorCode, + rxQecStatus.percentComplete, + rxQecStatus.selfcheckIrrDb, + rxQecStatus.iterCount, + rxQecStatus.updateCount); } static ssize_t adrv9025_tx_qec_status_read(struct adrv9025_rf_phy *phy, @@ -1499,8 +1658,12 @@ static ssize_t adrv9025_tx_qec_status_read(struct adrv9025_rf_phy *phy, if (ret) return adrv9025_dev_err(phy); - return sprintf(buf, "err %d %% %d perf %d iter cnt %d update cnt %d\n", txQecStatus.errorCode, txQecStatus.percentComplete, - txQecStatus.correctionMetric, txQecStatus.iterCount, txQecStatus.updateCount); + return scnprintf(buf, PAGE_SIZE, "err %d %% %d perf %d iter cnt %d update cnt %d\n", + txQecStatus.errorCode, + txQecStatus.percentComplete, + txQecStatus.correctionMetric, + txQecStatus.iterCount, + txQecStatus.updateCount); } static ssize_t adrv9025_tx_lol_status_read(struct adrv9025_rf_phy *phy, @@ -1516,18 +1679,43 @@ static ssize_t adrv9025_tx_lol_status_read(struct adrv9025_rf_phy *phy, if (ret) return adrv9025_dev_err(phy); - return sprintf(buf, "err %d %% %d var %d iter cnt %d update cnt %d\n", txLolStatus.errorCode, txLolStatus.percentComplete, - txLolStatus.varianceMetric, txLolStatus.iterCount, txLolStatus.updateCount); + return scnprintf(buf, PAGE_SIZE, "err %d %% %d var %d iter cnt %d update cnt %d\n", + txLolStatus.errorCode, + txLolStatus.percentComplete, + txLolStatus.varianceMetric, + txLolStatus.iterCount, + txLolStatus.updateCount); +} + +static ssize_t adrv9025_tx_dpd_status_read(struct adrv9025_rf_phy *phy, + adi_adrv9025_TxChannels_e txChannel, + char *buf) +{ + adi_adrv9025_DpdStatus_v2_t txDpdStatus = { 0 }; + int ret; + + mutex_lock(&phy->lock); + ret = adi_adrv9025_DpdStatusGet_v2(phy->madDevice, txChannel, &txDpdStatus); + mutex_unlock(&phy->lock); + if (ret) + return adrv9025_dev_err(phy); + + return scnprintf(buf, PAGE_SIZE, "err %d %% %d iter cnt %d update cnt %d\n", + txDpdStatus.dpdErrorCode, + txDpdStatus.dpdPercentComplete, + txDpdStatus.dpdIterCount, + txDpdStatus.dpdUpdateCount); } static ssize_t adrv9025_debugfs_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct adrv9025_debugfs_entry *entry = file->private_data; + adi_adrv9025_TxChannels_e tx_ch = ADI_ADRV9025_TXOFF; struct adrv9025_rf_phy *phy = entry->phy; + ssize_t len = 0; char buf[700]; u64 val = 0; - ssize_t len = 0; u8 chan; int ret; @@ -1549,11 +1737,24 @@ static ssize_t adrv9025_debugfs_read(struct file *file, char __user *userbuf, val = *(u64 *)entry->out_value; break; default: - ret = -EINVAL; + return -EINVAL; } - } else if (entry->cmd) { switch (entry->cmd) { + case DBGFS_ORX1_TO_TX: + case DBGFS_ORX2_TO_TX: + case DBGFS_ORX3_TO_TX: + case DBGFS_ORX4_TO_TX: + mutex_lock(&phy->lock); + ret = adi_adrv9025_TxToOrxMappingGet(phy->madDevice, + ADI_ADRV9025_ORX1 << (entry->cmd - DBGFS_ORX1_TO_TX), + &tx_ch); + mutex_unlock(&phy->lock); + if (ret) + return adrv9025_dev_err(phy); + + val = (u64)tx_ch; + break; case DBGFS_RX0_QEC_STATUS: case DBGFS_RX1_QEC_STATUS: case DBGFS_RX2_QEC_STATUS: @@ -1586,6 +1787,16 @@ static ssize_t adrv9025_debugfs_read(struct file *file, char __user *userbuf, return ret; len = ret; break; + case DBGFS_TX0_DPD_STATUS: + case DBGFS_TX1_DPD_STATUS: + case DBGFS_TX2_DPD_STATUS: + case DBGFS_TX3_DPD_STATUS: + chan = ADI_ADRV9025_TX1 << (entry->cmd - DBGFS_TX0_DPD_STATUS); + ret = adrv9025_tx_dpd_status_read(phy, chan, buf); + if (ret < 0) + return ret; + len = ret; + break; default: val = entry->val; } @@ -1594,7 +1805,7 @@ static ssize_t adrv9025_debugfs_read(struct file *file, char __user *userbuf, } if (!len) - len = snprintf(buf, sizeof(buf), "%llu\n", val); + len = scnprintf(buf, sizeof(buf), "%llu\n", val); return simple_read_from_buffer(userbuf, count, ppos, buf, len); } @@ -1668,7 +1879,23 @@ static ssize_t adrv9025_debugfs_write(struct file *file, entry->val = val; return count; + case DBGFS_ORX1_TO_TX: + case DBGFS_ORX2_TO_TX: + case DBGFS_ORX3_TO_TX: + case DBGFS_ORX4_TO_TX: + if (ret != 1) + return -EINVAL; + + mutex_lock(&phy->lock); + ret = adi_adrv9025_TxToOrxMappingSet(phy->madDevice, + ADI_ADRV9025_ORX1 << (entry->cmd - DBGFS_ORX1_TO_TX), + val); + mutex_unlock(&phy->lock); + if (ret) + return adrv9025_dev_err(phy); + entry->val = val; + return count; default: break; } @@ -1691,7 +1918,7 @@ static ssize_t adrv9025_debugfs_write(struct file *file, *(u64 *)entry->out_value = val; break; default: - ret = -EINVAL; + return -EINVAL; } } @@ -1723,7 +1950,6 @@ static int adrv9025_register_debugfs(struct iio_dev *indio_dev) { struct adrv9025_rf_phy *phy = iio_priv(indio_dev); umode_t mode = 0644; - struct dentry *d; int i; if (!iio_get_debugfs_dentry(indio_dev)) @@ -1734,6 +1960,10 @@ static int adrv9025_register_debugfs(struct iio_dev *indio_dev) adrv9025_add_debugfs_entry(phy, "bist_framer_loopback", DBGFS_BIST_FRAMER_LOOPBACK); adrv9025_add_debugfs_entry(phy, "bist_tone", DBGFS_BIST_TONE); + adrv9025_add_debugfs_entry(phy, "orx1_to_tx_mapping", DBGFS_ORX1_TO_TX); + adrv9025_add_debugfs_entry(phy, "orx2_to_tx_mapping", DBGFS_ORX2_TO_TX); + adrv9025_add_debugfs_entry(phy, "orx3_to_tx_mapping", DBGFS_ORX3_TO_TX); + adrv9025_add_debugfs_entry(phy, "orx4_to_tx_mapping", DBGFS_ORX4_TO_TX); adrv9025_add_debugfs_entry(phy, "rx0_qec_status", DBGFS_RX0_QEC_STATUS); adrv9025_add_debugfs_entry(phy, "rx1_qec_status", DBGFS_RX1_QEC_STATUS); adrv9025_add_debugfs_entry(phy, "rx2_qec_status", DBGFS_RX2_QEC_STATUS); @@ -1748,14 +1978,18 @@ static int adrv9025_register_debugfs(struct iio_dev *indio_dev) adrv9025_add_debugfs_entry(phy, "tx1_lol_status", DBGFS_TX1_LOL_STATUS); adrv9025_add_debugfs_entry(phy, "tx2_lol_status", DBGFS_TX2_LOL_STATUS); adrv9025_add_debugfs_entry(phy, "tx3_lol_status", DBGFS_TX3_LOL_STATUS); + adrv9025_add_debugfs_entry(phy, "tx0_dpd_status", DBGFS_TX0_DPD_STATUS); + adrv9025_add_debugfs_entry(phy, "tx1_dpd_status", DBGFS_TX1_DPD_STATUS); + adrv9025_add_debugfs_entry(phy, "tx2_dpd_status", DBGFS_TX2_DPD_STATUS); + adrv9025_add_debugfs_entry(phy, "tx3_dpd_status", DBGFS_TX3_DPD_STATUS); for (i = 0; i < phy->adrv9025_debugfs_entry_index; i++) { if (phy->adrv9025_debugfs_entry_index > DBGFS_BIST_TONE) mode = 0400; - d = debugfs_create_file(phy->debugfs_entry[i].propname, mode, - iio_get_debugfs_dentry(indio_dev), - &phy->debugfs_entry[i], - &adrv9025_debugfs_reg_fops); + debugfs_create_file(phy->debugfs_entry[i].propname, mode, + iio_get_debugfs_dentry(indio_dev), + &phy->debugfs_entry[i], + &adrv9025_debugfs_reg_fops); } return 0; } @@ -1824,7 +2058,7 @@ static int adrv9025_clk_register(struct adrv9025_rf_phy *phy, const char *name, char c_name[ADRV9025_MAX_CLK_NAME + 1], p_name[2][ADRV9025_MAX_CLK_NAME + 1]; const char *_parent_name[2]; - u32 rate; + u32 rate = 0; int ret; /* struct adrv9025_clock assignments */ @@ -2075,7 +2309,7 @@ static int adrv9025_jesd204_link_init(struct jesd204_dev *jdev, lnk->jesd_encoder = deframer->enableJesd204C ? JESD204_ENCODER_64B66B : JESD204_ENCODER_8B10B; lnk->subclass = JESD204_SUBCLASS_1; lnk->is_transmit = true; - }; + } return JESD204_STATE_CHANGE_DONE; } @@ -2313,8 +2547,7 @@ static int adrv9025_jesd204_clks_enable(struct jesd204_dev *jdev, priv->link[lnk->link_id].source_id, 0); if (ret) return adrv9025_dev_err(phy); - - }; + } return JESD204_STATE_CHANGE_DONE; } @@ -2369,8 +2602,7 @@ static int adrv9025_jesd204_link_enable(struct jesd204_dev *jdev, priv->link[lnk->link_id].source_id, 1); if (ret) return adrv9025_dev_err(phy); - - }; + } return JESD204_STATE_CHANGE_DONE; } @@ -2446,7 +2678,7 @@ static int adrv9025_jesd204_link_running(struct jesd204_dev *jdev, ADI_ADRV9025_TRACKING_CAL_ENABLE); if (ret) return adrv9025_dev_err(phy); - }; + } return JESD204_STATE_CHANGE_DONE; } @@ -2812,6 +3044,221 @@ static int adrv9025_phy_parse_agc_dt(struct iio_dev *iodev, struct device *dev) &phy->agcConfig->agcSlowloopFastGainChangeBlockEnable, 0, 0, 1); } +static int adrv9025_phy_parse_dpd_config_dt(struct iio_dev *iodev, struct device *dev) +{ + struct adrv9025_rf_phy *phy = iio_priv(iodev); + struct device_node *np = dev->of_node; + int ret; + + ret = ADRV9025_OF_PROP("adi,dpd-indirect-regularization-value", + &phy->dpdTrackingConfig->dpdIndirectRegularizationValue, 20, 0, 63); + if (ret) + return ret; + + ret = ADRV9025_OF_PROP("adi,dpd-direct-regularization-value", + &phy->dpdTrackingConfig->dpdDirectRegularizationValue, 35, 0, 63); + if (ret) + return ret; + + ret = ADRV9025_OF_PROP("adi,dpd-samples", + &phy->dpdTrackingConfig->dpdSamples, 16384, 4096, 61440); + if (ret) + return ret; + + ret = ADRV9025_OF_PROP("adi,dpd-m-threshold", + &phy->dpdTrackingConfig->dpdMThreshold, 2920, 0, 32767); + if (ret) + return ret; + + ret = ADRV9025_OF_PROP("adi,dpd-peak-search-window-size", + &phy->dpdTrackingConfig->dpdPeakSearchWindowSize, 65535, 0, 16777215); + if (ret) + return ret; + + ret = ADRV9025_OF_PROP("adi,dpd-update-mode", + &phy->dpdTrackingConfig->dpdUpdateMode, 0, 0, 2); + if (ret) + return ret; + + ret = ADRV9025_OF_PROP("adi,dpd-min-avg-signal-level", + &phy->dpdTrackingConfig->minAvgSignalLevel, 519, 0, 65535); + if (ret) + return ret; + + ret = ADRV9025_OF_PROP("adi,dpd-mu", + &phy->dpdTrackingConfig->dpdMu, 50, 0, 100); + if (ret) + return ret; + + ret = ADRV9025_OF_PROP("adi,dpd-min-avg-signal-level-orx", + &phy->dpdTrackingConfig->minAvgSignalLevelOrx, 519, 0, 65535); + if (ret) + return ret; + + ret = ADRV9025_OF_PROP("adi,dpd-filter-sel", + &phy->dpdTrackingConfig->dpdFilterSel, 0, 0, 1); + if (ret) + return ret; + + ret = ADRV9025_OF_PROP("adi,dpd-enable-direct-learning", + &phy->dpdTrackingConfig->enableDirectLearning, 0, 0, 1); + if (ret) + return ret; + + return ADRV9025_OF_PROP("adi,dpd-indirect-regularization-low-power-value", + &phy->dpdTrackingConfig->dpdIndirectRegularizationLowPowerValue, + 20, 0, 63); +} + +static int adrv9025_parse_dpd_coef(struct adrv9025_rf_phy *phy, char *data, u32 size) +{ + u8 dpdNumFeatures = 0, i, j, k, lut; + char *ptr = data; + char coef[10]; + char *line; + int ret; + + while ((line = strsep(&ptr, "\n"))) { + /* skip comment lines or blank lines */ + if (line[0] == '#' || !line[0]) + continue; + + if (line >= data + size) + break; + + if (dpdNumFeatures >= ADI_ADRV9025_MAX_DPD_FEATURES) { + dev_err(&phy->spi->dev, + "ERROR: DPD coefficient table exceeds max number of features (%d)\n", + ADI_ADRV9025_MAX_DPD_FEATURES); + break; + } + + ret = sscanf(line, "%hhu %hhu %hhu %hhu %s", &i, &j, &k, &lut, coef); + if (ret != 5) { + dev_err(&phy->spi->dev, + "ERROR: Malformed DPD coefficient table\n"); + return -EINVAL; + } + + if (i == 1 && j == 1 && k == 0) + k = 1; // for 1x1x0, force k to 1 + + phy->dpdModelConfig->dpdFeatures[dpdNumFeatures].i = i; + phy->dpdModelConfig->dpdFeatures[dpdNumFeatures].j = j; + phy->dpdModelConfig->dpdFeatures[dpdNumFeatures].k = k; + phy->dpdModelConfig->dpdFeatures[dpdNumFeatures].lut = lut; + phy->dpdModelConfig->dpdFeatures[dpdNumFeatures].coeffReal_xM = 0; + phy->dpdModelConfig->dpdFeatures[dpdNumFeatures].coeffImaginary_xM = 0; + + dpdNumFeatures++; + } + + phy->dpdModelConfig->txChannelMask = 0; + phy->dpdModelConfig->dpdNumFeatures = dpdNumFeatures; + + return size; +} + +static ssize_t adrv9025_dpd_coef_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(kobj_to_dev(kobj)); + struct adrv9025_rf_phy *phy = iio_priv(indio_dev); + int ret; + + /* force a one write() call as it simplifies things a lot */ + if (off) { + dev_err(&phy->spi->dev, "Coefficients must be set in one write() call\n"); + return -EINVAL; + } + + mutex_lock(&phy->lock); + ret = adrv9025_parse_dpd_coef(phy, buf, count); + mutex_unlock(&phy->lock); + + return ret ? ret : count; +} + +static ssize_t adrv9025_dpd_coef_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(kobj_to_dev(kobj)); + struct adrv9025_rf_phy *phy = iio_priv(indio_dev); + ssize_t len = 0; + int idx; + int ret; + + mutex_lock(&phy->lock); + if (!phy->dpdModelConfig) { + mutex_unlock(&phy->lock); + return -ENODATA; + } + + char *kbuf __free(kfree) = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!kbuf) { + mutex_unlock(&phy->lock); + return -ENOMEM; + } + + for (idx = 0; idx < phy->dpdModelConfig->dpdNumFeatures; idx++) { + adi_adrv9025_DpdFeature_v2_t *f = &phy->dpdModelConfig->dpdFeatures[idx]; + + ret = scnprintf(kbuf + len, PAGE_SIZE - len, "%u %u %u %u %d %d\n", + f->i, f->j, f->k, f->lut, + f->coeffReal_xM, f->coeffImaginary_xM); + if (ret < 0) + break; + len += ret; + if (len >= PAGE_SIZE) + break; + } + mutex_unlock(&phy->lock); + + ret = memory_read_from_buffer(buf, count, &off, kbuf, len); + + return ret; +} + +static BIN_ATTR(dpd_coef_model, 0644, + adrv9025_dpd_coef_read, + adrv9025_dpd_coef_write, PAGE_SIZE); + +struct adrv9025_bin_attr_drop { + const struct bin_attribute *attr; + struct device *dev; +}; + +static void adrv9025_remove_bin_file(void *data) +{ + struct adrv9025_bin_attr_drop *drop = data; + + device_remove_bin_file(drop->dev, drop->attr); +} + +static int adrv9025_bin_attr_add(struct device *dev, const struct bin_attribute *attr) +{ + struct adrv9025_bin_attr_drop *drop; + int ret; + + drop = devm_kzalloc(dev, sizeof(*drop), GFP_KERNEL); + if (!drop) + return -ENOMEM; + + drop->attr = attr; + drop->dev = dev; + + ret = device_create_bin_file(dev, attr); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, adrv9025_remove_bin_file, drop); +} + +static const struct bin_attribute *adrv9025_bin_attributes = + &bin_attr_dpd_coef_model; + static int adrv9025_probe(struct spi_device *spi) { struct iio_dev *indio_dev; @@ -2849,12 +3296,22 @@ static int adrv9025_probe(struct spi_device *spi) phy->agcConfig = kzalloc(sizeof(adi_adrv9025_AgcCfg_t), GFP_KERNEL); if (!(phy->agcConfig)) return -ENOMEM; + phy->dpdModelConfig = devm_kzalloc(&spi->dev, sizeof(adi_adrv9025_DpdModelConfig_v2_t), GFP_KERNEL); + if (!(phy->dpdModelConfig)) + return -ENOMEM; + phy->dpdTrackingConfig = devm_kzalloc(&spi->dev, sizeof(adi_adrv9025_DpdTrackingConfig_t), GFP_KERNEL); + if (!(phy->dpdTrackingConfig)) + return -ENOMEM; mutex_init(&phy->lock); ret = adrv9025_phy_parse_agc_dt(indio_dev, &spi->dev); if (ret) return ret; + ret = adrv9025_phy_parse_dpd_config_dt(indio_dev, &spi->dev); + if (ret) + return ret; + priv = jesd204_dev_priv(jdev); priv->phy = phy; @@ -2935,13 +3392,15 @@ static int adrv9025_probe(struct spi_device *spi) ret = of_property_read_string(np, "adi,init-profile-name", &name); if (ret) { dev_err(&spi->dev, "error missing dt property: adi,init-profile-name\n"); - return -EINVAL; + return adrv9025_dev_err(phy); } ret = adi_adrv9025_UtilityInitFileLoad(phy->madDevice, name, &phy->adrv9025PostMcsInitInst); if (ret) return adrv9025_dev_err(phy); + phy->cal_mask.channelMask = phy->adrv9025PostMcsInitInst.initCals.channelMask; + adrv9025_clk_register(phy, "-rx_sampl_clk", __clk_get_name(phy->dev_clk), NULL, CLK_GET_RATE_NOCACHE | CLK_IGNORE_UNUSED, RX_SAMPL_CLK); @@ -3009,6 +3468,10 @@ static int adrv9025_probe(struct spi_device *spi) } } + ret = adrv9025_bin_attr_add(&indio_dev->dev, adrv9025_bin_attributes); + if (ret) + goto out_iio_device_unregister; + adi_adrv9025_ApiVersionGet(phy->madDevice, &apiVersion); adi_adrv9025_Shutdown(phy->madDevice); adi_adrv9025_HwClose(phy->madDevice); diff --git a/drivers/iio/adc/adrv902x/adrv9025.h b/drivers/iio/adc/adrv902x/adrv9025.h index 9ef9976e0ca5df..9c66193c29c1ae 100644 --- a/drivers/iio/adc/adrv902x/adrv9025.h +++ b/drivers/iio/adc/adrv902x/adrv9025.h @@ -36,6 +36,10 @@ enum debugfs_cmd { DBGFS_BIST_FRAMER_0_PRBS, DBGFS_BIST_FRAMER_LOOPBACK, DBGFS_BIST_TONE, + DBGFS_ORX1_TO_TX, + DBGFS_ORX2_TO_TX, + DBGFS_ORX3_TO_TX, + DBGFS_ORX4_TO_TX, DBGFS_RX0_QEC_STATUS, DBGFS_RX1_QEC_STATUS, DBGFS_RX2_QEC_STATUS, @@ -50,6 +54,10 @@ enum debugfs_cmd { DBGFS_TX1_LOL_STATUS, DBGFS_TX2_LOL_STATUS, DBGFS_TX3_LOL_STATUS, + DBGFS_TX0_DPD_STATUS, + DBGFS_TX1_DPD_STATUS, + DBGFS_TX2_DPD_STATUS, + DBGFS_TX3_DPD_STATUS, }; enum adrv9025_rx_ext_info { @@ -64,6 +72,7 @@ enum adrv9025_tx_ext_info { TX_QEC, TX_LOL, TX_RF_BANDWIDTH, + TX_DPD, }; enum adrv9025_iio_voltage_in { @@ -158,6 +167,11 @@ struct adrv9025_rf_phy { bool is_initialized; int spi_device_id; + + /* DPD */ + adi_adrv9025_DpdModelConfig_v2_t *dpdModelConfig; + adi_adrv9025_TxChannels_e dpdTxChannel; + adi_adrv9025_DpdTrackingConfig_t *dpdTrackingConfig; }; int adrv9025_hdl_loopback(struct adrv9025_rf_phy *phy, bool enable);