#if defined(CONFIG_BCM_KF_ARM_BCM963XX) /* <:copyright-BRCM:2013:DUAL/GPL:standard Copyright (c) 2013 Broadcom All Rights Reserved Unless you and Broadcom execute a separate written software license agreement governing use of this software, this software is licensed to you under the terms of the GNU General Public License version 2 (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, with the following added to such license: As a special exception, the copyright holders of this software give you permission to link this software with independent modules, and to copy and distribute the resulting executable under terms of your choice, provided that you also meet, for each linked independent module, the terms and conditions of the license of that module. An independent module is a module which is not derived from this software. The special exception does not apply to any modifications of the software. Not withstanding the above, under no circumstances may you combine this software in any way with any other Broadcom software provided under a license other than the GPL, without Broadcom's express prior written consent. :> */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_PLAT_CA9_MPCORE #include #endif #include #include #include #include static DEFINE_RAW_SPINLOCK(boot_lock); /* write pen_release in a way that is guaranteed to be visible to all * observers, irrespective of whatever they'are taking part in coherency * or not. This is necessary for the hotplug code to work reliably. */ static void __cpuinit write_pen_release(int val) { pen_release = val; smp_wmb(); sync_cache_w(&pen_release); } static void __cpuinit platform_secondary_init(unsigned int cpu) { trace_hardirqs_off(); /* * let the primary processor know we're out of the * pen, then head off into the C entry point */ write_pen_release(-1); /* * Synchronise with the boot thread. */ raw_spin_lock(&boot_lock); raw_spin_unlock(&boot_lock); } static int __cpuinit platform_boot_secondary(unsigned int cpu, struct task_struct *idle) { unsigned long timeout; /* * set synchronisation state between this boot processor * and the secondary one */ raw_spin_lock(&boot_lock); /* * The secondary processor is waiting to be released from * the holding pen - release it, then wait for it to flag * that it has been released by resetting pen_release. * * Note that "pen_release" is the hardware CPU ID, whereas * "cpu" is Linux's internal ID. */ write_pen_release(cpu_logical_map(cpu)); /* * Send the secondary CPU a soft interrupt, thereby causing * the boot monitor to read the system wide flags register, * and branch to the address found there. */ arch_send_wakeup_ipi_mask(cpumask_of(cpu)); timeout = jiffies + (1 * HZ); while (time_before(jiffies, timeout)) { smp_rmb(); if (pen_release == -1) break; udelay(10); } /* * now the secondary core is starting up let it run its * calibrations, then wait for it to finish */ raw_spin_unlock(&boot_lock); return pen_release != -1 ? -ENOSYS : 0; } #ifdef CONFIG_PLAT_CA9_MPCORE static void __init platform_smp_init_cpus(void) { void __iomem *scu_base = scu_base_addr(); unsigned int i, ncores; ncores = scu_base ? scu_get_core_count(scu_base) : 1; /* sanity check */ if (ncores > nr_cpu_ids) { pr_warn("SMP: %u cores greater than maximum (%u), clipping\n", ncores, nr_cpu_ids); ncores = nr_cpu_ids; } for (i = 0; i < ncores; i++) set_cpu_possible(i, true); } #endif #ifdef CONFIG_BCM963138 extern int pmc_cpu_core_power_up(unsigned cpu); /* * Wakeup secondary core * This is SoC-specific code used by the platform SMP code. */ void plat_wake_secondary_cpu(unsigned cpu, void (*_sec_entry_va)(void)) { void __iomem *bootlut_base = __io_address(BOOTLUT_PHYS_BASE); u32 val; /* 1) covert the virtual starting address into physical, then * write it to boot look-up table. */ val = virt_to_phys(_sec_entry_va); __raw_writel(val, bootlut_base + 0x20); /* 2) power up the 2nd core here */ pmc_cpu_core_power_up(1); } #endif static void __init platform_smp_prepare_cpus(unsigned int max_cpus) { /* * Initialise the SCU and wake up the secondary core using * wakeup_secondary(). */ #ifdef CONFIG_PLAT_CA9_MPCORE scu_enable(scu_base_addr()); #endif #ifdef CONFIG_BCM963138 plat_wake_secondary_cpu(max_cpus, platform_secondary_startup); #else /* 1) covert the virtual starting address into physical, then * write it to register for the bootloader to read from */ GPIO->GeneralPurpose = virt_to_phys(platform_secondary_startup); #endif } struct smp_operations bcm_smp_ops __initdata = { #ifdef CONFIG_PLAT_CA9_MPCORE .smp_init_cpus = platform_smp_init_cpus, #endif .smp_prepare_cpus = platform_smp_prepare_cpus, .smp_secondary_init = platform_secondary_init, .smp_boot_secondary = platform_boot_secondary, #ifdef CONFIG_HOTPLUG_CPU .cpu_die = platform_cpu_die, #endif }; CPU_METHOD_OF_DECLARE(bcm_smp, "brcm,bca-smp", &bcm_smp_ops); #endif /* CONFIG_BCM_KF_ARM_BCM963XX */