pps_gen.c 3.08 KB
Newer Older
1
#include <wrc.h>
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
2
#include "board.h"
3
#include "pps_gen.h"
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
4

5
#include "hw/pps_gen_regs.h"
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
6

7
/* PPS Generator driver */
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
8

9 10
/* 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's avatar
Tomasz Wlostowski committed
11

12
/* Default width (in 8ns units) of the pulses on the PPS output */
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
13

14
#define PPS_WIDTH 100000
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
15

16 17
#define ppsg_write(reg, val) \
	*(volatile uint32_t *) (BASE_PPS_GEN + (offsetof(struct PPSG_WB, reg))) = (val)
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
18

19 20
#define ppsg_read(reg) \
	*(volatile uint32_t *) (BASE_PPS_GEN + (offsetof(struct PPSG_WB, reg)))
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
21

22
void pps_gen_init()
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
23
{
24 25 26 27
	uint32_t cr;

	cr = PPSG_CR_CNT_EN | PPSG_CR_PWIDTH_W(PPS_WIDTH);

28
	ppsg_write(CR, cr);
29

30 31 32
	ppsg_write(ADJ_UTCLO, 0);
	ppsg_write(ADJ_UTCHI, 0);
	ppsg_write(ADJ_NSEC, 0);
33

34 35 36
	ppsg_write(CR, cr | PPSG_CR_CNT_SET);
	ppsg_write(CR, cr);
	ppsg_write(ESCR, 0x6);	/* enable PPS output */
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
37 38
}

39 40
/* Adjusts the nanosecond (refclk cycle) counter by atomically adding (how_much) cycles. */
int pps_gen_adjust(int counter, int64_t how_much)
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
41
{
42 43 44
	TRACE_DEV("Adjust: counter = %s [%c%d]\n",
		  counter == PPSG_ADJUST_SEC ? "seconds" : "nanoseconds",
		  how_much < 0 ? '-' : '+', (int32_t) abs(how_much));
45

46 47 48 49 50 51
	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));
52
	} else {
53 54
		ppsg_write(ADJ_UTCLO, (uint32_t) (how_much & 0xffffffffLL));
		ppsg_write(ADJ_UTCHI, (uint32_t) (how_much >> 32) & 0xff);
55 56 57
		ppsg_write(ADJ_NSEC, 0);
	}

58
	ppsg_write(CR, ppsg_read(CR) | PPSG_CR_CNT_ADJ);
59
	return 0;
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
60 61
}

62
/* Sets the current time */
63
void pps_gen_set_time(uint64_t seconds, uint32_t nanoseconds)
64
{
65 66 67 68 69
	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));
70

71
	ppsg_write(CR, (ppsg_read(CR) & 0xfffffffb) | PPSG_CR_CNT_SET);
72 73
}

74 75 76 77
uint64_t pps_get_utc(void)
{
	uint64_t out;
	uint32_t low, high;
78

79
	low = ppsg_read(CNTR_UTCLO);
80
	high = ppsg_read(CNTR_UTCHI);
81

82
	high &= 0xFF;		/* CNTR_UTCHI has only 8 bits defined -- rest are HDL don't care */
83

84
	out = (uint64_t) low | (uint64_t) high << 32;
85 86 87
	return out;
}

88
void pps_gen_get_time(uint64_t * seconds, uint32_t * nanoseconds)
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
89
{
90 91
	uint32_t ns_cnt;
	uint64_t sec1, sec2;
92

93
	do {
94
		sec1 = pps_get_utc();
95
		ns_cnt = ppsg_read(CNTR_NSEC) & 0xFFFFFFFUL;	/* 28-bit wide register */
96
		sec2 = pps_get_utc();
97 98 99 100 101 102 103 104
	} 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's avatar
Tomasz Wlostowski committed
105
}
106

107 108
/* Returns 1 when the adjustment operation is not yet finished */
int pps_gen_busy()
109
{
110
	uint32_t cr = ppsg_read(CR);
111
	return cr & PPSG_CR_CNT_ADJ ? 0 : 1;
112 113
}

114 115 116
/* Enables/disables PPS output */
int pps_gen_enable_output(int enable)
{
117 118 119 120 121 122 123
	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));
124

125
	return 0;
126
}