msm: support FMEM on 8064
Add the required support for FMEM on 8064 (whether
DMM is on or off) and turn FMEM on.
Change-Id: I3c0fe2408442689c7614a2f4b21436363d569621
Signed-off-by: Larry Bassel <lbassel@codeaurora.org>
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 7c117e8..b216027 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -231,6 +231,17 @@
select MSM_PM8X60 if PM
select CPU_HAS_L2_PMU
select HOLES_IN_ZONE if SPARSEMEM
+ select ENABLE_DMM
+ select MEMORY_HOTPLUG if ENABLE_DMM
+ select MEMORY_HOTREMOVE if ENABLE_DMM
+ select ARCH_ENABLE_MEMORY_HOTPLUG if ENABLE_DMM
+ select ARCH_ENABLE_MEMORY_HOTREMOVE if ENABLE_DMM
+ select MIGRATION if ENABLE_DMM
+ select ARCH_MEMORY_PROBE if ENABLE_DMM
+ select ARCH_MEMORY_REMOVE if ENABLE_DMM
+ select FIX_MOVABLE_ZONE if ENABLE_DMM
+ select CLEANCACHE
+ select QCACHE
config ARCH_MSMCOPPER
bool "MSM Copper"
diff --git a/arch/arm/mach-msm/board-8064.c b/arch/arm/mach-msm/board-8064.c
index f9b1e2e..ceae29c 100644
--- a/arch/arm/mach-msm/board-8064.c
+++ b/arch/arm/mach-msm/board-8064.c
@@ -27,6 +27,7 @@
#include <linux/platform_data/qcom_crypto_device.h>
#include <linux/ion.h>
#include <linux/memory.h>
+#include <linux/memblock.h>
#include <linux/i2c/atmel_mxt_ts.h>
#include <linux/cyttsp.h>
#include <linux/i2c/isa1200.h>
@@ -67,6 +68,7 @@
#include <mach/msm_rtb.h>
#include <sound/cs8427.h>
#include <media/gpio-ir-recv.h>
+#include <linux/fmem.h>
#include "msm_watchdog.h"
#include "board-8064.h"
@@ -107,6 +109,11 @@
#define MSM_ION_HEAP_NUM 1
#endif
+#define APQ8064_FIXED_AREA_START 0xa0000000
+#define MAX_FIXED_AREA_SIZE 0x10000000
+#define MSM_MM_FW_SIZE 0x200000
+#define APQ8064_FW_START (APQ8064_FIXED_AREA_START - MSM_MM_FW_SIZE)
+
#ifdef CONFIG_KERNEL_PMEM_EBI_REGION
static unsigned pmem_kernel_ebi1_size = MSM_PMEM_KERNEL_EBI1_SIZE;
static int __init pmem_kernel_ebi1_size_setup(char *p)
@@ -187,6 +194,9 @@
#endif /* CONFIG_MSM_MULTIMEDIA_USE_ION */
#endif /* CONFIG_ANDROID_PMEM */
+struct fmem_platform_data apq8064_fmem_pdata = {
+};
+
static struct memtype_reserve apq8064_reserve_table[] __initdata = {
[MEMTYPE_SMI] = {
},
@@ -243,26 +253,37 @@
return MEMTYPE_EBI1;
}
+#define FMEM_ENABLED 1
+
#ifdef CONFIG_ION_MSM
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
static struct ion_cp_heap_pdata cp_mm_apq8064_ion_pdata = {
.permission_type = IPT_TYPE_MM_CARVEOUT,
.align = PAGE_SIZE,
+ .reusable = FMEM_ENABLED,
+ .mem_is_fmem = FMEM_ENABLED,
+ .fixed_position = FIXED_MIDDLE,
};
static struct ion_cp_heap_pdata cp_mfc_apq8064_ion_pdata = {
.permission_type = IPT_TYPE_MFC_SHAREDMEM,
.align = PAGE_SIZE,
+ .reusable = 0,
+ .mem_is_fmem = FMEM_ENABLED,
+ .fixed_position = FIXED_HIGH,
};
static struct ion_co_heap_pdata co_apq8064_ion_pdata = {
.adjacent_mem_id = INVALID_HEAP_ID,
.align = PAGE_SIZE,
+ .mem_is_fmem = 0,
};
static struct ion_co_heap_pdata fw_co_apq8064_ion_pdata = {
.adjacent_mem_id = ION_CP_MM_HEAP_ID,
.align = SZ_128K,
+ .mem_is_fmem = FMEM_ENABLED,
+ .fixed_position = FIXED_LOW,
};
#endif
@@ -352,15 +373,180 @@
};
#endif
+static struct platform_device apq8064_fmem_device = {
+ .name = "fmem",
+ .id = 1,
+ .dev = { .platform_data = &apq8064_fmem_pdata },
+};
+
+static void __init reserve_mem_for_ion(enum ion_memory_types mem_type,
+ unsigned long size)
+{
+ apq8064_reserve_table[mem_type].size += size;
+}
+
+static void __init apq8064_reserve_fixed_area(unsigned long fixed_area_size)
+{
+#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION)
+ int ret;
+
+ if (fixed_area_size > MAX_FIXED_AREA_SIZE)
+ panic("fixed area size is larger than %dM\n",
+ MAX_FIXED_AREA_SIZE >> 20);
+
+ reserve_info->fixed_area_size = fixed_area_size;
+ reserve_info->fixed_area_start = APQ8064_FW_START;
+
+ ret = memblock_remove(reserve_info->fixed_area_start,
+ reserve_info->fixed_area_size);
+ BUG_ON(ret);
+#endif
+}
+
+/**
+ * Reserve memory for ION and calculate amount of reusable memory for fmem.
+ * We only reserve memory for heaps that are not reusable. However, we only
+ * support one reusable heap at the moment so we ignore the reusable flag for
+ * other than the first heap with reusable flag set. Also handle special case
+ * for video heaps (MM,FW, and MFC). Video requires heaps MM and MFC to be
+ * at a higher address than FW in addition to not more than 256MB away from the
+ * base address of the firmware. This means that if MM is reusable the other
+ * two heaps must be allocated in the same region as FW. This is handled by the
+ * mem_is_fmem flag in the platform data. In addition the MM heap must be
+ * adjacent to the FW heap for content protection purposes.
+ */
static void __init reserve_ion_memory(void)
{
#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION)
- apq8064_reserve_table[MEMTYPE_EBI1].size += MSM_ION_MM_SIZE;
- apq8064_reserve_table[MEMTYPE_EBI1].size += MSM_ION_MM_FW_SIZE;
- apq8064_reserve_table[MEMTYPE_EBI1].size += MSM_ION_SF_SIZE;
- apq8064_reserve_table[MEMTYPE_EBI1].size += MSM_ION_MFC_SIZE;
- apq8064_reserve_table[MEMTYPE_EBI1].size += MSM_ION_QSECOM_SIZE;
- apq8064_reserve_table[MEMTYPE_EBI1].size += MSM_ION_AUDIO_SIZE;
+ unsigned int i;
+ unsigned int reusable_count = 0;
+ unsigned int fixed_size = 0;
+ unsigned int fixed_low_size, fixed_middle_size, fixed_high_size;
+ unsigned long fixed_low_start, fixed_middle_start, fixed_high_start;
+
+ apq8064_fmem_pdata.size = 0;
+ apq8064_fmem_pdata.reserved_size_low = 0;
+ apq8064_fmem_pdata.reserved_size_high = 0;
+ fixed_low_size = 0;
+ fixed_middle_size = 0;
+ fixed_high_size = 0;
+
+ /* We only support 1 reusable heap. Check if more than one heap
+ * is specified as reusable and set as non-reusable if found.
+ */
+ for (i = 0; i < apq8064_ion_pdata.nr; ++i) {
+ const struct ion_platform_heap *heap =
+ &(apq8064_ion_pdata.heaps[i]);
+
+ if (heap->type == ION_HEAP_TYPE_CP && heap->extra_data) {
+ struct ion_cp_heap_pdata *data = heap->extra_data;
+
+ reusable_count += (data->reusable) ? 1 : 0;
+
+ if (data->reusable && reusable_count > 1) {
+ pr_err("%s: Too many heaps specified as "
+ "reusable. Heap %s was not configured "
+ "as reusable.\n", __func__, heap->name);
+ data->reusable = 0;
+ }
+ }
+ }
+
+ for (i = 0; i < apq8064_ion_pdata.nr; ++i) {
+ const struct ion_platform_heap *heap =
+ &(apq8064_ion_pdata.heaps[i]);
+
+ if (heap->extra_data) {
+ int fixed_position = NOT_FIXED;
+ int mem_is_fmem = 0;
+
+ switch (heap->type) {
+ case ION_HEAP_TYPE_CP:
+ mem_is_fmem = ((struct ion_cp_heap_pdata *)
+ heap->extra_data)->mem_is_fmem;
+ fixed_position = ((struct ion_cp_heap_pdata *)
+ heap->extra_data)->fixed_position;
+ break;
+ case ION_HEAP_TYPE_CARVEOUT:
+ mem_is_fmem = ((struct ion_co_heap_pdata *)
+ heap->extra_data)->mem_is_fmem;
+ fixed_position = ((struct ion_co_heap_pdata *)
+ heap->extra_data)->fixed_position;
+ break;
+ default:
+ break;
+ }
+
+ if (fixed_position != NOT_FIXED)
+ fixed_size += heap->size;
+ else
+ reserve_mem_for_ion(MEMTYPE_EBI1, heap->size);
+
+ if (fixed_position == FIXED_LOW)
+ fixed_low_size += heap->size;
+ else if (fixed_position == FIXED_MIDDLE)
+ fixed_middle_size += heap->size;
+ else if (fixed_position == FIXED_HIGH)
+ fixed_high_size += heap->size;
+
+ if (mem_is_fmem)
+ apq8064_fmem_pdata.size += heap->size;
+ }
+ }
+
+ if (!fixed_size)
+ return;
+
+ if (apq8064_fmem_pdata.size) {
+ apq8064_fmem_pdata.reserved_size_low = fixed_low_size;
+ apq8064_fmem_pdata.reserved_size_high = fixed_high_size;
+ }
+
+ /* Since the fixed area may be carved out of lowmem,
+ * make sure the length is a multiple of 1M.
+ */
+ fixed_size = (fixed_size + MSM_MM_FW_SIZE + SECTION_SIZE - 1)
+ & SECTION_MASK;
+ apq8064_reserve_fixed_area(fixed_size);
+
+ fixed_low_start = APQ8064_FIXED_AREA_START;
+ fixed_middle_start = fixed_low_start + fixed_low_size;
+ fixed_high_start = fixed_middle_start + fixed_middle_size;
+
+ for (i = 0; i < apq8064_ion_pdata.nr; ++i) {
+ struct ion_platform_heap *heap = &(apq8064_ion_pdata.heaps[i]);
+
+ if (heap->extra_data) {
+ int fixed_position = NOT_FIXED;
+
+ switch (heap->type) {
+ case ION_HEAP_TYPE_CP:
+ fixed_position = ((struct ion_cp_heap_pdata *)
+ heap->extra_data)->fixed_position;
+ break;
+ case ION_HEAP_TYPE_CARVEOUT:
+ fixed_position = ((struct ion_co_heap_pdata *)
+ heap->extra_data)->fixed_position;
+ break;
+ default:
+ break;
+ }
+
+ switch (fixed_position) {
+ case FIXED_LOW:
+ heap->base = fixed_low_start;
+ break;
+ case FIXED_MIDDLE:
+ heap->base = fixed_middle_start;
+ break;
+ case FIXED_HIGH:
+ heap->base = fixed_high_start;
+ break;
+ default:
+ break;
+ }
+ }
+ }
#endif
}
@@ -381,6 +567,7 @@
static struct reserve_info apq8064_reserve_info __initdata = {
.memtype_reserve_table = apq8064_reserve_table,
.calculate_reserve_sizes = apq8064_calculate_reserve_sizes,
+ .reserve_fixed_area = apq8064_reserve_fixed_area,
.paddr_to_memtype = apq8064_paddr_to_memtype,
};
@@ -401,12 +588,14 @@
/* Check if 32 bit overflow occured */
if (high < mb->start)
- high = ~0UL;
+ high = -PAGE_SIZE;
low &= ~(bank_size - 1);
if (high - low <= bank_size)
- return;
+ goto no_dmm;
+
+#ifdef CONFIG_ENABLE_DMM
apq8064_reserve_info.low_unstable_address = mb->start -
MIN_MEMORY_BLOCK_SIZE + mb->size;
apq8064_reserve_info.max_unstable_size = MIN_MEMORY_BLOCK_SIZE;
@@ -416,6 +605,11 @@
apq8064_reserve_info.low_unstable_address,
apq8064_reserve_info.max_unstable_size,
apq8064_reserve_info.bank_size);
+ return;
+#endif
+no_dmm:
+ apq8064_reserve_info.low_unstable_address = high;
+ apq8064_reserve_info.max_unstable_size = 0;
}
static int apq8064_change_memory_power(u64 start, u64 size,
@@ -446,14 +640,29 @@
{
apq8064_set_display_params(prim_panel_name, ext_panel_name);
msm_reserve();
+ if (apq8064_fmem_pdata.size) {
+#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION)
+ if (reserve_info->fixed_area_size) {
+ apq8064_fmem_pdata.phys =
+ reserve_info->fixed_area_start + MSM_MM_FW_SIZE;
+ pr_info("mm fw at %lx (fixed) size %x\n",
+ reserve_info->fixed_area_start, MSM_MM_FW_SIZE);
+ pr_info("fmem start %lx (fixed) size %lx\n",
+ apq8064_fmem_pdata.phys,
+ apq8064_fmem_pdata.size);
+ }
+#endif
+ }
}
static void __init place_movable_zone(void)
{
+#ifdef CONFIG_ENABLE_DMM
movable_reserved_start = apq8064_reserve_info.low_unstable_address;
movable_reserved_size = apq8064_reserve_info.max_unstable_size;
pr_info("movable zone start %lx size %lx\n",
movable_reserved_start, movable_reserved_size);
+#endif
}
static void __init apq8064_early_reserve(void)
@@ -1867,6 +2076,7 @@
&android_usb_device,
&msm_device_wcnss_wlan,
&msm_device_iris_fm,
+ &apq8064_fmem_device,
#ifdef CONFIG_ANDROID_PMEM
#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION
&apq8064_android_pmem_device,