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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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<> 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);