1 /* 2 * SMP support for SoCs with SCU covered by mach-shmobile 3 * 4 * Copyright (C) 2013 Magnus Damm 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 */ 10 #include <linux/cpu.h> 11 #include <linux/delay.h> 12 #include <linux/init.h> 13 #include <linux/io.h> 14 #include <linux/smp.h> 15 #include <asm/cacheflush.h> 16 #include <asm/smp_plat.h> 17 #include <asm/smp_scu.h> 18 #include "common.h" 19 20 21 static phys_addr_t shmobile_scu_base_phys; 22 static void __iomem *shmobile_scu_base; 23 24 static int shmobile_smp_scu_notifier_call(struct notifier_block *nfb, 25 unsigned long action, void *hcpu) 26 { 27 unsigned int cpu = (long)hcpu; 28 29 switch (action) { 30 case CPU_UP_PREPARE: 31 /* For this particular CPU register SCU SMP boot vector */ 32 shmobile_smp_hook(cpu, virt_to_phys(shmobile_boot_scu), 33 shmobile_scu_base_phys); 34 break; 35 }; 36 37 return NOTIFY_OK; 38 } 39 40 static struct notifier_block shmobile_smp_scu_notifier = { 41 .notifier_call = shmobile_smp_scu_notifier_call, 42 }; 43 44 void __init shmobile_smp_scu_prepare_cpus(phys_addr_t scu_base_phys, 45 unsigned int max_cpus) 46 { 47 /* install boot code shared by all CPUs */ 48 shmobile_boot_fn = virt_to_phys(shmobile_smp_boot); 49 50 /* enable SCU and cache coherency on booting CPU */ 51 shmobile_scu_base_phys = scu_base_phys; 52 shmobile_scu_base = ioremap(scu_base_phys, PAGE_SIZE); 53 scu_enable(shmobile_scu_base); 54 scu_power_mode(shmobile_scu_base, SCU_PM_NORMAL); 55 56 /* Use CPU notifier for reset vector control */ 57 register_cpu_notifier(&shmobile_smp_scu_notifier); 58 } 59 60 #ifdef CONFIG_HOTPLUG_CPU 61 void shmobile_smp_scu_cpu_die(unsigned int cpu) 62 { 63 /* For this particular CPU deregister boot vector */ 64 shmobile_smp_hook(cpu, 0, 0); 65 66 dsb(); 67 flush_cache_all(); 68 69 /* disable cache coherency */ 70 scu_power_mode(shmobile_scu_base, SCU_PM_POWEROFF); 71 72 /* jump to shared mach-shmobile sleep / reset code */ 73 shmobile_smp_sleep(); 74 } 75 76 static int shmobile_smp_scu_psr_core_disabled(int cpu) 77 { 78 unsigned long mask = SCU_PM_POWEROFF << (cpu * 8); 79 80 if ((__raw_readl(shmobile_scu_base + 8) & mask) == mask) 81 return 1; 82 83 return 0; 84 } 85 86 int shmobile_smp_scu_cpu_kill(unsigned int cpu) 87 { 88 int k; 89 90 /* this function is running on another CPU than the offline target, 91 * here we need wait for shutdown code in platform_cpu_die() to 92 * finish before asking SoC-specific code to power off the CPU core. 93 */ 94 for (k = 0; k < 1000; k++) { 95 if (shmobile_smp_scu_psr_core_disabled(cpu)) 96 return 1; 97 98 mdelay(1); 99 } 100 101 return 0; 102 } 103 #endif 104