forked from 0xha0/df_datalink
784 lines
26 KiB
C
784 lines
26 KiB
C
/**
|
|
* \file ad9528.c
|
|
* \brief Contains source code for configuring and initializing AD9528 clock device
|
|
*/
|
|
|
|
/**
|
|
* \page Disclaimer Legal Disclaimer
|
|
* Copyright 2015-2017 Analog Devices Inc.
|
|
* Released under the AD9371 API license.
|
|
*
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include "ad9528.h"
|
|
#include "../../../include/error.h"
|
|
|
|
static uint32_t _desired_time_to_elapse_ms = 0;
|
|
|
|
/**
|
|
* \brief Write data to a register
|
|
*/
|
|
int32_t AD9528_spiWriteByte(ad9528Device_t *device, uint16_t addr,
|
|
uint8_t data)
|
|
{
|
|
uint8_t buf[3];
|
|
int32_t status;
|
|
|
|
buf[0] = (addr >> 8) & 0x7F;
|
|
buf[1] = addr & 0xFF;
|
|
buf[2] = data;
|
|
status = spi_write_and_read_3wire(device->spi_desc,
|
|
buf, NULL, 3, 0);
|
|
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* \brief Read data from a register
|
|
*/
|
|
int32_t AD9528_spiReadByte(ad9528Device_t *device, uint16_t addr,
|
|
uint8_t *readdata)
|
|
{
|
|
uint8_t tx_buf[2];
|
|
uint8_t rx_buf[1];
|
|
int32_t status;
|
|
|
|
tx_buf[0] = 0x80 | addr >> 8;
|
|
tx_buf[1] = addr & 0xFF;
|
|
|
|
status = spi_write_and_read_3wire(device->spi_desc,
|
|
tx_buf, rx_buf, 2, 1);
|
|
|
|
*readdata = rx_buf[0];
|
|
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* \brief Modify a field of bits from a register
|
|
*/
|
|
int32_t AD9528_spiWriteField(ad9528Device_t *device,
|
|
uint16_t addr, uint8_t fieldVal, uint8_t mask, uint8_t startBit)
|
|
{
|
|
int32_t errVal;
|
|
uint8_t readVal;
|
|
|
|
errVal = AD9528_spiReadByte(device, addr, &readVal);
|
|
if (errVal < 0)
|
|
return errVal;
|
|
|
|
readVal = (readVal & ~mask) | ((fieldVal << startBit) & mask);
|
|
|
|
return AD9528_spiWriteByte(device, addr, readVal);
|
|
}
|
|
|
|
/**
|
|
* \brief Set the desired timeout value
|
|
*/
|
|
int32_t AD9528_setTimeout_ms(uint32_t timeOut_ms)
|
|
{
|
|
_desired_time_to_elapse_ms = timeOut_ms;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* \brief Verify if the desired time has elapsed
|
|
*/
|
|
int32_t AD9528_hasTimeoutExpired()
|
|
{
|
|
udelay(1000);
|
|
|
|
_desired_time_to_elapse_ms--;
|
|
if (_desired_time_to_elapse_ms > 0)
|
|
return SUCCESS;
|
|
|
|
return FAILURE;
|
|
}
|
|
|
|
/**
|
|
* \brief Performs a hard reset on the AD9528 DUT
|
|
*
|
|
* <B>Dependencies</B>
|
|
* - device->spiSettings->chipSelectIndex
|
|
*
|
|
* \param device is structure pointer to the AD9528 data structure containing settings
|
|
* \return Returns enum ADI_ERR, ADIERR_OK=pass, !ADIERR_OK=fail
|
|
*/
|
|
int32_t AD9528_resetDevice(ad9528Device_t *device)
|
|
{
|
|
gpio_direction_output(device->gpio_resetb, 0);
|
|
mdelay(100);
|
|
gpio_direction_output(device->gpio_resetb, 1);
|
|
mdelay(100);
|
|
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_ADI_SPI_CONFIG_A, 0x81);
|
|
mdelay(100);
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_ADI_SPI_CONFIG_A, 0x00);
|
|
mdelay(100);
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* \brief Sets the AD9528 device SPI settings (3wire/4wire, MSBFirst, etc).
|
|
*
|
|
* This function will use the settings in the device->spiSettings structure
|
|
* to set SPI stream mode, address auto increment direction, MSBFirst/LSBfirst,
|
|
* and 3wire/4wire mode. The AD9528 device always uses SPI MODE 0 (CPHA=0, CPOL=0).
|
|
* This function will update your device->spiSettings to set CHPA=0 and CPOL=0 and
|
|
* longInstructionWord =1 to use a 16bit instruction word.
|
|
*
|
|
* <B>Dependencies</B>
|
|
* - writes device->spiSettings->CPHA = 0
|
|
* - writes device->spiSettings->CPOL = 0
|
|
* - writes device->spiSettings->longInstructionWord = 1
|
|
*
|
|
* - device->spiSettings->MSBFirst
|
|
* - device->spiSettings->enSpiStreaming
|
|
* - device->spiSettings->autoIncAddrUp
|
|
* - device->spiSettings->fourWireMode
|
|
*
|
|
* \param device is structure pointer to the AD9528 data structure containing settings
|
|
*
|
|
* \return Returns enum ADI_ERR, ADIERR_OK=pass, !ADIERR_OK=fail
|
|
*/
|
|
int32_t AD9528_setSpiSettings(ad9528Device_t *device)
|
|
{
|
|
uint8_t spiReg = 0;
|
|
|
|
device->spiSettings->CPHA = 0;
|
|
device->spiSettings->CPOL = 0;
|
|
device->spiSettings->longInstructionWord = 1;
|
|
|
|
if (device->spiSettings->MSBFirst == 0) {
|
|
spiReg |= 0x42; /* SPI bit is 1=LSB first */
|
|
}
|
|
|
|
if (device->spiSettings->autoIncAddrUp > 0) {
|
|
spiReg |= 0x24;
|
|
}
|
|
|
|
if (device->spiSettings->fourWireMode > 0) {
|
|
spiReg |= 0x18;
|
|
}
|
|
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_ADI_SPI_CONFIG_A, spiReg);
|
|
|
|
if (device->spiSettings->enSpiStreaming > 0) {
|
|
/* Allow SPI streaming mode: SPI message ends when chip select deasserts */
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_ADI_SPI_CONFIG_B, 0x00);
|
|
} else {
|
|
/* Force single instruction mode */
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_ADI_SPI_CONFIG_B, 0x80);
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* \brief Helper function for ADI transceiver eval boards to init the AD9528 data structure
|
|
*
|
|
* This function inits the AD9528 device data structure with defaults that
|
|
* are known to work with the ADI AD9369 and AD9371 evaluation boards. NOTE: This function will
|
|
* modify the contents of the AD9528 device data structure.
|
|
*
|
|
* <B>Dependencies</B>
|
|
* - spi->chipSelectIndex
|
|
* - spi
|
|
*
|
|
* \param device Pointer to the AD9528 device data structure to initialize
|
|
* \param vcxoFrequency_Hz The VCXO frequency that is connected to the AD9528
|
|
* \param refAFrequency_Hz The reference clock frequency being input into the AD9528 REFCLK A input
|
|
* \param outputDeviceClock_Hz Desired output clock frequency to the FPGA and transceiver device
|
|
*
|
|
* \return Returns enum ADI_ERR, 0=pass, >0 = fail
|
|
*/
|
|
int32_t AD9528_initDeviceDataStruct(ad9528Device_t *device,
|
|
uint32_t vcxoFrequency_Hz, uint32_t refAFrequency_Hz,
|
|
uint32_t outputDeviceClock_Hz)
|
|
{
|
|
// uint16_t chDiv;
|
|
// uint8_t i = 0;
|
|
//
|
|
// uint32_t pll1PfdFreq_Hz = 0;
|
|
// uint8_t pll1Ndiv = 0;
|
|
// uint8_t validPll1DivSetting = 0;
|
|
// uint8_t refAdiv = 1;
|
|
//
|
|
//
|
|
// uint8_t pll2RfDiv = 3; /* Valid values 3,4,5 */
|
|
// uint16_t pll2Ndiv = 1; /* Valid values 1-256 */
|
|
// uint32_t pll2InputFreq_Hz = 0;
|
|
// uint64_t pll2VcoFreq_Hz = 0;
|
|
// uint16_t pll2NdivStart = 0;
|
|
// uint8_t validPll2DivSetting = 0;
|
|
// uint64_t chDivU64 = 0;
|
|
//
|
|
// static const uint64_t PLL2_MIN_VCO_FREQ_HZ = 3450000000ULL;
|
|
// static const uint64_t PLL2_MAX_VCO_FREQ_HZ = 4025000000ULL;
|
|
|
|
// struct spi_init_param spi_param;
|
|
// int32_t status;
|
|
//
|
|
// status = gpio_get(&device->gpio_resetb, CLK_RESETB);
|
|
// status |= gpio_get(&device->gpio_sysref_req, ADRV_SYSREF_REQ);
|
|
//
|
|
// spi_param.id = 0;
|
|
// spi_param.mode = SPI_MODE_0;
|
|
// spi_param.chip_select = CLK_CS;
|
|
// status |= spi_init(&device->spi_desc, &spi_param);
|
|
//
|
|
// if (status != SUCCESS)
|
|
// return FAILURE;
|
|
|
|
/*******************************************************************
|
|
* PLL1 Settings
|
|
*******************************************************************
|
|
*/
|
|
|
|
/* find valid PLL1 Ndiv and REFA div */
|
|
// for (pll1Ndiv = 1; pll1Ndiv < 20; pll1Ndiv++) {
|
|
// pll1PfdFreq_Hz = vcxoFrequency_Hz / pll1Ndiv;
|
|
//
|
|
// if ((pll1PfdFreq_Hz <= 110000000) && (pll1PfdFreq_Hz >= 1000000) &&
|
|
// (pll1PfdFreq_Hz <= refAFrequency_Hz) &&
|
|
// (refAFrequency_Hz % pll1PfdFreq_Hz == 0)
|
|
// ) {
|
|
// validPll1DivSetting = 1;
|
|
// refAdiv = refAFrequency_Hz / pll1PfdFreq_Hz ;
|
|
// break;
|
|
// }
|
|
// }
|
|
//
|
|
// if (validPll1DivSetting == 0) {
|
|
// return FAILURE;
|
|
// }
|
|
|
|
// device->pll1Settings->nDivider =
|
|
// pll1Ndiv; //This init function always runs PLL1 at 30.72MHz Pfd rate
|
|
// device->pll1Settings->refA_bufferCtrl =
|
|
// DIFFERENTIAL; //DISABLED = 0, SINGLE_ENDED, NEG_SINGLE_ENDED, DIFFERENTIAL
|
|
// device->pll1Settings->refA_Divider = refAdiv;
|
|
// device->pll1Settings->refA_Frequency_Hz = refAFrequency_Hz;
|
|
// device->pll1Settings->refB_bufferCtrl =
|
|
// DISABLED; //DISABLED = 0, SINGLE_ENDED, NEG_SINGLE_ENDED, DIFFERENTIAL
|
|
// device->pll1Settings->refB_Divider = 1;
|
|
// device->pll1Settings->refB_Frequency_Hz = 0;
|
|
// device->pll1Settings->vcxoBufferCtrl =
|
|
// NEG_SINGLE_ENDED; //DISABLED = 0, SINGLE_ENDED, NEG_SINGLE_ENDED, DIFFERENTIAL
|
|
// device->pll1Settings->vcxo_Frequency_Hz = vcxoFrequency_Hz;
|
|
|
|
/*******************************************************************
|
|
* PLL2 Settings
|
|
*******************************************************************
|
|
*/
|
|
|
|
/*******************************************************************
|
|
* Output Distribution Settings
|
|
* 9369CE02A uses the following clock outputs
|
|
* OUT 1: FPGA REFCLK
|
|
* OUT 3: FPGA SYSREF
|
|
* OUT 12: DUT SYSREF
|
|
* OUT 13: DUT REFCLK
|
|
*******************************************************************
|
|
*/
|
|
|
|
/* PLL 2 input clock can be several different things. Currently hard coded by this init funciton to use
|
|
* PLL 1 output (VCXO Frequency) with no scaling
|
|
*/
|
|
// pll2InputFreq_Hz = device->pll1Settings->vcxo_Frequency_Hz;
|
|
// validPll2DivSetting = 0;
|
|
|
|
/* Sweep PLL2 Divider settings to find something that has a valid integer divide (3,4,5)*/
|
|
// for (pll2RfDiv = 3; pll2RfDiv <= 5; pll2RfDiv++) {
|
|
//
|
|
// pll2NdivStart = (PLL2_MIN_VCO_FREQ_HZ / pll2RfDiv / pll2InputFreq_Hz);
|
|
// /* find valid PLL2 Ndiv (1-256) */
|
|
// for (pll2Ndiv = pll2NdivStart; pll2Ndiv <= 256; pll2Ndiv++) {
|
|
// pll2VcoFreq_Hz = (pll2InputFreq_Hz * pll2RfDiv * pll2Ndiv);
|
|
//
|
|
// if (pll2VcoFreq_Hz < PLL2_MIN_VCO_FREQ_HZ) {
|
|
// /* Continue to next PLL2 N div */
|
|
// continue;
|
|
// } else if (pll2VcoFreq_Hz > PLL2_MAX_VCO_FREQ_HZ) {
|
|
// /* Break to next PLL2 RF div */
|
|
// break;
|
|
// }
|
|
//
|
|
// if (((pll2VcoFreq_Hz / pll2RfDiv) % outputDeviceClock_Hz) == 0) {
|
|
//
|
|
// device->pll2Settings->rfDivider = pll2RfDiv;
|
|
// device->pll2Settings->n2Divider = pll2Ndiv;
|
|
// device->pll2Settings->totalNdiv = pll2RfDiv * pll2Ndiv;
|
|
// chDivU64 = ((pll2VcoFreq_Hz / pll2RfDiv) / outputDeviceClock_Hz);
|
|
//
|
|
// if ((chDivU64 < 1) || (chDivU64 > 256)) {
|
|
// return FAILURE;
|
|
// } else {
|
|
// chDiv = (uint16_t)(chDivU64);
|
|
// }
|
|
//
|
|
// validPll2DivSetting = 1;
|
|
// break;
|
|
// }
|
|
// }
|
|
//
|
|
// if (validPll2DivSetting == 1) {
|
|
// break;
|
|
// }
|
|
// }
|
|
//
|
|
// if (validPll2DivSetting == 0) {
|
|
// return FAILURE;
|
|
// }
|
|
//
|
|
// device->outputSettings->outPowerDown = (uint16_t)(
|
|
// ~0x318A); //enable outputs 13,12,8,7,3 and 1
|
|
//
|
|
// for (i=0; i<14; i++) {
|
|
// device->outputSettings->outSource[i] =
|
|
// CHANNEL_DIV; //CHANNEL_DIV , PLL1_OUTPUT, SYSREF, INV_PLL1_OUTPUT
|
|
// device->outputSettings->outBufferCtrl[i] = LVDS; //LVDS, LVDS_BOOST, HSTL
|
|
// device->outputSettings->outChannelDiv[i] = chDiv;
|
|
// device->outputSettings->outFrequency_Hz[i] = outputDeviceClock_Hz;
|
|
// device->outputSettings->outAnalogDelay[i] = 0;
|
|
// device->outputSettings->outDigitalDelay[i] = 0;
|
|
// }
|
|
//
|
|
//
|
|
// device->outputSettings->outSource[3] = SYSREF;
|
|
// device->outputSettings->outSource[7] = SYSREF;
|
|
// device->outputSettings->outSource[12] = SYSREF;
|
|
//
|
|
//
|
|
// device->sysrefSettings->sysrefDivide =
|
|
// 512; //SYSREF frequency = VCXO / 2 / sysrefDivde
|
|
// device->sysrefSettings->sysrefNshotMode =
|
|
// ONE_PULSE; //ONE_PULSE=0, TWO_PULSES=2, FOUR_PULSES=3, SIX_PULSES=4, EIGHT_PULSES=5
|
|
// device->sysrefSettings->sysrefPatternMode =
|
|
// CONTINUOUS; //NSHOT=0, CONTINUOUS=1, PRBS=2, STOP=3
|
|
// device->sysrefSettings->sysrefPinBufferMode =
|
|
// DISABLED; //DISABLED = 0, SINGLE_ENDED, NEG_SINGLE_ENDED, DIFFERENTIAL
|
|
// device->sysrefSettings->sysrefPinEdgeMode =
|
|
// LEVEL_ACTIVE_HIGH; //LEVEL_ACTIVE_HIGH=0, LEVEL_ACTIVE_LOW=1, RISING_EDGE=2, FALLING_EDGE=3
|
|
// device->sysrefSettings->sysrefRequestMethod = PIN; //SPI=0,PIN=1
|
|
// device->sysrefSettings->sysrefSource =
|
|
// INTERNAL; //EXTERNAL=0, EXT_RESAMPLED=1, INTERNAL=2
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
int32_t AD9528_init(ad9528Device_t **device, const ad9528Init_t *init_device)
|
|
{
|
|
int ret = SUCCESS;
|
|
ad9528Device_t *dev;
|
|
|
|
dev = (ad9528Device_t *)malloc(sizeof(*dev));
|
|
if (!dev)
|
|
return FAILURE;
|
|
|
|
/* SPI */
|
|
ret = spi_init(&dev->spi_desc, init_device->spi_init);
|
|
ret |= gpio_get(&dev->gpio_resetb, init_device->gpio_init_resetb);
|
|
ret |= gpio_get(&dev->gpio_sysref_req, init_device->gpio_init_sysref_req);
|
|
|
|
gpio_direction_output(dev->gpio_resetb, 0);
|
|
gpio_direction_output(dev->gpio_sysref_req, 0);
|
|
|
|
AD9528_resetDevice(dev);
|
|
|
|
dev->spiSettings = init_device->spiSettings;
|
|
dev->pll1Settings = init_device->pll1Settings;
|
|
dev->pll2Settings = init_device->pll2Settings;
|
|
dev->outputSettings = init_device->outputSettings;
|
|
dev->sysrefSettings = init_device->sysrefSettings;
|
|
|
|
*device = dev;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* \brief Initializes the AD9528 by writing all SPI registers
|
|
*
|
|
* Initializes the AD9528 for use with the AD9369 transceiver. Designed to work with
|
|
* a reference clock of 122.88MHz. Reference clock is expected to be 30.72MHz.
|
|
* The output clocks from the AD9528 can run at 122.88M, 153.6M, 184.32M, 245.76M, or 307.2M.
|
|
* Typical setup: REFA frequency = 30.72, VCXO = 122.88MHz
|
|
* SYNC(SYSREF) pulses output at rate of 120kHz
|
|
*
|
|
* <B>Dependencies</B>
|
|
* - All parameters in device structure
|
|
*
|
|
* \param device is structure pointer to AD9528 clock device structure
|
|
*
|
|
* \return Returns enum ADI_ERR, 0=pass, >0 = fail
|
|
*/
|
|
int32_t AD9528_config(ad9528Device_t
|
|
*device) //writes all registers to desired values, leaves clock outputs off, SYSREF output disabled.
|
|
{
|
|
uint8_t pll2_Adiv=0;
|
|
uint8_t pll2_Bdiv=0;
|
|
uint8_t reg108=0;
|
|
uint8_t reg109=0x4;
|
|
uint8_t i = 0;
|
|
uint8_t j = 0;
|
|
uint8_t reg508;
|
|
int32_t retval = SUCCESS;
|
|
|
|
retval = AD9528_setSpiSettings(device);
|
|
if (retval != SUCCESS) {
|
|
return retval;
|
|
}
|
|
|
|
/**********************************************************************
|
|
* Configure PLL1
|
|
**********************************************************************
|
|
*/
|
|
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_REF_A_DIVIDER_LSB,
|
|
device->pll1Settings->refA_Divider & 0xFF); //REF A div[7:0]
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_REF_A_DIVIDER_MSB,
|
|
(device->pll1Settings->refA_Divider >> 8) & 0x03); //REF A div[9:8]
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_REF_B_DIVIDER_LSB,
|
|
device->pll1Settings->refB_Divider & 0xFF); //REF B div[7:0]
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_REF_B_DIVIDER_MSB,
|
|
(device->pll1Settings->refB_Divider >> 8) & 0x03); //REF B div[9:8]
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_PLL1_N_DIV_LSB,
|
|
device->pll1Settings->nDivider & 0xFF); //PLL1 N div[7:0]
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_PLL1_N_DIV_MSB,
|
|
(device->pll1Settings->nDivider >> 8) & 0x03); //PLL1 N div[9:8]
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_PLL1_CHARGEPUMP,
|
|
0x0A); //PLL1 Charge Pump control 1
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_PLL1_CP_CTRL2,
|
|
0x03); //PLL1 Charge Pump control 2
|
|
|
|
switch (device->pll1Settings->refA_bufferCtrl) {
|
|
case DISABLED:
|
|
reg108 &= ~0x28;
|
|
reg109 &= ~0x01;
|
|
break; //clear all REFA enable bits
|
|
case SINGLE_ENDED:
|
|
reg108 |= 0x08;
|
|
reg109 &= ~0x01;
|
|
break; //Set REFA receiver enable, clear REFA SE negative input enable
|
|
case NEG_SINGLE_ENDED:
|
|
reg108 |= 0x08;
|
|
reg108 &= ~0x10;
|
|
reg109 |= 0x01;
|
|
break; //Disable REFA diff receiver, enable REFA receiver and use negative REFA input
|
|
case DIFFERENTIAL:
|
|
reg108 |= 0x28;
|
|
reg109 &= ~0x01;
|
|
break;
|
|
default:
|
|
reg108 |= 0x08;
|
|
reg109 &= ~0x01;
|
|
break; //default to single ended case
|
|
}
|
|
switch (device->pll1Settings->refB_bufferCtrl) {
|
|
case DISABLED:
|
|
reg108 &= ~0x50;
|
|
reg109 &= ~0x02;
|
|
break; //clear all REFB enable bits
|
|
case SINGLE_ENDED:
|
|
reg108 |= 0x10;
|
|
reg109 &= ~0x02;
|
|
break; //Set REFB receiver enable, clear REFB SE negative input enable
|
|
case NEG_SINGLE_ENDED:
|
|
reg108 |= 0x10;
|
|
reg108 &= ~0x40;
|
|
reg109 |= 0x02;
|
|
break; //Disable REFB diff receiver, enable REFB receiver and use negative REFB input
|
|
case DIFFERENTIAL:
|
|
reg108 |= 0x50;
|
|
reg109 &= ~0x02;
|
|
break;
|
|
default:
|
|
reg108 |= 0x10;
|
|
reg109 &= ~0x02;
|
|
break; //default to single ended case
|
|
}
|
|
switch (device->pll1Settings->vcxoBufferCtrl) {
|
|
case DISABLED:
|
|
reg108 &= ~0x03;
|
|
reg108 |= 0x04;
|
|
break; //power down VCXO input
|
|
case SINGLE_ENDED:
|
|
reg108 &= ~0x07;
|
|
break; //Clear VCXO receiver power down, defaults to Single ended mode
|
|
case NEG_SINGLE_ENDED:
|
|
reg108 &= ~0x07;
|
|
reg108 |= 0x02;
|
|
break; //Clear VCXO power down, enable Negative single ended mode[1]
|
|
case DIFFERENTIAL:
|
|
reg108 &= ~0x07;
|
|
reg108 |= 0x01;
|
|
break; //Clear VCXO power down, enable differential input buffer[0]
|
|
default:
|
|
reg108 &= ~0x07;
|
|
break; //default to positive single ended case
|
|
}
|
|
|
|
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_INPUT_RECEIVERS1,
|
|
reg108); //Input receiver settings (single ended or differential clock inputs)
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_INPUT_RECEIVERS2,
|
|
reg109); //Input receiver settings
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_INPUT_RECEIVERS3,
|
|
0x0A); //Reference selection mode
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_PLL1_FASTLOCK,
|
|
0x00); //Disable Fast lock
|
|
|
|
/**********************************************************************
|
|
* Configure PLL2
|
|
**********************************************************************
|
|
*/
|
|
pll2_Bdiv = device->pll2Settings->totalNdiv / 4;
|
|
pll2_Adiv = device->pll2Settings->totalNdiv % 4;
|
|
|
|
if (pll2_Adiv > 3) { //A div valid range (0 to 3)
|
|
return FAILURE;
|
|
}
|
|
if (pll2_Bdiv < 3 || pll2_Bdiv > 63) {
|
|
return FAILURE;
|
|
}
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_PLL2_CHARGEPUMP,
|
|
0xE6); //PLL2 Charge Pump[7:0]
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_PLL2_N_DIV,
|
|
((pll2_Adiv & 0x3) << 6) | (pll2_Bdiv & 0x3F)); //PLL2 A and B divider
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_PLL2_CTRL, 0x03); //PLL2 control
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_PLL2_VCO_CTRL, 0x01); //Manual VCO CAL
|
|
|
|
if (device->pll2Settings->rfDivider < 3
|
|
|| device->pll2Settings->rfDivider > 5) {
|
|
return FAILURE;
|
|
}
|
|
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_PLL2_VCO_DIV,
|
|
device->pll2Settings->rfDivider); //VCO divider
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_PLL2_LF_CTRL1,
|
|
0x3A); //PLL2 Loop filter [7:0]
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_PLL2_LF_CTRL2,
|
|
0x00); //PLL2 Loop filter [8]
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_PLL2_RDIV, 0x01); //R divider
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_PLL2_REPLICA_CHDIV,
|
|
(device->pll2Settings->totalNdiv / device->pll2Settings->rfDivider)
|
|
-1 ); //Replica channel divider: Ndivider = rfDivider * R divider
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_PLL2_REPLICA_DIV_PHASE,
|
|
0x00); //Replica Channel Divider phase
|
|
|
|
/**********************************************************************
|
|
* Configure Output Distribution
|
|
**********************************************************************
|
|
*/
|
|
for (i=0; i<14; i=i+1) {
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_CH_OUT0_CTRL1 + j,
|
|
((device->outputSettings->outSource[i] & 0x7) << 5) |
|
|
(device->outputSettings->outAnalogDelay[i] & 0x1F));
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_CH_OUT0_CTRL2 + j,
|
|
((device->outputSettings->outBufferCtrl[i] & 0x3) << 6) |
|
|
(device->outputSettings->outDigitalDelay[i] & 0x3F));
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_CH_OUT0_CHDIV + j,
|
|
device->outputSettings->outChannelDiv[i]-1);
|
|
j = j+3;
|
|
}
|
|
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_OUTPUT_SYNC, 0x00); //Sync outputs
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_MASK_SYNC1, 0x00); //Mask SYNC[7:0]
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_MASK_SYNC2, 0x00); //Mask SYNC[13:8]
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_EN_OUTPUT_PATH_SEL1,
|
|
0x00); //Bypass SYSREF resample [6:0], bit0:Enable VCXO to feed output distrubution
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_EN_OUTPUT_PATH_SEL2,
|
|
0x00); //Bypass SYSREF resample [13:7]
|
|
|
|
/**********************************************************************
|
|
* Configure SYSREF operation
|
|
**********************************************************************
|
|
*/
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_SYSERF_DIV_LSB,
|
|
device->sysrefSettings->sysrefDivide & 0xFF); //SYSREF divider [7:0]
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_SYSERF_DIV_MSB,
|
|
(device->sysrefSettings->sysrefDivide >> 8) & 0xFF); //SYSREF divider [7:0]
|
|
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_SYSREF_CTRL3,
|
|
((device->sysrefSettings->sysrefRequestMethod & 0x1) << 7) |
|
|
((device->sysrefSettings->sysrefPinEdgeMode & 0x3) << 5) );
|
|
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_SYSREF_CTRL4,
|
|
((device->sysrefSettings->sysrefSource & 0x03) << 6) |
|
|
((device->sysrefSettings->sysrefPatternMode & 0x3) << 4 ) |
|
|
((device->sysrefSettings->sysrefNshotMode & 0x7) << 1) );
|
|
|
|
if(device->sysrefSettings->sysrefPinBufferMode == DISABLED) {
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_SYSREF_CTRL5,
|
|
0x04); //power down input buffer for external SYSREF
|
|
} else if(device->sysrefSettings->sysrefPinBufferMode == SINGLE_ENDED) {
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_SYSREF_CTRL5,
|
|
0x00); //enable positive singled ended input
|
|
} else if(device->sysrefSettings->sysrefPinBufferMode == NEG_SINGLE_ENDED) {
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_SYSREF_CTRL5,
|
|
0x02); //enable negative singled ended input
|
|
} else if(device->sysrefSettings->sysrefPinBufferMode == DIFFERENTIAL) {
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_SYSREF_CTRL5,
|
|
0x01); //enable differential input buffer
|
|
} else {
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_SYSREF_CTRL5,
|
|
0x04); //power down input buffer for external SYSREF
|
|
}
|
|
|
|
/**********************************************************************
|
|
* Power down control
|
|
**********************************************************************
|
|
*/
|
|
|
|
if (device->pll1Settings->bypassPll1 > 0) {
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_POWERDOWN_CTRL,
|
|
0x14);// power up PLL2 and output distribution, power down PLL1
|
|
} else {
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_POWERDOWN_CTRL,
|
|
0x10);// power up PLL1, PLL2 and output distribution
|
|
}
|
|
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_CH_POWERDOWN1,
|
|
device->outputSettings->outPowerDown & 0xFF); //power down out[7:0]
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_CH_POWERDOWN2,
|
|
((device->outputSettings->outPowerDown >> 8) & 0x3F)); //power down out[13:8]
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_LDO_ENABLES1,
|
|
0xFF); //LDO enable per output channel [7:0]
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_LDO_ENABLES2,
|
|
0xFF); //LDO enable per output channel [15:8]
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_STATUS0_CTRL,
|
|
0x07); //STATUS 0 outputs REFA present
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_STATUS1_CTRL,
|
|
0x01); //STATUS 1 outputs PLL1 and PLL2 locked
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_STATUS_OE,
|
|
0x0C);// Make Status0 and Status1 outputs after reset.
|
|
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_IO_UPDATE,
|
|
0x01); //IO update to make all these settings valid in the 9528
|
|
|
|
AD9528_setTimeout_ms(1000);
|
|
|
|
do {
|
|
AD9528_spiReadByte(device, AD9528_ADDR_STATUS_READBACK0, ®508);
|
|
if(AD9528_hasTimeoutExpired() > 0) {
|
|
return FAILURE;
|
|
}
|
|
} while((reg508 & 0xE7) != 0xE7);
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
|
|
/// Send a SPI message to request a SYSREF pulse or continuous SYSREF from the AD9528.
|
|
/*******************************************************************************//**
|
|
* Requests a SYSREF from the AD9528. It will use whatever settings for SYSREF
|
|
* that are current configured in the AD9528. This could be a single pulse,
|
|
* multiple pulses, or continuous pulses.
|
|
* <B>Dependencies</B>
|
|
* - device->spiSettings
|
|
* - device->sysrefSettings->sysrefPatternMode
|
|
*
|
|
* \param device is structure pointer to AD9528 clock device structure
|
|
* \param enableSYSREF If NSHOT SYSREF mode, this parameter is ignored. If PRBS/CONTINUOUS SYSREF mode, 1= enable SYSREF, 0= disable SYSREF.
|
|
*
|
|
* \return Returns enum ADI_ERR, 0=pass, >0 = fail
|
|
***********************************************************************************/
|
|
int32_t AD9528_requestSysref(ad9528Device_t *device, uint8_t enableSYSREF)
|
|
{
|
|
uint8_t _enableSYSREF=0;
|
|
|
|
if (device->sysrefSettings->sysrefPatternMode == NSHOT || enableSYSREF > 0) {
|
|
_enableSYSREF = 1;
|
|
} else {
|
|
_enableSYSREF = 0;
|
|
}
|
|
|
|
AD9528_spiWriteField(device, AD9528_ADDR_SYSREF_CTRL4, _enableSYSREF, 0x01,
|
|
0x00); //Set SPI SYSREF request bit
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_IO_UPDATE,
|
|
0x01); //IO update to make all these settings valid in the 9528
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
/// Allow changing the 9528 SYSREF frequency and pattern mode(CONTINUOUS, PRBS vs NSHOT mode)
|
|
/*******************************************************************************//**
|
|
* NOTE: this function will update the device structure with the parameter values passed
|
|
* into this function. The device->sysrefSettings->sysrefPatternMode, device->sysrefSettings->sysrefNshotMode,
|
|
* and device->sysrefSettings->sysrefDivide values will be updated in the device structure.
|
|
*
|
|
* <B>Dependencies</B>
|
|
* - device->spiSettings
|
|
*
|
|
* \param device is structure pointer to AD9528 clock device structure
|
|
* \param divideFromPll1Out Sets the SYSREF pulse frequency. SYSREF frequency = PLL1_VCXO frequency/divideFromPll1Out
|
|
* \param sysrefPatternMode Valid settings: NSHOT, CONTINUOUS, PRBS, STOP
|
|
* \param nShotPulses Valid settings: ONE_PULSE=0, TWO_PULSES=2, FOUR_PULSES=3, SIX_PULSES=4, EIGHT_PULSES=5
|
|
*
|
|
* \return Returns enum ADI_ERR, 0=pass, >0 = fail
|
|
***********************************************************************************/
|
|
int32_t AD9528_setupSYSREF(ad9528Device_t *device,
|
|
uint16_t divideFromPll1Out, sysrefPatternMode_t sysrefPatternMode,
|
|
sysrefNshotMode_t nShotPulses)
|
|
{
|
|
uint8_t reg403=0;
|
|
|
|
device->sysrefSettings->sysrefPatternMode = sysrefPatternMode;
|
|
device->sysrefSettings->sysrefNshotMode = nShotPulses;
|
|
device->sysrefSettings->sysrefDivide = divideFromPll1Out >>
|
|
1; //SYSREF frequency is (PLL1)VCXO frequency / 2 / device->sysrefSettings->sysrefDivide
|
|
|
|
AD9528_spiReadByte(device, AD9528_ADDR_SYSREF_CTRL4, ®403);
|
|
|
|
reg403 = reg403 & ~0x01; //clear bit 0 to stop a continuous sysref if it is on.
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_SYSREF_CTRL4, reg403);
|
|
|
|
reg403 = reg403 & ~0x3E; //clear bits for pattern mode and Nshot mode bitfields
|
|
reg403 = reg403 | ((sysrefPatternMode & 0x3) << 4) | ((nShotPulses & 0x7) << 1);
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_SYSREF_CTRL4,
|
|
reg403); //update internal sysref pattern mode and Nshot mode (number of pulses)
|
|
|
|
//SYSREF request bit x403[0] is left 0. The next call to AD9528_requestSysref will start the SYSREF
|
|
AD9528_spiWriteByte(device, AD9528_ADDR_IO_UPDATE,
|
|
0x01); //IO update to make all these settings valid in the 9528
|
|
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
int32_t AD9528_sysrefReq(ad9528Device_t *device,
|
|
ad9528SysrefReqMode_t mode)
|
|
{
|
|
if (mode == SYSREF_CONT_ON)
|
|
gpio_direction_output(device->gpio_sysref_req, 1);
|
|
else if (mode == SYSREF_CONT_OFF)
|
|
gpio_direction_output(device->gpio_sysref_req, 0);
|
|
else if (mode == SYSREF_PULSE) {
|
|
gpio_direction_output(device->gpio_sysref_req, 1);
|
|
mdelay(1);
|
|
gpio_direction_output(device->gpio_sysref_req, 0);
|
|
} else
|
|
return FAILURE;
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* \brief Free the allocated resources
|
|
*/
|
|
int32_t AD9528_remove(ad9528Device_t *device)
|
|
{
|
|
int32_t status;
|
|
|
|
status = gpio_remove(device->gpio_resetb);
|
|
status |= gpio_remove(device->gpio_sysref_req);
|
|
status |= spi_remove(device->spi_desc);
|
|
|
|
return SUCCESS;
|
|
}
|