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