162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 1991, 1992, 1995 Linus Torvalds 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Adapted for PowerPC (PReP) by Gary Thomas 662306a36Sopenharmony_ci * Modified by Cort Dougan (cort@cs.nmt.edu). 762306a36Sopenharmony_ci * Copied and modified from arch/i386/kernel/time.c 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci#include <linux/errno.h> 1162306a36Sopenharmony_ci#include <linux/sched.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/param.h> 1462306a36Sopenharmony_ci#include <linux/string.h> 1562306a36Sopenharmony_ci#include <linux/mm.h> 1662306a36Sopenharmony_ci#include <linux/interrupt.h> 1762306a36Sopenharmony_ci#include <linux/time.h> 1862306a36Sopenharmony_ci#include <linux/timex.h> 1962306a36Sopenharmony_ci#include <linux/kernel_stat.h> 2062306a36Sopenharmony_ci#include <linux/mc146818rtc.h> 2162306a36Sopenharmony_ci#include <linux/init.h> 2262306a36Sopenharmony_ci#include <linux/bcd.h> 2362306a36Sopenharmony_ci#include <linux/ioport.h> 2462306a36Sopenharmony_ci#include <linux/of_address.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include <asm/io.h> 2762306a36Sopenharmony_ci#include <asm/nvram.h> 2862306a36Sopenharmony_ci#include <asm/sections.h> 2962306a36Sopenharmony_ci#include <asm/time.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include <platforms/chrp/chrp.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define NVRAM_AS0 0x74 3462306a36Sopenharmony_ci#define NVRAM_AS1 0x75 3562306a36Sopenharmony_ci#define NVRAM_DATA 0x77 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic int nvram_as1 = NVRAM_AS1; 3862306a36Sopenharmony_cistatic int nvram_as0 = NVRAM_AS0; 3962306a36Sopenharmony_cistatic int nvram_data = NVRAM_DATA; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cilong __init chrp_time_init(void) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci struct device_node *rtcs; 4462306a36Sopenharmony_ci struct resource r; 4562306a36Sopenharmony_ci int base; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci rtcs = of_find_compatible_node(NULL, "rtc", "pnpPNP,b00"); 4862306a36Sopenharmony_ci if (rtcs == NULL) 4962306a36Sopenharmony_ci rtcs = of_find_compatible_node(NULL, "rtc", "ds1385-rtc"); 5062306a36Sopenharmony_ci if (rtcs == NULL) 5162306a36Sopenharmony_ci return 0; 5262306a36Sopenharmony_ci if (of_address_to_resource(rtcs, 0, &r)) { 5362306a36Sopenharmony_ci of_node_put(rtcs); 5462306a36Sopenharmony_ci return 0; 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci of_node_put(rtcs); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci base = r.start; 5962306a36Sopenharmony_ci nvram_as1 = 0; 6062306a36Sopenharmony_ci nvram_as0 = base; 6162306a36Sopenharmony_ci nvram_data = base + 1; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci return 0; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic int chrp_cmos_clock_read(int addr) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci if (nvram_as1 != 0) 6962306a36Sopenharmony_ci outb(addr>>8, nvram_as1); 7062306a36Sopenharmony_ci outb(addr, nvram_as0); 7162306a36Sopenharmony_ci return (inb(nvram_data)); 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic void chrp_cmos_clock_write(unsigned long val, int addr) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci if (nvram_as1 != 0) 7762306a36Sopenharmony_ci outb(addr>>8, nvram_as1); 7862306a36Sopenharmony_ci outb(addr, nvram_as0); 7962306a36Sopenharmony_ci outb(val, nvram_data); 8062306a36Sopenharmony_ci return; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/* 8462306a36Sopenharmony_ci * Set the hardware clock. -- Cort 8562306a36Sopenharmony_ci */ 8662306a36Sopenharmony_ciint chrp_set_rtc_time(struct rtc_time *tmarg) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci unsigned char save_control, save_freq_select; 8962306a36Sopenharmony_ci struct rtc_time tm = *tmarg; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci spin_lock(&rtc_lock); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci save_control = chrp_cmos_clock_read(RTC_CONTROL); /* tell the clock it's being set */ 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci chrp_cmos_clock_write((save_control|RTC_SET), RTC_CONTROL); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci save_freq_select = chrp_cmos_clock_read(RTC_FREQ_SELECT); /* stop and reset prescaler */ 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci chrp_cmos_clock_write((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { 10262306a36Sopenharmony_ci tm.tm_sec = bin2bcd(tm.tm_sec); 10362306a36Sopenharmony_ci tm.tm_min = bin2bcd(tm.tm_min); 10462306a36Sopenharmony_ci tm.tm_hour = bin2bcd(tm.tm_hour); 10562306a36Sopenharmony_ci tm.tm_mon = bin2bcd(tm.tm_mon); 10662306a36Sopenharmony_ci tm.tm_mday = bin2bcd(tm.tm_mday); 10762306a36Sopenharmony_ci tm.tm_year = bin2bcd(tm.tm_year); 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci chrp_cmos_clock_write(tm.tm_sec,RTC_SECONDS); 11062306a36Sopenharmony_ci chrp_cmos_clock_write(tm.tm_min,RTC_MINUTES); 11162306a36Sopenharmony_ci chrp_cmos_clock_write(tm.tm_hour,RTC_HOURS); 11262306a36Sopenharmony_ci chrp_cmos_clock_write(tm.tm_mon,RTC_MONTH); 11362306a36Sopenharmony_ci chrp_cmos_clock_write(tm.tm_mday,RTC_DAY_OF_MONTH); 11462306a36Sopenharmony_ci chrp_cmos_clock_write(tm.tm_year,RTC_YEAR); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci /* The following flags have to be released exactly in this order, 11762306a36Sopenharmony_ci * otherwise the DS12887 (popular MC146818A clone with integrated 11862306a36Sopenharmony_ci * battery and quartz) will not reset the oscillator and will not 11962306a36Sopenharmony_ci * update precisely 500 ms later. You won't find this mentioned in 12062306a36Sopenharmony_ci * the Dallas Semiconductor data sheets, but who believes data 12162306a36Sopenharmony_ci * sheets anyway ... -- Markus Kuhn 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_ci chrp_cmos_clock_write(save_control, RTC_CONTROL); 12462306a36Sopenharmony_ci chrp_cmos_clock_write(save_freq_select, RTC_FREQ_SELECT); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci spin_unlock(&rtc_lock); 12762306a36Sopenharmony_ci return 0; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_civoid chrp_get_rtc_time(struct rtc_time *tm) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci unsigned int year, mon, day, hour, min, sec; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci do { 13562306a36Sopenharmony_ci sec = chrp_cmos_clock_read(RTC_SECONDS); 13662306a36Sopenharmony_ci min = chrp_cmos_clock_read(RTC_MINUTES); 13762306a36Sopenharmony_ci hour = chrp_cmos_clock_read(RTC_HOURS); 13862306a36Sopenharmony_ci day = chrp_cmos_clock_read(RTC_DAY_OF_MONTH); 13962306a36Sopenharmony_ci mon = chrp_cmos_clock_read(RTC_MONTH); 14062306a36Sopenharmony_ci year = chrp_cmos_clock_read(RTC_YEAR); 14162306a36Sopenharmony_ci } while (sec != chrp_cmos_clock_read(RTC_SECONDS)); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (!(chrp_cmos_clock_read(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { 14462306a36Sopenharmony_ci sec = bcd2bin(sec); 14562306a36Sopenharmony_ci min = bcd2bin(min); 14662306a36Sopenharmony_ci hour = bcd2bin(hour); 14762306a36Sopenharmony_ci day = bcd2bin(day); 14862306a36Sopenharmony_ci mon = bcd2bin(mon); 14962306a36Sopenharmony_ci year = bcd2bin(year); 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci if (year < 70) 15262306a36Sopenharmony_ci year += 100; 15362306a36Sopenharmony_ci tm->tm_sec = sec; 15462306a36Sopenharmony_ci tm->tm_min = min; 15562306a36Sopenharmony_ci tm->tm_hour = hour; 15662306a36Sopenharmony_ci tm->tm_mday = day; 15762306a36Sopenharmony_ci tm->tm_mon = mon; 15862306a36Sopenharmony_ci tm->tm_year = year; 15962306a36Sopenharmony_ci} 160