Linux Audio

Check our new training course

Loading...
v4.17
  1// SPDX-License-Identifier: GPL-2.0
  2// Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
  3//		http://www.samsung.com
  4//
  5// Cloned from linux/arch/arm/mach-vexpress/platsmp.c
  6//
  7//  Copyright (C) 2002 ARM Ltd.
  8//  All Rights Reserved
 
 
 
 
 
 
  9
 10#include <linux/init.h>
 11#include <linux/errno.h>
 12#include <linux/delay.h>
 
 13#include <linux/jiffies.h>
 14#include <linux/smp.h>
 15#include <linux/io.h>
 16#include <linux/of_address.h>
 17#include <linux/soc/samsung/exynos-regs-pmu.h>
 18
 19#include <asm/cacheflush.h>
 20#include <asm/cp15.h>
 21#include <asm/smp_plat.h>
 22#include <asm/smp_scu.h>
 23#include <asm/firmware.h>
 24
 25#include <mach/map.h>
 26
 27#include "common.h"
 
 28
 29extern void exynos4_secondary_startup(void);
 30
 31#ifdef CONFIG_HOTPLUG_CPU
 32static inline void cpu_leave_lowpower(u32 core_id)
 33{
 34	unsigned int v;
 35
 36	asm volatile(
 37	"mrc	p15, 0, %0, c1, c0, 0\n"
 38	"	orr	%0, %0, %1\n"
 39	"	mcr	p15, 0, %0, c1, c0, 0\n"
 40	"	mrc	p15, 0, %0, c1, c0, 1\n"
 41	"	orr	%0, %0, %2\n"
 42	"	mcr	p15, 0, %0, c1, c0, 1\n"
 43	  : "=&r" (v)
 44	  : "Ir" (CR_C), "Ir" (0x40)
 45	  : "cc");
 46}
 47
 48static inline void platform_do_lowpower(unsigned int cpu, int *spurious)
 49{
 50	u32 mpidr = cpu_logical_map(cpu);
 51	u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
 52
 53	for (;;) {
 54
 55		/* Turn the CPU off on next WFI instruction. */
 56		exynos_cpu_power_down(core_id);
 57
 58		wfi();
 59
 60		if (pen_release == core_id) {
 61			/*
 62			 * OK, proper wakeup, we're done
 63			 */
 64			break;
 65		}
 66
 67		/*
 68		 * Getting here, means that we have come out of WFI without
 69		 * having been woken up - this shouldn't happen
 70		 *
 71		 * Just note it happening - when we're woken, we can report
 72		 * its occurrence.
 73		 */
 74		(*spurious)++;
 75	}
 76}
 77#endif /* CONFIG_HOTPLUG_CPU */
 78
 79/**
 80 * exynos_core_power_down : power down the specified cpu
 81 * @cpu : the cpu to power down
 82 *
 83 * Power down the specified cpu. The sequence must be finished by a
 84 * call to cpu_do_idle()
 85 *
 86 */
 87void exynos_cpu_power_down(int cpu)
 88{
 89	u32 core_conf;
 90
 91	if (cpu == 0 && (soc_is_exynos5420() || soc_is_exynos5800())) {
 92		/*
 93		 * Bypass power down for CPU0 during suspend. Check for
 94		 * the SYS_PWR_REG value to decide if we are suspending
 95		 * the system.
 96		 */
 97		int val = pmu_raw_readl(EXYNOS5_ARM_CORE0_SYS_PWR_REG);
 98
 99		if (!(val & S5P_CORE_LOCAL_PWR_EN))
100			return;
101	}
102
103	core_conf = pmu_raw_readl(EXYNOS_ARM_CORE_CONFIGURATION(cpu));
104	core_conf &= ~S5P_CORE_LOCAL_PWR_EN;
105	pmu_raw_writel(core_conf, EXYNOS_ARM_CORE_CONFIGURATION(cpu));
106}
107
108/**
109 * exynos_cpu_power_up : power up the specified cpu
110 * @cpu : the cpu to power up
111 *
112 * Power up the specified cpu
113 */
114void exynos_cpu_power_up(int cpu)
115{
116	u32 core_conf = S5P_CORE_LOCAL_PWR_EN;
117
118	if (soc_is_exynos3250())
119		core_conf |= S5P_CORE_AUTOWAKEUP_EN;
120
121	pmu_raw_writel(core_conf,
122			EXYNOS_ARM_CORE_CONFIGURATION(cpu));
123}
124
125/**
126 * exynos_cpu_power_state : returns the power state of the cpu
127 * @cpu : the cpu to retrieve the power state from
128 *
129 */
130int exynos_cpu_power_state(int cpu)
131{
132	return (pmu_raw_readl(EXYNOS_ARM_CORE_STATUS(cpu)) &
133			S5P_CORE_LOCAL_PWR_EN);
134}
135
136/**
137 * exynos_cluster_power_down : power down the specified cluster
138 * @cluster : the cluster to power down
139 */
140void exynos_cluster_power_down(int cluster)
141{
142	pmu_raw_writel(0, EXYNOS_COMMON_CONFIGURATION(cluster));
143}
144
145/**
146 * exynos_cluster_power_up : power up the specified cluster
147 * @cluster : the cluster to power up
148 */
149void exynos_cluster_power_up(int cluster)
150{
151	pmu_raw_writel(S5P_CORE_LOCAL_PWR_EN,
152			EXYNOS_COMMON_CONFIGURATION(cluster));
153}
154
155/**
156 * exynos_cluster_power_state : returns the power state of the cluster
157 * @cluster : the cluster to retrieve the power state from
158 *
159 */
160int exynos_cluster_power_state(int cluster)
161{
162	return (pmu_raw_readl(EXYNOS_COMMON_STATUS(cluster)) &
163		S5P_CORE_LOCAL_PWR_EN);
164}
165
166static void __iomem *cpu_boot_reg_base(void)
167{
168	if (soc_is_exynos4210() && samsung_rev() == EXYNOS4210_REV_1_1)
169		return pmu_base_addr + S5P_INFORM5;
170	return sysram_base_addr;
171}
172
173static inline void __iomem *cpu_boot_reg(int cpu)
174{
175	void __iomem *boot_reg;
176
177	boot_reg = cpu_boot_reg_base();
178	if (!boot_reg)
179		return IOMEM_ERR_PTR(-ENODEV);
180	if (soc_is_exynos4412())
181		boot_reg += 4*cpu;
182	else if (soc_is_exynos5420() || soc_is_exynos5800())
183		boot_reg += 4;
184	return boot_reg;
185}
186
187/*
188 * Set wake up by local power mode and execute software reset for given core.
189 *
190 * Currently this is needed only when booting secondary CPU on Exynos3250.
191 */
192void exynos_core_restart(u32 core_id)
193{
194	u32 val;
195
196	if (!of_machine_is_compatible("samsung,exynos3250"))
197		return;
198
199	while (!pmu_raw_readl(S5P_PMU_SPARE2))
200		udelay(10);
201	udelay(10);
202
203	val = pmu_raw_readl(EXYNOS_ARM_CORE_STATUS(core_id));
204	val |= S5P_CORE_WAKEUP_FROM_LOCAL_CFG;
205	pmu_raw_writel(val, EXYNOS_ARM_CORE_STATUS(core_id));
206
207	pmu_raw_writel(EXYNOS_CORE_PO_RESET(core_id), EXYNOS_SWRESET);
208}
209
210/*
211 * Write pen_release in a way that is guaranteed to be visible to all
212 * observers, irrespective of whether they're taking part in coherency
213 * or not.  This is necessary for the hotplug code to work reliably.
214 */
215static void write_pen_release(int val)
216{
217	pen_release = val;
218	smp_wmb();
219	sync_cache_w(&pen_release);
220}
221
222static void __iomem *scu_base_addr(void)
223{
224	return (void __iomem *)(S5P_VA_SCU);
225}
226
227static DEFINE_SPINLOCK(boot_lock);
228
229static void exynos_secondary_init(unsigned int cpu)
230{
231	/*
232	 * let the primary processor know we're out of the
233	 * pen, then head off into the C entry point
234	 */
235	write_pen_release(-1);
236
237	/*
238	 * Synchronise with the boot thread.
239	 */
240	spin_lock(&boot_lock);
241	spin_unlock(&boot_lock);
242}
243
244int exynos_set_boot_addr(u32 core_id, unsigned long boot_addr)
245{
246	int ret;
247
248	/*
249	 * Try to set boot address using firmware first
250	 * and fall back to boot register if it fails.
251	 */
252	ret = call_firmware_op(set_cpu_boot_addr, core_id, boot_addr);
253	if (ret && ret != -ENOSYS)
254		goto fail;
255	if (ret == -ENOSYS) {
256		void __iomem *boot_reg = cpu_boot_reg(core_id);
257
258		if (IS_ERR(boot_reg)) {
259			ret = PTR_ERR(boot_reg);
260			goto fail;
261		}
262		writel_relaxed(boot_addr, boot_reg);
263		ret = 0;
264	}
265fail:
266	return ret;
267}
268
269int exynos_get_boot_addr(u32 core_id, unsigned long *boot_addr)
270{
271	int ret;
272
273	/*
274	 * Try to get boot address using firmware first
275	 * and fall back to boot register if it fails.
276	 */
277	ret = call_firmware_op(get_cpu_boot_addr, core_id, boot_addr);
278	if (ret && ret != -ENOSYS)
279		goto fail;
280	if (ret == -ENOSYS) {
281		void __iomem *boot_reg = cpu_boot_reg(core_id);
282
283		if (IS_ERR(boot_reg)) {
284			ret = PTR_ERR(boot_reg);
285			goto fail;
286		}
287		*boot_addr = readl_relaxed(boot_reg);
288		ret = 0;
289	}
290fail:
291	return ret;
292}
293
294static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle)
295{
296	unsigned long timeout;
297	u32 mpidr = cpu_logical_map(cpu);
298	u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
299	int ret = -ENOSYS;
300
301	/*
302	 * Set synchronisation state between this boot processor
303	 * and the secondary one
304	 */
305	spin_lock(&boot_lock);
306
307	/*
308	 * The secondary processor is waiting to be released from
309	 * the holding pen - release it, then wait for it to flag
310	 * that it has been released by resetting pen_release.
311	 *
312	 * Note that "pen_release" is the hardware CPU core ID, whereas
313	 * "cpu" is Linux's internal ID.
314	 */
315	write_pen_release(core_id);
 
 
 
 
316
317	if (!exynos_cpu_power_state(core_id)) {
318		exynos_cpu_power_up(core_id);
319		timeout = 10;
320
321		/* wait max 10 ms until cpu1 is on */
322		while (exynos_cpu_power_state(core_id)
323		       != S5P_CORE_LOCAL_PWR_EN) {
324			if (timeout-- == 0)
325				break;
326
327			mdelay(1);
328		}
329
330		if (timeout == 0) {
331			printk(KERN_ERR "cpu1 power enable failed");
332			spin_unlock(&boot_lock);
333			return -ETIMEDOUT;
334		}
335	}
336
337	exynos_core_restart(core_id);
338
339	/*
340	 * Send the secondary CPU a soft interrupt, thereby causing
341	 * the boot monitor to read the system wide flags register,
342	 * and branch to the address found there.
343	 */
344
345	timeout = jiffies + (1 * HZ);
346	while (time_before(jiffies, timeout)) {
347		unsigned long boot_addr;
348
349		smp_rmb();
350
351		boot_addr = __pa_symbol(exynos4_secondary_startup);
 
 
 
 
 
 
 
 
 
352
353		ret = exynos_set_boot_addr(core_id, boot_addr);
354		if (ret)
355			goto fail;
356
357		call_firmware_op(cpu_boot, core_id);
358
359		if (soc_is_exynos3250())
360			dsb_sev();
361		else
362			arch_send_wakeup_ipi_mask(cpumask_of(cpu));
363
364		if (pen_release == -1)
365			break;
366
367		udelay(10);
368	}
369
370	if (pen_release != -1)
371		ret = -ETIMEDOUT;
372
373	/*
374	 * now the secondary core is starting up let it run its
375	 * calibrations, then wait for it to finish
376	 */
377fail:
378	spin_unlock(&boot_lock);
379
380	return pen_release != -1 ? ret : 0;
381}
382
383static void __init exynos_smp_prepare_cpus(unsigned int max_cpus)
 
 
 
 
 
384{
385	int i;
 
386
387	exynos_sysram_init();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
388
389	exynos_set_delayed_reset_assertion(true);
 
 
 
 
 
 
390
391	if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
392		scu_enable(scu_base_addr());
393
394	/*
395	 * Write the address of secondary startup into the
396	 * system-wide flags register. The boot monitor waits
397	 * until it receives a soft interrupt, and then the
398	 * secondary CPU branches to this address.
399	 *
400	 * Try using firmware operation first and fall back to
401	 * boot register if it fails.
402	 */
403	for (i = 1; i < max_cpus; ++i) {
 
404		unsigned long boot_addr;
405		u32 mpidr;
406		u32 core_id;
407		int ret;
408
409		mpidr = cpu_logical_map(i);
410		core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
411		boot_addr = __pa_symbol(exynos4_secondary_startup);
412
413		ret = exynos_set_boot_addr(core_id, boot_addr);
414		if (ret)
415			break;
416	}
417}
418
419#ifdef CONFIG_HOTPLUG_CPU
420/*
421 * platform-specific code to shutdown a CPU
422 *
423 * Called with IRQs disabled
424 */
425static void exynos_cpu_die(unsigned int cpu)
426{
427	int spurious = 0;
428	u32 mpidr = cpu_logical_map(cpu);
429	u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
430
431	v7_exit_coherency_flush(louis);
432
433	platform_do_lowpower(cpu, &spurious);
434
435	/*
436	 * bring this CPU back into the world of cache
437	 * coherency, and then restore interrupts
438	 */
439	cpu_leave_lowpower(core_id);
440
441	if (spurious)
442		pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious);
443}
444#endif /* CONFIG_HOTPLUG_CPU */
445
446const struct smp_operations exynos_smp_ops __initconst = {
 
447	.smp_prepare_cpus	= exynos_smp_prepare_cpus,
448	.smp_secondary_init	= exynos_secondary_init,
449	.smp_boot_secondary	= exynos_boot_secondary,
450#ifdef CONFIG_HOTPLUG_CPU
451	.cpu_die		= exynos_cpu_die,
452#endif
453};
v3.15
  1/* linux/arch/arm/mach-exynos4/platsmp.c
  2 *
  3 * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
  4 *		http://www.samsung.com
  5 *
  6 * Cloned from linux/arch/arm/mach-vexpress/platsmp.c
  7 *
  8 *  Copyright (C) 2002 ARM Ltd.
  9 *  All Rights Reserved
 10 *
 11 * This program is free software; you can redistribute it and/or modify
 12 * it under the terms of the GNU General Public License version 2 as
 13 * published by the Free Software Foundation.
 14*/
 15
 16#include <linux/init.h>
 17#include <linux/errno.h>
 18#include <linux/delay.h>
 19#include <linux/device.h>
 20#include <linux/jiffies.h>
 21#include <linux/smp.h>
 22#include <linux/io.h>
 
 
 23
 24#include <asm/cacheflush.h>
 
 25#include <asm/smp_plat.h>
 26#include <asm/smp_scu.h>
 27#include <asm/firmware.h>
 28
 29#include <plat/cpu.h>
 30
 31#include "common.h"
 32#include "regs-pmu.h"
 33
 34extern void exynos4_secondary_startup(void);
 35
 36static inline void __iomem *cpu_boot_reg_base(void)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 37{
 38	if (soc_is_exynos4210() && samsung_rev() == EXYNOS4210_REV_1_1)
 39		return S5P_INFORM5;
 40	return S5P_VA_SYSRAM;
 41}
 42
 43static inline void __iomem *cpu_boot_reg(int cpu)
 44{
 45	void __iomem *boot_reg;
 46
 47	boot_reg = cpu_boot_reg_base();
 
 
 48	if (soc_is_exynos4412())
 49		boot_reg += 4*cpu;
 50	else if (soc_is_exynos5420())
 51		boot_reg += 4;
 52	return boot_reg;
 53}
 54
 55/*
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 56 * Write pen_release in a way that is guaranteed to be visible to all
 57 * observers, irrespective of whether they're taking part in coherency
 58 * or not.  This is necessary for the hotplug code to work reliably.
 59 */
 60static void write_pen_release(int val)
 61{
 62	pen_release = val;
 63	smp_wmb();
 64	sync_cache_w(&pen_release);
 65}
 66
 67static void __iomem *scu_base_addr(void)
 68{
 69	return (void __iomem *)(S5P_VA_SCU);
 70}
 71
 72static DEFINE_SPINLOCK(boot_lock);
 73
 74static void exynos_secondary_init(unsigned int cpu)
 75{
 76	/*
 77	 * let the primary processor know we're out of the
 78	 * pen, then head off into the C entry point
 79	 */
 80	write_pen_release(-1);
 81
 82	/*
 83	 * Synchronise with the boot thread.
 84	 */
 85	spin_lock(&boot_lock);
 86	spin_unlock(&boot_lock);
 87}
 88
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 89static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle)
 90{
 91	unsigned long timeout;
 92	unsigned long phys_cpu = cpu_logical_map(cpu);
 
 
 93
 94	/*
 95	 * Set synchronisation state between this boot processor
 96	 * and the secondary one
 97	 */
 98	spin_lock(&boot_lock);
 99
100	/*
101	 * The secondary processor is waiting to be released from
102	 * the holding pen - release it, then wait for it to flag
103	 * that it has been released by resetting pen_release.
104	 *
105	 * Note that "pen_release" is the hardware CPU ID, whereas
106	 * "cpu" is Linux's internal ID.
107	 */
108	write_pen_release(phys_cpu);
109
110	if (!(__raw_readl(S5P_ARM_CORE1_STATUS) & S5P_CORE_LOCAL_PWR_EN)) {
111		__raw_writel(S5P_CORE_LOCAL_PWR_EN,
112			     S5P_ARM_CORE1_CONFIGURATION);
113
 
 
114		timeout = 10;
115
116		/* wait max 10 ms until cpu1 is on */
117		while ((__raw_readl(S5P_ARM_CORE1_STATUS)
118			& S5P_CORE_LOCAL_PWR_EN) != S5P_CORE_LOCAL_PWR_EN) {
119			if (timeout-- == 0)
120				break;
121
122			mdelay(1);
123		}
124
125		if (timeout == 0) {
126			printk(KERN_ERR "cpu1 power enable failed");
127			spin_unlock(&boot_lock);
128			return -ETIMEDOUT;
129		}
130	}
 
 
 
131	/*
132	 * Send the secondary CPU a soft interrupt, thereby causing
133	 * the boot monitor to read the system wide flags register,
134	 * and branch to the address found there.
135	 */
136
137	timeout = jiffies + (1 * HZ);
138	while (time_before(jiffies, timeout)) {
139		unsigned long boot_addr;
140
141		smp_rmb();
142
143		boot_addr = virt_to_phys(exynos4_secondary_startup);
144
145		/*
146		 * Try to set boot address using firmware first
147		 * and fall back to boot register if it fails.
148		 */
149		if (call_firmware_op(set_cpu_boot_addr, phys_cpu, boot_addr))
150			__raw_writel(boot_addr, cpu_boot_reg(phys_cpu));
151
152		call_firmware_op(cpu_boot, phys_cpu);
153
154		arch_send_wakeup_ipi_mask(cpumask_of(cpu));
 
 
 
 
 
 
 
 
 
155
156		if (pen_release == -1)
157			break;
158
159		udelay(10);
160	}
161
 
 
 
162	/*
163	 * now the secondary core is starting up let it run its
164	 * calibrations, then wait for it to finish
165	 */
 
166	spin_unlock(&boot_lock);
167
168	return pen_release != -1 ? -ENOSYS : 0;
169}
170
171/*
172 * Initialise the CPU possible map early - this describes the CPUs
173 * which may be present or become present in the system.
174 */
175
176static void __init exynos_smp_init_cpus(void)
177{
178	void __iomem *scu_base = scu_base_addr();
179	unsigned int i, ncores;
180
181	if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A9)
182		ncores = scu_base ? scu_get_core_count(scu_base) : 1;
183	else
184		/*
185		 * CPU Nodes are passed thru DT and set_cpu_possible
186		 * is set by "arm_dt_init_cpu_maps".
187		 */
188		return;
189
190	/* sanity check */
191	if (ncores > nr_cpu_ids) {
192		pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
193			ncores, nr_cpu_ids);
194		ncores = nr_cpu_ids;
195	}
196
197	for (i = 0; i < ncores; i++)
198		set_cpu_possible(i, true);
199}
200
201static void __init exynos_smp_prepare_cpus(unsigned int max_cpus)
202{
203	int i;
204
205	if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A9)
206		scu_enable(scu_base_addr());
207
208	/*
209	 * Write the address of secondary startup into the
210	 * system-wide flags register. The boot monitor waits
211	 * until it receives a soft interrupt, and then the
212	 * secondary CPU branches to this address.
213	 *
214	 * Try using firmware operation first and fall back to
215	 * boot register if it fails.
216	 */
217	for (i = 1; i < max_cpus; ++i) {
218		unsigned long phys_cpu;
219		unsigned long boot_addr;
 
 
 
 
 
 
 
220
221		phys_cpu = cpu_logical_map(i);
222		boot_addr = virt_to_phys(exynos4_secondary_startup);
 
 
 
223
224		if (call_firmware_op(set_cpu_boot_addr, phys_cpu, boot_addr))
225			__raw_writel(boot_addr, cpu_boot_reg(phys_cpu));
226	}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227}
 
228
229struct smp_operations exynos_smp_ops __initdata = {
230	.smp_init_cpus		= exynos_smp_init_cpus,
231	.smp_prepare_cpus	= exynos_smp_prepare_cpus,
232	.smp_secondary_init	= exynos_secondary_init,
233	.smp_boot_secondary	= exynos_boot_secondary,
234#ifdef CONFIG_HOTPLUG_CPU
235	.cpu_die		= exynos_cpu_die,
236#endif
237};