summaryrefslogtreecommitdiffstats
path: root/arch/ppc/mm/mem_pieces.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/ppc/mm/mem_pieces.c')
-rw-r--r--arch/ppc/mm/mem_pieces.c163
1 files changed, 163 insertions, 0 deletions
diff --git a/arch/ppc/mm/mem_pieces.c b/arch/ppc/mm/mem_pieces.c
new file mode 100644
index 000000000000..3d639052017e
--- /dev/null
+++ b/arch/ppc/mm/mem_pieces.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 1996 Paul Mackerras <paulus@cs.anu.edu.au>
+ * Changes to accommodate Power Macintoshes.
+ * Cort Dougan <cort@cs.nmt.edu>
+ * Rewrites.
+ * Grant Erickson <grant@lcse.umn.edu>
+ * General rework and split from mm/init.c.
+ *
+ * Module name: mem_pieces.c
+ *
+ * Description:
+ * Routines and data structures for manipulating and representing
+ * phyiscal memory extents (i.e. address/length pairs).
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/init.h>
+#include <asm/page.h>
+
+#include "mem_pieces.h"
+
+extern struct mem_pieces phys_avail;
+
+static void mem_pieces_print(struct mem_pieces *);
+
+/*
+ * Scan a region for a piece of a given size with the required alignment.
+ */
+void __init *
+mem_pieces_find(unsigned int size, unsigned int align)
+{
+ int i;
+ unsigned a, e;
+ struct mem_pieces *mp = &phys_avail;
+
+ for (i = 0; i < mp->n_regions; ++i) {
+ a = mp->regions[i].address;
+ e = a + mp->regions[i].size;
+ a = (a + align - 1) & -align;
+ if (a + size <= e) {
+ mem_pieces_remove(mp, a, size, 1);
+ return (void *) __va(a);
+ }
+ }
+ panic("Couldn't find %u bytes at %u alignment\n", size, align);
+
+ return NULL;
+}
+
+/*
+ * Remove some memory from an array of pieces
+ */
+void __init
+mem_pieces_remove(struct mem_pieces *mp, unsigned int start, unsigned int size,
+ int must_exist)
+{
+ int i, j;
+ unsigned int end, rs, re;
+ struct reg_property *rp;
+
+ end = start + size;
+ for (i = 0, rp = mp->regions; i < mp->n_regions; ++i, ++rp) {
+ if (end > rp->address && start < rp->address + rp->size)
+ break;
+ }
+ if (i >= mp->n_regions) {
+ if (must_exist)
+ printk("mem_pieces_remove: [%x,%x) not in any region\n",
+ start, end);
+ return;
+ }
+ for (; i < mp->n_regions && end > rp->address; ++i, ++rp) {
+ rs = rp->address;
+ re = rs + rp->size;
+ if (must_exist && (start < rs || end > re)) {
+ printk("mem_pieces_remove: bad overlap [%x,%x) with",
+ start, end);
+ mem_pieces_print(mp);
+ must_exist = 0;
+ }
+ if (start > rs) {
+ rp->size = start - rs;
+ if (end < re) {
+ /* need to split this entry */
+ if (mp->n_regions >= MEM_PIECES_MAX)
+ panic("eek... mem_pieces overflow");
+ for (j = mp->n_regions; j > i + 1; --j)
+ mp->regions[j] = mp->regions[j-1];
+ ++mp->n_regions;
+ rp[1].address = end;
+ rp[1].size = re - end;
+ }
+ } else {
+ if (end < re) {
+ rp->address = end;
+ rp->size = re - end;
+ } else {
+ /* need to delete this entry */
+ for (j = i; j < mp->n_regions - 1; ++j)
+ mp->regions[j] = mp->regions[j+1];
+ --mp->n_regions;
+ --i;
+ --rp;
+ }
+ }
+ }
+}
+
+static void __init
+mem_pieces_print(struct mem_pieces *mp)
+{
+ int i;
+
+ for (i = 0; i < mp->n_regions; ++i)
+ printk(" [%x, %x)", mp->regions[i].address,
+ mp->regions[i].address + mp->regions[i].size);
+ printk("\n");
+}
+
+void __init
+mem_pieces_sort(struct mem_pieces *mp)
+{
+ unsigned long a, s;
+ int i, j;
+
+ for (i = 1; i < mp->n_regions; ++i) {
+ a = mp->regions[i].address;
+ s = mp->regions[i].size;
+ for (j = i - 1; j >= 0; --j) {
+ if (a >= mp->regions[j].address)
+ break;
+ mp->regions[j+1] = mp->regions[j];
+ }
+ mp->regions[j+1].address = a;
+ mp->regions[j+1].size = s;
+ }
+}
+
+void __init
+mem_pieces_coalesce(struct mem_pieces *mp)
+{
+ unsigned long a, s, ns;
+ int i, j, d;
+
+ d = 0;
+ for (i = 0; i < mp->n_regions; i = j) {
+ a = mp->regions[i].address;
+ s = mp->regions[i].size;
+ for (j = i + 1; j < mp->n_regions
+ && mp->regions[j].address - a <= s; ++j) {
+ ns = mp->regions[j].address + mp->regions[j].size - a;
+ if (ns > s)
+ s = ns;
+ }
+ mp->regions[d].address = a;
+ mp->regions[d].size = s;
+ ++d;
+ }
+ mp->n_regions = d;
+}