/***************************************************************************//** * @file hmc7044.c * @brief Implementation of HMC7044, HMC7043 Driver. * @author DBogdan (dragos.bogdan@analog.com) ******************************************************************************** * Copyright 2018-2020(c) Analog Devices, Inc. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * - Neither the name of Analog Devices, Inc. nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * - The use of this software may or may not infringe the patent rights * of one or more patent holders. This license does not release you * from the requirement that you obtain separate licenses from these * patent holders to use this software. * - Use of the software either in source or binary form, must be run * on or directly connected to an Analog Devices Inc. component. * * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *******************************************************************************/ /******************************************************************************/ /***************************** Include Files **********************************/ /******************************************************************************/ #include #include #include "hmc7044.h" #include "../../../include/error.h" #include "../../../include/util.h" /******************************************************************************/ /********************** Macros and Constants Definitions **********************/ /******************************************************************************/ #define HMC7044_WRITE (0 << 15) #define HMC7044_READ (1 << 15) #define HMC7044_CNT(x) (((x) - 1) << 13) #define HMC7044_ADDR(x) ((x) & 0xFFF) /* Global Control */ #define HMC7044_REG_SOFT_RESET 0x0000 #define HMC7044_SOFT_RESET BIT(0) #define HMC7044_REG_REQ_MODE_0 0x0001 #define HMC7044_RESEED_REQ BIT(7) #define HMC7044_HIGH_PERF_DISTRIB_PATH BIT(6) #define HMC7044_HIGH_PERF_PLL_VCO BIT(5) #define HMC7044_FORCE_HOLDOVER BIT(4) #define HMC7044_MUTE_OUT_DIV BIT(3) #define HMC7044_PULSE_GEN_REQ BIT(2) #define HMC7044_RESTART_DIV_FSM BIT(1) #define HMC7044_SLEEP_MODE BIT(0) #define HMC7044_REG_REQ_MODE_1 0x0002 #define HMC7044_PLL2_AUTOTUNE_TRIG BIT(2) #define HMC7044_SLIP_REQ BIT(1) #define HMC7044_REG_EN_CTRL_0 0x0003 #define HMC7044_RF_RESEEDER_EN BIT(5) #define HMC7044_VCO_SEL(x) (((x) & 0x3) << 3) #define HMC7044_VCO_EXT 0 #define HMC7044_VCO_HIGH 1 #define HMC7044_VCO_LOW 2 #define HMC7044_SYSREF_TIMER_EN BIT(2) #define HMC7044_PLL2_EN BIT(1) #define HMC7044_PLL1_EN BIT(0) #define HMC7044_REG_EN_CTRL_1 0x0004 #define HMC7044_SEVEN_PAIRS(x) ((x) & 0x7f) #define HMC7044_REG_GLOB_MODE 0x0005 #define HMC7044_REF_PATH_EN(x) ((x) & 0xf) #define HMC7044_RFSYNC_EN BIT(4) #define HMC7044_VCOIN_MODE_EN BIT(5) #define HMC7044_SYNC_PIN_MODE(x) (((x) & 0x3) << 6) /* PLL1 */ #define HMC7044_REG_CLKIN0_BUF_CTRL 0x000A #define HMC7044_REG_CLKIN1_BUF_CTRL 0x000B #define HMC7044_REG_CLKIN2_BUF_CTRL 0x000C #define HMC7044_REG_CLKIN3_BUF_CTRL 0x000D #define HMC7044_REG_OSCIN_BUF_CTRL 0x000E #define HMC7044_REG_PLL1_REF_PRIO_CTRL 0x0014 #define HMC7044_HIGH_Z_EN BIT(4) #define HMC7044_LVPECL_EN BIT(3) #define HMC7044_AC_COUPLING_EN BIT(2) #define HMC7044_100_OHM_EN BIT(1) #define HMC7044_BUF_EN BIT(0) #define HMC7044_REG_CLKIN_PRESCALER(x) (0x001C + (x)) #define HMC7044_REG_OSCIN_PRESCALER 0x0020 #define HMC7044_REG_PLL1_R_LSB 0x0021 #define HMC7044_R1_LSB(x) ((x) & 0xff) #define HMC7044_REG_PLL1_R_MSB 0x0022 #define HMC7044_R1_MSB(x) (((x) & 0xff00) >> 8) #define HMC7044_REG_PLL1_N_LSB 0x0026 #define HMC7044_N1_LSB(x) ((x) & 0xff) #define HMC7044_REG_PLL1_N_MSB 0x0027 #define HMC7044_N1_MSB(x) (((x) & 0xff00) >> 8) #define HMC7044_REG_PLL1_LOCK_DETECT 0x0028 #define HMC7044_LOCK_DETECT_SLIP BIT(5) #define HMC7044_LOCK_DETECT_TIMER(x) ((x) & 0x1f) /* PLL2 */ #define HMC7044_REG_PLL2_FREQ_DOUBLER 0x0032 #define HMC7044_PLL2_FREQ_DOUBLER_DIS BIT(0) #define HMC7044_REG_PLL2_R_LSB 0x0033 #define HMC7044_R2_LSB(x) ((x) & 0xff) #define HMC7044_REG_PLL2_R_MSB 0x0034 #define HMC7044_R2_MSB(x) (((x) & 0xf00) >> 8) #define HMC7044_REG_PLL2_N_LSB 0x0035 #define HMC7044_N2_LSB(x) ((x) & 0xff) #define HMC7044_REG_PLL2_N_MSB 0x0036 #define HMC7044_N2_MSB(x) (((x) & 0xff00) >> 8) #define HMC7044_REG_OSCOUT_PATH 0x0039 #define HMC7044_REG_OSCOUT_DRIVER_0 0x003A #define HMC7044_REG_OSCOUT_DRIVER_1 0x003B /* GPIO/SDATA Control */ #define HMC7044_REG_GPI_CTRL(x) (0x0046 + (x)) #define HMC7044_REG_GPI_SEL(x) ((x) & 0xf) #define HMC7044_REG_GPO_CTRL(x) (0x0050 + (x)) #define HMC7044_GPO_SEL(x) (((x) & 0x3f) << 2) #define HMC7044_GPO_MODE BIT(1) #define HMC7044_GPO_EN BIT(0) /* SYSREF/SYNC Control */ #define HMC7044_REG_PULSE_GEN 0x005A #define HMC7044_PULSE_GEN_MODE(x) ((x) & 0x7) #define HMC7044_REG_SYNC 0x005B #define HMC7044_SYNC_RETIME BIT(2) #define HMC7044_SYNC_THROUGH_PLL2 BIT(1) #define HMC7044_SYNC_POLARITY BIT(0) #define HMC7044_REG_SYSREF_TIMER_LSB 0x005C #define HMC7044_SYSREF_TIMER_LSB(x) ((x) & 0xff) #define HMC7044_REG_SYSREF_TIMER_MSB 0x005D #define HMC7044_SYSREF_TIMER_MSB(x) (((x) & 0xf00) >> 8) #define HMC7044_CLK_INPUT_CTRL 0x0064 #define HMC7044_LOW_FREQ_INPUT_MODE BIT(0) #define HMC7044_DIV_2_INPUT_MODE BIT(1) /* Status and Alarm readback */ #define HMC7044_REG_ALARM_READBACK 0x007D #define HMC7044_REG_PLL1_STATUS 0x0082 #define HMC7044_PLL1_FSM_STATE(x) ((x) & 0x7) #define HMC7044_PLL1_ACTIVE_CLKIN(x) (((x) >> 3) & 0x3) #define HMC7044_PLL2_LOCK_DETECT(x) ((x) & 0x1) #define HMC7044_SYSREF_SYNC_STAT(x) ((x) & 0x2) #define HMC7044_CLK_OUT_PH_STATUS(x) ((x) & 0x4) #define HMC7044_PLL1_PLL2_LOCK_STAT(x) ((x) & 0x8) #define HMC7044_SYNC_REQ_STATUS(x) ((x) & 0x10) /* Other Controls */ #define HMC7044_REG_CLK_OUT_DRV_LOW_PW 0x009F #define HMC7044_REG_CLK_OUT_DRV_HIGH_PW 0x00A0 #define HMC7044_REG_PLL1_DELAY 0x00A5 #define HMC7044_REG_PLL1_HOLDOVER 0x00A8 #define HMC7044_REG_VTUNE_PRESET 0x00B0 /* Clock Distribution */ #define HMC7044_REG_CH_OUT_CRTL_0(ch) (0x00C8 + 0xA * (ch)) #define HMC7044_HI_PERF_MODE BIT(7) #define HMC7044_SYNC_EN BIT(6) #define HMC7044_CH_EN BIT(0) #define HMC7044_START_UP_MODE_DYN_EN (BIT(3) | BIT(2)) #define HMC7044_REG_CH_OUT_CRTL_1(ch) (0x00C9 + 0xA * (ch)) #define HMC7044_DIV_LSB(x) ((x) & 0xFF) #define HMC7044_REG_CH_OUT_CRTL_2(ch) (0x00CA + 0xA * (ch)) #define HMC7044_DIV_MSB(x) (((x) >> 8) & 0xFF) #define HMC7044_REG_CH_OUT_CRTL_3(ch) (0x00CB + 0xA * (ch)) #define HMC7044_REG_CH_OUT_CRTL_4(ch) (0x00CC + 0xA * (ch)) #define HMC7044_REG_CH_OUT_CRTL_5(ch) (0x00CD + 0xA * (ch)) #define HMC7044_REG_CH_OUT_CRTL_6(ch) (0x00CE + 0xA * (ch)) #define HMC7044_REG_CH_OUT_CRTL_7(ch) (0x00CF + 0xA * (ch)) #define HMC7044_REG_CH_OUT_CRTL_8(ch) (0x00D0 + 0xA * (ch)) #define HMC7044_DRIVER_MODE(x) (((x) & 0x3) << 3) #define HMC7044_DRIVER_Z_MODE(x) (((x) & 0x3) << 0) #define HMC7044_DYN_DRIVER_EN BIT(5) #define HMC7044_FORCE_MUTE_EN BIT(7) #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_RECOMM_LCM_MIN 30000 #define HMC7044_RECOMM_LCM_MAX 70000 #define HMC7044_RECOMM_FPD1 10000 #define HMC7044_R1_MAX 65535 #define HMC7044_N1_MAX 65535 #define HMC7044_R2_MIN 1 #define HMC7044_R2_MAX 4095 #define HMC7044_N2_MIN 8 #define HMC7044_N2_MAX 65535 #define HMC7044_OUT_DIV_MIN 1 #define HMC7044_OUT_DIV_MAX 4094 /******************************************************************************/ /************************** Functions Implementation **************************/ /******************************************************************************/ /** * SPI register write to device. * @param dev - The device structure. * @param reg - The register address. * @param val - The register data. * @return SUCCESS in case of success, negative error code otherwise. */ int32_t hmc7044_write(struct hmc7044_dev *dev, uint16_t reg, uint8_t val) { uint8_t buf[3]; uint16_t cmd; cmd = HMC7044_WRITE | HMC7044_CNT(1) | HMC7044_ADDR(reg); buf[0] = cmd >> 8; buf[1] = cmd & 0xFF; buf[2] = val; return spi_write_and_read_3wire(dev->spi_desc, buf, NULL, ARRAY_SIZE(buf), 0); } /** * SPI register read from device. * @param dev - The device structure. * @param reg - The register address. * @param val - The register data. * @return SUCCESS in case of success, negative error code otherwise. */ int32_t hmc7044_read(struct hmc7044_dev *dev, uint16_t reg, uint8_t *val) { uint8_t wr_buf[2]; uint8_t rd_data[1]; uint16_t cmd; int ret; cmd = HMC7044_READ | HMC7044_CNT(1) | HMC7044_ADDR(reg); wr_buf[0] = cmd >> 8; wr_buf[1] = cmd & 0xFF; ret = spi_write_and_read_3wire(dev->spi_desc, wr_buf, rd_data, ARRAY_SIZE(wr_buf), ARRAY_SIZE(rd_data)); if (ret != SUCCESS) return ret; *val = rd_data[0]; return SUCCESS; } /** * Calculate the output channel divider. * @param rate - The desired rate. * @param parent_rate - The parent rate. * @return The output divider. */ uint32_t hmc7044_calc_out_div(uint32_t rate, uint32_t parent_rate) { uint32_t div; div = DIV_ROUND_CLOSEST(parent_rate, rate); /* Supported odd divide ratios are 1, 3, and 5 */ if ((div != 1) && (div != 3) && (div != 5) && (div % 2)) div = DIV_ROUND_CLOSEST(parent_rate, rate * 2) * 2; div = clamp_t(unsigned int, div, HMC7044_OUT_DIV_MIN, HMC7044_OUT_DIV_MAX); return div; } /** * Recalculate rate corresponding to a channel. * @param dev - The device structure. * @param chan_num - Channel number. * @param rate - Channel rate. * @return SUCCESS in case of success, negative error code otherwise. */ int32_t hmc7044_clk_recalc_rate(struct hmc7044_dev *dev, uint32_t chan_num, uint64_t *rate) { int i; struct hmc7044_chan_spec *chan = NULL; /* Find the reqested channel number */ for (i = 0; i < dev->num_channels; i++) { if (dev->channels[i].num == chan_num) { chan = &dev->channels[i]; break; } } if (chan == NULL ) return FAILURE; *rate = dev->pll2_freq / chan->divider; return SUCCESS; } /** * Calculate closest possible rate * @param dev - The device structure * @param rate - The desired rate. * @param rounded_rate - The closest possible rate of desired rate. * @return SUCCESS in case of success, negative error code otherwise. */ int32_t hmc7044_clk_round_rate(struct hmc7044_dev *dev, uint32_t rate, uint64_t *rounded_rate) { uint32_t div = hmc7044_calc_out_div(rate, dev->pll2_freq); *rounded_rate = DIV_ROUND_CLOSEST(dev->pll2_freq, div); return SUCCESS; } /** * Set channel rate. * @param dev - The device structure. * @param chan_num - Channel number. * @param rate - Channel rate. * @return SUCCESS in case of success, negative error code otherwise. */ int32_t hmc7044_clk_set_rate(struct hmc7044_dev *dev, uint32_t chan_num, uint64_t rate) { uint32_t div; int32_t ret; int i; struct hmc7044_chan_spec *chan = NULL; /* Find the reqested channel number */ for (i = 0; i < dev->num_channels; i++) { if (dev->channels[i].num == chan_num) { chan = &dev->channels[i]; break; } } if (chan == NULL ) return FAILURE; div = hmc7044_calc_out_div(rate, dev->pll2_freq); chan->divider = div; ret = hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_1(chan->num), HMC7044_DIV_LSB(div)); if(ret < 0) return ret; return hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_2(chan->num), HMC7044_DIV_MSB(div)); } /** * Setup the device. * @param dev - The device structure. * @return SUCCESS in case of success, negative error code otherwise. */ static int32_t hmc7044_setup(struct hmc7044_dev *dev) { struct hmc7044_chan_spec *chan; bool high_vco_en; bool pll2_freq_doubler_en; uint32_t vcxo_freq, pll2_freq; uint32_t clkin_freq[4]; uint32_t lcm_freq; uint32_t in_prescaler[5]; uint32_t pll1_lock_detect; uint32_t n1, r1; uint32_t pfd1_freq; uint32_t vco_limit; uint32_t n2[2], r2[2]; uint32_t i, ref_en = 0; vcxo_freq = dev->vcxo_freq / 1000; pll2_freq = dev->pll2_freq / 1000; lcm_freq = vcxo_freq; for (i = 0; i < ARRAY_SIZE(clkin_freq); i++) { if (dev->clkin_freq_ccf[i]) clkin_freq[i] = dev->clkin_freq_ccf[i] / 1000; else clkin_freq[i] = dev->clkin_freq[i] / 1000; if (clkin_freq[i]) { lcm_freq = greatest_common_divisor(clkin_freq[i], lcm_freq); ref_en |= BIT(i); } } while (lcm_freq > HMC7044_RECOMM_LCM_MAX) lcm_freq /= 2; for (i = 0; i < ARRAY_SIZE(clkin_freq); i++) { if (clkin_freq[i]) in_prescaler[i] = clkin_freq[i] / lcm_freq; else in_prescaler[i] = 1; } in_prescaler[4] = vcxo_freq / lcm_freq; pll1_lock_detect = log_base_2((lcm_freq * 4000) / dev->pll1_loop_bw); /* fVCXO / N1 = fLCM / R1 */ rational_best_approximation(vcxo_freq, lcm_freq, HMC7044_N1_MAX, HMC7044_R1_MAX, &n1, &r1); pfd1_freq = vcxo_freq / n1; while ((pfd1_freq > HMC7044_RECOMM_FPD1) && (n1 <= HMC7044_N1_MAX / 2) && (r1 <= HMC7044_R1_MAX / 2)) { pfd1_freq /= 2; n1 *= 2; r1 *= 2; } dev->pll1_pfd = pfd1_freq; if (pll2_freq < HMC7044_LOW_VCO_MIN || pll2_freq > HMC7044_HIGH_VCO_MAX) return FAILURE; 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, HMC7044_N2_MAX, HMC7044_R2_MAX, &n2[0], &r2[0]); if (pll2_freq != vcxo_freq * n2[0] / r2[0]) { rational_best_approximation(pll2_freq, vcxo_freq, HMC7044_N2_MAX, HMC7044_R2_MAX, &n2[1], &r2[1]); if (abs((int)pll2_freq - (int)(vcxo_freq * 2 * n2[0] / r2[0])) > abs((int)pll2_freq - (int)(vcxo_freq * n2[1] / r2[1]))) { n2[0] = n2[1]; r2[0] = r2[1]; pll2_freq_doubler_en = false; } } while ((n2[0] < HMC7044_N2_MIN) && (r2[0] <= HMC7044_R2_MAX / 2)) { n2[0] *= 2; r2[0] *= 2; } if (n2[0] < HMC7044_N2_MIN) return -FAILURE; /* Resets all registers to default values */ hmc7044_write(dev, HMC7044_REG_SOFT_RESET, HMC7044_SOFT_RESET); mdelay(10); hmc7044_write(dev, HMC7044_REG_SOFT_RESET, 0); mdelay(10); /* Disable all channels */ for (i = 0; i < HMC7044_NUM_CHAN; i++) hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_0(i), 0); /* Load the configuration updates (provided by Analog Devices) */ hmc7044_write(dev, HMC7044_REG_CLK_OUT_DRV_LOW_PW, 0x4d); hmc7044_write(dev, HMC7044_REG_CLK_OUT_DRV_HIGH_PW, 0xdf); hmc7044_write(dev, HMC7044_REG_PLL1_DELAY, 0x06); hmc7044_write(dev, HMC7044_REG_PLL1_HOLDOVER, 0x06); hmc7044_write(dev, HMC7044_REG_VTUNE_PRESET, 0x04); uint8_t rd_back; hmc7044_read(dev, HMC7044_REG_CLK_OUT_DRV_LOW_PW, &rd_back); hmc7044_read(dev, HMC7044_REG_CLK_OUT_DRV_HIGH_PW, &rd_back); hmc7044_read(dev, HMC7044_REG_PLL1_DELAY, &rd_back); hmc7044_read(dev, HMC7044_REG_PLL1_HOLDOVER, &rd_back); hmc7044_read(dev, HMC7044_REG_VTUNE_PRESET, &rd_back); hmc7044_write(dev, HMC7044_REG_GLOB_MODE, HMC7044_SYNC_PIN_MODE(dev->sync_pin_mode) | (dev->clkin0_rfsync_en ? HMC7044_RFSYNC_EN : 0) | (dev->clkin1_vcoin_en ? HMC7044_VCOIN_MODE_EN : 0) | HMC7044_REF_PATH_EN(ref_en)); /* Program PLL2 */ /* Select the VCO range */ hmc7044_write(dev, HMC7044_REG_EN_CTRL_0, (dev->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); /* Program the dividers */ hmc7044_write(dev, HMC7044_REG_PLL2_R_LSB, HMC7044_R2_LSB(r2[0])); hmc7044_write(dev, HMC7044_REG_PLL2_R_MSB, HMC7044_R2_MSB(r2[0])); hmc7044_write(dev, HMC7044_REG_PLL2_N_LSB, HMC7044_N2_LSB(n2[0])); hmc7044_write(dev, HMC7044_REG_PLL2_N_MSB, HMC7044_N2_MSB(n2[0])); /* Program the reference doubler */ hmc7044_write(dev, HMC7044_REG_PLL2_FREQ_DOUBLER, pll2_freq_doubler_en ? 0 : HMC7044_PLL2_FREQ_DOUBLER_DIS); /* Program PLL1 */ /* Set the lock detect timer threshold */ hmc7044_write(dev, HMC7044_REG_PLL1_LOCK_DETECT, HMC7044_LOCK_DETECT_TIMER(pll1_lock_detect)); /* Set the LCM */ for (i = 0; i < ARRAY_SIZE(clkin_freq); i++) { hmc7044_write(dev, HMC7044_REG_CLKIN_PRESCALER(i), in_prescaler[i]); } hmc7044_write(dev, HMC7044_REG_OSCIN_PRESCALER, in_prescaler[4]); /* Program the dividers */ hmc7044_write(dev, HMC7044_REG_PLL1_R_LSB, HMC7044_R2_LSB(r1)); hmc7044_write(dev, HMC7044_REG_PLL1_R_MSB, HMC7044_R2_MSB(r1)); hmc7044_write(dev, HMC7044_REG_PLL1_N_LSB, HMC7044_N2_LSB(n1)); hmc7044_write(dev, HMC7044_REG_PLL1_N_MSB, HMC7044_N2_MSB(n1)); hmc7044_write(dev, HMC7044_REG_PLL1_REF_PRIO_CTRL, dev->pll1_ref_prio_ctrl); /* Program the SYSREF timer */ /* Set the divide ratio */ hmc7044_write(dev, HMC7044_REG_SYSREF_TIMER_LSB, HMC7044_SYSREF_TIMER_LSB(dev->sysref_timer_div)); hmc7044_write(dev, HMC7044_REG_SYSREF_TIMER_MSB, HMC7044_SYSREF_TIMER_MSB(dev->sysref_timer_div)); /* Set the pulse generator mode configuration */ hmc7044_write(dev, HMC7044_REG_PULSE_GEN, HMC7044_PULSE_GEN_MODE(dev->pulse_gen_mode)); /* Enable the input buffers */ hmc7044_write(dev, HMC7044_REG_CLKIN0_BUF_CTRL, dev->in_buf_mode[0]); hmc7044_write(dev, HMC7044_REG_CLKIN1_BUF_CTRL, dev->in_buf_mode[1]); hmc7044_write(dev, HMC7044_REG_CLKIN2_BUF_CTRL, dev->in_buf_mode[2]); hmc7044_write(dev, HMC7044_REG_CLKIN3_BUF_CTRL, dev->in_buf_mode[3]); hmc7044_write(dev, HMC7044_REG_OSCIN_BUF_CTRL, dev->in_buf_mode[4]); /* Set GPIOs */ for (i = 0; i < ARRAY_SIZE(dev->gpi_ctrl); i++) { hmc7044_write(dev, HMC7044_REG_GPI_CTRL(i), dev->gpi_ctrl[i]); } for (i = 0; i < ARRAY_SIZE(dev->gpo_ctrl); i++) { hmc7044_write(dev, HMC7044_REG_GPO_CTRL(i), dev->gpo_ctrl[i]); } mdelay(10); /* Program the output channels */ for (i = 0; i < dev->num_channels; i++) { chan = &dev->channels[i]; if (chan->num >= HMC7044_NUM_CHAN || chan->disable) continue; hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_1(chan->num), HMC7044_DIV_LSB(chan->divider)); hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_2(chan->num), HMC7044_DIV_MSB(chan->divider)); hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_8(chan->num), HMC7044_DRIVER_MODE(chan->driver_mode) | HMC7044_DRIVER_Z_MODE(chan->driver_impedance) | (chan->dynamic_driver_enable ? HMC7044_DYN_DRIVER_EN : 0) | (chan->force_mute_enable ? HMC7044_FORCE_MUTE_EN : 0)); hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_3(chan->num), chan->fine_delay & 0x1F); hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_4(chan->num), chan->coarse_delay & 0x1F); hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_7(chan->num), chan->out_mux_mode & 0x3); hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_0(chan->num), (chan->start_up_mode_dynamic_enable ? HMC7044_START_UP_MODE_DYN_EN : 0) | (chan->output_control0_rb4_enable ? BIT(4) : 0) | (chan->high_performance_mode_dis ? 0 : HMC7044_HI_PERF_MODE) | HMC7044_SYNC_EN | HMC7044_CH_EN); } mdelay(10); /* Do a restart to reset the system and initiate calibration */ hmc7044_write(dev, HMC7044_REG_REQ_MODE_0, HMC7044_RESTART_DIV_FSM); mdelay(1); hmc7044_write(dev, HMC7044_REG_REQ_MODE_0, (dev->high_performance_mode_clock_dist_en ? HMC7044_HIGH_PERF_DISTRIB_PATH : 0)); mdelay(1); return SUCCESS; } /** * Setup the device. * @param dev - The device structure. * @return SUCCESS in case of success, negative error code otherwise. */ static int32_t hmc7043_setup(struct hmc7044_dev *dev) { struct hmc7044_chan_spec *chan; uint32_t i; if (dev->clkin_freq_ccf[0]) dev->pll2_freq = dev->clkin_freq_ccf[0]; else dev->pll2_freq = dev->clkin_freq[0]; if (!dev->pll2_freq) { printf("%s: Failed to get valid parent rate\n", __func__); return FAILURE; } /* Resets all registers to default values */ hmc7044_write(dev, HMC7044_REG_SOFT_RESET, HMC7044_SOFT_RESET); mdelay(10); hmc7044_write(dev, HMC7044_REG_SOFT_RESET, 0); mdelay(10); /* Load the configuration updates (provided by Analog Devices) */ hmc7044_write(dev, HMC7044_REG_CLK_OUT_DRV_LOW_PW, 0x4d); hmc7044_write(dev, HMC7044_REG_CLK_OUT_DRV_HIGH_PW, 0xdf); /* Disable all channels */ for (i = 0; i < HMC7044_NUM_CHAN; i++) hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_0(i), 0); if (dev->pll2_freq < 1000000000U) hmc7044_write(dev, HMC7044_CLK_INPUT_CTRL, HMC7044_LOW_FREQ_INPUT_MODE); hmc7044_write(dev, HMC7044_REG_EN_CTRL_0, (dev->rf_reseeder_en ? HMC7044_RF_RESEEDER_EN : 0) | HMC7044_SYSREF_TIMER_EN); /* Program the SYSREF timer */ /* Set the divide ratio */ hmc7044_write(dev, HMC7044_REG_SYSREF_TIMER_LSB, HMC7044_SYSREF_TIMER_LSB(dev->sysref_timer_div)); hmc7044_write(dev, HMC7044_REG_SYSREF_TIMER_MSB, HMC7044_SYSREF_TIMER_MSB(dev->sysref_timer_div)); /* Set the pulse generator mode configuration */ hmc7044_write(dev, HMC7044_REG_PULSE_GEN, HMC7044_PULSE_GEN_MODE(dev->pulse_gen_mode)); /* Enable the input buffers */ hmc7044_write(dev, HMC7044_REG_CLKIN0_BUF_CTRL, dev->in_buf_mode[0]); hmc7044_write(dev, HMC7044_REG_CLKIN1_BUF_CTRL, dev->in_buf_mode[1]); /* Set GPIOs */ hmc7044_write(dev, HMC7044_REG_GPI_CTRL(0), dev->gpi_ctrl[0]); hmc7044_write(dev, HMC7044_REG_GPO_CTRL(0), dev->gpo_ctrl[0]); /* Program the output channels */ for (i = 0; i < dev->num_channels; i++) { chan = &dev->channels[i]; if (chan->num >= HMC7044_NUM_CHAN || chan->disable) continue; hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_1(chan->num), HMC7044_DIV_LSB(chan->divider)); hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_2(chan->num), HMC7044_DIV_MSB(chan->divider)); hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_8(chan->num), HMC7044_DRIVER_MODE(chan->driver_mode) | HMC7044_DRIVER_Z_MODE(chan->driver_impedance) | (chan->dynamic_driver_enable ? HMC7044_DYN_DRIVER_EN : 0) | (chan->force_mute_enable ? HMC7044_FORCE_MUTE_EN : 0)); hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_3(chan->num), chan->fine_delay & 0x1F); hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_4(chan->num), chan->coarse_delay & 0x1F); hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_7(chan->num), chan->out_mux_mode & 0x3); hmc7044_write(dev, HMC7044_REG_CH_OUT_CRTL_0(chan->num), (chan->start_up_mode_dynamic_enable ? HMC7044_START_UP_MODE_DYN_EN : 0) | (chan->output_control0_rb4_enable ? BIT(4) : 0) | (chan->high_performance_mode_dis ? 0 : HMC7044_HI_PERF_MODE) | HMC7044_SYNC_EN | HMC7044_CH_EN); } mdelay(10); /* Do a restart to reset the system and initiate calibration */ hmc7044_write(dev, HMC7044_REG_REQ_MODE_0, HMC7044_RESTART_DIV_FSM); mdelay(1); hmc7044_write(dev, HMC7044_REG_REQ_MODE_0, (dev->high_performance_mode_clock_dist_en ? HMC7044_HIGH_PERF_DISTRIB_PATH : 0)); mdelay(1); return SUCCESS; } /***************************************************************************//** * @brief hmc7044_reg_init *******************************************************************************/ int32_t hmc7044_reg_init(struct hmc7044_dev **device, const struct hmc7044_init_param *init_param) { int ret = SUCCESS; struct hmc7044_dev *dev; dev = (struct hmc7044_dev *)malloc(sizeof(*dev)); if (!dev) return FAILURE; /* SPI */ ret = spi_init(&dev->spi_desc, init_param->spi_init); *device = dev; return ret; } /** * Initialize the device. * @param device - The device structure. * @param init_param - The structure that contains the device initial * parameters. * @return SUCCESS in case of success, negative error code otherwise. */ int32_t hmc7044_init(struct hmc7044_dev **device, const struct hmc7044_init_param *init_param) { struct hmc7044_dev *dev; int32_t ret; unsigned int i; dev = (struct hmc7044_dev *)malloc(sizeof(*dev)); if (!dev) return FAILURE; ret = spi_init(&dev->spi_desc, init_param->spi_init); if (ret < 0) return ret; dev->is_hmc7043 = init_param->is_hmc7043; dev->clkin_freq[0] = init_param->clkin_freq[0]; dev->clkin_freq[1] = init_param->clkin_freq[1]; dev->clkin_freq[2] = init_param->clkin_freq[2]; dev->clkin_freq[3] = init_param->clkin_freq[3]; dev->clkin_freq_ccf[0] = init_param->clkin_freq_ccf[0]; dev->clkin_freq_ccf[1] = init_param->clkin_freq_ccf[1]; dev->clkin_freq_ccf[2] = init_param->clkin_freq_ccf[2]; dev->clkin_freq_ccf[3] = init_param->clkin_freq_ccf[3]; dev->vcxo_freq = init_param->vcxo_freq; dev->pll1_pfd = init_param->pll1_pfd; dev->pll2_freq = init_param->pll2_freq; dev->pll1_loop_bw = init_param->pll1_loop_bw; dev->sysref_timer_div = init_param->sysref_timer_div; dev->pll1_ref_prio_ctrl = init_param->pll1_ref_prio_ctrl; dev->clkin0_rfsync_en = init_param->clkin0_rfsync_en; dev->clkin1_vcoin_en = init_param->clkin1_vcoin_en; dev->high_performance_mode_clock_dist_en = init_param->high_performance_mode_clock_dist_en; dev->rf_reseeder_en = !init_param->rf_reseeder_disable; dev->sync_pin_mode = init_param->sync_pin_mode; dev->pulse_gen_mode = init_param->pulse_gen_mode; dev->in_buf_mode[0] = init_param->in_buf_mode[0]; dev->in_buf_mode[1] = init_param->in_buf_mode[1]; dev->in_buf_mode[2] = init_param->in_buf_mode[2]; dev->in_buf_mode[3] = init_param->in_buf_mode[3]; dev->in_buf_mode[4] = init_param->in_buf_mode[4]; dev->gpi_ctrl[0] = init_param->gpi_ctrl[0]; dev->gpi_ctrl[1] = init_param->gpi_ctrl[1]; dev->gpi_ctrl[2] = init_param->gpi_ctrl[2]; dev->gpi_ctrl[3] = init_param->gpi_ctrl[3]; dev->gpo_ctrl[0] = init_param->gpo_ctrl[0]; dev->gpo_ctrl[1] = init_param->gpo_ctrl[1]; dev->gpo_ctrl[2] = init_param->gpo_ctrl[2]; dev->gpo_ctrl[3] = init_param->gpo_ctrl[3]; dev->num_channels = init_param->num_channels; dev->channels = (struct hmc7044_chan_spec *) malloc(sizeof(*dev->channels) * dev->num_channels); for (i = 0; i < dev->num_channels; i++) { dev->channels[i].num = init_param->channels[i].num; dev->channels[i].disable = init_param->channels[i].disable; dev->channels[i].divider = init_param->channels[i].divider; dev->channels[i].driver_mode = init_param->channels[i].driver_mode; dev->channels[i].high_performance_mode_dis = init_param->channels[i].high_performance_mode_dis; dev->channels[i].start_up_mode_dynamic_enable = init_param->channels[i].start_up_mode_dynamic_enable; dev->channels[i].dynamic_driver_enable = init_param->channels[i].dynamic_driver_enable; dev->channels[i].output_control0_rb4_enable = init_param->channels[i].output_control0_rb4_enable; dev->channels[i].force_mute_enable = init_param->channels[i].force_mute_enable; dev->channels[i].driver_impedance = init_param->channels[i].driver_impedance; dev->channels[i].coarse_delay = init_param->channels[i].coarse_delay; dev->channels[i].fine_delay = init_param->channels[i].fine_delay; dev->channels[i].out_mux_mode = init_param->channels[i].out_mux_mode; } *device = dev; if (!dev->is_hmc7043) return hmc7044_setup(dev); else return hmc7043_setup(dev); } /** * Remove the device - release resources. * @param device - The device structure. * @return SUCCESS in case of success, negative error code otherwise. */ int32_t hmc7044_remove(struct hmc7044_dev *device) { int32_t ret; ret = spi_remove(device->spi_desc); free(device->channels); free(device); return ret; }