Merge branch 'for-2.6.27' of git://linux-nfs.org/~bfields/linux
[pandora-kernel.git] / arch / powerpc / platforms / pseries / hotplug-memory.c
index 3c5727d..a1a368d 100644 (file)
 #include <asm/machdep.h>
 #include <asm/pSeries_reconfig.h>
 
-static int pseries_remove_memory(struct device_node *np)
+static int pseries_remove_lmb(unsigned long base, unsigned int lmb_size)
 {
-       const char *type;
-       const unsigned int *my_index;
-       const unsigned int *regs;
-       u64 start_pfn, start;
+       unsigned long start, start_pfn;
        struct zone *zone;
-       int ret = -EINVAL;
-
-       /*
-        * Check to see if we are actually removing memory
-        */
-       type = of_get_property(np, "device_type", NULL);
-       if (type == NULL || strcmp(type, "memory") != 0)
-               return 0;
+       int ret;
 
-       /*
-        * Find the memory index and size of the removing section
-        */
-       my_index = of_get_property(np, "ibm,my-drc-index", NULL);
-       if (!my_index)
-               return ret;
-
-       regs = of_get_property(np, "reg", NULL);
-       if (!regs)
-               return ret;
-
-       start_pfn = section_nr_to_pfn(*my_index & 0xffff);
+       start_pfn = base >> PFN_SECTION_SHIFT;
        zone = page_zone(pfn_to_page(start_pfn));
 
        /*
@@ -54,56 +33,111 @@ static int pseries_remove_memory(struct device_node *np)
         * to sysfs "state" file and we can't remove sysfs entries
         * while writing to it. So we have to defer it to here.
         */
-       ret = __remove_pages(zone, start_pfn, regs[3] >> PAGE_SHIFT);
+       ret = __remove_pages(zone, start_pfn, lmb_size >> PAGE_SHIFT);
        if (ret)
                return ret;
 
        /*
         * Update memory regions for memory remove
         */
-       lmb_remove(start_pfn << PAGE_SHIFT, regs[3]);
+       lmb_remove(base, lmb_size);
 
        /*
         * Remove htab bolted mappings for this section of memory
         */
-       start = (unsigned long)__va(start_pfn << PAGE_SHIFT);
-       ret = remove_section_mapping(start, start + regs[3]);
+       start = (unsigned long)__va(base);
+       ret = remove_section_mapping(start, start + lmb_size);
        return ret;
 }
 
-static int pseries_add_memory(struct device_node *np)
+static int pseries_remove_memory(struct device_node *np)
 {
        const char *type;
-       const unsigned int *my_index;
        const unsigned int *regs;
-       u64 start_pfn;
+       unsigned long base;
+       unsigned int lmb_size;
        int ret = -EINVAL;
 
        /*
-        * Check to see if we are actually adding memory
+        * Check to see if we are actually removing memory
         */
        type = of_get_property(np, "device_type", NULL);
        if (type == NULL || strcmp(type, "memory") != 0)
                return 0;
 
        /*
-        * Find the memory index and size of the added section
+        * Find the bae address and size of the lmb
         */
-       my_index = of_get_property(np, "ibm,my-drc-index", NULL);
-       if (!my_index)
+       regs = of_get_property(np, "reg", NULL);
+       if (!regs)
                return ret;
 
+       base = *(unsigned long *)regs;
+       lmb_size = regs[3];
+
+       ret = pseries_remove_lmb(base, lmb_size);
+       return ret;
+}
+
+static int pseries_add_memory(struct device_node *np)
+{
+       const char *type;
+       const unsigned int *regs;
+       unsigned long base;
+       unsigned int lmb_size;
+       int ret = -EINVAL;
+
+       /*
+        * Check to see if we are actually adding memory
+        */
+       type = of_get_property(np, "device_type", NULL);
+       if (type == NULL || strcmp(type, "memory") != 0)
+               return 0;
+
+       /*
+        * Find the base and size of the lmb
+        */
        regs = of_get_property(np, "reg", NULL);
        if (!regs)
                return ret;
 
-       start_pfn = section_nr_to_pfn(*my_index & 0xffff);
+       base = *(unsigned long *)regs;
+       lmb_size = regs[3];
 
        /*
         * Update memory region to represent the memory add
         */
-       lmb_add(start_pfn << PAGE_SHIFT, regs[3]);
-       return 0;
+       ret = lmb_add(base, lmb_size);
+       return (ret < 0) ? -EINVAL : 0;
+}
+
+static int pseries_drconf_memory(unsigned long *base, unsigned int action)
+{
+       struct device_node *np;
+       const unsigned long *lmb_size;
+       int rc;
+
+       np = of_find_node_by_path("/ibm,dynamic-reconfiguration-memory");
+       if (!np)
+               return -EINVAL;
+
+       lmb_size = of_get_property(np, "ibm,lmb-size", NULL);
+       if (!lmb_size) {
+               of_node_put(np);
+               return -EINVAL;
+       }
+
+       if (action == PSERIES_DRCONF_MEM_ADD) {
+               rc = lmb_add(*base, *lmb_size);
+               rc = (rc < 0) ? -EINVAL : 0;
+       } else if (action == PSERIES_DRCONF_MEM_REMOVE) {
+               rc = pseries_remove_lmb(*base, *lmb_size);
+       } else {
+               rc = -EINVAL;
+       }
+
+       of_node_put(np);
+       return rc;
 }
 
 static int pseries_memory_notifier(struct notifier_block *nb,
@@ -120,6 +154,11 @@ static int pseries_memory_notifier(struct notifier_block *nb,
                if (pseries_remove_memory(node))
                        err = NOTIFY_BAD;
                break;
+       case PSERIES_DRCONF_MEM_ADD:
+       case PSERIES_DRCONF_MEM_REMOVE:
+               if (pseries_drconf_memory(node, action))
+                       err = NOTIFY_BAD;
+               break;
        default:
                err = NOTIFY_DONE;
                break;