firmware/br-ext-chip-allwinner/board/v83x/kernel/patches/00000-arch_arm_mach-sunxi_s...

718 lines
21 KiB
Diff

diff -drupN a/arch/arm/mach-sunxi/sun8iw6-mcpm.c b/arch/arm/mach-sunxi/sun8iw6-mcpm.c
--- a/arch/arm/mach-sunxi/sun8iw6-mcpm.c 1970-01-01 03:00:00.000000000 +0300
+++ b/arch/arm/mach-sunxi/sun8iw6-mcpm.c 2022-06-12 05:28:14.000000000 +0300
@@ -0,0 +1,713 @@
+/*
+ * linux/arch/arm/mach-sunxi/sun8i-mcpm.c
+ *
+ * Copyright(c) 2013-2015 Allwinnertech Co., Ltd.
+ * http://www.allwinnertech.com
+ *
+ * Author: sunny <sunny@allwinnertech.com>
+ *
+ * allwinner sun8i platform multi-cluster power management operations.
+ *
+ * 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/init.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/cpu.h>
+
+#include <asm/mcpm.h>
+#include <asm/proc-fns.h>
+#include <asm/cacheflush.h>
+#include <asm/cputype.h>
+#include <asm/cp15.h>
+#include <asm/smp_plat.h>
+
+#include <mach/cci.h>
+#include <mach/platform.h>
+#include <mach/sun8i/platsmp.h>
+#include <linux/arisc/arisc.h>
+#include <linux/arisc/arisc-notifier.h>
+#include <mach/sunxi-chip.h>
+#include <mach/sunxi-smc.h>
+#include <asm/firmware.h>
+#include <mach/cpuidle-sunxi.h>
+
+#ifdef CONFIG_MCPM_WITH_ARISC_DVFS_SUPPORT
+/* sync with arisc module */
+static bool arisc_ready;
+#endif
+
+#define is_arisc_ready() (arisc_ready)
+#define set_arisc_ready(x) (arisc_ready = (x))
+
+#if defined(CONFIG_ARCH_SUN8IW6)
+static unsigned int soc_version;
+#define CORES_PER_CLUSTER (4)
+#elif defined(CONFIG_ARCH_SUN8IW9)
+#define CORES_PER_CLUSTER (3)
+#endif
+
+/* sun8i platform support two clusters,
+ * cluster0 : cortex-a7,
+ * cluster1 : cortex-a7.
+ */
+
+#define CLUSTER_0 0
+#define CLUSTER_1 1
+
+#define SUN8I_C0_CLSUTER_PWRUP_FREQ (600000) /* freq base on khz */
+#define SUN8I_C1_CLSUTER_PWRUP_FREQ (600000) /* freq base on khz */
+
+static unsigned int cluster_pll[MAX_CLUSTERS];
+static unsigned int cluster_powerup_freq[MAX_CLUSTERS];
+
+/* sunxi cluster and cpu power-management models */
+static void __iomem *sun8i_prcm_base;
+static void __iomem *sun8i_cpuxcfg_base;
+static void __iomem *sun8i_cpuscfg_base;
+
+
+/* add by huangshr
+ * to check cpu really power state*/
+cpumask_t cpu_power_up_state_mask;
+/* end by huangshr*/
+
+extern u32 cpu_cfg_readl(void __iomem *reg);
+extern void cpu_cfg_writel(u32 val, void __iomem *reg);
+
+int sun8i_is_wfi_mode(int pcpu, int pcluster)
+{
+ return cpu_cfg_readl(sun8i_cpuxcfg_base + SUNXI_CLUSTER_CPU_STATUS(pcluster)) & (1 << (16 + pcpu));
+}
+
+int sun8i_l2cache_is_wif_mode(int pcluster)
+{
+ return cpu_cfg_readl(sun8i_cpuxcfg_base + SUNXI_CLUSTER_CPU_STATUS(pcluster)) & (1 << 0);
+}
+
+void sun8i_set_secondary_entry(unsigned long boot_addr)
+{
+ sunxi_set_secondary_entry((void *)boot_addr);
+}
+
+static int sun8i_cpu_power_switch_set(unsigned int cluster, unsigned int cpu, bool enable)
+{
+ if (enable) {
+ if (0x00 == sunxi_smc_readl(sun8i_prcm_base + SUNXI_CPU_PWR_CLAMP(cluster, cpu))) {
+ pr_debug("%s: power switch enable already\n", __func__);
+ return 0;
+ }
+ /* de-active cpu power clamp */
+ sunxi_smc_writel(0xFE, sun8i_prcm_base + SUNXI_CPU_PWR_CLAMP(cluster, cpu));
+ udelay(20);
+
+ sunxi_smc_writel(0xF8, sun8i_prcm_base + SUNXI_CPU_PWR_CLAMP(cluster, cpu));
+ udelay(10);
+
+ sunxi_smc_writel(0xE0, sun8i_prcm_base + SUNXI_CPU_PWR_CLAMP(cluster, cpu));
+ udelay(10);
+#ifdef CONFIG_ARCH_SUN8IW6
+ sunxi_smc_writel(0xc0, sun8i_prcm_base + SUNXI_CPU_PWR_CLAMP(cluster, cpu));
+ udelay(10);
+#endif
+ sunxi_smc_writel(0x80, sun8i_prcm_base + SUNXI_CPU_PWR_CLAMP(cluster, cpu));
+ udelay(10);
+
+ sunxi_smc_writel(0x00, sun8i_prcm_base + SUNXI_CPU_PWR_CLAMP(cluster, cpu));
+ udelay(20);
+ while (0x00 != sunxi_smc_readl(sun8i_prcm_base + SUNXI_CPU_PWR_CLAMP(cluster, cpu))) {
+ ;
+ }
+ } else {
+ if (0xFF == sunxi_smc_readl(sun8i_prcm_base + SUNXI_CPU_PWR_CLAMP(cluster, cpu))) {
+ pr_debug("%s: power switch disable already\n", __func__);
+ return 0;
+ }
+ sunxi_smc_writel(0xFF, sun8i_prcm_base + SUNXI_CPU_PWR_CLAMP(cluster, cpu));
+ udelay(30);
+ while (0xFF != sunxi_smc_readl(sun8i_prcm_base + SUNXI_CPU_PWR_CLAMP(cluster, cpu))) {
+ ;
+ }
+ }
+ return 0;
+}
+
+int sun8i_cpu_power_set(unsigned int cluster, unsigned int cpu, bool enable)
+{
+ unsigned int value;
+ if (enable) {
+ /*
+ * power-up cpu core process
+ */
+ pr_debug("sun8i power-up cluster-%d cpu-%d\n", cluster, cpu);
+
+ cpumask_set_cpu(((cluster)*4 + cpu), &cpu_power_up_state_mask);
+
+ /* assert cpu core reset */
+ value = cpu_cfg_readl(sun8i_cpuxcfg_base + SUNXI_CPU_RST_CTRL(cluster));
+ value &= (~(1<<cpu));
+ cpu_cfg_writel(value, sun8i_cpuxcfg_base + SUNXI_CPU_RST_CTRL(cluster));
+ udelay(10);
+
+ /* assert cpu power-on reset */
+ value = sunxi_smc_readl(sun8i_cpuscfg_base + SUNXI_CLUSTER_PWRON_RESET(cluster));
+ value &= (~(1<<cpu));
+ sunxi_smc_writel(value, sun8i_cpuscfg_base + SUNXI_CLUSTER_PWRON_RESET(cluster));
+ udelay(10);
+
+ /* L1RSTDISABLE hold low */
+ value = cpu_cfg_readl(sun8i_cpuxcfg_base + SUNXI_CLUSTER_CTRL0(cluster));
+ value &= ~(1<<cpu);
+ cpu_cfg_writel(value, sun8i_cpuxcfg_base + SUNXI_CLUSTER_CTRL0(cluster));
+
+ /* release power switch */
+ sun8i_cpu_power_switch_set(cluster, cpu, 1);
+
+#ifdef CONFIG_ARCH_SUN8IW6
+ if (cpu == 0) {
+ if (soc_version == SUN8IW6P1_REV_A) {
+ /* de-assert cpu power-on reset */
+ value = sunxi_smc_readl(sun8i_cpuscfg_base + SUNXI_CLUSTER_PWRON_RESET(cluster));
+ value |= ((1<<cpu));
+ sunxi_smc_writel(value, sun8i_cpuscfg_base + SUNXI_CLUSTER_PWRON_RESET(cluster));
+ udelay(10);
+
+ /* assert cpu core reset */
+ value = cpu_cfg_readl(sun8i_cpuxcfg_base + SUNXI_CPU_RST_CTRL(cluster));
+ value |= (1<<cpu);
+ cpu_cfg_writel(value, sun8i_cpuxcfg_base + SUNXI_CPU_RST_CTRL(cluster));
+ udelay(10);
+ return 0;
+ } else {
+ /* bit4: C1_cpu0 */
+ cpu = 4;
+ }
+ }
+#endif
+
+ /* clear power-off gating */
+ value = sunxi_smc_readl(sun8i_prcm_base + SUNXI_CLUSTER_PWROFF_GATING(cluster));
+ value &= (~(0x1<<cpu));
+ sunxi_smc_writel(value, sun8i_prcm_base + SUNXI_CLUSTER_PWROFF_GATING(cluster));
+ udelay(20);
+
+#ifdef CONFIG_ARCH_SUN8IW6
+ if (cpu == 4)
+ cpu = 0;
+#endif
+
+ /* de-assert cpu power-on reset */
+ value = sunxi_smc_readl(sun8i_cpuscfg_base + SUNXI_CLUSTER_PWRON_RESET(cluster));
+ value |= ((1<<cpu));
+ sunxi_smc_writel(value, sun8i_cpuscfg_base + SUNXI_CLUSTER_PWRON_RESET(cluster));
+ udelay(10);
+
+ /* de-assert core reset */
+ value = cpu_cfg_readl(sun8i_cpuxcfg_base + SUNXI_CPU_RST_CTRL(cluster));
+ value |= (1<<cpu);
+ cpu_cfg_writel(value, sun8i_cpuxcfg_base + SUNXI_CPU_RST_CTRL(cluster));
+ udelay(10);
+
+ pr_debug("sun8i power-up cluster-%d cpu-%d already\n", cluster, cpu);
+ } else {
+ /*
+ * power-down cpu core process
+ */
+ pr_debug("sun8i power-down cluster-%d cpu-%d\n", cluster, cpu);
+#ifdef CONFIG_ARCH_SUN8IW6
+ if (cpu == 0) {
+ if (soc_version == SUN8IW6P1_REV_A) {
+ /* assert cpu core reset */
+ value = cpu_cfg_readl(sun8i_cpuxcfg_base + SUNXI_CPU_RST_CTRL(cluster));
+ value &= (~(1<<cpu));
+ cpu_cfg_writel(value, sun8i_cpuxcfg_base + SUNXI_CPU_RST_CTRL(cluster));
+ udelay(10);
+
+ cpumask_clear_cpu(((cluster)*4 + cpu), &cpu_power_up_state_mask);
+ return 0;
+ } else {
+ /* bit4: C1_cpu0 */
+ cpu = 4;
+ }
+ }
+#endif
+ /* enable cpu power-off gating */
+ value = sunxi_smc_readl(sun8i_prcm_base + SUNXI_CLUSTER_PWROFF_GATING(cluster));
+ value |= (1 << cpu);
+ sunxi_smc_writel(value, sun8i_prcm_base + SUNXI_CLUSTER_PWROFF_GATING(cluster));
+ udelay(20);
+
+#ifdef CONFIG_ARCH_SUN8IW6
+ if (cpu == 4)
+ cpu = 0;
+#endif
+
+ /* active the power output switch */
+ sun8i_cpu_power_switch_set(cluster, cpu, 0);
+
+ cpumask_clear_cpu(((cluster)*4 + cpu), &cpu_power_up_state_mask);
+ pr_debug("sun8i power-down cpu%d ok.\n", cpu);
+ }
+ return 0;
+}
+
+static int sun8i_cluster_power_set(unsigned int cluster, bool enable)
+{
+ unsigned int value;
+ int i;
+#ifdef CONFIG_MCPM_WITH_ARISC_DVFS_SUPPORT
+ /* cluster operation must wait arisc ready */
+ if (!is_arisc_ready()) {
+ pr_debug("%s: arisc not ready, can't power-up cluster\n", __func__);
+ return -EINVAL;
+ }
+#endif
+ if (enable) {
+ pr_debug("sun8i power-up cluster-%d\n", cluster);
+
+ /* assert cluster cores resets */
+ value = cpu_cfg_readl(sun8i_cpuxcfg_base + SUNXI_CPU_RST_CTRL(cluster));
+#if defined(CONFIG_ARCH_SUN8IW6)
+ value &= (~(0xF<<0)); /* Core Reset */
+#elif defined(CONFIG_ARCH_SUN8IW9)
+ value &= (~(0x7<<0)); /* Core Reset */
+#endif
+ cpu_cfg_writel(value, sun8i_cpuxcfg_base + SUNXI_CPU_RST_CTRL(cluster));
+ udelay(10);
+
+ /* assert cluster cores power-on reset */
+ value = sunxi_smc_readl(sun8i_cpuscfg_base + SUNXI_CLUSTER_PWRON_RESET(cluster));
+#if defined(CONFIG_ARCH_SUN8IW6)
+ value &= (~(0xF));
+#elif defined(CONFIG_ARCH_SUN8IW9)
+ value &= (~(0x7));
+#endif
+ sunxi_smc_writel(value, sun8i_cpuscfg_base + SUNXI_CLUSTER_PWRON_RESET(cluster));
+ udelay(10);
+
+ /* assert cluster resets */
+ value = cpu_cfg_readl(sun8i_cpuxcfg_base + SUNXI_CPU_RST_CTRL(cluster));
+ value &= (~(0x1<<24)); /* SOC DBG Reset */
+#if defined(CONFIG_ARCH_SUN8IW6)
+ value &= (~(0xF<<20)); /* ETM Reset */
+ value &= (~(0xF<<16)); /* Debug Reset */
+#elif defined(CONFIG_ARCH_SUN8IW9)
+ value &= (~(0x7<<20)); /* ETM Reset */
+ value &= (~(0x7<<16)); /* Debug Reset */
+#endif
+ value &= (~(0x1<<12)); /* HReset */
+ value &= (~(0x1<<8)); /* L2 Cache Reset*/
+ cpu_cfg_writel(value, sun8i_cpuxcfg_base + SUNXI_CPU_RST_CTRL(cluster));
+ udelay(10);
+
+ /* Set L2RSTDISABLE LOW */
+ value = cpu_cfg_readl(sun8i_cpuxcfg_base + SUNXI_CLUSTER_CTRL0(cluster));
+ value &= (~(0x1<<4));
+ cpu_cfg_writel(value, sun8i_cpuxcfg_base + SUNXI_CLUSTER_CTRL0(cluster));
+
+#ifdef CONFIG_MCPM_WITH_ARISC_DVFS_SUPPORT
+ /* notify arisc to power-up cluster */
+ arisc_dvfs_set_cpufreq(cluster_powerup_freq[cluster],
+ cluster_pll[cluster], ARISC_MESSAGE_ATTR_SOFTSYN, NULL, NULL);
+ mdelay(1);
+#endif
+
+#ifdef CONFIG_ARCH_SUN8IW6
+ if (soc_version == SUN8IW6P1_REV_A)
+ sun8i_cpu_power_switch_set(cluster, 0, 1);
+#endif
+ /* active ACINACTM */
+ value = cpu_cfg_readl(sun8i_cpuxcfg_base + SUNXI_CLUSTER_CTRL1(cluster));
+ value |= (1<<0);
+ cpu_cfg_writel(value, sun8i_cpuxcfg_base + SUNXI_CLUSTER_CTRL1(cluster));
+
+ /* clear cluster power-off gating */
+ value = sunxi_smc_readl(sun8i_prcm_base + SUNXI_CLUSTER_PWROFF_GATING(cluster));
+#ifdef CONFIG_ARCH_SUN8IW6
+ if (soc_version == SUN8IW6P1_REV_A)
+ value &= (~(0x1<<4));
+ value &= (~(0x1<<0));
+#else
+ value &= (~(0x1<<4));
+#endif
+ sunxi_smc_writel(value, sun8i_prcm_base + SUNXI_CLUSTER_PWROFF_GATING(cluster));
+ udelay(20);
+
+ /* de-active ACINACTM */
+ value = cpu_cfg_readl(sun8i_cpuxcfg_base + SUNXI_CLUSTER_CTRL1(cluster));
+ value &= (~(1<<0));
+ cpu_cfg_writel(value, sun8i_cpuxcfg_base + SUNXI_CLUSTER_CTRL1(cluster));
+
+ /* de-assert cores reset */
+ value = cpu_cfg_readl(sun8i_cpuxcfg_base + SUNXI_CPU_RST_CTRL(cluster));
+ value |= (0x1<<24); /* SOC DBG Reset */
+#if defined(CONFIG_ARCH_SUN8IW6)
+ value |= (0xF<<20); /* ETM Reset */
+ value |= (0xF<<16); /* Debug Reset */
+#elif defined(CONFIG_ARCH_SUN8IW9)
+ value |= (0x7<<20); /* ETM Reset */
+ value |= (0x7<<16); /* Debug Reset */
+#endif
+ value |= (0x1<<12); /* HReset */
+ value |= (0x1<<8); /* L2 Cache Reset*/
+ cpu_cfg_writel(value, sun8i_cpuxcfg_base + SUNXI_CPU_RST_CTRL(cluster));
+ udelay(20);
+ pr_debug("sun8i power-up cluster-%d ok\n", cluster);
+ } else {
+
+ pr_debug("sun8i power-down cluster-%d\n", cluster);
+
+ /* active ACINACTM */
+ value = cpu_cfg_readl(sun8i_cpuxcfg_base + SUNXI_CLUSTER_CTRL1(cluster));
+ value |= (1<<0);
+ cpu_cfg_writel(value, sun8i_cpuxcfg_base + SUNXI_CLUSTER_CTRL1(cluster));
+
+ while (1) {
+ if (sun8i_l2cache_is_wif_mode(cluster)) {
+ break;
+ }
+ /* maybe should support timeout to avoid deadloop */
+ }
+
+ /* assert cluster cores resets */
+ value = cpu_cfg_readl(sun8i_cpuxcfg_base + SUNXI_CPU_RST_CTRL(cluster));
+#if defined(CONFIG_ARCH_SUN8IW6)
+ value &= (~(0xF<<0)); /* Core Reset */
+#elif defined(CONFIG_ARCH_SUN8IW9)
+ value &= (~(0x7<<0)); /* Core Reset */
+#endif
+ cpu_cfg_writel(value, sun8i_cpuxcfg_base + SUNXI_CPU_RST_CTRL(cluster));
+ udelay(10);
+
+ /* assert cluster cores power-on reset */
+ value = sunxi_smc_readl(sun8i_cpuscfg_base + SUNXI_CLUSTER_PWRON_RESET(cluster));
+#if defined(CONFIG_ARCH_SUN8IW6)
+ value &= (~(0xF));
+#elif defined(CONFIG_ARCH_SUN8IW9)
+ value &= (~(0x7));
+#endif
+ sunxi_smc_writel(value, sun8i_cpuscfg_base + SUNXI_CLUSTER_PWRON_RESET(cluster));
+ udelay(10);
+
+ /* assert cluster resets */
+ value = cpu_cfg_readl(sun8i_cpuxcfg_base + SUNXI_CPU_RST_CTRL(cluster));
+ value &= (~(0x1<<24)); /* SOC DBG Reset */
+#if defined(CONFIG_ARCH_SUN8IW6)
+ value &= (~(0xF<<20)); /* ETM Reset */
+ value &= (~(0xF<<16)); /* Debug Reset */
+#elif defined(CONFIG_ARCH_SUN8IW9)
+ value &= (~(0x7<<20)); /* ETM Reset */
+ value &= (~(0x7<<16)); /* Debug Reset */
+#endif
+ value &= (~(0x1<<12)); /* HReset */
+ value &= (~(0x1<<8)); /* L2 Cache Reset*/
+ cpu_cfg_writel(value, sun8i_cpuxcfg_base + SUNXI_CPU_RST_CTRL(cluster));
+ udelay(10);
+
+ /* enable cluster and cores power-off gating */
+ value = sunxi_smc_readl(sun8i_prcm_base + SUNXI_CLUSTER_PWROFF_GATING(cluster));
+ value |= (1<<4);
+#if defined(CONFIG_ARCH_SUN8IW6)
+ value |= (0xF<<0);
+#elif defined(CONFIG_ARCH_SUN8IW9)
+ value |= (0x7<<0);
+#endif
+ sunxi_smc_writel(value, sun8i_prcm_base + SUNXI_CLUSTER_PWROFF_GATING(cluster));
+ udelay(20);
+
+ /* disable cluster cores power switch */
+ for (i = 0; i < CORES_PER_CLUSTER; i++) {
+ sun8i_cpu_power_switch_set(cluster, i, 0);
+ }
+
+#ifdef CONFIG_MCPM_WITH_ARISC_DVFS_SUPPORT
+ /* notify arisc to power-down cluster,
+ * arisc will disable cluster clock and power-off cpu power domain.
+ */
+ arisc_dvfs_set_cpufreq(0, cluster_pll[cluster],
+ ARISC_MESSAGE_ATTR_SOFTSYN, NULL, NULL);
+#endif
+ pr_debug("sun8i power-down cluster-%d ok\n", cluster);
+ }
+
+ return 0;
+}
+
+static int sun8i_cluster_power_status(unsigned int cluster)
+{
+ int status = 0;
+ unsigned int value;
+#ifdef CONFIG_ARCH_SUN8IW6
+ unsigned int value_rst;
+#endif
+
+ value = cpu_cfg_readl(sun8i_cpuxcfg_base + SUNXI_CLUSTER_CPU_STATUS(cluster));
+
+ /* cluster WFI status :
+ * all cpu cores enter WFI mode + L2Cache enter WFI status
+ */
+#ifdef CONFIG_ARCH_SUN8IW6
+ value_rst = cpu_cfg_readl(sun8i_cpuxcfg_base + SUNXI_CPU_RST_CTRL(cluster));
+ if (soc_version == SUN8IW6P1_REV_A) {
+ if ((!(value_rst & 0x1)) && (((value >> 16) & 0xe) == 0xe)) {
+ status = 1;
+ }
+ } else {
+ if (((value >> 16) & 0xf) == 0xf) {
+ status = 1;
+ }
+ }
+#elif defined(CONFIG_ARCH_SUN8IW9)
+ if (((value >> 16) & 0x7) == 0x7) {
+ status = 1;
+ }
+#endif
+ return status;
+}
+
+static int sun8i_mcpm_cpu_powerup(unsigned int cpu, unsigned int cluster)
+{
+ sun8i_set_secondary_entry(virt_to_phys(mcpm_entry_point));
+ return sun8i_cpu_power_set(cluster, cpu, 1);
+}
+
+static int sun8i_mcpm_cluster_powerup(unsigned int cluster)
+{
+ return sun8i_cluster_power_set(cluster, 1);
+}
+
+extern bool mcpm_cluster_unused(unsigned int cluster);
+
+static int sun8i_mcpm_wait_for_powerdown(unsigned int cpu, unsigned int cluster)
+{
+ /* power-down cpu core */
+ sun8i_cpu_power_set(cluster, cpu, 0);
+
+#ifndef CONFIG_BL_SWITCHER
+ /* if bL swithcer disable, the last-man should power-down cluster */
+ if ((mcpm_cluster_unused(cluster)) &&
+ (sun8i_cluster_power_status(cluster))) {
+ sun8i_cluster_power_set(cluster, 0);
+ }
+#endif
+ return 0;
+}
+
+static void sun8i_mcpm_cluster_cache_disbale(void)
+{
+ /*
+ * Flush all cache levels for this cluster.
+ *
+ * Cluster1/Cluster0 can hit in the cache with SCTLR.C=0, so we don't need
+ * a preliminary flush here for those CPUs. At least, that's
+ * the theory -- without the extra flush, Linux explodes on
+ * RTSM (maybe not needed anymore, to be investigated).
+ */
+ flush_cache_all();
+ set_cr(get_cr() & ~CR_C);
+ flush_cache_all();
+
+ /*
+ * This is a harmless no-op. On platforms with a real
+ * outer cache this might either be needed or not,
+ * depending on where the outer cache sits.
+ */
+ outer_flush_all();
+
+ /* Disable local coherency by clearing the ACTLR "SMP" bit: */
+ set_auxcr(get_auxcr() & ~(1 << 6));
+}
+
+static void sun8i_mcpm_cpu_cache_disable(void)
+{
+ /*
+ * Flush the local CPU cache.
+ *
+ * Cluster1/Cluster0 can hit in the cache with SCTLR.C=0,
+ * so we don't need
+ * a preliminary flush here for those CPUs. At least, that's
+ * the theory -- without the extra flush, Linux explodes on
+ * RTSM (maybe not needed anymore, to be investigated).
+ */
+ flush_cache_louis();
+ set_cr(get_cr() & ~CR_C);
+ flush_cache_louis();
+
+ /* Disable local coherency by clearing the ACTLR "SMP" bit: */
+ set_auxcr(get_auxcr() & ~(1 << 6));
+}
+
+static void sun8i_cpu_suspend_prepare(unsigned int cpu, unsigned int cluster)
+{
+ return;
+}
+
+static void sun8i_cpu_powerdown_prepare(unsigned int cpu, unsigned int cluster)
+{
+ return;
+}
+
+static void sun8i_cluster_powerdown_prepare(unsigned int cluster)
+{
+
+}
+
+static void sun8i_cpu_is_up(unsigned int cpu, unsigned int cluster)
+{
+
+}
+
+static void sun8i_cluster_is_up(unsigned int cluster)
+{
+
+}
+
+static const struct mcpm_platform_ops sun8i_mcpm_power_ops = {
+ .cluster_powerup = sun8i_mcpm_cluster_powerup,
+ .cpu_powerup = sun8i_mcpm_cpu_powerup,
+ .wait_for_powerdown = sun8i_mcpm_wait_for_powerdown,
+ .cluster_cache_disable = sun8i_mcpm_cluster_cache_disbale,
+ .cpu_cache_disable = sun8i_mcpm_cpu_cache_disable,
+ .cpu_suspend_prepare = sun8i_cpu_suspend_prepare,
+ .cpu_powerdown_prepare = sun8i_cpu_powerdown_prepare,
+ .cluster_powerdown_prepare = sun8i_cluster_powerdown_prepare,
+ .cpu_is_up = sun8i_cpu_is_up,
+ .cluster_is_up = sun8i_cluster_is_up,
+};
+#ifdef CONFIG_MCPM_WITH_ARISC_DVFS_SUPPORT
+/* sun8i_arisc_notify_call - callback that gets triggered when arisc ready. */
+static int sun8i_arisc_notify_call(struct notifier_block *nfb,
+ unsigned long action, void *parg)
+{
+ unsigned int mpidr, cpu, cluster;
+
+ mpidr = read_cpuid_mpidr();
+ cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
+ cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
+
+ switch (action) {
+ case ARISC_INIT_READY: {
+ unsigned int cpu;
+
+ /* arisc ready now */
+ set_arisc_ready(1);
+
+ /* power-off cluster-1 first */
+ sun8i_cluster_power_set((cluster == CLUSTER_0) ? CLUSTER_1 : CLUSTER_0, 0);
+
+ /* power-up off-line cpus*/
+ for_each_present_cpu(cpu) {
+ if (num_online_cpus() >= setup_max_cpus) {
+ break;
+ }
+ if (!cpu_online(cpu)) {
+ cpu_up(cpu);
+ }
+ }
+ break;
+ }
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block sun8i_arisc_notifier = {
+ &sun8i_arisc_notify_call,
+ NULL,
+ 0
+};
+#endif
+static void __init sun8i_mcpm_boot_cpu_init(void)
+{
+ unsigned int mpidr, cpu, cluster;
+
+ mpidr = read_cpuid_mpidr();
+ cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
+ cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
+
+ pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
+ BUG_ON(cpu >= MAX_CPUS_PER_CLUSTER || cluster >= MAX_NR_CLUSTERS);
+
+ cpumask_clear(&cpu_power_up_state_mask);
+ cpumask_set_cpu((cluster*4 + cpu), &cpu_power_up_state_mask);
+}
+
+
+int sun8i_mcpm_cpu_map_init(void)
+{
+ /* default cpu logical map */
+ cpu_logical_map(0) = 0x000;
+ cpu_logical_map(1) = 0x001;
+ cpu_logical_map(2) = 0x002;
+ cpu_logical_map(3) = 0x003;
+ cpu_logical_map(4) = 0x100;
+ cpu_logical_map(5) = 0x101;
+ cpu_logical_map(6) = 0x102;
+ cpu_logical_map(7) = 0x103;
+
+
+ return 0;
+}
+extern void sun8i_power_up_setup(unsigned int affinity_level);
+extern int __init cci_init(void);
+
+static int __init sun8i_mcpm_init(void)
+{
+ int ret;
+
+ /* initialize cci driver first */
+ cci_init();
+
+ /* initialize prcm and cpucfg model virtual base address */
+ sun8i_prcm_base = (void __iomem *)(SUNXI_R_PRCM_VBASE);
+ sun8i_cpuxcfg_base = (void __iomem *)(SUNXI_CPUXCFG_VBASE);
+ sun8i_cpuscfg_base = (void __iomem *)(SUNXI_R_CPUCFG_VBASE);
+ sun8i_mcpm_boot_cpu_init();
+
+ ret = mcpm_platform_register(&sun8i_mcpm_power_ops);
+#ifndef CONFIG_SUNXI_TRUSTZONE
+ if (!ret) {
+ ret = mcpm_sync_init(sun8i_power_up_setup);
+ }
+#endif
+ /* set sun8i platform non-boot cpu startup entry. */
+ sun8i_set_secondary_entry(virt_to_phys(mcpm_entry_point));
+
+ /* initialize cluster0 and cluster1 cluster pll number */
+ cluster_pll[CLUSTER_0] = ARISC_DVFS_PLL1;
+ cluster_pll[CLUSTER_1] = ARISC_DVFS_PLL2;
+
+ /* initialize ca7 and ca15 cluster power-up freq as deafult */
+ cluster_powerup_freq[CLUSTER_0] = SUN8I_C0_CLSUTER_PWRUP_FREQ;
+ cluster_powerup_freq[CLUSTER_1] = SUN8I_C1_CLSUTER_PWRUP_FREQ;
+#ifdef CONFIG_MCPM_WITH_ARISC_DVFS_SUPPORT
+ /* register arisc ready notifier */
+ arisc_register_notifier(&sun8i_arisc_notifier);
+#endif
+#ifdef CONFIG_FPGA_V7_PLATFORM
+ /* hard-encode by sunny to support sun8i 2big+2little,
+ * we should use device-tree to config the cluster and cpu
+ * topology information.
+ * but sun8i not support device-tree now,
+ * so I just hard-encode for temp debug.
+ */
+ cpu_logical_map(0) = 0x000;
+ cpu_logical_map(1) = 0x001;
+ cpu_logical_map(2) = 0x100;
+ cpu_logical_map(3) = 0x101;
+#endif
+
+#ifdef CONFIG_ARCH_SUN8IW6
+ soc_version = sunxi_get_soc_ver();
+#endif
+
+ return 0;
+}
+early_initcall(sun8i_mcpm_init);