aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2011-09-19 13:45:01 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2011-09-20 02:09:57 -0400
commit628daa8d5abfd904a7329a660c5c374212230123 (patch)
tree83981d6c9566dd6bb16c45ea43938df8ed5d350f /arch
parentec27329ffb3b4f619be9f0065c473fcb36ea52ce (diff)
powerpc/powernv: Add RTC and NVRAM support plus RTAS fallbacks
Implements OPAL RTC and NVRAM support and wire all that up to the powernv platform. We use RTAS for RTC as a fallback if available. Using RTAS for nvram is not supported yet, pending some rework/cleanup and generalization of the pSeries & CHRP code. We also use RTAS fallbacks for power off and reboot Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/powerpc/include/asm/opal.h6
-rw-r--r--arch/powerpc/platforms/powernv/Makefile2
-rw-r--r--arch/powerpc/platforms/powernv/opal-nvram.c88
-rw-r--r--arch/powerpc/platforms/powernv/opal-rtc.c97
-rw-r--r--arch/powerpc/platforms/powernv/setup.c57
5 files changed, 229 insertions, 21 deletions
diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index 749de00a02d5..77ebe50020a2 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -430,6 +430,12 @@ extern int opal_put_chars(uint32_t vtermno, const char *buf, int total_len);
430 430
431extern void hvc_opal_init_early(void); 431extern void hvc_opal_init_early(void);
432 432
433struct rtc_time;
434extern int opal_set_rtc_time(struct rtc_time *tm);
435extern void opal_get_rtc_time(struct rtc_time *tm);
436extern unsigned long opal_get_boot_time(void);
437extern void opal_nvram_init(void);
438
433#endif /* __ASSEMBLY__ */ 439#endif /* __ASSEMBLY__ */
434 440
435#endif /* __OPAL_H */ 441#endif /* __OPAL_H */
diff --git a/arch/powerpc/platforms/powernv/Makefile b/arch/powerpc/platforms/powernv/Makefile
index 8f69c0db612c..618ad836f28b 100644
--- a/arch/powerpc/platforms/powernv/Makefile
+++ b/arch/powerpc/platforms/powernv/Makefile
@@ -1,2 +1,4 @@
1obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o 1obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o
2obj-y += opal-rtc.o opal-nvram.o
3
2obj-$(CONFIG_SMP) += smp.o 4obj-$(CONFIG_SMP) += smp.o
diff --git a/arch/powerpc/platforms/powernv/opal-nvram.c b/arch/powerpc/platforms/powernv/opal-nvram.c
new file mode 100644
index 000000000000..3f83e1ae26ac
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/opal-nvram.c
@@ -0,0 +1,88 @@
1/*
2 * PowerNV nvram code.
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#define DEBUG
13
14#include <linux/kernel.h>
15#include <linux/init.h>
16#include <linux/of.h>
17
18#include <asm/opal.h>
19#include <asm/machdep.h>
20
21static unsigned int nvram_size;
22
23static ssize_t opal_nvram_size(void)
24{
25 return nvram_size;
26}
27
28static ssize_t opal_nvram_read(char *buf, size_t count, loff_t *index)
29{
30 s64 rc;
31 int off;
32
33 if (*index >= nvram_size)
34 return 0;
35 off = *index;
36 if ((off + count) > nvram_size)
37 count = nvram_size - off;
38 rc = opal_read_nvram(__pa(buf), count, off);
39 if (rc != OPAL_SUCCESS)
40 return -EIO;
41 *index += count;
42 return count;
43}
44
45static ssize_t opal_nvram_write(char *buf, size_t count, loff_t *index)
46{
47 s64 rc = OPAL_BUSY;
48 int off;
49
50 if (*index >= nvram_size)
51 return 0;
52 off = *index;
53 if ((off + count) > nvram_size)
54 count = nvram_size - off;
55
56 while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
57 rc = opal_write_nvram(__pa(buf), count, off);
58 if (rc == OPAL_BUSY_EVENT)
59 opal_poll_events(NULL);
60 }
61 *index += count;
62 return count;
63}
64
65void __init opal_nvram_init(void)
66{
67 struct device_node *np;
68 const u32 *nbytes_p;
69
70 np = of_find_compatible_node(NULL, NULL, "ibm,opal-nvram");
71 if (np == NULL)
72 return;
73
74 nbytes_p = of_get_property(np, "#bytes", NULL);
75 if (!nbytes_p) {
76 of_node_put(np);
77 return;
78 }
79 nvram_size = *nbytes_p;
80
81 printk(KERN_INFO "OPAL nvram setup, %u bytes\n", nvram_size);
82 of_node_put(np);
83
84 ppc_md.nvram_read = opal_nvram_read;
85 ppc_md.nvram_write = opal_nvram_write;
86 ppc_md.nvram_size = opal_nvram_size;
87}
88
diff --git a/arch/powerpc/platforms/powernv/opal-rtc.c b/arch/powerpc/platforms/powernv/opal-rtc.c
new file mode 100644
index 000000000000..2aa7641aac9b
--- /dev/null
+++ b/arch/powerpc/platforms/powernv/opal-rtc.c
@@ -0,0 +1,97 @@
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>
18
19#include <asm/opal.h>
20#include <asm/firmware.h>
21
22static void 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
32 GregorianDay(tm);
33}
34
35unsigned long __init opal_get_boot_time(void)
36{
37 struct rtc_time tm;
38 u32 y_m_d;
39 u64 h_m_s_ms;
40 long rc = OPAL_BUSY;
41
42 while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
43 rc = opal_rtc_read(&y_m_d, &h_m_s_ms);
44 if (rc == OPAL_BUSY_EVENT)
45 opal_poll_events(NULL);
46 else
47 mdelay(10);
48 }
49 if (rc != OPAL_SUCCESS)
50 return 0;
51 opal_to_tm(y_m_d, h_m_s_ms, &tm);
52 return mktime(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
53 tm.tm_hour, tm.tm_min, tm.tm_sec);
54}
55
56void opal_get_rtc_time(struct rtc_time *tm)
57{
58 long rc = OPAL_BUSY;
59 u32 y_m_d;
60 u64 h_m_s_ms;
61
62 while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
63 rc = opal_rtc_read(&y_m_d, &h_m_s_ms);
64 if (rc == OPAL_BUSY_EVENT)
65 opal_poll_events(NULL);
66 else
67 mdelay(10);
68 }
69 if (rc != OPAL_SUCCESS)
70 return;
71 opal_to_tm(y_m_d, h_m_s_ms, tm);
72}
73
74int opal_set_rtc_time(struct rtc_time *tm)
75{
76 long rc = OPAL_BUSY;
77 u32 y_m_d = 0;
78 u64 h_m_s_ms = 0;
79
80 y_m_d |= ((u32)bin2bcd((tm->tm_year + 1900) / 100)) << 24;
81 y_m_d |= ((u32)bin2bcd((tm->tm_year + 1900) % 100)) << 16;
82 y_m_d |= ((u32)bin2bcd((tm->tm_mon + 1))) << 8;
83 y_m_d |= ((u32)bin2bcd(tm->tm_mday));
84
85 h_m_s_ms |= ((u64)bin2bcd(tm->tm_hour)) << 56;
86 h_m_s_ms |= ((u64)bin2bcd(tm->tm_min)) << 48;
87 h_m_s_ms |= ((u64)bin2bcd(tm->tm_sec)) << 40;
88
89 while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
90 rc = opal_rtc_write(y_m_d, h_m_s_ms);
91 if (rc == OPAL_BUSY_EVENT)
92 opal_poll_events(NULL);
93 else
94 mdelay(10);
95 }
96 return rc == OPAL_SUCCESS ? 0 : -EIO;
97}
diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c
index 0fac0a6c951e..4a2b2e279593 100644
--- a/arch/powerpc/platforms/powernv/setup.c
+++ b/arch/powerpc/platforms/powernv/setup.c
@@ -29,7 +29,9 @@
29#include <asm/machdep.h> 29#include <asm/machdep.h>
30#include <asm/firmware.h> 30#include <asm/firmware.h>
31#include <asm/xics.h> 31#include <asm/xics.h>
32#include <asm/rtas.h>
32#include <asm/opal.h> 33#include <asm/opal.h>
34#include <asm/xics.h>
33 35
34#include "powernv.h" 36#include "powernv.h"
35 37
@@ -40,7 +42,9 @@ static void __init pnv_setup_arch(void)
40 42
41 /* XXX PCI */ 43 /* XXX PCI */
42 44
43 /* XXX NVRAM */ 45 /* Setup RTC and NVRAM callbacks */
46 if (firmware_has_feature(FW_FEATURE_OPAL))
47 opal_nvram_init();
44 48
45 /* Enable NAP mode */ 49 /* Enable NAP mode */
46 powersave_nap = 1; 50 powersave_nap = 1;
@@ -118,30 +122,40 @@ static void __noreturn pnv_halt(void)
118 pnv_power_off(); 122 pnv_power_off();
119} 123}
120 124
121static unsigned long __init pnv_get_boot_time(void) 125static void pnv_progress(char *s, unsigned short hex)
122{
123 return 0;
124}
125
126static void pnv_get_rtc_time(struct rtc_time *rtc_tm)
127{ 126{
128} 127}
129 128
130static int pnv_set_rtc_time(struct rtc_time *tm) 129#ifdef CONFIG_KEXEC
130static void pnv_kexec_cpu_down(int crash_shutdown, int secondary)
131{ 131{
132 return 0; 132 xics_kexec_teardown_cpu(secondary);
133} 133}
134#endif /* CONFIG_KEXEC */
134 135
135static void pnv_progress(char *s, unsigned short hex) 136static void __init pnv_setup_machdep_opal(void)
136{ 137{
138 ppc_md.get_boot_time = opal_get_boot_time;
139 ppc_md.get_rtc_time = opal_get_rtc_time;
140 ppc_md.set_rtc_time = opal_set_rtc_time;
141 ppc_md.restart = pnv_restart;
142 ppc_md.power_off = pnv_power_off;
143 ppc_md.halt = pnv_halt;
137} 144}
138 145
139#ifdef CONFIG_KEXEC 146#ifdef CONFIG_PPC_POWERNV_RTAS
140static void pnv_kexec_cpu_down(int crash_shutdown, int secondary) 147static void __init pnv_setup_machdep_rtas(void)
141{ 148{
142 xics_kexec_teardown_cpu(secondary); 149 if (rtas_token("get-time-of-day") != RTAS_UNKNOWN_SERVICE) {
150 ppc_md.get_boot_time = rtas_get_boot_time;
151 ppc_md.get_rtc_time = rtas_get_rtc_time;
152 ppc_md.set_rtc_time = rtas_set_rtc_time;
153 }
154 ppc_md.restart = rtas_restart;
155 ppc_md.power_off = rtas_power_off;
156 ppc_md.halt = rtas_halt;
143} 157}
144#endif /* CONFIG_KEXEC */ 158#endif /* CONFIG_PPC_POWERNV_RTAS */
145 159
146static int __init pnv_probe(void) 160static int __init pnv_probe(void)
147{ 161{
@@ -152,6 +166,13 @@ static int __init pnv_probe(void)
152 166
153 hpte_init_native(); 167 hpte_init_native();
154 168
169 if (firmware_has_feature(FW_FEATURE_OPAL))
170 pnv_setup_machdep_opal();
171#ifdef CONFIG_PPC_POWERNV_RTAS
172 else if (rtas.base)
173 pnv_setup_machdep_rtas();
174#endif /* CONFIG_PPC_POWERNV_RTAS */
175
155 pr_debug("PowerNV detected !\n"); 176 pr_debug("PowerNV detected !\n");
156 177
157 return 1; 178 return 1;
@@ -160,16 +181,10 @@ static int __init pnv_probe(void)
160define_machine(powernv) { 181define_machine(powernv) {
161 .name = "PowerNV", 182 .name = "PowerNV",
162 .probe = pnv_probe, 183 .probe = pnv_probe,
163 .setup_arch = pnv_setup_arch,
164 .init_early = pnv_init_early, 184 .init_early = pnv_init_early,
185 .setup_arch = pnv_setup_arch,
165 .init_IRQ = pnv_init_IRQ, 186 .init_IRQ = pnv_init_IRQ,
166 .show_cpuinfo = pnv_show_cpuinfo, 187 .show_cpuinfo = pnv_show_cpuinfo,
167 .restart = pnv_restart,
168 .power_off = pnv_power_off,
169 .halt = pnv_halt,
170 .get_boot_time = pnv_get_boot_time,
171 .get_rtc_time = pnv_get_rtc_time,
172 .set_rtc_time = pnv_set_rtc_time,
173 .progress = pnv_progress, 188 .progress = pnv_progress,
174 .power_save = power7_idle, 189 .power_save = power7_idle,
175 .calibrate_decr = generic_calibrate_decr, 190 .calibrate_decr = generic_calibrate_decr,