msm: ocmem: Add support for tail growth.
Add support for an OCMEM Zone to grow from the tail to
allow maximum sharing among subsystems.
A zone growing from tail has subsequent allocations that
start at lower addresses compared to an initial allocation.
Change-Id: Ibdd65c49bc7e3ff3a5398b6851828587de257e0a
Signed-off-by: Naveen Ramaraj <nramaraj@codeaurora.org>
diff --git a/arch/arm/mach-msm/include/mach/ocmem_priv.h b/arch/arm/mach-msm/include/mach/ocmem_priv.h
index 4b46c01..daf32a5 100644
--- a/arch/arm/mach-msm/include/mach/ocmem_priv.h
+++ b/arch/arm/mach-msm/include/mach/ocmem_priv.h
@@ -76,4 +76,6 @@
struct ocmem_zone *get_zone(unsigned);
unsigned long allocate_head(struct ocmem_zone *, unsigned long);
int free_head(struct ocmem_zone *, unsigned long, unsigned long);
+unsigned long allocate_tail(struct ocmem_zone *, unsigned long);
+int free_tail(struct ocmem_zone *, unsigned long, unsigned long);
#endif
diff --git a/arch/arm/mach-msm/ocmem.c b/arch/arm/mach-msm/ocmem.c
index 203b8d1..ed0b2f0 100644
--- a/arch/arm/mach-msm/ocmem.c
+++ b/arch/arm/mach-msm/ocmem.c
@@ -29,6 +29,7 @@
unsigned long p_start;
unsigned long p_size;
unsigned long p_min;
+ unsigned int p_tail;
};
struct ocmem_plat_data {
@@ -70,19 +71,20 @@
unsigned long start;
unsigned long size;
unsigned long min;
+ unsigned int tail;
};
/* This static table will go away with device tree support */
static struct ocmem_quota_table qt[OCMEM_CLIENT_MAX] = {
- /* name, id, start, size, min */
- { "graphics", OCMEM_GRAPHICS, 0x0, 0x100000, 0x80000},
- { "video", OCMEM_VIDEO, 0x100000, 0x80000, 0x55000},
- { "camera", OCMEM_CAMERA, 0x0, 0x0, 0x0},
- { "voice", OCMEM_VOICE, 0x0, 0x0, 0x0 },
- { "hp_audio", OCMEM_HP_AUDIO, 0x0, 0x0, 0x0},
- { "lp_audio", OCMEM_LP_AUDIO, 0x80000, 0xA0000, 0xA0000},
- { "blast", OCMEM_BLAST, 0x120000, 0x20000, 0x20000},
- { "sensors", OCMEM_SENSORS, 0x140000, 0x40000, 0x40000},
+ /* name, id, start, size, min, tail */
+ { "graphics", OCMEM_GRAPHICS, 0x0, 0x100000, 0x80000, 0},
+ { "video", OCMEM_VIDEO, 0x100000, 0x80000, 0x55000, 1},
+ { "camera", OCMEM_CAMERA, 0x0, 0x0, 0x0, 0},
+ { "voice", OCMEM_VOICE, 0x0, 0x0, 0x0, 0 },
+ { "hp_audio", OCMEM_HP_AUDIO, 0x0, 0x0, 0x0, 0},
+ { "lp_audio", OCMEM_LP_AUDIO, 0x80000, 0xA0000, 0xA0000, 0},
+ { "blast", OCMEM_BLAST, 0x120000, 0x20000, 0x20000, 0},
+ { "sensors", OCMEM_SENSORS, 0x140000, 0x40000, 0x40000, 0},
};
static inline int get_id(const char *name)
@@ -142,6 +144,7 @@
parts[j].p_size = qt[i].size;
parts[j].p_start = qt[i].start;
parts[j].p_min = qt[i].min;
+ parts[j].p_tail = qt[i].tail;
j++;
}
BUG_ON(j != nr_parts);
@@ -225,8 +228,13 @@
zone->max_regions = 0;
INIT_LIST_HEAD(&zone->region_list);
zone->z_ops = z_ops;
- z_ops->allocate = allocate_head;
- z_ops->free = free_head;
+ if (part->p_tail) {
+ z_ops->allocate = allocate_tail;
+ z_ops->free = free_tail;
+ } else {
+ z_ops->allocate = allocate_head;
+ z_ops->free = free_head;
+ }
active_zones++;
if (active_zones == 1)
diff --git a/arch/arm/mach-msm/ocmem_allocator.c b/arch/arm/mach-msm/ocmem_allocator.c
index 9f073fc..71cacda 100644
--- a/arch/arm/mach-msm/ocmem_allocator.c
+++ b/arch/arm/mach-msm/ocmem_allocator.c
@@ -15,6 +15,27 @@
#include <linux/genalloc.h>
/* All allocator operations are serialized by ocmem driver */
+
+/* The allocators work as follows:
+ Constraints:
+ 1) There is no IOMMU access to OCMEM hence successive allocations
+ in the zone must be physically contiguous
+ 2) Allocations must be freed in reverse order within a zone.
+
+ z->z_start: Fixed pointer to the start of a zone
+ z->z_end: Fixed pointer to the end of a zone
+
+ z->z_head: Movable pointer to the next free area when growing at head
+ Fixed on zones that grow from tail
+
+ z->z_tail: Movable pointer to the next free area when growing at tail
+ Fixed on zones that grow from head
+
+ z->z_free: Free space in a zone that is updated on an allocation/free
+
+ reserve: Enable libgenpool to simulate tail allocations
+*/
+
unsigned long allocate_head(struct ocmem_zone *z, unsigned long size)
{
@@ -30,6 +51,31 @@
return offset;
}
+unsigned long allocate_tail(struct ocmem_zone *z, unsigned long size)
+{
+ unsigned long offset;
+ unsigned long reserve;
+ unsigned long head;
+
+ if (z->z_tail < (z->z_head + size))
+ return -ENOMEM;
+
+ reserve = z->z_tail - z->z_head - size;
+ if (reserve) {
+ head = gen_pool_alloc(z->z_pool, reserve);
+ offset = gen_pool_alloc(z->z_pool, size);
+ gen_pool_free(z->z_pool, head, reserve);
+ } else
+ offset = gen_pool_alloc(z->z_pool, size);
+
+ if (!offset)
+ return -ENOMEM;
+
+ z->z_tail -= size;
+ z->z_free -= size;
+ return offset;
+}
+
int free_head(struct ocmem_zone *z, unsigned long offset,
unsigned long size)
{
@@ -43,3 +89,17 @@
z->z_free += size;
return 0;
}
+
+int free_tail(struct ocmem_zone *z, unsigned long offset,
+ unsigned long size)
+{
+ if (offset > z->z_tail) {
+ pr_err("ocmem: Detected out of order free "
+ "leading to fragmentation\n");
+ return -EINVAL;
+ }
+ gen_pool_free(z->z_pool, offset, size);
+ z->z_tail += size;
+ z->z_free += size;
+ return 0;
+}