powerpc: Put exception configuration in a common place
[linux-2.6-block.git] / arch / powerpc / platforms / pseries / setup.c
index 9cc9b88fce792362e7c012afed4db24ee8016c95..ba7dc126b5e5bb789ab77c0cc4b07fb6473fd51d 100644 (file)
@@ -319,15 +319,23 @@ static void pseries_lpar_idle(void)
  * to ever be a problem in practice we can move this into a kernel thread to
  * finish off the process later in boot.
  */
-long pSeries_enable_reloc_on_exc(void)
+void pseries_enable_reloc_on_exc(void)
 {
        long rc;
        unsigned int delay, total_delay = 0;
 
        while (1) {
                rc = enable_reloc_on_exceptions();
-               if (!H_IS_LONG_BUSY(rc))
-                       return rc;
+               if (!H_IS_LONG_BUSY(rc)) {
+                       if (rc == H_P2) {
+                               pr_info("Relocation on exceptions not"
+                                       " supported\n");
+                       } else if (rc != H_SUCCESS) {
+                               pr_warn("Unable to enable relocation"
+                                       " on exceptions: %ld\n", rc);
+                       }
+                       break;
+               }
 
                delay = get_longbusy_msecs(rc);
                total_delay += delay;
@@ -335,66 +343,81 @@ long pSeries_enable_reloc_on_exc(void)
                        pr_warn("Warning: Giving up waiting to enable "
                                "relocation on exceptions (%u msec)!\n",
                                total_delay);
-                       return rc;
+                       return;
                }
 
                mdelay(delay);
        }
 }
-EXPORT_SYMBOL(pSeries_enable_reloc_on_exc);
+EXPORT_SYMBOL(pseries_enable_reloc_on_exc);
 
-long pSeries_disable_reloc_on_exc(void)
+void pseries_disable_reloc_on_exc(void)
 {
        long rc;
 
        while (1) {
                rc = disable_reloc_on_exceptions();
                if (!H_IS_LONG_BUSY(rc))
-                       return rc;
+                       break;
                mdelay(get_longbusy_msecs(rc));
        }
+       if (rc != H_SUCCESS)
+               pr_warning("Warning: Failed to disable relocation on "
+                          "exceptions: %ld\n", rc);
 }
-EXPORT_SYMBOL(pSeries_disable_reloc_on_exc);
+EXPORT_SYMBOL(pseries_disable_reloc_on_exc);
 
 #ifdef CONFIG_KEXEC
 static void pSeries_machine_kexec(struct kimage *image)
 {
-       long rc;
-
-       if (firmware_has_feature(FW_FEATURE_SET_MODE)) {
-               rc = pSeries_disable_reloc_on_exc();
-               if (rc != H_SUCCESS)
-                       pr_warning("Warning: Failed to disable relocation on "
-                                  "exceptions: %ld\n", rc);
-       }
+       if (firmware_has_feature(FW_FEATURE_SET_MODE))
+               pseries_disable_reloc_on_exc();
 
        default_machine_kexec(image);
 }
 #endif
 
 #ifdef __LITTLE_ENDIAN__
-long pseries_big_endian_exceptions(void)
+void pseries_big_endian_exceptions(void)
 {
        long rc;
 
        while (1) {
                rc = enable_big_endian_exceptions();
                if (!H_IS_LONG_BUSY(rc))
-                       return rc;
+                       break;
                mdelay(get_longbusy_msecs(rc));
        }
+
+       /*
+        * At this point it is unlikely panic() will get anything
+        * out to the user, since this is called very late in kexec
+        * but at least this will stop us from continuing on further
+        * and creating an even more difficult to debug situation.
+        *
+        * There is a known problem when kdump'ing, if cpus are offline
+        * the above call will fail. Rather than panicking again, keep
+        * going and hope the kdump kernel is also little endian, which
+        * it usually is.
+        */
+       if (rc && !kdump_in_progress())
+               panic("Could not enable big endian exceptions");
 }
 
-static long pseries_little_endian_exceptions(void)
+void pseries_little_endian_exceptions(void)
 {
        long rc;
 
        while (1) {
                rc = enable_little_endian_exceptions();
                if (!H_IS_LONG_BUSY(rc))
-                       return rc;
+                       break;
                mdelay(get_longbusy_msecs(rc));
        }
+       if (rc) {
+               ppc_md.progress("H_SET_MODE LE exception fail", 0);
+               panic("Could not enable little endian exceptions");
+       }
 }
 #endif
 
@@ -464,18 +487,6 @@ static void __init pSeries_setup_arch(void)
        }
 
        ppc_md.pcibios_root_bridge_prepare = pseries_root_bridge_prepare;
-
-       if (firmware_has_feature(FW_FEATURE_SET_MODE)) {
-               long rc;
-
-               rc = pSeries_enable_reloc_on_exc();
-               if (rc == H_P2) {
-                       pr_info("Relocation on exceptions not supported\n");
-               } else if (rc != H_SUCCESS) {
-                       pr_warn("Unable to enable relocation on exceptions: "
-                               "%ld\n", rc);
-               }
-       }
 }
 
 static int __init pSeries_init_panel(void)
@@ -678,23 +689,6 @@ static int __init pSeries_probe(void)
 
        pr_debug("pSeries detected, looking for LPAR capability...\n");
 
-
-#ifdef __LITTLE_ENDIAN__
-       if (firmware_has_feature(FW_FEATURE_SET_MODE)) {
-               long rc;
-               /*
-                * Tell the hypervisor that we want our exceptions to
-                * be taken in little endian mode. If this fails we don't
-                * want to use BUG() because it will trigger an exception.
-                */
-               rc = pseries_little_endian_exceptions();
-               if (rc) {
-                       ppc_md.progress("H_SET_MODE LE exception fail", 0);
-                       panic("Could not enable little endian exceptions");
-               }
-       }
-#endif
-
        if (firmware_has_feature(FW_FEATURE_LPAR))
                hpte_init_lpar();
        else