Initial commit

This commit is contained in:
Gatsby Van
2024-11-04 19:43:31 +08:00
parent f3fc4279b1
commit 1e44e8ea8b
124 changed files with 46332 additions and 0 deletions
+783
View File
@@ -0,0 +1,783 @@
/**
* \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, &reg508);
if(AD9528_hasTimeoutExpired() > 0) {
return FAILURE;
}
} while((reg508 & 0x26) != 0x26);
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, &reg403);
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;
}
+245
View File
@@ -0,0 +1,245 @@
/**
* \file ad9528.h
* \brief Contains function declarations and ad9528Device_t structure typedef for ad9528.c
*/
/**
* \page Disclaimer Legal Disclaimer
* Copyright 2015-2017 Analog Devices Inc.
* Released under the AD9371 API license.
*
*/
#ifndef _AD9528_H_
#define _AD9528_H_
/******************************************************************************/
/***************************** Include Files **********************************/
/******************************************************************************/
#include <stdint.h>
#include "delay.h"
#include "spi.h"
#include "gpio.h"
/******************************************************************************/
/********************** Macros and Constants Definitions **********************/
/******************************************************************************/
/* AD9528 SPI Address defines */
#define AD9528_ADDR_ADI_SPI_CONFIG_A 0x000
#define AD9528_ADDR_ADI_SPI_CONFIG_B 0x001
#define AD9528_ADDR_IO_UPDATE 0x00F
#define AD9528_ADDR_REF_A_DIVIDER_LSB 0x100
#define AD9528_ADDR_REF_A_DIVIDER_MSB 0x101
#define AD9528_ADDR_REF_B_DIVIDER_LSB 0x102
#define AD9528_ADDR_REF_B_DIVIDER_MSB 0x103
#define AD9528_ADDR_PLL1_N_DIV_LSB 0x104
#define AD9528_ADDR_PLL1_N_DIV_MSB 0x105
#define AD9528_ADDR_PLL1_CHARGEPUMP 0x106
#define AD9528_ADDR_PLL1_CP_CTRL2 0x107
#define AD9528_ADDR_INPUT_RECEIVERS1 0x108
#define AD9528_ADDR_INPUT_RECEIVERS2 0x109
#define AD9528_ADDR_INPUT_RECEIVERS3 0x10A
#define AD9528_ADDR_PLL1_FASTLOCK 0x10B
#define AD9528_ADDR_PLL2_CHARGEPUMP 0x200
#define AD9528_ADDR_PLL2_N_DIV 0x201
#define AD9528_ADDR_PLL2_CTRL 0x202
#define AD9528_ADDR_PLL2_VCO_CTRL 0x203
#define AD9528_ADDR_PLL2_VCO_DIV 0x204
#define AD9528_ADDR_PLL2_LF_CTRL1 0x205
#define AD9528_ADDR_PLL2_LF_CTRL2 0x206
#define AD9528_ADDR_PLL2_RDIV 0x207
#define AD9528_ADDR_PLL2_REPLICA_CHDIV 0x208
#define AD9528_ADDR_PLL2_REPLICA_DIV_PHASE 0x209
#define AD9528_ADDR_CH_OUT0_CTRL1 0x300
#define AD9528_ADDR_CH_OUT0_CTRL2 0x301
#define AD9528_ADDR_CH_OUT0_CHDIV 0x302
#define AD9528_ADDR_OUTPUT_SYNC 0x32A
#define AD9528_ADDR_MASK_SYNC1 0x32B
#define AD9528_ADDR_MASK_SYNC2 0x32C
#define AD9528_ADDR_EN_OUTPUT_PATH_SEL1 0x32D
#define AD9528_ADDR_EN_OUTPUT_PATH_SEL2 0x32E
#define AD9528_ADDR_SYSERF_DIV_LSB 0x400
#define AD9528_ADDR_SYSERF_DIV_MSB 0x401
#define AD9528_ADDR_SYSREF_CTRL3 0x402
#define AD9528_ADDR_SYSREF_CTRL4 0x403
#define AD9528_ADDR_SYSREF_CTRL5 0x404
#define AD9528_ADDR_POWERDOWN_CTRL 0x500
#define AD9528_ADDR_CH_POWERDOWN1 0x501
#define AD9528_ADDR_CH_POWERDOWN2 0x502
#define AD9528_ADDR_LDO_ENABLES1 0x503
#define AD9528_ADDR_LDO_ENABLES2 0x504
#define AD9528_ADDR_STATUS0_CTRL 0x505
#define AD9528_ADDR_STATUS1_CTRL 0x506
#define AD9528_ADDR_STATUS_OE 0x507
#define AD9528_ADDR_STATUS_READBACK0 0x508
#define AD9528_ADDR_STATUS_READBACK1 0x509
/******************************************************************************/
/*************************** Types Declarations *******************************/
/******************************************************************************/
///Enum to select what source outputs from each AD9528 output channel
typedef enum {
AD9528_CHANNEL_DIV = 0, PLL1_OUTPUT = 1, //VCXO frequency
SYSREF = 2,
INV_PLL1_OUTPUT = 5 //VCXO inverted
} ad9528outSourceSel_t;
///Enum to select what output buffer standard is used for the clock outputs
typedef enum {
AD9528_LVDS = 0, AD9528_LVDS_BOOST = 1, AD9528_HSTL = 2
} ad9528outBufferControl_t;
///Enum to select the clock input buffer mode.
typedef enum {
DISABLED = 0, SINGLE_ENDED, NEG_SINGLE_ENDED, DIFFERENTIAL
} ad9528RefBuffer_t;
/// Structure to hold AD9528 PLL1 settings
typedef struct {
uint32_t refA_Frequency_Hz;
uint16_t refA_Divider;
ad9528RefBuffer_t refA_bufferCtrl;
uint32_t refB_Frequency_Hz;
uint16_t refB_Divider;
ad9528RefBuffer_t refB_bufferCtrl;
uint32_t vcxo_Frequency_Hz;
ad9528RefBuffer_t vcxoBufferCtrl;
uint16_t nDivider;
uint8_t bypassPll1;
} ad9528pll1Settings_t;
/// Structure to hold AD9528 PLL2 settings
typedef struct {
uint8_t rfDivider; //VCO divider: Valid range (3,4,5)
uint8_t n2Divider; //PLL2 N2 Divider
uint8_t totalNdiv; //NDiv = 4*Bdiv + Adiv //Bdiv valid range (3 to 63), Adiv valid range (0-3)
} ad9528pll2Settings_t;
/// Structure to hold AD9528 Output Clock settings
typedef struct {
uint16_t outPowerDown; //bit per output, if 1 power down that output.
ad9528outSourceSel_t
outSource[14]; //CHANNEL_DIV, PLL1_OUTPUT, SYSREF, INV_PLL1_OUTPUT
ad9528outBufferControl_t outBufferCtrl[14]; //LVDS, LVDS_BOOST, HSTL
uint8_t outAnalogDelay[14]; //5 bits
uint8_t outDigitalDelay[14]; //6 bits, 1/2 clock resolution @ channel div input frequency
uint8_t outChannelDiv[14]; //8 bit channel divider
uint32_t outFrequency_Hz[14]; //output clock frequency per clock output
} ad9528outputSettings_t;
///Enum to select the SYSREF output source
typedef enum {
EXTERNAL = 0, EXT_RESAMPLED = 1, INTERNAL = 2
} sysrefSourceSelection_t;
///Enum to choose the SYSREF pattern mode.
typedef enum {
NSHOT = 0, CONTINUOUS = 1, PRBS = 2, STOP = 3
} sysrefPatternMode_t;
///Enum to select the SYSREF input pin mode.
typedef enum {
LEVEL_ACTIVE_HIGH = 0, LEVEL_ACTIVE_LOW = 1, RISING_EDGE = 2, FALLING_EDGE = 3
} sysrefPinEdgeMode_t;
///Enum to select the SYSREF output # of pulses for NSHOT mode.
typedef enum {
ONE_PULSE = 0, TWO_PULSES = 2, FOUR_PULSES = 3, SIX_PULSES = 4, EIGHT_PULSES = 5
} sysrefNshotMode_t;
//Enum to select how a SYSREF is requested.
typedef enum {
SPI = 0, PIN = 1
} sysrefRequestMethod_t;
/// Structure to hold AD9528 SYSREF output settings
typedef struct {
sysrefRequestMethod_t sysrefRequestMethod;
sysrefSourceSelection_t sysrefSource;
sysrefPinEdgeMode_t sysrefPinEdgeMode;
ad9528RefBuffer_t sysrefPinBufferMode;
sysrefPatternMode_t sysrefPatternMode;
sysrefNshotMode_t sysrefNshotMode;
uint16_t sysrefDivide;
} ad9528sysrefSettings_t;
/// Structure to hold AD9528 SPI settings
typedef struct {
uint8_t chipSelectIndex; ///< valid 1~8
uint8_t writeBitPolarity; ///< the level of the write bit of a SPI write instruction word, value is inverted for SPI read operation
uint8_t longInstructionWord; ///< 1 = 16bit instruction word, 0 = 8bit instruction word
uint8_t MSBFirst; ///< 1 = MSBFirst, 0 = LSBFirst
uint8_t CPHA; ///< clock phase, sets which clock edge the data updates (valid 0 or 1)
uint8_t CPOL; ///< clock polarity 0 = clock starts low, 1 = clock starts high
uint8_t enSpiStreaming; ///< Not implemented. SW feature to improve SPI throughput.
uint8_t autoIncAddrUp; ///< Not implemented. For SPI Streaming, set address increment direction. 1= next addr = addr+1, 0:addr = addr-1
uint8_t fourWireMode; ///< 1: Use 4-wire SPI, 0: 3-wire SPI (SDIO pin is bidirectional). NOTE: ADI's FPGA platform always uses 4-wire mode.
uint32_t spiClkFreq_Hz; ///< SPI Clk frequency in Hz (default 25000000), platform will use next lowest frequency that it's baud rate generator can create */
} ad9528spiSettings_t;
/// Structure to hold AD9528 settings
typedef struct {
struct gpio_desc *gpio_resetb;
struct gpio_desc *gpio_sysref_req;
struct spi_desc *spi_desc;
ad9528spiSettings_t *spiSettings;
ad9528pll1Settings_t *pll1Settings;
ad9528pll2Settings_t *pll2Settings;
ad9528outputSettings_t *outputSettings;
ad9528sysrefSettings_t *sysrefSettings;
} ad9528Device_t;
typedef struct {
struct gpio_init_param *gpio_init_resetb;
struct gpio_init_param *gpio_init_sysref_req;
struct spi_init_param *spi_init;
ad9528spiSettings_t *spiSettings;
ad9528pll1Settings_t *pll1Settings;
ad9528pll2Settings_t *pll2Settings;
ad9528outputSettings_t *outputSettings;
ad9528sysrefSettings_t *sysrefSettings;
} ad9528Init_t;
typedef enum {
SYSREF_CONT_ON,
SYSREF_CONT_OFF,
SYSREF_PULSE,
} ad9528SysrefReqMode_t;
/******************************************************************************/
/************************ Functions Declarations ******************************/
/******************************************************************************/
//Helper function to setup the 9528 device data structure specifically for the ADI transceiver evaluation cards
int32_t AD9528_initDeviceDataStruct(ad9528Device_t *device,
uint32_t vcxoFrequency_Hz, uint32_t refAFrequency_Hz,
uint32_t outputDeviceClock_Hz);
int32_t AD9528_spiReadByte(ad9528Device_t *device, uint16_t addr,
uint8_t *readdata);
//AD9528 API functions
int32_t AD9528_hasTimeoutExpired();
int32_t AD9528_resetDevice(ad9528Device_t *device);
int32_t AD9528_init(ad9528Device_t **device, const ad9528Init_t *init_device);
int32_t AD9528_config(ad9528Device_t
*device); //writes all registers to desired values, leaves clock outputs off.
int32_t AD9528_setupSYSREF(ad9528Device_t *device,
uint16_t divideFromPll1Out, sysrefPatternMode_t sysrefPatternMode,
sysrefNshotMode_t nShotPulses);
int32_t AD9528_requestSysref(ad9528Device_t *device, uint8_t enableSYSREF);
int32_t AD9528_sysrefReq(ad9528Device_t *device,
ad9528SysrefReqMode_t mode);
int32_t AD9528_remove(ad9528Device_t *device);
#endif
+927
View File
@@ -0,0 +1,927 @@
/***************************************************************************//**
* @file hmc7044.c
* @brief Implementation of HMC7044, HMC7043 Driver.
* @author DBogdan ([email protected])
********************************************************************************
* 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 <stdlib.h>
#include <stdio.h>
#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;
}
+276
View File
@@ -0,0 +1,276 @@
/***************************************************************************//**
* @file hmc7044.h
* @brief Header file of HMC7044, HMC7043 Driver.
* @author DBogdan ([email protected])
********************************************************************************
* 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.
*******************************************************************************/
#ifndef HMC7044_H_
#define HMC7044_H_
/******************************************************************************/
/***************************** Include Files **********************************/
/******************************************************************************/
#include <stdbool.h>
#include <stdint.h>
#include "delay.h"
#include "spi.h"
/******************************************************************************/
/*************************** Types Declarations *******************************/
/******************************************************************************/
// Enum to select SYNC pin configuration with respect to PLL2 (0x0005)
typedef enum
{
SYNC_DISABLED = 0,
SYNC_MULTICHIP = 1,
SYNC_PULSE_GEN = 2,
SYNC_ALARM = 3
} hmc7044SyncPinModeSelect_t;
// Enum to select input buffer mode (0x000A, 0x000B, 0x000C, 0x000D, 0x000E)
typedef enum
{
BUFFER_ENABLE = (1) << 0,
BUFFER_DISABLE = (0) << 0,
ENABLE_INTERNAL_TERM = (1) << 1,
ENABLE_AC_COUPLE = (1) << 2,
ENABLE_LVPECL = (1) << 3,
ENABLE_HIGH_Z = (1) << 4
} hmc7044InputBufferMode_t;
// Enum to select output driver mode (0x003A, 0x003B, 0x00D0, 0x00DA, 0x00E4,
// 0x00EE, 0x00F8, 0x0102, 0x010C, 0x0116, 0x0120, 0x012A, 0x0134, 0x013E,
// 0x0148, 0x0152)
typedef enum
{
CML = 0,
LVPECL = 1,
LVDS = 2,
CMOS = 3
} hmc7044DriverMode_t;
// Enum to select the GPIx functionality. (0x0046, 0x0047, 0x0048, 0x0049)
typedef enum
{
GPI_ENABLE = (1) << 0,
GPI_DISABLE = (0) << 0,
FORCE_PLL1_HOLDOVER = (0x01) << 1 | GPI_ENABLE,
SELECT_PLL1_REF_BIT1 = (0x02) << 1 | GPI_ENABLE,
SELECT_PLL1_REF_BIT2 = (0x03) << 1 | GPI_ENABLE,
CHIP_SLEEP_MODE = (0x04) << 1 | GPI_ENABLE,
MUTE_REQUSET = (0x05) << 1 | GPI_ENABLE,
SELECT_INTERNAL_VCO_TYPE = (0x06) << 1 | GPI_ENABLE,
SELECT_HI_PERF_MODE = (0x07) << 1 | GPI_ENABLE,
PULSE_GEN_REQUSET = (0x08) << 1 | GPI_ENABLE,
RESEED_REQUEST = (0x09) << 1 | GPI_ENABLE,
RESTART_REQUSET = (0x0A) << 1 | GPI_ENABLE,
FORCE_FANOUT_MODE = (0x0B) << 1 | GPI_ENABLE,
SLIP_REQUEST = (0x0D) << 1 | GPI_ENABLE,
} hmc7044GPIControl_t;
// Enum to select the GPOx functionality. (0x0050, 0x0051, 0x0052, 0x0053)
typedef enum
{
GPO_ENABLE = (1) << 0,
GPO_DISABLE = (0) << 0,
GPO_CMOS_MODE = (1) << 1,
GPO_OPEN_DRAIN_MODE = (0) << 1,
ALARM_SIGNAL = (0x00) << 3 | GPO_ENABLE,
SPI_SDATA = (0x01) << 3 | GPO_ENABLE,
CLKIN3_SIGNAL_LOSS = (0x02) << 3 | GPO_ENABLE,
CLKIN2_SIGNAL_LOSS = (0x03) << 3 | GPO_ENABLE,
CLKIN1_SIGNAL_LOSS = (0x04) << 3 | GPO_ENABLE,
CLKIN0_SIGNAL_LOSS = (0x05) << 3 | GPO_ENABLE,
PLL1_HOLDOVER_ENABLE = (0x06) << 3 | GPO_ENABLE,
PLL1_LOCK_DETECT = (0x07) << 3 | GPO_ENABLE,
PLL1_ACQUIRING_LOCK = (0x08) << 3 | GPO_ENABLE,
PLL1_NEAR_CLOCK_ACQ_STAT = (0x09) << 3 | GPO_ENABLE,
PLL2_LOCK_DETECT = (0x0A) << 3 | GPO_ENABLE,
PLL2_SYSREF_SYNC = (0x0B) << 3 | GPO_ENABLE,
CLOCK_OUTPUT_PHASE_STAT = (0x0C) << 3 | GPO_ENABLE,
PLL1_PLL2_LOCKED = (0x0D) << 3 | GPO_ENABLE,
SYNC_REQUSRT_STAT = (0x0E) << 3 | GPO_ENABLE,
PLL1_ACTIVE_CLKIN0 = (0x0F) << 3 | GPO_ENABLE,
PLL1_ACTIVE_CLKIN1 = (0x10) << 3 | GPO_ENABLE,
PLL1_HOLDOVER_IN_RANGE_STAT = (0x11) << 3 | GPO_ENABLE,
PLL1_HOLDOVER_IN_STAT = (0x12) << 3 | GPO_ENABLE,
PLL1_VCXO_STAT = (0x13) << 3 | GPO_ENABLE,
PLL1_ACTIVE_CLKINx_STAT = (0x14) << 3 | GPO_ENABLE,
PLL1_FSM_BIT0 = (0x15) << 3 | GPO_ENABLE,
PLL1_FSM_BIT1 = (0x16) << 3 | GPO_ENABLE,
PLL1_FSM_BIT2 = (0x17) << 3 | GPO_ENABLE,
PLL1_HOLDOVER_EXIT_PHASE_BIT0 = (0x18) << 3 | GPO_ENABLE,
PLL1_HOLDOVER_EXIT_PHASE_BIT1 = (0x19) << 3 | GPO_ENABLE,
CHANNEL_OUTPUT_FSM_BUSY = (0x1A) << 3 | GPO_ENABLE,
SYSREF_FSM_STAT_BIT0 = (0x1B) << 3 | GPO_ENABLE,
SYSREF_FSM_STAT_BIT1 = (0x1C) << 3 | GPO_ENABLE,
SYSREF_FSM_STAT_BIT2 = (0x1D) << 3 | GPO_ENABLE,
SYSREF_FSM_STAT_BIT3 = (0x1E) << 3 | GPO_ENABLE,
FORCE_LOGIC_1 = (0x1F) << 3 | GPO_ENABLE,
FORCE_LOGIC_0 = (0x20) << 3 | GPO_ENABLE,
PLL1_HOLDOVER_AVG_VALUE_BIT0 = (0x27) << 3 | GPO_ENABLE,
PLL1_HOLDOVER_AVG_VALUE_BIT1 = (0x28) << 3 | GPO_ENABLE,
PLL1_HOLDOVER_AVG_VALUE_BIT2 = (0x29) << 3 | GPO_ENABLE,
PLL1_HOLDOVER_AVG_VALUE_BIT3 = (0x2A) << 3 | GPO_ENABLE,
PLL1_HOLDOVER_CURR_VALUE_BIT0 = (0x2B) << 3 | GPO_ENABLE,
PLL1_HOLDOVER_CURR_VALUE_BIT1 = (0x2C) << 3 | GPO_ENABLE,
PLL1_HOLDOVER_CURR_VALUE_BIT2 = (0x2D) << 3 | GPO_ENABLE,
PLL1_HOLDOVER_CURR_VALUE_BIT3 = (0x2E) << 3 | GPO_ENABLE
} hmc7044GPOControl_t;
// Enum to select output channel (0x00CF, 0x00D9, 0x00E3, 0x00ED, 0x00F7,
// 0x0101, 0x010B, 0x0115, 0x011F, 0x0129, 0x0133, 0x013D, 0x0147, 0x0151
typedef enum
{
CHANNEL_DIV = 0,
ANALOG_DELAY = 1,
OTHER_CHANNEL = 2,
VCO_CLOCK = 3
} hmc7044OutputMuxSelect_t;
// Enum to select output driver impedance selection for CML mode (0x00D0,
// 0x00DA, 0x00E4, 0x00EE, 0x00F8, 0x0102, 0x010C, 0x0116, 0x0120, 0x012A,
// 0x0134, 0x013E, 0x0148, 0x0152)
typedef enum
{
INTERNAL_RES_DISABLE = 0,
INTERNAL_100OHM_PER_PIN = 1,
// INTERNAL_RES_RESERVED = 2,
INTERNAL_50OHM_PER_PIN = 3
} hmc7044DriverImpedance_t;
// Enum to select pulse generator mode (0x005A)
typedef enum
{
PULSE_LEVEL_SENSITIVE = 0,
PULSE_ONE = 1,
PULSE_TWO = 2,
PULSE_FOUR = 3,
PULSE_EIGHT = 4,
PULSE_SIXTEEN = 5,
// PULSE_SIXTEEN = 6,
PULSE_CONTINOUS = 7
} hmc7044SyncPulseGenControl_t;
struct hmc7044_chan_spec {
unsigned int num;
bool disable;
bool high_performance_mode_dis;
bool start_up_mode_dynamic_enable;
bool dynamic_driver_enable;
bool output_control0_rb4_enable;
bool force_mute_enable;
unsigned int divider;
unsigned int coarse_delay;
unsigned int fine_delay;
hmc7044DriverMode_t driver_mode;
hmc7044DriverImpedance_t driver_impedance;
hmc7044OutputMuxSelect_t out_mux_mode;
};
struct hmc7044_config {
uint16_t addr;
uint8_t value;
};
struct hmc7044_dev {
struct spi_desc *spi_desc;
bool is_hmc7043;
uint32_t clkin_freq[4];
uint32_t clkin_freq_ccf[4];
uint32_t vcxo_freq;
uint32_t pll1_pfd;
uint32_t pll2_freq;
uint32_t pll1_loop_bw;
uint32_t sysref_timer_div;
unsigned int pll1_ref_prio_ctrl;
bool clkin0_rfsync_en;
bool clkin1_vcoin_en;
bool high_performance_mode_clock_dist_en;
bool rf_reseeder_en;
hmc7044SyncPinModeSelect_t sync_pin_mode;
hmc7044SyncPulseGenControl_t pulse_gen_mode;
uint32_t in_buf_mode[5];
uint32_t gpi_ctrl[4];
uint32_t gpo_ctrl[4];
uint32_t num_channels;
struct hmc7044_chan_spec *channels;
};
struct hmc7044_init_param {
struct spi_init_param *spi_init;
bool is_hmc7043;
uint32_t clkin_freq[4];
uint32_t clkin_freq_ccf[4];
uint32_t vcxo_freq;
uint32_t pll1_pfd;
uint32_t pll2_freq;
uint32_t pll1_loop_bw;
uint32_t sysref_timer_div;
unsigned int pll1_ref_prio_ctrl;
bool clkin0_rfsync_en;
bool clkin1_vcoin_en;
bool high_performance_mode_clock_dist_en;
bool rf_reseeder_disable;
hmc7044SyncPinModeSelect_t sync_pin_mode;
hmc7044SyncPulseGenControl_t pulse_gen_mode;
uint32_t in_buf_mode[5];
uint32_t gpi_ctrl[4];
uint32_t gpo_ctrl[4];
uint32_t num_channels;
struct hmc7044_chan_spec *channels;
};
/******************************************************************************/
/************************ Functions Declarations ******************************/
/******************************************************************************/
/* Initialize the device. */
int32_t hmc7044_reg_init(struct hmc7044_dev **device,
const struct hmc7044_init_param *init_param);
int32_t hmc7044_init(struct hmc7044_dev **device,
const struct hmc7044_init_param *init_param);
/* Remove the device. */
int32_t hmc7044_remove(struct hmc7044_dev *device);
int32_t hmc7044_write(struct hmc7044_dev *dev,
uint16_t reg, uint8_t val);
int32_t hmc7044_read(struct hmc7044_dev *dev, uint16_t reg, uint8_t *val);
int32_t hmc7044_clk_recalc_rate(struct hmc7044_dev *dev, uint32_t chan_num,
uint64_t *rate);
int32_t hmc7044_clk_round_rate(struct hmc7044_dev *dev, uint32_t rate,
uint64_t *rounded_rate);
int32_t hmc7044_clk_set_rate(struct hmc7044_dev *dev, uint32_t chan_num,
uint64_t rate);
#endif // HMC7044_H_
+128
View File
@@ -0,0 +1,128 @@
/***************************************************************************//**
* @file lmk04828.c
* @brief Implementation of AD9680 Driver.
* @author DBogdan ([email protected])
********************************************************************************
* Copyright 2014-2016(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 <stdlib.h>
#include <stdio.h>
#include "lmk04828.h"
#include "../../../include/error.h"
/***************************************************************************//**
* @brief lmk04828_read
*******************************************************************************/
int32_t lmk04828_read(struct lmk04828_dev *dev,
uint16_t reg_addr,
uint8_t *reg_data)
{
uint8_t tx_buf[2];
uint8_t rx_buf[1];
int32_t ret;
tx_buf[0] = 0x80 | (reg_addr >> 8);
tx_buf[1] = reg_addr & 0xFF;
ret = spi_write_and_read_3wire(dev->spi_desc,
tx_buf, rx_buf, 2, 1);
*reg_data = rx_buf[0];
return ret;
}
/***************************************************************************//**
* @brief lmk04828_write
*******************************************************************************/
int32_t lmk04828_write(struct lmk04828_dev *dev,
uint16_t reg_addr,
uint8_t reg_data)
{
uint8_t buf[3];
int32_t ret;
buf[0] = reg_addr >> 8;
buf[1] = reg_addr & 0xFF;
buf[2] = reg_data;
ret = spi_write_and_read_3wire(dev->spi_desc,
buf, NULL, 3, 0);
return ret;
}
/***************************************************************************//**
* @brief lmk04828_init
*******************************************************************************/
int32_t lmk04828_init(struct lmk04828_dev **device,
const struct lmk04828_init_param *init_param)
{
int ret = SUCCESS;
struct lmk04828_dev *dev;
dev = (struct lmk04828_dev *)malloc(sizeof(*dev));
if (!dev)
return FAILURE;
/* SPI */
ret = spi_init(&dev->spi_desc, init_param->spi_init);
*device = dev;
return ret;
}
/***************************************************************************//**
* @brief Free the resources allocated by lmk04828_setup().
*
* @param dev - The device structure.
*
* @return SUCCESS in case of success, negative error code otherwise.
*******************************************************************************/
int32_t lmk04828_remove(struct lmk04828_dev *dev)
{
int32_t ret;
ret = spi_remove(dev->spi_desc);
free(dev);
return ret;
}
+90
View File
@@ -0,0 +1,90 @@
/***************************************************************************//**
* @file lmk04828.h
* @brief Header file of lmk04828 Driver.
* @author DBogdan ([email protected])
********************************************************************************
* Copyright 2014-2016(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.
*******************************************************************************/
#ifndef LMK04828_H_
#define LMK04828_H_
/******************************************************************************/
/***************************** Include Files **********************************/
/******************************************************************************/
#include <stdint.h>
#include "delay.h"
#include "spi.h"
#include "gpio.h"
/******************************************************************************/
/********************** Macros and Constants Definitions **********************/
/******************************************************************************/
/******************************************************************************/
/*************************** Types Declarations *******************************/
/******************************************************************************/
struct lmk04828_dev {
/* SPI */
struct spi_desc *spi_desc;
};
struct lmk04828_init_param {
/* SPI */
struct spi_init_param *spi_init;
};
struct lmk04828_config {
uint16_t addr;
uint8_t value;
};
/******************************************************************************/
/************************ Functions Declarations ******************************/
/******************************************************************************/
int32_t lmk04828_read(struct lmk04828_dev *dev,
uint16_t reg_addr,
uint8_t *reg_data);
int32_t lmk04828_write(struct lmk04828_dev *dev,
uint16_t reg_addr,
uint8_t reg_data);
int32_t lmk04828_init(struct lmk04828_dev **device,
const struct lmk04828_init_param *init_param);
int32_t lmk04828_remove(struct lmk04828_dev *dev);
#endif
+129
View File
@@ -0,0 +1,129 @@
/***************************************************************************//**
* @file lmx2594.c
* @brief Implementation of AD9680 Driver.
* @author DBogdan ([email protected])
********************************************************************************
* Copyright 2014-2016(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 "../lmx2594/lmx2594.h"
#include <stdlib.h>
#include <stdio.h>
#include "../../../include/error.h"
/***************************************************************************//**
* @brief lmx2594_read
*******************************************************************************/
int32_t lmx2594_read(struct lmx2594_dev *dev,
uint8_t reg_addr,
uint16_t *reg_data)
{
uint8_t tx_buf[1];
uint8_t rx_buf[2];
int32_t ret;
tx_buf[0] = 0x80 | reg_addr;
ret = spi_write_and_read_3wire(dev->spi_desc,
tx_buf, rx_buf, 1, 2);
*reg_data = rx_buf[1];
*reg_data |= ((uint16_t)rx_buf[0]) << 8;
return ret;
}
/***************************************************************************//**
* @brief lmx2594_write
*******************************************************************************/
int32_t lmx2594_write(struct lmx2594_dev *dev,
uint8_t reg_addr,
uint16_t reg_data)
{
uint8_t buf[3];
int32_t ret;
buf[0] = reg_addr;
buf[1] = reg_data >> 8;
buf[2] = reg_data & 0xFF;
ret = spi_write_and_read_3wire(dev->spi_desc,
buf, NULL, 3, 0);
return ret;
}
/***************************************************************************//**
* @brief lmx2594_init
*******************************************************************************/
int32_t lmx2594_init(struct lmx2594_dev **device,
const struct lmx2594_init_param *init_param)
{
int ret = SUCCESS;
struct lmx2594_dev *dev;
dev = (struct lmx2594_dev *)malloc(sizeof(*dev));
if (!dev)
return FAILURE;
/* SPI */
ret = spi_init(&dev->spi_desc, init_param->spi_init);
*device = dev;
return ret;
}
/***************************************************************************//**
* @brief Free the resources allocated by lmx2594_setup().
*
* @param dev - The device structure.
*
* @return SUCCESS in case of success, negative error code otherwise.
*******************************************************************************/
int32_t lmx2594_remove(struct lmx2594_dev *dev)
{
int32_t ret;
ret = spi_remove(dev->spi_desc);
free(dev);
return ret;
}
+90
View File
@@ -0,0 +1,90 @@
/***************************************************************************//**
* @file lmx2594.h
* @brief Header file of lmx2594 Driver.
* @author DBogdan ([email protected])
********************************************************************************
* Copyright 2014-2016(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.
*******************************************************************************/
#ifndef LMX2594_H_
#define LMX2594_H_
/******************************************************************************/
/***************************** Include Files **********************************/
/******************************************************************************/
#include <stdint.h>
#include "delay.h"
#include "spi.h"
#include "gpio.h"
/******************************************************************************/
/********************** Macros and Constants Definitions **********************/
/******************************************************************************/
/******************************************************************************/
/*************************** Types Declarations *******************************/
/******************************************************************************/
struct lmx2594_dev {
/* SPI */
struct spi_desc *spi_desc;
};
struct lmx2594_init_param {
/* SPI */
struct spi_init_param *spi_init;
};
struct lmx2594_config {
uint8_t addr;
uint16_t value;
};
/******************************************************************************/
/************************ Functions Declarations ******************************/
/******************************************************************************/
int32_t lmx2594_read(struct lmx2594_dev *dev,
uint8_t reg_addr,
uint16_t *reg_data);
int32_t lmx2594_write(struct lmx2594_dev *dev,
uint8_t reg_addr,
uint16_t reg_data);
int32_t lmx2594_init(struct lmx2594_dev **device,
const struct lmx2594_init_param *init_param);
int32_t lmx2594_remove(struct lmx2594_dev *dev);
#endif