text: /*
   * Copyright (c) 2016 Xilinx, Inc.  All rights reserved.
   *
   * Xilinx, Inc.
   * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A
   * COURTESY TO YOU.  BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS
   * ONE POSSIBLE   IMPLEMENTATION OF THIS FEATURE, APPLICATION OR
   * STANDARD, XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION
   * IS FREE FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE
   * FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION.
   * XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO
   * THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO
   * ANY WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE
   * FROM CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY
   * AND FITNESS FOR A PARTICULAR PURPOSE.
   *
   */
  
  /*****************************************************************************/
  /**
  *
  * @file si5328drv.c
  *
  * This file contains low-level driver functions for controlling the
  * SiliconLabs Si5328 clock generator as mounted on the KC705 demo board.
  * The user should refer to the hardware device specification for more details
  * of the device operation.
  *
  * <pre>
  * MODIFICATION HISTORY:
  *
  * Ver   Who Date         Changes
  * ----- --- ----------   -----------------------------------------------
  * 1.00  hf  2014/10/10   First release
  * 1.10  MG  2016/07/05   Updated LOCKT register
  * 1.11  YH  2016/09/14   Add option to enable fast switching
  * 1.12  YH  2017/07/19   Added SI5328 control using PS IIC
  * 1.13  GM  2017/11/15   Issued powerdown on CLKIN1 & CLKIN2 depending on
  *                           Free Run Mode to force correct clock selection
  * </pre>
  *
  ****************************************************************************/
  
  #include <stdlib.h>
  #include "xil_types.h"
  #include "si5328drv.h"
  #if defined (ARMR5) || (__aarch64__)
  #include "xiicps.h"
  #endif
  
  #include "xiic.h"
  #include "xparameters.h"
  
  /******************************************************************************
   * User settable constant that depends on the specific board design.
   * The defaults are for the Xilinx KC705 board.
   *****************************************************************************/
  
  /**
   * Default register settings that differ from the (power-on-)reset values.
   * This array consists of pairs with the first value the register number and
   * the second number the register value.
   */
  u8 SI5328_DEFAULTS[] = {
      // Disable output clocks during calibration (bit 4 SQ_ICAL=1),
      // other bits are default
        3, 0x15,
      // Auto select clock (automatic revertive) (bit 7:6 AUTOSEL_REG)10
      // History delay default
        4, 0x92,
      // Disable CKOUT2 (SFOUT2_REG=001)
      // set CKOUT1 to LVDS (SFOUT1_REG=111)
      // (default is LVPECL for both)
        6, 0x0F,
      // Enable CKOUT1 output (bit 2 DSBL1_REG = 1)
      // disable CKOUT2 output (bit 3 DSBL2_REG=0)
       10, 0x08,
      // Disable CKIN2 input buffer (bit 1 PD_CK2=1)
      // enable CKIN1 buffer (bit 0 PD_CK1=0)
      // (bit 6 is reserved, write default value)
       11, 0x42,
  
  #if	(XPAR_VID_PHY_CONTROLLER_HDMI_FAST_SWITCH)
      // Set lock time to 13.3ms (bits 2:0 LOCKT=011)
      // and set valid time to 2ms (bits 4:3 VALTIME=00)
      // other bits are default
       19, 0x23,
  #else
  	// Set lock time to 0.833ms (bits 2:0 LOCKT=111)
  	// and set valid time to 100ms (bits 4:3 VALTIME=01)
  	// other bits are default
  	19, 0x2f,
  #endif
      // Enable fast locking (bit 0 FASTLOCK=1)
      137, 0x01   // FASTLOCK=1 (enable fast locking)
      };
  
  #if defined (ARMR5) || (__aarch64__)
  /*****************************************************************************/
  /**
   * Send a list of register settings to the Si5328 clock generator.
   *
   * @param    InstancePtr contains the pointer to PS IIC master device
   * @param    IICAddress contains the 7 bit IIC address of the Si5328 device.
   * @param    BufPtr is a pointer to an array with alternating register addresses
   *           and register values to program into the Si5328. The array length
   *           must be at least 2*NumRegs.
   * @param    NumRegs contains the number of registers to write.
   *
   * @return   SI5328_SUCCESS for success, SI5328_ERR_IIC for IIC access failure,
   *           SI5328_ERR_PARM when the number of registers to write is less than
   *           one.
   *
   * @note     Private function. Does not modify the contents of the buffer
   *           pointed to by BufPtr.
   *****************************************************************************/
  s32 Si5328_DoSettings_Ps(XIicPs *InstancePtr, u16 IICAddress,
  		u8 *BufPtr, s32 NumRegs) {
  	s32 Status;
  	s32 i;
  
  	// Check the number of registers to write. It must be at least one.
  	if (NumRegs < 1) {
  		if (SI5328_DEBUG) {
  			xil_printf("Si5328: ERROR: Illegal number of registers to write.");
  		}
  		return SI5328_ERR_PARM;
  	}
  	/*
  	 * Wait until bus is idle to start another transfer.
  	 */
  	while (XIicPs_BusIsBusy(InstancePtr)) {
  		/* NOP */
  	}
  
  	for (i = 0; i < NumRegs; i++) {
  		//        result = XIic_Send(IICBaseAddress, IICAddress,
  		//                           BufPtr + (i << 1), 2,
  		//                           XIIC_STOP);
  		Status = XIicPs_MasterSendPolled(InstancePtr,
  				BufPtr + (i << 1), 2, IICAddress);
  		if (Status != XST_SUCCESS) {
  			if (SI5328_DEBUG) {
  				xil_printf("Si5328: ERROR: IIC write request error.");
  			}
  			return SI5328_ERR_IIC;
  		}
  
  
  	}
  	return SI5328_SUCCESS;
  }
  #endif
  
  #if defined (ARMR5) || (__aarch64__)
  /*****************************************************************************/
  /**
   * Reset the SiliconLabs Si5328 clock generator.
   *
   * @param    InstancePtr contains the pointer to PS IIC master device
   * @param    IICAddress contains the 7 bit IIC address of the Si5328 device.
   *
   * @return   SI5328_SUCCESS for success, SI5328_ERR_IIC for IIC access failure.
   *
   * @note     Private function.
   *****************************************************************************/
  s32 Si5328_Reset_Ps(XIicPs *InstancePtr, u16 IICAddress) {
      s32 Status;
      u8 buf[2];
  
      if (SI5328_DEBUG) {
          xil_printf("Resetting Si5328.\n");
      }
      buf[0] = 136;  // Register number
      buf[1] = 0x80; // bit 7 = RST_REG = 1: start of reset
      Status = Si5328_DoSettings_Ps(InstancePtr, IICAddress, buf, 2);
      if (Status != SI5328_SUCCESS) {
          // Not all bytes were sent: IIC error occurred
          return SI5328_ERR_IIC;
      }
      // Should wait here for minimum reset active time (this time is not
      // documented in the Si5328 data sheet). Since IIC is slow, assume that
      // enough time has passed before the next IIC command is finished.
      buf[1] = 0x00; // bit 7 = RST_REG = 0: end of reset
      Status = Si5328_DoSettings_Ps(InstancePtr, IICAddress, buf, 2);
      if (Status != SI5328_SUCCESS) {
          // Not all bytes were sent: IIC error occurred
          return SI5328_ERR_IIC;
      }
      return SI5328_SUCCESS;
  }
  #endif
  
  #if defined (ARMR5) || (__aarch64__)
  /*****************************************************************************/
  /**
   * Initialize the SiliconLabs Si5328 clock generator. After initialization,
   * the clock generator is not generating a clock yet. Call si5328_set_clock
   * to start the clock generator.
   *
   * @param    InstancePtr contains the pointer to PS IIC master device
   * @param    IICAddress contains the 7 bit IIC address of the Si5328 device.
   *
   * @return   SI5328_SUCCESS for success, SI5328_ERR_IIC for IIC access failure.
   *****************************************************************************/
  s32 Si5328_Init_Ps(XIicPs *InstancePtr, u16 IICAddress) {
      s32 result;
  
      if (SI5328_DEBUG) {
          xil_printf("Si5328: Initializing.\n");
      }
      result = Si5328_DoSettings_Ps(InstancePtr, IICAddress,
                                 SI5328_DEFAULTS, sizeof(SI5328_DEFAULTS) / 2);
      return result;
  }
  #endif
  
  #if defined (ARMR5) || (__aarch64__)
  /*****************************************************************************/
  /**
   * Set the output frequency of the Si5328 clock generator.
   *
   * @param    InstancePtr contains the pointer to PS IIC master device
   * @param    IICAddress contains the 7 bit IIC address of the Si5328 device.
   * @param    ClkSrc selects the clock input to use.
   * @param    ClkInFreq contains the frequency of the input clock (2kHz-710MHz).
   * @param    ClkOutFreq contains the desired output clock frequency
   *           (2kHz-945MHz).
   *
   * @return   SI5328_SUCCESS for success, SI5328_ERR_IIC for IIC access failure,
   *           SI5328_ERR_FREQ when the requested frequency cannot be generated,
   *           SI5328_ERR_PARM when the ClkSrc or ClkDest parameters are invalid
   *           or the ClkInFreq or ClkOutFreq are out of range.
   *****************************************************************************/
  s32 Si5328_SetClock_Ps(XIicPs *InstancePtr, u16 IICAddress, u8 ClkSrc,
                      u32 ClkInFreq, u32 ClkOutFreq) {
      u32 NCn_ls, N2_ls, N3n;
      u8  N1_hs, N2_hs, BwSel;
      s32 Status;
      u8 regs = 0;
  //    u8  buf[14*2]; // Need to set 14 registers
      if (ClkSrc == SI5328_CLKSRC_XTAL) {
  	regs = 14;
      } else {
  	regs = 43;
      }
      u8  buf[regs*2]; // Need to set 14 registers
      s32 i;
  
      // Sanity check
      if ((ClkSrc < SI5328_CLKSRC_CLK1) || (ClkSrc > SI5328_CLKSRC_XTAL)) {
          if (SI5328_DEBUG) {
              xil_printf("Si5328: ERROR: Incorrect input clock selected!\n");
          }
          return SI5328_ERR_PARM;
      }
      if (ClkSrc == SI5328_CLKSRC_CLK2) {
          if (SI5328_DEBUG) {
              xil_printf("Si5328: ERROR: Clock input 2 not supported!");
          }
          return SI5328_ERR_PARM;
      }
      if ((ClkInFreq < SI5328_FIN_MIN) || (ClkInFreq > SI5328_FIN_MAX)) {
          if (SI5328_DEBUG) {
              xil_printf("Si5328: ERROR: Input frequency out of range!\n");
          }
          return SI5328_ERR_PARM;
      }
      if ((ClkOutFreq < SI5328_FOUT_MIN) || (ClkOutFreq > SI5328_FOUT_MAX)) {
          if (SI5328_DEBUG) {
              xil_printf("Si5328: ERROR: Output frequency out of range!\n");
          }
          return SI5328_ERR_PARM;
      }
  
      // Calculate the frequency settings for the Si5328
      Status = Si5328_CalcFreqSettings(ClkInFreq, ClkOutFreq,
                                       &N1_hs, &NCn_ls, &N2_hs, &N2_ls, &N3n,
                                       &BwSel);
      if (Status != SI5328_SUCCESS) {
          if (SI5328_DEBUG) {
              xil_printf("Si5328: ERROR: Could not determine settings for requested frequency!\n");
          }
          return Status;
      }
  
      /*
       * Set the clock settings
       */
      if (SI5328_DEBUG) {
          xil_printf("Si5328: Programming frequency settings.\n");
      }
      i = 0;
  
  
      // Free running mode or use a reference clock
      if (ClkSrc == SI5328_CLKSRC_XTAL) {
  
      buf[i] = 0;
      if (ClkSrc == SI5328_CLKSRC_XTAL) {
          // Enable free running mode
          buf[i+1] = 0x54;
      } else {
          // Disable free running mode
          buf[i+1] = 0x14;
      }
      i += 2;
  
      // Loop bandwidth
      buf[i]   = 2;
      buf[i+1] = (BwSel << 4) | 0x02;
      i += 2;
  
      // Enable/Disable CLKIN1/2 based on Free Run Mode
      buf[i] = 11;
      if (ClkSrc == SI5328_CLKSRC_CLK1) {
          // Enable CKIN1 & Disable CKIN2
          buf[i+1] = 0x42;
      } else {
          // Disable CKIN1 & Enable CKIN2
          buf[i+1] = 0x41;
      }
      i += 2;
  
      // N1_HS
      buf[i]   = 25;
      buf[i+1] = N1_hs << 5;
      i += 2;
  
      // NC1_LS
      buf[i]   = 31;
      buf[i+1] = (u8)((NCn_ls & 0x000F0000) >> 16);
      buf[i+2] = 32;
      buf[i+3] = (u8)((NCn_ls & 0x0000FF00) >>  8);
      buf[i+4] = 33;
      buf[i+5] = (u8)( NCn_ls & 0x000000FF       );
      i += 6;
  
  //    // NC2_LS
  //    buf[i]   = 34;
  //    buf[i+1] = (u8)((NCn_ls & 0x000F0000) >> 16);
  //    buf[i+2] = 35;
  //    buf[i+3] = (u8)((NCn_ls & 0x0000FF00) >>  8);
  //    buf[i+4] = 36;
  //    buf[i+5] = (u8)( NCn_ls & 0x000000FF       );
  //    i += 6;
  
      // N2_HS and N2_LS
      buf[i]    = 40;
      buf[i+1]  = (N2_hs << 5);
      // N2_LS upper bits (same register as N2_HS)
      buf[i+1] |= (u8)((N2_ls & 0x000F0000) >> 16);
      buf[i+2]  = 41;
      buf[i+3]  = (u8)((N2_ls & 0x0000FF00) >>  8);
      buf[i+4]  = 42;
      buf[i+5]  = (u8)( N2_ls & 0x000000FF       );
      i += 6;
  
      // N3n
      if (ClkSrc == SI5328_CLKSRC_CLK1) {
          // N31
          buf[i]   = 43;
          buf[i+2] = 44;
          buf[i+4] = 45;
      } else {
          // N32
          buf[i]   = 46;
          buf[i+2] = 47;
          buf[i+4] = 48;
      }
      buf[i+1] = (u8)((N3n & 0x00070000) >> 16);
      buf[i+3] = (u8)((N3n & 0x0000FF00) >>  8);
      buf[i+5] = (u8)( N3n & 0x000000FF       );
      i += 6;
  
      // Start calibration
      buf[i]   = 136;
      buf[i+1] = 0x40;
      i += 2;
  
  } else if (ClkInFreq == 48000) { //192000) {
  	// input is 48Khz
  	buf [i] = 0;
  	buf [i+1] = 0x34;
  	i += 2;
  	buf [i] = 1;
  	buf [i+1] = 0xE4;
  	i += 2;
  	buf [i] = 2;
  	buf [i+1] = 0x22;
  	i += 2;
  	buf [i] = 3;
  	buf [i+1] = 0x05;
  	i += 2;
  	buf [i] = 4;
  	buf [i+1] = 0x12;
  	i += 2;
  	buf [i] = 5;
  	buf [i+1] = 0xED;
  	i += 2;
  	buf [i] = 6;
  	buf [i+1] = 0x2D;
  	i += 2;
  	buf [i] = 7;
  	buf [i+1] = 0x2A;
  	i += 2;
  	buf [i] = 8;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 9;
  	buf [i+1] = 0xC0;
  	i += 2;
  	buf [i] = 10;
  	buf [i+1] = 0x08;
  	i += 2;
  	buf [i] = 11;
  	buf [i+1] = 0x42;
  	i += 2;
  	buf [i] = 19;
  	buf [i+1] = 0x29;
  	i += 2;
  	buf [i] = 20;
  	buf [i+1] = 0x3E;
  	i += 2;
  	buf [i] = 21;
  	buf [i+1] = 0xFF;
  	i += 2;
  	buf [i] = 22;
  	buf [i+1] = 0xDF;
  	i += 2;
  	buf [i] = 23;
  	buf [i+1] = 0x1F;
  	i += 2;
  	buf [i] = 24;
  	buf [i+1] = 0x3F;
  	i += 2;
  	buf [i] = 25;
  	buf [i+1] = 0x20;
  	i += 2;
  	buf [i] = 31;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 32;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 33;
  	buf [i+1] = 0x1D;
  	i += 2;
  	buf [i] = 34;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 35;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 36;
  	buf [i+1] = 0x1D;
  	i += 2;
  	buf [i] = 40;
  	buf [i+1] = 0x20;
  	i += 2;
  	buf [i] = 41;
  	buf [i+1] = 0x59;
  	i += 2;
  	buf [i] = 42;
  	buf [i+1] = 0xFF;
  	i += 2;
  	buf [i] = 43;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 44;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 45;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 46;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 47;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 48;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 55;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 131;
  	buf [i+1] = 0x1F;
  	i += 2;
  	buf [i] = 132;
  	buf [i+1] = 0x02;
  	i += 2;
  	buf [i] = 137;
  	buf [i+1] = 0x01;
  	i += 2;
  	buf [i] = 138;
  	buf [i+1] = 0x0; //0x0F;
  	i += 2;
  	buf [i] = 139;
  	buf [i+1] = 0x0; //0xFF;
  	i += 2;
  	buf [i] = 142;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 143;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 136;
  	buf [i+1] = 0x40;
  	i += 2;
      } else if (ClkInFreq == 44100) { //76400) {
  	// input is 44.1 Khz
  	buf [i] =  0;
  	buf [i+1] = 0x34;
  	i += 2;
  	buf [i] =  1;
  	buf [i+1] = 0xE4;
  	i += 2;
  	buf [i] =  2;
  	buf [i+1] = 0x22;
  	i += 2;
  	buf [i] =  3;
  	buf [i+1] = 0x05;
  	i += 2;
  	buf [i] =  4;
  	buf [i+1] = 0x12;
  	i += 2;
  	buf [i] =  5;
  	buf [i+1] = 0xED;
  	i += 2;
  	buf [i] =  6;
  	buf [i+1] = 0x2D;
  	i += 2;
  	buf [i] =  7;
  	buf [i+1] = 0x2A;
  	i += 2;
  	buf [i] =  8;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] =  9;
  	buf [i+1] = 0xC0;
  	i += 2;
  	buf [i] = 10;
  	buf [i+1] = 0x08;
  	i += 2;
  	buf [i] = 11;
  	buf [i+1] = 0x42;
  	i += 2;
  	buf [i] = 19;
  	buf [i+1] = 0x29;
  	i += 2;
  	buf [i] = 20;
  	buf [i+1] = 0x3E;
  	i += 2;
  	buf [i] = 21;
  	buf [i+1] = 0xFF;
  	i += 2;
  	buf [i] = 22;
  	buf [i+1] = 0xDF;
  	i += 2;
  	buf [i] = 23;
  	buf [i+1] = 0x1F;
  	i += 2;
  	buf [i] = 24;
  	buf [i+1] = 0x3F;
  	i += 2;
  	buf [i] = 25;
  	buf [i+1] = 0x60;
  	i += 2;
  	buf [i] = 31;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 32;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 33;
  	buf [i+1] = 0x15;
  	i += 2;
  	buf [i] = 34;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 35;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 36;
  	buf [i+1] = 0x15;
  	i += 2;
  	buf [i] = 40;
  	buf [i+1] = 0x80;
  	i += 2;
  	buf [i] = 41;
  	buf [i+1] = 0x39;
  	i += 2;
  	buf [i] = 42;
  	buf [i+1] = 0xBF;
  	i += 2;
  	buf [i] = 43;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 44;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 45;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 46;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 47;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 48;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 55;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] =131;
  	buf [i+1] = 0x1F;
  	i += 2;
  	buf [i] =132;
  	buf [i+1] = 0x02;
  	i += 2;
  	buf [i] =137;
  	buf [i+1] = 0x01;
  	i += 2;
  	buf [i] =138;
  	buf [i+1] = 0x00;//F;
  	i += 2;
  	buf [i] =139;
  	buf [i+1] = 0x00;//FF;
  	i += 2;
  	buf [i] =142;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] =143;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] =136;
  	buf [i+1] = 0x40;
  	i += 2;
  
      } else if (ClkInFreq == 32000) { //128000) {
  	//for 32Khz input
  	buf [i] =  0;
  	buf [i+1] = 0x34;
  	i += 2;
  	buf [i] =  1;
  	buf [i+1] = 0xE4;
  	i += 2;
  	buf [i] =  2;
  	buf [i+1] = 0x22;
  	i += 2;
  	buf [i] =  3;
  	buf [i+1] = 0x05;
  	i += 2;
  	buf [i] =  4;
  	buf [i+1] = 0x12;
  	i += 2;
  	buf [i] =  5;
  	buf [i+1] = 0xED;
  	i += 2;
  	buf [i] =  6;
  	buf [i+1] = 0x2D;
  	i += 2;
  	buf [i] =  7;
  	buf [i+1] = 0x2A;
  	i += 2;
  	buf [i] =  8;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] =  9;
  	buf [i+1] = 0xC0;
  	i += 2;
  	buf [i] = 10;
  	buf [i+1] = 0x08;
  	i += 2;
  	buf [i] = 11;
  	buf [i+1] = 0x42;
  	i += 2;
  	buf [i] = 19;
  	buf [i+1] = 0x29;
  	i += 2;
  	buf [i] = 20;
  	buf [i+1] = 0x3E;
  	i += 2;
  	buf [i] = 21;
  	buf [i+1] = 0xFF;
  	i += 2;
  	buf [i] = 22;
  	buf [i+1] = 0xDF;
  	i += 2;
  	buf [i] = 23;
  	buf [i+1] = 0x1F;
  	i += 2;
  	buf [i] = 24;
  	buf [i+1] = 0x3F;
  	i += 2;
  	buf [i] = 25;
  	buf [i+1] = 0x20;
  	i += 2;
  	buf [i] = 31;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 32;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 33;
  	buf [i+1] = 0x2B;
  	i += 2;
  	buf [i] = 34;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 35;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 36;
  	buf [i+1] = 0x2B;
  	i += 2;
  	buf [i] = 40;
  	buf [i+1] = 0x20;
  	i += 2;
  	buf [i] = 41;
  	buf [i+1] = 0x83;
  	i += 2;
  	buf [i] = 42;
  	buf [i+1] = 0xFF;
  	i += 2;
  	buf [i] = 43;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 44;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 45;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 46;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 47;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 48;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] = 55;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] =131;
  	buf [i+1] = 0x1F;
  	i += 2;
  	buf [i] =132;
  	buf [i+1] = 0x02;
  	i += 2;
  	buf [i] =137;
  	buf [i+1] = 0x01;
  	i += 2;
  	buf [i] =138;
  	buf [i+1] = 0x00;//F;
  	i += 2;
  	buf [i] =139;
  	buf [i+1] = 0x00;//FF;
  	i += 2;
  	buf [i] =142;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] =143;
  	buf [i+1] = 0x00;
  	i += 2;
  	buf [i] =136;
  	buf [i+1] = 0x40;
  	i += 2;
  
  
      }
  
      // Sanity check
      if (i != sizeof(buf)) {
          if (SI5328_DEBUG) {
              xil_printf("Si5328: FATAL ERROR: Incorrect buffer size while programming frequency settings!");
          }
          exit(-1);
      }
  
      // Send all register settings to the Si5328
      Status = Si5328_DoSettings_Ps(InstancePtr, IICAddress,
                                 buf, i / 2);
      return Status;
  }
  
  #endif
  
  /******************************************************************************
   * Definitions independent on the specific board design. Should not be changed.
   *****************************************************************************/
  
  /*****************************************************************************/
  /**
   * Send a list of register settings to the Si5328 clock generator.
   *
   * @param    IICBaseAddress contains the base address of the IIC master
   *           device.
   * @param    IICAddress contains the 7 bit IIC address of the Si5328 device.
   * @param    BufPtr is a pointer to an array with alternating register addresses
   *           and register values to program into the Si5328. The array length
   *           must be at least 2*NumRegs.
   * @param    NumRegs contains the number of registers to write.
   *
   * @return   SI5328_SUCCESS for success, SI5328_ERR_IIC for IIC access failure,
   *           SI5328_ERR_PARM when the number of registers to write is less than
   *           one.
   *
   * @note     Private function. Does not modify the contents of the buffer
   *           pointed to by BufPtr.
   *****************************************************************************/
  int Si5328_DoSettings(u32 IICBaseAddress, u8 IICAddress,
                        u8 *BufPtr, int NumRegs) {
      int result;
      int i;
  
      // Check the number of registers to write. It must be at least one.
      if (NumRegs < 1) {
          if (SI5328_DEBUG) {
              xil_printf("Si5328: ERROR: Illegal number of registers to write.");
          }
          return SI5328_ERR_PARM;
      }
      for (i = 0; i < NumRegs; i++) {
          result = XIic_Send(IICBaseAddress, IICAddress,
                             BufPtr + (i << 1), 2,
                             XIIC_STOP);
          if (result != 2) {
              if (SI5328_DEBUG) {
                  xil_printf("Si5328: ERROR: IIC write request error.");
              }
              return SI5328_ERR_IIC;
          }
      }
      return SI5328_SUCCESS;
  }
  
  
  /*****************************************************************************/
  /**
   * Reset the SiliconLabs Si5328 clock generator.
   *
   * @param    IICBaseAddress contains the base address of the IIC master
   *           device.
   * @param    IICAddress contains the 7 bit IIC address of the Si5328 device.
   *
   * @return   SI5328_SUCCESS for success, SI5328_ERR_IIC for IIC access failure.
   *
   * @note     Private function.
   *****************************************************************************/
  int Si5328_Reset(u32 IICBaseAddress, u8 IICAddress) {
      int result;
      u8 buf[2];
  
      if (SI5328_DEBUG) {
          xil_printf("Resetting Si5328.\n");
      }
      buf[0] = 136;  // Register number
      buf[1] = 0x80; // bit 7 = RST_REG = 1: start of reset
      result = Si5328_DoSettings(IICBaseAddress, IICAddress, buf, 2);
      if (result != SI5328_SUCCESS) {
          // Not all bytes were sent: IIC error occurred
          return SI5328_ERR_IIC;
      }
      // Should wait here for minimum reset active time (this time is not
      // documented in the Si5328 data sheet). Since IIC is slow, assume that
      // enough time has passed before the next IIC command is finished.
      buf[1] = 0x00; // bit 7 = RST_REG = 0: end of reset
      result = Si5328_DoSettings(IICBaseAddress, IICAddress, buf, 2);
      if (result != SI5328_SUCCESS) {
          // Not all bytes were sent: IIC error occurred
          return SI5328_ERR_IIC;
      }
      return SI5328_SUCCESS;
  }
  
  
  /*****************************************************************************/
  /**
   * Initialize the SiliconLabs Si5328 clock generator. After initialization,
   * the clock generator is not generating a clock yet. Call si5328_set_clock
   * to start the clock generator.
   *
   * @param    IICBaseAddress contains the base address of the IIC master
   *           device.
   * @param    IICAddress contains the 7 bit IIC address of the Si5328 device.
   *
   * @return   SI5328_SUCCESS for success, SI5328_ERR_IIC for IIC access failure.
   *****************************************************************************/
  int Si5328_Init(u32 IICBaseAddress, u8 IICAddress) {
      int result;
  
      //Si5328_Reset(IICBaseAddress, IICAddress);
      if (SI5328_DEBUG) {
          xil_printf("Si5328: Initializing.\n");
      }
      result = Si5328_DoSettings(IICBaseAddress, IICAddress,
                                 SI5328_DEFAULTS, sizeof(SI5328_DEFAULTS) / 2);
      return result;
  }
  
  
  /*****************************************************************************/
  /**
   * Find the closest rational approximation for the N2_LS/N3 fraction.
   *
   * @param f  Holds the N2_LS/N3 fraction in 36.28 fixed point notation.
   * @param md Holds the maximum denominator (N3) value allowed.
   * @param num Will store the numinator (N2_LS) found.
   * @param denom Will store the denominator (N3) found.
   */
  void Si5328_RatApprox(u64 f, u64 md, u32 *num, u32 *denom)
  {
      /*  a: Continued fraction coefficients. */
      u64 a, h[3] = { 0, 1, 0 }, k[3] = { 1, 0, 0 };
      u64 x, d, n = 1;
      int i = 0;
  
      // Degenerate case: only n/1 solution allowed. Return trunc(f)/1.
      if (md <= 1) {
          *denom = 1;
          *num = (u32)(f >> 28);
          return;
      }
  
      // Multiply fraction until there are no more digits after the decimal point
      n <<= 28;
      for (i = 0; i < 28; i++) {
          if ((f & 0x1) == 0) {
              n >>= 1;
              f >>= 1;
          } else {
              break;
          }
      }
      d = f;
  
      /* Continued fraction and check denominator each step */
      for (i = 0; i < 64; i++) {
          a = n ? d / n : 0;
          if (i && !a) {
              break;
          }
  
          x = d;
          d = n;
          n = x % n;
  
          x = a;
          if (k[1] * a + k[0] >= md) {
              x = (md - k[0]) / k[1];
              if (x * 2 >= a || k[1] >= md) {
                  i = 65;
              } else {
                  break;
              }
          }
  
          h[2] = x * h[1] + h[0];
          h[0] = h[1];
          h[1] = h[2];
          k[2] = x * k[1] + k[0];
          k[0] = k[1];
          k[1] = k[2];
      }
      *denom = (u32)k[1];
      *num   = (u32)h[1];
  }
  
  
  /*****************************************************************************/
  /**
   * Search through the possible settings for the N2_LS parameter. Finds the best
   * setting for N2_LS and N3n with the values for N1_HS, NCn_LS, and N2_HS
   * already set in settings.
   *
   * @param settings Holds the settings up till now.
   * @return 1 when the best possible result has been found.
   * @note     Private function.
   */
  int Si5328_FindN2ls(si5328_settings_t *settings) {
      u32 result = 0;
      u64 f3_actual;
      u64 fosc_actual;
      u64 fout_actual;
      u64 delta_fout;
      u64 n2_ls_div_n3;
      u32 mult;
  
      n2_ls_div_n3 = settings->fosc / (settings->fin >> 28) / settings->n2_hs / 2;
      Si5328_RatApprox(n2_ls_div_n3, settings->n3_max, &(settings->n2_ls), &(settings->n3));
      settings->n2_ls *= 2;
      // Rational approximation returns the smalles ratio possible. Upscaling
      // might be needed when when one or both of the numbers are too low.
      if (settings->n2_ls < settings->n2_ls_min) {
          mult =  settings->n2_ls_min / settings->n2_ls;
          mult = (settings->n2_ls_min % settings->n2_ls) ? mult + 1 : mult;
          settings->n2_ls *= mult;
          settings->n3    *= mult;
      }
      if (settings->n3 < settings->n3_min) {
          mult =  settings->n3_min / settings->n3;
          mult = (settings->n3_min % settings->n3) ? mult + 1 : mult;
          settings->n2_ls *= mult;
          settings->n3    *= mult;
      }
      if (SI5328_DEBUG) {
          xil_printf("\t\t\tTrying N2_LS = %d N3 = %d.\n",
              settings->n2_ls, settings->n3);
      }
      // Check if N2_LS and N3 are within the required ranges
      if ((settings->n2_ls < settings->n2_ls_min) || (settings->n2_ls > settings->n2_ls_max)) {
           xil_printf("\t\t\tN2_LS out of range.\n");
      } else if ((settings->n3 < settings->n3_min) || (settings->n3 > settings->n3_max)) {
          xil_printf("\t\t\tN3 out of range.\n");
      }
      else {
          // N2_LS and N3 values within range: check actual output frequency
          f3_actual = settings->fin / settings->n3;
          fosc_actual = f3_actual * settings->n2_hs * settings->n2_ls;
          fout_actual = fosc_actual / (settings->n1_hs * settings->nc_ls);
          delta_fout = fout_actual - settings->fout;
          // Check actual frequencies for validity
          if ((f3_actual < ((u64)SI5328_F3_MIN) << 28) || (f3_actual > ((u64)SI5328_F3_MAX) << 28)) {
              if (SI5328_DEBUG) {
                  xil_printf("\t\t\tF3 frequency out of range.\n");
              }
          } else if ((fosc_actual < ((u64)SI5328_FOSC_MIN) << 28) || (fosc_actual > ((u64)SI5328_FOSC_MAX) << 28)) {
              if (SI5328_DEBUG) {
                  xil_printf("\t\t\tFosc frequency out of range.\n");
              }
          } else if ((fout_actual < ((u64)SI5328_FOUT_MIN) << 28) || (fout_actual >((u64)SI5328_FOUT_MAX) << 28)) {
              if (SI5328_DEBUG) {
                  xil_printf("\t\t\tFout frequency out of range.\n");
              }
          } else {
              if (SI5328_DEBUG) {
                  xil_printf("\t\t\tFound solution: fout = %dHz delta = %dHz.\n",
                      (u32)(fout_actual >> 28), (u32)(delta_fout >> 28));
                  xil_printf("\t\t\t                fosc = %dkHz f3 = %dHz.\n",
                      (u32)((fosc_actual >> 28) / 1000), (u32)(f3_actual >> 28));
              }
              if (((u64)llabs(delta_fout)) < settings->best_delta_fout) {
                  // Found a better solution: remember this one!
                  if (SI5328_DEBUG) {
                      xil_printf("\t\t\tThis solution is the best yet!\n");
                  }
                  settings->best_n1_hs = settings->n1_hs;
                  settings->best_nc_ls = settings->nc_ls;
                  settings->best_n2_hs = settings->n2_hs;
                  settings->best_n2_ls = settings->n2_ls;
                  settings->best_n3 = settings->n3;
                  settings->best_fout = fout_actual;
                  settings->best_delta_fout = llabs(delta_fout);
                  if (delta_fout == 0) {
                      // Best possible result found. Skip the rest of the possibilities.
                      result = 1;
                  }
              }
          }
      }
      return result;
  }
  
  
  /*****************************************************************************/
  /**
   * Find a valid setting for N2_HS and N2_LS. Finds the best
   * setting for N2_HS, N2_LS, and N3n with the values for N1_HS, and NCn_LS
   * already set in settings. Iterates over all possibilities
   * of N2_HS and then performs a binary search over the N2_LS values.
   *
   * @param settings Holds the settings up till now.
   * @return 1 when the best possible result has been found.
   * @note     Private function.
   */
  int Si5328_FindN2(si5328_settings_t *settings) {
      u32 result;
  
      for (settings->n2_hs = SI5328_N2_HS_MAX; settings->n2_hs >= SI5328_N2_HS_MIN; settings->n2_hs--) {
          if (SI5328_DEBUG) {
              xil_printf("\t\tTrying N2_HS = %d.\n", settings->n2_hs);
          }
          settings->n2_ls_min = (u32)(settings->fosc / ((u64)(SI5328_F3_MAX * settings->n2_hs) << 28));
          if (settings->n2_ls_min < SI5328_N2_LS_MIN) {
              settings->n2_ls_min = SI5328_N2_LS_MIN;
          }
          settings->n2_ls_max = (u32)(settings->fosc / ((u64)(SI5328_F3_MIN * settings->n2_hs) << 28));
          if (settings->n2_ls_max > SI5328_N2_LS_MAX) {
              settings->n2_ls_max = SI5328_N2_LS_MAX;
          }
          result = Si5328_FindN2ls(settings);
          if (result) {
              // Best possible result found. Skip the rest of the possibilities.
              break;
          }
      }
      return result;
  }
  
  
  /*****************************************************************************/
  /**
   * Calculates the valid range for NCn_LS with the value for the output
   * frequency and N1_HS already set in settings.
   *
   * @param settings Holds the input and output frequencies and the setting
   *                 for N1_HS.
   * @return -1 when there are no valid settings for NCn_LS, 0 otherwise.
   * @note     Private function.
   */
  int Si5328_CalcNclsLimits(si5328_settings_t *settings) {
      // Calculate limits for NCn_LS
      settings->nc_ls_min = settings->n1_min / settings->n1_hs;
      if (settings->nc_ls_min < SI5328_NC_LS_MIN) {
          settings->nc_ls_min = SI5328_NC_LS_MIN;
      }
      // Make sure NC_ls_min is one or even
      if ((settings->nc_ls_min > 1) && ((settings->nc_ls_min & 0x1) == 1)) {
          settings->nc_ls_min++;
      }
      settings->nc_ls_max = settings->n1_max / settings->n1_hs;
      if (settings->nc_ls_max > SI5328_NC_LS_MAX) {
          settings->nc_ls_max = SI5328_NC_LS_MAX;
      }
      // Make sure NC_ls_max is even
      if ((settings->nc_ls_max & 0x1) == 1) {
          settings->nc_ls_max--;
      }
      // Check if actual N1 is within limits
      if ((settings->nc_ls_max * settings->n1_hs < settings->n1_min) ||
          (settings->nc_ls_min * settings->n1_hs > settings->n1_max)) {
          // No valid NC_ls possible: take next N1_hs
          return -1;
      }
      return 0;
  }
  
  
  /*****************************************************************************/
  /**
   * Find a valid setting for NCn_LS that can deliver the correct output
   * frequency. Assumes that the valid range is relatively small so a full search
   * can be done (should be true for video clock frequencies).
   *
   * @param settings Holds the input and output frequencies, the setting for
   *                 N1_HS, and the limits for NCn_LS.
   * @return 1 when the best possible result has been found.
   * @note     Private function.
   */
  int Si5328_FindNcls(si5328_settings_t *settings) {
      u64 fosc_1;
      u32 result;
  
      fosc_1 = settings->fout * settings->n1_hs;
      for (settings->nc_ls = settings->nc_ls_min; settings->nc_ls <= settings->nc_ls_max;) {
          settings->fosc = fosc_1 * settings->nc_ls;
          if (SI5328_DEBUG) {
              xil_printf("\tTrying NCn_LS = %d: fosc = %dkHz.\n",
                      settings->nc_ls, (u32)((settings->fosc >> 28) / 1000));
          }
          result = Si5328_FindN2(settings);
          if (result) {
              // Best possible result found. Skip the rest of the possibilities.
              break;
          }
          if (settings->nc_ls == 1) {
              settings->nc_ls++;
          } else {
              settings->nc_ls += 2;
          }
      }
      return result;
  }
  
  /*****************************************************************************/
  /**
   * Calculate the frequency settings for the desired output frequency.
   *
   * @param    ClkInFreq contains the frequency of the input clock.
   * @param    ClkOutFreq contains the desired output clock frequency.
   * @param    N1_hs  will be set to the value for the N1_HS register.
   * @param    NCn_ls will be set to the value for the NCn_LS register.
   * @param    N2_hs  will be set to the value for the N2_HS register.
   * @param    N2_ls  will be set to the value for the N2_LS register.
   * @param    N3n    will be set to the value for the N3n register.
   * @param    BwSel  will be set to the value for the BW_SEL register.
   *
   * @return   SI5328_SUCCESS for success, SI5328_ERR_FREQ when the requested
   *           frequency cannot be generated.
   * @note     Private function.
   *****************************************************************************/
  int Si5328_CalcFreqSettings(u32 ClkInFreq, u32 ClkOutFreq,
                          u8  *N1_hs, u32 *NCn_ls,
                          u8  *N2_hs, u32 *N2_ls,
                          u32 *N3n,   u8  *BwSel) {
      /* TBD */
      si5328_settings_t settings;
      int result;
  
      settings.fin = (u64)ClkInFreq  << 28; // 32.28 fixed point
      settings.fout= (u64)ClkOutFreq << 28; // 32.28 fixed point
      settings.best_delta_fout = settings.fout; // High frequency error to start with
  
      // Calculate some limits for N1_HS * NCn_LS and for N3 base on the input
      // and output frequencies.
      settings.n1_min = (int)(SI5328_FOSC_MIN / ClkOutFreq);
      if (settings.n1_min < SI5328_N1_HS_MIN * SI5328_NC_LS_MIN) {
          settings.n1_min = SI5328_N1_HS_MIN * SI5328_NC_LS_MIN;
      }
      settings.n1_max = (int)(SI5328_FOSC_MAX / ClkOutFreq);
      if (settings.n1_max > SI5328_N1_HS_MAX * SI5328_NC_LS_MAX) {
          settings.n1_max = SI5328_N1_HS_MAX * SI5328_NC_LS_MAX;
      }
      settings.n3_min = ClkInFreq / SI5328_F3_MAX;
      if (settings.n3_min < SI5328_N3_MIN) {
          settings.n3_min = SI5328_N3_MIN;
      }
      settings.n3_max = ClkInFreq / SI5328_F3_MIN;
      if (settings.n3_max > SI5328_N3_MAX) {
          settings.n3_max = SI5328_N3_MAX;
      }
      // Find a valid oscillator frequency with the highest setting of N1_HS
      // possible (reduces power)
      for (settings.n1_hs = SI5328_N1_HS_MAX; settings.n1_hs >= SI5328_N1_HS_MIN; settings.n1_hs--) {
          if (SI5328_DEBUG) {
              xil_printf("Trying N1_HS = %d.\n", settings.n1_hs);
          }
          result = Si5328_CalcNclsLimits(&settings);
          if (result) {
              if (SI5328_DEBUG) {
                  xil_printf("\tNo valid settings for NCn_LS.\n");
              }
              continue;
          }
          result = Si5328_FindNcls(&settings);
          if (result) {
              // Best possible result found. Skip the rest of the possibilities.
              break;
          }
      }
      if (settings.best_delta_fout /= settings.fout) {
          if (SI5328_DEBUG) {
              xil_printf("Si5328: ERROR: No valid settings found.");
          }
          return SI5328_ERR_FREQ;
      }
      if (SI5328_DEBUG) {
          xil_printf("Si5328: Found solution: fout = %dHz.\n",
                     (u32)(settings.best_fout >> 28));
      }
  
      // Post processing: convert temporary values to actual register settings
      *N1_hs  = (u8)settings.best_n1_hs - 4;
      *NCn_ls =     settings.best_nc_ls - 1;
      *N2_hs  = (u8)settings.best_n2_hs - 4;
      *N2_ls  =     settings.best_n2_ls - 1;
      *N3n    =     settings.best_n3    - 1;
      /* How must the bandwidth selection be determined? Not all settings will
       * be valid.
      refclk        2, 0xA2,  //              BWSEL_REG=1010 (?)
      free running  2, 0x42,  //              BWSEL_REG=0100 (?)
      */
      *BwSel  = 6; //4
      return SI5328_SUCCESS;
  }
  
  
  /*****************************************************************************/
  /**
   * Set the output frequency of the Si5328 clock generator.
   *
   * @param    IICBaseAddress contains the base address of the IIC master
   *           device.
   * @param    IICAddress contains the 7 bit IIC address of the Si5328 device.
   * @param    ClkSrc selects the clock input to use.
   * @param    ClkInFreq contains the frequency of the input clock (2kHz-710MHz).
   * @param    ClkOutFreq contains the desired output clock frequency
   *           (2kHz-945MHz).
   *
   * @return   SI5328_SUCCESS for success, SI5328_ERR_IIC for IIC access failure,
   *           SI5328_ERR_FREQ when the requested frequency cannot be generated,
   *           SI5328_ERR_PARM when the ClkSrc or ClkDest parameters are invalid
   *           or the ClkInFreq or ClkOutFreq are out of range.
   *****************************************************************************/
  int Si5328_SetClock(u32 IICBaseAddress, u8 IICAddress, u8 ClkSrc,
                      u32 ClkInFreq, u32 ClkOutFreq) {
      u32 NCn_ls, N2_ls, N3n;
      u8  N1_hs, N2_hs, BwSel;
      int result;
      u8  buf[14*2]; // Need to set 14 registers
      int i;
  
      // Sanity check
      if ((ClkSrc < SI5328_CLKSRC_CLK1) || (ClkSrc > SI5328_CLKSRC_XTAL)) {
          if (SI5328_DEBUG) {
              xil_printf("Si5328: ERROR: Incorrect input clock selected!\n");
          }
          return SI5328_ERR_PARM;
      }
      if (ClkSrc == SI5328_CLKSRC_CLK2) {
          if (SI5328_DEBUG) {
              xil_printf("Si5328: ERROR: Clock input 2 not supported!");
          }
          return SI5328_ERR_PARM;
      }
      if ((ClkInFreq < SI5328_FIN_MIN) || (ClkInFreq > SI5328_FIN_MAX)) {
          if (SI5328_DEBUG) {
              xil_printf("Si5328: ERROR: Input frequency out of range!\n");
          }
          return SI5328_ERR_PARM;
      }
      if ((ClkOutFreq < SI5328_FOUT_MIN) || (ClkOutFreq > SI5328_FOUT_MAX)) {
          if (SI5328_DEBUG) {
              xil_printf("Si5328: ERROR: Output frequency out of range!\n");
          }
          return SI5328_ERR_PARM;
      }
  
      // Calculate the frequency settings for the Si5328
      result = Si5328_CalcFreqSettings(ClkInFreq, ClkOutFreq,
                                       &N1_hs, &NCn_ls, &N2_hs, &N2_ls, &N3n,
                                       &BwSel);
      if (result != SI5328_SUCCESS) {
          if (SI5328_DEBUG) {
              xil_printf("Si5328: ERROR: Could not determine settings for requested frequency!\n");
          }
          return result;
      }
  
      /*
       * Set the clock settings
       */
      if (SI5328_DEBUG) {
          xil_printf("Si5328: Programming frequency settings.\n");
      }
      i = 0;
  
      // Free running mode or use a reference clock
      buf[i] = 0;
      if (ClkSrc == SI5328_CLKSRC_XTAL) {
          // Enable free running mode
          buf[i+1] = 0x54;
      } else {
          // Disable free running mode
          buf[i+1] = 0x14;
      }
      i += 2;
  
      // Loop bandwidth
      buf[i]   = 2;
      buf[i+1] = (BwSel << 4) | 0x02;
      i += 2;
  
      // Enable/Disable CLKIN1/2 based on Free Run Mode
      buf[i] = 11;
      if (ClkSrc == SI5328_CLKSRC_CLK1) {
          // Enable CKIN1 & Disable CKIN2
          buf[i+1] = 0x42;
      } else {
          // Disable CKIN1 & Enable CKIN2
          buf[i+1] = 0x41;
      }
      i += 2;
  
      // N1_HS
      buf[i]   = 25;
      buf[i+1] = N1_hs << 5;
      i += 2;
  
      // NC1_LS
      buf[i]   = 31;
      buf[i+1] = (u8)((NCn_ls & 0x000F0000) >> 16);
      buf[i+2] = 32;
      buf[i+3] = (u8)((NCn_ls & 0x0000FF00) >>  8);
      buf[i+4] = 33;
      buf[i+5] = (u8)( NCn_ls & 0x000000FF       );
      i += 6;
  
  //    // NC2_LS
  //    buf[i]   = 34;
  //    buf[i+1] = (u8)((NCn_ls & 0x000F0000) >> 16);
  //    buf[i+2] = 35;
  //    buf[i+3] = (u8)((NCn_ls & 0x0000FF00) >>  8);
  //    buf[i+4] = 36;
  //    buf[i+5] = (u8)( NCn_ls & 0x000000FF       );
  //    i += 6;
  
      // N2_HS and N2_LS
      buf[i]    = 40;
      buf[i+1]  = (N2_hs << 5);
      // N2_LS upper bits (same register as N2_HS)
      buf[i+1] |= (u8)((N2_ls & 0x000F0000) >> 16);
      buf[i+2]  = 41;
      buf[i+3]  = (u8)((N2_ls & 0x0000FF00) >>  8);
      buf[i+4]  = 42;
      buf[i+5]  = (u8)( N2_ls & 0x000000FF       );
      i += 6;
  
      // N3n
      if (ClkSrc == SI5328_CLKSRC_CLK1) {
          // N31
          buf[i]   = 43;
          buf[i+2] = 44;
          buf[i+4] = 45;
      } else {
          // N32
          buf[i]   = 46;
          buf[i+2] = 47;
          buf[i+4] = 48;
      }
      buf[i+1] = (u8)((N3n & 0x00070000) >> 16);
      buf[i+3] = (u8)((N3n & 0x0000FF00) >>  8);
      buf[i+5] = (u8)( N3n & 0x000000FF       );
      i += 6;
  
      // Start calibration
      buf[i]   = 136;
      buf[i+1] = 0x40;
      i += 2;
  
      // Sanity check
      if (i != sizeof(buf)) {
          if (SI5328_DEBUG) {
              xil_printf("Si5328: FATAL ERROR: Incorrect buffer size while programming frequency settings!");
          }
          exit(-1);
      }
  
      // Send all register settings to the Si5328
      result = Si5328_DoSettings(IICBaseAddress, IICAddress,
                                 buf, i / 2);
      return result;
  }
  