Merge branch '2019-10-28-azure-ci-support'
[oweals/u-boot.git] / cmd / date.c
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 <common.h>
11 #include <command.h>
12 #include <dm.h>
13 #include <rtc.h>
14 #include <i2c.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 #ifdef CONFIG_NEEDS_MANUAL_RELOC
23 #define RELOC(a)        ((typeof(a))((unsigned long)(a) + gd->reloc_off))
24 #else
25 #define RELOC(a)        a
26 #endif
27
28 int mk_date (const char *, struct rtc_time *);
29
30 static struct rtc_time default_tm = { 0, 0, 0, 1, 1, 2000, 6, 0, 0 };
31
32 static int do_date(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
33 {
34         struct rtc_time tm;
35         int rcode = 0;
36         int old_bus __maybe_unused;
37
38         /* switch to correct I2C bus */
39 #ifdef CONFIG_DM_RTC
40         struct udevice *dev;
41
42         rcode = uclass_get_device(UCLASS_RTC, 0, &dev);
43         if (rcode) {
44                 printf("Cannot find RTC: err=%d\n", rcode);
45                 return CMD_RET_FAILURE;
46         }
47 #elif defined(CONFIG_SYS_I2C)
48         old_bus = i2c_get_bus_num();
49         i2c_set_bus_num(CONFIG_SYS_RTC_BUS_NUM);
50 #else
51         old_bus = I2C_GET_BUS();
52         I2C_SET_BUS(CONFIG_SYS_RTC_BUS_NUM);
53 #endif
54
55         switch (argc) {
56         case 2:                 /* set date & time */
57                 if (strcmp(argv[1],"reset") == 0) {
58                         puts ("Reset RTC...\n");
59 #ifdef CONFIG_DM_RTC
60                         rcode = dm_rtc_reset(dev);
61                         if (!rcode)
62                                 rcode = dm_rtc_set(dev, &default_tm);
63 #else
64                         rtc_reset();
65                         rcode = rtc_set(&default_tm);
66 #endif
67                         if (rcode)
68                                 puts("## Failed to set date after RTC reset\n");
69                 } else {
70                         /* initialize tm with current time */
71 #ifdef CONFIG_DM_RTC
72                         rcode = dm_rtc_get(dev, &tm);
73 #else
74                         rcode = rtc_get(&tm);
75 #endif
76                         if (!rcode) {
77                                 /* insert new date & time */
78                                 if (mk_date(argv[1], &tm) != 0) {
79                                         puts ("## Bad date format\n");
80                                         break;
81                                 }
82                                 /* and write to RTC */
83 #ifdef CONFIG_DM_RTC
84                                 rcode = dm_rtc_set(dev, &tm);
85 #else
86                                 rcode = rtc_set(&tm);
87 #endif
88                                 if (rcode) {
89                                         printf("## Set date failed: err=%d\n",
90                                                rcode);
91                                 }
92                         } else {
93                                 puts("## Get date failed\n");
94                         }
95                 }
96                 /* FALL TROUGH */
97         case 1:                 /* get date & time */
98 #ifdef CONFIG_DM_RTC
99                 rcode = dm_rtc_get(dev, &tm);
100 #else
101                 rcode = rtc_get(&tm);
102 #endif
103                 if (rcode) {
104                         puts("## Get date failed\n");
105                         break;
106                 }
107
108                 printf ("Date: %4d-%02d-%02d (%sday)    Time: %2d:%02d:%02d\n",
109                         tm.tm_year, tm.tm_mon, tm.tm_mday,
110                         (tm.tm_wday<0 || tm.tm_wday>6) ?
111                                 "unknown " : RELOC(weekdays[tm.tm_wday]),
112                         tm.tm_hour, tm.tm_min, tm.tm_sec);
113
114                 break;
115         default:
116                 rcode = CMD_RET_USAGE;
117         }
118
119         /* switch back to original I2C bus */
120 #ifdef CONFIG_SYS_I2C
121         i2c_set_bus_num(old_bus);
122 #elif !defined(CONFIG_DM_RTC)
123         I2C_SET_BUS(old_bus);
124 #endif
125
126         return rcode ? CMD_RET_FAILURE : 0;
127 }
128
129 /*
130  * simple conversion of two-digit string with error checking
131  */
132 static int cnvrt2 (const char *str, int *valp)
133 {
134         int val;
135
136         if ((*str < '0') || (*str > '9'))
137                 return (-1);
138
139         val = *str - '0';
140
141         ++str;
142
143         if ((*str < '0') || (*str > '9'))
144                 return (-1);
145
146         *valp = 10 * val + (*str - '0');
147
148         return (0);
149 }
150
151 /*
152  * Convert date string: MMDDhhmm[[CC]YY][.ss]
153  *
154  * Some basic checking for valid values is done, but this will not catch
155  * all possible error conditions.
156  */
157 int mk_date (const char *datestr, struct rtc_time *tmp)
158 {
159         int len, val;
160         char *ptr;
161
162         ptr = strchr(datestr, '.');
163         len = strlen(datestr);
164
165         /* Set seconds */
166         if (ptr) {
167                 int sec;
168
169                 ptr++;
170                 if ((len - (ptr - datestr)) != 2)
171                         return (-1);
172
173                 len -= 3;
174
175                 if (cnvrt2 (ptr, &sec))
176                         return (-1);
177
178                 tmp->tm_sec = sec;
179         } else {
180                 tmp->tm_sec = 0;
181         }
182
183         if (len == 12) {                /* MMDDhhmmCCYY */
184                 int year, century;
185
186                 if (cnvrt2 (datestr+ 8, &century) ||
187                     cnvrt2 (datestr+10, &year) ) {
188                         return (-1);
189                 }
190                 tmp->tm_year = 100 * century + year;
191         } else if (len == 10) {         /* MMDDhhmmYY   */
192                 int year, century;
193
194                 century = tmp->tm_year / 100;
195                 if (cnvrt2 (datestr+ 8, &year))
196                         return (-1);
197                 tmp->tm_year = 100 * century + year;
198         }
199
200         switch (len) {
201         case 8:                 /* MMDDhhmm     */
202                 /* fall thru */
203         case 10:                /* MMDDhhmmYY   */
204                 /* fall thru */
205         case 12:                /* MMDDhhmmCCYY */
206                 if (cnvrt2 (datestr+0, &val) ||
207                     val > 12) {
208                         break;
209                 }
210                 tmp->tm_mon  = val;
211                 if (cnvrt2 (datestr+2, &val) ||
212                     val > ((tmp->tm_mon==2) ? 29 : 31)) {
213                         break;
214                 }
215                 tmp->tm_mday = val;
216
217                 if (cnvrt2 (datestr+4, &val) ||
218                     val > 23) {
219                         break;
220                 }
221                 tmp->tm_hour = val;
222
223                 if (cnvrt2 (datestr+6, &val) ||
224                     val > 59) {
225                         break;
226                 }
227                 tmp->tm_min  = val;
228
229                 /* calculate day of week */
230                 rtc_calc_weekday(tmp);
231
232                 return (0);
233         default:
234                 break;
235         }
236
237         return (-1);
238 }
239
240 /***************************************************/
241
242 U_BOOT_CMD(
243         date,   2,      1,      do_date,
244         "get/set/reset date & time",
245         "[MMDDhhmm[[CC]YY][.ss]]\ndate reset\n"
246         "  - without arguments: print date & time\n"
247         "  - with numeric argument: set the system date & time\n"
248         "  - with 'reset' argument: reset the RTC"
249 );