1/* 2 * SGS M48-T59Y TOD/NVRAM Driver 3 * 4 * (C) Copyright 2000 5 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. 6 * 7 * (C) Copyright 1999, by Curt McDowell, 08-06-99, Broadcom Corp. 8 * 9 * (C) Copyright 2001, James Dougherty, 07/18/01, Broadcom Corp. 10 * 11 * See file CREDITS for list of people who contributed to this 12 * project. 13 * 14 * This program is free software; you can redistribute it and/or 15 * modify it under the terms of the GNU General Public License as 16 * published by the Free Software Foundation; either version 2 of 17 * the License, or (at your option) any later version. 18 * 19 * This program is distributed in the hope that it will be useful, 20 * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 * GNU General Public License for more details. 23 * 24 * You should have received a copy of the GNU General Public License 25 * along with this program; if not, write to the Free Software 26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 27 * MA 02111-1307 USA 28 */ 29 30/* 31 * SGS M48-T59Y TOD/NVRAM Driver 32 * 33 * The SGS M48 an 8K NVRAM starting at offset M48_BASE_ADDR and 34 * continuing for 8176 bytes. After that starts the Time-Of-Day (TOD) 35 * registers which are used to set/get the internal date/time functions. 36 * 37 * This module implements Y2K compliance by taking full year numbers 38 * and translating back and forth from the TOD 2-digit year. 39 * 40 * NOTE: for proper interaction with an operating system, the TOD should 41 * be used to store Universal Coordinated Time (GMT) and timezone 42 * conversions should be used. 43 * 44 * Here is a diagram of the memory layout: 45 * 46 * +---------------------------------------------+ 0xffe0a000 47 * | Non-volatile memory | . 48 * | | . 49 * | (8176 bytes of Non-volatile memory) | . 50 * | | . 51 * +---------------------------------------------+ 0xffe0bff0 52 * | Flags | 53 * +---------------------------------------------+ 0xffe0bff1 54 * | Unused | 55 * +---------------------------------------------+ 0xffe0bff2 56 * | Alarm Seconds | 57 * +---------------------------------------------+ 0xffe0bff3 58 * | Alarm Minutes | 59 * +---------------------------------------------+ 0xffe0bff4 60 * | Alarm Date | 61 * +---------------------------------------------+ 0xffe0bff5 62 * | Interrupts | 63 * +---------------------------------------------+ 0xffe0bff6 64 * | WatchDog | 65 * +---------------------------------------------+ 0xffe0bff7 66 * | Calibration | 67 * +---------------------------------------------+ 0xffe0bff8 68 * | Seconds | 69 * +---------------------------------------------+ 0xffe0bff9 70 * | Minutes | 71 * +---------------------------------------------+ 0xffe0bffa 72 * | Hours | 73 * +---------------------------------------------+ 0xffe0bffb 74 * | Day | 75 * +---------------------------------------------+ 0xffe0bffc 76 * | Date | 77 * +---------------------------------------------+ 0xffe0bffd 78 * | Month | 79 * +---------------------------------------------+ 0xffe0bffe 80 * | Year (2 digits only) | 81 * +---------------------------------------------+ 0xffe0bfff 82 */ 83#include <common.h> 84#include <rtc.h> 85#include "bmw.h" 86 87/* 88 * Imported from mousse.h: 89 * 90 * TOD_REG_BASE Base of m48t59y TOD registers 91 * SYS_TOD_UNPROTECT() Disable NVRAM write protect 92 * SYS_TOD_PROTECT() Re-enable NVRAM write protect 93 */ 94 95#define YEAR 0xf 96#define MONTH 0xe 97#define DAY 0xd 98#define DAY_OF_WEEK 0xc 99#define HOUR 0xb 100#define MINUTE 0xa 101#define SECOND 0x9 102#define CONTROL 0x8 103#define WATCH 0x7 104#define INTCTL 0x6 105#define WD_DATE 0x5 106#define WD_HOUR 0x4 107#define WD_MIN 0x3 108#define WD_SEC 0x2 109#define _UNUSED 0x1 110#define FLAGS 0x0 111 112#define M48_ADDR ((volatile unsigned char *) TOD_REG_BASE) 113 114int m48_tod_init(void) 115{ 116 SYS_TOD_UNPROTECT(); 117 118 M48_ADDR[CONTROL] = 0; 119 M48_ADDR[WATCH] = 0; 120 M48_ADDR[INTCTL] = 0; 121 122 /* 123 * If the oscillator is currently stopped (as on a new part shipped 124 * from the factory), start it running. 125 * 126 * Here is an example of the TOD bytes on a brand new M48T59Y part: 127 * 00 00 00 00 00 00 00 00 00 88 8c c3 bf c8 f5 01 128 */ 129 130 if (M48_ADDR[SECOND] & 0x80) 131 M48_ADDR[SECOND] = 0; 132 133 /* Is battery low */ 134 if ( M48_ADDR[FLAGS] & 0x10) { 135 printf("NOTICE: Battery low on Real-Time Clock (replace SNAPHAT).\n"); 136 } 137 138 SYS_TOD_PROTECT(); 139 140 return 0; 141} 142 143/* 144 * m48_tod_set 145 */ 146 147static int to_bcd(int value) 148{ 149 return value / 10 * 16 + value % 10; 150} 151 152static int from_bcd(int value) 153{ 154 return value / 16 * 10 + value % 16; 155} 156 157static int day_of_week(int y, int m, int d) /* 0-6 ==> Sun-Sat */ 158{ 159 static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}; 160 y -= m < 3; 161 return (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7; 162} 163 164/* 165 * Note: the TOD should store the current GMT 166 */ 167 168int m48_tod_set(int year, /* 1980-2079 */ 169 int month, /* 01-12 */ 170 int day, /* 01-31 */ 171 int hour, /* 00-23 */ 172 int minute, /* 00-59 */ 173 int second) /* 00-59 */ 174 175{ 176 SYS_TOD_UNPROTECT(); 177 178 M48_ADDR[CONTROL] |= 0x80; /* Set WRITE bit */ 179 180 M48_ADDR[YEAR] = to_bcd(year % 100); 181 M48_ADDR[MONTH] = to_bcd(month); 182 M48_ADDR[DAY] = to_bcd(day); 183 M48_ADDR[DAY_OF_WEEK] = day_of_week(year, month, day) + 1; 184 M48_ADDR[HOUR] = to_bcd(hour); 185 M48_ADDR[MINUTE] = to_bcd(minute); 186 M48_ADDR[SECOND] = to_bcd(second); 187 188 M48_ADDR[CONTROL] &= ~0x80; /* Clear WRITE bit */ 189 190 SYS_TOD_PROTECT(); 191 192 return 0; 193} 194 195/* 196 * Note: the TOD should store the current GMT 197 */ 198 199int m48_tod_get(int *year, /* 1980-2079 */ 200 int *month, /* 01-12 */ 201 int *day, /* 01-31 */ 202 int *hour, /* 00-23 */ 203 int *minute, /* 00-59 */ 204 int *second) /* 00-59 */ 205{ 206 int y; 207 208 SYS_TOD_UNPROTECT(); 209 210 M48_ADDR[CONTROL] |= 0x40; /* Set READ bit */ 211 212 y = from_bcd(M48_ADDR[YEAR]); 213 *year = y < 80 ? 2000 + y : 1900 + y; 214 *month = from_bcd(M48_ADDR[MONTH]); 215 *day = from_bcd(M48_ADDR[DAY]); 216 /* day_of_week = M48_ADDR[DAY_OF_WEEK] & 0xf; */ 217 *hour = from_bcd(M48_ADDR[HOUR]); 218 *minute = from_bcd(M48_ADDR[MINUTE]); 219 *second = from_bcd(M48_ADDR[SECOND] & 0x7f); 220 221 M48_ADDR[CONTROL] &= ~0x40; /* Clear READ bit */ 222 223 SYS_TOD_PROTECT(); 224 225 return 0; 226} 227 228int m48_tod_get_second(void) 229{ 230 return from_bcd(M48_ADDR[SECOND] & 0x7f); 231} 232 233/* 234 * Watchdog function 235 * 236 * If usec is 0, the watchdog timer is disarmed. 237 * 238 * If usec is non-zero, the watchdog timer is armed (or re-armed) for 239 * approximately usec microseconds (if the exact requested usec is 240 * not supported by the chip, the next higher available value is used). 241 * 242 * Minimum watchdog timeout = 62500 usec 243 * Maximum watchdog timeout = 124 sec (124000000 usec) 244 */ 245 246void m48_watchdog_arm(int usec) 247{ 248 int mpy, res; 249 250 SYS_TOD_UNPROTECT(); 251 252 if (usec == 0) { 253 res = 0; 254 mpy = 0; 255 } else if (usec < 2000000) { /* Resolution: 1/16s if below 2s */ 256 res = 0; 257 mpy = (usec + 62499) / 62500; 258 } else if (usec < 8000000) { /* Resolution: 1/4s if below 8s */ 259 res = 1; 260 mpy = (usec + 249999) / 250000; 261 } else if (usec < 32000000) { /* Resolution: 1s if below 32s */ 262 res = 2; 263 mpy = (usec + 999999) / 1000000; 264 } else { /* Resolution: 4s up to 124s */ 265 res = 3; 266 mpy = (usec + 3999999) / 4000000; 267 if (mpy > 31) 268 mpy = 31; 269 } 270 271 M48_ADDR[WATCH] = (0x80 | /* Steer to RST signal (IRQ = N/C) */ 272 mpy << 2 | 273 res); 274 275 SYS_TOD_PROTECT(); 276} 277 278/* 279 * U-Boot RTC support. 280 */ 281int 282rtc_get( struct rtc_time *tmp ) 283{ 284 m48_tod_get(&tmp->tm_year, 285 &tmp->tm_mon, 286 &tmp->tm_mday, 287 &tmp->tm_hour, 288 &tmp->tm_min, 289 &tmp->tm_sec); 290 tmp->tm_yday = 0; 291 tmp->tm_isdst= 0; 292 293#ifdef RTC_DEBUG 294 printf( "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", 295 tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, 296 tmp->tm_hour, tmp->tm_min, tmp->tm_sec ); 297#endif 298 299 return 0; 300} 301 302int rtc_set( struct rtc_time *tmp ) 303{ 304 m48_tod_set(tmp->tm_year, /* 1980-2079 */ 305 tmp->tm_mon, /* 01-12 */ 306 tmp->tm_mday, /* 01-31 */ 307 tmp->tm_hour, /* 00-23 */ 308 tmp->tm_min, /* 00-59 */ 309 tmp->tm_sec); /* 00-59 */ 310 311#ifdef RTC_DEBUG 312 printf( "Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", 313 tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, 314 tmp->tm_hour, tmp->tm_min, tmp->tm_sec); 315#endif 316 317 return 0; 318} 319 320void 321rtc_reset (void) 322{ 323 m48_tod_init(); 324} 325