162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * (c) Copyright 2004 Benjamin Herrenschmidt (benh@kernel.crashing.org), 462306a36Sopenharmony_ci * IBM Corp. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#undef DEBUG 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/errno.h> 1062306a36Sopenharmony_ci#include <linux/sched.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/param.h> 1362306a36Sopenharmony_ci#include <linux/string.h> 1462306a36Sopenharmony_ci#include <linux/mm.h> 1562306a36Sopenharmony_ci#include <linux/init.h> 1662306a36Sopenharmony_ci#include <linux/time.h> 1762306a36Sopenharmony_ci#include <linux/adb.h> 1862306a36Sopenharmony_ci#include <linux/pmu.h> 1962306a36Sopenharmony_ci#include <linux/interrupt.h> 2062306a36Sopenharmony_ci#include <linux/mc146818rtc.h> 2162306a36Sopenharmony_ci#include <linux/bcd.h> 2262306a36Sopenharmony_ci#include <linux/of_address.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <asm/sections.h> 2562306a36Sopenharmony_ci#include <asm/io.h> 2662306a36Sopenharmony_ci#include <asm/machdep.h> 2762306a36Sopenharmony_ci#include <asm/time.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include "maple.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#ifdef DEBUG 3262306a36Sopenharmony_ci#define DBG(x...) printk(x) 3362306a36Sopenharmony_ci#else 3462306a36Sopenharmony_ci#define DBG(x...) 3562306a36Sopenharmony_ci#endif 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic int maple_rtc_addr; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic int maple_clock_read(int addr) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci outb_p(addr, maple_rtc_addr); 4262306a36Sopenharmony_ci return inb_p(maple_rtc_addr+1); 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic void maple_clock_write(unsigned long val, int addr) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci outb_p(addr, maple_rtc_addr); 4862306a36Sopenharmony_ci outb_p(val, maple_rtc_addr+1); 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_civoid maple_get_rtc_time(struct rtc_time *tm) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci do { 5462306a36Sopenharmony_ci tm->tm_sec = maple_clock_read(RTC_SECONDS); 5562306a36Sopenharmony_ci tm->tm_min = maple_clock_read(RTC_MINUTES); 5662306a36Sopenharmony_ci tm->tm_hour = maple_clock_read(RTC_HOURS); 5762306a36Sopenharmony_ci tm->tm_mday = maple_clock_read(RTC_DAY_OF_MONTH); 5862306a36Sopenharmony_ci tm->tm_mon = maple_clock_read(RTC_MONTH); 5962306a36Sopenharmony_ci tm->tm_year = maple_clock_read(RTC_YEAR); 6062306a36Sopenharmony_ci } while (tm->tm_sec != maple_clock_read(RTC_SECONDS)); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (!(maple_clock_read(RTC_CONTROL) & RTC_DM_BINARY) 6362306a36Sopenharmony_ci || RTC_ALWAYS_BCD) { 6462306a36Sopenharmony_ci tm->tm_sec = bcd2bin(tm->tm_sec); 6562306a36Sopenharmony_ci tm->tm_min = bcd2bin(tm->tm_min); 6662306a36Sopenharmony_ci tm->tm_hour = bcd2bin(tm->tm_hour); 6762306a36Sopenharmony_ci tm->tm_mday = bcd2bin(tm->tm_mday); 6862306a36Sopenharmony_ci tm->tm_mon = bcd2bin(tm->tm_mon); 6962306a36Sopenharmony_ci tm->tm_year = bcd2bin(tm->tm_year); 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci if ((tm->tm_year + 1900) < 1970) 7262306a36Sopenharmony_ci tm->tm_year += 100; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci tm->tm_wday = -1; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ciint maple_set_rtc_time(struct rtc_time *tm) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci unsigned char save_control, save_freq_select; 8062306a36Sopenharmony_ci int sec, min, hour, mon, mday, year; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci spin_lock(&rtc_lock); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci save_control = maple_clock_read(RTC_CONTROL); /* tell the clock it's being set */ 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci maple_clock_write((save_control|RTC_SET), RTC_CONTROL); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci save_freq_select = maple_clock_read(RTC_FREQ_SELECT); /* stop and reset prescaler */ 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci maple_clock_write((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci sec = tm->tm_sec; 9362306a36Sopenharmony_ci min = tm->tm_min; 9462306a36Sopenharmony_ci hour = tm->tm_hour; 9562306a36Sopenharmony_ci mon = tm->tm_mon; 9662306a36Sopenharmony_ci mday = tm->tm_mday; 9762306a36Sopenharmony_ci year = tm->tm_year; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { 10062306a36Sopenharmony_ci sec = bin2bcd(sec); 10162306a36Sopenharmony_ci min = bin2bcd(min); 10262306a36Sopenharmony_ci hour = bin2bcd(hour); 10362306a36Sopenharmony_ci mon = bin2bcd(mon); 10462306a36Sopenharmony_ci mday = bin2bcd(mday); 10562306a36Sopenharmony_ci year = bin2bcd(year); 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci maple_clock_write(sec, RTC_SECONDS); 10862306a36Sopenharmony_ci maple_clock_write(min, RTC_MINUTES); 10962306a36Sopenharmony_ci maple_clock_write(hour, RTC_HOURS); 11062306a36Sopenharmony_ci maple_clock_write(mon, RTC_MONTH); 11162306a36Sopenharmony_ci maple_clock_write(mday, RTC_DAY_OF_MONTH); 11262306a36Sopenharmony_ci maple_clock_write(year, RTC_YEAR); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* The following flags have to be released exactly in this order, 11562306a36Sopenharmony_ci * otherwise the DS12887 (popular MC146818A clone with integrated 11662306a36Sopenharmony_ci * battery and quartz) will not reset the oscillator and will not 11762306a36Sopenharmony_ci * update precisely 500 ms later. You won't find this mentioned in 11862306a36Sopenharmony_ci * the Dallas Semiconductor data sheets, but who believes data 11962306a36Sopenharmony_ci * sheets anyway ... -- Markus Kuhn 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_ci maple_clock_write(save_control, RTC_CONTROL); 12262306a36Sopenharmony_ci maple_clock_write(save_freq_select, RTC_FREQ_SELECT); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci spin_unlock(&rtc_lock); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci return 0; 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic struct resource rtc_iores = { 13062306a36Sopenharmony_ci .name = "rtc", 13162306a36Sopenharmony_ci .flags = IORESOURCE_IO | IORESOURCE_BUSY, 13262306a36Sopenharmony_ci}; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_citime64_t __init maple_get_boot_time(void) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci struct rtc_time tm; 13762306a36Sopenharmony_ci struct device_node *rtcs; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci rtcs = of_find_compatible_node(NULL, "rtc", "pnpPNP,b00"); 14062306a36Sopenharmony_ci if (rtcs) { 14162306a36Sopenharmony_ci struct resource r; 14262306a36Sopenharmony_ci if (of_address_to_resource(rtcs, 0, &r)) { 14362306a36Sopenharmony_ci printk(KERN_EMERG "Maple: Unable to translate RTC" 14462306a36Sopenharmony_ci " address\n"); 14562306a36Sopenharmony_ci goto bail; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci if (!(r.flags & IORESOURCE_IO)) { 14862306a36Sopenharmony_ci printk(KERN_EMERG "Maple: RTC address isn't PIO!\n"); 14962306a36Sopenharmony_ci goto bail; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci maple_rtc_addr = r.start; 15262306a36Sopenharmony_ci printk(KERN_INFO "Maple: Found RTC at IO 0x%x\n", 15362306a36Sopenharmony_ci maple_rtc_addr); 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci bail: 15662306a36Sopenharmony_ci of_node_put(rtcs); 15762306a36Sopenharmony_ci if (maple_rtc_addr == 0) { 15862306a36Sopenharmony_ci maple_rtc_addr = RTC_PORT(0); /* legacy address */ 15962306a36Sopenharmony_ci printk(KERN_INFO "Maple: No device node for RTC, assuming " 16062306a36Sopenharmony_ci "legacy address (0x%x)\n", maple_rtc_addr); 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci rtc_iores.start = maple_rtc_addr; 16462306a36Sopenharmony_ci rtc_iores.end = maple_rtc_addr + 7; 16562306a36Sopenharmony_ci request_resource(&ioport_resource, &rtc_iores); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci maple_get_rtc_time(&tm); 16862306a36Sopenharmony_ci return rtc_tm_to_time64(&tm); 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 171