powerpc: Merge rtas.c into arch/powerpc/kernel

This splits arch/ppc64/kernel/rtas.c into arch/powerpc/kernel/rtas.c,
which contains generic RTAS functions useful on any CHRP platform,
and arch/powerpc/platforms/pseries/rtas-fw.[ch], which contain
some pSeries-specific firmware flashing bits.  The parts of rtas.c
that are to do with pSeries-specific error logging are protected
by a new CONFIG_RTAS_ERROR_LOGGING symbol.  The inclusion of rtas.o
is controlled by the CONFIG_PPC_RTAS symbol, and the relevant
platforms select that.

Signed-off-by: Paul Mackerras <paulus@samba.org>
diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig
index 7a3b6fc..2d57f58 100644
--- a/arch/powerpc/platforms/pseries/Kconfig
+++ b/arch/powerpc/platforms/pseries/Kconfig
@@ -21,11 +21,6 @@
 	depends on PPC_PSERIES
 	default y if !EMBEDDED
 
-config PPC_RTAS
-	bool
-	depends on PPC_PSERIES || PPC_BPA
-	default y
-
 config RTAS_PROC
 	bool "Proc interface to RTAS"
 	depends on PPC_RTAS
diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile
index 4c81730..d5c160b 100644
--- a/arch/powerpc/platforms/pseries/Makefile
+++ b/arch/powerpc/platforms/pseries/Makefile
@@ -1,4 +1,4 @@
 obj-y			:= pci.o lpar.o hvCall.o nvram.o reconfig.o \
-			   setup.o iommu.o ras.o
+			   setup.o iommu.o rtas-fw.o ras.o
 obj-$(CONFIG_SMP)	+= smp.o
 obj-$(CONFIG_IBMVIO)	+= vio.o
diff --git a/arch/powerpc/platforms/pseries/rtas-fw.c b/arch/powerpc/platforms/pseries/rtas-fw.c
new file mode 100644
index 0000000..15d81d7
--- /dev/null
+++ b/arch/powerpc/platforms/pseries/rtas-fw.c
@@ -0,0 +1,138 @@
+/*
+ *
+ * Procedures for firmware flash updates on pSeries systems.
+ *
+ * Peter Bergner, IBM	March 2001.
+ * Copyright (C) 2001 IBM.
+ *
+ *      This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License
+ *      as published by the Free Software Foundation; either version
+ *      2 of the License, or (at your option) any later version.
+ */
+
+#include <stdarg.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <asm/prom.h>
+#include <asm/rtas.h>
+#include <asm/semaphore.h>
+#include <asm/machdep.h>
+#include <asm/page.h>
+#include <asm/param.h>
+#include <asm/system.h>
+#include <asm/abs_addr.h>
+#include <asm/udbg.h>
+#include <asm/delay.h>
+#include <asm/uaccess.h>
+#include <asm/systemcfg.h>
+
+#include "rtas-fw.h"
+
+struct flash_block_list_header rtas_firmware_flash_list = {0, NULL};
+
+#define FLASH_BLOCK_LIST_VERSION (1UL)
+
+static void rtas_flash_firmware(void)
+{
+	unsigned long image_size;
+	struct flash_block_list *f, *next, *flist;
+	unsigned long rtas_block_list;
+	int i, status, update_token;
+
+	update_token = rtas_token("ibm,update-flash-64-and-reboot");
+	if (update_token == RTAS_UNKNOWN_SERVICE) {
+		printk(KERN_ALERT "FLASH: ibm,update-flash-64-and-reboot is not available -- not a service partition?\n");
+		printk(KERN_ALERT "FLASH: firmware will not be flashed\n");
+		return;
+	}
+
+	/* NOTE: the "first" block list is a global var with no data
+	 * blocks in the kernel data segment.  We do this because
+	 * we want to ensure this block_list addr is under 4GB.
+	 */
+	rtas_firmware_flash_list.num_blocks = 0;
+	flist = (struct flash_block_list *)&rtas_firmware_flash_list;
+	rtas_block_list = virt_to_abs(flist);
+	if (rtas_block_list >= 4UL*1024*1024*1024) {
+		printk(KERN_ALERT "FLASH: kernel bug...flash list header addr above 4GB\n");
+		return;
+	}
+
+	printk(KERN_ALERT "FLASH: preparing saved firmware image for flash\n");
+	/* Update the block_list in place. */
+	image_size = 0;
+	for (f = flist; f; f = next) {
+		/* Translate data addrs to absolute */
+		for (i = 0; i < f->num_blocks; i++) {
+			f->blocks[i].data = (char *)virt_to_abs(f->blocks[i].data);
+			image_size += f->blocks[i].length;
+		}
+		next = f->next;
+		/* Don't translate NULL pointer for last entry */
+		if (f->next)
+			f->next = (struct flash_block_list *)virt_to_abs(f->next);
+		else
+			f->next = NULL;
+		/* make num_blocks into the version/length field */
+		f->num_blocks = (FLASH_BLOCK_LIST_VERSION << 56) | ((f->num_blocks+1)*16);
+	}
+
+	printk(KERN_ALERT "FLASH: flash image is %ld bytes\n", image_size);
+	printk(KERN_ALERT "FLASH: performing flash and reboot\n");
+	rtas_progress("Flashing        \n", 0x0);
+	rtas_progress("Please Wait...  ", 0x0);
+	printk(KERN_ALERT "FLASH: this will take several minutes.  Do not power off!\n");
+	status = rtas_call(update_token, 1, 1, NULL, rtas_block_list);
+	switch (status) {	/* should only get "bad" status */
+	    case 0:
+		printk(KERN_ALERT "FLASH: success\n");
+		break;
+	    case -1:
+		printk(KERN_ALERT "FLASH: hardware error.  Firmware may not be not flashed\n");
+		break;
+	    case -3:
+		printk(KERN_ALERT "FLASH: image is corrupt or not correct for this platform.  Firmware not flashed\n");
+		break;
+	    case -4:
+		printk(KERN_ALERT "FLASH: flash failed when partially complete.  System may not reboot\n");
+		break;
+	    default:
+		printk(KERN_ALERT "FLASH: unknown flash return code %d\n", status);
+		break;
+	}
+}
+
+void rtas_flash_bypass_warning(void)
+{
+	printk(KERN_ALERT "FLASH: firmware flash requires a reboot\n");
+	printk(KERN_ALERT "FLASH: the firmware image will NOT be flashed\n");
+}
+
+
+void rtas_fw_restart(char *cmd)
+{
+	if (rtas_firmware_flash_list.next)
+		rtas_flash_firmware();
+	rtas_restart(cmd);
+}
+
+void rtas_fw_power_off(void)
+{
+	if (rtas_firmware_flash_list.next)
+		rtas_flash_bypass_warning();
+	rtas_power_off();
+}
+
+void rtas_fw_halt(void)
+{
+	if (rtas_firmware_flash_list.next)
+		rtas_flash_bypass_warning();
+	rtas_halt();
+}
+
+EXPORT_SYMBOL(rtas_firmware_flash_list);
diff --git a/arch/powerpc/platforms/pseries/rtas-fw.h b/arch/powerpc/platforms/pseries/rtas-fw.h
new file mode 100644
index 0000000..e70fa69
--- /dev/null
+++ b/arch/powerpc/platforms/pseries/rtas-fw.h
@@ -0,0 +1,3 @@
+void rtas_fw_restart(char *cmd);
+void rtas_fw_power_off(void);
+void rtas_fw_halt(void);
diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c
index 0fa5bea..7e7e556 100644
--- a/arch/powerpc/platforms/pseries/setup.c
+++ b/arch/powerpc/platforms/pseries/setup.c
@@ -1,5 +1,5 @@
 /*
- *  linux/arch/ppc/kernel/setup.c
+ *  64-bit pSeries and RS/6000 setup code.
  *
  *  Copyright (C) 1995  Linus Torvalds
  *  Adapted from 'alpha' version by Gary Thomas
@@ -67,6 +67,8 @@
 #include <asm/i8259.h>
 #include <asm/udbg.h>
 
+#include "rtas-fw.h"
+
 #ifdef DEBUG
 #define DBG(fmt...) udbg_printf(fmt)
 #else
@@ -589,9 +591,9 @@
 	.pcibios_fixup		= pSeries_final_fixup,
 	.pci_probe_mode		= pSeries_pci_probe_mode,
 	.irq_bus_setup		= pSeries_irq_bus_setup,
-	.restart		= rtas_restart,
-	.power_off		= rtas_power_off,
-	.halt			= rtas_halt,
+	.restart		= rtas_fw_restart,
+	.power_off		= rtas_fw_power_off,
+	.halt			= rtas_fw_halt,
 	.panic			= rtas_os_term,
 	.cpu_die		= pSeries_mach_cpu_die,
 	.get_boot_time		= rtas_get_boot_time,