/* * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include <../hikey960_def.h> #include #include "hisi_pwrc.h" /* resource lock api */ #define RES0_LOCK_BASE (SOC_PCTRL_RESOURCE0_LOCK_ADDR(PCTRL_BASE)) #define RES1_LOCK_BASE (SOC_PCTRL_RESOURCE1_LOCK_ADDR(PCTRL_BASE)) #define RES2_LOCK_BASE (SOC_PCTRL_RESOURCE2_LOCK_ADDR(PCTRL_BASE)) #define LOCK_BIT (0x1 << 28) #define LOCK_ID_MASK (0x7u << 29) #define CPUIDLE_LOCK_ID(core) (0x6 - (core)) #define LOCK_UNLOCK_OFFSET 0x4 #define LOCK_STAT_OFFSET 0x8 #define CLUSTER0_CPUS_ONLINE_MASK (0xF << 16) #define CLUSTER1_CPUS_ONLINE_MASK (0xF << 20) /* cpu hotplug flag api */ #define SCTRL_BASE (SOC_ACPU_SCTRL_BASE_ADDR) #define REG_SCBAKDATA3_OFFSET (SOC_SCTRL_SCBAKDATA3_ADDR(SCTRL_BASE)) #define REG_SCBAKDATA8_OFFSET (SOC_SCTRL_SCBAKDATA8_ADDR(SCTRL_BASE)) #define REG_SCBAKDATA9_OFFSET (SOC_SCTRL_SCBAKDATA9_ADDR(SCTRL_BASE)) #define CPUIDLE_FLAG_REG(cluster) \ ((cluster == 0) ? REG_SCBAKDATA8_OFFSET : \ REG_SCBAKDATA9_OFFSET) #define CLUSTER_IDLE_BIT BIT(8) #define CLUSTER_IDLE_MASK (CLUSTER_IDLE_BIT | 0x0F) #define AP_SUSPEND_FLAG (1 << 16) #define CLUSTER_PWDN_IDLE (0<<28) #define CLUSTER_PWDN_HOTPLUG (1<<28) #define CLUSTER_PWDN_SR (2<<28) #define CLUSTER0_PDC_OFFSET 0x260 #define CLUSTER1_PDC_OFFSET 0x300 #define PDC_EN_OFFSET 0x0 #define PDC_COREPWRINTEN_OFFSET 0x4 #define PDC_COREPWRINTSTAT_OFFSET 0x8 #define PDC_COREGICMASK_OFFSET 0xc #define PDC_COREPOWERUP_OFFSET 0x10 #define PDC_COREPOWERDN_OFFSET 0x14 #define PDC_COREPOWERSTAT_OFFSET 0x18 #define PDC_COREPWRSTAT_MASK (0XFFFF) enum pdc_gic_mask { PDC_MASK_GIC_WAKE_IRQ, PDC_UNMASK_GIC_WAKE_IRQ }; enum pdc_finish_int_mask { PDC_DISABLE_FINISH_INT, PDC_ENABLE_FINISH_INT }; static void hisi_resource_lock(unsigned int lockid, unsigned int offset) { unsigned int lock_id = (lockid << 29); unsigned int lock_val = lock_id | LOCK_BIT; unsigned int lock_state; do { mmio_write_32(offset, lock_val); lock_state = mmio_read_32(LOCK_STAT_OFFSET + (uintptr_t)offset); } while ((lock_state & LOCK_ID_MASK) != lock_id); } static void hisi_resource_unlock(unsigned int lockid, unsigned int offset) { unsigned int lock_val = (lockid << 29) | LOCK_BIT; mmio_write_32((LOCK_UNLOCK_OFFSET + (uintptr_t)offset), lock_val); } static void hisi_cpuhotplug_lock(unsigned int cluster, unsigned int core) { unsigned int lock_id; lock_id = (cluster << 2) + core; hisi_resource_lock(lock_id, RES2_LOCK_BASE); } static void hisi_cpuhotplug_unlock(unsigned int cluster, unsigned int core) { unsigned int lock_id; lock_id = (cluster << 2) + core; hisi_resource_unlock(lock_id, RES2_LOCK_BASE); } /* get the resource lock */ void hisi_cpuidle_lock(unsigned int cluster, unsigned int core) { unsigned int offset = (cluster == 0 ? RES0_LOCK_BASE : RES1_LOCK_BASE); hisi_resource_lock(CPUIDLE_LOCK_ID(core), offset); } /* release the resource lock */ void hisi_cpuidle_unlock(unsigned int cluster, unsigned int core) { unsigned int offset = (cluster == 0 ? RES0_LOCK_BASE : RES1_LOCK_BASE); hisi_resource_unlock(CPUIDLE_LOCK_ID(core), offset); } unsigned int hisi_get_cpuidle_flag(unsigned int cluster) { unsigned int val; val = mmio_read_32(CPUIDLE_FLAG_REG(cluster)); val &= 0xF; return val; } void hisi_set_cpuidle_flag(unsigned int cluster, unsigned int core) { mmio_setbits_32(CPUIDLE_FLAG_REG(cluster), BIT(core)); } void hisi_clear_cpuidle_flag(unsigned int cluster, unsigned int core) { mmio_clrbits_32(CPUIDLE_FLAG_REG(cluster), BIT(core)); } int hisi_test_ap_suspend_flag(void) { unsigned int val1; unsigned int val2; val1 = mmio_read_32(CPUIDLE_FLAG_REG(0)); val1 &= AP_SUSPEND_FLAG; val2 = mmio_read_32(CPUIDLE_FLAG_REG(1)); val2 &= AP_SUSPEND_FLAG; val1 |= val2; return (val1 != 0); } void hisi_set_cluster_pwdn_flag(unsigned int cluster, unsigned int core, unsigned int value) { unsigned int val; hisi_cpuhotplug_lock(cluster, core); val = mmio_read_32(REG_SCBAKDATA3_OFFSET); val &= ~(0x3U << ((2 * cluster) + 28)); val |= (value << (2 * cluster)); mmio_write_32(REG_SCBAKDATA3_OFFSET, val); hisi_cpuhotplug_unlock(cluster, core); } unsigned int hisi_get_cpu_boot_flag(unsigned int cluster, unsigned int core) { unsigned int val; hisi_cpuhotplug_lock(cluster, core); val = mmio_read_32(REG_SCBAKDATA3_OFFSET); val = val >> (16 + (cluster << 2)); val &= 0xF; hisi_cpuhotplug_unlock(cluster, core); return val; } unsigned int hisi_test_cpu_down(unsigned int cluster, unsigned int core) { unsigned int val; hisi_cpuhotplug_lock(cluster, core); val = mmio_read_32(REG_SCBAKDATA3_OFFSET); val = val >> (16 + (cluster << 2)); val &= 0xF; hisi_cpuhotplug_unlock(cluster, core); if (val) return 0; else return 1; } void hisi_set_cpu_boot_flag(unsigned int cluster, unsigned int core) { unsigned int flag = BIT((cluster<<2) + core + 16); hisi_cpuhotplug_lock(cluster, core); mmio_setbits_32(REG_SCBAKDATA3_OFFSET, flag); hisi_cpuhotplug_unlock(cluster, core); } void hisi_clear_cpu_boot_flag(unsigned int cluster, unsigned int core) { unsigned int flag = BIT((cluster<<2) + core + 16); hisi_cpuhotplug_lock(cluster, core); mmio_clrbits_32(REG_SCBAKDATA3_OFFSET, flag); hisi_cpuhotplug_unlock(cluster, core); } int cluster_is_powered_on(unsigned int cluster) { unsigned int val = mmio_read_32(REG_SCBAKDATA3_OFFSET); int ret; if (cluster == 0) ret = val & CLUSTER0_CPUS_ONLINE_MASK; else ret = val & CLUSTER1_CPUS_ONLINE_MASK; return !!ret; } static void *hisi_get_pdc_addr(unsigned int cluster) { void *pdc_base_addr; uintptr_t addr; if (cluster == 0) addr = SOC_CRGPERIPH_A53_PDCEN_ADDR(CRG_BASE); else addr = SOC_CRGPERIPH_MAIA_PDCEN_ADDR(CRG_BASE); pdc_base_addr = (void *)addr; return pdc_base_addr; } static unsigned int hisi_get_pdc_stat(unsigned int cluster) { void *pdc_base_addr = hisi_get_pdc_addr(cluster); unsigned int val; val = mmio_read_32((uintptr_t)pdc_base_addr + PDC_COREPOWERSTAT_OFFSET); return val; } static int check_hotplug(unsigned int cluster, unsigned int boot_flag) { unsigned int mask = 0xF; if (hisi_test_ap_suspend_flag() || ((boot_flag & mask) == mask)) return 0; return 1; } int hisi_test_pwrdn_allcores(unsigned int cluster, unsigned int core) { unsigned int mask = 0xf << (core * 4); unsigned int pdc_stat = hisi_get_pdc_stat(cluster); unsigned int boot_flag = hisi_get_cpu_boot_flag(cluster, core); unsigned int cpuidle_flag = hisi_get_cpuidle_flag(cluster); mask = (PDC_COREPWRSTAT_MASK & (~mask)); pdc_stat &= mask; if ((boot_flag ^ cpuidle_flag) || pdc_stat || check_hotplug(cluster, boot_flag)) return 0; else return 1; } void hisi_disable_pdc(unsigned int cluster) { void *pdc_base_addr = hisi_get_pdc_addr(cluster); mmio_write_32((uintptr_t)pdc_base_addr, 0x0); } void hisi_enable_pdc(unsigned int cluster) { void *pdc_base_addr = hisi_get_pdc_addr(cluster); mmio_write_32((uintptr_t)pdc_base_addr, 0x1); } void hisi_pdc_set_intmask(void *pdc_base_addr, unsigned int core, enum pdc_finish_int_mask intmask) { unsigned int val; val = mmio_read_32((uintptr_t)pdc_base_addr + PDC_COREPWRINTEN_OFFSET); if (intmask == PDC_ENABLE_FINISH_INT) val |= BIT(core); else val &= ~BIT(core); mmio_write_32((uintptr_t)pdc_base_addr + PDC_COREPWRINTEN_OFFSET, val); } static inline void hisi_pdc_set_gicmask(void *pdc_base_addr, unsigned int core, enum pdc_gic_mask gicmask) { unsigned int val; val = mmio_read_32((uintptr_t)pdc_base_addr + PDC_COREGICMASK_OFFSET); if (gicmask == PDC_MASK_GIC_WAKE_IRQ) val |= BIT(core); else val &= ~BIT(core); mmio_write_32((uintptr_t)pdc_base_addr + PDC_COREGICMASK_OFFSET, val); } void hisi_pdc_mask_cluster_wakeirq(unsigned int cluster) { int i; void *pdc_base_addr = hisi_get_pdc_addr(cluster); for (i = 0; i < 4; i++) hisi_pdc_set_gicmask(pdc_base_addr, i, PDC_MASK_GIC_WAKE_IRQ); } static void hisi_pdc_powerup_core(unsigned int cluster, unsigned int core, enum pdc_gic_mask gicmask, enum pdc_finish_int_mask intmask) { void *pdc_base_addr = hisi_get_pdc_addr(cluster); mmio_write_32((uintptr_t)pdc_base_addr + PDC_COREPOWERUP_OFFSET, BIT(core)); } static void hisi_pdc_powerdn_core(unsigned int cluster, unsigned int core, enum pdc_gic_mask gicmask, enum pdc_finish_int_mask intmask) { void *pdc_base_addr = hisi_get_pdc_addr(cluster); mmio_write_32((uintptr_t)pdc_base_addr + PDC_COREPOWERDN_OFFSET, BIT(core)); } void hisi_powerup_core(unsigned int cluster, unsigned int core) { hisi_pdc_powerup_core(cluster, core, PDC_MASK_GIC_WAKE_IRQ, PDC_DISABLE_FINISH_INT); } void hisi_powerdn_core(unsigned int cluster, unsigned int core) { hisi_pdc_powerdn_core(cluster, core, PDC_MASK_GIC_WAKE_IRQ, PDC_DISABLE_FINISH_INT); } void hisi_powerup_cluster(unsigned int cluster, unsigned int core) { hisi_ipc_pm_on_off(core, cluster, PM_ON); } void hisi_powerdn_cluster(unsigned int cluster, unsigned int core) { void *pdc_base_addr = hisi_get_pdc_addr(cluster); hisi_set_cluster_pwdn_flag(cluster, core, CLUSTER_PWDN_HOTPLUG); mmio_write_32((uintptr_t)pdc_base_addr + PDC_COREPWRINTEN_OFFSET, (0x10001 << core)); mmio_write_32((uintptr_t)pdc_base_addr + PDC_COREPOWERDN_OFFSET, BIT(core)); } void hisi_enter_core_idle(unsigned int cluster, unsigned int core) { hisi_pdc_powerdn_core(cluster, core, PDC_UNMASK_GIC_WAKE_IRQ, PDC_DISABLE_FINISH_INT); } void hisi_enter_cluster_idle(unsigned int cluster, unsigned int core) { void *pdc_base_addr = hisi_get_pdc_addr(cluster); hisi_set_cluster_pwdn_flag(cluster, core, CLUSTER_PWDN_IDLE); mmio_write_32((uintptr_t)pdc_base_addr + PDC_COREPWRINTEN_OFFSET, (0x10001 << core)); mmio_write_32((uintptr_t)pdc_base_addr + PDC_COREPOWERDN_OFFSET, BIT(core)); } void hisi_enter_ap_suspend(unsigned int cluster, unsigned int core) { hisi_ipc_pm_suspend(core, cluster, 0x3); }