vserver 1.9.5.x5
[linux-2.6.git] / arch / ppc64 / kernel / prom.c
index f121ffb..eed1a70 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/stringify.h>
 #include <linux/delay.h>
 #include <linux/initrd.h>
+#include <linux/bitops.h>
 #include <asm/prom.h>
 #include <asm/rtas.h>
 #include <asm/lmb.h>
@@ -43,8 +44,6 @@
 #include <asm/system.h>
 #include <asm/mmu.h>
 #include <asm/pgtable.h>
-#include <asm/bitops.h>
-#include <asm/naca.h>
 #include <asm/pci.h>
 #include <asm/iommu.h>
 #include <asm/bootinfo.h>
@@ -52,7 +51,6 @@
 #include <asm/btext.h>
 #include <asm/sections.h>
 #include <asm/machdep.h>
-#include "open_pic.h"
 
 #ifdef DEBUG
 #define DBG(fmt...) udbg_printf(fmt)
@@ -97,7 +95,7 @@ static struct device_node *allnodes = NULL;
 /* use when traversing tree through the allnext, child, sibling,
  * or parent members of struct device_node.
  */
-static rwlock_t devtree_lock = RW_LOCK_UNLOCKED;
+static DEFINE_RWLOCK(devtree_lock);
 
 /* export that to outside world */
 struct device_node *of_chosen;
@@ -442,7 +440,7 @@ static unsigned long __init interpret_isa_props(struct device_node *np,
        if (rp != 0 && l >= sizeof(struct isa_reg_property)) {
                i = 0;
                adr = (struct address_range *) mem_start;
-               while ((l -= sizeof(struct reg_property)) >= 0) {
+               while ((l -= sizeof(struct isa_reg_property)) >= 0) {
                        if (!measure_only) {
                                adr[i].space = rp[i].space;
                                adr[i].address = rp[i].address;
@@ -558,7 +556,7 @@ void __init finish_device_tree(void)
 
        DBG(" -> finish_device_tree\n");
 
-       if (naca->interrupt_controller == IC_INVALID) {
+       if (ppc64_interrupt_controller == IC_INVALID) {
                DBG("failed to configure interrupt controller type\n");
                panic("failed to configure interrupt controller type\n");
        }
@@ -719,6 +717,7 @@ static unsigned long __init unflatten_dt_node(unsigned long mem,
                                dad->next->sibling = np;
                        dad->next = np;
                }
+               kref_init(&np->kref);
        }
        while(1) {
                u32 sz, noff;
@@ -824,7 +823,7 @@ void __init unflatten_device_tree(void)
                        strlcpy(cmd_line, p, min(l, COMMAND_LINE_SIZE));
        }
 #ifdef CONFIG_CMDLINE
-       if (l == 0) /* dbl check */
+       if (l == 0 || (l == 1 && (*p) == 0))
                strlcpy(cmd_line, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
 #endif /* CONFIG_CMDLINE */
 
@@ -845,19 +844,28 @@ static int __init early_init_dt_scan_cpus(unsigned long node,
 
        /* On LPAR, look for the first ibm,pft-size property for the  hash table size
         */
-       if (systemcfg->platform == PLATFORM_PSERIES_LPAR && naca->pftSize == 0) {
+       if (systemcfg->platform == PLATFORM_PSERIES_LPAR && ppc64_pft_size == 0) {
                u32 *pft_size;
                pft_size = (u32 *)get_flat_dt_prop(node, "ibm,pft-size", NULL);
                if (pft_size != NULL) {
                        /* pft_size[0] is the NUMA CEC cookie */
-                       naca->pftSize = pft_size[1];
+                       ppc64_pft_size = pft_size[1];
                }
        }
 
-       /* Check if it's the boot-cpu, set it's hw index in paca now */
-       if (get_flat_dt_prop(node, "linux,boot-cpu", NULL) != NULL) {
-               u32 *prop = get_flat_dt_prop(node, "reg", NULL);
-               paca[0].hw_cpu_id = prop == NULL ? 0 : *prop;
+       if (initial_boot_params && initial_boot_params->version >= 2) {
+               /* version 2 of the kexec param format adds the phys cpuid
+                * of booted proc.
+                */
+               boot_cpuid_phys = initial_boot_params->boot_cpuid_phys;
+               boot_cpuid = 0;
+       } else {
+               /* Check if it's the boot-cpu, set it's hw index in paca now */
+               if (get_flat_dt_prop(node, "linux,boot-cpu", NULL) != NULL) {
+                       u32 *prop = get_flat_dt_prop(node, "reg", NULL);
+                       set_hard_smp_processor_id(0, prop == NULL ? 0 : *prop);
+                       boot_cpuid_phys = get_hard_smp_processor_id(0);
+               }
        }
 
        return 0;
@@ -1010,7 +1018,7 @@ void __init early_init_devtree(void *params)
        initial_boot_params = params;
 
        /* By default, hash size is not set */
-       naca->pftSize = 0;
+       ppc64_pft_size = 0;
 
        /* Retreive various informations from the /chosen node of the
         * device-tree, including the platform type, initrd location and
@@ -1024,6 +1032,7 @@ void __init early_init_devtree(void *params)
        scan_flat_dt(early_init_dt_scan_memory, NULL);
        lmb_analyze();
        systemcfg->physicalMemorySize = lmb_phys_mem_size();
+       lmb_reserve(0, __pa(klimit));
 
        DBG("Phys. mem: %lx\n", systemcfg->physicalMemorySize);
 
@@ -1038,7 +1047,7 @@ void __init early_init_devtree(void *params)
        /* If hash size wasn't obtained above, we calculate it now based on
         * the total RAM size
         */
-       if (naca->pftSize == 0) {
+       if (ppc64_pft_size == 0) {
                unsigned long rnd_mem_size, pteg_count;
 
                /* round mem_size up to next power of 2 */
@@ -1047,12 +1056,12 @@ void __init early_init_devtree(void *params)
                        rnd_mem_size <<= 1;
 
                /* # pages / 2 */
-               pteg_count = (rnd_mem_size >> (12 + 1));
+               pteg_count = max(rnd_mem_size >> (12 + 1), 1UL << 11);
 
-               naca->pftSize = __ilog2(pteg_count << 7);
+               ppc64_pft_size = __ilog2(pteg_count << 7);
        }
 
-       DBG("Hash pftSize: %x\n", (int)naca->pftSize);
+       DBG("Hash pftSize: %x\n", (int)ppc64_pft_size);
        DBG(" <- early_init_devtree()\n");
 }
 
@@ -1092,8 +1101,7 @@ prom_n_size_cells(struct device_node* np)
  * Work out the sense (active-low level / active-high edge)
  * of each interrupt from the device tree.
  */
-void __init
-prom_get_irq_senses(unsigned char *senses, int off, int max)
+void __init prom_get_irq_senses(unsigned char *senses, int off, int max)
 {
        struct device_node *np;
        int i, j;
@@ -1105,7 +1113,9 @@ prom_get_irq_senses(unsigned char *senses, int off, int max)
                for (j = 0; j < np->n_intrs; j++) {
                        i = np->intrs[j].line;
                        if (i >= off && i < max)
-                               senses[i-off] = np->intrs[j].sense;
+                               senses[i-off] = np->intrs[j].sense ?
+                                       IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE :
+                                       IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE;
                }
        }
 }
@@ -1466,24 +1476,31 @@ EXPORT_SYMBOL(of_get_next_child);
  *     @node:  Node to inc refcount, NULL is supported to
  *             simplify writing of callers
  *
- *     Returns the node itself or NULL if gone.
+ *     Returns node.
  */
 struct device_node *of_node_get(struct device_node *node)
 {
-       if (node && !OF_IS_STALE(node)) {
-               atomic_inc(&node->_users);
-               return node;
-       }
-       return NULL;
+       if (node)
+               kref_get(&node->kref);
+       return node;
 }
 EXPORT_SYMBOL(of_node_get);
 
+static inline struct device_node * kref_to_device_node(struct kref *kref)
+{
+       return container_of(kref, struct device_node, kref);
+}
+
 /**
- *     of_node_cleanup - release a dynamically allocated node
- *     @arg:  Node to be released
+ *     of_node_release - release a dynamically allocated node
+ *     @kref:  kref element of the node to be released
+ *
+ *     In of_node_put() this function is passed to kref_put()
+ *     as the destructor.
  */
-static void of_node_cleanup(struct device_node *node)
+static void of_node_release(struct kref *kref)
 {
+       struct device_node *node = kref_to_device_node(kref);
        struct property *prop = node->properties;
 
        if (!OF_IS_DYNAMIC(node))
@@ -1509,19 +1526,8 @@ static void of_node_cleanup(struct device_node *node)
  */
 void of_node_put(struct device_node *node)
 {
-       if (!node)
-               return;
-
-       WARN_ON(0 == atomic_read(&node->_users));
-
-       if (OF_IS_STALE(node)) {
-               if (atomic_dec_and_test(&node->_users)) {
-                       of_node_cleanup(node);
-                       return;
-               }
-       }
-       else
-               atomic_dec(&node->_users);
+       if (node)
+               kref_put(&node->kref, of_node_release);
 }
 EXPORT_SYMBOL(of_node_put);
 
@@ -1734,17 +1740,6 @@ static int of_finish_dynamic_node(struct device_node *node)
                node->devfn = (regs[0] >> 8) & 0xff;
        }
 
-       /* fixing up iommu_table */
-
-#ifdef CONFIG_PPC_PSERIES
-       if (strcmp(node->name, "pci") == 0 &&
-           get_property(node, "ibm,dma-window", NULL)) {
-               node->bussubno = node->busno;
-               iommu_devnode_init(node);
-       } else
-               node->iommu_table = parent->iommu_table;
-#endif /* CONFIG_PPC_PSERIES */
-
 out:
        of_node_put(parent);
        return err;
@@ -1775,6 +1770,7 @@ int of_add_node(const char *path, struct property *proplist)
 
        np->properties = proplist;
        OF_MARK_DYNAMIC(np);
+       kref_init(&np->kref);
        of_node_get(np);
        np->parent = derive_parent(path);
        if (!np->parent) {
@@ -1802,8 +1798,18 @@ int of_add_node(const char *path, struct property *proplist)
 }
 
 /*
- * Remove an OF device node from the system.
- * Caller should have already "gotten" np.
+ * Prepare an OF node for removal from system
+ */
+static void of_cleanup_node(struct device_node *np)
+{
+       if (np->iommu_table && get_property(np, "ibm,dma-window", NULL))
+               iommu_free_table(np);
+}
+
+/*
+ * "Unplug" a node from the device tree.  The caller must hold
+ * a reference to the node.  The memory associated with the node
+ * is not freed until its refcount goes to zero.
  */
 int of_remove_node(struct device_node *np)
 {
@@ -1818,8 +1824,9 @@ int of_remove_node(struct device_node *np)
                return -EBUSY;
        }
 
+       of_cleanup_node(np);
+
        write_lock(&devtree_lock);
-       OF_MARK_STALE(np);
        remove_node_proc_entries(np);
        if (allnodes == np)
                allnodes = np->allnext;
@@ -1844,6 +1851,7 @@ int of_remove_node(struct device_node *np)
        }
        write_unlock(&devtree_lock);
        of_node_put(parent);
+       of_node_put(np); /* Must decrement the refcount */
        return 0;
 }