1 /* 2 * (C) Copyright 2007 3 * Larry Johnson, lrj@acm.org 4 * 5 * based on rtc/m41t11.c which is ... 6 * 7 * (C) Copyright 2002 8 * Andrew May, Viasat Inc, amay@viasat.com 9 * 10 * SPDX-License-Identifier: GPL-2.0+ 11 */ 12 13 /* 14 * STMicroelectronics M41T60 serial access real-time clock 15 */ 16 17 /* #define DEBUG 1 */ 18 19 #include <common.h> 20 #include <command.h> 21 #include <rtc.h> 22 #include <i2c.h> 23 24 #if defined(CONFIG_SYS_I2C_RTC_ADDR) && defined(CONFIG_CMD_DATE) 25 26 /* 27 * Convert between century and "century bits" (CB1 and CB0). These routines 28 * assume years are in the range 1900 - 2299. 29 */ 30 31 static unsigned char year2cb(unsigned const year) 32 { 33 if (year < 1900 || year >= 2300) 34 printf("M41T60 RTC: year %d out of range\n", year); 35 36 return (year / 100) & 0x3; 37 } 38 39 static unsigned cb2year(unsigned const cb) 40 { 41 return 1900 + 100 * ((cb + 1) & 0x3); 42 } 43 44 /* 45 * These are simple defines for the chip local to here so they aren't too 46 * verbose. DAY/DATE aren't nice but that is how they are on the data sheet. 47 */ 48 #define RTC_SEC 0x0 49 #define RTC_MIN 0x1 50 #define RTC_HOUR 0x2 51 #define RTC_DAY 0x3 52 #define RTC_DATE 0x4 53 #define RTC_MONTH 0x5 54 #define RTC_YEAR 0x6 55 56 #define RTC_REG_CNT 7 57 58 #define RTC_CTRL 0x7 59 60 #if defined(DEBUG) 61 static void rtc_dump(char const *const label) 62 { 63 uchar data[8]; 64 65 if (i2c_read(CONFIG_SYS_I2C_RTC_ADDR, 0, 1, data, sizeof(data))) { 66 printf("I2C read failed in rtc_dump()\n"); 67 return; 68 } 69 printf("RTC dump %s: %02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X\n", 70 label, data[0], data[1], data[2], data[3], 71 data[4], data[5], data[6], data[7]); 72 } 73 #else 74 #define rtc_dump(label) 75 #endif 76 77 static uchar *rtc_validate(void) 78 { 79 /* 80 * This routine uses the OUT bit and the validity of the time values to 81 * determine whether there has been an initial power-up since the last 82 * time the routine was run. It assumes that the OUT bit is not being 83 * used for any other purpose. 84 */ 85 static const uchar daysInMonth[0x13] = { 86 0x00, 0x31, 0x29, 0x31, 0x30, 0x31, 0x30, 0x31, 87 0x31, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 88 0x31, 0x30, 0x31 89 }; 90 static uchar data[8]; 91 uchar min, date, month, years; 92 93 rtc_dump("begin validate"); 94 if (i2c_read(CONFIG_SYS_I2C_RTC_ADDR, 0, 1, data, sizeof(data))) { 95 printf("I2C read failed in rtc_validate()\n"); 96 return 0; 97 } 98 /* 99 * If the OUT bit is "1", there has been a loss of power, so stop the 100 * oscillator so it can be "kick-started" as per data sheet. 101 */ 102 if (0x00 != (data[RTC_CTRL] & 0x80)) { 103 printf("M41T60 RTC clock lost power.\n"); 104 data[RTC_SEC] = 0x80; 105 if (i2c_write(CONFIG_SYS_I2C_RTC_ADDR, RTC_SEC, 1, data, 1)) { 106 printf("I2C write failed in rtc_validate()\n"); 107 return 0; 108 } 109 } 110 /* 111 * If the oscillator is stopped or the date is invalid, then reset the 112 * OUT bit to "0", reset the date registers, and start the oscillator. 113 */ 114 min = data[RTC_MIN] & 0x7F; 115 date = data[RTC_DATE]; 116 month = data[RTC_MONTH] & 0x3F; 117 years = data[RTC_YEAR]; 118 if (0x59 < data[RTC_SEC] || 0x09 < (data[RTC_SEC] & 0x0F) || 119 0x59 < min || 0x09 < (min & 0x0F) || 120 0x23 < data[RTC_HOUR] || 0x09 < (data[RTC_HOUR] & 0x0F) || 121 0x07 < data[RTC_DAY] || 0x00 == data[RTC_DAY] || 122 0x12 < month || 123 0x99 < years || 0x09 < (years & 0x0F) || 124 daysInMonth[month] < date || 0x09 < (date & 0x0F) || 0x00 == date || 125 (0x29 == date && 0x02 == month && 126 ((0x00 != (years & 0x03)) || 127 (0x00 == years && 0x00 != (data[RTC_MONTH] & 0xC0))))) { 128 printf("Resetting M41T60 RTC clock.\n"); 129 /* 130 * Set to 00:00:00 1900-01-01 (Monday) 131 */ 132 data[RTC_SEC] = 0x00; 133 data[RTC_MIN] &= 0x80; /* preserve OFIE bit */ 134 data[RTC_HOUR] = 0x00; 135 data[RTC_DAY] = 0x02; 136 data[RTC_DATE] = 0x01; 137 data[RTC_MONTH] = 0xC1; 138 data[RTC_YEAR] = 0x00; 139 data[RTC_CTRL] &= 0x7F; /* reset OUT bit */ 140 141 if (i2c_write(CONFIG_SYS_I2C_RTC_ADDR, 0, 1, data, sizeof(data))) { 142 printf("I2C write failed in rtc_validate()\n"); 143 return 0; 144 } 145 } 146 return data; 147 } 148 149 int rtc_get(struct rtc_time *tmp) 150 { 151 uchar const *const data = rtc_validate(); 152 153 if (!data) 154 return -1; 155 156 tmp->tm_sec = bcd2bin(data[RTC_SEC] & 0x7F); 157 tmp->tm_min = bcd2bin(data[RTC_MIN] & 0x7F); 158 tmp->tm_hour = bcd2bin(data[RTC_HOUR] & 0x3F); 159 tmp->tm_mday = bcd2bin(data[RTC_DATE] & 0x3F); 160 tmp->tm_mon = bcd2bin(data[RTC_MONTH] & 0x1F); 161 tmp->tm_year = cb2year(data[RTC_MONTH] >> 6) + bcd2bin(data[RTC_YEAR]); 162 tmp->tm_wday = bcd2bin(data[RTC_DAY] & 0x07) - 1; 163 tmp->tm_yday = 0; 164 tmp->tm_isdst = 0; 165 166 debug("Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", 167 tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, 168 tmp->tm_hour, tmp->tm_min, tmp->tm_sec); 169 170 return 0; 171 } 172 173 int rtc_set(struct rtc_time *tmp) 174 { 175 uchar *const data = rtc_validate(); 176 177 if (!data) 178 return -1; 179 180 debug("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", 181 tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, 182 tmp->tm_hour, tmp->tm_min, tmp->tm_sec); 183 184 data[RTC_SEC] = (data[RTC_SEC] & 0x80) | (bin2bcd(tmp->tm_sec) & 0x7F); 185 data[RTC_MIN] = (data[RTC_MIN] & 0X80) | (bin2bcd(tmp->tm_min) & 0X7F); 186 data[RTC_HOUR] = bin2bcd(tmp->tm_hour) & 0x3F; 187 data[RTC_DATE] = bin2bcd(tmp->tm_mday) & 0x3F; 188 data[RTC_MONTH] = bin2bcd(tmp->tm_mon) & 0x1F; 189 data[RTC_YEAR] = bin2bcd(tmp->tm_year % 100); 190 data[RTC_MONTH] |= year2cb(tmp->tm_year) << 6; 191 data[RTC_DAY] = bin2bcd(tmp->tm_wday + 1) & 0x07; 192 if (i2c_write(CONFIG_SYS_I2C_RTC_ADDR, 0, 1, data, RTC_REG_CNT)) { 193 printf("I2C write failed in rtc_set()\n"); 194 return -1; 195 } 196 197 return 0; 198 } 199 200 void rtc_reset(void) 201 { 202 uchar *const data = rtc_validate(); 203 char const *const s = getenv("rtccal"); 204 205 if (!data) 206 return; 207 208 rtc_dump("begin reset"); 209 /* 210 * If environmental variable "rtccal" is present, it must be a hex value 211 * between 0x00 and 0x3F, inclusive. The five least-significan bits 212 * represent the calibration magnitude, and the sixth bit the sign bit. 213 * If these do not match the contents of the hardware register, that 214 * register is updated. The value 0x00 imples no correction. Consult 215 * the M41T60 documentation for further details. 216 */ 217 if (s) { 218 unsigned long const l = simple_strtoul(s, 0, 16); 219 220 if (l <= 0x3F) { 221 if ((data[RTC_CTRL] & 0x3F) != l) { 222 printf("Setting RTC calibration to 0x%02lX\n", 223 l); 224 data[RTC_CTRL] &= 0xC0; 225 data[RTC_CTRL] |= (uchar) l; 226 } 227 } else 228 printf("environment parameter \"rtccal\" not valid: " 229 "ignoring\n"); 230 } 231 /* 232 * Turn off frequency test. 233 */ 234 data[RTC_CTRL] &= 0xBF; 235 if (i2c_write(CONFIG_SYS_I2C_RTC_ADDR, RTC_CTRL, 1, data + RTC_CTRL, 1)) { 236 printf("I2C write failed in rtc_reset()\n"); 237 return; 238 } 239 rtc_dump("end reset"); 240 } 241 #endif /* CONFIG_RTC_M41T60 && CONFIG_SYS_I2C_RTC_ADDR && CONFIG_CMD_DATE */ 242