162306a36Sopenharmony_ci/* adjtimex() tick adjustment test
262306a36Sopenharmony_ci *		by:   John Stultz <john.stultz@linaro.org>
362306a36Sopenharmony_ci *		(C) Copyright Linaro Limited 2015
462306a36Sopenharmony_ci *		Licensed under the GPLv2
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *  To build:
762306a36Sopenharmony_ci *	$ gcc adjtick.c -o adjtick -lrt
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci *   This program is free software: you can redistribute it and/or modify
1062306a36Sopenharmony_ci *   it under the terms of the GNU General Public License as published by
1162306a36Sopenharmony_ci *   the Free Software Foundation, either version 2 of the License, or
1262306a36Sopenharmony_ci *   (at your option) any later version.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci *   This program is distributed in the hope that it will be useful,
1562306a36Sopenharmony_ci *   but WITHOUT ANY WARRANTY; without even the implied warranty of
1662306a36Sopenharmony_ci *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1762306a36Sopenharmony_ci *   GNU General Public License for more details.
1862306a36Sopenharmony_ci */
1962306a36Sopenharmony_ci#include <stdio.h>
2062306a36Sopenharmony_ci#include <unistd.h>
2162306a36Sopenharmony_ci#include <stdlib.h>
2262306a36Sopenharmony_ci#include <sys/time.h>
2362306a36Sopenharmony_ci#include <sys/timex.h>
2462306a36Sopenharmony_ci#include <time.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include "../kselftest.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define CLOCK_MONOTONIC_RAW	4
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define NSEC_PER_SEC		1000000000LL
3162306a36Sopenharmony_ci#define USEC_PER_SEC		1000000
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define MILLION			1000000
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cilong systick;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cilong long llabs(long long val)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	if (val < 0)
4062306a36Sopenharmony_ci		val = -val;
4162306a36Sopenharmony_ci	return val;
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ciunsigned long long ts_to_nsec(struct timespec ts)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistruct timespec nsec_to_ts(long long ns)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	struct timespec ts;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	ts.tv_sec = ns/NSEC_PER_SEC;
5462306a36Sopenharmony_ci	ts.tv_nsec = ns%NSEC_PER_SEC;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	return ts;
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cilong long diff_timespec(struct timespec start, struct timespec end)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	long long start_ns, end_ns;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	start_ns = ts_to_nsec(start);
6462306a36Sopenharmony_ci	end_ns = ts_to_nsec(end);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	return end_ns - start_ns;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_civoid get_monotonic_and_raw(struct timespec *mon, struct timespec *raw)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	struct timespec start, mid, end;
7262306a36Sopenharmony_ci	long long diff = 0, tmp;
7362306a36Sopenharmony_ci	int i;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	clock_gettime(CLOCK_MONOTONIC, mon);
7662306a36Sopenharmony_ci	clock_gettime(CLOCK_MONOTONIC_RAW, raw);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	/* Try to get a more tightly bound pairing */
7962306a36Sopenharmony_ci	for (i = 0; i < 3; i++) {
8062306a36Sopenharmony_ci		long long newdiff;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci		clock_gettime(CLOCK_MONOTONIC, &start);
8362306a36Sopenharmony_ci		clock_gettime(CLOCK_MONOTONIC_RAW, &mid);
8462306a36Sopenharmony_ci		clock_gettime(CLOCK_MONOTONIC, &end);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci		newdiff = diff_timespec(start, end);
8762306a36Sopenharmony_ci		if (diff == 0 || newdiff < diff) {
8862306a36Sopenharmony_ci			diff = newdiff;
8962306a36Sopenharmony_ci			*raw = mid;
9062306a36Sopenharmony_ci			tmp = (ts_to_nsec(start) + ts_to_nsec(end))/2;
9162306a36Sopenharmony_ci			*mon = nsec_to_ts(tmp);
9262306a36Sopenharmony_ci		}
9362306a36Sopenharmony_ci	}
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cilong long get_ppm_drift(void)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	struct timespec mon_start, raw_start, mon_end, raw_end;
9962306a36Sopenharmony_ci	long long delta1, delta2, eppm;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	get_monotonic_and_raw(&mon_start, &raw_start);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	sleep(15);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	get_monotonic_and_raw(&mon_end, &raw_end);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	delta1 = diff_timespec(mon_start, mon_end);
10862306a36Sopenharmony_ci	delta2 = diff_timespec(raw_start, raw_end);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	eppm = (delta1*MILLION)/delta2 - MILLION;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	return eppm;
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ciint check_tick_adj(long tickval)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	long long eppm, ppm;
11862306a36Sopenharmony_ci	struct timex tx1;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	tx1.modes	 = ADJ_TICK;
12162306a36Sopenharmony_ci	tx1.modes	|= ADJ_OFFSET;
12262306a36Sopenharmony_ci	tx1.modes	|= ADJ_FREQUENCY;
12362306a36Sopenharmony_ci	tx1.modes	|= ADJ_STATUS;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	tx1.status	= STA_PLL;
12662306a36Sopenharmony_ci	tx1.offset	= 0;
12762306a36Sopenharmony_ci	tx1.freq	= 0;
12862306a36Sopenharmony_ci	tx1.tick	= tickval;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	adjtimex(&tx1);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	sleep(1);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	ppm = ((long long)tickval * MILLION)/systick - MILLION;
13562306a36Sopenharmony_ci	printf("Estimating tick (act: %ld usec, %lld ppm): ", tickval, ppm);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	eppm = get_ppm_drift();
13862306a36Sopenharmony_ci	printf("%lld usec, %lld ppm", systick + (systick * eppm / MILLION), eppm);
13962306a36Sopenharmony_ci	fflush(stdout);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	tx1.modes = 0;
14262306a36Sopenharmony_ci	adjtimex(&tx1);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	if (tx1.offset || tx1.freq || tx1.tick != tickval) {
14562306a36Sopenharmony_ci		printf("	[ERROR]\n");
14662306a36Sopenharmony_ci		printf("\tUnexpected adjtimex return values, make sure ntpd is not running.\n");
14762306a36Sopenharmony_ci		return -1;
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	/*
15162306a36Sopenharmony_ci	 * Here we use 100ppm difference as an error bound.
15262306a36Sopenharmony_ci	 * We likely should see better, but some coarse clocksources
15362306a36Sopenharmony_ci	 * cannot match the HZ tick size accurately, so we have a
15462306a36Sopenharmony_ci	 * internal correction factor that doesn't scale exactly
15562306a36Sopenharmony_ci	 * with the adjustment, resulting in > 10ppm error during
15662306a36Sopenharmony_ci	 * a 10% adjustment. 100ppm also gives us more breathing
15762306a36Sopenharmony_ci	 * room for interruptions during the measurement.
15862306a36Sopenharmony_ci	 */
15962306a36Sopenharmony_ci	if (llabs(eppm - ppm) > 100) {
16062306a36Sopenharmony_ci		printf("	[FAILED]\n");
16162306a36Sopenharmony_ci		return -1;
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci	printf("	[OK]\n");
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	return  0;
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ciint main(int argc, char **argv)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	struct timespec raw;
17162306a36Sopenharmony_ci	long tick, max, interval, err;
17262306a36Sopenharmony_ci	struct timex tx1;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	err = 0;
17562306a36Sopenharmony_ci	setbuf(stdout, NULL);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	if (clock_gettime(CLOCK_MONOTONIC_RAW, &raw)) {
17862306a36Sopenharmony_ci		printf("ERR: NO CLOCK_MONOTONIC_RAW\n");
17962306a36Sopenharmony_ci		return -1;
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	printf("Each iteration takes about 15 seconds\n");
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	systick = sysconf(_SC_CLK_TCK);
18562306a36Sopenharmony_ci	systick = USEC_PER_SEC/sysconf(_SC_CLK_TCK);
18662306a36Sopenharmony_ci	max = systick/10; /* +/- 10% */
18762306a36Sopenharmony_ci	interval = max/4; /* in 4 steps each side */
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	for (tick = (systick - max); tick < (systick + max); tick += interval) {
19062306a36Sopenharmony_ci		if (check_tick_adj(tick)) {
19162306a36Sopenharmony_ci			err = 1;
19262306a36Sopenharmony_ci			break;
19362306a36Sopenharmony_ci		}
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	/* Reset things to zero */
19762306a36Sopenharmony_ci	tx1.modes	 = ADJ_TICK;
19862306a36Sopenharmony_ci	tx1.modes	|= ADJ_OFFSET;
19962306a36Sopenharmony_ci	tx1.modes	|= ADJ_FREQUENCY;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	tx1.offset	 = 0;
20262306a36Sopenharmony_ci	tx1.freq	 = 0;
20362306a36Sopenharmony_ci	tx1.tick	 = systick;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	adjtimex(&tx1);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	if (err)
20862306a36Sopenharmony_ci		return ksft_exit_fail();
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	return ksft_exit_pass();
21162306a36Sopenharmony_ci}
212