Skip to content

Commit 628daa8

Browse files
committed
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 <[email protected]>
1 parent ec27329 commit 628daa8

File tree

5 files changed

+229
-21
lines changed

5 files changed

+229
-21
lines changed

arch/powerpc/include/asm/opal.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,12 @@ extern int opal_put_chars(uint32_t vtermno, const char *buf, int total_len);
430430

431431
extern void hvc_opal_init_early(void);
432432

433+
struct rtc_time;
434+
extern int opal_set_rtc_time(struct rtc_time *tm);
435+
extern void opal_get_rtc_time(struct rtc_time *tm);
436+
extern unsigned long opal_get_boot_time(void);
437+
extern void opal_nvram_init(void);
438+
433439
#endif /* __ASSEMBLY__ */
434440

435441
#endif /* __OPAL_H */
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o
2+
obj-y += opal-rtc.o opal-nvram.o
3+
24
obj-$(CONFIG_SMP) += smp.o
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
21+
static unsigned int nvram_size;
22+
23+
static ssize_t opal_nvram_size(void)
24+
{
25+
return nvram_size;
26+
}
27+
28+
static 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+
45+
static 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+
65+
void __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+
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
22+
static 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+
35+
unsigned 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+
56+
void 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+
74+
int 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+
}

arch/powerpc/platforms/powernv/setup.c

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@
2929
#include <asm/machdep.h>
3030
#include <asm/firmware.h>
3131
#include <asm/xics.h>
32+
#include <asm/rtas.h>
3233
#include <asm/opal.h>
34+
#include <asm/xics.h>
3335

3436
#include "powernv.h"
3537

@@ -40,7 +42,9 @@ static void __init pnv_setup_arch(void)
4042

4143
/* XXX PCI */
4244

43-
/* XXX NVRAM */
45+
/* Setup RTC and NVRAM callbacks */
46+
if (firmware_has_feature(FW_FEATURE_OPAL))
47+
opal_nvram_init();
4448

4549
/* Enable NAP mode */
4650
powersave_nap = 1;
@@ -118,30 +122,40 @@ static void __noreturn pnv_halt(void)
118122
pnv_power_off();
119123
}
120124

121-
static unsigned long __init pnv_get_boot_time(void)
122-
{
123-
return 0;
124-
}
125-
126-
static void pnv_get_rtc_time(struct rtc_time *rtc_tm)
125+
static void pnv_progress(char *s, unsigned short hex)
127126
{
128127
}
129128

130-
static int pnv_set_rtc_time(struct rtc_time *tm)
129+
#ifdef CONFIG_KEXEC
130+
static void pnv_kexec_cpu_down(int crash_shutdown, int secondary)
131131
{
132-
return 0;
132+
xics_kexec_teardown_cpu(secondary);
133133
}
134+
#endif /* CONFIG_KEXEC */
134135

135-
static void pnv_progress(char *s, unsigned short hex)
136+
static void __init pnv_setup_machdep_opal(void)
136137
{
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;
137144
}
138145

139-
#ifdef CONFIG_KEXEC
140-
static void pnv_kexec_cpu_down(int crash_shutdown, int secondary)
146+
#ifdef CONFIG_PPC_POWERNV_RTAS
147+
static void __init pnv_setup_machdep_rtas(void)
141148
{
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;
143157
}
144-
#endif /* CONFIG_KEXEC */
158+
#endif /* CONFIG_PPC_POWERNV_RTAS */
145159

146160
static int __init pnv_probe(void)
147161
{
@@ -152,6 +166,13 @@ static int __init pnv_probe(void)
152166

153167
hpte_init_native();
154168

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+
155176
pr_debug("PowerNV detected !\n");
156177

157178
return 1;
@@ -160,16 +181,10 @@ static int __init pnv_probe(void)
160181
define_machine(powernv) {
161182
.name = "PowerNV",
162183
.probe = pnv_probe,
163-
.setup_arch = pnv_setup_arch,
164184
.init_early = pnv_init_early,
185+
.setup_arch = pnv_setup_arch,
165186
.init_IRQ = pnv_init_IRQ,
166187
.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,
173188
.progress = pnv_progress,
174189
.power_save = power7_idle,
175190
.calibrate_decr = generic_calibrate_decr,

0 commit comments

Comments
 (0)