1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * PowerNV Real Time Clock. 4 * 5 * Copyright 2011 IBM Corp. 6 */ 7 8 9 #include <linux/kernel.h> 10 #include <linux/time.h> 11 #include <linux/bcd.h> 12 #include <linux/rtc.h> 13 #include <linux/delay.h> 14 #include <linux/of.h> 15 #include <linux/of_platform.h> 16 #include <linux/platform_device.h> 17 18 #include <asm/opal.h> 19 #include <asm/firmware.h> 20 #include <asm/machdep.h> 21 22 static void __init opal_to_tm(u32 y_m_d, u64 h_m_s_ms, struct rtc_time *tm) 23 { 24 tm->tm_year = ((bcd2bin(y_m_d >> 24) * 100) + 25 bcd2bin((y_m_d >> 16) & 0xff)) - 1900; 26 tm->tm_mon = bcd2bin((y_m_d >> 8) & 0xff) - 1; 27 tm->tm_mday = bcd2bin(y_m_d & 0xff); 28 tm->tm_hour = bcd2bin((h_m_s_ms >> 56) & 0xff); 29 tm->tm_min = bcd2bin((h_m_s_ms >> 48) & 0xff); 30 tm->tm_sec = bcd2bin((h_m_s_ms >> 40) & 0xff); 31 tm->tm_wday = -1; 32 } 33 34 time64_t __init opal_get_boot_time(void) 35 { 36 struct rtc_time tm; 37 u32 y_m_d; 38 u64 h_m_s_ms; 39 __be32 __y_m_d; 40 __be64 __h_m_s_ms; 41 long rc = OPAL_BUSY; 42 43 if (!opal_check_token(OPAL_RTC_READ)) 44 return 0; 45 46 while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { 47 rc = opal_rtc_read(&__y_m_d, &__h_m_s_ms); 48 if (rc == OPAL_BUSY_EVENT) { 49 mdelay(OPAL_BUSY_DELAY_MS); 50 opal_poll_events(NULL); 51 } else if (rc == OPAL_BUSY) { 52 mdelay(OPAL_BUSY_DELAY_MS); 53 } 54 } 55 if (rc != OPAL_SUCCESS) 56 return 0; 57 58 y_m_d = be32_to_cpu(__y_m_d); 59 h_m_s_ms = be64_to_cpu(__h_m_s_ms); 60 opal_to_tm(y_m_d, h_m_s_ms, &tm); 61 return rtc_tm_to_time64(&tm); 62 } 63 64 static __init int opal_time_init(void) 65 { 66 struct platform_device *pdev; 67 struct device_node *rtc; 68 69 rtc = of_find_node_by_path("/ibm,opal/rtc"); 70 if (rtc) { 71 pdev = of_platform_device_create(rtc, "opal-rtc", NULL); 72 of_node_put(rtc); 73 } else { 74 if (opal_check_token(OPAL_RTC_READ) || 75 opal_check_token(OPAL_READ_TPO)) 76 pdev = platform_device_register_simple("opal-rtc", -1, 77 NULL, 0); 78 else 79 return -ENODEV; 80 } 81 82 return PTR_ERR_OR_ZERO(pdev); 83 } 84 machine_subsys_initcall(powernv, opal_time_init); 85