Commit f0953590 authored by Tomasz Wlostowski's avatar Tomasz Wlostowski Committed by Grzegorz Daniluk

softpll: rewritten external channel, we don't use bang-bang phase detector any more

parent 245d3efb
......@@ -153,7 +153,6 @@ int ad9516_set_output_divider(int output, int ratio, int phase_offset)
ad9516_write_reg(base + 1, div_ctl | (1<<7) | (phase_offset & 0xf));
} else {
uint8_t div_ctl = ad9516_read_reg(base + 1);
TRACE("DivCtl: %x\n", div_ctl);
ad9516_write_reg(base + 1, (div_ctl & (~(1<<7))) | (phase_offset & 0xf)); /* disable bypass bit */
ad9516_write_reg(base, (lcycles << 4) | hcycles);
}
......
This diff is collapsed.
......@@ -75,6 +75,21 @@ struct softpll_state {
struct spll_ptracker_state ptrackers[MAX_PTRACKERS];
};
static const struct stringlist_entry seq_states [] =
{
{ SEQ_START_EXT, "start-ext" },
{ SEQ_WAIT_EXT, "wait-ext" },
{ SEQ_START_HELPER, "start-helper" },
{ SEQ_WAIT_HELPER, "wait-helper" },
{ SEQ_START_MAIN, "start-main" },
{ SEQ_WAIT_MAIN, "wait-main" },
{ SEQ_DISABLED, "disabled" },
{ SEQ_READY, "ready" },
{ SEQ_CLEAR_DACS, "clear-dacs" },
{ SEQ_WAIT_CLEAR_DACS, "wait-clear-dacs" },
{ 0, NULL }
};
static volatile struct softpll_state softpll;
static volatile int ptracker_mask = 0;
......@@ -153,8 +168,10 @@ static inline void sequencing_fsm(struct softpll_state *s, int tag_value, int ta
/* State "Wait until we are locked to external 10MHz clock" */
case SEQ_WAIT_EXT:
{
if (external_locked(&s->ext))
s->seq_state = SEQ_START_HELPER;
if (external_locked(&s->ext)) {
start_ptrackers(s);
s->seq_state = SEQ_READY;
}
break;
}
......@@ -190,7 +207,7 @@ static inline void sequencing_fsm(struct softpll_state *s, int tag_value, int ta
case SEQ_WAIT_MAIN:
{
if (s->mpll.ld.locked)
if (s->mpll.ld.locked)
{
start_ptrackers(s);
s->seq_state = SEQ_READY;
......@@ -200,16 +217,13 @@ static inline void sequencing_fsm(struct softpll_state *s, int tag_value, int ta
case SEQ_READY:
{
if (!s->helper.ld.locked)
{
if (s->mode == SPLL_MODE_GRAND_MASTER && !external_locked(&s->ext)) {
s->delock_count++;
s->seq_state = SEQ_CLEAR_DACS;
} else if (s->mode == SPLL_MODE_GRAND_MASTER && !external_locked(&s->ext))
{
} else if (!s->helper.ld.locked) {
s->delock_count++;
s->seq_state = SEQ_START_EXT;
} else if (s->mode == SPLL_MODE_SLAVE && !s->mpll.ld.locked)
{
s->seq_state = SEQ_CLEAR_DACS;
} else if (s->mode == SPLL_MODE_SLAVE && !s->mpll.ld.locked) {
s->delock_count++;
s->seq_state = SEQ_CLEAR_DACS;
}
......@@ -220,47 +234,22 @@ static inline void sequencing_fsm(struct softpll_state *s, int tag_value, int ta
static inline void update_loops(struct softpll_state *s, int tag_value, int tag_source)
{
if(s->mode == SPLL_MODE_GRAND_MASTER) {
switch(s->seq_state) {
case SEQ_WAIT_EXT:
case SEQ_START_HELPER:
case SEQ_WAIT_HELPER:
case SEQ_START_MAIN:
case SEQ_WAIT_MAIN:
case SEQ_READY:
external_update(&s->ext, tag_value, tag_source);
break;
}
}
switch(s->seq_state) {
case SEQ_WAIT_HELPER:
case SEQ_START_MAIN:
case SEQ_WAIT_MAIN:
case SEQ_READY:
helper_update(&s->helper, tag_value, tag_source);
break;
}
if(s->seq_state == SEQ_WAIT_MAIN)
{
mpll_update(&s->mpll, tag_value, tag_source);
}
helper_update(&s->helper, tag_value, tag_source);
if(s->seq_state == SEQ_READY)
if(s->helper.ld.locked)
{
if(s->mode == SPLL_MODE_SLAVE)
{
int i;
mpll_update(&s->mpll, tag_value, tag_source);
mpll_update(&s->mpll, tag_value, tag_source);
for (i = 0; i < spll_n_chan_out - 1; i++)
mpll_update(&s->aux[i].pll.dmtd, tag_value, tag_source); // fixme: bb hooks here
if(s->seq_state == SEQ_READY) {
if(s->mode == SPLL_MODE_SLAVE) {
int i;
for (i = 0; i < spll_n_chan_out - 1; i++)
mpll_update(&s->aux[i].pll.dmtd, tag_value, tag_source);
}
update_ptrackers(s, tag_value, tag_source);
}
update_ptrackers(s, tag_value, tag_source);
}
}
......@@ -301,7 +290,6 @@ void spll_init(int mode, int slave_ref_channel, int align_pps)
spll_n_chan_ref = SPLL_CSR_N_REF_R(csr);
spll_n_chan_out = SPLL_CSR_N_OUT_R(csr);
s->mode = mode;
s->delock_count = 0;
......@@ -318,16 +306,6 @@ void spll_init(int mode, int slave_ref_channel, int align_pps)
PPSG->ESCR = 0;
PPSG->CR = PPSG_CR_CNT_EN | PPSG_CR_CNT_RST | PPSG_CR_PWIDTH_W(PPS_WIDTH);
if(mode == SPLL_MODE_GRAND_MASTER)
{
if(SPLL->ECCR & SPLL_ECCR_EXT_SUPPORTED)
external_init(&s->ext, spll_n_chan_ref + spll_n_chan_out, align_pps);
else {
TRACE_DEV("softpll: attempting to enable GM mode on non-GM hardware.\n");
return;
}
}
if(mode == SPLL_MODE_DISABLED)
s->seq_state = SEQ_DISABLED;
else
......@@ -354,6 +332,17 @@ void spll_init(int mode, int slave_ref_channel, int align_pps)
for (i = 0; i < spll_n_chan_ref; i++)
ptracker_init(&s->ptrackers[i], i, PTRACKER_AVERAGE_SAMPLES);
if(mode == SPLL_MODE_GRAND_MASTER) {
if(SPLL->ECCR & SPLL_ECCR_EXT_SUPPORTED) {
s->ext.helper = &s->helper;
s->ext.main = &s->mpll;
external_init(&s->ext, spll_n_chan_ref + spll_n_chan_out, align_pps);
} else {
TRACE_DEV("softpll: attempting to enable GM mode on non-GM hardware.\n");
return;
}
}
TRACE_DEV
("softpll: mode %s, %d ref channels, %d out channels\n",
modes[mode], spll_n_chan_ref, spll_n_chan_out);
......@@ -400,6 +389,11 @@ void spll_stop_channel(int channel)
mpll_stop(&s->aux[channel - 1].pll.dmtd);
}
int spll_ext_locked()
{
return external_locked( (struct spll_external_state *) &softpll.ext);
}
int spll_check_lock(int channel)
{
if (!channel)
......@@ -409,29 +403,6 @@ int spll_check_lock(int channel)
&& softpll.aux[channel - 1].pll.dmtd.ld.locked;
}
#ifdef CONFIG_PPSI /* use __div64_32 from ppsi library to save libgcc memory */
static int32_t from_picos(int32_t ps)
{
extern uint32_t __div64_32(uint64_t *n, uint32_t base);
uint64_t ups = ps;
if (ps >= 0) {
ups *= 1 << HPLL_N;
__div64_32(&ups, CLOCK_PERIOD_PICOSECONDS);
return ups;
}
ups = -ps * (1 << HPLL_N);
__div64_32(&ups, CLOCK_PERIOD_PICOSECONDS);
return -ups;
}
#else /* previous implementation: ptp-noposix has no __div64_32 available */
static int32_t from_picos(int32_t ps)
{
return (int32_t) ((int64_t) ps * (int64_t) (1 << HPLL_N) /
(int64_t) CLOCK_PERIOD_PICOSECONDS);
}
#endif
static int32_t to_picos(int32_t units)
{
return (int32_t) (((int64_t) units *
......@@ -443,8 +414,7 @@ static void set_phase_shift(int channel, int32_t value_picoseconds)
{
struct spll_main_state *st = (struct spll_main_state *)
(!channel ? &softpll.mpll : &softpll.aux[channel - 1].pll.dmtd);
int div = (DIVIDE_DMTD_CLOCKS_BY_2 ? 2 : 1);
mpll_set_phase_shift(st, from_picos(value_picoseconds) / div);
mpll_set_phase_shift(st, value_picoseconds);
softpll.mpll_shift_ps = value_picoseconds;
}
......@@ -502,15 +472,12 @@ void spll_get_num_channels(int *n_ref, int *n_out)
void spll_show_stats()
{
if (softpll.mode > 0)
TRACE_DEV
("softpll: irq_count %d sequencer_state %d mode %d "
"alignment_state %d HL%d EL%d ML%d HY=%d "
"MY=%d EY=%d DelCnt=%d extsc=%d\n",
irq_count, softpll.seq_state, softpll.mode,
softpll.ext.realign_state, softpll.helper.ld.locked,
softpll.ext.ld.locked, softpll.mpll.ld.locked,
softpll.helper.pi.y, softpll.mpll.pi.y, softpll.ext.pi.y,
softpll.delock_count, softpll.ext.sample_n);
TRACE_DEV("softpll: irqs %d seq %s mode %d "
"alignment_state %d HL%d ML%d HY=%d MY=%d DelCnt=%d\n",
irq_count, stringlist_lookup(seq_states, softpll.seq_state), softpll.mode,
softpll.ext.align_state, softpll.helper.ld.locked, softpll.mpll.ld.locked,
softpll.helper.pi.y, softpll.mpll.pi.y,
softpll.delock_count);
}
int spll_shifter_busy(int channel)
......@@ -666,3 +633,33 @@ void spll_set_dac(int index, int value)
softpll.aux[index - 1].pll.dmtd.pi.y = value;
}
}
void spll_update()
{
switch(softpll.mode) {
case SPLL_MODE_GRAND_MASTER:
external_align_fsm(&softpll.ext);
break;
}
spll_update_aux_clocks();
}
int spll_measure_frequency(int osc)
{
volatile uint32_t *reg;
switch(osc) {
case SPLL_OSC_REF:
reg = &SPLL->F_REF;
break;
case SPLL_OSC_DMTD:
reg = &SPLL->F_DMTD;
break;
case SPLL_OSC_EXT:
reg = &SPLL->F_EXT;
break;
}
timer_delay_ms(2000);
return (*reg ) & (0xfffffff);
}
......@@ -39,6 +39,10 @@
#define SPLL_PD_DDMTD 0
#define SPLL_PD_BANGBANG 1
/* Channels for spll_measure_frequency() */
#define SPLL_OSC_REF 0
#define SPLL_OSC_DMTD 1
#define SPLL_OSC_EXT 2
/* Note on channel naming:
- ref_channel means a PHY recovered clock input. There can be one (as in WR core) or more (WR switch).
......@@ -101,9 +105,10 @@ void spll_enable_ptracker(int ref_channel, int enable);
/* Reads tracked phase shift value for given reference channel */
int spll_read_ptracker(int ref_channel, int32_t *phase_ps, int *enabled);
/* Calls aux clock handling state machine. Must be called regularly (although it is not time-critical)
in the main loop of the program if aux clocks are used in the design. */
int spll_update_aux_clocks();
/* Calls non-realtime update state machine. Must be called regularly (although
* it is not time-critical) in the main loop of the program if aux clocks or
* external reference are used in the design. */
void spll_update();
/* Returns the status of given aux clock output (SPLL_AUX_) */
int spll_get_aux_status(int out_channel);
......@@ -122,5 +127,7 @@ void spll_set_dac(int out_channel, int value);
/* Returns current DAC sample value for output (out_channel) */
int spll_get_dac(int out_channel);
int spll_measure_frequency(int osc);
#endif // __SOFTPLL_NG_H
......@@ -125,6 +125,7 @@ Channels (spll_n_chan_ref ... spll_n_chan_out + spll_n_chan_ref-1) are the outpu
void spll_enable_tagger(int channel, int enable)
{
TRACE("EnableTagger %d %d\n", channel, enable);
if (channel >= spll_n_chan_ref) { /* Output channel? */
if (enable)
SPLL->OCER |= 1 << (channel - spll_n_chan_ref);
......@@ -137,7 +138,7 @@ void spll_enable_tagger(int channel, int enable)
SPLL->RCER &= ~(1 << channel);
}
// TRACE("%s: ch %d, OCER 0x%x, RCER 0x%x\n", __FUNCTION__, channel, SPLL->OCER, SPLL->RCER);
TRACE("%s: ch %d, OCER 0x%x, RCER 0x%x\n", __FUNCTION__, channel, SPLL->OCER, SPLL->RCER);
}
void biquad_init(spll_biquad_t *bq, const int *coefs, int shift)
......@@ -167,3 +168,13 @@ int biquad_update(spll_biquad_t *bq, int x)
return y;
}
const char *stringlist_lookup(const struct stringlist_entry *slist, int id)
{
int i;
for(i=0; slist[i].str; i++) {
if(slist[i].id == id)
return slist[i].str;
}
return "<unknown>";
}
......@@ -66,6 +66,21 @@ typedef struct {
int yd[2], xd[2]; /* I/O delay lines */
} spll_biquad_t;
/* long-term-average filter */
typedef struct {
int acc;
int n;
int window;
int pos;
int size;
int log[16];
} spll_lta_t;
struct stringlist_entry {
int id;
const char *str;
};
/* initializes the PI controller state. Currently almost a stub. */
void pi_init(spll_pi_t *pi);
......@@ -83,4 +98,6 @@ void spll_enable_tagger(int channel, int enable);
void biquad_init(spll_biquad_t *bq, const int *coefs, int shift);
int biquad_update(spll_biquad_t *bq, int x);
const char *stringlist_lookup(const struct stringlist_entry *slist, int id);
#endif // __SPLL_COMMON_H
......@@ -13,152 +13,171 @@
#include "spll_external.h"
#include "spll_debug.h"
#define BB_ERROR_BITS 16
#include "trace.h"
#include "irq.h"
void external_init(volatile struct spll_external_state *s, int ext_ref,
int realign_clocks)
{
#define ALIGN_STATE_EXT_OFF 0
#define ALIGN_STATE_START 1
#define ALIGN_STATE_INIT_CSYNC 2
#define ALIGN_STATE_WAIT_CSYNC 3
#define ALIGN_STATE_START_ALIGNMENT 7
#define ALIGN_STATE_WAIT_SAMPLE 4
#define ALIGN_STATE_COMPENSATE_DELAY 5
#define ALIGN_STATE_LOCKED 6
#define ALIGN_STATE_START_MAIN 8
s->pi.y_min = 5;
s->pi.y_max = (1 << DAC_BITS) - 5;
s->pi.kp = (int)(300);
s->pi.ki = (int)(1);
s->pi.anti_windup = 1;
s->pi.bias = 32768;
/* Phase branch lock detection */
s->ld.threshold = 400;
s->ld.lock_samples = 10000;
s->ld.delock_samples = 9990;
s->ref_src = ext_ref;
s->ph_err_cur = 0;
s->ph_err_d0 = 0;
s->ph_raw_d0 = 0;
s->realign_clocks = realign_clocks;
s->realign_state = (realign_clocks ? REALIGN_STAGE1 : REALIGN_DISABLED);
pi_init((spll_pi_t *)&s->pi);
ld_init((spll_lock_det_t *)&s->ld);
lowpass_init((spll_lowpass_t *)&s->lp_short, 4000);
lowpass_init((spll_lowpass_t *)&s->lp_long, 300);
}
#define ALIGN_SAMPLE_PERIOD 100000
#define ALIGN_TARGET 0
static inline void realign_fsm(struct spll_external_state *s)
{
switch (s->realign_state) {
case REALIGN_STAGE1:
SPLL->ECCR |= SPLL_ECCR_ALIGN_EN;
#define EXT_PERIOD_NS 100
#define EXT_FREQ_HZ 10000000
#define EXT_PPS_LATENCY_PS 30000 // fixme: make configurable
s->realign_state = REALIGN_STAGE1_WAIT;
s->realign_timer = timer_get_tics() + 2 * TICS_PER_SECOND;
break;
case REALIGN_STAGE1_WAIT:
void external_init(volatile struct spll_external_state *s, int ext_ref,
int realign_clocks)
{
int idx = spll_n_chan_ref + spll_n_chan_out;
if (SPLL->ECCR & SPLL_ECCR_ALIGN_DONE)
s->realign_state = REALIGN_STAGE2;
else if (time_after(timer_get_tics(), s->realign_timer)) {
SPLL->ECCR &= ~SPLL_ECCR_ALIGN_EN;
s->realign_state = REALIGN_PPS_INVALID;
}
break;
case REALIGN_STAGE2:
if (s->ld.locked) {
PPSG->CR = PPSG_CR_CNT_RST | PPSG_CR_CNT_EN;
PPSG->ADJ_UTCLO = 0;
PPSG->ADJ_UTCHI = 0;
PPSG->ADJ_NSEC = 0;
PPSG->ESCR = PPSG_ESCR_SYNC;
s->realign_state = REALIGN_STAGE2_WAIT;
s->realign_timer = timer_get_tics()
+ 2 * TICS_PER_SECOND;
}
break;
case REALIGN_STAGE2_WAIT:
if (PPSG->ESCR & PPSG_ESCR_SYNC) {
PPSG->ESCR = PPSG_ESCR_PPS_VALID | PPSG_ESCR_TM_VALID;
s->realign_state = REALIGN_DONE;
} else if (time_after(timer_get_tics(), s->realign_timer)) {
PPSG->ESCR = 0;
s->realign_state = REALIGN_PPS_INVALID;
}
break;
helper_init(s->helper, idx);
mpll_init(s->main, idx, spll_n_chan_ref);
case REALIGN_PPS_INVALID:
case REALIGN_DISABLED:
case REALIGN_DONE:
return;
}
s->align_state = ALIGN_STATE_EXT_OFF;
s->enabled = 0;
}
int external_update(struct spll_external_state *s, int tag, int source)
void external_start(struct spll_external_state *s)
{
int err, y, y2, ylt;
if (source == s->ref_src) {
int wrap = tag & (1 << BB_ERROR_BITS) ? 1 : 0;
realign_fsm(s);
tag &= ((1 << BB_ERROR_BITS) - 1);
// mprintf("err %d\n", tag);
if (wrap) {
if (tag > s->ph_raw_d0)
s->ph_err_offset -= (1 << BB_ERROR_BITS);
else if (tag <= s->ph_raw_d0)
s->ph_err_offset += (1 << BB_ERROR_BITS);
}
s->ph_raw_d0 = tag;
helper_start(s->helper);
err = (tag + s->ph_err_offset) - s->ph_err_d0;
s->ph_err_d0 = (tag + s->ph_err_offset);
y = pi_update(&s->pi, err);
y2 = lowpass_update(&s->lp_short, y);
ylt = lowpass_update(&s->lp_long, y);
if (!(SPLL->ECCR & SPLL_ECCR_EXT_REF_PRESENT)) {
/* no reference? de-lock now */
ld_init(&s->ld);
y2 = 32000;
}
SPLL->DAC_MAIN = y2 & 0xffff;
SPLL->ECCR = SPLL_ECCR_EXT_EN;
spll_debug(DBG_ERR | DBG_EXT, ylt, 0);
spll_debug(DBG_SAMPLE_ID | DBG_EXT, s->sample_n++, 0);
spll_debug(DBG_Y | DBG_EXT, y2, 1);
s->align_state = ALIGN_STATE_START;
s->enabled = 1;
spll_debug (DBG_EVENT | DBG_EXT, DBG_EVT_START, 1);
}
if (ld_update(&s->ld, y2 - ylt))
return SPLL_LOCKED;
int external_locked(struct spll_external_state *s)
{
if (!s->helper->ld.locked || !s->main->ld.locked)
return 0;
switch(s->align_state) {
case ALIGN_STATE_EXT_OFF:
case ALIGN_STATE_START:
case ALIGN_STATE_START_MAIN:
case ALIGN_STATE_INIT_CSYNC:
case ALIGN_STATE_WAIT_CSYNC:
return 0;
default:
return 1;
}
return SPLL_LOCKING;
}
void external_start(struct spll_external_state *s)
static int align_sample(int channel, int *v)
{
SPLL->ECCR = 0;
s->sample_n = 0;
s->realign_state =
(s->realign_clocks ? REALIGN_STAGE1 : REALIGN_DISABLED);
SPLL->ECCR = SPLL_ECCR_EXT_EN;
spll_debug(DBG_EVENT | DBG_EXT, DBG_EVT_START, 1);
int mask = (1 << channel);
if(SPLL->AL_CR & mask) {
SPLL->AL_CR = mask; // ack
int ci = SPLL->AL_CIN;
if(ci > 100 && ci < (EXT_FREQ_HZ - 100) ) { // give some metastability margin, when the counter transitions from EXT_FREQ_HZ-1 to 0
*v = ci * EXT_PERIOD_NS;
return 1;
}
}
return 0; // sample not valid
}
int external_locked(struct spll_external_state *s)
void external_align_fsm( struct spll_external_state *s )
{
return (s->ld.locked
&& (s->realign_clocks ? s->realign_state == REALIGN_DONE : 1));
int v;
switch(s->align_state) {
case ALIGN_STATE_EXT_OFF:
break;
case ALIGN_STATE_START:
if(s->helper->ld.locked) {
disable_irq();
mpll_start(s->main);
enable_irq();
s->align_state = ALIGN_STATE_START_MAIN;
}
break;
case ALIGN_STATE_START_MAIN:
SPLL->AL_CR = 2;
if(s->helper->ld.locked && s->main->ld.locked) {
PPSG->CR = PPSG_CR_CNT_EN | PPSG_CR_PWIDTH_W(10);
PPSG->ADJ_NSEC = 3;
PPSG->ESCR = PPSG_ESCR_SYNC;
s->align_state = ALIGN_STATE_INIT_CSYNC;
TRACE_DEV("EXT: DMTD locked.\n");
}
break;
case ALIGN_STATE_INIT_CSYNC:
if (PPSG->ESCR & PPSG_ESCR_SYNC) {
PPSG->ESCR = PPSG_ESCR_PPS_VALID; // enable PPS output (even though it's not aligned yet)
s->align_timer = timer_get_tics() + 2 * TICS_PER_SECOND;
s->align_state = ALIGN_STATE_WAIT_CSYNC;
}
break;
case ALIGN_STATE_WAIT_CSYNC:
if(timer_get_tics() >= s->align_timer) {
s->align_state = ALIGN_STATE_START_ALIGNMENT;
s->align_shift = 0;
TRACE_DEV("EXT: CSync complete.\n");
}
break;
case ALIGN_STATE_START_ALIGNMENT:
if(align_sample(1, &v)) {
v %= ALIGN_SAMPLE_PERIOD;
if(v == 0 || v >= ALIGN_SAMPLE_PERIOD / 2) {
s->align_target = EXT_PERIOD_NS;
s->align_step = -100;
} else if (s > 0) {
s->align_target = 0;
s->align_step = 100;
}
TRACE_DEV("EXT: Align target %d, step %d.\n", s->align_target, s->align_step);
s->align_state = ALIGN_STATE_WAIT_SAMPLE;
}
break;
case ALIGN_STATE_WAIT_SAMPLE:
if(!mpll_shifter_busy(s->main) && align_sample(1, &v)) {
v %= ALIGN_SAMPLE_PERIOD;
if(v != s->align_target) {
s->align_shift += s->align_step;
mpll_set_phase_shift(s->main, s->align_shift);
} else if (v == s->align_target) {
s->align_shift += EXT_PPS_LATENCY_PS;
mpll_set_phase_shift(s->main, s->align_shift);
s->align_state = ALIGN_STATE_COMPENSATE_DELAY;
}
}
break;
case ALIGN_STATE_COMPENSATE_DELAY:
if(!mpll_shifter_busy(s->main)) {
TRACE_DEV("EXT: Align done.\n");
s->align_state = ALIGN_STATE_LOCKED;
}
break;
case ALIGN_STATE_LOCKED:
if(!external_locked(s)) {
s->align_state = ALIGN_STATE_START;
}
break;
default:
break;
}
}
......@@ -14,69 +14,29 @@
#define __SPLL_EXTERNAL_H
#include "spll_common.h"
#include "spll_helper.h"
#include "spll_main.h"
/* Alignment FSM states */
/* 1st alignment stage, done before starting the ext channel PLL:
alignment of the rising edge of the external clock (10 MHz), with
the rising edge of the local reference (62.5/125 MHz) and the PPS
signal. Because of non-integer ratio (6.25 or 12.5), the PLL must
know which edges shall be kept at phase==0. We align to the edge of
the 10 MHz clock which comes right after the edge of the PPS pulse
(see drawing below):
PLL reference (62.5 MHz) ____|^^^^|____|^^^^|____|^^^^|____|^^^^|____|^^^^|____|^^^^|____|^^^^|____|^^^^|____|^^^^|____|^^^^|____|^^^^|____|
External clock (10 MHz) ^^^^^^^^^|________________________|^^^^^^^^^^^^^^^^^^^^^^^^^|________________________|^^^^^^^^^^^^^^^^^^^^^^^^^|___
External PPS ___________|^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
*/
#define REALIGN_STAGE1 1
#define REALIGN_STAGE1_WAIT 2
/* 2nd alignment stage, done after the ext channel PLL has locked. We
make sure that the switch's internal PPS signal is produced exactly
on the edge of PLL reference in-phase with 10 MHz clock edge, which
has come right after the PPS input
PLL reference (62.5 MHz) ____|^^^^|____|^^^^|____|^^^^|____|^^^^|____|^^^^|____|^^^^|____|^^^^|____|^^^^|____|^^^^|____|^^^^|____|^^^^|____|
External clock (10 MHz) ^^^^^^^^^|________________________|^^^^^^^^^^^^^^^^^^^^^^^^^|________________________|^^^^^^^^^^^^^^^^^^^^^^^^^|___
External PPS ___________|^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Internal PPS __________________________________|^^^^^^^^^|______________________________________________________________________
^ aligned clock edges and PPS
*/
#define REALIGN_STAGE2 3
#define REALIGN_STAGE2_WAIT 4
/* Error state - PPS signal missing or of bad frequency */
#define REALIGN_PPS_INVALID 5
/* Realignment is disabled (i.e. the switch inputs only the reference
* frequency, but not time) */
#define REALIGN_DISABLED 6
struct spll_external_state {
struct spll_helper_state *helper;
struct spll_main_state *main;
/* Realignment done */
#define REALIGN_DONE 7
int enabled;
struct spll_external_state {
int ref_src;
int sample_n;