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