powerpc: Merge remaining RTAS code

This moves rtas-proc.c and rtas_flash.c into arch/powerpc/kernel, since
cell wants them as well as pseries (and chrp can use rtas-proc.c too,
at least in principle).  rtas_fw.c is gone, with its bits moved into
rtas_flash.c and rtas.c.

Signed-off-by: Paul Mackerras <paulus@samba.org>
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 1c44a1d..3cf03ab 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -278,7 +278,6 @@
 	select PPC_I8259
 	select PPC_RTAS
 	select RTAS_ERROR_LOGGING
-	select RTAS_FW
 	default y
 
 config PPC_CHRP
@@ -324,7 +323,6 @@
 	bool "  Cell Broadband Processor Architecture"
 	depends on PPC_MULTIPLATFORM && PPC64
 	select PPC_RTAS
-	select RTAS_FW
 	select MMIO_NVRAM
 
 config PPC_OF
@@ -356,10 +354,14 @@
 	depends on PPC_RTAS
 	default n
 
-config RTAS_FW
-	bool
+config RTAS_PROC
+	bool "Proc interface to RTAS"
 	depends on PPC_RTAS
-	default n
+	default y
+
+config RTAS_FLASH
+	tristate "Firmware flash interface"
+	depends on PPC64 && RTAS_PROC
 
 config MMIO_NVRAM
 	bool
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index abad305..601ddbf 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -18,7 +18,8 @@
 obj-$(CONFIG_POWER4)		+= idle_power4.o
 obj-$(CONFIG_PPC_OF)		+= of_device.o
 obj-$(CONFIG_PPC_RTAS)		+= rtas.o
-obj-$(CONFIG_RTAS_FW)		+= rtas_fw.o
+obj-$(CONFIG_RTAS_FLASH)	+= rtas_flash.o
+obj-$(CONFIG_RTAS_PROC)		+= rtas-proc.o
 obj-$(CONFIG_IBMVIO)		+= vio.o
 
 ifeq ($(CONFIG_PPC_MERGE),y)
diff --git a/arch/ppc64/kernel/rtas-proc.c b/arch/powerpc/kernel/rtas-proc.c
similarity index 100%
rename from arch/ppc64/kernel/rtas-proc.c
rename to arch/powerpc/kernel/rtas-proc.c
diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c
index 4d22eee..b7fc2d8 100644
--- a/arch/powerpc/kernel/rtas.c
+++ b/arch/powerpc/kernel/rtas.c
@@ -43,6 +43,13 @@
 unsigned long rtas_rmo_buf;
 
 /*
+ * If non-NULL, this gets called when the kernel terminates.
+ * This is done like this so rtas_flash can be a module.
+ */
+void (*rtas_flash_term_hook)(int);
+EXPORT_SYMBOL(rtas_flash_term_hook);
+
+/*
  * call_rtas_display_status and call_rtas_display_status_delay
  * are designed only for very early low-level debugging, which
  * is why the token is hard-coded to 10.
@@ -206,6 +213,7 @@
  
 	spin_unlock(&progress_lock);
 }
+EXPORT_SYMBOL(rtas_progress);		/* needed by rtas_flash module */
 
 int rtas_token(const char *service)
 {
@@ -492,6 +500,8 @@
 
 void rtas_restart(char *cmd)
 {
+	if (rtas_flash_term_hook)
+		rtas_flash_term_hook(SYS_RESTART);
 	printk("RTAS system-reboot returned %d\n",
 	       rtas_call(rtas_token("system-reboot"), 0, 1, NULL));
 	for (;;);
@@ -499,6 +509,8 @@
 
 void rtas_power_off(void)
 {
+	if (rtas_flash_term_hook)
+		rtas_flash_term_hook(SYS_POWER_OFF);
 	/* allow power on only with power button press */
 	printk("RTAS power-off returned %d\n",
 	       rtas_call(rtas_token("power-off"), 2, 1, NULL, -1, -1));
@@ -507,7 +519,12 @@
 
 void rtas_halt(void)
 {
-	rtas_power_off();
+	if (rtas_flash_term_hook)
+		rtas_flash_term_hook(SYS_HALT);
+	/* allow power on only with power button press */
+	printk("RTAS power-off returned %d\n",
+	       rtas_call(rtas_token("power-off"), 2, 1, NULL, -1, -1));
+	for (;;);
 }
 
 /* Must be in the RMO region, so we place it here */
diff --git a/arch/ppc64/kernel/rtas_flash.c b/arch/powerpc/kernel/rtas_flash.c
similarity index 83%
rename from arch/ppc64/kernel/rtas_flash.c
rename to arch/powerpc/kernel/rtas_flash.c
index 923e2e2..5050009 100644
--- a/arch/ppc64/kernel/rtas_flash.c
+++ b/arch/powerpc/kernel/rtas_flash.c
@@ -19,6 +19,7 @@
 #include <asm/delay.h>
 #include <asm/uaccess.h>
 #include <asm/rtas.h>
+#include <asm/abs_addr.h>
 
 #define MODULE_VERS "1.0"
 #define MODULE_NAME "rtas_flash"
@@ -71,10 +72,36 @@
 #define VALIDATE_BUF_SIZE 4096    
 #define RTAS_MSG_MAXLEN   64
 
+struct flash_block {
+	char *data;
+	unsigned long length;
+};
+
+/* This struct is very similar but not identical to
+ * that needed by the rtas flash update.
+ * All we need to do for rtas is rewrite num_blocks
+ * into a version/length and translate the pointers
+ * to absolute.
+ */
+#define FLASH_BLOCKS_PER_NODE ((PAGE_SIZE - 16) / sizeof(struct flash_block))
+struct flash_block_list {
+	unsigned long num_blocks;
+	struct flash_block_list *next;
+	struct flash_block blocks[FLASH_BLOCKS_PER_NODE];
+};
+struct flash_block_list_header { /* just the header of flash_block_list */
+	unsigned long num_blocks;
+	struct flash_block_list *next;
+};
+
+static struct flash_block_list_header rtas_firmware_flash_list = {0, NULL};
+
+#define FLASH_BLOCK_LIST_VERSION (1UL)
+
 /* Local copy of the flash block list.
  * We only allow one open of the flash proc file and create this
- * list as we go.  This list will be put in the kernel's
- * rtas_firmware_flash_list global var once it is fully read.
+ * list as we go.  This list will be put in the
+ * rtas_firmware_flash_list var once it is fully read.
  *
  * For convenience as we build the list we use virtual addrs,
  * we do not fill in the version number, and the length field
@@ -562,6 +589,86 @@
 	return 0;
 }
 
+static void rtas_flash_firmware(int reboot_type)
+{
+	unsigned long image_size;
+	struct flash_block_list *f, *next, *flist;
+	unsigned long rtas_block_list;
+	int i, status, update_token;
+
+	if (rtas_firmware_flash_list.next == NULL)
+		return;		/* nothing to do */
+
+	if (reboot_type != SYS_RESTART) {
+		printk(KERN_ALERT "FLASH: firmware flash requires a reboot\n");
+		printk(KERN_ALERT "FLASH: the firmware image will NOT be flashed\n");
+		return;
+	}
+
+	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;
+	}
+}
+
 static void remove_flash_pde(struct proc_dir_entry *dp)
 {
 	if (dp) {
@@ -701,6 +808,7 @@
 	if (rc != 0)
 		goto cleanup;
 
+	rtas_flash_term_hook = rtas_flash_firmware;
 	return 0;
 
 cleanup:
@@ -714,6 +822,7 @@
 
 void __exit rtas_flash_cleanup(void)
 {
+	rtas_flash_term_hook = NULL;
 	remove_flash_pde(firmware_flash_pde);
 	remove_flash_pde(firmware_update_pde);
 	remove_flash_pde(validate_pde);
diff --git a/arch/powerpc/kernel/rtas_fw.c b/arch/powerpc/kernel/rtas_fw.c
deleted file mode 100644
index 448922e..0000000
--- a/arch/powerpc/kernel/rtas_fw.c
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- *
- * Procedures for firmware flash updates.
- *
- * 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>
-
-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/Kconfig b/arch/powerpc/platforms/pseries/Kconfig
index 2d57f58..e3fc340 100644
--- a/arch/powerpc/platforms/pseries/Kconfig
+++ b/arch/powerpc/platforms/pseries/Kconfig
@@ -21,15 +21,6 @@
 	depends on PPC_PSERIES
 	default y if !EMBEDDED
 
-config RTAS_PROC
-	bool "Proc interface to RTAS"
-	depends on PPC_RTAS
-	default y
-
-config RTAS_FLASH
-	tristate "Firmware flash interface"
-	depends on PPC64 && RTAS_PROC
-
 config SCANLOG
 	tristate "Scanlog dump interface"
 	depends on RTAS_PROC && PPC_PSERIES
diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile
index 91909a8..b9938fe 100644
--- a/arch/powerpc/platforms/pseries/Makefile
+++ b/arch/powerpc/platforms/pseries/Makefile
@@ -1,5 +1,5 @@
 obj-y			:= pci.o lpar.o hvCall.o nvram.o reconfig.o \
-			   setup.o iommu.o ras.o
+			   setup.o iommu.o ras.o rtasd.o
 obj-$(CONFIG_SMP)	+= smp.o
 obj-$(CONFIG_IBMVIO)	+= vio.o
 obj-$(CONFIG_XICS)	+= xics.o
diff --git a/arch/ppc64/kernel/rtasd.c b/arch/powerpc/platforms/pseries/rtasd.c
similarity index 100%
rename from arch/ppc64/kernel/rtasd.c
rename to arch/powerpc/platforms/pseries/rtasd.c
diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c
index c0a3d91..f73d691 100644
--- a/arch/powerpc/platforms/pseries/setup.c
+++ b/arch/powerpc/platforms/pseries/setup.c
@@ -589,9 +589,9 @@
 	.pcibios_fixup		= pSeries_final_fixup,
 	.pci_probe_mode		= pSeries_pci_probe_mode,
 	.irq_bus_setup		= pSeries_irq_bus_setup,
-	.restart		= rtas_fw_restart,
-	.power_off		= rtas_fw_power_off,
-	.halt			= rtas_fw_halt,
+	.restart		= rtas_restart,
+	.power_off		= rtas_power_off,
+	.halt			= rtas_halt,
 	.panic			= rtas_os_term,
 	.cpu_die		= pSeries_mach_cpu_die,
 	.get_boot_time		= rtas_get_boot_time,
diff --git a/arch/ppc64/kernel/Makefile b/arch/ppc64/kernel/Makefile
index 990df09..74892ad 100644
--- a/arch/ppc64/kernel/Makefile
+++ b/arch/ppc64/kernel/Makefile
@@ -29,19 +29,17 @@
 obj-$(CONFIG_PPC_MULTIPLATFORM) += prom_init.o
 endif
 
-obj-$(CONFIG_PPC_PSERIES) += rtasd.o udbg_16550.o
+obj-$(CONFIG_PPC_PSERIES) += udbg_16550.o
 
 obj-$(CONFIG_KEXEC)		+= machine_kexec.o
 obj-$(CONFIG_EEH)		+= eeh.o
 obj-$(CONFIG_PROC_FS)		+= proc_ppc64.o
-obj-$(CONFIG_RTAS_FLASH)	+= rtas_flash.o
 obj-$(CONFIG_SMP)		+= smp.o
 obj-$(CONFIG_MODULES)		+= module.o
 ifneq ($(CONFIG_PPC_MERGE),y)
 obj-$(CONFIG_MODULES)		+= ppc_ksyms.o
 endif
 obj-$(CONFIG_PPC_RTAS)		+= rtas_pci.o
-obj-$(CONFIG_RTAS_PROC)		+= rtas-proc.o
 obj-$(CONFIG_SCANLOG)		+= scanlog.o
 obj-$(CONFIG_LPARCFG)		+= lparcfg.o
 obj-$(CONFIG_HVC_CONSOLE)	+= hvconsole.o
diff --git a/include/asm-powerpc/rtas.h b/include/asm-powerpc/rtas.h
index d9fd786..d1bb611 100644
--- a/include/asm-powerpc/rtas.h
+++ b/include/asm-powerpc/rtas.h
@@ -149,31 +149,11 @@
 	unsigned char buffer[1];
 };
 
-struct flash_block {
-	char *data;
-	unsigned long length;
-};
-
-/* This struct is very similar but not identical to
- * that needed by the rtas flash update.
- * All we need to do for rtas is rewrite num_blocks
- * into a version/length and translate the pointers
- * to absolute.
+/*
+ * This can be set by the rtas_flash module so that it can get called
+ * as the absolutely last thing before the kernel terminates.
  */
-#define FLASH_BLOCKS_PER_NODE ((PAGE_SIZE - 16) / sizeof(struct flash_block))
-struct flash_block_list {
-	unsigned long num_blocks;
-	struct flash_block_list *next;
-	struct flash_block blocks[FLASH_BLOCKS_PER_NODE];
-};
-struct flash_block_list_header { /* just the header of flash_block_list */
-	unsigned long num_blocks;
-	struct flash_block_list *next;
-};
-extern struct flash_block_list_header rtas_firmware_flash_list;
-void rtas_fw_restart(char *cmd);
-void rtas_fw_power_off(void);
-void rtas_fw_halt(void);
+extern void (*rtas_flash_term_hook)(int);
 
 extern struct rtas_t rtas;