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