diff --git a/Documentation/devicetree/bindings/iio/frequency/hmc7044.txt b/Documentation/devicetree/bindings/iio/frequency/hmc7044.txt index 04f4a734a49640..f0b1f98d70bbac 100644 --- a/Documentation/devicetree/bindings/iio/frequency/hmc7044.txt +++ b/Documentation/devicetree/bindings/iio/frequency/hmc7044.txt @@ -86,6 +86,7 @@ Optional properties hmc7044: pulse generator. - adi,clkin0-rf-sync-enable: CLKIN0 input is used for external RF sync. - adi,clkin1-vco-in-enable: CLKIN1 input is used for external VCO. + - adi,pll2-pfd-invert-enable: Inverts the polarity of PLL2's PFD - adi,rf-reseeder-disable: Disables RF reseed for SYSREF. Optional properties hmc7043: diff --git a/drivers/iio/frequency/hmc7044.c b/drivers/iio/frequency/hmc7044.c index a7560bfb45d638..a5795d6168790c 100644 --- a/drivers/iio/frequency/hmc7044.c +++ b/drivers/iio/frequency/hmc7044.c @@ -126,6 +126,11 @@ #define HMC7044_REG_PLL2_N_MSB 0x0036 #define HMC7044_N2_MSB(x) (((x) & 0xff00) >> 8) +#define HMC7044_REG_PLL2_PFD_CTRL 0x0038 +#define HMC7044_PFD_UP_EN BIT(4) +#define HMC7044_PFD_DOWN_EN BIT(3) +#define HMC7044_PFD_POLARITY BIT(0) + #define HMC7044_REG_OSCOUT_PATH 0x0039 #define HMC7044_REG_OSCOUT_DRIVER_0 0x003A #define HMC7044_REG_OSCOUT_DRIVER_1 0x003B @@ -217,10 +222,13 @@ #define HMC7044_NUM_CHAN 14 -#define HMC7044_LOW_VCO_MIN 2150000 -#define HMC7044_LOW_VCO_MAX 2880000 -#define HMC7044_HIGH_VCO_MIN 2650000 -#define HMC7044_HIGH_VCO_MAX 3200000 +#define HMC7044_LOW_VCO_MIN_KHZ 2150000 +#define HMC7044_LOW_VCO_MAX_KHZ 2880000 +#define HMC7044_HIGH_VCO_MIN_KHZ 2650000 +#define HMC7044_HIGH_VCO_MAX_KHZ 3200000 +#define HMC7044_EXT_VCO_MIN_KHZ 800000 +#define HMC7044_EXT_VCO_MAX_KHZ 3200000 +#define HMC7044_EXT_VCO_LOW_THRESH_KHZ 1000000 #define HMC7044_RECOMM_LCM_MIN 30000 #define HMC7044_RECOMM_LCM_MAX 70000 @@ -305,6 +313,7 @@ struct hmc7044 { bool pll1_ref_autorevert_en; bool clkin0_rfsync_en; bool clkin1_vcoin_en; + bool pll2_pfd_invert_en; bool high_performance_mode_clock_dist_en; bool rf_reseeder_en; bool oscout_path_en; @@ -890,7 +899,7 @@ static int hmc7044_info(struct iio_dev *indio_dev) return 0; } - if (hmc->device_id == HMC7044 && !hmc->clkin1_vcoin_en) { + if (hmc->device_id == HMC7044) { ret = hmc7044_read(indio_dev, HMC7044_REG_PLL1_STATUS, &pll1_stat); if (ret < 0) @@ -910,8 +919,6 @@ static int hmc7044_info(struct iio_dev *indio_dev) return ret; active = HMC7044_PLL1_ACTIVE_CLKIN(pll1_stat); - } else { - active = 1; } if (hmc->device_id == HMC7043) @@ -922,7 +929,7 @@ static int hmc7044_info(struct iio_dev *indio_dev) else clkin_freq = hmc->clkin_freq[active]; - if (hmc->device_id == HMC7044 && !hmc->clkin1_vcoin_en) + if (hmc->device_id == HMC7044) dev_info(&hmc->spi->dev, "PLL1: %s, CLKIN%u @ %u Hz, PFD: %u kHz - PLL2: %s @ %u.%06u MHz\n", pll1_fsm_states[HMC7044_PLL1_FSM_STATE(pll1_stat)], @@ -930,9 +937,40 @@ static int hmc7044_info(struct iio_dev *indio_dev) HMC7044_PLL2_LOCK_DETECT(alarm_stat) ? "Locked" : "Unlocked", hmc->pll2_freq / 1000000, hmc->pll2_freq % 1000000); - else - dev_info(&hmc->spi->dev, "CLKIN%u @ %u.%06u MHz\n", active, - clkin_freq / 1000000, clkin_freq % 1000000); + + return 0; +} + +static int hmc7044_validate_pll2_freq(struct hmc7044 *hmc, unsigned long pll2_freq_khz) +{ + unsigned long limit_max; + unsigned long limit_min; + + if (hmc->clkin1_vcoin_en) { + limit_max = HMC7044_EXT_VCO_MAX_KHZ; + limit_min = HMC7044_EXT_VCO_MIN_KHZ; + } else { + limit_max = HMC7044_HIGH_VCO_MAX_KHZ; + limit_min = HMC7044_LOW_VCO_MIN_KHZ; + } + + if (pll2_freq_khz < limit_min || + pll2_freq_khz > limit_max) { + if (hmc->ignore_vco_limits) { + /* + * Debug only, is at own risk! + * May fail across process, voltage and temperature + */ + dev_warn(&hmc->spi->dev, + "PLL2 frequency %lu kHz is out of range, ignoring limits\n", + pll2_freq_khz); + } else { + dev_err(&hmc->spi->dev, + "PLL2 frequency %lu kHz is out of range (%lu - %lu)\n", + pll2_freq_khz, limit_min, limit_max); + return -EINVAL; + } + } return 0; } @@ -948,12 +986,12 @@ static int hmc7044_setup(struct iio_dev *indio_dev) { struct hmc7044 *hmc = iio_priv(indio_dev); struct hmc7044_chan_spec *chan; - bool high_vco_en; bool pll2_freq_doubler_en; unsigned long vcxo_freq, pll2_freq; unsigned long clkin_freq[4]; unsigned long lcm_freq; unsigned int in_prescaler[5]; + unsigned int vco_sel; unsigned long pll1_lock_detect; unsigned long n1, r1; unsigned long n, r; @@ -961,6 +999,7 @@ static int hmc7044_setup(struct iio_dev *indio_dev) unsigned long vco_limit; unsigned long n2[2], r2[2]; unsigned int i, c, ref_en = 0; + u32 pll1_stat; int ret; vcxo_freq = hmc->vcxo_freq / 1000; @@ -1018,28 +1057,26 @@ static int hmc7044_setup(struct iio_dev *indio_dev) hmc->pll1_pfd = pfd1_freq; - if (pll2_freq < HMC7044_LOW_VCO_MIN || - pll2_freq > HMC7044_HIGH_VCO_MAX) { - if (hmc->ignore_vco_limits) { - /* Debug only, is at own risk! May fail across process, voltage and temperature */ - dev_warn(&hmc->spi->dev, - "PLL2 frequency %lu kHz is out of range, " - "ignoring limits\n", pll2_freq); - } else { - dev_err(&hmc->spi->dev, - "PLL2 frequency %lu kHz is out of range (%u - %u)\n", - pll2_freq, HMC7044_LOW_VCO_MIN / 1000, - HMC7044_HIGH_VCO_MAX / 1000); - return -EINVAL; + ret = hmc7044_validate_pll2_freq(hmc, pll2_freq); + if (ret) + return ret; + + if (hmc->clkin1_vcoin_en) { + vco_sel = HMC7044_VCO_EXT; + if (pll2_freq < HMC7044_EXT_VCO_LOW_THRESH_KHZ) { + ret = hmc7044_write(indio_dev, HMC7044_CLK_INPUT_CTRL, + HMC7044_LOW_FREQ_INPUT_MODE); + if (ret) + return ret; } + } else { + vco_limit = (HMC7044_LOW_VCO_MAX_KHZ + HMC7044_HIGH_VCO_MIN_KHZ) / 2; + if (pll2_freq >= vco_limit) + vco_sel = HMC7044_VCO_HIGH; + else + vco_sel = HMC7044_VCO_LOW; } - vco_limit = (HMC7044_LOW_VCO_MAX + HMC7044_HIGH_VCO_MIN) / 2; - if (pll2_freq >= vco_limit) - high_vco_en = true; - else - high_vco_en = false; - /* fVCO / N2 = fVCXO * doubler / R2 */ pll2_freq_doubler_en = true; rational_best_approximation(pll2_freq, vcxo_freq * 2, @@ -1110,40 +1147,28 @@ static int hmc7044_setup(struct iio_dev *indio_dev) return ret; /* Program PLL2 */ + ret = hmc7044_write(indio_dev, HMC7044_REG_PLL2_PFD_CTRL, + HMC7044_PFD_UP_EN | HMC7044_PFD_DOWN_EN | + (hmc->pll2_pfd_invert_en ? HMC7044_PFD_POLARITY : 0)); + if (ret) + return ret; - /* Select the VCO range */ - - if (hmc->clkin1_vcoin_en) { - hmc->pll2_freq = hmc->clkin_freq_ccf[1] ? - hmc->clkin_freq_ccf[1] : hmc->clkin_freq[1]; - - if (hmc->pll2_freq < 1000000000U) { - ret = hmc7044_write(indio_dev, HMC7044_CLK_INPUT_CTRL, - HMC7044_LOW_FREQ_INPUT_MODE); - if (ret) - return ret; - } + ret = hmc7044_write(indio_dev, HMC7044_REG_EN_CTRL_0, + (hmc->rf_reseeder_en ? HMC7044_RF_RESEEDER_EN : 0) | + HMC7044_VCO_SEL(vco_sel) | + HMC7044_SYSREF_TIMER_EN | HMC7044_PLL2_EN | + (ref_en ? HMC7044_PLL1_EN : 0)); + if (ret) + return ret; - ret = hmc7044_write(indio_dev, HMC7044_REG_EN_CTRL_0, - (hmc->rf_reseeder_en ? HMC7044_RF_RESEEDER_EN : 0) | - HMC7044_VCO_SEL(0) | - HMC7044_SYSREF_TIMER_EN); - if (ret) - return ret; + if (!ref_en) + dev_info(&hmc->spi->dev, + "PLL1 disabled, no valid CLKIN reference\n"); + if (hmc->clkin1_vcoin_en) { ret = hmc7044_write(indio_dev, HMC7044_REG_SYNC, HMC7044_SYNC_RETIME); if (ret) return ret; - } else { - ret = hmc7044_write(indio_dev, HMC7044_REG_EN_CTRL_0, - (hmc->rf_reseeder_en ? HMC7044_RF_RESEEDER_EN : 0) | - HMC7044_VCO_SEL(high_vco_en ? - HMC7044_VCO_HIGH : - HMC7044_VCO_LOW) | - HMC7044_SYSREF_TIMER_EN | HMC7044_PLL2_EN | - HMC7044_PLL1_EN); - if (ret) - return ret; } if (hmc->pll2_cap_bank_sel != ~0) { @@ -1354,17 +1379,11 @@ static int hmc7044_setup(struct iio_dev *indio_dev) if (ret) return ret; - if (!hmc->clkin1_vcoin_en) { - u32 pll1_stat; - - ret = hmc7044_read(indio_dev, HMC7044_REG_PLL1_STATUS, &pll1_stat); - if (ret < 0) - return ret; + ret = hmc7044_read(indio_dev, HMC7044_REG_PLL1_STATUS, &pll1_stat); + if (ret < 0) + return ret; - c = HMC7044_PLL1_ACTIVE_CLKIN(pll1_stat); - } else { - c = 1; /* CLKIN1 */ - } + c = HMC7044_PLL1_ACTIVE_CLKIN(pll1_stat); for (i = 0; i < hmc->num_channels; i++) { chan = &hmc->channels[i]; @@ -1697,6 +1716,11 @@ static int hmc7044_parse_dt(struct device *dev, hmc->clkin1_vcoin_en = of_property_read_bool(np, "adi,clkin1-vco-in-enable"); + hmc->pll2_pfd_invert_en = + of_property_read_bool(np, "adi,pll2-pfd-invert-enable"); + + if (hmc->pll2_pfd_invert_en && !hmc->clkin1_vcoin_en) + dev_warn(dev, "PLL2 PFD inverted using internal VCO.\n"); hmc->ignore_vco_limits = of_property_read_bool(np, "adi,ignore-vco-limits"); hmc->sync_through_pll2_force_r2_eq_1 = @@ -1918,10 +1942,7 @@ static int hmc7044_status_show(struct seq_file *file, void *offset) if (ret < 0) return ret; - if (hmc->clkin1_vcoin_en) - active = 1; - else - active = HMC7044_PLL1_ACTIVE_CLKIN(pll1_stat); + active = HMC7044_PLL1_ACTIVE_CLKIN(pll1_stat); if (hmc->clkin_freq_ccf[active]) clkin_freq = hmc->clkin_freq_ccf[active]; @@ -2139,7 +2160,7 @@ static int hmc7044_jesd204_clks_sync1(struct jesd204_dev *jdev, return ret; } } else { - if (hmc->device_id == HMC7044 && !hmc->clkin0_rfsync_en && !hmc->clkin1_vcoin_en) { + if (hmc->device_id == HMC7044 && !hmc->clkin0_rfsync_en) { ret = hmc7044_sync_pin_set(indio_dev, HMC7044_SYNC_PIN_SYNC); if (ret) return ret; @@ -2151,8 +2172,7 @@ static int hmc7044_jesd204_clks_sync1(struct jesd204_dev *jdev, } ret = hmc7044_toggle_bit(indio_dev, HMC7044_REG_REQ_MODE_0, - HMC7044_RESTART_DIV_FSM, (hmc->device_id == HMC7044 && - !hmc->clkin1_vcoin_en) ? 10000 : 1000); + HMC7044_RESTART_DIV_FSM, (hmc->device_id == HMC7044) ? 10000 : 1000); if (ret) return ret; @@ -2237,7 +2257,7 @@ static int hmc7044_jesd204_clks_sync3(struct jesd204_dev *jdev, __func__, val & 0xFF); } - if (hmc->device_id == HMC7044 && !hmc->clkin0_rfsync_en && !hmc->clkin1_vcoin_en) { + if (hmc->device_id == HMC7044 && !hmc->clkin0_rfsync_en) { ret = hmc7044_sync_pin_set(indio_dev, HMC7044_SYNC_PIN_PULSE_GEN_REQ); if (ret) return ret;