pps_gen.c 3.84 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
/*
 * 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.
 */
11
#include <wrc.h>
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
12
#include "board.h"
13
#include "pps_gen.h"
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
14

15
#include "hw/pps_gen_regs.h"
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
16

17
/* PPS Generator driver */
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
18

19 20
/* 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
21

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

25 26
#define ppsg_read(reg) \
	*(volatile uint32_t *) (BASE_PPS_GEN + (offsetof(struct PPSG_WB, reg)))
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
27

28
void shw_pps_gen_init()
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
29
{
30 31 32 33
	uint32_t cr;

	cr = PPSG_CR_CNT_EN | PPSG_CR_PWIDTH_W(PPS_WIDTH);

34
	ppsg_write(CR, cr);
35

36 37 38
	ppsg_write(ADJ_UTCLO, 0);
	ppsg_write(ADJ_UTCHI, 0);
	ppsg_write(ADJ_NSEC, 0);
39

40 41
	ppsg_write(CR, cr | PPSG_CR_CNT_SET);
	ppsg_write(CR, cr);
42
	ppsg_write(ESCR, 0);	/* disable PPS output */
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
43 44
}

45
/* Adjusts the nanosecond (refclk cycle) counter by atomically adding (how_much) cycles. */
46
int shw_pps_gen_adjust(int counter, int64_t how_much)
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
47
{
48
	wrc_verbose("Adjust: counter = %s [%c%d]\n",
49 50
		  counter == PPSG_ADJUST_SEC ? "seconds" : "nanoseconds",
		  how_much < 0 ? '-' : '+', (int32_t) abs(how_much));
51

52 53 54 55 56 57
	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));
58
	} else {
59 60
		ppsg_write(ADJ_UTCLO, (uint32_t) (how_much & 0xffffffffLL));
		ppsg_write(ADJ_UTCHI, (uint32_t) (how_much >> 32) & 0xff);
61 62 63
		ppsg_write(ADJ_NSEC, 0);
	}

64
	ppsg_write(CR, ppsg_read(CR) | PPSG_CR_CNT_ADJ);
65
	return 0;
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
66 67
}

68
/* Sets the current time */
69
void shw_pps_gen_set_time(uint64_t seconds, uint32_t nanoseconds, int counter)
70
{
71 72 73 74 75
	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));
76

77 78 79 80 81 82
	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);
83 84
}

85
static uint64_t pps_get_utc(void)
86 87 88
{
	uint64_t out;
	uint32_t low, high;
89

90
	low = ppsg_read(CNTR_UTCLO);
91
	high = ppsg_read(CNTR_UTCHI);
92

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

95
	out = (uint64_t) low | (uint64_t) high << 32;
96 97 98
	return out;
}

99 100
void __attribute__((weak)) shw_pps_gen_get_time(uint64_t * seconds,
						  uint32_t * nanoseconds)
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
101
{
102 103
	uint32_t ns_cnt;
	uint64_t sec1, sec2;
104

105
	do {
106
		sec1 = pps_get_utc();
107
		ns_cnt = ppsg_read(CNTR_NSEC) & 0xFFFFFFFUL;	/* 28-bit wide register */
108
		sec2 = pps_get_utc();
109 110 111 112 113
	} while (sec2 != sec1);

	if (seconds)
		*seconds = sec2;
	if (nanoseconds)
114
		*nanoseconds = ns_cnt * NS_PER_CLOCK;
Tomasz Wlostowski's avatar
Tomasz Wlostowski committed
115
}
116

117
/* Returns 1 when the adjustment operation is not yet finished */
118
int shw_pps_gen_busy()
119
{
120
	uint32_t cr = ppsg_read(CR);
121
	return cr & PPSG_CR_CNT_ADJ ? 0 : 1;
122 123
}

124
/* Enables/disables PPS output */
125
int shw_pps_gen_enable_output(int enable)
126
{
127 128 129 130 131 132 133
	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));
134

135
	return 0;
136
}
137 138 139 140 141 142 143 144 145 146 147 148

/* Masks/unmasks PPS output when link is down (useful in master mode) */
int shw_pps_gen_unmask_output(int unmask)
{
	uint32_t escr = ppsg_read(ESCR);
	if (unmask)
		ppsg_write(ESCR, escr | PPSG_ESCR_PPS_UNMASK);
	else
		ppsg_write(ESCR, escr & ~PPSG_ESCR_PPS_UNMASK);

	return 0;
}