Newer
Older
/*
* This work is part of the White Rabbit project
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Copyright (C) 2012 GSI (www.gsi.de)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* Author: Wesley W. Terpstra <w.terpstra@gsi.de>
*
* Released according to the GNU GPL, version 2 or any later version.
*/
Tomasz Wlostowski
committed
#include "pps_gen.h"
Tomasz Wlostowski
committed
#include "hw/pps_gen_regs.h"
Tomasz Wlostowski
committed
/* PPS Generator driver */
Tomasz Wlostowski
committed
/* Warning: references to "UTC" in the registers DO NOT MEAN actual UTC time, it's just a plain second counter
It doesn't care about leap seconds. */
Tomasz Wlostowski
committed
#define ppsg_write(reg, val) \
*(volatile uint32_t *) (BASE_PPS_GEN + (offsetof(struct PPSG_WB, reg))) = (val)
Tomasz Wlostowski
committed
#define ppsg_read(reg) \
*(volatile uint32_t *) (BASE_PPS_GEN + (offsetof(struct PPSG_WB, reg)))
Tomasz Wlostowski
committed
uint32_t cr;
cr = PPSG_CR_CNT_EN | PPSG_CR_PWIDTH_W(PPS_WIDTH);
Tomasz Wlostowski
committed
ppsg_write(ADJ_UTCLO, 0);
ppsg_write(ADJ_UTCHI, 0);
ppsg_write(ADJ_NSEC, 0);
Tomasz Wlostowski
committed
ppsg_write(CR, cr | PPSG_CR_CNT_SET);
ppsg_write(CR, cr);
ppsg_write(ESCR, 0); /* disable PPS output */
Tomasz Wlostowski
committed
/* Adjusts the nanosecond (refclk cycle) counter by atomically adding (how_much) cycles. */
int shw_pps_gen_adjust(int counter, int64_t how_much)
TRACE_DEV("Adjust: counter = %s [%c%d]\n",
counter == PPSG_ADJUST_SEC ? "seconds" : "nanoseconds",
how_much < 0 ? '-' : '+', (int32_t) abs(how_much));
Tomasz Wlostowski
committed
if (counter == PPSG_ADJUST_NSEC) {
ppsg_write(ADJ_UTCLO, 0);
ppsg_write(ADJ_UTCHI, 0);
ppsg_write(ADJ_NSEC,
(int32_t) ((int64_t) how_much * 1000LL /
(int64_t) REF_CLOCK_PERIOD_PS));
Tomasz Wlostowski
committed
} else {
ppsg_write(ADJ_UTCLO, (uint32_t) (how_much & 0xffffffffLL));
ppsg_write(ADJ_UTCHI, (uint32_t) (how_much >> 32) & 0xff);
Tomasz Wlostowski
committed
ppsg_write(ADJ_NSEC, 0);
}
ppsg_write(CR, ppsg_read(CR) | PPSG_CR_CNT_ADJ);
Tomasz Wlostowski
committed
return 0;
Tomasz Wlostowski
committed
/* Sets the current time */
void shw_pps_gen_set_time(uint64_t seconds, uint32_t nanoseconds, int counter)
ppsg_write(ADJ_UTCLO, (uint32_t) (seconds & 0xffffffffLL));
ppsg_write(ADJ_UTCHI, (uint32_t) (seconds >> 32) & 0xff);
ppsg_write(ADJ_NSEC,
(int32_t) ((int64_t) nanoseconds * 1000LL /
(int64_t) REF_CLOCK_PERIOD_PS));
if (counter == PPSG_SET_ALL)
ppsg_write(CR, (ppsg_read(CR) & 0xfffffffb) | PPSG_CR_CNT_SET);
else if (counter == PPSG_SET_SEC)
ppsg_write(ESCR, (ppsg_read(ESCR) & 0xffffffe7) | PPSG_ESCR_SEC_SET);
else if (counter == PPSG_SET_NSEC)
ppsg_write(ESCR, (ppsg_read(ESCR) & 0xffffffe7) | PPSG_ESCR_NSEC_SET);
{
uint64_t out;
uint32_t low, high;
low = ppsg_read(CNTR_UTCLO);
high = ppsg_read(CNTR_UTCHI);
high &= 0xFF; /* CNTR_UTCHI has only 8 bits defined -- rest are HDL don't care */
out = (uint64_t) low | (uint64_t) high << 32;
void shw_pps_gen_get_time(uint64_t * seconds, uint32_t * nanoseconds)
Tomasz Wlostowski
committed
uint32_t ns_cnt;
uint64_t sec1, sec2;
Tomasz Wlostowski
committed
do {
sec1 = pps_get_utc();
ns_cnt = ppsg_read(CNTR_NSEC) & 0xFFFFFFFUL; /* 28-bit wide register */
sec2 = pps_get_utc();
} while (sec2 != sec1);
if (seconds)
*seconds = sec2;
if (nanoseconds)
*nanoseconds =
(uint32_t) ((int64_t) ns_cnt *
(int64_t) REF_CLOCK_PERIOD_PS / 1000LL);
Tomasz Wlostowski
committed
/* Returns 1 when the adjustment operation is not yet finished */
Tomasz Wlostowski
committed
uint32_t cr = ppsg_read(CR);
return cr & PPSG_CR_CNT_ADJ ? 0 : 1;
Tomasz Wlostowski
committed
/* Enables/disables PPS output */
int shw_pps_gen_enable_output(int enable)
Tomasz Wlostowski
committed
{
uint32_t escr = ppsg_read(ESCR);
if (enable)
ppsg_write(ESCR,
escr | PPSG_ESCR_PPS_VALID | PPSG_ESCR_TM_VALID);
else
ppsg_write(ESCR,
escr & ~(PPSG_ESCR_PPS_VALID | PPSG_ESCR_TM_VALID));
Tomasz Wlostowski
committed