summaryrefslogtreecommitdiffstats
path: root/arch/sparc/kernel/prom_32.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sparc/kernel/prom_32.c')
-rw-r--r--arch/sparc/kernel/prom_32.c323
1 files changed, 323 insertions, 0 deletions
diff --git a/arch/sparc/kernel/prom_32.c b/arch/sparc/kernel/prom_32.c
new file mode 100644
index 000000000..3df960c13
--- /dev/null
+++ b/arch/sparc/kernel/prom_32.c
@@ -0,0 +1,323 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Procedures for creating, accessing and interpreting the device tree.
+ *
+ * Paul Mackerras August 1996.
+ * Copyright (C) 1996-2005 Paul Mackerras.
+ *
+ * Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner.
+ * {engebret|bergner}@us.ibm.com
+ *
+ * Adapted for sparc32 by David S. Miller davem@davemloft.net
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/memblock.h>
+
+#include <asm/prom.h>
+#include <asm/oplib.h>
+#include <asm/leon.h>
+#include <asm/leon_amba.h>
+
+#include "prom.h"
+
+void * __init prom_early_alloc(unsigned long size)
+{
+ void *ret;
+
+ ret = memblock_alloc(size, SMP_CACHE_BYTES);
+ if (!ret)
+ panic("%s: Failed to allocate %lu bytes\n", __func__, size);
+
+ prom_early_allocated += size;
+
+ return ret;
+}
+
+/* The following routines deal with the black magic of fully naming a
+ * node.
+ *
+ * Certain well known named nodes are just the simple name string.
+ *
+ * Actual devices have an address specifier appended to the base name
+ * string, like this "foo@addr". The "addr" can be in any number of
+ * formats, and the platform plus the type of the node determine the
+ * format and how it is constructed.
+ *
+ * For children of the ROOT node, the naming convention is fixed and
+ * determined by whether this is a sun4u or sun4v system.
+ *
+ * For children of other nodes, it is bus type specific. So
+ * we walk up the tree until we discover a "device_type" property
+ * we recognize and we go from there.
+ */
+static void __init sparc32_path_component(struct device_node *dp, char *tmp_buf)
+{
+ const char *name = of_get_property(dp, "name", NULL);
+ struct linux_prom_registers *regs;
+ struct property *rprop;
+
+ rprop = of_find_property(dp, "reg", NULL);
+ if (!rprop)
+ return;
+
+ regs = rprop->value;
+ sprintf(tmp_buf, "%s@%x,%x",
+ name,
+ regs->which_io, regs->phys_addr);
+}
+
+/* "name@slot,offset" */
+static void __init sbus_path_component(struct device_node *dp, char *tmp_buf)
+{
+ const char *name = of_get_property(dp, "name", NULL);
+ struct linux_prom_registers *regs;
+ struct property *prop;
+
+ prop = of_find_property(dp, "reg", NULL);
+ if (!prop)
+ return;
+
+ regs = prop->value;
+ sprintf(tmp_buf, "%s@%x,%x",
+ name,
+ regs->which_io,
+ regs->phys_addr);
+}
+
+/* "name@devnum[,func]" */
+static void __init pci_path_component(struct device_node *dp, char *tmp_buf)
+{
+ const char *name = of_get_property(dp, "name", NULL);
+ struct linux_prom_pci_registers *regs;
+ struct property *prop;
+ unsigned int devfn;
+
+ prop = of_find_property(dp, "reg", NULL);
+ if (!prop)
+ return;
+
+ regs = prop->value;
+ devfn = (regs->phys_hi >> 8) & 0xff;
+ if (devfn & 0x07) {
+ sprintf(tmp_buf, "%s@%x,%x",
+ name,
+ devfn >> 3,
+ devfn & 0x07);
+ } else {
+ sprintf(tmp_buf, "%s@%x",
+ name,
+ devfn >> 3);
+ }
+}
+
+/* "name@addrhi,addrlo" */
+static void __init ebus_path_component(struct device_node *dp, char *tmp_buf)
+{
+ const char *name = of_get_property(dp, "name", NULL);
+ struct linux_prom_registers *regs;
+ struct property *prop;
+
+ prop = of_find_property(dp, "reg", NULL);
+ if (!prop)
+ return;
+
+ regs = prop->value;
+
+ sprintf(tmp_buf, "%s@%x,%x",
+ name,
+ regs->which_io, regs->phys_addr);
+}
+
+/* "name@irq,addrlo" */
+static void __init ambapp_path_component(struct device_node *dp, char *tmp_buf)
+{
+ const char *name = of_get_property(dp, "name", NULL);
+ struct amba_prom_registers *regs;
+ unsigned int *intr;
+ unsigned int reg0;
+ struct property *prop;
+ int interrupt = 0;
+
+ /* In order to get a unique ID in the device tree (multiple AMBA devices
+ * may have the same name) the node number is printed
+ */
+ prop = of_find_property(dp, "reg", NULL);
+ if (!prop) {
+ reg0 = (unsigned int)dp->phandle;
+ } else {
+ regs = prop->value;
+ reg0 = regs->phys_addr;
+ }
+
+ /* Not all cores have Interrupt */
+ prop = of_find_property(dp, "interrupts", NULL);
+ if (!prop)
+ intr = &interrupt; /* IRQ0 does not exist */
+ else
+ intr = prop->value;
+
+ sprintf(tmp_buf, "%s@%x,%x", name, *intr, reg0);
+}
+
+static void __init __build_path_component(struct device_node *dp, char *tmp_buf)
+{
+ struct device_node *parent = dp->parent;
+
+ if (parent != NULL) {
+ if (of_node_is_type(parent, "pci") ||
+ of_node_is_type(parent, "pciex"))
+ return pci_path_component(dp, tmp_buf);
+ if (of_node_is_type(parent, "sbus"))
+ return sbus_path_component(dp, tmp_buf);
+ if (of_node_is_type(parent, "ebus"))
+ return ebus_path_component(dp, tmp_buf);
+ if (of_node_is_type(parent, "ambapp"))
+ return ambapp_path_component(dp, tmp_buf);
+
+ /* "isa" is handled with platform naming */
+ }
+
+ /* Use platform naming convention. */
+ return sparc32_path_component(dp, tmp_buf);
+}
+
+char * __init build_path_component(struct device_node *dp)
+{
+ const char *name = of_get_property(dp, "name", NULL);
+ char tmp_buf[64], *n;
+
+ tmp_buf[0] = '\0';
+ __build_path_component(dp, tmp_buf);
+ if (tmp_buf[0] == '\0')
+ strcpy(tmp_buf, name);
+
+ n = prom_early_alloc(strlen(tmp_buf) + 1);
+ strcpy(n, tmp_buf);
+
+ return n;
+}
+
+extern void restore_current(void);
+
+void __init of_console_init(void)
+{
+ char *msg = "OF stdout device is: %s\n";
+ struct device_node *dp;
+ unsigned long flags;
+ const char *type;
+ phandle node;
+ int skip, tmp, fd;
+
+ of_console_path = prom_early_alloc(256);
+
+ switch (prom_vers) {
+ case PROM_V0:
+ skip = 0;
+ switch (*romvec->pv_stdout) {
+ case PROMDEV_SCREEN:
+ type = "display";
+ break;
+
+ case PROMDEV_TTYB:
+ skip = 1;
+ fallthrough;
+
+ case PROMDEV_TTYA:
+ type = "serial";
+ break;
+
+ default:
+ prom_printf("Invalid PROM_V0 stdout value %u\n",
+ *romvec->pv_stdout);
+ prom_halt();
+ }
+
+ tmp = skip;
+ for_each_node_by_type(dp, type) {
+ if (!tmp--)
+ break;
+ }
+ if (!dp) {
+ prom_printf("Cannot find PROM_V0 console node.\n");
+ prom_halt();
+ }
+ of_console_device = dp;
+
+ sprintf(of_console_path, "%pOF", dp);
+ if (!strcmp(type, "serial")) {
+ strcat(of_console_path,
+ (skip ? ":b" : ":a"));
+ }
+ break;
+
+ default:
+ case PROM_V2:
+ case PROM_V3:
+ fd = *romvec->pv_v2bootargs.fd_stdout;
+
+ spin_lock_irqsave(&prom_lock, flags);
+ node = (*romvec->pv_v2devops.v2_inst2pkg)(fd);
+ restore_current();
+ spin_unlock_irqrestore(&prom_lock, flags);
+
+ if (!node) {
+ prom_printf("Cannot resolve stdout node from "
+ "instance %08x.\n", fd);
+ prom_halt();
+ }
+ dp = of_find_node_by_phandle(node);
+
+ if (!of_node_is_type(dp, "display") &&
+ !of_node_is_type(dp, "serial")) {
+ prom_printf("Console device_type is neither display "
+ "nor serial.\n");
+ prom_halt();
+ }
+
+ of_console_device = dp;
+
+ if (prom_vers == PROM_V2) {
+ sprintf(of_console_path, "%pOF", dp);
+ switch (*romvec->pv_stdout) {
+ case PROMDEV_TTYA:
+ strcat(of_console_path, ":a");
+ break;
+ case PROMDEV_TTYB:
+ strcat(of_console_path, ":b");
+ break;
+ }
+ } else {
+ const char *path;
+
+ dp = of_find_node_by_path("/");
+ path = of_get_property(dp, "stdout-path", NULL);
+ if (!path) {
+ prom_printf("No stdout-path in root node.\n");
+ prom_halt();
+ }
+ strcpy(of_console_path, path);
+ }
+ break;
+ }
+
+ of_console_options = strrchr(of_console_path, ':');
+ if (of_console_options) {
+ of_console_options++;
+ if (*of_console_options == '\0')
+ of_console_options = NULL;
+ }
+
+ printk(msg, of_console_path);
+}
+
+void __init of_fill_in_cpu_data(void)
+{
+}
+
+void __init irq_trans_init(struct device_node *dp)
+{
+}