Commit | Line | Data |
---|---|---|
628daa8d BH |
1 | /* |
2 | * PowerNV Real Time Clock. | |
3 | * | |
4 | * Copyright 2011 IBM Corp. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public License | |
8 | * as published by the Free Software Foundation; either version | |
9 | * 2 of the License, or (at your option) any later version. | |
10 | */ | |
11 | ||
12 | ||
13 | #include <linux/kernel.h> | |
14 | #include <linux/time.h> | |
15 | #include <linux/bcd.h> | |
16 | #include <linux/rtc.h> | |
17 | #include <linux/delay.h> | |
16b1d26e NG |
18 | #include <linux/platform_device.h> |
19 | #include <linux/of_platform.h> | |
628daa8d BH |
20 | |
21 | #include <asm/opal.h> | |
22 | #include <asm/firmware.h> | |
2c49195b | 23 | #include <asm/machdep.h> |
628daa8d BH |
24 | |
25 | static void opal_to_tm(u32 y_m_d, u64 h_m_s_ms, struct rtc_time *tm) | |
26 | { | |
27 | tm->tm_year = ((bcd2bin(y_m_d >> 24) * 100) + | |
28 | bcd2bin((y_m_d >> 16) & 0xff)) - 1900; | |
29 | tm->tm_mon = bcd2bin((y_m_d >> 8) & 0xff) - 1; | |
30 | tm->tm_mday = bcd2bin(y_m_d & 0xff); | |
31 | tm->tm_hour = bcd2bin((h_m_s_ms >> 56) & 0xff); | |
32 | tm->tm_min = bcd2bin((h_m_s_ms >> 48) & 0xff); | |
33 | tm->tm_sec = bcd2bin((h_m_s_ms >> 40) & 0xff); | |
00b912b0 | 34 | tm->tm_wday = -1; |
628daa8d BH |
35 | } |
36 | ||
37 | unsigned long __init opal_get_boot_time(void) | |
38 | { | |
39 | struct rtc_time tm; | |
40 | u32 y_m_d; | |
41 | u64 h_m_s_ms; | |
6feff6d4 AB |
42 | __be32 __y_m_d; |
43 | __be64 __h_m_s_ms; | |
628daa8d BH |
44 | long rc = OPAL_BUSY; |
45 | ||
035ed26f | 46 | if (!opal_check_token(OPAL_RTC_READ)) |
16b1d26e | 47 | return 0; |
035ed26f | 48 | |
628daa8d | 49 | while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { |
6feff6d4 | 50 | rc = opal_rtc_read(&__y_m_d, &__h_m_s_ms); |
628daa8d BH |
51 | if (rc == OPAL_BUSY_EVENT) |
52 | opal_poll_events(NULL); | |
57a90390 | 53 | else if (rc == OPAL_BUSY) |
628daa8d BH |
54 | mdelay(10); |
55 | } | |
035ed26f | 56 | if (rc != OPAL_SUCCESS) |
16b1d26e | 57 | return 0; |
035ed26f | 58 | |
6feff6d4 AB |
59 | y_m_d = be32_to_cpu(__y_m_d); |
60 | h_m_s_ms = be64_to_cpu(__h_m_s_ms); | |
628daa8d BH |
61 | opal_to_tm(y_m_d, h_m_s_ms, &tm); |
62 | return mktime(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, | |
63 | tm.tm_hour, tm.tm_min, tm.tm_sec); | |
64 | } | |
65 | ||
16b1d26e | 66 | static __init int opal_time_init(void) |
628daa8d | 67 | { |
16b1d26e NG |
68 | struct platform_device *pdev; |
69 | struct device_node *rtc; | |
628daa8d | 70 | |
16b1d26e NG |
71 | rtc = of_find_node_by_path("/ibm,opal/rtc"); |
72 | if (rtc) { | |
73 | pdev = of_platform_device_create(rtc, "opal-rtc", NULL); | |
74 | of_node_put(rtc); | |
75 | } else { | |
76 | if (opal_check_token(OPAL_RTC_READ) || | |
77 | opal_check_token(OPAL_READ_TPO)) | |
78 | pdev = platform_device_register_simple("opal-rtc", -1, | |
79 | NULL, 0); | |
628daa8d | 80 | else |
16b1d26e | 81 | return -ENODEV; |
628daa8d | 82 | } |
628daa8d | 83 | |
16b1d26e | 84 | return PTR_ERR_OR_ZERO(pdev); |
628daa8d | 85 | } |
16b1d26e | 86 | machine_subsys_initcall(powernv, opal_time_init); |