1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2001
4  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
5  */
6 
7 /*
8  * RTC, Date & Time support: get and set date & time
9  */
10 #include <command.h>
11 #include <dm.h>
12 #include <rtc.h>
13 #include <i2c.h>
14 #include <asm/global_data.h>
15 
16 DECLARE_GLOBAL_DATA_PTR;
17 
18 static const char * const weekdays[] = {
19 	"Sun", "Mon", "Tues", "Wednes", "Thurs", "Fri", "Satur",
20 };
21 
22 int mk_date (const char *, struct rtc_time *);
23 
24 static struct rtc_time default_tm = { 0, 0, 0, 1, 1, 2000, 6, 0, 0 };
25 
do_date(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])26 static int do_date(struct cmd_tbl *cmdtp, int flag, int argc,
27 		   char *const argv[])
28 {
29 	struct rtc_time tm;
30 	int rcode = 0;
31 	int old_bus __maybe_unused;
32 
33 	/* switch to correct I2C bus */
34 	struct udevice *dev;
35 
36 	rcode = uclass_get_device_by_seq(UCLASS_RTC, 0, &dev);
37 	if (rcode) {
38 		rcode = uclass_get_device(UCLASS_RTC, 0, &dev);
39 		if (rcode) {
40 			printf("Cannot find RTC: err=%d\n", rcode);
41 			return CMD_RET_FAILURE;
42 		}
43 	}
44 
45 	switch (argc) {
46 	case 2:			/* set date & time */
47 		if (strcmp(argv[1],"reset") == 0) {
48 			puts ("Reset RTC...\n");
49 			rcode = dm_rtc_reset(dev);
50 			if (!rcode)
51 				rcode = dm_rtc_set(dev, &default_tm);
52 			if (rcode)
53 				puts("## Failed to set date after RTC reset\n");
54 		} else {
55 			/* initialize tm with current time */
56 			rcode = dm_rtc_get(dev, &tm);
57 			if (!rcode) {
58 				/* insert new date & time */
59 				if (mk_date(argv[1], &tm) != 0) {
60 					puts ("## Bad date format\n");
61 					break;
62 				}
63 				/* and write to RTC */
64 				rcode = dm_rtc_set(dev, &tm);
65 				if (rcode) {
66 					printf("## Set date failed: err=%d\n",
67 					       rcode);
68 				}
69 			} else {
70 				puts("## Get date failed\n");
71 			}
72 		}
73 		fallthrough;
74 	case 1:			/* get date & time */
75 		rcode = dm_rtc_get(dev, &tm);
76 		if (rcode) {
77 			puts("## Get date failed\n");
78 			break;
79 		}
80 
81 		printf ("Date: %4d-%02d-%02d (%sday)    Time: %2d:%02d:%02d\n",
82 			tm.tm_year, tm.tm_mon, tm.tm_mday,
83 			(tm.tm_wday<0 || tm.tm_wday>6) ?
84 				"unknown " : weekdays[tm.tm_wday],
85 			tm.tm_hour, tm.tm_min, tm.tm_sec);
86 
87 		break;
88 	default:
89 		rcode = CMD_RET_USAGE;
90 	}
91 
92 	return rcode ? CMD_RET_FAILURE : 0;
93 }
94 
95 /*
96  * simple conversion of two-digit string with error checking
97  */
cnvrt2(const char * str,int * valp)98 static int cnvrt2 (const char *str, int *valp)
99 {
100 	int val;
101 
102 	if ((*str < '0') || (*str > '9'))
103 		return (-1);
104 
105 	val = *str - '0';
106 
107 	++str;
108 
109 	if ((*str < '0') || (*str > '9'))
110 		return (-1);
111 
112 	*valp = 10 * val + (*str - '0');
113 
114 	return (0);
115 }
116 
117 /*
118  * Convert date string: MMDDhhmm[[CC]YY][.ss]
119  *
120  * Some basic checking for valid values is done, but this will not catch
121  * all possible error conditions.
122  */
mk_date(const char * datestr,struct rtc_time * tmp)123 int mk_date (const char *datestr, struct rtc_time *tmp)
124 {
125 	int len, val;
126 	char *ptr;
127 
128 	ptr = strchr(datestr, '.');
129 	len = strlen(datestr);
130 
131 	/* Set seconds */
132 	if (ptr) {
133 		int sec;
134 
135 		ptr++;
136 		if ((len - (ptr - datestr)) != 2)
137 			return (-1);
138 
139 		len -= 3;
140 
141 		if (cnvrt2 (ptr, &sec))
142 			return (-1);
143 
144 		tmp->tm_sec = sec;
145 	} else {
146 		tmp->tm_sec = 0;
147 	}
148 
149 	if (len == 12) {		/* MMDDhhmmCCYY	*/
150 		int year, century;
151 
152 		if (cnvrt2 (datestr+ 8, &century) ||
153 		    cnvrt2 (datestr+10, &year) ) {
154 			return (-1);
155 		}
156 		tmp->tm_year = 100 * century + year;
157 	} else if (len == 10) {		/* MMDDhhmmYY	*/
158 		int year, century;
159 
160 		century = tmp->tm_year / 100;
161 		if (cnvrt2 (datestr+ 8, &year))
162 			return (-1);
163 		tmp->tm_year = 100 * century + year;
164 	}
165 
166 	switch (len) {
167 	case 8:			/* MMDDhhmm	*/
168 		/* fall thru */
169 	case 10:		/* MMDDhhmmYY	*/
170 		/* fall thru */
171 	case 12:		/* MMDDhhmmCCYY	*/
172 		if (cnvrt2 (datestr+0, &val) ||
173 		    val > 12) {
174 			break;
175 		}
176 		tmp->tm_mon  = val;
177 		if (cnvrt2 (datestr+2, &val) ||
178 		    val > ((tmp->tm_mon==2) ? 29 : 31)) {
179 			break;
180 		}
181 		tmp->tm_mday = val;
182 
183 		if (cnvrt2 (datestr+4, &val) ||
184 		    val > 23) {
185 			break;
186 		}
187 		tmp->tm_hour = val;
188 
189 		if (cnvrt2 (datestr+6, &val) ||
190 		    val > 59) {
191 			break;
192 		}
193 		tmp->tm_min  = val;
194 
195 		/* calculate day of week */
196 		rtc_calc_weekday(tmp);
197 
198 		return (0);
199 	default:
200 		break;
201 	}
202 
203 	return (-1);
204 }
205 
206 /***************************************************/
207 
208 U_BOOT_CMD(
209 	date,	2,	1,	do_date,
210 	"get/set/reset date & time",
211 	"[MMDDhhmm[[CC]YY][.ss]]\ndate reset\n"
212 	"  - without arguments: print date & time\n"
213 	"  - with numeric argument: set the system date & time\n"
214 	"  - with 'reset' argument: reset the RTC"
215 );
216