1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * (C) Copyright 2002
4 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
5 */
6
7#include <common.h>
8
9/*
10 * RTC test
11 *
12 * The Real Time Clock (RTC) operation is verified by this test.
13 * The following features are verified:
14 *   o) RTC Power Fault
15 *	This is verified by analyzing the rtc_get() return status.
16 *   o) Time uniformity
17 *      This is verified by reading RTC in polling within
18 *      a short period of time.
19 *   o) Passing month boundaries
20 *      This is checked by setting RTC to a second before
21 *      a month boundary and reading it after its passing the
22 *      boundary. The test is performed for both leap- and
23 *      nonleap-years.
24 */
25
26#include <post.h>
27#include <rtc.h>
28
29#if CFG_POST & CFG_SYS_POST_RTC
30
31static int rtc_post_skip (ulong * diff)
32{
33	struct rtc_time tm1;
34	struct rtc_time tm2;
35	ulong start1;
36	ulong start2;
37
38	rtc_get (&tm1);
39	start1 = get_timer (0);
40
41	while (1) {
42		rtc_get (&tm2);
43		start2 = get_timer (0);
44		if (tm1.tm_sec != tm2.tm_sec)
45			break;
46		if (start2 - start1 > 1500)
47			break;
48	}
49
50	if (tm1.tm_sec != tm2.tm_sec) {
51		*diff = start2 - start1;
52
53		return 0;
54	} else {
55		return -1;
56	}
57}
58
59static void rtc_post_restore (struct rtc_time *tm, unsigned int sec)
60{
61	time_t t = rtc_mktime(tm) + sec;
62	struct rtc_time ntm;
63
64	rtc_to_tm(t, &ntm);
65
66	rtc_set (&ntm);
67}
68
69int rtc_post_test (int flags)
70{
71	ulong diff;
72	unsigned int i;
73	struct rtc_time svtm;
74	static unsigned int daysnl[] =
75			{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
76	static unsigned int daysl[] =
77			{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
78	unsigned int ynl = 1999;
79	unsigned int yl = 2000;
80	unsigned int skipped = 0;
81	int reliable;
82
83	/* Time reliability */
84	reliable = rtc_get (&svtm);
85
86	/* Time uniformity */
87	if (rtc_post_skip (&diff) != 0) {
88		post_log ("Timeout while waiting for a new second !\n");
89
90		return -1;
91	}
92
93	for (i = 0; i < 5; i++) {
94		if (rtc_post_skip (&diff) != 0) {
95			post_log ("Timeout while waiting for a new second !\n");
96
97			return -1;
98		}
99
100		if (diff < 950 || diff > 1050) {
101			post_log ("Invalid second duration !\n");
102
103			return -1;
104		}
105	}
106
107	/* Passing month boundaries */
108
109	if (rtc_post_skip (&diff) != 0) {
110		post_log ("Timeout while waiting for a new second !\n");
111
112		return -1;
113	}
114	rtc_get (&svtm);
115
116	for (i = 0; i < 12; i++) {
117		time_t t;
118		struct rtc_time tm;
119
120		tm.tm_year = ynl;
121		tm.tm_mon = i + 1;
122		tm.tm_mday = daysnl[i];
123		tm.tm_hour = 23;
124		tm.tm_min = 59;
125		tm.tm_sec = 59;
126		t = rtc_mktime(&tm);
127		rtc_to_tm(t, &tm);
128		rtc_set (&tm);
129
130		skipped++;
131		if (rtc_post_skip (&diff) != 0) {
132			rtc_post_restore (&svtm, skipped);
133			post_log ("Timeout while waiting for a new second !\n");
134
135			return -1;
136		}
137
138		rtc_get (&tm);
139		if (tm.tm_mon == i + 1) {
140			rtc_post_restore (&svtm, skipped);
141			post_log ("Month %d boundary is not passed !\n", i + 1);
142
143			return -1;
144		}
145	}
146
147	for (i = 0; i < 12; i++) {
148		time_t t;
149		struct rtc_time tm;
150
151		tm.tm_year = yl;
152		tm.tm_mon = i + 1;
153		tm.tm_mday = daysl[i];
154		tm.tm_hour = 23;
155		tm.tm_min = 59;
156		tm.tm_sec = 59;
157		t = rtc_mktime(&tm);
158
159		rtc_to_tm(t, &tm);
160		rtc_set (&tm);
161
162		skipped++;
163		if (rtc_post_skip (&diff) != 0) {
164			rtc_post_restore (&svtm, skipped);
165			post_log ("Timeout while waiting for a new second !\n");
166
167			return -1;
168		}
169
170		rtc_get (&tm);
171		if (tm.tm_mon == i + 1) {
172			rtc_post_restore (&svtm, skipped);
173			post_log ("Month %d boundary is not passed !\n", i + 1);
174
175			return -1;
176		}
177	}
178	rtc_post_restore (&svtm, skipped);
179
180	/* If come here, then RTC operates correcty, check the correctness
181	 * of the time it reports.
182	 */
183	if (reliable < 0) {
184		post_log ("RTC Time is not reliable! Power fault? \n");
185
186		return -1;
187	}
188
189	return 0;
190}
191
192#endif /* CFG_POST & CFG_SYS_POST_RTC */
193