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>
diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c
index 0fac0a6c..4a2b2e2 100644
--- a/arch/powerpc/platforms/powernv/setup.c
+++ b/arch/powerpc/platforms/powernv/setup.c
@@ -29,7 +29,9 @@
 #include <asm/machdep.h>
 #include <asm/firmware.h>
 #include <asm/xics.h>
+#include <asm/rtas.h>
 #include <asm/opal.h>
+#include <asm/xics.h>
 
 #include "powernv.h"
 
@@ -40,7 +42,9 @@
 
 	/* XXX PCI */
 
-	/* XXX NVRAM */
+	/* Setup RTC and NVRAM callbacks */
+	if (firmware_has_feature(FW_FEATURE_OPAL))
+		opal_nvram_init();
 
 	/* Enable NAP mode */
 	powersave_nap = 1;
@@ -118,20 +122,6 @@
 	pnv_power_off();
 }
 
-static unsigned long __init pnv_get_boot_time(void)
-{
-	return 0;
-}
-
-static void pnv_get_rtc_time(struct rtc_time *rtc_tm)
-{
-}
-
-static int pnv_set_rtc_time(struct rtc_time *tm)
-{
-	return 0;
-}
-
 static void pnv_progress(char *s, unsigned short hex)
 {
 }
@@ -143,6 +133,30 @@
 }
 #endif /* CONFIG_KEXEC */
 
+static void __init pnv_setup_machdep_opal(void)
+{
+	ppc_md.get_boot_time = opal_get_boot_time;
+	ppc_md.get_rtc_time = opal_get_rtc_time;
+	ppc_md.set_rtc_time = opal_set_rtc_time;
+	ppc_md.restart = pnv_restart;
+	ppc_md.power_off = pnv_power_off;
+	ppc_md.halt = pnv_halt;
+}
+
+#ifdef CONFIG_PPC_POWERNV_RTAS
+static void __init pnv_setup_machdep_rtas(void)
+{
+	if (rtas_token("get-time-of-day") != RTAS_UNKNOWN_SERVICE) {
+		ppc_md.get_boot_time = rtas_get_boot_time;
+		ppc_md.get_rtc_time = rtas_get_rtc_time;
+		ppc_md.set_rtc_time = rtas_set_rtc_time;
+	}
+	ppc_md.restart = rtas_restart;
+	ppc_md.power_off = rtas_power_off;
+	ppc_md.halt = rtas_halt;
+}
+#endif /* CONFIG_PPC_POWERNV_RTAS */
+
 static int __init pnv_probe(void)
 {
 	unsigned long root = of_get_flat_dt_root();
@@ -152,6 +166,13 @@
 
 	hpte_init_native();
 
+	if (firmware_has_feature(FW_FEATURE_OPAL))
+		pnv_setup_machdep_opal();
+#ifdef CONFIG_PPC_POWERNV_RTAS
+	else if (rtas.base)
+		pnv_setup_machdep_rtas();
+#endif /* CONFIG_PPC_POWERNV_RTAS */
+
 	pr_debug("PowerNV detected !\n");
 
 	return 1;
@@ -160,16 +181,10 @@
 define_machine(powernv) {
 	.name			= "PowerNV",
 	.probe			= pnv_probe,
-	.setup_arch		= pnv_setup_arch,
 	.init_early		= pnv_init_early,
+	.setup_arch		= pnv_setup_arch,
 	.init_IRQ		= pnv_init_IRQ,
 	.show_cpuinfo		= pnv_show_cpuinfo,
-	.restart		= pnv_restart,
-	.power_off		= pnv_power_off,
-	.halt			= pnv_halt,
-	.get_boot_time		= pnv_get_boot_time,
-	.get_rtc_time		= pnv_get_rtc_time,
-	.set_rtc_time		= pnv_set_rtc_time,
 	.progress		= pnv_progress,
 	.power_save             = power7_idle,
 	.calibrate_decr		= generic_calibrate_decr,