Commit 72c57404 authored by Alessandro Rubini's avatar Alessandro Rubini

userspace/tools/wr_date: initial version (no ntp)

Signed-off-by: Alessandro Rubini's avatarAlessandro Rubini <rubini@gnudd.com>
parent 5e799398
......@@ -8,4 +8,4 @@ com
mapper
wmapper
shw_ver
wr_date
\ No newline at end of file
TOOLS = rtu_stat wr_mon wr_phytool spll_dbg_proxy load-lm32 load-virtex com
TOOLS += mapper wmapper shw_ver
TOOLS += mapper wmapper shw_ver wr_date
# # Standard stanza for cross-compilation (courtesy of the linux makefile)
......@@ -53,10 +53,10 @@ load-virtex: load-virtex.o load-fpga.o
load-lm32: load-lm32.o lm32-loader.o
${CC} -o $@ $^ $(LDFLAGS)
shw_ver.o: shw_ver.c
${CC} ${CFLAGS} -D__GIT_USR__="\"${GIT_USR}\"" -D__GIT_VER__="\"${GIT_VER}\"" $(LDFLAGS) -c -o $@ $^
shw_ver: shw_ver.o
${CC} -o $@ $^ $(LDFLAGS)
......
/*
* Trivial tool to set WR date in the switch from local or NTP time
*
* Alessandro Rubini, 2011, for CERN, 2013. GPL2 or later
*/
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <fcntl.h>
#include <time.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include "../../kernel/wbgen-regs/ppsg-regs.h"
#define WRDATE_CFG_FILE "/wr/etc/wrdate.conf"
/* Address for hardware, from nic-hardware.h */
#define FPGA_BASE_PPSG 0x10010500
void help(char *prgname)
{
fprintf(stderr, "%s: Use: \"%s [<options>] <cmd> [<args>]\n",
prgname, prgname);
fprintf(stderr,
" The program usss %s as default config file name\n"
" -f force: run even if not on a WR switch\n"
" -c <cfg> configfile to use in place of the default\n"
" -v verbose: report what the program does\n"
" -n do not act in practice\n"
" get print WR time to stdout\n"
" set <value> set WR time to scalar seconds\n"
" set host set from current host time\n"
" set ntp set from ntp (sets host time too)\n"
" set ntp:<ip> set from specified ntp server\n",
WRDATE_CFG_FILE);
exit(1);
}
int opt_verbose, opt_force, opt_not;
char *opt_cfgfile = WRDATE_CFG_FILE;
char *prgname;
/* Check that we actualy are on the wr switch, exit if not */
int wrdate_check_host(int fdmem)
{
int ret;
/*
* Check the specific CPU: a different switch will require a
* different memory address so this must be match.
* system(3) is bad, but it's fast to code
*/
ret = system("grep -q ARM926EJ-S /proc/cpuinfo");
if (opt_force && ret) {
fprintf(stderr, "%s: not running on the WR switch\n", prgname);
if (!opt_not)
exit(1);
}
return ret ? -1 : 0;
}
int wrdate_cfgfile(char *fname)
{
/* FIXME: parse config file */
return 0;
}
/* create a map, that is never goind to be released */
void *create_map(int fdmem, unsigned long address, unsigned long size)
{
unsigned long ps = getpagesize();
unsigned long offset, fragment, len;
void *mapaddr;
offset = address & ~(ps -1);
fragment = address & (ps -1);
len = address + size - offset;
mapaddr = mmap(0, len, PROT_READ | PROT_WRITE,
MAP_SHARED, fdmem, offset);
if (mapaddr == MAP_FAILED) {
fprintf(stderr, "%s: mmap: %s\n", prgname, strerror(errno));
exit(1);
}
return mapaddr + fragment;
}
int wrdate_get(struct PPSG_WB *pps)
{
unsigned long utch, utcl, nsec, tmp1, tmp2;
uint64_t utc;
time_t t;
struct timeval tv;
struct tm tm;
char s[64];
if (opt_not) {
gettimeofday(&tv, NULL);
utch = 0;
utcl = tv.tv_sec;
nsec = tv.tv_usec * 1000;
} else {
utch = pps->CNTR_UTCHI;
do {
utcl = pps->CNTR_UTCLO;
nsec = pps->CNTR_NSEC * 16; /* we count a 16.5MHz */
tmp1 = pps->CNTR_UTCHI;
tmp2 = pps->CNTR_UTCLO;
} while((tmp1 != utch) || (tmp2 != utcl));
}
utc = (uint64_t)(utch) << 32 | utcl;
t = utc;
localtime_r(&t, &tm);
strftime(s, sizeof(s), "%Y-%m-%d %H:%M:%S", &tm);
printf("%lli.%09li\n%s.%09li\n", utc, nsec, s, nsec);
return 0;
}
/* This returns wr time, used for syncing to a second transition */
void gettimeof_wr(struct timeval *tv, struct PPSG_WB *pps)
{
unsigned long utcl, nsec, tmp2;
/* FIXME: not 2038-clean */
do {
utcl = pps->CNTR_UTCLO;
nsec = pps->CNTR_NSEC * 16; /* we count a 16.5MHz */
tmp2 = pps->CNTR_UTCLO;
} while(tmp2 != utcl);
tv->tv_sec = utcl;
tv->tv_usec = nsec / 1000;
}
/* This sets WR time from host time */
int wrdate_internal_set(struct PPSG_WB *pps)
{
struct timeval tvh, tvr; /* host, rabbit */
signed long diff;
unsigned long prev;
gettimeofday(&tvh, NULL);
gettimeof_wr(&tvr, pps);
/* Warn if more than 200ms away */
diff = tvh.tv_usec - tvr.tv_usec;
if (diff > 500 * 1000)
diff -= 1000 * 1000;
if (diff < -500 * 1000)
diff += 1000 * 1000;
if (diff > 200 * 1000 || diff < -200 * 1000)
fprintf(stderr, "%s: Warning: fractional second differs by"
"more than 0.2 (%li ms)\n", prgname, diff / 1000);
if (opt_verbose) {
printf("Host time: %9li.%06li\n", (long)(tvh.tv_sec),
(long)(tvh.tv_usec));
printf("WR time: %9li.%06li\n", (long)(tvr.tv_sec),
(long)(tvr.tv_usec));
printf("Fractional difference: %li usec\n", diff);
}
if (0) { /* Older implementation, wrong and needless */
/* Wait for the wr time to pass the second. */
prev = tvr.tv_sec;
do
gettimeof_wr(&tvr, pps);
while (tvr.tv_sec == prev);
/* If we are near a second change in the host, wait for it */
gettimeofday(&tvh, NULL);
if (tvh.tv_usec > 500 * 1000) {
prev = tvh.tv_sec;
do
gettimeofday(&tvh, NULL);
while (tvh.tv_sec == prev);
}
/* Finally, write the host time to White Rabbit */
/* FIXME: not 2038-clean */
pps->CNTR_UTCLO = tvh.tv_sec; // no effect: procedure is wrong
} else { /* good implementation */
signed long long diff64;
/* diff is the expected step to be added, so host - WR */
diff = tvh.tv_usec - tvr.tv_usec;
diff64 = tvh.tv_sec - tvr.tv_sec;
if (diff > 500 * 1000)
diff64++;
if (diff < -500 * 1000)
diff64--;
if (opt_verbose)
printf("adjusting by %lli seconds\n", diff64);
/* We must write a signed "adjustment" value to registers */
pps->ADJ_UTCLO = diff64 & 0xffffffff;
pps->ADJ_UTCHI = (diff64 >> 32) & 0xff;
pps->ADJ_NSEC = 0;
asm("" : : : "memory"); /* barrier... */
pps->CR = pps->CR | PPSG_CR_CNT_ADJ;
}
return 0;
}
/* Frontend to the set mechanism: parse the argument */
int wrdate_set(struct PPSG_WB *pps, char *arg)
{
char *s;
unsigned long t; /* WARNING: 64 bit */
struct timeval tv;
if (!strcmp(arg, "host"))
return wrdate_internal_set(pps);
s = strdup(arg);
if (sscanf(arg, "%li%s", &t, s) == 1) {
tv.tv_sec = t;
tv.tv_usec = 0;
if (settimeofday(&tv, NULL) < 0) {
fprintf(stderr, "%s: settimeofday(%s): %s\n",
prgname, arg, strerror(errno));
exit(1);
}
return wrdate_internal_set(pps);
}
/* FIXME: other time formats */
printf(" FIXME\n");
return 0;
}
int main(int argc, char **argv)
{
int c, fd;
char *cmd;
struct PPSG_WB *pps;
prgname = argv[0];
while ( (c = getopt(argc, argv, "fc:vn")) != -1) {
switch(c) {
case 'f':
opt_force = 1;
break;
case 'c':
opt_cfgfile = optarg;
break;
case 'v':
opt_verbose = 1;
break;
case 'n':
opt_not = 1;
break;
default:
help(argv[0]);
}
}
if (optind > argc - 1)
help(argv[0]);
cmd = argv[optind++];
fd = open("/dev/mem", O_RDWR | O_SYNC);
if (fd < 0) {
fprintf(stderr, "%s: /dev/mem: %s\n", argv[0], strerror(errno));
exit(1);
}
wrdate_check_host(fd);
pps = create_map(fd, FPGA_BASE_PPSG, sizeof(*pps));
close(fd);
wrdate_cfgfile(opt_cfgfile);
if (!strcmp(cmd, "get")) {
if (optind < argc)
help(argv[0]);
return wrdate_get(pps);
}
/* only other command is "set", with one argument */
if (strcmp(cmd, "set") || optind != argc - 1)
help(argv[0]);
return wrdate_set(pps, argv[optind]);
}
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