1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * (C) Copyright 2001 4 * Denis Peter MPL AG Switzerland. d.peter@mpl.ch 5 */ 6 7/* 8 * Date & Time support for the MC146818 (PIXX4) RTC 9 */ 10 11#include <command.h> 12#include <dm.h> 13#include <rtc.h> 14 15#if defined(CONFIG_X86) || defined(CONFIG_TARGET_MALTA) 16#include <asm/io.h> 17#define in8(p) inb(p) 18#define out8(p, v) outb(v, p) 19#endif 20 21/* Set this to 1 to clear the CMOS RAM */ 22#define CLEAR_CMOS 0 23 24#define RTC_PORT_MC146818 0x70 25#define RTC_SECONDS 0x00 26#define RTC_SECONDS_ALARM 0x01 27#define RTC_MINUTES 0x02 28#define RTC_MINUTES_ALARM 0x03 29#define RTC_HOURS 0x04 30#define RTC_HOURS_ALARM 0x05 31#define RTC_DAY_OF_WEEK 0x06 32#define RTC_DATE_OF_MONTH 0x07 33#define RTC_MONTH 0x08 34#define RTC_YEAR 0x09 35#define RTC_CONFIG_A 0x0a 36#define RTC_CONFIG_B 0x0b 37#define RTC_CONFIG_C 0x0c 38#define RTC_CONFIG_D 0x0d 39#define RTC_REG_SIZE 0x80 40 41#define RTC_CONFIG_A_REF_CLCK_32KHZ (1 << 5) 42#define RTC_CONFIG_A_RATE_1024HZ 6 43 44#define RTC_CONFIG_B_24H (1 << 1) 45 46#define RTC_CONFIG_D_VALID_RAM_AND_TIME 0x80 47 48static int mc146818_read8(int reg) 49{ 50#ifdef CONFIG_SYS_RTC_REG_BASE_ADDR 51 return in8(CONFIG_SYS_RTC_REG_BASE_ADDR + reg); 52#else 53 int ofs = 0; 54 55 if (reg >= 128) { 56 ofs = 2; 57 reg -= 128; 58 } 59 out8(RTC_PORT_MC146818 + ofs, reg); 60 61 return in8(RTC_PORT_MC146818 + ofs + 1); 62#endif 63} 64 65static void mc146818_write8(int reg, uchar val) 66{ 67#ifdef CONFIG_SYS_RTC_REG_BASE_ADDR 68 out8(CONFIG_SYS_RTC_REG_BASE_ADDR + reg, val); 69#else 70 int ofs = 0; 71 72 if (reg >= 128) { 73 ofs = 2; 74 reg -= 128; 75 } 76 out8(RTC_PORT_MC146818 + ofs, reg); 77 out8(RTC_PORT_MC146818 + ofs + 1, val); 78#endif 79} 80 81static int mc146818_get(struct rtc_time *tmp) 82{ 83 uchar sec, min, hour, mday, wday __attribute__((unused)),mon, year; 84 85 /* here check if rtc can be accessed */ 86 while ((mc146818_read8(RTC_CONFIG_A) & 0x80) == 0x80) 87 ; 88 89 sec = mc146818_read8(RTC_SECONDS); 90 min = mc146818_read8(RTC_MINUTES); 91 hour = mc146818_read8(RTC_HOURS); 92 mday = mc146818_read8(RTC_DATE_OF_MONTH); 93 wday = mc146818_read8(RTC_DAY_OF_WEEK); 94 mon = mc146818_read8(RTC_MONTH); 95 year = mc146818_read8(RTC_YEAR); 96#ifdef RTC_DEBUG 97 printf("Get RTC year: %02x mon/cent: %02x mday: %02x wday: %02x hr: %02x min: %02x sec: %02x\n", 98 year, mon, mday, wday, hour, min, sec); 99 printf("Alarms: mday: %02x hour: %02x min: %02x sec: %02x\n", 100 mc146818_read8(RTC_CONFIG_D) & 0x3f, 101 mc146818_read8(RTC_HOURS_ALARM), 102 mc146818_read8(RTC_MINUTES_ALARM), 103 mc146818_read8(RTC_SECONDS_ALARM)); 104#endif 105 tmp->tm_sec = bcd2bin(sec & 0x7f); 106 tmp->tm_min = bcd2bin(min & 0x7f); 107 tmp->tm_hour = bcd2bin(hour & 0x3f); 108 tmp->tm_mday = bcd2bin(mday & 0x3f); 109 tmp->tm_mon = bcd2bin(mon & 0x1f); 110 tmp->tm_year = bcd2bin(year); 111 112 if (tmp->tm_year < 70) 113 tmp->tm_year += 2000; 114 else 115 tmp->tm_year += 1900; 116 117 tmp->tm_yday = 0; 118 tmp->tm_isdst = 0; 119 /* 120 * The mc146818 only updates wday if it is non-zero, sunday is 1 121 * saturday is 7. So let's use our library routine. 122 */ 123 rtc_calc_weekday(tmp); 124#ifdef RTC_DEBUG 125 printf("Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", 126 tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, 127 tmp->tm_hour, tmp->tm_min, tmp->tm_sec); 128#endif 129 130 return 0; 131} 132 133static int mc146818_set(struct rtc_time *tmp) 134{ 135#ifdef RTC_DEBUG 136 printf("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", 137 tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, 138 tmp->tm_hour, tmp->tm_min, tmp->tm_sec); 139#endif 140 /* Disable the RTC to update the regs */ 141 mc146818_write8(RTC_CONFIG_B, 0x82); 142 143 mc146818_write8(RTC_YEAR, bin2bcd(tmp->tm_year % 100)); 144 mc146818_write8(RTC_MONTH, bin2bcd(tmp->tm_mon)); 145 /* Sunday = 1, Saturday = 7 */ 146 mc146818_write8(RTC_DAY_OF_WEEK, bin2bcd(tmp->tm_wday + 1)); 147 mc146818_write8(RTC_DATE_OF_MONTH, bin2bcd(tmp->tm_mday)); 148 mc146818_write8(RTC_HOURS, bin2bcd(tmp->tm_hour)); 149 mc146818_write8(RTC_MINUTES, bin2bcd(tmp->tm_min)); 150 mc146818_write8(RTC_SECONDS, bin2bcd(tmp->tm_sec)); 151 152 /* Enable the RTC to update the regs */ 153 mc146818_write8(RTC_CONFIG_B, 0x02); 154 155 return 0; 156} 157 158static void mc146818_reset(void) 159{ 160 /* Disable the RTC to update the regs */ 161 mc146818_write8(RTC_CONFIG_B, 0x82); 162 163 /* Normal OP */ 164 mc146818_write8(RTC_CONFIG_A, 0x20); 165 mc146818_write8(RTC_CONFIG_B, 0x00); 166 mc146818_write8(RTC_CONFIG_B, 0x00); 167 168 /* Enable the RTC to update the regs */ 169 mc146818_write8(RTC_CONFIG_B, 0x02); 170} 171 172static void mc146818_init(void) 173{ 174#if CLEAR_CMOS 175 int i; 176 177 rtc_write8(RTC_SECONDS_ALARM, 0); 178 rtc_write8(RTC_MINUTES_ALARM, 0); 179 rtc_write8(RTC_HOURS_ALARM, 0); 180 for (i = RTC_CONFIG_A; i < RTC_REG_SIZE; i++) 181 rtc_write8(i, 0); 182 printf("RTC: zeroing CMOS RAM\n"); 183#endif 184 185 /* Setup the real time clock */ 186 mc146818_write8(RTC_CONFIG_B, RTC_CONFIG_B_24H); 187 /* Setup the frequency it operates at */ 188 mc146818_write8(RTC_CONFIG_A, RTC_CONFIG_A_REF_CLCK_32KHZ | 189 RTC_CONFIG_A_RATE_1024HZ); 190 /* Ensure all reserved bits are 0 in register D */ 191 mc146818_write8(RTC_CONFIG_D, RTC_CONFIG_D_VALID_RAM_AND_TIME); 192 193 /* Clear any pending interrupts */ 194 mc146818_read8(RTC_CONFIG_C); 195} 196 197#ifdef CONFIG_DM_RTC 198 199static int rtc_mc146818_get(struct udevice *dev, struct rtc_time *time) 200{ 201 return mc146818_get(time); 202} 203 204static int rtc_mc146818_set(struct udevice *dev, const struct rtc_time *time) 205{ 206 return mc146818_set((struct rtc_time *)time); 207} 208 209static int rtc_mc146818_reset(struct udevice *dev) 210{ 211 mc146818_reset(); 212 213 return 0; 214} 215 216static int rtc_mc146818_read8(struct udevice *dev, unsigned int reg) 217{ 218 return mc146818_read8(reg); 219} 220 221static int rtc_mc146818_write8(struct udevice *dev, unsigned int reg, int val) 222{ 223 mc146818_write8(reg, val); 224 225 return 0; 226} 227 228static int rtc_mc146818_probe(struct udevice *dev) 229{ 230 mc146818_init(); 231 232 return 0; 233} 234 235static const struct rtc_ops rtc_mc146818_ops = { 236 .get = rtc_mc146818_get, 237 .set = rtc_mc146818_set, 238 .reset = rtc_mc146818_reset, 239 .read8 = rtc_mc146818_read8, 240 .write8 = rtc_mc146818_write8, 241}; 242 243static const struct udevice_id rtc_mc146818_ids[] = { 244 { .compatible = "motorola,mc146818" }, 245 { } 246}; 247 248U_BOOT_DRIVER(motorola_mc146818) = { 249 .name = "motorola_mc146818", 250 .id = UCLASS_RTC, 251 .of_match = rtc_mc146818_ids, 252 .probe = rtc_mc146818_probe, 253 .ops = &rtc_mc146818_ops, 254}; 255 256#else /* !CONFIG_DM_RTC */ 257 258int rtc_get(struct rtc_time *tmp) 259{ 260 return mc146818_get(tmp); 261} 262 263int rtc_set(struct rtc_time *tmp) 264{ 265 return mc146818_set(tmp); 266} 267 268void rtc_reset(void) 269{ 270 mc146818_reset(); 271} 272 273int rtc_read8(int reg) 274{ 275 return mc146818_read8(reg); 276} 277 278void rtc_write8(int reg, uchar val) 279{ 280 mc146818_write8(reg, val); 281} 282 283u32 rtc_read32(int reg) 284{ 285 u32 value = 0; 286 int i; 287 288 for (i = 0; i < sizeof(value); i++) 289 value |= rtc_read8(reg + i) << (i << 3); 290 291 return value; 292} 293 294void rtc_write32(int reg, u32 value) 295{ 296 int i; 297 298 for (i = 0; i < sizeof(value); i++) 299 rtc_write8(reg + i, (value >> (i << 3)) & 0xff); 300} 301 302void rtc_init(void) 303{ 304 mc146818_init(); 305} 306 307#endif /* CONFIG_DM_RTC */ 308