aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeelesh Gupta <neelegup@linux.vnet.ibm.com>2014-10-14 04:38:36 -0400
committerMichael Ellerman <mpe@ellerman.id.au>2014-11-17 02:04:01 -0500
commit16b1d26e77b142546e2b9b6dc3b5aa5c44ae3b77 (patch)
tree20c1c8e82cecb391dafadc01eb57c7a6bd58b25a
parent59994fb01a102a448ba758c9b824a29b4a99cc1b (diff)
rtc/tpo: Driver to support rtc and wakeup on PowerNV platform
The patch implements the OPAL rtc driver that binds with the rtc driver subsystem. The driver uses the platform device infrastructure to probe the rtc device and register it to rtc class framework. The 'wakeup' is supported depending upon the property 'has-tpo' present in the OF node. It provides a way to load the generic rtc driver in in the absence of an OPAL driver. The patch also moves the existing OPAL rtc get/set time interfaces to the new driver and exposes the necessary OPAL calls using EXPORT_SYMBOL_GPL. Test results: ------------- Host: [root@tul169p1 ~]# ls -l /sys/class/rtc/ total 0 lrwxrwxrwx 1 root root 0 Oct 14 03:07 rtc0 -> ../../devices/opal-rtc/rtc/rtc0 [root@tul169p1 ~]# cat /sys/devices/opal-rtc/rtc/rtc0/time 08:10:07 [root@tul169p1 ~]# echo `date '+%s' -d '+ 2 minutes'` > /sys/class/rtc/rtc0/wakealarm [root@tul169p1 ~]# cat /sys/class/rtc/rtc0/wakealarm 1413274345 [root@tul169p1 ~]# FSP: $ smgr mfgState standby $ rtim timeofday System time is valid: 2014/10/14 08:12:04.225115 $ smgr mfgState ipling $ CC: devicetree@vger.kernel.org CC: tglx@linutronix.de CC: rtc-linux@googlegroups.com CC: a.zummo@towertech.it Signed-off-by: Neelesh Gupta <neelegup@linux.vnet.ibm.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
-rw-r--r--Documentation/devicetree/bindings/rtc/rtc-opal.txt16
-rw-r--r--arch/powerpc/include/asm/opal.h7
-rw-r--r--arch/powerpc/kernel/time.c1
-rw-r--r--arch/powerpc/platforms/powernv/opal-async.c3
-rw-r--r--arch/powerpc/platforms/powernv/opal-rtc.c65
-rw-r--r--arch/powerpc/platforms/powernv/opal-wrappers.S2
-rw-r--r--arch/powerpc/platforms/powernv/opal.c6
-rw-r--r--arch/powerpc/platforms/powernv/setup.c2
-rw-r--r--drivers/rtc/Kconfig11
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-opal.c261
11 files changed, 325 insertions, 50 deletions
diff --git a/Documentation/devicetree/bindings/rtc/rtc-opal.txt b/Documentation/devicetree/bindings/rtc/rtc-opal.txt
new file mode 100644
index 000000000000..af87e5ecac54
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/rtc-opal.txt
@@ -0,0 +1,16 @@
1IBM OPAL real-time clock
2------------------------
3
4Required properties:
5- comapatible: Should be "ibm,opal-rtc"
6
7Optional properties:
8- has-tpo: Decides if the wakeup is supported or not.
9
10Example:
11 rtc {
12 compatible = "ibm,opal-rtc";
13 has-tpo;
14 phandle = <0x10000029>;
15 linux,phandle = <0x10000029>;
16 };
diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index 5d073e50cac8..60250e2d1f0d 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -154,6 +154,8 @@ struct opal_sg_list {
154#define OPAL_HANDLE_HMI 98 154#define OPAL_HANDLE_HMI 98
155#define OPAL_REGISTER_DUMP_REGION 101 155#define OPAL_REGISTER_DUMP_REGION 101
156#define OPAL_UNREGISTER_DUMP_REGION 102 156#define OPAL_UNREGISTER_DUMP_REGION 102
157#define OPAL_WRITE_TPO 103
158#define OPAL_READ_TPO 104
157#define OPAL_IPMI_SEND 107 159#define OPAL_IPMI_SEND 107
158#define OPAL_IPMI_RECV 108 160#define OPAL_IPMI_RECV 108
159 161
@@ -832,6 +834,9 @@ int64_t opal_rtc_read(__be32 *year_month_day,
832 __be64 *hour_minute_second_millisecond); 834 __be64 *hour_minute_second_millisecond);
833int64_t opal_rtc_write(uint32_t year_month_day, 835int64_t opal_rtc_write(uint32_t year_month_day,
834 uint64_t hour_minute_second_millisecond); 836 uint64_t hour_minute_second_millisecond);
837int64_t opal_tpo_read(uint64_t token, __be32 *year_mon_day, __be32 *hour_min);
838int64_t opal_tpo_write(uint64_t token, uint32_t year_mon_day,
839 uint32_t hour_min);
835int64_t opal_cec_power_down(uint64_t request); 840int64_t opal_cec_power_down(uint64_t request);
836int64_t opal_cec_reboot(void); 841int64_t opal_cec_reboot(void);
837int64_t opal_read_nvram(uint64_t buffer, uint64_t size, uint64_t offset); 842int64_t opal_read_nvram(uint64_t buffer, uint64_t size, uint64_t offset);
@@ -1009,8 +1014,6 @@ extern int opal_async_wait_response(uint64_t token, struct opal_msg *msg);
1009extern int opal_get_sensor_data(u32 sensor_hndl, u32 *sensor_data); 1014extern int opal_get_sensor_data(u32 sensor_hndl, u32 *sensor_data);
1010 1015
1011struct rtc_time; 1016struct rtc_time;
1012extern int opal_set_rtc_time(struct rtc_time *tm);
1013extern void opal_get_rtc_time(struct rtc_time *tm);
1014extern unsigned long opal_get_boot_time(void); 1017extern unsigned long opal_get_boot_time(void);
1015extern void opal_nvram_init(void); 1018extern void opal_nvram_init(void);
1016extern void opal_flash_init(void); 1019extern void opal_flash_init(void);
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c
index 9f8ea617ff2c..fa7c4f12104f 100644
--- a/arch/powerpc/kernel/time.c
+++ b/arch/powerpc/kernel/time.c
@@ -989,6 +989,7 @@ void GregorianDay(struct rtc_time * tm)
989 989
990 tm->tm_wday = day % 7; 990 tm->tm_wday = day % 7;
991} 991}
992EXPORT_SYMBOL_GPL(GregorianDay);
992 993
993void to_tm(int tim, struct rtc_time * tm) 994void to_tm(int tim, struct rtc_time * tm)
994{ 995{
diff --git a/arch/powerpc/platforms/powernv/opal-async.c b/arch/powerpc/platforms/powernv/opal-async.c
index e462ab947d16..693b6cdac691 100644
--- a/arch/powerpc/platforms/powernv/opal-async.c
+++ b/arch/powerpc/platforms/powernv/opal-async.c
@@ -71,6 +71,7 @@ int opal_async_get_token_interruptible(void)
71 71
72 return token; 72 return token;
73} 73}
74EXPORT_SYMBOL_GPL(opal_async_get_token_interruptible);
74 75
75int __opal_async_release_token(int token) 76int __opal_async_release_token(int token)
76{ 77{
@@ -102,6 +103,7 @@ int opal_async_release_token(int token)
102 103
103 return 0; 104 return 0;
104} 105}
106EXPORT_SYMBOL_GPL(opal_async_release_token);
105 107
106int opal_async_wait_response(uint64_t token, struct opal_msg *msg) 108int opal_async_wait_response(uint64_t token, struct opal_msg *msg)
107{ 109{
@@ -120,6 +122,7 @@ int opal_async_wait_response(uint64_t token, struct opal_msg *msg)
120 122
121 return 0; 123 return 0;
122} 124}
125EXPORT_SYMBOL_GPL(opal_async_wait_response);
123 126
124static int opal_async_comp_event(struct notifier_block *nb, 127static int opal_async_comp_event(struct notifier_block *nb,
125 unsigned long msg_type, void *msg) 128 unsigned long msg_type, void *msg)
diff --git a/arch/powerpc/platforms/powernv/opal-rtc.c b/arch/powerpc/platforms/powernv/opal-rtc.c
index 499707ddaa9c..37dbee15769f 100644
--- a/arch/powerpc/platforms/powernv/opal-rtc.c
+++ b/arch/powerpc/platforms/powernv/opal-rtc.c
@@ -15,6 +15,8 @@
15#include <linux/bcd.h> 15#include <linux/bcd.h>
16#include <linux/rtc.h> 16#include <linux/rtc.h>
17#include <linux/delay.h> 17#include <linux/delay.h>
18#include <linux/platform_device.h>
19#include <linux/of_platform.h>
18 20
19#include <asm/opal.h> 21#include <asm/opal.h>
20#include <asm/firmware.h> 22#include <asm/firmware.h>
@@ -43,7 +45,7 @@ unsigned long __init opal_get_boot_time(void)
43 long rc = OPAL_BUSY; 45 long rc = OPAL_BUSY;
44 46
45 if (!opal_check_token(OPAL_RTC_READ)) 47 if (!opal_check_token(OPAL_RTC_READ))
46 goto out; 48 return 0;
47 49
48 while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { 50 while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
49 rc = opal_rtc_read(&__y_m_d, &__h_m_s_ms); 51 rc = opal_rtc_read(&__y_m_d, &__h_m_s_ms);
@@ -53,62 +55,33 @@ unsigned long __init opal_get_boot_time(void)
53 mdelay(10); 55 mdelay(10);
54 } 56 }
55 if (rc != OPAL_SUCCESS) 57 if (rc != OPAL_SUCCESS)
56 goto out; 58 return 0;
57 59
58 y_m_d = be32_to_cpu(__y_m_d); 60 y_m_d = be32_to_cpu(__y_m_d);
59 h_m_s_ms = be64_to_cpu(__h_m_s_ms); 61 h_m_s_ms = be64_to_cpu(__h_m_s_ms);
60 opal_to_tm(y_m_d, h_m_s_ms, &tm); 62 opal_to_tm(y_m_d, h_m_s_ms, &tm);
61 return mktime(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 63 return mktime(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
62 tm.tm_hour, tm.tm_min, tm.tm_sec); 64 tm.tm_hour, tm.tm_min, tm.tm_sec);
63out:
64 ppc_md.get_rtc_time = NULL;
65 ppc_md.set_rtc_time = NULL;
66 return 0;
67} 65}
68 66
69void opal_get_rtc_time(struct rtc_time *tm) 67static __init int opal_time_init(void)
70{ 68{
71 long rc = OPAL_BUSY; 69 struct platform_device *pdev;
72 u32 y_m_d; 70 struct device_node *rtc;
73 u64 h_m_s_ms;
74 __be32 __y_m_d;
75 __be64 __h_m_s_ms;
76 71
77 while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { 72 rtc = of_find_node_by_path("/ibm,opal/rtc");
78 rc = opal_rtc_read(&__y_m_d, &__h_m_s_ms); 73 if (rtc) {
79 if (rc == OPAL_BUSY_EVENT) 74 pdev = of_platform_device_create(rtc, "opal-rtc", NULL);
80 opal_poll_events(NULL); 75 of_node_put(rtc);
76 } else {
77 if (opal_check_token(OPAL_RTC_READ) ||
78 opal_check_token(OPAL_READ_TPO))
79 pdev = platform_device_register_simple("opal-rtc", -1,
80 NULL, 0);
81 else 81 else
82 mdelay(10); 82 return -ENODEV;
83 } 83 }
84 if (rc != OPAL_SUCCESS)
85 return;
86 y_m_d = be32_to_cpu(__y_m_d);
87 h_m_s_ms = be64_to_cpu(__h_m_s_ms);
88 opal_to_tm(y_m_d, h_m_s_ms, tm);
89}
90
91int opal_set_rtc_time(struct rtc_time *tm)
92{
93 long rc = OPAL_BUSY;
94 u32 y_m_d = 0;
95 u64 h_m_s_ms = 0;
96
97 y_m_d |= ((u32)bin2bcd((tm->tm_year + 1900) / 100)) << 24;
98 y_m_d |= ((u32)bin2bcd((tm->tm_year + 1900) % 100)) << 16;
99 y_m_d |= ((u32)bin2bcd((tm->tm_mon + 1))) << 8;
100 y_m_d |= ((u32)bin2bcd(tm->tm_mday));
101
102 h_m_s_ms |= ((u64)bin2bcd(tm->tm_hour)) << 56;
103 h_m_s_ms |= ((u64)bin2bcd(tm->tm_min)) << 48;
104 h_m_s_ms |= ((u64)bin2bcd(tm->tm_sec)) << 40;
105 84
106 while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { 85 return PTR_ERR_OR_ZERO(pdev);
107 rc = opal_rtc_write(y_m_d, h_m_s_ms);
108 if (rc == OPAL_BUSY_EVENT)
109 opal_poll_events(NULL);
110 else
111 mdelay(10);
112 }
113 return rc == OPAL_SUCCESS ? 0 : -EIO;
114} 86}
87machine_subsys_initcall(powernv, opal_time_init);
diff --git a/arch/powerpc/platforms/powernv/opal-wrappers.S b/arch/powerpc/platforms/powernv/opal-wrappers.S
index 59978af9babe..0a299be588af 100644
--- a/arch/powerpc/platforms/powernv/opal-wrappers.S
+++ b/arch/powerpc/platforms/powernv/opal-wrappers.S
@@ -250,5 +250,7 @@ OPAL_CALL(opal_handle_hmi, OPAL_HANDLE_HMI);
250OPAL_CALL(opal_register_dump_region, OPAL_REGISTER_DUMP_REGION); 250OPAL_CALL(opal_register_dump_region, OPAL_REGISTER_DUMP_REGION);
251OPAL_CALL(opal_unregister_dump_region, OPAL_UNREGISTER_DUMP_REGION); 251OPAL_CALL(opal_unregister_dump_region, OPAL_UNREGISTER_DUMP_REGION);
252OPAL_CALL(opal_pci_set_phb_cxl_mode, OPAL_PCI_SET_PHB_CXL_MODE); 252OPAL_CALL(opal_pci_set_phb_cxl_mode, OPAL_PCI_SET_PHB_CXL_MODE);
253OPAL_CALL(opal_tpo_write, OPAL_WRITE_TPO);
254OPAL_CALL(opal_tpo_read, OPAL_READ_TPO);
253OPAL_CALL(opal_ipmi_send, OPAL_IPMI_SEND); 255OPAL_CALL(opal_ipmi_send, OPAL_IPMI_SEND);
254OPAL_CALL(opal_ipmi_recv, OPAL_IPMI_RECV); 256OPAL_CALL(opal_ipmi_recv, OPAL_IPMI_RECV);
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
index 0297702e8ae9..a1c37f9d60d2 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -819,3 +819,9 @@ void opal_free_sg_list(struct opal_sg_list *sg)
819 sg = NULL; 819 sg = NULL;
820 } 820 }
821} 821}
822
823EXPORT_SYMBOL_GPL(opal_poll_events);
824EXPORT_SYMBOL_GPL(opal_rtc_read);
825EXPORT_SYMBOL_GPL(opal_rtc_write);
826EXPORT_SYMBOL_GPL(opal_tpo_read);
827EXPORT_SYMBOL_GPL(opal_tpo_write);
diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c
index 941831d67cb2..30b1c3e298a6 100644
--- a/arch/powerpc/platforms/powernv/setup.c
+++ b/arch/powerpc/platforms/powernv/setup.c
@@ -265,8 +265,6 @@ static unsigned long pnv_memory_block_size(void)
265static void __init pnv_setup_machdep_opal(void) 265static void __init pnv_setup_machdep_opal(void)
266{ 266{
267 ppc_md.get_boot_time = opal_get_boot_time; 267 ppc_md.get_boot_time = opal_get_boot_time;
268 ppc_md.get_rtc_time = opal_get_rtc_time;
269 ppc_md.set_rtc_time = opal_set_rtc_time;
270 ppc_md.restart = pnv_restart; 268 ppc_md.restart = pnv_restart;
271 pm_power_off = pnv_power_off; 269 pm_power_off = pnv_power_off;
272 ppc_md.halt = pnv_halt; 270 ppc_md.halt = pnv_halt;
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 6dd12ddbabc6..c8f0ec7464ce 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -979,6 +979,17 @@ config RTC_DRV_NUC900
979 If you say yes here you get support for the RTC subsystem of the 979 If you say yes here you get support for the RTC subsystem of the
980 NUC910/NUC920 used in embedded systems. 980 NUC910/NUC920 used in embedded systems.
981 981
982config RTC_DRV_OPAL
983 tristate "IBM OPAL RTC driver"
984 depends on PPC_POWERNV
985 default y
986 help
987 If you say yes here you get support for the PowerNV platform RTC
988 driver based on OPAL interfaces.
989
990 This driver can also be built as a module. If so, the module
991 will be called rtc-opal.
992
982comment "on-CPU RTC drivers" 993comment "on-CPU RTC drivers"
983 994
984config RTC_DRV_DAVINCI 995config RTC_DRV_DAVINCI
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index b188323c096a..c8ef3e1e6ccd 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -92,6 +92,7 @@ obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o
92obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o 92obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o
93obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o 93obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o
94obj-$(CONFIG_RTC_DRV_NUC900) += rtc-nuc900.o 94obj-$(CONFIG_RTC_DRV_NUC900) += rtc-nuc900.o
95obj-$(CONFIG_RTC_DRV_OPAL) += rtc-opal.o
95obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o 96obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
96obj-$(CONFIG_RTC_DRV_PALMAS) += rtc-palmas.o 97obj-$(CONFIG_RTC_DRV_PALMAS) += rtc-palmas.o
97obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o 98obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o
diff --git a/drivers/rtc/rtc-opal.c b/drivers/rtc/rtc-opal.c
new file mode 100644
index 000000000000..95f652165fe9
--- /dev/null
+++ b/drivers/rtc/rtc-opal.c
@@ -0,0 +1,261 @@
1/*
2 * IBM OPAL RTC driver
3 * Copyright (C) 2014 IBM
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program.
17 */
18
19#define DRVNAME "rtc-opal"
20#define pr_fmt(fmt) DRVNAME ": " fmt
21
22#include <linux/module.h>
23#include <linux/err.h>
24#include <linux/rtc.h>
25#include <linux/delay.h>
26#include <linux/bcd.h>
27#include <linux/platform_device.h>
28#include <linux/of.h>
29#include <asm/opal.h>
30#include <asm/firmware.h>
31
32static void opal_to_tm(u32 y_m_d, u64 h_m_s_ms, struct rtc_time *tm)
33{
34 tm->tm_year = ((bcd2bin(y_m_d >> 24) * 100) +
35 bcd2bin((y_m_d >> 16) & 0xff)) - 1900;
36 tm->tm_mon = bcd2bin((y_m_d >> 8) & 0xff) - 1;
37 tm->tm_mday = bcd2bin(y_m_d & 0xff);
38 tm->tm_hour = bcd2bin((h_m_s_ms >> 56) & 0xff);
39 tm->tm_min = bcd2bin((h_m_s_ms >> 48) & 0xff);
40 tm->tm_sec = bcd2bin((h_m_s_ms >> 40) & 0xff);
41
42 GregorianDay(tm);
43}
44
45static void tm_to_opal(struct rtc_time *tm, u32 *y_m_d, u64 *h_m_s_ms)
46{
47 *y_m_d |= ((u32)bin2bcd((tm->tm_year + 1900) / 100)) << 24;
48 *y_m_d |= ((u32)bin2bcd((tm->tm_year + 1900) % 100)) << 16;
49 *y_m_d |= ((u32)bin2bcd((tm->tm_mon + 1))) << 8;
50 *y_m_d |= ((u32)bin2bcd(tm->tm_mday));
51
52 *h_m_s_ms |= ((u64)bin2bcd(tm->tm_hour)) << 56;
53 *h_m_s_ms |= ((u64)bin2bcd(tm->tm_min)) << 48;
54 *h_m_s_ms |= ((u64)bin2bcd(tm->tm_sec)) << 40;
55}
56
57static int opal_get_rtc_time(struct device *dev, struct rtc_time *tm)
58{
59 long rc = OPAL_BUSY;
60 u32 y_m_d;
61 u64 h_m_s_ms;
62 __be32 __y_m_d;
63 __be64 __h_m_s_ms;
64
65 while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
66 rc = opal_rtc_read(&__y_m_d, &__h_m_s_ms);
67 if (rc == OPAL_BUSY_EVENT)
68 opal_poll_events(NULL);
69 else
70 msleep(10);
71 }
72
73 if (rc != OPAL_SUCCESS)
74 return -EIO;
75
76 y_m_d = be32_to_cpu(__y_m_d);
77 h_m_s_ms = be64_to_cpu(__h_m_s_ms);
78 opal_to_tm(y_m_d, h_m_s_ms, tm);
79
80 return 0;
81}
82
83static int opal_set_rtc_time(struct device *dev, struct rtc_time *tm)
84{
85 long rc = OPAL_BUSY;
86 u32 y_m_d = 0;
87 u64 h_m_s_ms = 0;
88
89 tm_to_opal(tm, &y_m_d, &h_m_s_ms);
90 while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
91 rc = opal_rtc_write(y_m_d, h_m_s_ms);
92 if (rc == OPAL_BUSY_EVENT)
93 opal_poll_events(NULL);
94 else
95 msleep(10);
96 }
97
98 return rc == OPAL_SUCCESS ? 0 : -EIO;
99}
100
101/*
102 * TPO Timed Power-On
103 *
104 * TPO get/set OPAL calls care about the hour and min and to make it consistent
105 * with the rtc utility time conversion functions, we use the 'u64' to store
106 * its value and perform bit shift by 32 before use..
107 */
108static int opal_get_tpo_time(struct device *dev, struct rtc_wkalrm *alarm)
109{
110 __be32 __y_m_d, __h_m;
111 struct opal_msg msg;
112 int rc, token;
113 u64 h_m_s_ms;
114 u32 y_m_d;
115
116 token = opal_async_get_token_interruptible();
117 if (token < 0) {
118 if (token != -ERESTARTSYS)
119 pr_err("Failed to get the async token\n");
120
121 return token;
122 }
123
124 rc = opal_tpo_read(token, &__y_m_d, &__h_m);
125 if (rc != OPAL_ASYNC_COMPLETION) {
126 rc = -EIO;
127 goto exit;
128 }
129
130 rc = opal_async_wait_response(token, &msg);
131 if (rc) {
132 rc = -EIO;
133 goto exit;
134 }
135
136 rc = be64_to_cpu(msg.params[1]);
137 if (rc != OPAL_SUCCESS) {
138 rc = -EIO;
139 goto exit;
140 }
141
142 y_m_d = be32_to_cpu(__y_m_d);
143 h_m_s_ms = ((u64)be32_to_cpu(__h_m) << 32);
144 opal_to_tm(y_m_d, h_m_s_ms, &alarm->time);
145
146exit:
147 opal_async_release_token(token);
148 return rc;
149}
150
151/* Set Timed Power-On */
152static int opal_set_tpo_time(struct device *dev, struct rtc_wkalrm *alarm)
153{
154 u64 h_m_s_ms = 0, token;
155 struct opal_msg msg;
156 u32 y_m_d = 0;
157 int rc;
158
159 tm_to_opal(&alarm->time, &y_m_d, &h_m_s_ms);
160
161 token = opal_async_get_token_interruptible();
162 if (token < 0) {
163 if (token != -ERESTARTSYS)
164 pr_err("Failed to get the async token\n");
165
166 return token;
167 }
168
169 /* TPO, we care about hour and minute */
170 rc = opal_tpo_write(token, y_m_d,
171 (u32)((h_m_s_ms >> 32) & 0xffff0000));
172 if (rc != OPAL_ASYNC_COMPLETION) {
173 rc = -EIO;
174 goto exit;
175 }
176
177 rc = opal_async_wait_response(token, &msg);
178 if (rc) {
179 rc = -EIO;
180 goto exit;
181 }
182
183 rc = be64_to_cpu(msg.params[1]);
184 if (rc != OPAL_SUCCESS)
185 rc = -EIO;
186
187exit:
188 opal_async_release_token(token);
189 return rc;
190}
191
192static const struct rtc_class_ops opal_rtc_ops = {
193 .read_time = opal_get_rtc_time,
194 .set_time = opal_set_rtc_time,
195 .read_alarm = opal_get_tpo_time,
196 .set_alarm = opal_set_tpo_time,
197};
198
199static int opal_rtc_probe(struct platform_device *pdev)
200{
201 struct rtc_device *rtc;
202
203 if (pdev->dev.of_node && of_get_property(pdev->dev.of_node, "has-tpo",
204 NULL))
205 device_set_wakeup_capable(&pdev->dev, true);
206
207 rtc = devm_rtc_device_register(&pdev->dev, DRVNAME, &opal_rtc_ops,
208 THIS_MODULE);
209 if (IS_ERR(rtc))
210 return PTR_ERR(rtc);
211
212 rtc->uie_unsupported = 1;
213
214 return 0;
215}
216
217static const struct of_device_id opal_rtc_match[] = {
218 {
219 .compatible = "ibm,opal-rtc",
220 },
221 { }
222};
223MODULE_DEVICE_TABLE(of, opal_rtc_match);
224
225static const struct platform_device_id opal_rtc_driver_ids[] = {
226 {
227 .name = "opal-rtc",
228 },
229 { }
230};
231MODULE_DEVICE_TABLE(platform, opal_rtc_driver_ids);
232
233static struct platform_driver opal_rtc_driver = {
234 .probe = opal_rtc_probe,
235 .id_table = opal_rtc_driver_ids,
236 .driver = {
237 .name = DRVNAME,
238 .owner = THIS_MODULE,
239 .of_match_table = opal_rtc_match,
240 },
241};
242
243static int __init opal_rtc_init(void)
244{
245 if (!firmware_has_feature(FW_FEATURE_OPAL))
246 return -ENODEV;
247
248 return platform_driver_register(&opal_rtc_driver);
249}
250
251static void __exit opal_rtc_exit(void)
252{
253 platform_driver_unregister(&opal_rtc_driver);
254}
255
256MODULE_AUTHOR("Neelesh Gupta <neelegup@linux.vnet.ibm.com>");
257MODULE_DESCRIPTION("IBM OPAL RTC driver");
258MODULE_LICENSE("GPL");
259
260module_init(opal_rtc_init);
261module_exit(opal_rtc_exit);