ASR_BASE
Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/marvell/linux/arch/arm/mach-mmp/mmp_cpuidle.c b/marvell/linux/arch/arm/mach-mmp/mmp_cpuidle.c
new file mode 100644
index 0000000..c2f4c07
--- /dev/null
+++ b/marvell/linux/arch/arm/mach-mmp/mmp_cpuidle.c
@@ -0,0 +1,280 @@
+/*
+ * linux/arch/arm/mach-mmp/mmp_cpuidle.c
+ *
+ * Author: Fangsuo Wu <fswu@marvell.com>
+ * Copyright: (C) 2013 marvell International Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#include <linux/cpu_pm.h>
+#include <linux/cpuidle.h>
+#include <linux/init.h>
+#include <linux/irqchip/arm-gic.h>
+#include <linux/kernel.h>
+#include <linux/pm_qos.h>
+#include <soc/asr/asrdcstat.h>
+#include <linux/cputype.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/mcpm.h>
+#include <trace/events/pxa.h>
+
+#include "help_v7.h"
+#include <soc/asr/mmp_cpuidle.h>
+#include "reset.h"
+#include "pm.h"
+
+#include <asm/cacheflush.h>
+
+#define LPM_NUM 16
+#define INVALID_LPM -1
+#define DEFAULT_LPM_FLAG 0xFFFFFFFF
+
+struct platform_idle *mmp_idle;
+static int mmp_wake_saved;
+static int asr_cpu_target_lpm[MAX_NR_CLUSTERS][MAX_CPUS_PER_CLUSTER];
+static unsigned int asr_cpu_entered_state_ptr[MAX_NR_CLUSTERS][MAX_CPUS_PER_CLUSTER];
+static int asr_cluster_state[MAX_NR_CLUSTERS];
+
+/*
+ * find_couple_state - Find the maximum state platform can enter
+ *
+ * @index: pointer to variable which stores the maximum state
+ * @cluster: cluster number
+ *
+ * Must be called with function holds mmp_lpm_lock
+ */
+static void find_coupled_state(int *index, int cluster)
+{
+ int i;
+ int platform_lpm = DEFAULT_LPM_FLAG;
+
+ for (i = 0; i < MAX_CPUS_PER_CLUSTER; i++)
+ platform_lpm &= asr_cpu_target_lpm[cluster][i];
+
+ *index = min(find_first_zero_bit((void *)&platform_lpm,
+ LPM_NUM), \
+ pm_qos_request(PM_QOS_CPUIDLE_BLOCK)) - 1;
+}
+
+void asr_set_target_lpm_state(unsigned int cpu, unsigned int cluster,
+ int target_state, int *entered_state_ptr)
+{
+ asr_cpu_target_lpm[cluster][cpu] = (1 << (target_state + 1)) - 1;
+ asr_cpu_entered_state_ptr[cluster][cpu] = (unsigned int)entered_state_ptr;
+}
+
+static void asr_set_entered_state(unsigned int cpu, unsigned int cluster,
+ int entered_state)
+{
+ int *state;
+ if (asr_cpu_entered_state_ptr[cluster][cpu]) {
+ state = (int *)asr_cpu_entered_state_ptr[cluster][cpu];
+ *state = entered_state;
+ }
+}
+
+static void asr_clear_lpm_state(unsigned int cpu, unsigned int cluster)
+{
+ asr_cpu_target_lpm[cluster][cpu] = 0;
+ asr_cpu_entered_state_ptr[cluster][cpu] = 0;
+}
+
+static void asr_cpu_cache_disable(void)
+{
+ v7_exit_coherency_flush(louis);
+}
+
+static void asr_cluster_cache_disable(void)
+{
+ int mpidr, this_cluster, state;
+ mpidr = read_cpuid_mpidr();
+ this_cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
+
+ find_coupled_state(&state, this_cluster);
+
+ if (state >= mmp_idle->l2_flush_state)
+ v7_exit_coherency_flush(all);
+ else
+ v7_exit_coherency_flush(louis);
+}
+
+static int asr_wait_for_powerdown(unsigned int cpu, unsigned int cluster)
+{
+ return 0;
+}
+
+static void asr_cpu_powerdown_prepare(unsigned int cpu, unsigned int cluster)
+{
+ int state = mmp_idle->cpudown_state;
+
+ if (asr_cpu_target_lpm[cluster][cpu] == 0)
+ asr_set_target_lpm_state(cpu, cluster, mmp_idle->hotplug_state, 0);
+
+ asr_set_entered_state(cpu, cluster, state);
+
+ if(mmp_idle->ops->set_pmu)
+ mmp_idle->ops->set_pmu(cpu, state);
+
+ trace_pxa_cpu_idle(LPM_ENTRY(state), cpu, cluster);
+ cpu_dcstat_event(cpu_dcstat_clk, cpu, CPU_IDLE_ENTER, state);
+}
+
+static void asr_cluster_powerdown_prepare(unsigned int cluster)
+{
+ int mpidr, cpu, this_cluster;
+ int state = 0;
+
+ mpidr = read_cpuid_mpidr();
+ cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
+ this_cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
+
+ BUG_ON(cluster != this_cluster);
+
+ cpu_cluster_pm_enter();
+
+ asr_cluster_state[this_cluster] = CLUSTER_DOWN;
+
+ find_coupled_state(&state, cluster);
+ asr_set_entered_state(cpu, this_cluster, state);
+
+ if (state >= mmp_idle->wakeup_state &&
+ state < mmp_idle->l2_flush_state &&
+ mmp_idle->ops->save_wakeup)
+ {
+ mmp_wake_saved = 1;
+ mmp_idle->ops->save_wakeup();
+ }
+
+ if(mmp_idle->ops->set_pmu)
+ mmp_idle->ops->set_pmu(cpu, state);
+
+ if ( (state >= mmp_idle->cpudown_state) && (state != LPM_D2_UDR) )
+ {
+ cpu_dcstat_event(cpu_dcstat_clk, cpu, CPU_M2_OR_DEEPER_ENTER,
+ state);
+ vol_dcstat_event(state);
+ vol_ledstatus_event(state);
+ }
+ trace_pxa_cpu_idle(LPM_ENTRY(state), cpu, cluster);
+ cpu_dcstat_event(cpu_dcstat_clk, cpu, CPU_IDLE_ENTER, state);
+}
+
+static int up_mode;
+static int __init __init_up(char *arg)
+{
+ up_mode = 1;
+ return 1;
+}
+__setup("up_mode", __init_up);
+
+static int mmp_pm_power_up(unsigned int cpu, unsigned int cluster)
+{
+ pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
+ if (cluster >= MAX_NR_CLUSTERS || cpu >= MAX_CPUS_PER_CLUSTER) {
+ pr_info("!!!%s: cpu %u cluster %u\n", __func__, cpu, cluster);
+ return -EINVAL;
+ }
+
+ if (up_mode)
+ return -EINVAL;
+
+ cpu_dcstat_event(cpu_dcstat_clk, cpu, CPU_IDLE_EXIT, MAX_LPM_INDEX);
+
+ mmp_cpu_power_up(cpu, cluster);
+
+ return 0;
+}
+
+static void asr_cluster_is_up(unsigned int cluster)
+{
+ if (asr_cluster_state[cluster] != CLUSTER_DOWN)
+ return;
+
+ asr_cluster_state[cluster] = CLUSTER_UP;
+
+ vol_dcstat_event(MAX_LPM_INDEX);
+ vol_ledstatus_event(MAX_LPM_INDEX);
+
+ if (mmp_wake_saved && mmp_idle->ops->restore_wakeup) {
+ mmp_wake_saved = 0;
+ mmp_idle->ops->restore_wakeup();
+ }
+ cpu_cluster_pm_exit();
+}
+
+static void asr_cpu_is_up(unsigned int cpu, unsigned int cluster)
+{
+ pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
+ BUG_ON(cluster >= MAX_NR_CLUSTERS || cpu >= MAX_CPUS_PER_CLUSTER);
+
+ cpu_dcstat_event(cpu_dcstat_clk, cpu, CPU_IDLE_EXIT, MAX_LPM_INDEX);
+
+ trace_pxa_cpu_idle(LPM_EXIT(0), cpu, cluster);
+
+ asr_clear_lpm_state(cpu, cluster);
+
+ if(mmp_idle->ops->clr_pmu)
+ mmp_idle->ops->clr_pmu(cpu);
+}
+
+/**
+ * mmp_platform_power_register - register platform power ops
+ *
+ * @idle: platform_idle structure points to platform power ops
+ *
+ * An error is returned if the registration has been done previously.
+ */
+int __init mmp_platform_power_register(struct platform_idle *idle)
+{
+ if (mmp_idle)
+ return -EBUSY;
+ mmp_idle = idle;
+
+#ifdef CONFIG_CPU_IDLE_MMP_V7
+ mcpm_platform_state_register(mmp_idle->states, mmp_idle->state_count);
+#endif
+
+ return 0;
+}
+
+static const struct mcpm_platform_ops mmp_pm_power_ops = {
+ .cpu_powerup = mmp_pm_power_up,
+ .cluster_powerdown_prepare = asr_cluster_powerdown_prepare,
+ .cpu_powerdown_prepare = asr_cpu_powerdown_prepare,
+ .cluster_cache_disable = asr_cluster_cache_disable,
+ .cpu_cache_disable = asr_cpu_cache_disable,
+ .cluster_is_up = asr_cluster_is_up,
+ .cpu_is_up = asr_cpu_is_up,
+ .wait_for_powerdown = asr_wait_for_powerdown,
+};
+
+static int __init mmp_pm_init(void)
+{
+ int ret;
+
+ /*
+ * TODO:Should check if hardware is initialized here.
+ * See vexpress_spc_check_loaded()
+ */
+ memset(asr_cpu_target_lpm, DEFAULT_LPM_FLAG, sizeof(asr_cpu_target_lpm));
+ memset(asr_cpu_entered_state_ptr, 0, sizeof(asr_cpu_entered_state_ptr));
+ memset(asr_cluster_state, 0, sizeof(asr_cluster_state));
+
+ ret = mcpm_platform_register(&mmp_pm_power_ops);
+ if (!ret)
+ pr_warning("Power ops has already been initialized\n");
+
+ if (mmp_idle->ops->power_up_setup) {
+ ret = mcpm_sync_init(mmp_idle->ops->power_up_setup);
+ if (!ret)
+ pr_info("mmp power management initialized\n");
+ } else
+ pr_warning("mmp power_up_setup function is NULL!\n");
+
+ return ret;
+}
+early_initcall(mmp_pm_init);