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++ = '\0'; 170 if ((len - (ptr - datestr)) != 2) 171 return (-1); 172 173 len = strlen (datestr); 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, ¢ury) || 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 ); 250