/* * arch/arm/mach-sunxi/include/mach/sun8i/platsmp.h * * Copyright(c) 2013-2015 Allwinnertech Co., Ltd. * http://www.allwinnertech.com * * Author: liugang * * sun8i smp ops header file * * 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. */ #ifndef __SUN8I_PLAT_SMP_H #define __SUN8I_PLAT_SMP_H #ifdef CONFIG_ARCH_SUN8IW6P1 #include #include static inline void enable_cpu(int cpu_nr) { unsigned int cluster; unsigned int cpu; unsigned int mpidr; unsigned int value; mpidr = cpu_logical_map(cpu_nr); cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); /* step1: power switch on */ sunxi_smc_writel(0x00, SUNXI_R_PRCM_VBASE + SUNXI_CPU_PWR_CLAMP(cluster, cpu)); while (0x00 != sunxi_smc_readl(SUNXI_R_PRCM_VBASE + SUNXI_CPU_PWR_CLAMP(cluster, cpu))) { ; } mdelay(5); /* step2: power gating off */ value = sunxi_smc_readl(SUNXI_R_PRCM_VBASE + SUNXI_CLUSTER_PWROFF_GATING(cluster)); value &= (~(0x1 << cpu)); sunxi_smc_writel(value, SUNXI_R_PRCM_VBASE + SUNXI_CLUSTER_PWROFF_GATING(cluster)); mdelay(2); /* step3: clear reset */ value = sunxi_smc_readl(SUNXI_R_CPUCFG_VBASE + SUNXI_CLUSTER_PWRON_RESET(cluster)); value |= (1 << cpu); sunxi_smc_writel(value, SUNXI_R_CPUCFG_VBASE + SUNXI_CLUSTER_PWRON_RESET(cluster)); /* step4: core reset */ value = sunxi_smc_readl(SUNXI_CPUXCFG_VBASE + SUNXI_CPU_RST_CTRL(cluster)); value |= (1 << cpu); sunxi_smc_writel(value, SUNXI_CPUXCFG_VBASE + SUNXI_CPU_RST_CTRL(cluster)); } static inline void disable_cpu(int cpu_nr) { unsigned int cluster; unsigned int cpu; unsigned int mpidr; unsigned int value; mpidr = cpu_logical_map(cpu_nr); cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); /* step1: core deassert */ value = sunxi_smc_readl(SUNXI_CPUXCFG_VBASE + SUNXI_CPU_RST_CTRL(cluster)); value &= ~(1 << cpu); sunxi_smc_writel(value, SUNXI_CPUXCFG_VBASE + SUNXI_CPU_RST_CTRL(cluster)); /* step2: deassert */ value = sunxi_smc_readl(SUNXI_R_CPUCFG_VBASE + SUNXI_CLUSTER_PWRON_RESET(cluster)); value &= ~(1 << cpu); sunxi_smc_writel(value, SUNXI_R_CPUCFG_VBASE + SUNXI_CLUSTER_PWRON_RESET(cluster)); /* step3: enable power gating off */ mdelay(2); value = sunxi_smc_readl(SUNXI_R_PRCM_VBASE + SUNXI_CLUSTER_PWROFF_GATING(cluster)); value |= 0x1 << cpu; sunxi_smc_writel(value, SUNXI_R_PRCM_VBASE + SUNXI_CLUSTER_PWROFF_GATING(cluster)); /* step1: power switch on */ mdelay(5); sunxi_smc_writel(0xff, SUNXI_R_PRCM_VBASE + SUNXI_CPU_PWR_CLAMP(cluster, cpu)); mdelay(1); } #else static inline void enable_cpu(int cpu) { u32 pwr_reg; /* step1: Assert nCOREPORESET LOW and hold L1RSTDISABLE LOW. * Ensure DBGPWRDUP is held LOW to prevent any external * debug access to the processor. */ /* assert cpu core reset */ sunxi_smc_writel(0, (void *)(SUNXI_R_CPUCFG_VBASE + CPUX_RESET_CTL(cpu))); /* L1RSTDISABLE hold low */ pwr_reg = sunxi_smc_readl((void *)(SUNXI_R_CPUCFG_VBASE + SUNXI_CPUCFG_GENCTL)); pwr_reg &= ~(1 << cpu); sunxi_smc_writel(pwr_reg, (void *)(SUNXI_R_CPUCFG_VBASE + SUNXI_CPUCFG_GENCTL)); #ifdef CONFIG_ARCH_SUN8IW1 /* step2: release power clamp */ /* write bit3, bit4 to 0 */ sunxi_smc_writel(0xe7, (void *)(SUNXI_R_PRCM_VBASE + SUNXI_CPUX_PWR_CLAMP(cpu))); while ((0xe7) != sunxi_smc_readl((void *)(SUNXI_R_CPUCFG_VBASE + SUNXI_CPUX_PWR_CLAMP_STATUS(cpu)))) ; /* write 012567 bit to 0 */ sunxi_smc_writel(0x00, (void *)(SUNXI_R_PRCM_VBASE + SUNXI_CPUX_PWR_CLAMP(cpu))); while ((0x00) != sunxi_smc_readl((void *)(SUNXI_R_CPUCFG_VBASE + SUNXI_CPUX_PWR_CLAMP_STATUS(cpu)))) ; mdelay(2); #endif /* step3: clear power-off gating */ pwr_reg = sunxi_smc_readl((void *)(SUNXI_R_PRCM_VBASE + SUNXI_CPU_PWROFF_REG)); pwr_reg &= ~(0x00000001 << cpu); sunxi_smc_writel(pwr_reg, (void *)(SUNXI_R_PRCM_VBASE + SUNXI_CPU_PWROFF_REG)); mdelay(1); /* step4: de-assert core reset */ sunxi_smc_writel(3, (void *)(SUNXI_R_CPUCFG_VBASE + CPUX_RESET_CTL(cpu))); } static inline void disable_cpu(int cpu) { u32 pwr_reg; sunxi_smc_writel(0, (void *)(SUNXI_R_CPUCFG_VBASE + CPUX_RESET_CTL(cpu))); /* step9: set up power-off signal */ pwr_reg = sunxi_smc_readl(IO_ADDRESS(SUNXI_R_PRCM_PBASE) + SUNXI_CPU_PWROFF_REG); pwr_reg |= (1 << cpu); sunxi_smc_writel(pwr_reg, IO_ADDRESS(SUNXI_R_PRCM_PBASE) + SUNXI_CPU_PWROFF_REG); usleep_range(1000, 1000); #ifdef CONFIG_ARCH_SUN8IW1 /* step10: active the power output clamp */ sunxi_smc_writel(0xff, IO_ADDRESS(SUNXI_R_PRCM_PBASE) + SUNXI_CPUX_PWR_CLAMP(cpu)); #endif } #endif /* * set the sencodary cpu boot entry address. */ static inline void sunxi_set_secondary_entry(void *entry) { #ifdef CONFIG_ARCH_SUN8IW6P1 sunxi_smc_writel((u32) entry, (void *)(SUNXI_R_CPUCFG_VBASE + PRIVATE_REG0)); #else writel((u32) entry, (void *)(SUNXI_R_CPUCFG_VBASE + SUNXI_CPUCFG_P_REG0)); #endif } /* * get the sencodary cpu boot entry address. */ static inline void *sunxi_get_secondary_entry(void) { #ifdef CONFIG_ARCH_SUN8IW6P1 return (void *) sunxi_smc_readl((void *)(SUNXI_R_CPUCFG_VBASE + PRIVATE_REG0)); #else return (void *)readl(SUNXI_R_CPUCFG_VBASE + SUNXI_CPUCFG_P_REG0); #endif } #endif /* __SUN8I_PLAT_SMP_H */