162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * This test checks the response of the system clock to frequency
462306a36Sopenharmony_ci * steps made with adjtimex(). The frequency error and stability of
562306a36Sopenharmony_ci * the CLOCK_MONOTONIC clock relative to the CLOCK_MONOTONIC_RAW clock
662306a36Sopenharmony_ci * is measured in two intervals following the step. The test fails if
762306a36Sopenharmony_ci * values from the second interval exceed specified limits.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Copyright (C) Miroslav Lichvar <mlichvar@redhat.com>  2017
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <math.h>
1362306a36Sopenharmony_ci#include <stdio.h>
1462306a36Sopenharmony_ci#include <sys/timex.h>
1562306a36Sopenharmony_ci#include <time.h>
1662306a36Sopenharmony_ci#include <unistd.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "../kselftest.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define SAMPLES 100
2162306a36Sopenharmony_ci#define SAMPLE_READINGS 10
2262306a36Sopenharmony_ci#define MEAN_SAMPLE_INTERVAL 0.1
2362306a36Sopenharmony_ci#define STEP_INTERVAL 1.0
2462306a36Sopenharmony_ci#define MAX_PRECISION 500e-9
2562306a36Sopenharmony_ci#define MAX_FREQ_ERROR 0.02e-6
2662306a36Sopenharmony_ci#define MAX_STDDEV 50e-9
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#ifndef ADJ_SETOFFSET
2962306a36Sopenharmony_ci  #define ADJ_SETOFFSET 0x0100
3062306a36Sopenharmony_ci#endif
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistruct sample {
3362306a36Sopenharmony_ci	double offset;
3462306a36Sopenharmony_ci	double time;
3562306a36Sopenharmony_ci};
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic time_t mono_raw_base;
3862306a36Sopenharmony_cistatic time_t mono_base;
3962306a36Sopenharmony_cistatic long user_hz;
4062306a36Sopenharmony_cistatic double precision;
4162306a36Sopenharmony_cistatic double mono_freq_offset;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic double diff_timespec(struct timespec *ts1, struct timespec *ts2)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	return ts1->tv_sec - ts2->tv_sec + (ts1->tv_nsec - ts2->tv_nsec) / 1e9;
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic double get_sample(struct sample *sample)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	double delay, mindelay = 0.0;
5162306a36Sopenharmony_ci	struct timespec ts1, ts2, ts3;
5262306a36Sopenharmony_ci	int i;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	for (i = 0; i < SAMPLE_READINGS; i++) {
5562306a36Sopenharmony_ci		clock_gettime(CLOCK_MONOTONIC_RAW, &ts1);
5662306a36Sopenharmony_ci		clock_gettime(CLOCK_MONOTONIC, &ts2);
5762306a36Sopenharmony_ci		clock_gettime(CLOCK_MONOTONIC_RAW, &ts3);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci		ts1.tv_sec -= mono_raw_base;
6062306a36Sopenharmony_ci		ts2.tv_sec -= mono_base;
6162306a36Sopenharmony_ci		ts3.tv_sec -= mono_raw_base;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci		delay = diff_timespec(&ts3, &ts1);
6462306a36Sopenharmony_ci		if (delay <= 1e-9) {
6562306a36Sopenharmony_ci			i--;
6662306a36Sopenharmony_ci			continue;
6762306a36Sopenharmony_ci		}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci		if (!i || delay < mindelay) {
7062306a36Sopenharmony_ci			sample->offset = diff_timespec(&ts2, &ts1);
7162306a36Sopenharmony_ci			sample->offset -= delay / 2.0;
7262306a36Sopenharmony_ci			sample->time = ts1.tv_sec + ts1.tv_nsec / 1e9;
7362306a36Sopenharmony_ci			mindelay = delay;
7462306a36Sopenharmony_ci		}
7562306a36Sopenharmony_ci	}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	return mindelay;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic void reset_ntp_error(void)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	struct timex txc;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	txc.modes = ADJ_SETOFFSET;
8562306a36Sopenharmony_ci	txc.time.tv_sec = 0;
8662306a36Sopenharmony_ci	txc.time.tv_usec = 0;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	if (adjtimex(&txc) < 0) {
8962306a36Sopenharmony_ci		perror("[FAIL] adjtimex");
9062306a36Sopenharmony_ci		ksft_exit_fail();
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic void set_frequency(double freq)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	struct timex txc;
9762306a36Sopenharmony_ci	int tick_offset;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	tick_offset = 1e6 * freq / user_hz;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	txc.modes = ADJ_TICK | ADJ_FREQUENCY;
10262306a36Sopenharmony_ci	txc.tick = 1000000 / user_hz + tick_offset;
10362306a36Sopenharmony_ci	txc.freq = (1e6 * freq - user_hz * tick_offset) * (1 << 16);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	if (adjtimex(&txc) < 0) {
10662306a36Sopenharmony_ci		perror("[FAIL] adjtimex");
10762306a36Sopenharmony_ci		ksft_exit_fail();
10862306a36Sopenharmony_ci	}
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic void regress(struct sample *samples, int n, double *intercept,
11262306a36Sopenharmony_ci		    double *slope, double *r_stddev, double *r_max)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	double x, y, r, x_sum, y_sum, xy_sum, x2_sum, r2_sum;
11562306a36Sopenharmony_ci	int i;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	x_sum = 0.0, y_sum = 0.0, xy_sum = 0.0, x2_sum = 0.0;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	for (i = 0; i < n; i++) {
12062306a36Sopenharmony_ci		x = samples[i].time;
12162306a36Sopenharmony_ci		y = samples[i].offset;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci		x_sum += x;
12462306a36Sopenharmony_ci		y_sum += y;
12562306a36Sopenharmony_ci		xy_sum += x * y;
12662306a36Sopenharmony_ci		x2_sum += x * x;
12762306a36Sopenharmony_ci	}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	*slope = (xy_sum - x_sum * y_sum / n) / (x2_sum - x_sum * x_sum / n);
13062306a36Sopenharmony_ci	*intercept = (y_sum - *slope * x_sum) / n;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	*r_max = 0.0, r2_sum = 0.0;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	for (i = 0; i < n; i++) {
13562306a36Sopenharmony_ci		x = samples[i].time;
13662306a36Sopenharmony_ci		y = samples[i].offset;
13762306a36Sopenharmony_ci		r = fabs(x * *slope + *intercept - y);
13862306a36Sopenharmony_ci		if (*r_max < r)
13962306a36Sopenharmony_ci			*r_max = r;
14062306a36Sopenharmony_ci		r2_sum += r * r;
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	*r_stddev = sqrt(r2_sum / n);
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic int run_test(int calibration, double freq_base, double freq_step)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	struct sample samples[SAMPLES];
14962306a36Sopenharmony_ci	double intercept, slope, stddev1, max1, stddev2, max2;
15062306a36Sopenharmony_ci	double freq_error1, freq_error2;
15162306a36Sopenharmony_ci	int i;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	set_frequency(freq_base);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	for (i = 0; i < 10; i++)
15662306a36Sopenharmony_ci		usleep(1e6 * MEAN_SAMPLE_INTERVAL / 10);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	reset_ntp_error();
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	set_frequency(freq_base + freq_step);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	for (i = 0; i < 10; i++)
16362306a36Sopenharmony_ci		usleep(rand() % 2000000 * STEP_INTERVAL / 10);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	set_frequency(freq_base);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	for (i = 0; i < SAMPLES; i++) {
16862306a36Sopenharmony_ci		usleep(rand() % 2000000 * MEAN_SAMPLE_INTERVAL);
16962306a36Sopenharmony_ci		get_sample(&samples[i]);
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	if (calibration) {
17362306a36Sopenharmony_ci		regress(samples, SAMPLES, &intercept, &slope, &stddev1, &max1);
17462306a36Sopenharmony_ci		mono_freq_offset = slope;
17562306a36Sopenharmony_ci		printf("CLOCK_MONOTONIC_RAW frequency offset: %11.3f ppm\n",
17662306a36Sopenharmony_ci		       1e6 * mono_freq_offset);
17762306a36Sopenharmony_ci		return 0;
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	regress(samples, SAMPLES / 2, &intercept, &slope, &stddev1, &max1);
18162306a36Sopenharmony_ci	freq_error1 = slope * (1.0 - mono_freq_offset) - mono_freq_offset -
18262306a36Sopenharmony_ci			freq_base;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	regress(samples + SAMPLES / 2, SAMPLES / 2, &intercept, &slope,
18562306a36Sopenharmony_ci		&stddev2, &max2);
18662306a36Sopenharmony_ci	freq_error2 = slope * (1.0 - mono_freq_offset) - mono_freq_offset -
18762306a36Sopenharmony_ci			freq_base;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	printf("%6.0f %+10.3f %6.0f %7.0f %+10.3f %6.0f %7.0f\t",
19062306a36Sopenharmony_ci	       1e6 * freq_step,
19162306a36Sopenharmony_ci	       1e6 * freq_error1, 1e9 * stddev1, 1e9 * max1,
19262306a36Sopenharmony_ci	       1e6 * freq_error2, 1e9 * stddev2, 1e9 * max2);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	if (fabs(freq_error2) > MAX_FREQ_ERROR || stddev2 > MAX_STDDEV) {
19562306a36Sopenharmony_ci		printf("[FAIL]\n");
19662306a36Sopenharmony_ci		return 1;
19762306a36Sopenharmony_ci	}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	printf("[OK]\n");
20062306a36Sopenharmony_ci	return 0;
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic void init_test(void)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	struct timespec ts;
20662306a36Sopenharmony_ci	struct sample sample;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts)) {
20962306a36Sopenharmony_ci		perror("[FAIL] clock_gettime(CLOCK_MONOTONIC_RAW)");
21062306a36Sopenharmony_ci		ksft_exit_fail();
21162306a36Sopenharmony_ci	}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	mono_raw_base = ts.tv_sec;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
21662306a36Sopenharmony_ci		perror("[FAIL] clock_gettime(CLOCK_MONOTONIC)");
21762306a36Sopenharmony_ci		ksft_exit_fail();
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	mono_base = ts.tv_sec;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	user_hz = sysconf(_SC_CLK_TCK);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	precision = get_sample(&sample) / 2.0;
22562306a36Sopenharmony_ci	printf("CLOCK_MONOTONIC_RAW+CLOCK_MONOTONIC precision: %.0f ns\t\t",
22662306a36Sopenharmony_ci	       1e9 * precision);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	if (precision > MAX_PRECISION)
22962306a36Sopenharmony_ci		ksft_exit_skip("precision: %.0f ns > MAX_PRECISION: %.0f ns\n",
23062306a36Sopenharmony_ci				1e9 * precision, 1e9 * MAX_PRECISION);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	printf("[OK]\n");
23362306a36Sopenharmony_ci	srand(ts.tv_sec ^ ts.tv_nsec);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	run_test(1, 0.0, 0.0);
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ciint main(int argc, char **argv)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	double freq_base, freq_step;
24162306a36Sopenharmony_ci	int i, j, fails = 0;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	init_test();
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	printf("Checking response to frequency step:\n");
24662306a36Sopenharmony_ci	printf("  Step           1st interval              2nd interval\n");
24762306a36Sopenharmony_ci	printf("             Freq    Dev     Max       Freq    Dev     Max\n");
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	for (i = 2; i >= 0; i--) {
25062306a36Sopenharmony_ci		for (j = 0; j < 5; j++) {
25162306a36Sopenharmony_ci			freq_base = (rand() % (1 << 24) - (1 << 23)) / 65536e6;
25262306a36Sopenharmony_ci			freq_step = 10e-6 * (1 << (6 * i));
25362306a36Sopenharmony_ci			fails += run_test(0, freq_base, freq_step);
25462306a36Sopenharmony_ci		}
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	set_frequency(0.0);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	if (fails)
26062306a36Sopenharmony_ci		return ksft_exit_fail();
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	return ksft_exit_pass();
26362306a36Sopenharmony_ci}
264