18c2ecf20Sopenharmony_ci/* adjtimex() tick adjustment test
28c2ecf20Sopenharmony_ci *		by:   John Stultz <john.stultz@linaro.org>
38c2ecf20Sopenharmony_ci *		(C) Copyright Linaro Limited 2015
48c2ecf20Sopenharmony_ci *		Licensed under the GPLv2
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci *  To build:
78c2ecf20Sopenharmony_ci *	$ gcc adjtick.c -o adjtick -lrt
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci *   This program is free software: you can redistribute it and/or modify
108c2ecf20Sopenharmony_ci *   it under the terms of the GNU General Public License as published by
118c2ecf20Sopenharmony_ci *   the Free Software Foundation, either version 2 of the License, or
128c2ecf20Sopenharmony_ci *   (at your option) any later version.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci *   This program is distributed in the hope that it will be useful,
158c2ecf20Sopenharmony_ci *   but WITHOUT ANY WARRANTY; without even the implied warranty of
168c2ecf20Sopenharmony_ci *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
178c2ecf20Sopenharmony_ci *   GNU General Public License for more details.
188c2ecf20Sopenharmony_ci */
198c2ecf20Sopenharmony_ci#include <stdio.h>
208c2ecf20Sopenharmony_ci#include <unistd.h>
218c2ecf20Sopenharmony_ci#include <stdlib.h>
228c2ecf20Sopenharmony_ci#include <sys/time.h>
238c2ecf20Sopenharmony_ci#include <sys/timex.h>
248c2ecf20Sopenharmony_ci#include <time.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include "../kselftest.h"
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#define CLOCK_MONOTONIC_RAW	4
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define NSEC_PER_SEC		1000000000LL
318c2ecf20Sopenharmony_ci#define USEC_PER_SEC		1000000
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define MILLION			1000000
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cilong systick;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cilong long llabs(long long val)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	if (val < 0)
408c2ecf20Sopenharmony_ci		val = -val;
418c2ecf20Sopenharmony_ci	return val;
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ciunsigned long long ts_to_nsec(struct timespec ts)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
478c2ecf20Sopenharmony_ci}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistruct timespec nsec_to_ts(long long ns)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	struct timespec ts;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	ts.tv_sec = ns/NSEC_PER_SEC;
548c2ecf20Sopenharmony_ci	ts.tv_nsec = ns%NSEC_PER_SEC;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	return ts;
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cilong long diff_timespec(struct timespec start, struct timespec end)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	long long start_ns, end_ns;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	start_ns = ts_to_nsec(start);
648c2ecf20Sopenharmony_ci	end_ns = ts_to_nsec(end);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	return end_ns - start_ns;
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_civoid get_monotonic_and_raw(struct timespec *mon, struct timespec *raw)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	struct timespec start, mid, end;
728c2ecf20Sopenharmony_ci	long long diff = 0, tmp;
738c2ecf20Sopenharmony_ci	int i;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	clock_gettime(CLOCK_MONOTONIC, mon);
768c2ecf20Sopenharmony_ci	clock_gettime(CLOCK_MONOTONIC_RAW, raw);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	/* Try to get a more tightly bound pairing */
798c2ecf20Sopenharmony_ci	for (i = 0; i < 3; i++) {
808c2ecf20Sopenharmony_ci		long long newdiff;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci		clock_gettime(CLOCK_MONOTONIC, &start);
838c2ecf20Sopenharmony_ci		clock_gettime(CLOCK_MONOTONIC_RAW, &mid);
848c2ecf20Sopenharmony_ci		clock_gettime(CLOCK_MONOTONIC, &end);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci		newdiff = diff_timespec(start, end);
878c2ecf20Sopenharmony_ci		if (diff == 0 || newdiff < diff) {
888c2ecf20Sopenharmony_ci			diff = newdiff;
898c2ecf20Sopenharmony_ci			*raw = mid;
908c2ecf20Sopenharmony_ci			tmp = (ts_to_nsec(start) + ts_to_nsec(end))/2;
918c2ecf20Sopenharmony_ci			*mon = nsec_to_ts(tmp);
928c2ecf20Sopenharmony_ci		}
938c2ecf20Sopenharmony_ci	}
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cilong long get_ppm_drift(void)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	struct timespec mon_start, raw_start, mon_end, raw_end;
998c2ecf20Sopenharmony_ci	long long delta1, delta2, eppm;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	get_monotonic_and_raw(&mon_start, &raw_start);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	sleep(15);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	get_monotonic_and_raw(&mon_end, &raw_end);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	delta1 = diff_timespec(mon_start, mon_end);
1088c2ecf20Sopenharmony_ci	delta2 = diff_timespec(raw_start, raw_end);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	eppm = (delta1*MILLION)/delta2 - MILLION;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	return eppm;
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ciint check_tick_adj(long tickval)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	long long eppm, ppm;
1188c2ecf20Sopenharmony_ci	struct timex tx1;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	tx1.modes	 = ADJ_TICK;
1218c2ecf20Sopenharmony_ci	tx1.modes	|= ADJ_OFFSET;
1228c2ecf20Sopenharmony_ci	tx1.modes	|= ADJ_FREQUENCY;
1238c2ecf20Sopenharmony_ci	tx1.modes	|= ADJ_STATUS;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	tx1.status	= STA_PLL;
1268c2ecf20Sopenharmony_ci	tx1.offset	= 0;
1278c2ecf20Sopenharmony_ci	tx1.freq	= 0;
1288c2ecf20Sopenharmony_ci	tx1.tick	= tickval;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	adjtimex(&tx1);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	sleep(1);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	ppm = ((long long)tickval * MILLION)/systick - MILLION;
1358c2ecf20Sopenharmony_ci	printf("Estimating tick (act: %ld usec, %lld ppm): ", tickval, ppm);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	eppm = get_ppm_drift();
1388c2ecf20Sopenharmony_ci	printf("%lld usec, %lld ppm", systick + (systick * eppm / MILLION), eppm);
1398c2ecf20Sopenharmony_ci	fflush(stdout);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	tx1.modes = 0;
1428c2ecf20Sopenharmony_ci	adjtimex(&tx1);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (tx1.offset || tx1.freq || tx1.tick != tickval) {
1458c2ecf20Sopenharmony_ci		printf("	[ERROR]\n");
1468c2ecf20Sopenharmony_ci		printf("\tUnexpected adjtimex return values, make sure ntpd is not running.\n");
1478c2ecf20Sopenharmony_ci		return -1;
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	/*
1518c2ecf20Sopenharmony_ci	 * Here we use 100ppm difference as an error bound.
1528c2ecf20Sopenharmony_ci	 * We likely should see better, but some coarse clocksources
1538c2ecf20Sopenharmony_ci	 * cannot match the HZ tick size accurately, so we have a
1548c2ecf20Sopenharmony_ci	 * internal correction factor that doesn't scale exactly
1558c2ecf20Sopenharmony_ci	 * with the adjustment, resulting in > 10ppm error during
1568c2ecf20Sopenharmony_ci	 * a 10% adjustment. 100ppm also gives us more breathing
1578c2ecf20Sopenharmony_ci	 * room for interruptions during the measurement.
1588c2ecf20Sopenharmony_ci	 */
1598c2ecf20Sopenharmony_ci	if (llabs(eppm - ppm) > 100) {
1608c2ecf20Sopenharmony_ci		printf("	[FAILED]\n");
1618c2ecf20Sopenharmony_ci		return -1;
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci	printf("	[OK]\n");
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	return  0;
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ciint main(int argv, char **argc)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	struct timespec raw;
1718c2ecf20Sopenharmony_ci	long tick, max, interval, err;
1728c2ecf20Sopenharmony_ci	struct timex tx1;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	err = 0;
1758c2ecf20Sopenharmony_ci	setbuf(stdout, NULL);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	if (clock_gettime(CLOCK_MONOTONIC_RAW, &raw)) {
1788c2ecf20Sopenharmony_ci		printf("ERR: NO CLOCK_MONOTONIC_RAW\n");
1798c2ecf20Sopenharmony_ci		return -1;
1808c2ecf20Sopenharmony_ci	}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	printf("Each iteration takes about 15 seconds\n");
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	systick = sysconf(_SC_CLK_TCK);
1858c2ecf20Sopenharmony_ci	systick = USEC_PER_SEC/sysconf(_SC_CLK_TCK);
1868c2ecf20Sopenharmony_ci	max = systick/10; /* +/- 10% */
1878c2ecf20Sopenharmony_ci	interval = max/4; /* in 4 steps each side */
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	for (tick = (systick - max); tick < (systick + max); tick += interval) {
1908c2ecf20Sopenharmony_ci		if (check_tick_adj(tick)) {
1918c2ecf20Sopenharmony_ci			err = 1;
1928c2ecf20Sopenharmony_ci			break;
1938c2ecf20Sopenharmony_ci		}
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	/* Reset things to zero */
1978c2ecf20Sopenharmony_ci	tx1.modes	 = ADJ_TICK;
1988c2ecf20Sopenharmony_ci	tx1.modes	|= ADJ_OFFSET;
1998c2ecf20Sopenharmony_ci	tx1.modes	|= ADJ_FREQUENCY;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	tx1.offset	 = 0;
2028c2ecf20Sopenharmony_ci	tx1.freq	 = 0;
2038c2ecf20Sopenharmony_ci	tx1.tick	 = systick;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	adjtimex(&tx1);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	if (err)
2088c2ecf20Sopenharmony_ci		return ksft_exit_fail();
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	return ksft_exit_pass();
2118c2ecf20Sopenharmony_ci}
212