Skip to content
Snippets Groups Projects
psci.c 3.51 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * Copyright (C) 2016 Socionext Inc.
     *   Author: Masahiro Yamada <yamada.masahiro@socionext.com>
     *
     * SPDX-License-Identifier:	GPL-2.0+
     */
    
    #include <common.h>
    #include <linux/bitops.h>
    #include <linux/io.h>
    #include <linux/kernel.h>
    #include <linux/psci.h>
    #include <linux/sizes.h>
    #include <asm/processor.h>
    #include <asm/psci.h>
    #include <asm/secure.h>
    
    #include "../debug.h"
    #include "../soc-info.h"
    #include "arm-mpcore.h"
    #include "cache-uniphier.h"
    
    #define UNIPHIER_SMPCTRL_ROM_RSV2	0x59801208
    
    void uniphier_smp_trampoline(void);
    void uniphier_smp_trampoline_end(void);
    u32 uniphier_smp_booted[CONFIG_ARMV7_PSCI_NR_CPUS];
    
    static int uniphier_get_nr_cpus(void)
    {
    	switch (uniphier_get_soc_type()) {
    	case SOC_UNIPHIER_SLD3:
    	case SOC_UNIPHIER_PRO4:
    	case SOC_UNIPHIER_PRO5:
    		return 2;
    	case SOC_UNIPHIER_PXS2:
    	case SOC_UNIPHIER_LD6B:
    		return 4;
    	default:
    		return 1;
    	}
    }
    
    static void uniphier_smp_kick_all_cpus(void)
    {
    	const u32 target_ways = BIT(0);
    	size_t trmp_size;
    	u32 trmp_src = (unsigned long)uniphier_smp_trampoline;
    	u32 trmp_src_end = (unsigned long)uniphier_smp_trampoline_end;
    	u32 trmp_dest, trmp_dest_end;
    	int nr_cpus, i;
    	int timeout = 1000;
    
    	nr_cpus = uniphier_get_nr_cpus();
    	if (nr_cpus == 1)
    		return;
    
    	for (i = 0; i < nr_cpus; i++)	/* lock ways for all CPUs */
    		uniphier_cache_set_active_ways(i, 0);
    	uniphier_cache_inv_way(target_ways);
    	uniphier_cache_enable();
    
    	/* copy trampoline code */
    	uniphier_cache_prefetch_range(trmp_src, trmp_src_end, target_ways);
    
    	trmp_size = trmp_src_end - trmp_src;
    
    	trmp_dest = trmp_src & (SZ_64K - 1);
    	trmp_dest += SZ_1M - SZ_64K * 2;
    
    	trmp_dest_end = trmp_dest + trmp_size;
    
    	uniphier_cache_touch_range(trmp_dest, trmp_dest_end, target_ways);
    
    	writel(trmp_dest, UNIPHIER_SMPCTRL_ROM_RSV2);
    
    	asm("dsb	ishst\n" /* Ensure the write to ROM_RSV2 is visible */
    	    "sev"); /* Bring up all secondary CPUs from Boot ROM into U-Boot */
    
    	while (--timeout) {
    		int all_booted = 1;
    
    		for (i = 1; i < nr_cpus; i++)
    			if (!uniphier_smp_booted[i])
    				all_booted = 0;
    		if (all_booted)
    			break;
    		udelay(1);
    
    		/* barrier here because uniphier_smp_booted[] may be updated */
    		cpu_relax();
    	}
    
    	if (!timeout)
    		printf("warning: some of secondary CPUs may not boot\n");
    
    	uniphier_cache_disable();
    }
    
    void psci_board_init(void)
    {
    	unsigned long scu_base;
    	u32 scu_ctrl, tmp;
    
    	asm("mrc p15, 4, %0, c15, c0, 0" : "=r" (scu_base));
    
    	scu_ctrl = readl(scu_base + 0x30);
    	if (!(scu_ctrl & 1))
    		writel(scu_ctrl | 0x1, scu_base + 0x30);
    
    	scu_ctrl = readl(scu_base + SCU_CTRL);
    	scu_ctrl |= SCU_ENABLE | SCU_STANDBY_ENABLE;
    	writel(scu_ctrl, scu_base + SCU_CTRL);
    
    	tmp = readl(scu_base + SCU_SNSAC);
    	tmp |= 0xfff;
    	writel(tmp, scu_base + SCU_SNSAC);
    
    	uniphier_smp_kick_all_cpus();
    }
    
    void psci_arch_init(void)
    {
    	u32 actlr;
    
    	asm("mrc p15, 0, %0, c1, c0, 1" : "=r" (actlr));
    	actlr |= 0x41;		/* set SMP and FW bits */
    	asm("mcr p15, 0, %0, c1, c0, 1" : : "r" (actlr));
    }
    
    u32 uniphier_psci_holding_pen_release __secure_data = 0xffffffff;
    
    int __secure psci_cpu_on(u32 function_id, u32 cpuid, u32 entry_point)
    {
    	u32 cpu = cpuid & 0xff;
    
    	debug_puts("[U-Boot PSCI]  psci_cpu_on: cpuid=");
    	debug_puth(cpuid);
    	debug_puts(", entry_point=");
    	debug_puth(entry_point);
    	debug_puts("\n");
    
    	psci_save_target_pc(cpu, entry_point);
    
    	/* We assume D-cache is off, so do not call flush_dcache() here */
    	uniphier_psci_holding_pen_release = cpu;
    
    	/* Send an event to wake up the secondary CPU. */
    	asm("dsb	ishst\n"
    	    "sev");
    
    	return PSCI_RET_SUCCESS;
    }
    
    
    void __secure psci_system_reset(u32 function_id)
    {
    	reset_cpu(0);
    }