Commit fd66dc57 authored by Tomasz Wlostowski's avatar Tomasz Wlostowski

boards/ertm14: imported eRTM14 board support files from tom-dev-ertm14 branch…

boards/ertm14: imported eRTM14 board support files from tom-dev-ertm14 branch and made the eRTM14 BSP
parent b4c4b73a
Pipeline #172 failed with stages
in 10 seconds
......@@ -2,3 +2,5 @@ obj-$(CONFIG_TARGET_GENERIC_PHY_8BIT) += boards/generic/board.o
obj-$(CONFIG_TARGET_GENERIC_PHY_16BIT) += boards/generic/board.o
obj-$(CONFIG_TARGET_WR_SWITCH) += boards/wr-switch/main.o boards/wr-switch/gpio-wrs.o boards/wr-switch/ad9516.o
obj-$(CONFIG_TARGET_AFCZ) += boards/afcz/board.o
obj-$(CONFIG_TARGET_ERTM14) += boards/ertm14/board.o boards/ertm14/dds_sync_unit.o boards/ertm14/ertm15_rf_distr.o boards/ertm14/phy_calibration.o
/*
* This work is part of the White Rabbit project
*
* Copyright (C) 2019 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <stdio.h>
#include <ppsi/ppsi.h>
#include "dev/gpio.h"
#include "dev/bb_spi.h"
#include "dev/ad951x.h"
#include "dev/ltc6950.h"
#include "dev/ad9910.h"
#include "dev/ad9520.h"
#include "dev/clock_monitor.h"
#include "dev/24aa025.h"
#include "dev/ad7888.h"
#include "dev/spi_flash.h"
#include "dev/bb_i2c.h"
#include "dev/pps_gen.h"
#include "dev/console.h"
#include "dev/endpoint.h"
#include "softpll_ng.h"
#include "storage.h"
#include "wrc_ptp.h"
#include "ertm15_rf_distr.h"
// allows the eRTM14 board to operate *without* the eRTM15 (no WR support, useful for IPMI testing)
#undef CONFIG_ERTM14_WITHOUT_ERTM15
#include "hw/wb_10mhz_align_unit.h"
#include "wrc-task.h"
#define ERTM14_IUART_MAX_PAYLOAD 100
#define ERTM14_IUART_MSG_MMC_UPDATE 0
#define ERTM14_IUART_MSG_IPMI_CONSOLE_REQ 2
#define ERTM14_IUART_MSG_IPMI_SNMP_REQ 3
#define ERTM14_IUART_MSG_IPMI_CONSOLE_RESP 4
#define ERTM14_IUART_MSG_IPMI_SNMP_RESP 5
#define ERTM14_PSYNC_DEBUG
#ifdef ERTM14_PSYNC_DEBUG
#define psync_dbg(...) pp_printf("[psync-dbg]"__VA_ARGS__)
#else
#define psync_dbg(...)
#endif
#define ertm_verbose(...) pp_printf("[ertm] "__VA_ARGS__)
#define ertm_error(...) pp_printf("ERROR [ertm] "__VA_ARGS__)
struct ertm14_board board;
static struct ertm14_board_config ertm14_configs[ ERTM14_MAX_CONFIGS ];
static struct ertm14_board_config *ertm14_current_config;
static struct gpio_pin pin_pll_main_cs_n = { &board.gpio_aux, 0 };
static struct gpio_pin pin_pll_main_sdi = { &board.gpio_aux, 1 };
static struct gpio_pin pin_pll_main_sdo = { &board.gpio_aux, 2 };
static struct gpio_pin pin_pll_main_sclk = { &board.gpio_aux, 3 };
static struct gpio_pin pin_pll_main_reset = { &board.gpio_aux, 4 };
static struct gpio_pin pin_pll_main_lock = { &board.gpio_aux, 5 };
static struct gpio_pin pin_pll_ext_cs_n = { &board.gpio_aux, 6 };
static struct gpio_pin pin_pll_ext_sdi = { &board.gpio_aux, 7 };
static struct gpio_pin pin_pll_ext_sdo = { &board.gpio_aux, 8 };
static struct gpio_pin pin_pll_ext_sclk = { &board.gpio_aux, 9 };
static struct gpio_pin pin_pll_ext_reset = { &board.gpio_aux, 10 };
static struct gpio_pin pin_pll_ext_lock = { &board.gpio_aux, 11 };
static struct gpio_pin pin_mac_addr_scl = { &board.gpio_aux, 13 };
static struct gpio_pin pin_mac_addr_sda = { &board.gpio_aux, 12 };
static struct gpio_pin pin_main_xo_en_n = { &board.gpio_aux, 14 };
static struct gpio_pin pin_ltc6950_sclk = { &board.gpio_aux, 15 };
static struct gpio_pin pin_ltc6950_sdi = { &board.gpio_aux, 16 };
static struct gpio_pin pin_ltc6950_sdo = { &board.gpio_aux, 17 };
static struct gpio_pin pin_ltc6950_ce_gen = { &board.gpio_aux, 18 };
static struct gpio_pin pin_ltc6950_ce_distr = { &board.gpio_aux, 19 };
static struct gpio_pin pin_ltc6950_sync = { &board.gpio_aux, 20 };
static struct gpio_pin pin_ad9910_lo_sdio = { &board.gpio_aux, 4+21 };
static struct gpio_pin pin_ad9910_lo_sclk = { &board.gpio_aux, 5+21 };
static struct gpio_pin pin_ad9910_lo_reset = { &board.gpio_aux, 6+21 };
static struct gpio_pin pin_ad9910_lo_io_update = { &board.gpio_aux, 3+21 };
const struct gpio_pin pin_ad9910_lo_sync_smp_err = { &board.gpio_aux, 5+21 };
static struct gpio_pin pin_ad9910_ref_sdio = { &board.gpio_aux, 4+28 };
static struct gpio_pin pin_ad9910_ref_sclk = { &board.gpio_aux, 5+28 };
static struct gpio_pin pin_ad9910_ref_reset = { &board.gpio_aux, 6+28 };
static struct gpio_pin pin_ad9910_ref_io_update = { &board.gpio_aux, 3+28 };
const struct gpio_pin pin_ad9910_ref_sync_smp_err = { &board.gpio_aux, 5+28 };
static struct gpio_pin pin_ocxo_override = { &board.gpio_aux, 48 };
static struct gpio_pin pin_ocxo_cs_n = { &board.gpio_aux, 51 };
static struct gpio_pin pin_ocxo_sclk = { &board.gpio_aux, 50 };
static struct gpio_pin pin_ocxo_data = { &board.gpio_aux, 49 };
static struct gpio_pin pin_pwrmon_adc_cs_n = { &board.gpio_aux, 52 };
static struct gpio_pin pin_pwrmon_adc_dout = { &board.gpio_aux, 46 };
static struct gpio_pin pin_pwrmon_adc_din = { &board.gpio_aux, 47 };
static struct gpio_pin pin_pwrmon_adc_sclk = { &board.gpio_aux, 45 };
static struct gpio_pin pin_ad9520_clka_scl = { &board.gpio_aux, 57 };
static struct gpio_pin pin_ad9520_clka_sda = { &board.gpio_aux, 58 };
static struct gpio_pin pin_ad9520_clkb_scl = { &board.gpio_aux, 59 };
static struct gpio_pin pin_ad9520_clkb_sda = { &board.gpio_aux, 60 };
static struct gpio_pin pin_sys_clk_sel_stb = { &board.gpio_aux, 61 };
static struct gpio_pin pin_sys_clk_sel_next = { &board.gpio_aux, 62 };
static struct ad95xx_config pll_ext_10mhz_config =
#include "configs/ertm_14_pll_ext_10mhz.h"
static struct ad95xx_config pll_main_dot050_config =
#include "configs/ertm_14_pll_main_dot050_config.h"
static struct ad95xx_config pll_main_ocxo_config =
#include "configs/ertm_14_pll_ocxo_config.h"
static struct ltc6950_config pll_ertm15_bootstrap_config =
#include "configs/ertm_15_ltc6950_config.h"
static struct ad95xx_config clk_dist_ertm15_default_config =
#include "configs/ertm_15_ad9520_default_config.h"
static spll_gain_schedule_t spll_main_ocxo_gain_sched;
static int has_new_config = 0;
static int ertm_init_complete = 0;
static int ertm14_update_config_task(void);
// fixme: use PRESENCE_A/B pins instead of LTC6950 PLL chip
static int check_ertm15_presence(void)
{
ltc6950_init(&board.ltc6950_pll, &board.spi_ltc6950);
int id = ltc6950_read( &board.ltc6950_pll, 0x16 );
if( id != 0x65 )
return 0;
return 1;
}
static void ertm14_spll_setup(void)
{
/* configure a suitable PI gain schedule for the SoftPLL: */
spll_gain_schedule_t* gs= &spll_main_ocxo_gain_sched;
gs->n_stages = 2;
/* we start with ~100 Hz bandwidth to make it lock reasonably fast */
gs->stages[0].kp = -4000 * 16;
gs->stages[0].ki = -5 * 16;
gs->stages[0].lock_samples = 30000;
gs->stages[0].shift = 16;
/* once it's locked, the loop bandwidth is switched to ~0.1 Hz to filter out WR link added phase noise */
gs->stages[1].kp = -3000;
gs->stages[1].ki = -5;
gs->stages[1].lock_samples = 10000;
gs->stages[1].shift = 16;
// disable 2nd stage for DOT050 and Morion OCXO
if ( board.mode & ERTM14_MODE_WITHOUT_ERTM15 )
gs->n_stages = 1;
if ( board.mode & ERTM14_MODE_OCXO_10MHZ )
gs->n_stages = 1;
#if 0
gs->n_stages = 1;
/* we start with ~100 Hz bandwidth to make it lock reasonably fast */
gs->stages[0].kp = -4000;
gs->stages[0].ki = -5;
gs->stages[0].lock_samples = 10000;
gs->stages[0].shift = 12;
#endif
spll_set_gain_schedule( gs );
}
static int ad9910_set_fine_delay( struct dds_sync_unit_channel *ch, int n_taps )
{
struct ad9910_device *dev;
if(ch->index == ERTM14_DDS_SYNC_REF)
dev = &board.dds_ad9910_ref;
else
dev = &board.dds_ad9910_lo;
ad9910_configure_sync( dev, 1, n_taps );
return 0;
}
static void ertm14_dds_trigger_ioupdate( struct ad9910_device *dev )
{
int channel = (dev == &board.dds_ad9910_ref ? ERTM14_DDS_IOUPDATE_REF : ERTM14_DDS_IOUPDATE_LO);
dds_sync_force_pulse( &board.dds_sync_dev, channel );
}
static int ertm14_switch_sys_clock( int use_sys_from_pll )
{
gen_gpio_out( &pin_sys_clk_sel_next, use_sys_from_pll );
gen_gpio_out( &pin_sys_clk_sel_stb, 1);
gen_gpio_out( &pin_sys_clk_sel_stb, 0);
return 0;
}
uint32_t ch_delays[] = { 100000, 100000, 100000, 100000, 100500, 100000 };
static int ertm14_dds_sync_init(void)
{
const int n_params = 4;
struct {
uint32_t id;
int channel;
const char *name;
} params[] = {
{ CAL_PARAM_DDS_LO_IOUPDATE_DELAY_PS, ERTM14_DDS_IOUPDATE_LO, "DDS LO IoUpdate" },
{ CAL_PARAM_DDS_REF_IOUPDATE_DELAY_PS, ERTM14_DDS_IOUPDATE_REF, "DDS REF IoUpdate" },
{ CAL_PARAM_CLKA_SYNC_DELAY_PS, ERTM14_PLL_SYNC_CLKA, "CLKA Dist SYNC" },
{ CAL_PARAM_CLKB_SYNC_DELAY_PS, ERTM14_PLL_SYNC_CLKB, "CLKB Dist SYNC" }
};
int i;
// retrieve calibration delays on DDS IOUPDATE and CLKAB SYNC lines from the calibration stored in eeprom
for( i = 0; i < n_params; i++ )
{
uint32_t val = ch_delays[ params[i].channel ];
int res = storage_get_calibration_parameter( params[i].id, &val );
pp_printf("Sync Unit channel '%s': delay = %d ps", params[i].name, val);
if (res < 0)
{
pp_printf("(default value)");
}
pp_printf("\n");
}
dds_sync_unit_create( &board.dds_sync_dev, BASE_ERTM14_DDS_SYNC_UNIT );
// Sync_in: continuous waveform, use external delay line (inside AD9910)
// produce a continuos sync clock for the DDSes
dds_sync_unit_setup_channel ( &board.dds_sync_dev, ERTM14_DDS_SYNC_LO, 1, ch_delays[ERTM14_DDS_SYNC_LO], 0, 1 );
dds_sync_unit_setup_channel ( &board.dds_sync_dev, ERTM14_DDS_SYNC_REF, 1, ch_delays[ERTM14_DDS_SYNC_REF], 0, 1 );
dds_sync_unit_set_external_fine_delay( &board.dds_sync_dev, ERTM14_DDS_SYNC_REF, 75, ad9910_set_fine_delay );
dds_sync_unit_set_external_fine_delay( &board.dds_sync_dev, ERTM14_DDS_SYNC_LO, 75, ad9910_set_fine_delay );
// DDS IOupdate: internal delay line, single-shot mode, positive polarity
pp_printf("ref delay = %d lo delay = %d\n", ch_delays[ERTM14_DDS_IOUPDATE_REF], ch_delays[ERTM14_DDS_IOUPDATE_LO] );
dds_sync_unit_setup_channel ( &board.dds_sync_dev, ERTM14_DDS_IOUPDATE_LO, 1, ch_delays[ERTM14_DDS_IOUPDATE_LO], 0, 0 );
dds_sync_unit_setup_channel ( &board.dds_sync_dev, ERTM14_DDS_IOUPDATE_REF, 1, ch_delays[ERTM14_DDS_IOUPDATE_REF], 0, 0 );
// CLKAB Sync: internal delay line, single-shot mode, negative polarity
dds_sync_unit_setup_channel ( &board.dds_sync_dev, ERTM14_PLL_SYNC_CLKA, 1, ch_delays[ERTM14_PLL_SYNC_CLKA], 1, 0 );
dds_sync_unit_setup_channel ( &board.dds_sync_dev, ERTM14_PLL_SYNC_CLKB, 1, ch_delays[ERTM14_PLL_SYNC_CLKB], 1, 0 );
return 0;
}
static void ertm14_dds_sync_calibrate(void)
{
shw_pps_gen_init();
shw_pps_gen_enable_output(1);
shw_pps_gen_unmask_output(1);
int i = 0, j;
uint32_t channel_mask = ( 1 << ERTM14_DDS_SYNC_LO ) | ( 1<< ERTM14_DDS_SYNC_REF );
int fine = 0;
#define MIN_SAMPLE_WINDOW_LENGTH 3
#define AD9910_FINE_DELAY_STEP_PS 75
struct dds_sync_window {
int smp_err;
int start;
int length;
int best_start;
int best_length;
int setpoint;
} windows[2];
for( j=0; j<2; j++ )
{
windows[j].best_start = -1;
windows[j].best_length = -1;
windows[j].start = -1;
windows[j].length = 0;
}
for(i = 0; i < 100; i++)
{
// pp_printf("Sync [fine %d]! ", fine);
dds_sync_unit_setup_channel ( &board.dds_sync_dev, ERTM14_DDS_SYNC_LO, 1, 100000 + fine, 0, 1 );
dds_sync_unit_setup_channel ( &board.dds_sync_dev, ERTM14_DDS_SYNC_REF, 1, 100000 + fine, 0, 1 );
dds_sync_unit_set_external_fine_delay( &board.dds_sync_dev, ERTM14_DDS_SYNC_REF, 75, ad9910_set_fine_delay );
dds_sync_unit_set_external_fine_delay( &board.dds_sync_dev, ERTM14_DDS_SYNC_LO, 75, ad9910_set_fine_delay );
dds_sync_unit_trigger( &board.dds_sync_dev, channel_mask, 1 );
while ( !dds_sync_unit_poll( &board.dds_sync_dev, channel_mask ) );
windows[0].smp_err = !!gen_gpio_in( &pin_ad9910_lo_sync_smp_err );
windows[1].smp_err = !!gen_gpio_in( &pin_ad9910_ref_sync_smp_err );
for( j=0; j<2;j++ )
{
if (windows[j].smp_err)
{
windows[j].start = -1;
windows[j].length = 0;
}
else
{
if( windows[j].start < 0 )
windows[j].start = fine;
windows[j].length++;
if( windows[j].length >= MIN_SAMPLE_WINDOW_LENGTH && windows[j].best_length < 0)
{
windows[j].best_start = windows[j].start;
windows[j].best_length = windows[j].length;
}
}
}
// pp_printf("SmpERR LO %d REF %d\n", windows[0].smp_err, windows[1].smp_err);
fine += AD9910_FINE_DELAY_STEP_PS;
}
for( j=0; j<2; j++ )
{
// sync_in fine delay setpoint is the
windows[j].setpoint = windows[j].best_start + ( AD9910_FINE_DELAY_STEP_PS * windows[j].best_length ) / 2;
}
pp_printf("DDS_LO SYNC start=%d ps length=%d ps setpoint=%d ps\n",
windows[0].best_start, windows[0].best_length, windows[0].setpoint
);
pp_printf("DDS_REF SYNC start=%d ps length=%d ps setpoint=%d ps\n",
windows[1].best_start, windows[1].best_length, windows[1].setpoint
);
dds_sync_unit_setup_channel ( &board.dds_sync_dev, ERTM14_DDS_SYNC_LO, 1, 100000 + windows[0].setpoint, 0, 1 );
dds_sync_unit_setup_channel ( &board.dds_sync_dev, ERTM14_DDS_SYNC_REF, 1, 100000 + windows[1].setpoint, 0, 1 );
}
static int ertm14_align_clocks(void)
{
uint32_t channel_mask = ( 1 << ERTM14_DDS_SYNC_LO ) | ( 1<< ERTM14_DDS_SYNC_REF );
dds_sync_unit_trigger( &board.dds_sync_dev, channel_mask, 1 );
while ( !dds_sync_unit_poll( &board.dds_sync_dev, channel_mask ) );
int smp_err_lo = !!gen_gpio_in( &pin_ad9910_lo_sync_smp_err );
int smp_err_ref = !!gen_gpio_in( &pin_ad9910_ref_sync_smp_err );
psync_dbg( "DDS SYNC complete: errLO=%d errREF=%d\n", smp_err_lo, smp_err_ref);
// now that we are done syncing DDS internal clocks, disable the fpga SYNC_CLK output
ad9910_configure_sync( &board.dds_ad9910_ref, 0, 0 );
ad9910_configure_sync( &board.dds_ad9910_lo, 0, 0 );
//for(;;)
{
channel_mask = (1 << ERTM14_PLL_SYNC_CLKA) |
(1 << ERTM14_PLL_SYNC_CLKB) |
(1 << ERTM14_DDS_IOUPDATE_LO) |
(1 << ERTM14_DDS_IOUPDATE_REF);
dds_sync_unit_setup_channel ( &board.dds_sync_dev, ERTM14_DDS_IOUPDATE_LO, 1, ch_delays[ERTM14_DDS_IOUPDATE_LO], 0, 0 );
//ch_delays[ERTM14_DDS_IOUPDATE_LO] += 200;
dds_sync_unit_trigger( &board.dds_sync_dev, channel_mask, 0 ); // trigger on PPS now
while ( !dds_sync_unit_poll( &board.dds_sync_dev, channel_mask ) );
psync_dbg( "DDS IOUPDATE & CLKAB sync complete\n");
}
return 0;
}
extern struct console_device console_ipmi_dev;
static void handle_iuart_request( uint8_t *buf, int size )
{
uint8_t tx_buf[ERTM14_IUART_MAX_PAYLOAD];
int n_tx;
int type = buf[0];
if( size <= 0 )
return;
switch( type )
{
case ERTM14_IUART_MSG_IPMI_CONSOLE_REQ:
n_tx = console_ipmi_process_request( &console_ipmi_dev, buf + 1, size - 1, tx_buf + 1, sizeof(tx_buf) - 1 );
tx_buf[0] = ERTM14_IUART_MSG_IPMI_CONSOLE_RESP;
iuart_send_message(&board.iuart_14, tx_buf, n_tx + 1);
break;
case ERTM14_IUART_MSG_IPMI_SNMP_REQ:
//snmp_respond(uint8_t *buf);
break;
default:
return;
}
}
static void iuart_14_poll(void)
{
int msg = iuart_recv_message(&board.iuart_14);
if (msg <= 0)
return;
//pp_printf("RxM\n");
if( msg == START_INSN_CHAR_VAL )
{
pp_printf("req %d %d %d\n",board.iuart_14.rx_buf, board.iuart_14.rx_csize, board.iuart_14.rx_csize );
handle_iuart_request( board.iuart_14.rx_buf, board.iuart_14.rx_csize );
}
}
static void ertm14_clock_monitor_init(void)
{
wb_cm_init(&board.ertm14_cmon, BASE_CLOCK_MONITOR, 5);
wb_cm_set_ref_frequency( &board.ertm14_cmon, DMTD_CLOCK_FREQ_HZ );
wb_cm_configure(&board.ertm14_cmon, ERTM14_CMON_CLK_DMTD, 2, 6250000 );
}
static void ertm14_align_ref_out_to_pps(void)
{
int i;
shw_pps_gen_init();
shw_pps_gen_enable_output(1);
shw_pps_gen_unmask_output(1);
for(i=0;i<10;)
{
writel( TAU_CSR_TRIG, TAU_REG_CSR + BASE_ERTM14_10MHZ_ALIGN_UNIT );
uint32_t csr = readl( TAU_REG_CSR + BASE_ERTM14_10MHZ_ALIGN_UNIT);
pp_printf("csr %x tau %x\n", csr, TAU_REG_CSR + BASE_ERTM14_10MHZ_ALIGN_UNIT );
if (csr & TAU_CSR_DONE)
{
int val = TAU_CSR_OFFSET_R( csr );
pp_printf("measured pps offset: %d\n", val);
i++;
}
usleep(200000);
}
}
#define CLK_PPS_STATE_START 0
#define CLK_PPS_STATE_MASTER 1
#define CLK_PPS_STATE_WAIT_SERVO 2
#define CLK_PPS_STATE_SLAVE 3
#define CLK_PPS_STATE_DONE 4
#define CLK_PPS_STATE_RUN_SYNC 5
struct ertm14_clk_pps_sync_fsm {
int state;
int prev_mode;
int prev_locked;
} clk_pps_sync_state;
static void ertm14_clk_pps_sync_restart(void)
{
struct ertm14_clk_pps_sync_fsm* fsm = &clk_pps_sync_state;
fsm->state = CLK_PPS_STATE_START;
}
static void ertm14_clk_pps_sync_init(void)
{
ertm14_clk_pps_sync_restart();
}
static void ertm14_clk_pps_sync_task(void)
{
extern struct pp_instance ppi_static;
struct pp_instance *ppi = &ppi_static;
struct ertm14_clk_pps_sync_fsm* fsm = &clk_pps_sync_state;
int mode = wrc_ptp_get_mode();
if( mode != fsm->prev_mode )
{
psync_dbg("mode changed, restarting sync fsm\n");
fsm->state = CLK_PPS_STATE_START;
}
fsm->prev_mode = mode;
switch(fsm->state)
{
case CLK_PPS_STATE_START:
{
fsm->prev_locked = -1;
int mode = wrc_ptp_get_mode();
if( mode == WRC_MODE_MASTER || mode == WRC_MODE_GM )
{
fsm->state = CLK_PPS_STATE_MASTER;
psync_dbg("master mode\n");
} else {
fsm->state = CLK_PPS_STATE_SLAVE;
psync_dbg("slave mode\n");
}
}
break;
case CLK_PPS_STATE_MASTER:
{
if( spll_check_lock(0) )
{
psync_dbg("pll locked\n");
fsm->state = CLK_PPS_STATE_RUN_SYNC;
}
}
break;
case CLK_PPS_STATE_SLAVE:
{
struct wr_servo_state *ss = &((struct wr_data *)ppi->ext_data)->servo_state;
if( ss->state == WR_TRACK_PHASE )
{
psync_dbg("servo tracking phase, proceeding with sync\n");
fsm->state = CLK_PPS_STATE_RUN_SYNC;
}
}
break;
case CLK_PPS_STATE_RUN_SYNC:
{
ertm14_align_clocks();
fsm->state = CLK_PPS_STATE_DONE;
}
break;
default : break;
}
}
// initializes the eRTM15 LTC6950 PLL & OCXO
void ertm15_pll_init(void)
{
ltc6950_init(&board.ltc6950_pll, &board.spi_ltc6950);
int id = ltc6950_read( &board.ltc6950_pll, 0x16 );
if( id != 0x65 )
{
pp_printf("Error initializing LTC6950 (read RevID: 0x%x, expected: 0x%x)\n", id, 0x65 );
}
// load default 'bootstrap' config and check what is the OCXO frequency
ltc6950_configure(&board.ltc6950_pll, &pll_ertm15_bootstrap_config);
ertm_verbose("Probing OCXO frequency...");
// measure the OCXO freq
wb_cm_restart(&board.ertm14_cmon);
while( ( wb_cm_read( &board.ertm14_cmon ) & ( 1 << ERTM14_CMON_CLK_PLL_FB) ) == 0 )
{
pp_printf(".");
usleep(200000);
}
int ocxo_freq = board.ertm14_cmon.freqs[ERTM14_CMON_CLK_PLL_FB];
pp_printf("%d MHz measured.\n", ocxo_freq);
int ocxo_10mhz = ocxo_freq > ( 10000000 - 20000 ) && ocxo_freq < ( 10000000 + 20000 );
int ocxo_100mhz = ocxo_freq > ( 100000000 - 20000 ) && ocxo_freq < ( 100000000 + 20000 );
if( ! (ocxo_100mhz || ocxo_10mhz) )
{
pp_printf("Error: the OCXO has neither 10 nor 100 MHz center frequency. WTF?\n");
}
if( ocxo_10mhz )
{ // PLL: R div = 1, N div = 100, LV/CM div: 50 (20 MHz output)
ltc6950_write( &board.ltc6950_pll, 0x15, 50 ); // RDIVOUT = 0, output div = 50
board.mode |= ERTM14_MODE_OCXO_10MHZ;
} else if (ocxo_100mhz)
{
pp_printf("Using 100 mhz ocxo\n");
//ltc6950_write( &board.ltc6950_pll, 0x15, 4 ); // RDIVOUT = 0, output div = 50
ltc6950_write( &board.ltc6950_pll, 0x8, 0x1 ); // reference divider = 1
ltc6950_write( &board.ltc6950_pll, 0x15, 50 ); // RDIVOUT = 0, output div = 50
ltc6950_write( &board.ltc6950_pll, 0x0a, 10 ); // N divider = 10 (VCO @ 1GHz, PFD @ 10 MHz)
board.mode |= ERTM14_MODE_OCXO_100MHZ;
}
//ertm14_align_ref_out_to_pps();
}
int ertm14_init_clkab_distribution()
{
bb_i2c_create( &board.i2c_clka_distr, &pin_ad9520_clka_scl, &pin_ad9520_clka_sda );
bb_i2c_create( &board.i2c_clkb_distr, &pin_ad9520_clkb_scl, &pin_ad9520_clkb_sda );
bb_i2c_init( &board.i2c_clka_distr );
bb_i2c_init( &board.i2c_clkb_distr );
ad9520_init( &board.dev_clka_distr, &board.i2c_clka_distr, 0x5c );
ad9520_init( &board.dev_clkb_distr, &board.i2c_clkb_distr, 0x5c );
pp_printf("Init CLKAB distribution\n");
ad9520_configure( &board.dev_clka_distr, &clk_dist_ertm15_default_config);
ad9520_configure( &board.dev_clkb_distr, &clk_dist_ertm15_default_config);
// set 250 MHz output on CLKA/CLKB on the front panel
ad9520_set_output_divider( &board.dev_clka_distr, ERTM14_CLKAB_OUT_FRONT_PANEL, 4 ); // divide by 4 -> 250 MHz
ad9520_set_output_divider( &board.dev_clkb_distr, ERTM14_CLKAB_OUT_FRONT_PANEL, 4 );
ad9520_enable_output( &board.dev_clka_distr, ERTM14_CLKAB_OUT_FRONT_PANEL, 1 );
ad9520_enable_output( &board.dev_clkb_distr, ERTM14_CLKAB_OUT_FRONT_PANEL, 1 );
// force a SYNC pulse to make sure the SYNC_N pins of the AD9520s are high
// (so that any clock output is possible)
dds_sync_force_pulse( &board.dds_sync_dev, ERTM14_PLL_SYNC_CLKA );
dds_sync_force_pulse( &board.dds_sync_dev, ERTM14_PLL_SYNC_CLKB );
}
int ertm14_init_ref_clock_distribution(void)
{
int main_stat = ad951x_init(&board.ad9516_main, &board.spi_pll_main, &pin_pll_main_reset, &pin_pll_main_lock);
int ext_stat = ad951x_init(&board.ad9516_ext, &board.spi_pll_ext, &pin_pll_ext_reset, &pin_pll_ext_lock);
if( main_stat < 0 )
{
ertm_error( "Failed to configure the main clock distribution AD9516 (chip not responding)\n ");
return -1;
}
if( ext_stat < 0 )
{
ertm_error( "Failed to configure the external 10 MHz clock multiplier AD9516 (chip not responding)\n ");
return -1;
}
if (board.mode & ERTM14_MODE_WITHOUT_ERTM15)
{
gen_gpio_out(&pin_main_xo_en_n, 0); // enable DOT050 VCXO
ad951x_configure(&board.ad9516_main, &pll_main_dot050_config);
}
else
{
gen_gpio_out(&pin_main_xo_en_n, 1); // disable DOT050 VCXO
ad951x_configure(&board.ad9516_main, &pll_main_ocxo_config);
// ad951x_configure(&board.ad9516_ext, &pll_ext_10mhz_config);
}
}
int ertm15_init_dds(void)
{
// reset both DDS chips
gen_gpio_out(&pin_ad9910_ref_reset, 1);
gen_gpio_out(&pin_ad9910_lo_reset, 1);
usleep(10);
gen_gpio_out(&pin_ad9910_ref_reset, 0);
gen_gpio_out(&pin_ad9910_lo_reset, 0);
// initialize DDS synchronizer unit
ertm14_dds_sync_init();
ad9910_probe( &board.dds_ad9910_ref, &board.spi_ad9910_ref, ertm14_dds_trigger_ioupdate );
ad9910_probe( &board.dds_ad9910_lo, &board.spi_ad9910_lo, ertm14_dds_trigger_ioupdate );
}
int ertm14_init_mac_eeprom(void)
{
bb_i2c_create( &board.i2c_mac_addr, &pin_mac_addr_scl, &pin_mac_addr_sda );
bb_i2c_init( &board.i2c_mac_addr );
m24aa025_init( &board.m24_mac_ids[0], &board.i2c_mac_addr, 0x50 );
m24aa025_init( &board.m24_mac_ids[1], &board.i2c_mac_addr, 0x51 );
uint8_t mac[6];
m24aa025_read_mac( &board.m24_mac_ids[0], mac );
ep_set_mac_addr( mac );
}
int ertm14_init(void)
{
int i;
uint32_t id;
has_new_config = 0;
ertm_init_complete = 0;
memset( &board, 0, sizeof( struct ertm14_board ));
#ifdef CONFIG_ERTM14_WITHOUT_ERTM15
board.mode |= ERTM14_MODE_WITHOUT_ERTM15;
#endif
ertm14_config_init();
wb_gpio_create( &board.gpio_aux, BASE_AUXWB );
gen_gpio_set_dir(&pin_main_xo_en_n, 1);
gen_gpio_out(&pin_main_xo_en_n, 0);
bb_spi_create ( &board.spi_pll_main,
&pin_pll_main_cs_n,
&pin_pll_main_sdi,
&pin_pll_main_sdo,
&pin_pll_main_sclk,
AD951X_BIT_DELAY
);
bb_spi_create ( &board.spi_pll_ext,
&pin_pll_ext_cs_n,
&pin_pll_ext_sdi,
&pin_pll_ext_sdo,
&pin_pll_ext_sclk,
AD951X_BIT_DELAY
);
bb_spi_create( &board.spi_ltc6950,
&pin_ltc6950_ce_gen,
&pin_ltc6950_sdi,
&pin_ltc6950_sdo,
&pin_ltc6950_sclk,
100 );
bb_spi_create( &board.spi_ad9910_ref,
NULL,
&pin_ad9910_ref_sdio,
&pin_ad9910_ref_sdio,
&pin_ad9910_ref_sclk,
100 );
bb_spi_create( &board.spi_ad9910_lo,
NULL,
&pin_ad9910_lo_sdio,
&pin_ad9910_lo_sdio,
&pin_ad9910_lo_sclk,
100 );
int ertm15_present = check_ertm15_presence();
if( !ertm15_present )
board.mode |= ERTM14_MODE_WITHOUT_ERTM15;
if ( board.mode & ERTM14_MODE_WITHOUT_ERTM15 )
ertm_verbose( "Configuring board *WITHOUT* eRTM15 support (eRTM15 not found or disabled in software).\n");
else
ertm_verbose( "Configuring board WITH eRTM15 support.\n");
ertm14_clock_monitor_init();
if( ! (board.mode & ERTM14_MODE_WITHOUT_ERTM15 ) )
{
ertm15_pll_init();
}
ertm14_init_ref_clock_distribution();
ertm_verbose("Switching system clock to CLK_SYS\n");
ertm14_switch_sys_clock(1);
gen_gpio_out(&pin_ocxo_override, 0);
bb_spi_create( &board.spi_ocxo_dac,
&pin_ocxo_cs_n,
&pin_ocxo_data,
&pin_ocxo_data,
&pin_ocxo_sclk,
100 );
/* Unique MAC address storage chips (eRTM14 - IC7 and IC8) */
ertm14_init_mac_eeprom();
/* RF Power Monitor ADC (eRTM15 - IC43) */
bb_spi_create( &board.spi_ad7888,
&pin_pwrmon_adc_cs_n,
&pin_pwrmon_adc_din,
&pin_pwrmon_adc_dout,
&pin_pwrmon_adc_sclk,
100 );
ad7888_create( &board.pwrmon_adc, &board.spi_ad7888 );
if( ! (board.mode & ERTM14_MODE_WITHOUT_ERTM15 ) )
{
ertm_verbose("Initializing RF distribution\n");
/* RF distribution switches and shift registers controlling these (eRTM15 - IC26..28) */
ertm15_rf_distr_init( &board.rf_distr, &board.pwrmon_adc );
/* Now that the clocks are ready, init the DDS synthesizers */
ertm_verbose("Initializing DDSes\n");
ertm15_init_dds();
ad9910_program(&board.dds_ad9910_ref, 205000000ULL, 0, 0x0 );
ad9910_program(&board.dds_ad9910_lo, 205000000ULL, 0, 0x0 );
}
/* Setup the SoftPLL for the OCXO */
ertm14_spll_setup();
if( ! (board.mode & ERTM14_MODE_WITHOUT_ERTM15 ) )
{
/* Init CLKA/CLKB distribution */
ertm_verbose("Initializing CLKA/CLKB distribution...\n");
ertm14_init_clkab_distribution();
ertm_verbose("Calibrating DDS sync pulse...\n");
ertm14_dds_sync_calibrate();
}
ertm_verbose("Init IUART14\n");
iuart_init_bare( &board.iuart_14, BASE_IUART_14, 115200 );
ertm_verbose("eRTM14/15 early init done\n");
ertm_init_complete = 1;
return 0;
}
void ertm14_config_init()
{
int i, j;
for(i = 0; i < ERTM14_MAX_CONFIGS; i++)
{
struct ertm14_board_config *cfg = &ertm14_configs[i];
cfg->valid = 1;
cfg->lo.ftw = 0x39374BC6;
cfg->ref.ftw = 0x39374BC6;
cfg->lo.ampl_factor = 50;
cfg->ref.ampl_factor = 50;
for( j = 0; j <= ERTM14_RF_OUT_MAX_ID; j++)
{
cfg->ref.out_state [j] = ERTM15_RF_OUT_MONITOR;
cfg->lo.out_state [j] = ERTM15_RF_OUT_MONITOR;
}
for(j = 0; j <= ERTM14_CLKAB_OUT_MAX_ID; j++)
{
cfg->clka_freq_hz[j] = 500000000;
cfg->clkb_freq_hz[j] = 500000000;
}
cfg->clka_enable_mask = -1; //( 1<<11);
cfg->clkb_enable_mask = -1; //( 1<<11);
}
};
struct ertm14_board_config *ertm14_get_config(int config_id)
{
return &ertm14_configs[config_id];
}
int ertm14_apply_config(int config_id)
{
ertm14_current_config = &ertm14_configs[config_id];
has_new_config = 1;
}
int ertm14_get_current_config_id()
{
int i;
for(i = 0; i < ERTM14_MAX_CONFIGS; i++)
if( &ertm14_configs[i] == ertm14_current_config )
return i;
return -1;
}
static int ertm14_commit_config( struct ertm14_board_config *cfg )
{
int i;
for( i = 0; i <= ERTM14_CLKAB_OUT_MAX_ID; i++)
{
// digital clocks
int freq_a = cfg->clka_freq_hz[i];
int freq_b = cfg->clkb_freq_hz[i];
int div_a = ertm14_get_clkab_divider( freq_a );
int div_b = ertm14_get_clkab_divider( freq_b );
int enable_a = ( cfg->clka_enable_mask & (1<<i) ) ? 1 : 0;
int enable_b = ( cfg->clkb_enable_mask & (1<<i) ) ? 1 : 0;
pp_printf("CLKA%d: freq=%d Hz, divider=%d, enable=%d\n", i, freq_a, div_a, enable_a);
pp_printf("CLKA%d: freq=%d Hz, divider=%d, enable=%d\n", i, freq_b, div_b, enable_b);
ad9520_set_output_divider( &board.dev_clka_distr, i, div_a ); // divide by 4 -> 250 MHz
ad9520_set_output_divider( &board.dev_clkb_distr, i, div_b );
ad9520_enable_output( &board.dev_clka_distr, i, enable_a );
ad9520_enable_output( &board.dev_clkb_distr, i, enable_b );
}
// DDSes
ad9910_program(&board.dds_ad9910_lo, cfg->lo.ftw, 0, cfg->lo.ampl_factor );
ad9910_program(&board.dds_ad9910_ref, cfg->ref.ftw, 0, cfg->ref.ampl_factor );
pp_printf("DDS LO: FTW=0x%08x, ampl=%d\n", cfg->lo.ftw, cfg->lo.ampl_factor );
pp_printf("DDS REF: FTW=0x%08x, ampl=%d\n", cfg->ref.ftw, cfg->ref.ampl_factor );
for( i = ERTM14_RF_OUT_MIN_ID; i <= ERTM14_RF_OUT_MAX_ID; i++)
{
int st_lo = cfg->lo.out_state[i] == ERTM15_RF_OUT_ON ? 1 : 0;
int st_ref = cfg->ref.out_state[i] == ERTM15_RF_OUT_ON ? 1 : 0;
pp_printf("i %d lo %x ref %x\n", i, st_lo, st_ref );
ertm15_rf_distr_output_enable( &board.rf_distr, ERTM15_RF_LO, i, st_lo );
ertm15_rf_distr_output_enable( &board.rf_distr, ERTM15_RF_REF, i, st_ref );
}
ertm15_update_rf_switches( &board.rf_distr );
}
static int ertm14_update_config_task(void)
{
if (has_new_config && ertm_init_complete && ertm14_current_config->valid)
{
int i;
pp_printf("New config detected, applying...\n");
has_new_config = 0;
if (!(board.mode & ERTM14_MODE_WITHOUT_ERTM15))
{
ertm14_commit_config(ertm14_current_config);
ertm14_clk_pps_sync_restart();
}
}
}
static struct {
int freq;
int divider;
} clkab_freqs [] = {
{ 1000000000, 1 },
{ 500000000, 2},
{ 250000000, 4},
{ 100000000, 5},
{ 125000000, 8},
{ 62500000, 16},
{-1,-1}
};
int ertm14_get_clkab_divider( int freq )
{
int i;
for (i=0;clkab_freqs[i].freq >= 0; i++)
{
if (clkab_freqs[i].freq == freq)
return clkab_freqs[i].divider;
}
return -1;
}
int ertm14_get_supported_clkab_freqs( int *freqs, int max_count )
{
int i;
for(i = 0;clkab_freqs[i].freq >= 0; i++)
{
if( i < max_count )
{
freqs [i] = clkab_freqs[i].freq;
} else
break;
}
return i;
}
int wrc_board_early_init()
{
/*initialize flash*/
flash_init();
/*initialize I2C bus*/
bb_i2c_init( &dev_i2c_fmc );
/*init storage (Flash / W1 EEPROM / I2C EEPROM*/
storage_init( &dev_i2c_fmc, FMC_EEPROM_ADR);
net_rst();
ep_init();
/* Sleep for 1s to make sure WRS v4.2 always realizes that
* the link is down */
timer_delay_ms(200);
ep_enable(1, 1);
timer_delay_ms(200);
return ertm14_init();
}
int wrc_board_init()
{
return 0;
}
extern int phy_calibration_poll();
extern void phy_calibration_init();
int wrc_board_create_tasks()
{
wrc_task_create( "iuart14", NULL, iuart_14_poll );
wrc_task_create( "clk-pps-sync", ertm14_clk_pps_sync_init, ertm14_clk_pps_sync_task );
wrc_task_create( "ertm-config", NULL, ertm14_update_config_task );
wrc_task_create( "phy-cal", phy_calibration_init, phy_calibration_poll );
return 0;
}
/*
* This work is part of the White Rabbit project
*
* Released according to the GNU GPL, version 2 or any later version.
*/
#ifndef __BOARD_ERTM14_H
#define __BOARD_ERTM14_H
#include "dev/gpio.h"
#include "dev/bb_spi.h"
#include "dev/ad951x.h"
#include "dev/ltc6950.h"
#include "dev/ad9910.h"
#include "dev/ad9520.h"
#include "dev/clock_monitor.h"
#include "dev/24aa025.h"
#include "dev/ad7888.h"
#include "ertm15_rf_distr.h"
#include "dds_sync_unit.h"
#include "dev/spi_flash.h"
#include "dev/bb_i2c.h"
#include "dev/iuart.h"
#define BOARD_MAX_CONSOLE_DEVICES 2
/* Board-specific parameters */
#define TICS_PER_SECOND 1000
/* WR Core system/CPU clock frequency in Hz */
#define CPU_CLOCK 62500000ULL
/* WR Reference clock period (picoseconds) and frequency (Hz) */
#define REF_CLOCK_PERIOD_PS 16000
#define REF_CLOCK_FREQ_HZ 62500000
/* Center DMTD frequency (Hz) */
#define DMTD_CLOCK_FREQ_HZ 62500000
/* Baud rate of the builtin UART (does not apply to the VUART) */
#define CONSOLE_UART_BAUDRATE 921600ULL
/* Maximum number of simultaneously created sockets */
#define NET_MAX_SOCKETS 12
/* Socket buffer size, determines the max. RX packet size */
#define NET_MAX_SKBUF_SIZE 512
/* Number of auxillary clock channels - usually equal to the number of FMCs */
#define NUM_AUX_CLOCKS 1
int board_init(void);
int board_update(void);
/* spll parameter that are board-specific */
# define BOARD_DIVIDE_DMTD_CLOCKS 0
# define NS_PER_CLOCK 16
#define BOARD_MAX_CHAN_REF 1
#define BOARD_MAX_CHAN_AUX 2
#define BOARD_MAX_PTRACKERS 1
#ifdef CONFIG_IP
#define HAS_IP 1
#else
#define HAS_IP 0
#endif
#define ERTM14_MAX_CONFIGS 8
extern unsigned char *BASE_MINIC;
extern unsigned char *BASE_EP;
#define SDB_ADDRESS 0x50000
#define FMC_EEPROM_ADR 0x50
#define BASE_AUXWB 0x48000
#define BASE_SOFTPLL 0x40200
#define BASE_PPS_GEN 0x40300
#define BASE_UART 0x40500
#define BASE_SYSCON 0x40400
#define BASE_EP 0x40100
#define BASE_MINIC 0x40000
#define BASE_ONEWIRE 0x40600
#define BASE_IUART_14 (BASE_AUXWB + 0x200)
#define BASE_ERTM14_DDS_SYNC_UNIT (BASE_AUXWB + 0x300)
#define BASE_CLOCK_MONITOR (BASE_AUXWB + 0x100)
#define BASE_ERTM14_10MHZ_ALIGN_UNIT (BASE_AUXWB + 0x400)
#define ERTM14_RF_OUT_MIN_ID 4
#define ERTM14_RF_OUT_MAX_ID 12
#define ERTM14_CLKAB_OUT_MIN_ID 0
#define ERTM14_CLKAB_OUT_MAX_ID 11
// clock monitor core channels (see ertm14_top.vhd for assignment to the clock monitor core)
#define ERTM14_CMON_CLK_SYS 0
#define ERTM14_CMON_CLK_DMTD 1
#define ERTM14_CMON_CLK_PLL_FB 2
#define ERTM14_CMON_CLK_REF 3
#define ERTM14_CMON_CLK_RX 4
#define ERTM14_CLKAB_OUT_FRONT_PANEL 11
// #define ERTM14_CALIBRATION_DEBUG 1
#define ERTM14_MODE_WITHOUT_ERTM15 (1 << 0)
#define ERTM14_MODE_OCXO_10MHZ (1 << 1)
#define ERTM14_MODE_OCXO_100MHZ (1 << 2)
struct ertm14_board
{
struct gpio_device gpio_aux;
struct wb_clock_monitor_device ertm14_cmon;
struct spi_bus spi_pll_main;
struct spi_bus spi_pll_ext;
struct spi_bus spi_ltc6950;
struct spi_bus spi_ad9910_ref;
struct spi_bus spi_ad9910_lo;
struct spi_bus spi_ocxo_dac;
struct spi_bus spi_ad7888;
//struct spi_bus spi_flash;
struct i2c_bus i2c_clka_distr;
struct i2c_bus i2c_clkb_distr;
struct ad951x_device ad9516_main;
struct ad951x_device ad9516_ext;
struct ltc6950_device ltc6950_pll;
struct ad9910_device dds_ad9910_ref;
struct ad9910_device dds_ad9910_lo;
struct ad7888_device pwrmon_adc;
struct ertm15_rf_distribution_device rf_distr;
//struct spi_flash_device dev_flash;
struct ad9520_device dev_clka_distr;
struct ad9520_device dev_clkb_distr;
struct i2c_bus i2c_mac_addr;
struct m24aa025_device m24_mac_ids[2];
struct dds_sync_unit_device dds_sync_dev;
struct iuart_device iuart_14;
int mode;
};
struct ertm14_dds_config
{
uint32_t ftw;
uint8_t out_state[ERTM14_RF_OUT_MAX_ID + 1];
int out_power[ERTM14_RF_OUT_MAX_ID + 1];
int amp_power;
int ampl_factor;
};
struct ertm14_board_config
{
int valid;
struct ertm14_dds_config ref;
struct ertm14_dds_config lo;
uint32_t clka_freq_hz[ERTM14_CLKAB_OUT_MAX_ID + 1];
uint32_t clkb_freq_hz[ERTM14_CLKAB_OUT_MAX_ID + 1];
uint32_t clka_enable_mask;
uint32_t clkb_enable_mask;
};
extern struct ertm14_board board;
void ertm14_config_init(void);
struct ertm14_board_config *ertm14_get_config(int config_id);
int ertm14_apply_config(int config_id);
int ertm14_get_current_config_id(void);
int ertm14_is_config_ready(void);
int ertm14_get_clkab_divider( int freq );
#endif /* __BOARD_WRC_H */
#!/usr/bin/python
import sys
import re
n_regs = 0;
regs = []
for l in open(sys.argv[1],"rb").readlines():
r = re.match('^"(\w+)","\w+","(\w+)"', l)
if r != None:
addr = int(r.group(1), 16)
value = int(r.group(2), 16)
regs += [(addr,value)]
n_regs+=1
print("{")
print("%d," % n_regs)
print("{")
for r in regs:
print(" { 0x%02x, 0x%02x }, " % r );
print("}\n};\n")
{
68,
{
{ 0x00, 0x18 },
{ 0x01, 0x00 },
{ 0x02, 0x10 },
{ 0x03, 0xc3 },
{ 0x04, 0x00 },
{ 0x10, 0x7c },
{ 0x11, 0x01 },
{ 0x12, 0x00 },
{ 0x13, 0x06 },
{ 0x14, 0x09 },
{ 0x15, 0x00 },
{ 0x16, 0x05 },
{ 0x17, 0x00 },
{ 0x18, 0x07 },
{ 0x19, 0x00 },
{ 0x1a, 0x00 },
{ 0x1b, 0x00 },
{ 0x1c, 0x43 },
{ 0x1d, 0x00 },
{ 0x1e, 0x00 },
{ 0x1f, 0x0e },
{ 0xa0, 0x01 },
{ 0xa1, 0x00 },
{ 0xa2, 0x00 },
{ 0xa3, 0x01 },
{ 0xa4, 0x00 },
{ 0xa5, 0x00 },
{ 0xa6, 0x01 },
{ 0xa7, 0x00 },
{ 0xa8, 0x00 },
{ 0xa9, 0x01 },
{ 0xaa, 0x00 },
{ 0xab, 0x00 },
{ 0xf0, 0x0a },
{ 0xf1, 0x0a },
{ 0xf2, 0x0a },
{ 0xf3, 0x0a },
{ 0xf4, 0x0a },
{ 0xf5, 0x0a },
{ 0x140, 0x42 },
{ 0x141, 0x43 },
{ 0x142, 0x43 },
{ 0x143, 0x43 },
{ 0x190, 0x00 },
{ 0x191, 0x80 },
{ 0x192, 0x00 },
{ 0x193, 0xbb },
{ 0x194, 0x00 },
{ 0x195, 0x00 },
{ 0x196, 0x00 },
{ 0x197, 0x00 },
{ 0x198, 0x00 },
{ 0x199, 0x22 },
{ 0x19a, 0x00 },
{ 0x19b, 0x11 },
{ 0x19c, 0x20 },
{ 0x19d, 0x00 },
{ 0x19e, 0x22 },
{ 0x19f, 0x00 },
{ 0x1a0, 0x11 },
{ 0x1a1, 0x00 },
{ 0x1a2, 0x00 },
{ 0x1a3, 0x00 },
{ 0x1e0, 0x02 },
{ 0x1e1, 0x02 },
{ 0x230, 0x00 },
{ 0x231, 0x00 },
{ 0x232, 0x00 },
}
};
"AD9516 Setup File"
"Rev.","1.1.0"
""
"Addr(Hex)","Value(Bin)","Value(Hex)"
"0000","00011000","18"
"0001","00000000","00"
"0002","00010000","10"
"0003","11000011","C3"
"0004","00000000","00"
"0010","01111100","7C"
"0011","00000001","01"
"0012","00000000","00"
"0013","00000110","06"
"0014","00001001","09"
"0015","00000000","00"
"0016","00000101","05"
"0017","00000000","00"
"0018","00000111","07"
"0019","00000000","00"
"001A","00000000","00"
"001B","00000000","00"
"001C","00000010","02"
"001D","00000000","00"
"001E","00000000","00"
"001F","00001110","0E"
"00A0","00000001","01"
"00A1","00000000","00"
"00A2","00000000","00"
"00A3","00000001","01"
"00A4","00000000","00"
"00A5","00000000","00"
"00A6","00000001","01"
"00A7","00000000","00"
"00A8","00000000","00"
"00A9","00000001","01"
"00AA","00000000","00"
"00AB","00000000","00"
"00F0","00001000","08"
"00F1","00001010","0A"
"00F2","00001000","08"
"00F3","00001010","0A"
"00F4","00001010","0A"
"00F5","00001000","08"
"0140","01000011","43"
"0141","01000010","42"
"0142","01000011","43"
"0143","01000010","42"
"0190","00100010","22"
"0191","00000000","00"
"0192","00000000","00"
"0193","00100010","22"
"0194","00000000","00"
"0195","00000000","00"
"0196","00100010","22"
"0197","00000000","00"
"0198","00000000","00"
"0199","00100010","22"
"019A","00000000","00"
"019B","00000000","00"
"019C","00100000","20"
"019D","00000000","00"
"019E","00100010","22"
"019F","00000000","00"
"01A0","00000000","00"
"01A1","00100000","20"
"01A2","00000000","00"
"01A3","00000000","00"
"01E0","00000000","00"
"01E1","00000010","02"
"0230","00000000","00"
"0231","00000000","00"
"0232","00000000","00"
"","",""
"Other Settings..."
"REF 1:",10
"REF 2:",0
"VCO:",1500
"CLK:",1200
"CPRSet:",5100
"Auto Update:",1
"Load All Regs:",1
{
68,
{
{ 0x00, 0x18 },
{ 0x01, 0x00 },
{ 0x02, 0x10 },
{ 0x03, 0xc3 },
{ 0x04, 0x00 },
{ 0x10, 0x7c },
{ 0x11, 0x01 },
{ 0x12, 0x00 },
{ 0x13, 0x06 },
{ 0x14, 0x09 },
{ 0x15, 0x00 },
{ 0x16, 0x05 },
{ 0x17, 0x00 },
{ 0x18, 0x07 },
{ 0x19, 0x00 },
{ 0x1a, 0x00 },
{ 0x1b, 0x00 },
{ 0x1c, 0x02 },
{ 0x1d, 0x00 },
{ 0x1e, 0x00 },
{ 0x1f, 0x0e },
{ 0xa0, 0x01 },
{ 0xa1, 0x00 },
{ 0xa2, 0x00 },
{ 0xa3, 0x01 },
{ 0xa4, 0x00 },
{ 0xa5, 0x00 },
{ 0xa6, 0x01 },
{ 0xa7, 0x00 },
{ 0xa8, 0x00 },
{ 0xa9, 0x01 },
{ 0xaa, 0x00 },
{ 0xab, 0x00 },
{ 0xf0, 0x08 },
{ 0xf1, 0x0a },
{ 0xf2, 0x08 },
{ 0xf3, 0x0a },
{ 0xf4, 0x0a },
{ 0xf5, 0x08 },
{ 0x140, 0x43 },
{ 0x141, 0x42 },
{ 0x142, 0x43 },
{ 0x143, 0x42 },
{ 0x190, 0x22 },
{ 0x191, 0x00 },
{ 0x192, 0x00 },
{ 0x193, 0xff },
{ 0x194, 0x00 },
{ 0x195, 0x00 },
{ 0x196, 0x22 },
{ 0x197, 0x00 },
{ 0x198, 0x00 },
{ 0x199, 0x22 },
{ 0x19a, 0x00 },
{ 0x19b, 0x00 },
{ 0x19c, 0x00 },
{ 0x19d, 0x00 },
{ 0x19e, 0x22 },
{ 0x19f, 0x00 },
{ 0x1a0, 0x00 },
{ 0x1a1, 0x00 },
{ 0x1a2, 0x00 },
{ 0x1a3, 0x00 },
{ 0x1e0, 0x00 },
{ 0x1e1, 0x02 },
{ 0x230, 0x00 },
{ 0x231, 0x00 },
{ 0x232, 0x00 },
}
};
"AD9516 Setup File"
"Rev.","1.1.0"
""
"Addr(Hex)","Value(Bin)","Value(Hex)"
"0000","00011000","18"
"0001","00000000","00"
"0002","00010000","10"
"0003","11000011","C3"
"0004","00000000","00"
"0010","01111101","7D"
"0011","00000001","01"
"0012","00000000","00"
"0013","00000110","06"
"0014","00001001","09"
"0015","00000000","00"
"0016","00000101","05"
"0017","00000000","00"
"0018","00000111","07"
"0019","00000000","00"
"001A","00000000","00"
"001B","00000000","00"
"001C","00000010","02"
"001D","00000000","00"
"001E","00000000","00"
"001F","00001110","0E"
"00A0","00000001","01"
"00A1","00000000","00"
"00A2","00000000","00"
"00A3","00000001","01"
"00A4","00000000","00"
"00A5","00000000","00"
"00A6","00000001","01"
"00A7","00000000","00"
"00A8","00000000","00"
"00A9","00000001","01"
"00AA","00000000","00"
"00AB","00000000","00"
"00F0","00001000","08"
"00F1","00001010","0A"
"00F2","00001000","08"
"00F3","00001010","0A"
"00F4","00001010","0A"
"00F5","00001000","08"
"0140","01000011","43"
"0141","01000010","42"
"0142","01000011","43"
"0143","01000010","42"
"0190","00100010","22"
"0191","10000000","80"
"0192","00000000","00"
"0193","00100010","22"
"0194","10000000","80"
"0195","00000000","00"
"0196","00100010","22"
"0197","10000000","80"
"0198","00000000","00"
"0199","00000000","00"
"019A","00000000","00"
"019B","00000000","00"
"019C","00100000","20"
"019D","00000000","00"
"019E","00000000","00"
"019F","00000000","00"
"01A0","00000000","00"
"01A1","00100000","20"
"01A2","00000000","00"
"01A3","00000000","00"
"01E0","00000000","00"
"01E1","00000001","01"
"0230","00000000","00"
"0231","00000000","00"
"0232","00000000","00"
"","",""
"Other Settings..."
"REF 1:",10
"REF 2:",0
"VCO:",1500
"CLK:",125
"CPRSet:",5100
"Auto Update:",1
"Load All Regs:",1
{
68,
{
{ 0x00, 0x18 },
{ 0x01, 0x00 },
{ 0x02, 0x10 },
{ 0x03, 0xc3 },
{ 0x04, 0x00 },
{ 0x10, 0x7d },
{ 0x11, 0x01 },
{ 0x12, 0x00 },
{ 0x13, 0x06 },
{ 0x14, 0x09 },
{ 0x15, 0x00 },
{ 0x16, 0x05 },
{ 0x17, 0x00 },
{ 0x18, 0x07 },
{ 0x19, 0x00 },
{ 0x1a, 0x00 },
{ 0x1b, 0x00 },
{ 0x1c, 0x02 },
{ 0x1d, 0x00 },
{ 0x1e, 0x00 },
{ 0x1f, 0x0e },
{ 0xa0, 0x01 },
{ 0xa1, 0x00 },
{ 0xa2, 0x00 },
{ 0xa3, 0x01 },
{ 0xa4, 0x00 },
{ 0xa5, 0x00 },
{ 0xa6, 0x01 },
{ 0xa7, 0x00 },
{ 0xa8, 0x00 },
{ 0xa9, 0x01 },
{ 0xaa, 0x00 },
{ 0xab, 0x00 },
{ 0xf0, 0x08 },
{ 0xf1, 0x0a },
{ 0xf2, 0x08 },
{ 0xf3, 0x0a },
{ 0xf4, 0x0a },
{ 0xf5, 0x08 },
{ 0x140, 0x43 },
{ 0x141, 0x42 },
{ 0x142, 0x43 },
{ 0x143, 0x42 },
{ 0x190, 0x00 },
{ 0x191, 0x00 },
{ 0x192, 0x00 },
{ 0x193, 0x88 },
{ 0x194, 0x80 },
{ 0x195, 0x00 },
{ 0x196, 0x00 },
{ 0x197, 0x00 },
{ 0x198, 0x00 },
{ 0x199, 0x00 },
{ 0x19a, 0x00 },
{ 0x19b, 0x00 },
{ 0x19c, 0x00 },
{ 0x19d, 0x00 },
{ 0x19e, 0x00 },
{ 0x19f, 0x00 },
{ 0x1a0, 0x00 },
{ 0x1a1, 0x00 },
{ 0x1a2, 0x00 },
{ 0x1a3, 0x00 },
{ 0x1e0, 0x00 },
{ 0x1e1, 0x01 },
{ 0x230, 0x00 },
{ 0x231, 0x00 },
{ 0x232, 0x00 },
}
};
{
83,
{
{ 0x00, 0x00 },
{ 0x02, 0x00 },
{ 0x03, 0x00 },
{ 0x04, 0x01 }, // readback active
{ 0x05, 0x00 },
{ 0x06, 0x00 },
{ 0x10, 0x7c },
{ 0x11, 0x01 },
{ 0x12, 0x00 },
{ 0x13, 0x00 },
{ 0x14, 0x03 },
{ 0x15, 0x00 },
{ 0x16, 0x04 },
{ 0x17, 0x01 << 2 }, // status -> N div out
{ 0x18, 0x07 },
{ 0x19, 0x00 },
{ 0x1a, 0x00 },
{ 0x1b, 0x00 },
{ 0x1c, 0x00 },
{ 0x1d, 0x80 },
{ 0x1e, 0x00 },
{ 0x1f, 0x00 },
{ 0x20, 0x00 },
{ 0xf0, 0x64 },
{ 0xf1, 0x64 },
{ 0xf2, 0x64 },
{ 0xf3, 0x64 },
{ 0xf4, 0x64 },
{ 0xf5, 0x64 },
{ 0xf6, 0x64 },
{ 0xf7, 0x64 },
{ 0xf8, 0x64 },
{ 0xf9, 0x64 },
{ 0xfa, 0x64 },
{ 0xfb, 0x64 },
{ 0xfc, 0x00 },
{ 0xfd, 0x00 },
{ 0xfe, 0x00 },
{ 0x190, 0x11 },
{ 0x191, 0xc0 },
{ 0x192, 0x00 },
{ 0x193, 0x11 },
{ 0x194, 0xc0 },
{ 0x195, 0x00 },
{ 0x196, 0x11 },
{ 0x197, 0xc0 },
{ 0x198, 0x00 },
{ 0x199, 0x11 },
{ 0x19a, 0xc0 },
{ 0x19b, 0x00 },
{ 0x19c, 0x00 },
{ 0x1e0, 0x00 },
{ 0x1e1, 0x21 },
{ 0x230, 0x00 },
{ 0x231, 0x00 },
{ 0x232, 0x00 },
{ 0xa00, 0x00 },
{ 0xa01, 0x00 },
{ 0xa02, 0x00 },
{ 0xa03, 0x02 },
{ 0xa04, 0x00 },
{ 0xa05, 0x04 },
{ 0xa06, 0x0e },
{ 0xa07, 0x00 },
{ 0xa08, 0x10 },
{ 0xa09, 0x0e },
{ 0xa0a, 0x00 },
{ 0xa0b, 0xf0 },
{ 0xa0c, 0x0b },
{ 0xa0d, 0x01 },
{ 0xa0e, 0x90 },
{ 0xa0f, 0x01 },
{ 0xa10, 0x01 },
{ 0xa11, 0xe0 },
{ 0xa12, 0x01 },
{ 0xa13, 0x02 },
{ 0xa14, 0x30 },
{ 0xa15, 0x80 },
{ 0xa16, 0xff },
{ 0xb00, 0x00 },
{ 0xb01, 0x00 },
{ 0xb02, 0x00 },
{ 0xb03, 0x00 },
}
};
"AD9520 Setup File"
"Rev.","0.0.1"
""
"Addr(Hex)","Value(Bin)","Value(Hex)"
"0000","00000000","00"
"0002","00000000","00"
"0003","00000000","00"
"0004","00000000","00"
"0005","00000000","00"
"0006","00000000","00"
"0010","01111101","7D"
"0011","00000001","01"
"0012","00000000","00"
"0013","00000000","00"
"0014","00000011","03"
"0015","00000000","00"
"0016","00000110","06"
"0017","00000000","00"
"0018","00000110","06"
"0019","00000000","00"
"001A","00000000","00"
"001B","00000000","00"
"001C","01000000","40"
"001D","10000000","80"
"001E","00000000","00"
"001F","00000000","00"
"0020","00000000","00"
"00F0","01100100","64"
"00F1","01100100","64"
"00F2","01100100","64"
"00F3","01100100","64"
"00F4","01100100","64"
"00F5","01100100","64"
"00F6","01100100","64"
"00F7","01100100","64"
"00F8","01100100","64"
"00F9","01100100","64"
"00FA","01100100","64"
"00FB","01100100","64"
"00FC","00000000","00"
"00FD","00000000","00"
"00FE","00000000","00"
"0190","00110011","33"
"0191","00000000","00"
"0192","00000000","00"
"0193","00110011","33"
"0194","00000000","00"
"0195","00000000","00"
"0196","00110011","33"
"0197","00000000","00"
"0198","00000000","00"
"0199","00110011","33"
"019A","00000000","00"
"019B","00000000","00"
"019C","00000000","00"
"01E0","00000000","00"
"01E1","00100001","21"
"0230","00000000","00"
"0231","00000000","00"
"0232","00000000","00"
"0A00","00000000","00"
"0A01","00000000","00"
"0A02","00000000","00"
"0A03","00000010","02"
"0A04","00000000","00"
"0A05","00000100","04"
"0A06","00001110","0E"
"0A07","00000000","00"
"0A08","00010000","10"
"0A09","00001110","0E"
"0A0A","00000000","00"
"0A0B","11110000","F0"
"0A0C","00001011","0B"
"0A0D","00000001","01"
"0A0E","10010000","90"
"0A0F","00000001","01"
"0A10","00000001","01"
"0A11","11100000","E0"
"0A12","00000001","01"
"0A13","00000010","02"
"0A14","00110000","30"
"0A15","10000000","80"
"0A16","11111111","FF"
"0B00","00000000","00"
"0B01","00000000","00"
"0B02","00000000","00"
"0B03","00000000","00"
"","",""
"Other Settings..."
"REF 1:",30.72
"REF 2:",30.72
"VCO:",2949.12
"CLK:",1000
"CPRSet:",5100
"Auto Update:",1
"Load All Regs:",0
""
"CheckSum Values"
"8-Bit Device Checksum (uses R0x000 to R0x232): ","0x6EA"
"8-Bit Data Pattern Checksum (all regs): ","0xB3C"
"16-Bit Device Checksum (uses R0x000 to R0x232): ","0x3638A"
"16-Bit Data Pattern Checksum (all regs): ","0x5CD74"
// this is the default bootstrap config for LTC6950:
// R divider = 1
// N divider = 100
// LV/CM output: bypass divider (forward whatever OCXO produces to the FPGA)
{
23,
{{0x00, 0x00},
{0x01, 0x00},
{0x02, 0x00},
{0x03, 0x08},
{0x04, 0x00},
{0x05, 0x0b}, // CP current = 11.2 mA
{0x06, 0x00},
{0x07, 0x00},
{0x08, 0x01},
{0x09, 0x00},
{0x0a, 0x64}, // N divider = 100 (VCO @ 1GHz, PFD @ 10 MHz)
{0x0b, 0x04}, // disable FILTR for 100 MHz
{0x0c, 0x00},
{0x0d, 0x81},
{0x0e, 0x00},
{0x0f, 0x84}, // 250 MHz
{0x10, 0x00},
{0x11, 0x80},
{0x12, 0x00},
{0x13, 0x80},
{0x14, 0x00},
{0x15, 0x81}, // RDIVOUT = 1 (drive LV/CM with R divider output)
{0x16, 0x00}}
};
python ./ad9510_convert_regs.py ertm_14_pll_main_dot050.stp > ertm_14_pll_main_dot050_config.h
\ No newline at end of file
/*
* This work is part of the White Rabbit project
*
* Copyright (C) 2019 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* ertm14_dds_sync - driver for DDS Sync Unit (fine pulse generator) */
#include "board.h"
#include "dev/ad9910.h"
#include "hw/wb_dds_sync_unit.h"
#include "dds_sync_unit.h"
#define DS_CSR_FORCE0_OFFSET 8 // fixme
void dds_sync_unit_create( struct dds_sync_unit_device *dev, uint32_t base )
{
int i;
dev->base = (void*) base;
writel( DS_CSR_PLL_RST, dev->base + DS_REG_CSR ); // reset pll
usleep(10);
writel( 0, dev->base + DS_REG_CSR ); // un-reset pll
for(i=0;i<DDS_SYNC_N_CHANNELS;i++)
{
dev->channels[i].delay_tap_size = 78 /* ps */;
dev->channels[i].index = i;
}
}
void dds_sync_unit_setup_channel ( struct dds_sync_unit_device* dev, int ch, int enable, int pps_offset_ps, int polarity, int continuous )
{
dev->channels[ch].flags = (enable ? DDS_SYNC_ENABLED : 0 );
dev->channels[ch].pps_offset_ps = pps_offset_ps;
if(polarity)
dev->channels[ch].flags |= DDS_SYNC_NEGATIVE;
if(continuous)
dev->channels[ch].flags |= DDS_SYNC_CONTINUOUS;
}
void dds_sync_unit_set_external_fine_delay ( struct dds_sync_unit_device* dev, int ch, int tap_size, int (*set_external_delay)( struct dds_sync_unit_channel* ch, int ) )
{
dev->channels[ch].flags |= DDS_SYNC_USE_EXT_FINE_DELAY;
dev->channels[ch].delay_tap_size = tap_size;
dev->channels[ch].set_external_delay = set_external_delay;
}
void dds_sync_force_pulse( struct dds_sync_unit_device* dev, int channel )
{
struct dds_sync_unit_channel* ch = &dev->channels[channel];
int polarity = ch->flags & DDS_SYNC_NEGATIVE;
uint32_t ocr = (1 << DS_OCR0_PPS_OFFS_SHIFT)
| (0x0 << DS_OCR0_MASK_SHIFT)
| (0 << DS_OCR0_FINE_SHIFT)
| (polarity ? DS_OCR0_POL : 0 );
writel( ocr, dev->base + DS_REG_OCR0 + 4 * channel); // configure
uint32_t trig_mask = ( 1 << ( channel + DS_CSR_FORCE0_OFFSET) );
// pp_printf("ForceSync ch %x ocr %x mask %x\n", channel, ocr, trig_mask);
writel( trig_mask, dev->base + DS_REG_CSR ); // configure
}
static uint8_t rotr( uint8_t x, int n )
{
return (x >> n) | (x << (8-n) );
}
void dds_sync_unit_trigger( struct dds_sync_unit_device* dev, uint32_t mask, int force_now )
{
int i;
uint32_t trig_mask = 0;
for(i = 0 ; i < DDS_SYNC_N_CHANNELS; i++ )
{
struct dds_sync_unit_channel* ch = &dev->channels[i];
if( (ch->flags & DDS_SYNC_ENABLED) && ( mask & (1<<i)))
{
uint32_t ocr;
int polarity = ch->flags & DDS_SYNC_NEGATIVE;
int continuous = ch->flags & DDS_SYNC_CONTINUOUS;
uint32_t coarse_par = ch->pps_offset_ps / 16000; // refclk period = 16 ns = 16000 ps
uint32_t coarse_ser = ch->pps_offset_ps / 2000 - coarse_par * 8;
uint32_t fine = (ch->pps_offset_ps % 2000) / ch->delay_tap_size;
uint32_t mask = coarse_ser; // 24/09 VHDL generates mask internally
ocr = (coarse_par << DS_OCR0_PPS_OFFS_SHIFT)
| (mask << DS_OCR0_MASK_SHIFT)
| (fine << DS_OCR0_FINE_SHIFT)
| (polarity ? DS_OCR0_POL : 0 )
| (continuous ? DS_OCR0_CONT : 0 );
//pp_printf("Channel %d OCR %x\n", c->index, ocr );
writel( ocr, dev->base + DS_REG_OCR0 + 4 * i); // configure
if(ch->flags & DDS_SYNC_USE_EXT_FINE_DELAY)
{
ch->set_external_delay( ch, fine );
}
trig_mask |= (1<<i);
}
}
// pp_printf("TrigMask %x\n", trig_mask );
if(force_now)
trig_mask <<= DS_CSR_FORCE0_OFFSET; // fixme: use bitshifts from header file
writel( trig_mask, dev->base + DS_REG_CSR); // arm trigger
}
int dds_sync_unit_poll( struct dds_sync_unit_device* dev, uint32_t mask )
{
uint32_t rv = readl( dev->base + DS_REG_CSR);
int i;
for(i = 0 ; i < DDS_SYNC_N_CHANNELS; i++ )
{
if( (mask & (1<<i)) == 0 )
continue;
struct dds_sync_unit_channel* ch = &dev->channels[i];
uint32_t mask = 1 << ( DS_CSR_READY_SHIFT + i);
if( (ch->flags & DDS_SYNC_ENABLED) && ( (rv & mask) == 0 ) )
return 0;
}
return 1;
}
/*
* This work is part of the White Rabbit project
*
* Copyright (C) 2019 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __ERTM14_DDS_SYNC_H
#define __ERTM14_DDS_SYNC_H
#define DDS_SYNC_ENABLED 0x1
#define DDS_SYNC_NEGATIVE 0x2
#define DDS_SYNC_USE_EXT_FINE_DELAY 0x4
#define DDS_SYNC_CONTINUOUS 0x8
#define DDS_SYNC_N_CHANNELS 6
// sync unit channels
// SYNC_IN(+/-) of AD9910
#define ERTM14_DDS_SYNC_LO 0
#define ERTM14_DDS_SYNC_REF 1 // fixme: inverted cannel order in HDL
// SYNC_N inputs of the AD9520s (backplane clock distribution)
#define ERTM14_PLL_SYNC_CLKA 2
#define ERTM14_PLL_SYNC_CLKB 3
// I/O_UPDATE(+/-) of AD9910
#define ERTM14_DDS_IOUPDATE_LO 4
#define ERTM14_DDS_IOUPDATE_REF 5
struct dds_sync_unit_channel {
uint32_t flags;
int pps_offset_ps;
int index;
int delay_tap_size;
int (*set_external_delay)( struct dds_sync_unit_channel* ch, int n_taps );
};
struct dds_sync_unit_device {
void* base;
struct dds_sync_unit_channel channels[DDS_SYNC_N_CHANNELS];
};
void dds_sync_unit_create( struct dds_sync_unit_device *dev, uint32_t base );
void dds_sync_unit_setup_channel ( struct dds_sync_unit_device* dev, int ch, int enable, int pps_offset_ps, int polarity, int continuous );
void dds_sync_unit_set_external_fine_delay ( struct dds_sync_unit_device* dev, int ch, int tap_size, int (*set_external_delay)( struct dds_sync_unit_channel* ch, int ) );
void dds_sync_unit_trigger( struct dds_sync_unit_device* dev, uint32_t mask, int force_now );
void dds_sync_force_pulse( struct dds_sync_unit_device* dev, int ch );
int dds_sync_unit_poll( struct dds_sync_unit_device* dev, uint32_t mask );
#endif
/*
* This work is part of the White Rabbit project
*
* Copyright (C) 2019 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdint.h>
#include <math.h>
#include "board.h"
#include "dev/gpio.h"
#include "dev/74x595.h"
#include "dev/ad7888.h"
#include "ertm15_rf_distr.h"
static const struct gpio_pin pin_lo_ctrl_ser = { &board.gpio_aux, 39 };
static const struct gpio_pin pin_lo_ctrl_updtclk = { &board.gpio_aux, 40 };
static const struct gpio_pin pin_lo_ctrl_shftclk = { &board.gpio_aux, 41 };
static const struct gpio_pin pin_ref_ctrl_ser = { &board.gpio_aux, 42 };
static const struct gpio_pin pin_ref_ctrl_updtclk = { &board.gpio_aux, 43 };
static const struct gpio_pin pin_ref_ctrl_shftclk = { &board.gpio_aux, 44 };
static struct gpio_device gpio_rfsw_ref;
static struct gpio_device gpio_rfsw_lo;
// mapping between x595 shift reg (IC26..28 on eRTM15 and the RF switch control pins)
static const struct pin_mapping
{
uint8_t path; // RF path (REF/LO)
uint8_t channel; // MTCA.4 channel
uint8_t ctrl1, ctrl2; // RF switch pin indices (CTRL1/CTRL2)
} rf_switch_sreg_pin_mapping[] = {
// REF outputs
{ERTM15_RF_REF, 4, 8 + 5, 8 + 4},
{ERTM15_RF_REF, 5, 8 + 6, 8 + 7},
{ERTM15_RF_REF, 6, 8 + 1, 8 + 0},
{ERTM15_RF_REF, 7, 8 + 3, 8 + 2},
{ERTM15_RF_REF, 8, 4, 5},
{ERTM15_RF_REF, 9, 6, 7},
{ERTM15_RF_REF, 10, 16 + 7, 16 + 6},
{ERTM15_RF_REF, 11, 2, 3},
{ERTM15_RF_REF, 12, 0, 1},
// LO outputs
{ERTM15_RF_LO, 4, 16 + 7, 16 + 6}, // ic28 7, 6
{ERTM15_RF_LO, 5, 0 + 6, 0 + 7}, // ic26 6, 7
{ERTM15_RF_LO, 6, 0 + 4, 0 + 5}, // ic26 4, 5
{ERTM15_RF_LO, 7, 0 + 0, 0 + 1}, // ic26 15, 1
{ERTM15_RF_LO, 8, 0 + 2, 0 + 3}, // ic26 2, 3
{ERTM15_RF_LO, 9, 8 + 6, 8 + 7}, // ic27 6, 7
{ERTM15_RF_LO, 10, 8 + 5, 8 + 4}, // ic27 5, 4
{ERTM15_RF_LO, 11, 8 + 1, 8 + 0}, // ic27 1, 15
{ERTM15_RF_LO, 12, 8 + 3, 8 + 2}, // ic27 3, 2
{ERTM15_RF_REF, 0, 0, 0}};
static const struct pin_mapping* find_pins_for_channel ( int path, int channel )
{
int i;
for(i=0; rf_switch_sreg_pin_mapping[i].channel !=0; i++ )
{
if( rf_switch_sreg_pin_mapping[i].channel == channel && rf_switch_sreg_pin_mapping[i].path == path )
{
return &rf_switch_sreg_pin_mapping[i];
}
}
return NULL;
}
static int rf_switch_set( int path, int channel, int state)
{
struct gpio_device *gpio = ( path == ERTM15_RF_REF ? &gpio_rfsw_ref : &gpio_rfsw_lo);
const struct pin_mapping *pins = find_pins_for_channel( path, channel );
struct gpio_pin ctrl1, ctrl2;
ctrl1.device = gpio;
ctrl1.pin = pins->ctrl1;
ctrl2.device = gpio;
ctrl2.pin = pins->ctrl2;
if(!pins)
return -1;
// HSWA2-30DR+ I/O CTRL pins function:
// CTRL1 = 0, CTRL2 = 0: OFF
// CTRL1 = 0, CTRL2 = 1: MONITOR
// CTRL1 = 1, CTRL2 = 0: ON
switch(state)
{
case ERTM15_RF_OUT_ON:
gen_gpio_out( &ctrl1, 1 );
gen_gpio_out( &ctrl2, 0 );
break;
case ERTM15_RF_OUT_MONITOR:
gen_gpio_out( &ctrl1, 0 );
gen_gpio_out( &ctrl2, 1 );
break;
case ERTM15_RF_OUT_OFF:
gen_gpio_out( &ctrl1, 0 );
gen_gpio_out( &ctrl2, 0 );
break;
}
return 0;
}
void ertm15_rf_distr_init( struct ertm15_rf_distribution_device *dev, struct ad7888_device *pwr_mon_adc )
{
x595_gpio_create ( &gpio_rfsw_ref, 3, &pin_ref_ctrl_updtclk, &pin_ref_ctrl_shftclk, NULL, &pin_ref_ctrl_ser );
x595_gpio_create ( &gpio_rfsw_lo, 3, &pin_lo_ctrl_updtclk, &pin_lo_ctrl_shftclk, NULL, &pin_lo_ctrl_ser );
//x595_test( &gpio_rfsw_ref );
dev->pwr_ref_valid = 0;
dev->pwr_lo_valid = 0;
dev->ref_enabled = 0;
dev->lo_enabled = 0;
dev->pwr_ref_valid = 0;
dev->pwr_mon_adc = pwr_mon_adc;
// disable all RF outputs to the backplane
ertm15_update_rf_switches( dev );
}
static int convert_power( int adc_value )
{
//pp_printf("ADCV %d\n", adc_value );
float adc_voltage = (float) adc_value / 4096.0 * 2.5;
float rf_power = 10.0 * log( adc_voltage / 2.0 ) / log( 10.0 ) + 15.0; // 2V = 0 dBm, compensate for 15 dB attenuator
return (int) (rf_power * 100.0);
}
#define ADC_CH_REF_DDS_PA 2
#define ADC_CH_LO_DDS_PA 0
#define ADC_CH_REF_DDS_DISTR 3
#define ADC_CH_LO_DDS_DISTR 1
int ertm15_rf_distr_measure_power ( struct ertm15_rf_distribution_device *dev )
{
ad7888_start_conversion( dev->pwr_mon_adc, 0x0f );
while( dev->pwr_mon_adc->channel_valid != 0x0f )
{
ad7888_poll( dev->pwr_mon_adc );
usleep(1000);
}
dev->pwr_ref_in = convert_power( dev->pwr_mon_adc->channel[ADC_CH_REF_DDS_PA] );
dev->pwr_lo_in = convert_power( dev->pwr_mon_adc->channel[ADC_CH_LO_DDS_PA] );
int i;
for( i = 4; i <= 12; i ++ )
{
dev->pwr_ref_valid &= ~(1<<i);
if( ! (dev->ref_enabled & (1<<i) ) )
{
rf_switch_set( ERTM15_RF_REF, i, ERTM15_RF_OUT_MONITOR );
usleep(10000);
int raw_pwr = ad7888_meas_channel( dev->pwr_mon_adc, ADC_CH_REF_DDS_DISTR );
dev->pwr_ref_ch[ i ] = convert_power( raw_pwr );
pp_printf("Ch REF %d: pwr %d v %d\n", i, dev->pwr_ref_ch[i], dev->pwr_mon_adc->channel_valid );
dev->pwr_ref_valid |= (1<<i);
rf_switch_set( ERTM15_RF_REF, i, ERTM15_RF_OUT_OFF );
}
}
return 0;
}
void ertm15_rf_distr_output_enable( struct ertm15_rf_distribution_device *dev, int path, int channel, int enabled )
{
uint16_t* mask = (path == ERTM15_RF_LO ? &dev->lo_enabled : &dev->ref_enabled );
if( enabled )
*mask |= ( 1 << channel );
else
*mask &= ~( 1 << channel );
}
void ertm15_update_rf_switches( struct ertm15_rf_distribution_device *dev )
{
int i;
for( i = 0; rf_switch_sreg_pin_mapping[i].channel != 0; i++ )
{
const struct pin_mapping* p = &rf_switch_sreg_pin_mapping[i];
int enabled = ( p->path == ERTM15_RF_LO ? dev->lo_enabled : dev->ref_enabled ) & (1 << p->channel );
pp_printf("rf_distr: switch %s ch %d -> %s\n", p->path == ERTM15_RF_LO ? "LO" : "REF", p->channel, enabled ? "ON" : "OFF" );
rf_switch_set( p->path, p->channel, enabled ? ERTM15_RF_OUT_ON : ERTM15_RF_OUT_OFF );
}
}
/*
* This work is part of the White Rabbit project
*
* Copyright (C) 2019 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __ERTM15_RF_DISTR_H
#define __ERTM15_RF_DISTR_H
#include <stdint.h>
#define ERTM15_RF_OUT_ON 0
#define ERTM15_RF_OUT_MONITOR 1
#define ERTM15_RF_OUT_OFF 2
#define ERTM15_RF_LO 0
#define ERTM15_RF_REF 1
struct ad7888_device;
struct ertm15_rf_distribution_device {
uint16_t pwr_lo_valid;
uint16_t lo_enabled;
uint16_t pwr_ref_valid;
uint16_t ref_enabled;
int pwr_lo_ch [ 16 ];
int pwr_ref_ch [ 16 ];
int pwr_lo_in;
int pwr_ref_in;
struct ad7888_device *pwr_mon_adc;
};
void ertm15_rf_distr_init( struct ertm15_rf_distribution_device *dev, struct ad7888_device *pwr_mon_adc );
int ertm15_rf_distr_measure_power ( struct ertm15_rf_distribution_device *dev );
void ertm15_rf_distr_output_enable( struct ertm15_rf_distribution_device *dev, int path, int channel, int enabled );
void ertm15_update_rf_switches( struct ertm15_rf_distribution_device *dev );
#endif
/*
Register definitions for slave core: 10 MHz Output Aligner Unit
* File : 10mhz_align_unit.h
* Author : auto-generated by wbgen2 from 10mhz_align_unit_wb.wb
* Created : Sun Sep 8 01:41:43 2019
* Standard : ANSI C
THIS FILE WAS GENERATED BY wbgen2 FROM SOURCE FILE 10mhz_align_unit_wb.wb
DO NOT HAND-EDIT UNLESS IT'S ABSOLUTELY NECESSARY!
*/
#ifndef __WBGEN2_REGDEFS_10MHZ_ALIGN_UNIT_WB_WB
#define __WBGEN2_REGDEFS_10MHZ_ALIGN_UNIT_WB_WB
#ifdef __KERNEL__
#include <linux/types.h>
#else
#include <inttypes.h>
#endif
#if defined( __GNUC__)
#define PACKED __attribute__ ((packed))
#else
#error "Unsupported compiler?"
#endif
#ifndef __WBGEN2_MACROS_DEFINED__
#define __WBGEN2_MACROS_DEFINED__
#define WBGEN2_GEN_MASK(offset, size) (((1<<(size))-1) << (offset))
#define WBGEN2_GEN_WRITE(value, offset, size) (((value) & ((1<<(size))-1)) << (offset))
#define WBGEN2_GEN_READ(reg, offset, size) (((reg) >> (offset)) & ((1<<(size))-1))
#define WBGEN2_SIGN_EXTEND(value, bits) (((value) & (1<<bits) ? ~((1<<(bits))-1): 0 ) | (value))
#endif
/* definitions for register: Control/Status Register */
/* definitions for field: Measurement trigger in reg: Control/Status Register */
#define TAU_CSR_TRIG WBGEN2_GEN_MASK(0, 1)
/* definitions for field: Measurement complete in reg: Control/Status Register */
#define TAU_CSR_DONE WBGEN2_GEN_MASK(1, 1)
/* definitions for field: Measured offset in reg: Control/Status Register */
#define TAU_CSR_OFFSET_MASK WBGEN2_GEN_MASK(2, 12)
#define TAU_CSR_OFFSET_SHIFT 2
#define TAU_CSR_OFFSET_W(value) WBGEN2_GEN_WRITE(value, 2, 12)
#define TAU_CSR_OFFSET_R(reg) WBGEN2_GEN_READ(reg, 2, 12)
/* definitions for register: IDELAY Control */
/* definitions for field: Load in reg: IDELAY Control */
#define TAU_IDELAY_CR_LOAD WBGEN2_GEN_MASK(0, 1)
/* definitions for field: Delay Taps in reg: IDELAY Control */
#define TAU_IDELAY_CR_TAPS_MASK WBGEN2_GEN_MASK(1, 5)
#define TAU_IDELAY_CR_TAPS_SHIFT 1
#define TAU_IDELAY_CR_TAPS_W(value) WBGEN2_GEN_WRITE(value, 1, 5)
#define TAU_IDELAY_CR_TAPS_R(reg) WBGEN2_GEN_READ(reg, 1, 5)
/* definitions for register: Dummy */
/* definitions for field: Load in reg: Dummy */
#define TAU_DUMMY_LOAD WBGEN2_GEN_MASK(0, 1)
/* [0x0]: REG Control/Status Register */
#define TAU_REG_CSR 0x00000000
/* [0x4]: REG IDELAY Control */
#define TAU_REG_IDELAY_CR 0x00000004
/* [0x8]: REG Dummy */
#define TAU_REG_DUMMY 0x00000008
#endif
/*
Register definitions for slave core: DDS/PLL Sync Pulse Generator Unit
* File : wb_dds_sync_unit.h
* Author : auto-generated by wbgen2 from dds_sync_unit_wb.wb
* Created : Sun Sep 22 19:43:55 2019
* Standard : ANSI C
THIS FILE WAS GENERATED BY wbgen2 FROM SOURCE FILE dds_sync_unit_wb.wb
DO NOT HAND-EDIT UNLESS IT'S ABSOLUTELY NECESSARY!
*/
#ifndef __WBGEN2_REGDEFS_DDS_SYNC_UNIT_WB_WB
#define __WBGEN2_REGDEFS_DDS_SYNC_UNIT_WB_WB
#ifdef __KERNEL__
#include <linux/types.h>
#else
#include <inttypes.h>
#endif
#if defined( __GNUC__)
#define PACKED __attribute__ ((packed))
#else
#error "Unsupported compiler?"
#endif
#ifndef __WBGEN2_MACROS_DEFINED__
#define __WBGEN2_MACROS_DEFINED__
#define WBGEN2_GEN_MASK(offset, size) (((1<<(size))-1) << (offset))
#define WBGEN2_GEN_WRITE(value, offset, size) (((value) & ((1<<(size))-1)) << (offset))
#define WBGEN2_GEN_READ(reg, offset, size) (((reg) >> (offset)) & ((1<<(size))-1))
#define WBGEN2_SIGN_EXTEND(value, bits) (((value) & (1<<bits) ? ~((1<<(bits))-1): 0 ) | (value))
#endif
/* definitions for register: Control/Status Register */
/* definitions for field: Trigger Sync Pulse 0 in reg: Control/Status Register */
#define DS_CSR_TRIG0 WBGEN2_GEN_MASK(0, 1)
/* definitions for field: Trigger Sync Pulse 1 in reg: Control/Status Register */
#define DS_CSR_TRIG1 WBGEN2_GEN_MASK(1, 1)
/* definitions for field: Trigger Sync Pulse 2 in reg: Control/Status Register */
#define DS_CSR_TRIG2 WBGEN2_GEN_MASK(2, 1)
/* definitions for field: Trigger Sync Pulse 3 in reg: Control/Status Register */
#define DS_CSR_TRIG3 WBGEN2_GEN_MASK(3, 1)
/* definitions for field: Trigger Sync Pulse 4 in reg: Control/Status Register */
#define DS_CSR_TRIG4 WBGEN2_GEN_MASK(4, 1)
/* definitions for field: Trigger Sync Pulse 5 in reg: Control/Status Register */
#define DS_CSR_TRIG5 WBGEN2_GEN_MASK(5, 1)
/* definitions for field: Trigger Sync Pulse 6 in reg: Control/Status Register */
#define DS_CSR_TRIG6 WBGEN2_GEN_MASK(6, 1)
/* definitions for field: Trigger Sync Pulse 7 in reg: Control/Status Register */
#define DS_CSR_TRIG7 WBGEN2_GEN_MASK(7, 1)
/* definitions for field: Immediately Force Sync Pulse 0 in reg: Control/Status Register */
#define DS_CSR_FORCE0 WBGEN2_GEN_MASK(8, 1)
/* definitions for field: Immediately Force Sync Pulse 1 in reg: Control/Status Register */
#define DS_CSR_FORCE1 WBGEN2_GEN_MASK(9, 1)
/* definitions for field: Immediately Force Sync Pulse 2 in reg: Control/Status Register */
#define DS_CSR_FORCE2 WBGEN2_GEN_MASK(10, 1)
/* definitions for field: Immediately Force Sync Pulse 3 in reg: Control/Status Register */
#define DS_CSR_FORCE3 WBGEN2_GEN_MASK(11, 1)
/* definitions for field: Immediately Force Sync Pulse 4 in reg: Control/Status Register */
#define DS_CSR_FORCE4 WBGEN2_GEN_MASK(12, 1)
/* definitions for field: Immediately Force Sync Pulse 5 in reg: Control/Status Register */
#define DS_CSR_FORCE5 WBGEN2_GEN_MASK(13, 1)
/* definitions for field: Sync Pulse Ready in reg: Control/Status Register */
#define DS_CSR_READY_MASK WBGEN2_GEN_MASK(14, 6)
#define DS_CSR_READY_SHIFT 14
#define DS_CSR_READY_W(value) WBGEN2_GEN_WRITE(value, 14, 6)
#define DS_CSR_READY_R(reg) WBGEN2_GEN_READ(reg, 14, 6)
/* definitions for field: PLL Reset in reg: Control/Status Register */
#define DS_CSR_PLL_RST WBGEN2_GEN_MASK(20, 1)
/* definitions for register: Output 0 Control */
/* definitions for field: WR PPS offset in reg: Output 0 Control */
#define DS_OCR0_PPS_OFFS_MASK WBGEN2_GEN_MASK(0, 4)
#define DS_OCR0_PPS_OFFS_SHIFT 0
#define DS_OCR0_PPS_OFFS_W(value) WBGEN2_GEN_WRITE(value, 0, 4)
#define DS_OCR0_PPS_OFFS_R(reg) WBGEN2_GEN_READ(reg, 0, 4)
/* definitions for field: Fine delay adjust in reg: Output 0 Control */
#define DS_OCR0_FINE_MASK WBGEN2_GEN_MASK(4, 5)
#define DS_OCR0_FINE_SHIFT 4
#define DS_OCR0_FINE_W(value) WBGEN2_GEN_WRITE(value, 4, 5)
#define DS_OCR0_FINE_R(reg) WBGEN2_GEN_READ(reg, 4, 5)
/* definitions for field: Polarity in reg: Output 0 Control */
#define DS_OCR0_POL WBGEN2_GEN_MASK(9, 1)
/* definitions for field: Serdes Bitmask in reg: Output 0 Control */
#define DS_OCR0_MASK_MASK WBGEN2_GEN_MASK(10, 8)
#define DS_OCR0_MASK_SHIFT 10
#define DS_OCR0_MASK_W(value) WBGEN2_GEN_WRITE(value, 10, 8)
#define DS_OCR0_MASK_R(reg) WBGEN2_GEN_READ(reg, 10, 8)
/* definitions for field: Continuous mode select in reg: Output 0 Control */
#define DS_OCR0_CONT WBGEN2_GEN_MASK(18, 1)
/* definitions for register: Output 1 Control */
/* definitions for field: WR PPS offset in reg: Output 1 Control */
#define DS_OCR1_PPS_OFFS_MASK WBGEN2_GEN_MASK(0, 4)
#define DS_OCR1_PPS_OFFS_SHIFT 0
#define DS_OCR1_PPS_OFFS_W(value) WBGEN2_GEN_WRITE(value, 0, 4)
#define DS_OCR1_PPS_OFFS_R(reg) WBGEN2_GEN_READ(reg, 0, 4)
/* definitions for field: Fine delay adjust in reg: Output 1 Control */
#define DS_OCR1_FINE_MASK WBGEN2_GEN_MASK(4, 5)
#define DS_OCR1_FINE_SHIFT 4
#define DS_OCR1_FINE_W(value) WBGEN2_GEN_WRITE(value, 4, 5)
#define DS_OCR1_FINE_R(reg) WBGEN2_GEN_READ(reg, 4, 5)
/* definitions for field: Polarity in reg: Output 1 Control */
#define DS_OCR1_POL WBGEN2_GEN_MASK(9, 1)
/* definitions for field: Serdes Bitmask in reg: Output 1 Control */
#define DS_OCR1_MASK_MASK WBGEN2_GEN_MASK(10, 8)
#define DS_OCR1_MASK_SHIFT 10
#define DS_OCR1_MASK_W(value) WBGEN2_GEN_WRITE(value, 10, 8)
#define DS_OCR1_MASK_R(reg) WBGEN2_GEN_READ(reg, 10, 8)
/* definitions for field: Continuous mode select in reg: Output 1 Control */
#define DS_OCR1_CONT WBGEN2_GEN_MASK(18, 1)
/* definitions for register: Output 2 Control */
/* definitions for field: WR PPS offset in reg: Output 2 Control */
#define DS_OCR2_PPS_OFFS_MASK WBGEN2_GEN_MASK(0, 4)
#define DS_OCR2_PPS_OFFS_SHIFT 0
#define DS_OCR2_PPS_OFFS_W(value) WBGEN2_GEN_WRITE(value, 0, 4)
#define DS_OCR2_PPS_OFFS_R(reg) WBGEN2_GEN_READ(reg, 0, 4)
/* definitions for field: Fine delay adjust in reg: Output 2 Control */
#define DS_OCR2_FINE_MASK WBGEN2_GEN_MASK(4, 5)
#define DS_OCR2_FINE_SHIFT 4
#define DS_OCR2_FINE_W(value) WBGEN2_GEN_WRITE(value, 4, 5)
#define DS_OCR2_FINE_R(reg) WBGEN2_GEN_READ(reg, 4, 5)
/* definitions for field: Polarity in reg: Output 2 Control */
#define DS_OCR2_POL WBGEN2_GEN_MASK(9, 1)
/* definitions for field: Serdes Bitmask in reg: Output 2 Control */
#define DS_OCR2_MASK_MASK WBGEN2_GEN_MASK(10, 8)
#define DS_OCR2_MASK_SHIFT 10
#define DS_OCR2_MASK_W(value) WBGEN2_GEN_WRITE(value, 10, 8)
#define DS_OCR2_MASK_R(reg) WBGEN2_GEN_READ(reg, 10, 8)
/* definitions for field: Continuous mode select in reg: Output 2 Control */
#define DS_OCR2_CONT WBGEN2_GEN_MASK(18, 1)
/* definitions for register: Output 3 Control */
/* definitions for field: WR PPS offset in reg: Output 3 Control */
#define DS_OCR3_PPS_OFFS_MASK WBGEN2_GEN_MASK(0, 4)
#define DS_OCR3_PPS_OFFS_SHIFT 0
#define DS_OCR3_PPS_OFFS_W(value) WBGEN2_GEN_WRITE(value, 0, 4)
#define DS_OCR3_PPS_OFFS_R(reg) WBGEN2_GEN_READ(reg, 0, 4)
/* definitions for field: Fine delay adjust in reg: Output 3 Control */
#define DS_OCR3_FINE_MASK WBGEN2_GEN_MASK(4, 5)
#define DS_OCR3_FINE_SHIFT 4
#define DS_OCR3_FINE_W(value) WBGEN2_GEN_WRITE(value, 4, 5)
#define DS_OCR3_FINE_R(reg) WBGEN2_GEN_READ(reg, 4, 5)
/* definitions for field: Polarity in reg: Output 3 Control */
#define DS_OCR3_POL WBGEN2_GEN_MASK(9, 1)
/* definitions for field: Serdes Bitmask in reg: Output 3 Control */
#define DS_OCR3_MASK_MASK WBGEN2_GEN_MASK(10, 8)
#define DS_OCR3_MASK_SHIFT 10
#define DS_OCR3_MASK_W(value) WBGEN2_GEN_WRITE(value, 10, 8)
#define DS_OCR3_MASK_R(reg) WBGEN2_GEN_READ(reg, 10, 8)
/* definitions for field: Continuous mode select in reg: Output 3 Control */
#define DS_OCR3_CONT WBGEN2_GEN_MASK(18, 1)
/* definitions for register: Output 4 Control */
/* definitions for field: WR PPS offset in reg: Output 4 Control */
#define DS_OCR4_PPS_OFFS_MASK WBGEN2_GEN_MASK(0, 4)
#define DS_OCR4_PPS_OFFS_SHIFT 0
#define DS_OCR4_PPS_OFFS_W(value) WBGEN2_GEN_WRITE(value, 0, 4)
#define DS_OCR4_PPS_OFFS_R(reg) WBGEN2_GEN_READ(reg, 0, 4)
/* definitions for field: Fine delay adjust in reg: Output 4 Control */
#define DS_OCR4_FINE_MASK WBGEN2_GEN_MASK(4, 5)
#define DS_OCR4_FINE_SHIFT 4
#define DS_OCR4_FINE_W(value) WBGEN2_GEN_WRITE(value, 4, 5)
#define DS_OCR4_FINE_R(reg) WBGEN2_GEN_READ(reg, 4, 5)
/* definitions for field: Polarity in reg: Output 4 Control */
#define DS_OCR4_POL WBGEN2_GEN_MASK(9, 1)
/* definitions for field: Serdes Bitmask in reg: Output 4 Control */
#define DS_OCR4_MASK_MASK WBGEN2_GEN_MASK(10, 8)
#define DS_OCR4_MASK_SHIFT 10
#define DS_OCR4_MASK_W(value) WBGEN2_GEN_WRITE(value, 10, 8)
#define DS_OCR4_MASK_R(reg) WBGEN2_GEN_READ(reg, 10, 8)
/* definitions for field: Continuous mode select in reg: Output 4 Control */
#define DS_OCR4_CONT WBGEN2_GEN_MASK(18, 1)
/* definitions for register: Output 5 Control */
/* definitions for field: WR PPS offset in reg: Output 5 Control */
#define DS_OCR5_PPS_OFFS_MASK WBGEN2_GEN_MASK(0, 4)
#define DS_OCR5_PPS_OFFS_SHIFT 0
#define DS_OCR5_PPS_OFFS_W(value) WBGEN2_GEN_WRITE(value, 0, 4)
#define DS_OCR5_PPS_OFFS_R(reg) WBGEN2_GEN_READ(reg, 0, 4)
/* definitions for field: Fine delay adjust in reg: Output 5 Control */
#define DS_OCR5_FINE_MASK WBGEN2_GEN_MASK(4, 5)
#define DS_OCR5_FINE_SHIFT 4
#define DS_OCR5_FINE_W(value) WBGEN2_GEN_WRITE(value, 4, 5)
#define DS_OCR5_FINE_R(reg) WBGEN2_GEN_READ(reg, 4, 5)
/* definitions for field: Polarity in reg: Output 5 Control */
#define DS_OCR5_POL WBGEN2_GEN_MASK(9, 1)
/* definitions for field: Serdes Bitmask in reg: Output 5 Control */
#define DS_OCR5_MASK_MASK WBGEN2_GEN_MASK(10, 8)
#define DS_OCR5_MASK_SHIFT 10
#define DS_OCR5_MASK_W(value) WBGEN2_GEN_WRITE(value, 10, 8)
#define DS_OCR5_MASK_R(reg) WBGEN2_GEN_READ(reg, 10, 8)
/* definitions for field: Continuous mode select in reg: Output 5 Control */
#define DS_OCR5_CONT WBGEN2_GEN_MASK(18, 1)
/* definitions for register: Output 6 Control */
/* definitions for field: WR PPS offset in reg: Output 6 Control */
#define DS_OCR6_PPS_OFFS_MASK WBGEN2_GEN_MASK(0, 4)
#define DS_OCR6_PPS_OFFS_SHIFT 0
#define DS_OCR6_PPS_OFFS_W(value) WBGEN2_GEN_WRITE(value, 0, 4)
#define DS_OCR6_PPS_OFFS_R(reg) WBGEN2_GEN_READ(reg, 0, 4)
/* definitions for field: Fine delay adjust in reg: Output 6 Control */
#define DS_OCR6_FINE_MASK WBGEN2_GEN_MASK(4, 5)
#define DS_OCR6_FINE_SHIFT 4
#define DS_OCR6_FINE_W(value) WBGEN2_GEN_WRITE(value, 4, 5)
#define DS_OCR6_FINE_R(reg) WBGEN2_GEN_READ(reg, 4, 5)
/* definitions for field: Polarity in reg: Output 6 Control */
#define DS_OCR6_POL WBGEN2_GEN_MASK(9, 1)
/* definitions for field: Serdes Bitmask in reg: Output 6 Control */
#define DS_OCR6_MASK_MASK WBGEN2_GEN_MASK(10, 8)
#define DS_OCR6_MASK_SHIFT 10
#define DS_OCR6_MASK_W(value) WBGEN2_GEN_WRITE(value, 10, 8)
#define DS_OCR6_MASK_R(reg) WBGEN2_GEN_READ(reg, 10, 8)
/* definitions for field: Continuous mode select in reg: Output 6 Control */
#define DS_OCR6_CONT WBGEN2_GEN_MASK(18, 1)
/* definitions for register: Output 7 Control */
/* definitions for field: WR PPS offset in reg: Output 7 Control */
#define DS_OCR7_PPS_OFFS_MASK WBGEN2_GEN_MASK(0, 4)
#define DS_OCR7_PPS_OFFS_SHIFT 0
#define DS_OCR7_PPS_OFFS_W(value) WBGEN2_GEN_WRITE(value, 0, 4)
#define DS_OCR7_PPS_OFFS_R(reg) WBGEN2_GEN_READ(reg, 0, 4)
/* definitions for field: Fine delay adjust in reg: Output 7 Control */
#define DS_OCR7_FINE_MASK WBGEN2_GEN_MASK(4, 5)
#define DS_OCR7_FINE_SHIFT 4
#define DS_OCR7_FINE_W(value) WBGEN2_GEN_WRITE(value, 4, 5)
#define DS_OCR7_FINE_R(reg) WBGEN2_GEN_READ(reg, 4, 5)
/* definitions for field: Polarity in reg: Output 7 Control */
#define DS_OCR7_POL WBGEN2_GEN_MASK(9, 1)
/* definitions for field: Serdes Bitmask in reg: Output 7 Control */
#define DS_OCR7_MASK_MASK WBGEN2_GEN_MASK(10, 8)
#define DS_OCR7_MASK_SHIFT 10
#define DS_OCR7_MASK_W(value) WBGEN2_GEN_WRITE(value, 10, 8)
#define DS_OCR7_MASK_R(reg) WBGEN2_GEN_READ(reg, 10, 8)
/* definitions for field: Continuous mode select in reg: Output 7 Control */
#define DS_OCR7_CONT WBGEN2_GEN_MASK(18, 1)
/* [0x0]: REG Control/Status Register */
#define DS_REG_CSR 0x00000000
/* [0x4]: REG Output 0 Control */
#define DS_REG_OCR0 0x00000004
/* [0x8]: REG Output 1 Control */
#define DS_REG_OCR1 0x00000008
/* [0xc]: REG Output 2 Control */
#define DS_REG_OCR2 0x0000000c
/* [0x10]: REG Output 3 Control */
#define DS_REG_OCR3 0x00000010
/* [0x14]: REG Output 4 Control */
#define DS_REG_OCR4 0x00000014
/* [0x18]: REG Output 5 Control */
#define DS_REG_OCR5 0x00000018
/* [0x1c]: REG Output 6 Control */
#define DS_REG_OCR6 0x0000001c
/* [0x20]: REG Output 7 Control */
#define DS_REG_OCR7 0x00000020
#endif
#include "board.h"
#include "dev/syscon.h"
#include "dev/endpoint.h"
#include <softpll_ng.h>
#include "storage.h"
#include "util.h"
#include <wrc-task.h>
#include <hw/endpoint_regs.h>
#include <hw/endpoint_mdio.h>
#include "dev/clock_monitor.h"
#ifdef ERTM14_CALIBRATION_DEBUG
#define cal_dbg(...) pp_printf("[cal-dbg]"__VA_ARGS__)
#else
#define cal_dbg(...)
#endif
#define DEFAULT_COMMA_POS 0
#define MDIO_DBG1_RESET_TX (1 << 0)
#define MDIO_DBG1_TX_ENABLE (1 << 1)
#define MDIO_DBG1_RX_ENABLE (1 << 2)
#define MDIO_DBG1_RESET_RX (1 << 3)
#define MDIO_DBG1_GTX_QPLL_RESET (1 << 4)
#define MDIO_DBG1_GTX_TXUSRPLL_RESET (1 << 5)
#define MDIO_DBG1_COMMA_TARGET_POS(x) ( ((x) & 0x7f) << 6)
#define MDIO_DBG1_DMTD_SOURCE_TXOUTCLK (1 << 14)
#define MDIO_DBG1_DMTD_SOURCE_RXRECCLK (0 << 14)
#define MDIO_DBG0_GTX_QPLL_LOCKED (1 << 0)
#define MDIO_DBG0_LINK_UP (1 << 1)
#define MDIO_DBG0_LINK_ALIGNED (1 << 2)
#define MDIO_DBG0_RESET_TX_DONE (1 << 3)
#define MDIO_DBG0_GTX_TXUSRPLL_LOCKED (1 << 4)
#define MDIO_DBG0_RESET_RX_DONE (1 << 5)
#define MDIO_DBG1 (19<<2)
#define MDIO_DBG0 (18<<2)
#define TX_SETUP_STATE_START 0
#define TX_SETUP_STATE_RESET_PCS 1
#define TX_SETUP_STATE_WAIT_LOCK 2
#define TX_SETUP_STATE_MEASURE_PHASE 3
#define TX_SETUP_DONE 4
#define TX_SETUP_VALIDATE 5
#define TX_SETUP_STATE_DISABLED 6
#define RX_SETUP_STATE_INIT 0
#define RX_SETUP_STATE_RESET_PCS 1
#define RX_SETUP_STATE_WAIT_LOCK 2
#define RX_SETUP_STATE_MEASURE_PHASE 3
#define RX_SETUP_DONE 4
#define RX_SETUP_VALIDATE 5
#define RX_SETUP_STATE_DISABLED 6
#define FSM_DEBUG_REFRESH_PERIOD_MS 1000
#define FSM_PHY_LOCK_TIMEOUT_MS 1000
#define FSM_SPLL_LOCK_TIMEOUT_MS 10000
#define FSM_DMTD_TIMEOUT_MS 100
#define FSM_EARLY_LINK_UP_TIMEOUT_MS 100
#define FSM_STABILIZE_TIMEOUT_MS 100
struct wrc_port_tx_setup_state
{
int state;
int cnt;
int attempts;
int cal_saved_phase;
int cal_saved_phase_valid;
int cal_file_updated;
int measured_phase;
int expected_phase;
int tollerance;
int update_cnt;
int expected_phase_valid;
timeout_t refresh_timeout;
timeout_t phy_lock_timeout;
timeout_t spll_lock_timeout;
timeout_t dmtd_timeout;
};
struct wrc_port_rx_setup_state
{
int cpos_stat[20];
int state;
int attempts;
int prev_link_up;
timeout_t link_timeout;
timeout_t stabilize_timeout;
};
static struct wrc_port_tx_setup_state tx_state;
static struct wrc_port_rx_setup_state rx_state;
static void tx_fsm_init(struct wrc_port_tx_setup_state *fsm)
{
fsm->attempts = 0;
fsm->state = TX_SETUP_STATE_START;
fsm->expected_phase = 0;
fsm->expected_phase_valid = 0;
fsm->tollerance = 300;
fsm->update_cnt = 0;
fsm->cal_saved_phase_valid = 0;
fsm->cal_saved_phase = 0;
fsm->cal_file_updated = 0;
fsm->cnt = 0;
if( !storage_get_calibration_parameter( CAL_PARAM_PHY_TARGET_TX_PHASE, &fsm->cal_saved_phase ) )
{
cal_dbg("read tx target phase :%d ps\n", fsm->cal_saved_phase);
fsm->cal_saved_phase_valid = 1;
}
tmo_init(&fsm->spll_lock_timeout, FSM_SPLL_LOCK_TIMEOUT_MS);
}
static int tx_fsm_update()
{
struct wrc_port_tx_setup_state *fsm = &tx_state;
//pp_printf("Tics %d st %d\n", timer_get_tics(), fsm->state );
switch (fsm->state)
{
case TX_SETUP_STATE_START:
{
if( tmo_expired( &fsm->spll_lock_timeout ) )
{
cal_dbg("Can't lock the SoftPLL. This is necessary for PHY calibratoin to continue. Retrying...\n");
tmo_restart( &fsm->spll_lock_timeout );
}
if( spll_check_lock( 0 ) )
{
spll_enable_ptracker(0, 0);
ep_pcs_write(MDIO_DBG1, MDIO_DBG1_RESET_RX | MDIO_DBG1_DMTD_SOURCE_TXOUTCLK);
fsm->state = TX_SETUP_STATE_RESET_PCS;
tmo_init( &fsm->refresh_timeout, FSM_DEBUG_REFRESH_PERIOD_MS );
}
break;
}
case TX_SETUP_STATE_RESET_PCS:
{
uint32_t dbg1 = MDIO_DBG1_RESET_TX | MDIO_DBG1_RESET_RX | MDIO_DBG1_DMTD_SOURCE_TXOUTCLK | MDIO_DBG1_GTX_QPLL_RESET | MDIO_DBG1_GTX_TXUSRPLL_RESET;
spll_enable_ptracker(0, 0);
// reset the QPLL
ep_pcs_write(MDIO_DBG1, dbg1 );
dbg1 &= ~MDIO_DBG1_GTX_QPLL_RESET;
ep_pcs_write(MDIO_DBG1, dbg1 );
// pp_printf("Qpll: ");
// wait for lock
while( !( ep_pcs_read(MDIO_DBG0) & MDIO_DBG0_GTX_QPLL_LOCKED ) )
usleep(1);
//pp_printf("QPLL OK\n");
// QPLL ok: un-reset TX path (+ UsrClk PLL)
dbg1 &= ~MDIO_DBG1_RESET_TX;
ep_pcs_write(MDIO_DBG1, dbg1 );
usleep(100);
dbg1 &= ~MDIO_DBG1_GTX_TXUSRPLL_RESET;
ep_pcs_write(MDIO_DBG1, dbg1 );
fsm->state = TX_SETUP_STATE_WAIT_LOCK;
tmo_init( &fsm->phy_lock_timeout, FSM_PHY_LOCK_TIMEOUT_MS );
break;
}
case TX_SETUP_STATE_WAIT_LOCK:
{
uint32_t dbg0 = ep_pcs_read(MDIO_DBG0);
if (dbg0 & MDIO_DBG0_RESET_TX_DONE)
{
fsm->attempts++;
fsm->state = TX_SETUP_STATE_MEASURE_PHASE;
usleep(10000);
spll_set_ptracker_average_samples( 0, 10 );
spll_enable_ptracker(0, 1);
tmo_init( &fsm->dmtd_timeout, FSM_DMTD_TIMEOUT_MS );
}
else if( tmo_expired(&fsm->phy_lock_timeout) )
{
fsm->state = TX_SETUP_STATE_RESET_PCS;
cal_dbg("PHY PLL lock timeout, retrying...[ dbg0 %04x]\n", dbg0 );
}
break;
}
case TX_SETUP_STATE_MEASURE_PHASE:
{
int phase, enabled; //, p2;
int rv = spll_read_ptracker(0, &phase, &enabled);
if (!rv)
{
if( tmo_expired( &fsm->dmtd_timeout ) )
{
cal_dbg("Phase measurement timeout, retrying...\n");
//for(;;)
// spll_show_stats();
fsm->state = TX_SETUP_STATE_RESET_PCS;
}
return 0;
}
// p2 = fsm->measured_phase = phase;
cal_dbg("samples %d last-phase %d\n", fsm->attempts, phase );
if(tmo_expired(&fsm->refresh_timeout))
{
// pp_printf("[tx-cal] samples %d last-phase %d\n", fsm->attempts, fsm->measured_phase);
tmo_restart(&fsm->refresh_timeout);
}
if (!fsm->expected_phase_valid)
{
if (fsm->cal_saved_phase_valid)
{
//pr_info("Using phase from file :%d\n",
// fsm->cal_saved_phase);
fsm->expected_phase = fsm->cal_saved_phase;
fsm->tollerance = 150; /*ps, bins are 200 ps wide*/
}
else // find a sane default
{
fsm->expected_phase = 10;
fsm->tollerance = 350; // fixme: this works for PHY oversampling at 5 Gbps (must be made generic at some time...)
}
fsm->expected_phase_valid = 1;
}
int phase_min = fsm->expected_phase - fsm->tollerance;
int phase_max = fsm->expected_phase + fsm->tollerance;
if (within_range(phase, phase_min, phase_max, 16000))
{
int i;
fsm->measured_phase = phase;
cal_dbg("FIX phase %d\n", fsm->measured_phase );
//spll_enable_ptracker(0, 0);
//spll_set_ptracker_average_samples( PTRACKER_AVERAGE_SAMPLES );
//spll_enable_ptracker(0, 1);
fsm->state = TX_SETUP_VALIDATE;
}
else
{
fsm->state = TX_SETUP_STATE_RESET_PCS;
}
break;
}
case TX_SETUP_VALIDATE:
{
int phase, enabled;
//int rv = spll_read_ptracker(0, &phase, &enabled);
//if (!rv)
// return 0;
//fsm->measured_phase = phase;
cal_dbg("TX calibration complete (phase %d ps)\n", fsm->measured_phase);
spll_enable_ptracker(0, 0);
// enable the PCS on the port
ep_pcs_write(MDIO_DBG1, MDIO_DBG1_TX_ENABLE | MDIO_DBG1_DMTD_SOURCE_RXRECCLK);
if( !fsm->cal_saved_phase_valid )
{
cal_dbg("saving new target phase: %d ps\n", fsm->measured_phase);
storage_set_calibration_parameter( CAL_PARAM_PHY_TARGET_TX_PHASE, fsm->measured_phase);
storage_save_calibration();
}
fsm->state = TX_SETUP_DONE;
break;
}
case TX_SETUP_DONE:
{
int early_link_up = ep_pcs_read(MDIO_DBG0) & MDIO_DBG0_LINK_UP;
return 1;
break;
}
}
return 0;
}
static void rx_fsm_init( )
{
struct wrc_port_rx_setup_state* fsm = &rx_state;
fsm->attempts = 0;
fsm->state = RX_SETUP_STATE_INIT;
fsm->prev_link_up = 0;
memset(fsm->cpos_stat, 0, sizeof(fsm->cpos_stat ));
}
static int rx_fsm_update( )
{
struct wrc_port_rx_setup_state* fsm = &rx_state;
struct wrc_port_tx_setup_state* fsm_tx = &tx_state;
int early_link_up = ep_pcs_read(MDIO_DBG0) & MDIO_DBG0_LINK_UP;
if( fsm_tx->state != TX_SETUP_DONE )
{
fsm->state = RX_SETUP_STATE_INIT;
return 0;
}
switch( fsm->state )
{
case RX_SETUP_STATE_INIT:
{
ep_pcs_write( MDIO_DBG1, MDIO_DBG1_TX_ENABLE | MDIO_DBG1_DMTD_SOURCE_RXRECCLK | MDIO_DBG1_COMMA_TARGET_POS(DEFAULT_COMMA_POS) );
if (early_link_up) {
if ( fsm_tx->state == TX_SETUP_DONE )
{
cal_dbg("RX calibration started.\n");
fsm->state = RX_SETUP_STATE_RESET_PCS;
}
}
fsm->attempts = 0;
break;
}
case RX_SETUP_STATE_RESET_PCS:
{
if (early_link_up)
{
fsm->state = RX_SETUP_STATE_WAIT_LOCK;
ep_pcs_write( MDIO_DBG1, MDIO_DBG1_RESET_RX | MDIO_DBG1_TX_ENABLE | MDIO_DBG1_DMTD_SOURCE_RXRECCLK | MDIO_DBG1_COMMA_TARGET_POS(DEFAULT_COMMA_POS) );
usleep(1);
ep_pcs_write( MDIO_DBG1, MDIO_DBG1_TX_ENABLE | MDIO_DBG1_DMTD_SOURCE_RXRECCLK | MDIO_DBG1_COMMA_TARGET_POS(DEFAULT_COMMA_POS) );
usleep(10000);
fsm->attempts++;
tmo_init(&fsm->link_timeout, FSM_EARLY_LINK_UP_TIMEOUT_MS);
}
break;
}
case RX_SETUP_STATE_WAIT_LOCK:
{
uint16_t dbg0 = ep_pcs_read( MDIO_DBG0);
int rx_up = dbg0 & MDIO_DBG0_LINK_UP;
int rx_aligned = dbg0 & MDIO_DBG0_LINK_ALIGNED;
int rx_comma_pos = (dbg0 >> 7) & 0x7f;
int rx_comma_valid = (dbg0 >> 7) & 0x80 ? 1 : 0;
if ( tmo_expired(&fsm->link_timeout) && !rx_up) {
fsm->state = RX_SETUP_STATE_INIT;
}
else
{
if ( !rx_up )
return 0;
// fsm->cpos_stat[rx_comma_pos]++;
if( rx_aligned )
{
# if 0
int i;
{
dbg0 = ep_pcs_read( MDIO_DBG0);
rx_up = dbg0 & MDIO_DBG0_LINK_UP;
rx_aligned = dbg0 & MDIO_DBG0_LINK_ALIGNED;
rx_comma_pos = (dbg0 >> 7) & 0x7f;
rx_comma_valid = (dbg0 >> 7) & 0x80 ? 1 : 0;
pp_printf("Dbg0 %x up %d algn %d cpos %d cvalid %d\n", dbg0, rx_up, rx_aligned, rx_comma_pos, rx_comma_valid );
usleep(100000);
}
usleep(100000);
#endif
fsm->state = RX_SETUP_VALIDATE;
tmo_init( &fsm->stabilize_timeout, FSM_STABILIZE_TIMEOUT_MS );
} else {
fsm->state = RX_SETUP_STATE_RESET_PCS;
}
}
break;
}
case RX_SETUP_VALIDATE:
{
if( !tmo_expired( &fsm->stabilize_timeout ))
return 0;
uint16_t dbg0 = ep_pcs_read( MDIO_DBG0);
int rx_up = dbg0 & MDIO_DBG0_LINK_UP;
int rx_aligned = dbg0 & MDIO_DBG0_LINK_ALIGNED;
int rx_comma_pos = (dbg0 >> 7) & 0x7f;
int rx_comma_valid = (dbg0 >> 7) & 0x80 ? 1 : 0;
if ( rx_up && rx_aligned && rx_comma_valid && (rx_comma_pos == DEFAULT_COMMA_POS) )
{
uint16_t dbg0 = ep_pcs_read( MDIO_DBG0);
int rx_comma_pos = (dbg0 >> 7) & 0x7f;
ep_pcs_write( MDIO_DBG1, MDIO_DBG1_RX_ENABLE | MDIO_DBG1_TX_ENABLE | MDIO_DBG1_DMTD_SOURCE_RXRECCLK | MDIO_DBG1_COMMA_TARGET_POS(DEFAULT_COMMA_POS) );
ep_pcs_write( MDIO_REG_MCR, MDIO_MCR_SPEED1000_MASK | MDIO_MCR_FULLDPLX_MASK | MDIO_MCR_ANENABLE | MDIO_MCR_ANRESTART );
cal_dbg("RX calibration complete (after %d attempts) comma @ %d taps.\n", fsm->attempts, rx_comma_pos );
spll_enable_ptracker( 0, 0 );
spll_set_ptracker_average_samples( 0, PTRACKER_AVERAGE_SAMPLES );
fsm->state = RX_SETUP_DONE;
} else {
cal_dbg("weird, can't stabilize link. Retrying [%d %d %d %d]\n", rx_up, rx_aligned, rx_comma_valid, rx_comma_pos );
fsm->state = RX_SETUP_STATE_RESET_PCS;
}
break;
}
case RX_SETUP_DONE:
{
uint16_t dbg0 = ep_pcs_read( MDIO_DBG0);
int link_up = ep_link_up(NULL);
if( ! (dbg0 & MDIO_DBG0_LINK_UP ) /*|| ( ( fsm->prev_link_up && !link_up ) )*/ )
{
cal_dbg("port went down, need RX recalibration.\n");
fsm->state = RX_SETUP_STATE_INIT;
fsm->prev_link_up = link_up;
return 0;
}
fsm->prev_link_up = link_up;
return 1;
break;
}
}
return 0;
}
int phy_calibration_poll()
{
tx_fsm_update();
rx_fsm_update();
return 0;
}
void phy_calibration_init()
{
cal_dbg("Initializing PHY calibrator...\n");
ep_pcs_write(MDIO_REG_MCR, MDIO_MCR_PDOWN); /* reset the PHY */
timer_delay_ms(200);
ep_pcs_write(MDIO_REG_MCR, MDIO_MCR_RESET); /* reset the PHY */
ep_pcs_write(MDIO_REG_MCR, 0); /* reset the PHY */
spll_init( SPLL_MODE_FREE_RUNNING_MASTER, 0, 0 );
spll_set_ptracker_average_samples( 0, 10 );
tx_fsm_init(&tx_state);
rx_fsm_init(&rx_state);
}
void phy_calibration_disable()
{
tx_state.state = TX_SETUP_STATE_DISABLED;
rx_state.state = RX_SETUP_STATE_DISABLED;
}
\ No newline at end of file
......@@ -23,6 +23,8 @@
# include "boards/wr-switch/board.h"
#elif defined(CONFIG_TARGET_AFCZ)
# include "boards/afcz/board.h"
#elif defined(CONFIG_TARGET_ERTM14)
# include "boards/ertm14/board.h"
#endif
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment