aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/rtc
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-12-11 20:48:14 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2014-12-11 20:48:14 -0500
commit140cd7fb04a4a2bc09a30980bc8104cc89e09330 (patch)
tree776d57c7508f946d592de4334d4d3cb50fd36220 /drivers/rtc
parent27afc5dbda52ee3dbcd0bda7375c917c6936b470 (diff)
parent56548fc0e86cb9156af7a7e1f15ba78f251dafaf (diff)
Merge tag 'powerpc-3.19-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mpe/linux
Pull powerpc updates from Michael Ellerman: "Some nice cleanups like removing bootmem, and removal of __get_cpu_var(). There is one patch to mm/gup.c. This is the generic GUP implementation, but is only used by us and arm(64). We have an ack from Steve Capper, and although we didn't get an ack from Andrew he told us to take the patch through the powerpc tree. There's one cxl patch. This is in drivers/misc, but Greg said he was happy for us to manage fixes for it. There is an infrastructure patch to support an IPMI driver for OPAL. There is also an RTC driver for OPAL. We weren't able to get any response from the RTC maintainer, Alessandro Zummo, so in the end we just merged the driver. The usual batch of Freescale updates from Scott" * tag 'powerpc-3.19-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mpe/linux: (101 commits) powerpc/powernv: Return to cpu offline loop when finished in KVM guest powerpc/book3s: Fix partial invalidation of TLBs in MCE code. powerpc/mm: don't do tlbie for updatepp request with NO HPTE fault powerpc/xmon: Cleanup the breakpoint flags powerpc/xmon: Enable HW instruction breakpoint on POWER8 powerpc/mm/thp: Use tlbiel if possible powerpc/mm/thp: Remove code duplication powerpc/mm/hugetlb: Sanity check gigantic hugepage count powerpc/oprofile: Disable pagefaults during user stack read powerpc/mm: Check for matching hpte without taking hpte lock powerpc: Drop useless warning in eeh_init() powerpc/powernv: Cleanup unused MCE definitions/declarations. powerpc/eeh: Dump PHB diag-data early powerpc/eeh: Recover EEH error on ownership change for BCM5719 powerpc/eeh: Set EEH_PE_RESET on PE reset powerpc/eeh: Refactor eeh_reset_pe() powerpc: Remove more traces of bootmem powerpc/pseries: Initialise nvram_pstore_info's buf_lock cxl: Name interrupts in /proc/interrupt cxl: Return error to PSL if IRQ demultiplexing fails & print clearer warning ...
Diffstat (limited to 'drivers/rtc')
-rw-r--r--drivers/rtc/Kconfig11
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-opal.c261
3 files changed, 273 insertions, 0 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 4511ddc1ac31..f15cddfeb897 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -987,6 +987,17 @@ config RTC_DRV_NUC900
987 If you say yes here you get support for the RTC subsystem of the 987 If you say yes here you get support for the RTC subsystem of the
988 NUC910/NUC920 used in embedded systems. 988 NUC910/NUC920 used in embedded systems.
989 989
990config RTC_DRV_OPAL
991 tristate "IBM OPAL RTC driver"
992 depends on PPC_POWERNV
993 default y
994 help
995 If you say yes here you get support for the PowerNV platform RTC
996 driver based on OPAL interfaces.
997
998 This driver can also be built as a module. If so, the module
999 will be called rtc-opal.
1000
990comment "on-CPU RTC drivers" 1001comment "on-CPU RTC drivers"
991 1002
992config RTC_DRV_DAVINCI 1003config 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);