]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blobdiff - arch/powerpc/kernel/prom_init.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/bunk/trivial
[mirror_ubuntu-zesty-kernel.git] / arch / powerpc / kernel / prom_init.c
index 6dc33d19fc2abcfcf1c6ce3726c5cbf4adcf49a6..7881ec96ef117ff3a02580c95c236771e0593a73 100644 (file)
@@ -94,11 +94,17 @@ extern const struct linux_logo logo_linux_clut224;
 #ifdef CONFIG_PPC64
 #define RELOC(x)        (*PTRRELOC(&(x)))
 #define ADDR(x)                (u32) add_reloc_offset((unsigned long)(x))
+#define OF_WORKAROUNDS 0
 #else
 #define RELOC(x)       (x)
 #define ADDR(x)                (u32) (x)
+#define OF_WORKAROUNDS of_workarounds
+int of_workarounds;
 #endif
 
+#define OF_WA_CLAIM    1       /* do phys/virt claim separately, then map */
+#define OF_WA_LONGTRAIL        2       /* work around longtrail bugs */
+
 #define PROM_BUG() do {                                                \
         prom_printf("kernel BUG at %s line 0x%x!\n",           \
                    RELOC(__FILE__), __LINE__);                 \
@@ -111,11 +117,6 @@ extern const struct linux_logo logo_linux_clut224;
 #define prom_debug(x...)
 #endif
 
-#ifdef CONFIG_PPC32
-#define PLATFORM_POWERMAC      _MACH_Pmac
-#define PLATFORM_CHRP          _MACH_chrp
-#endif
-
 
 typedef u32 prom_arg_t;
 
@@ -128,15 +129,16 @@ struct prom_args {
 
 struct prom_t {
        ihandle root;
-       ihandle chosen;
+       phandle chosen;
        int cpu;
        ihandle stdout;
        ihandle mmumap;
+       ihandle memory;
 };
 
 struct mem_map_entry {
-       unsigned long   base;
-       unsigned long   size;
+       u64     base;
+       u64     size;
 };
 
 typedef u32 cell_t;
@@ -190,6 +192,11 @@ static unsigned long __initdata alloc_bottom;
 static unsigned long __initdata rmo_top;
 static unsigned long __initdata ram_top;
 
+#ifdef CONFIG_KEXEC
+static unsigned long __initdata prom_crashk_base;
+static unsigned long __initdata prom_crashk_size;
+#endif
+
 static struct mem_map_entry __initdata mem_reserve_map[MEM_RESERVE_MAP_SIZE];
 static int __initdata mem_reserve_cnt;
 
@@ -263,7 +270,7 @@ static int __init call_prom_ret(const char *service, int nargs, int nret,
        va_end(list);
 
        for (i = 0; i < nret; i++)
-               rets[nargs+i] = 0;
+               args.args[nargs+i] = 0;
 
        if (enter_prom(&args, RELOC(prom_entry)) < 0)
                return PROM_ERROR;
@@ -360,16 +367,36 @@ static void __init prom_printf(const char *format, ...)
 static unsigned int __init prom_claim(unsigned long virt, unsigned long size,
                                unsigned long align)
 {
-       int ret;
        struct prom_t *_prom = &RELOC(prom);
 
-       ret = call_prom("claim", 3, 1, (prom_arg_t)virt, (prom_arg_t)size,
-                       (prom_arg_t)align);
-       if (ret != -1 && _prom->mmumap != 0)
-               /* old pmacs need us to map as well */
+       if (align == 0 && (OF_WORKAROUNDS & OF_WA_CLAIM)) {
+               /*
+                * Old OF requires we claim physical and virtual separately
+                * and then map explicitly (assuming virtual mode)
+                */
+               int ret;
+               prom_arg_t result;
+
+               ret = call_prom_ret("call-method", 5, 2, &result,
+                                   ADDR("claim"), _prom->memory,
+                                   align, size, virt);
+               if (ret != 0 || result == -1)
+                       return -1;
+               ret = call_prom_ret("call-method", 5, 2, &result,
+                                   ADDR("claim"), _prom->mmumap,
+                                   align, size, virt);
+               if (ret != 0) {
+                       call_prom("call-method", 4, 1, ADDR("release"),
+                                 _prom->memory, size, virt);
+                       return -1;
+               }
+               /* the 0x12 is M (coherence) + PP == read/write */
                call_prom("call-method", 6, 1,
-                         ADDR("map"), _prom->mmumap, 0, size, virt, virt);
-       return ret;
+                         ADDR("map"), _prom->mmumap, 0x12, size, virt, virt);
+               return virt;
+       }
+       return call_prom("claim", 3, 1, (prom_arg_t)virt, (prom_arg_t)size,
+                        (prom_arg_t)align);
 }
 
 static void __init __attribute__((noreturn)) prom_panic(const char *reason)
@@ -415,11 +442,52 @@ static int inline prom_getproplen(phandle node, const char *pname)
        return call_prom("getproplen", 2, 1, node, ADDR(pname));
 }
 
-static int inline prom_setprop(phandle node, const char *pname,
-                              void *value, size_t valuelen)
+static void add_string(char **str, const char *q)
 {
-       return call_prom("setprop", 4, 1, node, ADDR(pname),
-                        (u32)(unsigned long) value, (u32) valuelen);
+       char *p = *str;
+
+       while (*q)
+               *p++ = *q++;
+       *p++ = ' ';
+       *str = p;
+}
+
+static char *tohex(unsigned int x)
+{
+       static char digits[] = "0123456789abcdef";
+       static char result[9];
+       int i;
+
+       result[8] = 0;
+       i = 8;
+       do {
+               --i;
+               result[i] = digits[x & 0xf];
+               x >>= 4;
+       } while (x != 0 && i > 0);
+       return &result[i];
+}
+
+static int __init prom_setprop(phandle node, const char *nodename,
+                              const char *pname, void *value, size_t valuelen)
+{
+       char cmd[256], *p;
+
+       if (!(OF_WORKAROUNDS & OF_WA_LONGTRAIL))
+               return call_prom("setprop", 4, 1, node, ADDR(pname),
+                                (u32)(unsigned long) value, (u32) valuelen);
+
+       /* gah... setprop doesn't work on longtrail, have to use interpret */
+       p = cmd;
+       add_string(&p, "dev");
+       add_string(&p, nodename);
+       add_string(&p, tohex((u32)(unsigned long) value));
+       add_string(&p, tohex(valuelen));
+       add_string(&p, tohex(ADDR(pname)));
+       add_string(&p, tohex(strlen(RELOC(pname))));
+       add_string(&p, "property");
+       *p = 0;
+       return call_prom("interpret", 1, 1, (u32)(unsigned long) cmd);
 }
 
 /* We can't use the standard versions because of RELOC headaches. */
@@ -490,7 +558,8 @@ unsigned long prom_memparse(const char *ptr, const char **retptr)
 static void __init early_cmdline_parse(void)
 {
        struct prom_t *_prom = &RELOC(prom);
-       char *opt, *p;
+       const char *opt;
+       char *p;
        int l = 0;
 
        RELOC(prom_cmd_line[0]) = 0;
@@ -527,6 +596,35 @@ static void __init early_cmdline_parse(void)
                RELOC(prom_memory_limit) = ALIGN(RELOC(prom_memory_limit), 0x1000000);
 #endif
        }
+
+#ifdef CONFIG_KEXEC
+       /*
+        * crashkernel=size@addr specifies the location to reserve for
+        * crash kernel.
+        */
+       opt = strstr(RELOC(prom_cmd_line), RELOC("crashkernel="));
+       if (opt) {
+               opt += 12;
+               RELOC(prom_crashk_size) = 
+                       prom_memparse(opt, (const char **)&opt);
+
+               if (ALIGN(RELOC(prom_crashk_size), 0x1000000) !=
+                       RELOC(prom_crashk_size)) {
+                       prom_printf("Warning: crashkernel size is not "
+                                       "aligned to 16MB\n");
+               }
+
+               /*
+                * At present, the crash kernel always run at 32MB.
+                * Just ignore whatever user passed.
+                */
+               RELOC(prom_crashk_base) = 0x2000000;
+               if (*opt == '@') {
+                       prom_printf("Warning: PPC64 kdump kernel always runs "
+                                       "at 32 MB\n");
+               }
+       }
+#endif
 }
 
 #ifdef CONFIG_PPC_PSERIES
@@ -800,9 +898,9 @@ static unsigned long __init prom_next_cell(int s, cell_t **cellp)
  * If problems seem to show up, it would be a good start to track
  * them down.
  */
-static void reserve_mem(unsigned long base, unsigned long size)
+static void reserve_mem(u64 base, u64 size)
 {
-       unsigned long top = base + size;
+       u64 top = base + size;
        unsigned long cnt = RELOC(mem_reserve_cnt);
 
        if (size == 0)
@@ -948,6 +1046,12 @@ static void __init prom_init_mem(void)
        prom_printf("  alloc_top_hi : %x\n", RELOC(alloc_top_high));
        prom_printf("  rmo_top      : %x\n", RELOC(rmo_top));
        prom_printf("  ram_top      : %x\n", RELOC(ram_top));
+#ifdef CONFIG_KEXEC
+       if (RELOC(prom_crashk_base)) {
+               prom_printf("  crashk_base  : %x\n",  RELOC(prom_crashk_base));
+               prom_printf("  crashk_size  : %x\n", RELOC(prom_crashk_size));
+       }
+#endif
 }
 
 
@@ -980,7 +1084,7 @@ static void __init prom_instantiate_rtas(void)
 
        rtas_inst = call_prom("open", 1, 1, ADDR("/rtas"));
        if (!IHANDLE_VALID(rtas_inst)) {
-               prom_printf("opening rtas package failed");
+               prom_printf("opening rtas package failed (%x)\n", rtas_inst);
                return;
        }
 
@@ -988,7 +1092,7 @@ static void __init prom_instantiate_rtas(void)
 
        if (call_prom_ret("call-method", 3, 2, &entry,
                          ADDR("instantiate-rtas"),
-                         rtas_inst, base) == PROM_ERROR
+                         rtas_inst, base) != 0
            || entry == 0) {
                prom_printf(" failed\n");
                return;
@@ -997,8 +1101,10 @@ static void __init prom_instantiate_rtas(void)
 
        reserve_mem(base, size);
 
-       prom_setprop(rtas_node, "linux,rtas-base", &base, sizeof(base));
-       prom_setprop(rtas_node, "linux,rtas-entry", &entry, sizeof(entry));
+       prom_setprop(rtas_node, "/rtas", "linux,rtas-base",
+                    &base, sizeof(base));
+       prom_setprop(rtas_node, "/rtas", "linux,rtas-entry",
+                    &entry, sizeof(entry));
 
        prom_debug("rtas base     = 0x%x\n", base);
        prom_debug("rtas entry    = 0x%x\n", entry);
@@ -1089,10 +1195,6 @@ static void __init prom_initialize_tce_table(void)
                if (base < local_alloc_bottom)
                        local_alloc_bottom = base;
 
-               /* Save away the TCE table attributes for later use. */
-               prom_setprop(node, "linux,tce-base", &base, sizeof(base));
-               prom_setprop(node, "linux,tce-size", &minsize, sizeof(minsize));
-
                /* It seems OF doesn't null-terminate the path :-( */
                memset(path, 0, sizeof(path));
                /* Call OF to setup the TCE hardware */
@@ -1101,6 +1203,10 @@ static void __init prom_initialize_tce_table(void)
                        prom_printf("package-to-path failed\n");
                }
 
+               /* Save away the TCE table attributes for later use. */
+               prom_setprop(node, path, "linux,tce-base", &base, sizeof(base));
+               prom_setprop(node, path, "linux,tce-size", &minsize, sizeof(minsize));
+
                prom_debug("TCE table: %s\n", path);
                prom_debug("\tnode = 0x%x\n", node);
                prom_debug("\tbase = 0x%x\n", base);
@@ -1342,6 +1448,7 @@ static void __init prom_init_client_services(unsigned long pp)
 /*
  * For really old powermacs, we need to map things we claim.
  * For that, we need the ihandle of the mmu.
+ * Also, on the longtrail, we need to work around other bugs.
  */
 static void __init prom_find_mmu(void)
 {
@@ -1355,12 +1462,19 @@ static void __init prom_find_mmu(void)
        if (prom_getprop(oprom, "model", version, sizeof(version)) <= 0)
                return;
        version[sizeof(version) - 1] = 0;
-       prom_printf("OF version is '%s'\n", version);
        /* XXX might need to add other versions here */
-       if (strcmp(version, "Open Firmware, 1.0.5") != 0)
+       if (strcmp(version, "Open Firmware, 1.0.5") == 0)
+               of_workarounds = OF_WA_CLAIM;
+       else if (strncmp(version, "FirmWorks,3.", 12) == 0) {
+               of_workarounds = OF_WA_CLAIM | OF_WA_LONGTRAIL;
+               call_prom("interpret", 1, 1, "dev /memory 0 to allow-reclaim");
+       } else
                return;
+       _prom->memory = call_prom("open", 1, 1, ADDR("/memory"));
        prom_getprop(_prom->chosen, "mmu", &_prom->mmumap,
                     sizeof(_prom->mmumap));
+       if (!IHANDLE_VALID(_prom->memory) || !IHANDLE_VALID(_prom->mmumap))
+               of_workarounds &= ~OF_WA_CLAIM;         /* hmmm */
 }
 #else
 #define prom_find_mmu()
@@ -1382,16 +1496,17 @@ static void __init prom_init_stdout(void)
        memset(path, 0, 256);
        call_prom("instance-to-path", 3, 1, _prom->stdout, path, 255);
        val = call_prom("instance-to-package", 1, 1, _prom->stdout);
-       prom_setprop(_prom->chosen, "linux,stdout-package", &val, sizeof(val));
+       prom_setprop(_prom->chosen, "/chosen", "linux,stdout-package",
+                    &val, sizeof(val));
        prom_printf("OF stdout device is: %s\n", RELOC(of_stdout_device));
-       prom_setprop(_prom->chosen, "linux,stdout-path",
-                    RELOC(of_stdout_device), strlen(RELOC(of_stdout_device))+1);
+       prom_setprop(_prom->chosen, "/chosen", "linux,stdout-path",
+                    path, strlen(path) + 1);
 
        /* If it's a display, note it */
        memset(type, 0, sizeof(type));
        prom_getprop(val, "device_type", type, sizeof(type));
        if (strcmp(type, RELOC("display")) == 0)
-               prom_setprop(val, "linux,boot-display", NULL, 0);
+               prom_setprop(val, path, "linux,boot-display", NULL, 0);
 }
 
 static void __init prom_close_stdin(void)
@@ -1426,6 +1541,8 @@ static int __init prom_find_machine_type(void)
 #ifdef CONFIG_PPC64
                        if (strstr(p, RELOC("Momentum,Maple")))
                                return PLATFORM_MAPLE;
+                       if (strstr(p, RELOC("IBM,CPB")))
+                               return PLATFORM_CELL;
 #endif
                        i += sl + 1;
                }
@@ -1514,7 +1631,7 @@ static void __init prom_check_displays(void)
 
                /* Success */
                prom_printf("done\n");
-               prom_setprop(node, "linux,opened", NULL, 0);
+               prom_setprop(node, path, "linux,opened", NULL, 0);
 
                /* Setup a usable color table when the appropriate
                 * method is available. Should update this to set-colors */
@@ -1884,9 +2001,11 @@ static void __init fixup_device_tree(void)
        /* interrupt on this revision of u3 is number 0 and level */
        interrupts[0] = 0;
        interrupts[1] = 1;
-       prom_setprop(i2c, "interrupts", &interrupts, sizeof(interrupts));
+       prom_setprop(i2c, "/u3@0,f8000000/i2c@f8001000", "interrupts",
+                    &interrupts, sizeof(interrupts));
        parent = (u32)mpic;
-       prom_setprop(i2c, "interrupt-parent", &parent, sizeof(parent));
+       prom_setprop(i2c, "/u3@0,f8000000/i2c@f8001000", "interrupt-parent",
+                    &parent, sizeof(parent));
 #endif
 }
 
@@ -1918,15 +2037,15 @@ static void __init prom_check_initrd(unsigned long r3, unsigned long r4)
        if (r3 && r4 && r4 != 0xdeadbeef) {
                unsigned long val;
 
-               RELOC(prom_initrd_start) = (r3 >= KERNELBASE) ? __pa(r3) : r3;
+               RELOC(prom_initrd_start) = is_kernel_addr(r3) ? __pa(r3) : r3;
                RELOC(prom_initrd_end) = RELOC(prom_initrd_start) + r4;
 
                val = RELOC(prom_initrd_start);
-               prom_setprop(_prom->chosen, "linux,initrd-start", &val,
-                            sizeof(val));
+               prom_setprop(_prom->chosen, "/chosen", "linux,initrd-start",
+                            &val, sizeof(val));
                val = RELOC(prom_initrd_end);
-               prom_setprop(_prom->chosen, "linux,initrd-end", &val,
-                            sizeof(val));
+               prom_setprop(_prom->chosen, "/chosen", "linux,initrd-end",
+                            &val, sizeof(val));
 
                reserve_mem(RELOC(prom_initrd_start),
                            RELOC(prom_initrd_end) - RELOC(prom_initrd_start));
@@ -1969,14 +2088,15 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4,
        prom_init_client_services(pp);
 
        /*
-        * Init prom stdout device
+        * See if this OF is old enough that we need to do explicit maps
+        * and other workarounds
         */
-       prom_init_stdout();
+       prom_find_mmu();
 
        /*
-        * See if this OF is old enough that we need to do explicit maps
+        * Init prom stdout device
         */
-       prom_find_mmu();
+       prom_init_stdout();
 
        /*
         * Check for an initrd
@@ -1989,14 +2109,15 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4,
         */
        RELOC(of_platform) = prom_find_machine_type();
        getprop_rval = RELOC(of_platform);
-       prom_setprop(_prom->chosen, "linux,platform",
+       prom_setprop(_prom->chosen, "/chosen", "linux,platform",
                     &getprop_rval, sizeof(getprop_rval));
 
 #ifdef CONFIG_PPC_PSERIES
        /*
         * On pSeries, inform the firmware about our capabilities
         */
-       if (RELOC(of_platform) & PLATFORM_PSERIES)
+       if (RELOC(of_platform) == PLATFORM_PSERIES ||
+           RELOC(of_platform) == PLATFORM_PSERIES_LPAR)
                prom_send_capabilities();
 #endif
 
@@ -2016,6 +2137,10 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4,
         */
        prom_init_mem();
 
+#ifdef CONFIG_KEXEC
+       if (RELOC(prom_crashk_base))
+               reserve_mem(RELOC(prom_crashk_base), RELOC(prom_crashk_size));
+#endif
        /*
         * Determine which cpu is actually running right _now_
         */
@@ -2050,26 +2175,38 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4,
         * Fill in some infos for use by the kernel later on
         */
        if (RELOC(prom_memory_limit))
-               prom_setprop(_prom->chosen, "linux,memory-limit",
+               prom_setprop(_prom->chosen, "/chosen", "linux,memory-limit",
                             &RELOC(prom_memory_limit),
                             sizeof(prom_memory_limit));
 #ifdef CONFIG_PPC64
        if (RELOC(ppc64_iommu_off))
-               prom_setprop(_prom->chosen, "linux,iommu-off", NULL, 0);
+               prom_setprop(_prom->chosen, "/chosen", "linux,iommu-off",
+                            NULL, 0);
 
        if (RELOC(iommu_force_on))
-               prom_setprop(_prom->chosen, "linux,iommu-force-on", NULL, 0);
+               prom_setprop(_prom->chosen, "/chosen", "linux,iommu-force-on",
+                            NULL, 0);
 
        if (RELOC(prom_tce_alloc_start)) {
-               prom_setprop(_prom->chosen, "linux,tce-alloc-start",
+               prom_setprop(_prom->chosen, "/chosen", "linux,tce-alloc-start",
                             &RELOC(prom_tce_alloc_start),
                             sizeof(prom_tce_alloc_start));
-               prom_setprop(_prom->chosen, "linux,tce-alloc-end",
+               prom_setprop(_prom->chosen, "/chosen", "linux,tce-alloc-end",
                             &RELOC(prom_tce_alloc_end),
                             sizeof(prom_tce_alloc_end));
        }
 #endif
 
+#ifdef CONFIG_KEXEC
+       if (RELOC(prom_crashk_base)) {
+               prom_setprop(_prom->chosen, "/chosen", "linux,crashkernel-base",
+                       PTRRELOC(&prom_crashk_base),
+                       sizeof(RELOC(prom_crashk_base)));
+               prom_setprop(_prom->chosen, "/chosen", "linux,crashkernel-size",
+                       PTRRELOC(&prom_crashk_size),
+                       sizeof(RELOC(prom_crashk_size)));
+       }
+#endif
        /*
         * Fixup any known bugs in the device-tree
         */
@@ -2081,8 +2218,13 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4,
        prom_printf("copying OF device tree ...\n");
        flatten_device_tree();
 
-       /* in case stdin is USB and still active on IBM machines... */
-       prom_close_stdin();
+       /*
+        * in case stdin is USB and still active on IBM machines...
+        * Unfortunately quiesce crashes on some powermacs if we have
+        * closed stdin already (in particular the powerbook 101).
+        */
+       if (RELOC(of_platform) != PLATFORM_POWERMAC)
+               prom_close_stdin();
 
        /*
         * Call OF "quiesce" method to shut down pending DMA's from