18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 1991, 1992, 1995 Linus Torvalds 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Adapted for PowerPC (PReP) by Gary Thomas 68c2ecf20Sopenharmony_ci * Modified by Cort Dougan (cort@cs.nmt.edu). 78c2ecf20Sopenharmony_ci * Copied and modified from arch/i386/kernel/time.c 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci#include <linux/errno.h> 118c2ecf20Sopenharmony_ci#include <linux/sched.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/param.h> 148c2ecf20Sopenharmony_ci#include <linux/string.h> 158c2ecf20Sopenharmony_ci#include <linux/mm.h> 168c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 178c2ecf20Sopenharmony_ci#include <linux/time.h> 188c2ecf20Sopenharmony_ci#include <linux/timex.h> 198c2ecf20Sopenharmony_ci#include <linux/kernel_stat.h> 208c2ecf20Sopenharmony_ci#include <linux/mc146818rtc.h> 218c2ecf20Sopenharmony_ci#include <linux/init.h> 228c2ecf20Sopenharmony_ci#include <linux/bcd.h> 238c2ecf20Sopenharmony_ci#include <linux/ioport.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <asm/io.h> 268c2ecf20Sopenharmony_ci#include <asm/nvram.h> 278c2ecf20Sopenharmony_ci#include <asm/prom.h> 288c2ecf20Sopenharmony_ci#include <asm/sections.h> 298c2ecf20Sopenharmony_ci#include <asm/time.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include <platforms/chrp/chrp.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ciextern spinlock_t rtc_lock; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define NVRAM_AS0 0x74 368c2ecf20Sopenharmony_ci#define NVRAM_AS1 0x75 378c2ecf20Sopenharmony_ci#define NVRAM_DATA 0x77 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic int nvram_as1 = NVRAM_AS1; 408c2ecf20Sopenharmony_cistatic int nvram_as0 = NVRAM_AS0; 418c2ecf20Sopenharmony_cistatic int nvram_data = NVRAM_DATA; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cilong __init chrp_time_init(void) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct device_node *rtcs; 468c2ecf20Sopenharmony_ci struct resource r; 478c2ecf20Sopenharmony_ci int base; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci rtcs = of_find_compatible_node(NULL, "rtc", "pnpPNP,b00"); 508c2ecf20Sopenharmony_ci if (rtcs == NULL) 518c2ecf20Sopenharmony_ci rtcs = of_find_compatible_node(NULL, "rtc", "ds1385-rtc"); 528c2ecf20Sopenharmony_ci if (rtcs == NULL) 538c2ecf20Sopenharmony_ci return 0; 548c2ecf20Sopenharmony_ci if (of_address_to_resource(rtcs, 0, &r)) { 558c2ecf20Sopenharmony_ci of_node_put(rtcs); 568c2ecf20Sopenharmony_ci return 0; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci of_node_put(rtcs); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci base = r.start; 618c2ecf20Sopenharmony_ci nvram_as1 = 0; 628c2ecf20Sopenharmony_ci nvram_as0 = base; 638c2ecf20Sopenharmony_ci nvram_data = base + 1; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci return 0; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic int chrp_cmos_clock_read(int addr) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci if (nvram_as1 != 0) 718c2ecf20Sopenharmony_ci outb(addr>>8, nvram_as1); 728c2ecf20Sopenharmony_ci outb(addr, nvram_as0); 738c2ecf20Sopenharmony_ci return (inb(nvram_data)); 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic void chrp_cmos_clock_write(unsigned long val, int addr) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci if (nvram_as1 != 0) 798c2ecf20Sopenharmony_ci outb(addr>>8, nvram_as1); 808c2ecf20Sopenharmony_ci outb(addr, nvram_as0); 818c2ecf20Sopenharmony_ci outb(val, nvram_data); 828c2ecf20Sopenharmony_ci return; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/* 868c2ecf20Sopenharmony_ci * Set the hardware clock. -- Cort 878c2ecf20Sopenharmony_ci */ 888c2ecf20Sopenharmony_ciint chrp_set_rtc_time(struct rtc_time *tmarg) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci unsigned char save_control, save_freq_select; 918c2ecf20Sopenharmony_ci struct rtc_time tm = *tmarg; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci spin_lock(&rtc_lock); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci save_control = chrp_cmos_clock_read(RTC_CONTROL); /* tell the clock it's being set */ 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci chrp_cmos_clock_write((save_control|RTC_SET), RTC_CONTROL); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci save_freq_select = chrp_cmos_clock_read(RTC_FREQ_SELECT); /* stop and reset prescaler */ 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci chrp_cmos_clock_write((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { 1048c2ecf20Sopenharmony_ci tm.tm_sec = bin2bcd(tm.tm_sec); 1058c2ecf20Sopenharmony_ci tm.tm_min = bin2bcd(tm.tm_min); 1068c2ecf20Sopenharmony_ci tm.tm_hour = bin2bcd(tm.tm_hour); 1078c2ecf20Sopenharmony_ci tm.tm_mon = bin2bcd(tm.tm_mon); 1088c2ecf20Sopenharmony_ci tm.tm_mday = bin2bcd(tm.tm_mday); 1098c2ecf20Sopenharmony_ci tm.tm_year = bin2bcd(tm.tm_year); 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci chrp_cmos_clock_write(tm.tm_sec,RTC_SECONDS); 1128c2ecf20Sopenharmony_ci chrp_cmos_clock_write(tm.tm_min,RTC_MINUTES); 1138c2ecf20Sopenharmony_ci chrp_cmos_clock_write(tm.tm_hour,RTC_HOURS); 1148c2ecf20Sopenharmony_ci chrp_cmos_clock_write(tm.tm_mon,RTC_MONTH); 1158c2ecf20Sopenharmony_ci chrp_cmos_clock_write(tm.tm_mday,RTC_DAY_OF_MONTH); 1168c2ecf20Sopenharmony_ci chrp_cmos_clock_write(tm.tm_year,RTC_YEAR); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci /* The following flags have to be released exactly in this order, 1198c2ecf20Sopenharmony_ci * otherwise the DS12887 (popular MC146818A clone with integrated 1208c2ecf20Sopenharmony_ci * battery and quartz) will not reset the oscillator and will not 1218c2ecf20Sopenharmony_ci * update precisely 500 ms later. You won't find this mentioned in 1228c2ecf20Sopenharmony_ci * the Dallas Semiconductor data sheets, but who believes data 1238c2ecf20Sopenharmony_ci * sheets anyway ... -- Markus Kuhn 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_ci chrp_cmos_clock_write(save_control, RTC_CONTROL); 1268c2ecf20Sopenharmony_ci chrp_cmos_clock_write(save_freq_select, RTC_FREQ_SELECT); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci spin_unlock(&rtc_lock); 1298c2ecf20Sopenharmony_ci return 0; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_civoid chrp_get_rtc_time(struct rtc_time *tm) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci unsigned int year, mon, day, hour, min, sec; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci do { 1378c2ecf20Sopenharmony_ci sec = chrp_cmos_clock_read(RTC_SECONDS); 1388c2ecf20Sopenharmony_ci min = chrp_cmos_clock_read(RTC_MINUTES); 1398c2ecf20Sopenharmony_ci hour = chrp_cmos_clock_read(RTC_HOURS); 1408c2ecf20Sopenharmony_ci day = chrp_cmos_clock_read(RTC_DAY_OF_MONTH); 1418c2ecf20Sopenharmony_ci mon = chrp_cmos_clock_read(RTC_MONTH); 1428c2ecf20Sopenharmony_ci year = chrp_cmos_clock_read(RTC_YEAR); 1438c2ecf20Sopenharmony_ci } while (sec != chrp_cmos_clock_read(RTC_SECONDS)); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (!(chrp_cmos_clock_read(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { 1468c2ecf20Sopenharmony_ci sec = bcd2bin(sec); 1478c2ecf20Sopenharmony_ci min = bcd2bin(min); 1488c2ecf20Sopenharmony_ci hour = bcd2bin(hour); 1498c2ecf20Sopenharmony_ci day = bcd2bin(day); 1508c2ecf20Sopenharmony_ci mon = bcd2bin(mon); 1518c2ecf20Sopenharmony_ci year = bcd2bin(year); 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci if (year < 70) 1548c2ecf20Sopenharmony_ci year += 100; 1558c2ecf20Sopenharmony_ci tm->tm_sec = sec; 1568c2ecf20Sopenharmony_ci tm->tm_min = min; 1578c2ecf20Sopenharmony_ci tm->tm_hour = hour; 1588c2ecf20Sopenharmony_ci tm->tm_mday = day; 1598c2ecf20Sopenharmony_ci tm->tm_mon = mon; 1608c2ecf20Sopenharmony_ci tm->tm_year = year; 1618c2ecf20Sopenharmony_ci} 162