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 CONFIG_POST & CONFIG_SYS_POST_RTC
30 
rtc_post_skip(ulong * diff)31 static 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 
rtc_post_restore(struct rtc_time * tm,unsigned int sec)59 static 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 
rtc_post_test(int flags)69 int 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 /* CONFIG_POST & CONFIG_SYS_POST_RTC */
193