| 
									
										
										
										
											2010-02-21 17:46:23 -08:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  *  linux/arch/arm/mach-tegra/platsmp.c | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  Copyright (C) 2002 ARM Ltd. | 
					
						
							|  |  |  |  *  All Rights Reserved | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  Copyright (C) 2009 Palm | 
					
						
							|  |  |  |  *  All Rights Reserved | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or modify | 
					
						
							|  |  |  |  * it under the terms of the GNU General Public License version 2 as | 
					
						
							|  |  |  |  * published by the Free Software Foundation. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #include <linux/init.h>
 | 
					
						
							|  |  |  | #include <linux/errno.h>
 | 
					
						
							|  |  |  | #include <linux/delay.h>
 | 
					
						
							|  |  |  | #include <linux/device.h>
 | 
					
						
							|  |  |  | #include <linux/jiffies.h>
 | 
					
						
							|  |  |  | #include <linux/smp.h>
 | 
					
						
							|  |  |  | #include <linux/io.h>
 | 
					
						
							| 
									
										
										
										
											2013-01-11 13:16:21 +05:30
										 |  |  | #include <linux/clk/tegra.h>
 | 
					
						
							| 
									
										
										
										
											2010-02-21 17:46:23 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include <asm/cacheflush.h>
 | 
					
						
							|  |  |  | #include <asm/mach-types.h>
 | 
					
						
							|  |  |  | #include <asm/smp_scu.h>
 | 
					
						
							| 
									
										
										
										
											2013-01-03 15:31:31 +08:00
										 |  |  | #include <asm/smp_plat.h>
 | 
					
						
							| 
									
										
										
										
											2010-02-21 17:46:23 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-02-10 01:47:45 +02:00
										 |  |  | #include "fuse.h"
 | 
					
						
							|  |  |  | #include "flowctrl.h"
 | 
					
						
							|  |  |  | #include "reset.h"
 | 
					
						
							| 
									
										
										
										
											2013-02-26 16:28:06 +00:00
										 |  |  | #include "pmc.h"
 | 
					
						
							| 
									
										
										
										
											2012-02-10 01:47:45 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-09-08 13:15:22 +01:00
										 |  |  | #include "common.h"
 | 
					
						
							| 
									
										
										
										
											2012-10-04 14:24:09 -06:00
										 |  |  | #include "iomap.h"
 | 
					
						
							| 
									
										
										
										
											2011-09-08 13:15:22 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-03 15:31:31 +08:00
										 |  |  | static cpumask_t tegra_cpu_init_mask; | 
					
						
							| 
									
										
										
										
											2010-02-21 17:46:23 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-17 15:43:14 -04:00
										 |  |  | static void tegra_secondary_init(unsigned int cpu) | 
					
						
							| 
									
										
										
										
											2010-02-21 17:46:23 -08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-03 15:31:31 +08:00
										 |  |  | 	cpumask_set_cpu(cpu, &tegra_cpu_init_mask); | 
					
						
							| 
									
										
										
										
											2010-02-21 17:46:23 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-22 14:24:27 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | static int tegra20_boot_secondary(unsigned int cpu, struct task_struct *idle) | 
					
						
							| 
									
										
										
										
											2010-02-21 17:46:23 -08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-02-22 14:24:27 +08:00
										 |  |  | 	cpu = cpu_logical_map(cpu); | 
					
						
							| 
									
										
										
										
											2010-02-21 17:46:23 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-22 14:24:27 +08:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Force the CPU into reset. The CPU must remain in reset when | 
					
						
							|  |  |  | 	 * the flow controller state is cleared (which will cause the | 
					
						
							|  |  |  | 	 * flow controller to stop driving reset if the CPU has been | 
					
						
							|  |  |  | 	 * power-gated via the flow controller). This will have no | 
					
						
							|  |  |  | 	 * effect on first boot of the CPU since it should already be | 
					
						
							|  |  |  | 	 * in reset. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	tegra_put_cpu_in_reset(cpu); | 
					
						
							| 
									
										
										
										
											2010-02-21 17:46:23 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-22 14:24:27 +08:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Unhalt the CPU. If the flow controller was used to | 
					
						
							|  |  |  | 	 * power-gate the CPU this will cause the flow controller to | 
					
						
							|  |  |  | 	 * stop driving reset. The CPU will remain in reset because the | 
					
						
							|  |  |  | 	 * clock and reset block is now driving reset. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	flowctrl_write_cpu_halt(cpu, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	tegra_enable_cpu_clock(cpu); | 
					
						
							|  |  |  | 	flowctrl_write_cpu_csr(cpu, 0); /* Clear flow controller CSR. */ | 
					
						
							|  |  |  | 	tegra_cpu_out_of_reset(cpu); | 
					
						
							| 
									
										
										
										
											2012-02-10 01:47:45 +02:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2010-02-21 17:46:23 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-22 14:24:27 +08:00
										 |  |  | static int tegra30_boot_secondary(unsigned int cpu, struct task_struct *idle) | 
					
						
							| 
									
										
										
										
											2012-02-10 01:47:50 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-02-26 16:28:06 +00:00
										 |  |  | 	int ret; | 
					
						
							| 
									
										
										
										
											2012-02-10 01:47:50 +02:00
										 |  |  | 	unsigned long timeout; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-22 14:24:27 +08:00
										 |  |  | 	cpu = cpu_logical_map(cpu); | 
					
						
							|  |  |  | 	tegra_put_cpu_in_reset(cpu); | 
					
						
							|  |  |  | 	flowctrl_write_cpu_halt(cpu, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-03 15:31:31 +08:00
										 |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * The power up sequence of cold boot CPU and warm boot CPU | 
					
						
							|  |  |  | 	 * was different. | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * For warm boot CPU that was resumed from CPU hotplug, the | 
					
						
							|  |  |  | 	 * power will be resumed automatically after un-halting the | 
					
						
							|  |  |  | 	 * flow controller of the warm boot CPU. We need to wait for | 
					
						
							|  |  |  | 	 * the confirmaiton that the CPU is powered then removing | 
					
						
							|  |  |  | 	 * the IO clamps. | 
					
						
							|  |  |  | 	 * For cold boot CPU, do not wait. After the cold boot CPU be | 
					
						
							|  |  |  | 	 * booted, it will run to tegra_secondary_init() and set | 
					
						
							| 
									
										
										
										
											2013-02-22 14:24:27 +08:00
										 |  |  | 	 * tegra_cpu_init_mask which influences what tegra30_boot_secondary() | 
					
						
							| 
									
										
										
										
											2013-01-03 15:31:31 +08:00
										 |  |  | 	 * next time around. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (cpumask_test_cpu(cpu, &tegra_cpu_init_mask)) { | 
					
						
							| 
									
										
										
										
											2013-01-07 10:56:14 +08:00
										 |  |  | 		timeout = jiffies + msecs_to_jiffies(50); | 
					
						
							| 
									
										
										
										
											2013-01-03 15:31:31 +08:00
										 |  |  | 		do { | 
					
						
							| 
									
										
										
										
											2013-02-26 16:28:06 +00:00
										 |  |  | 			if (tegra_pmc_cpu_is_powered(cpu)) | 
					
						
							| 
									
										
										
										
											2013-01-03 15:31:31 +08:00
										 |  |  | 				goto remove_clamps; | 
					
						
							|  |  |  | 			udelay(10); | 
					
						
							|  |  |  | 		} while (time_before(jiffies, timeout)); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * The power status of the cold boot CPU is power gated as | 
					
						
							|  |  |  | 	 * default. To power up the cold boot CPU, the power should | 
					
						
							|  |  |  | 	 * be un-gated by un-toggling the power gate register | 
					
						
							|  |  |  | 	 * manually. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2013-02-26 16:28:06 +00:00
										 |  |  | 	if (!tegra_pmc_cpu_is_powered(cpu)) { | 
					
						
							|  |  |  | 		ret = tegra_pmc_cpu_power_on(cpu); | 
					
						
							| 
									
										
										
										
											2012-02-10 01:47:50 +02:00
										 |  |  | 		if (ret) | 
					
						
							|  |  |  | 			return ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Wait for the power to come up. */ | 
					
						
							| 
									
										
										
										
											2013-01-07 10:56:14 +08:00
										 |  |  | 		timeout = jiffies + msecs_to_jiffies(100); | 
					
						
							| 
									
										
										
										
											2013-02-26 16:28:06 +00:00
										 |  |  | 		while (tegra_pmc_cpu_is_powered(cpu)) { | 
					
						
							| 
									
										
										
										
											2012-02-10 01:47:50 +02:00
										 |  |  | 			if (time_after(jiffies, timeout)) | 
					
						
							|  |  |  | 				return -ETIMEDOUT; | 
					
						
							|  |  |  | 			udelay(10); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-03 15:31:31 +08:00
										 |  |  | remove_clamps: | 
					
						
							| 
									
										
										
										
											2012-02-10 01:47:50 +02:00
										 |  |  | 	/* CPU partition is powered. Enable the CPU clock. */ | 
					
						
							| 
									
										
										
										
											2012-08-16 17:31:49 +08:00
										 |  |  | 	tegra_enable_cpu_clock(cpu); | 
					
						
							| 
									
										
										
										
											2012-02-10 01:47:50 +02:00
										 |  |  | 	udelay(10); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Remove I/O clamps. */ | 
					
						
							| 
									
										
										
										
											2013-02-26 16:28:06 +00:00
										 |  |  | 	ret = tegra_pmc_cpu_remove_clamping(cpu); | 
					
						
							| 
									
										
										
										
											2013-02-22 14:24:25 +08:00
										 |  |  | 	if (ret) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-02-10 01:47:50 +02:00
										 |  |  | 	udelay(10); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-22 14:24:27 +08:00
										 |  |  | 	flowctrl_write_cpu_csr(cpu, 0); /* Clear flow controller CSR. */ | 
					
						
							|  |  |  | 	tegra_cpu_out_of_reset(cpu); | 
					
						
							| 
									
										
										
										
											2012-02-10 01:47:50 +02:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-26 16:28:07 +00:00
										 |  |  | static int tegra114_boot_secondary(unsigned int cpu, struct task_struct *idle) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-05-20 18:39:27 +08:00
										 |  |  | 	int ret = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-26 16:28:07 +00:00
										 |  |  | 	cpu = cpu_logical_map(cpu); | 
					
						
							| 
									
										
										
										
											2013-05-20 18:39:27 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (cpumask_test_cpu(cpu, &tegra_cpu_init_mask)) { | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Warm boot flow | 
					
						
							|  |  |  | 		 * The flow controller in charge of the power state and | 
					
						
							|  |  |  | 		 * control for each CPU. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		/* set SCLK as event trigger for flow controller */ | 
					
						
							|  |  |  | 		flowctrl_write_cpu_csr(cpu, 1); | 
					
						
							|  |  |  | 		flowctrl_write_cpu_halt(cpu, | 
					
						
							|  |  |  | 				FLOW_CTRL_WAITEVENT | FLOW_CTRL_SCLK_RESUME); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Cold boot flow | 
					
						
							|  |  |  | 		 * The CPU is powered up by toggling PMC directly. It will | 
					
						
							|  |  |  | 		 * also initial power state in flow controller. After that, | 
					
						
							|  |  |  | 		 * the CPU's power state is maintained by flow controller. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		ret = tegra_pmc_cpu_power_on(cpu); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ret; | 
					
						
							| 
									
										
										
										
											2013-02-26 16:28:07 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-17 15:43:14 -04:00
										 |  |  | static int tegra_boot_secondary(unsigned int cpu, | 
					
						
							| 
									
										
										
										
											2013-02-22 14:24:27 +08:00
										 |  |  | 					  struct task_struct *idle) | 
					
						
							| 
									
										
										
										
											2012-02-10 01:47:45 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-02-22 14:24:27 +08:00
										 |  |  | 	if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) && tegra_chip_id == TEGRA20) | 
					
						
							|  |  |  | 		return tegra20_boot_secondary(cpu, idle); | 
					
						
							|  |  |  | 	if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) && tegra_chip_id == TEGRA30) | 
					
						
							|  |  |  | 		return tegra30_boot_secondary(cpu, idle); | 
					
						
							| 
									
										
										
										
											2013-02-26 16:28:07 +00:00
										 |  |  | 	if (IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC) && tegra_chip_id == TEGRA114) | 
					
						
							|  |  |  | 		return tegra114_boot_secondary(cpu, idle); | 
					
						
							| 
									
										
										
										
											2010-02-21 17:46:23 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-22 14:24:27 +08:00
										 |  |  | 	return -EINVAL; | 
					
						
							| 
									
										
										
										
											2010-02-21 17:46:23 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-09-08 13:15:22 +01:00
										 |  |  | static void __init tegra_smp_prepare_cpus(unsigned int max_cpus) | 
					
						
							| 
									
										
										
										
											2010-02-21 17:46:23 -08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-03 15:31:31 +08:00
										 |  |  | 	/* Always mark the boot CPU (CPU0) as initialized. */ | 
					
						
							|  |  |  | 	cpumask_set_cpu(0, &tegra_cpu_init_mask); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-22 07:52:02 +02:00
										 |  |  | 	if (scu_a9_has_base()) | 
					
						
							|  |  |  | 		scu_enable(IO_ADDRESS(scu_a9_get_base())); | 
					
						
							| 
									
										
										
										
											2010-02-21 17:46:23 -08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2011-09-08 13:15:22 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | struct smp_operations tegra_smp_ops __initdata = { | 
					
						
							|  |  |  | 	.smp_prepare_cpus	= tegra_smp_prepare_cpus, | 
					
						
							|  |  |  | 	.smp_secondary_init	= tegra_secondary_init, | 
					
						
							|  |  |  | 	.smp_boot_secondary	= tegra_boot_secondary, | 
					
						
							|  |  |  | #ifdef CONFIG_HOTPLUG_CPU
 | 
					
						
							| 
									
										
										
										
											2013-01-03 14:43:00 +08:00
										 |  |  | 	.cpu_kill		= tegra_cpu_kill, | 
					
						
							| 
									
										
										
										
											2011-09-08 13:15:22 +01:00
										 |  |  | 	.cpu_die		= tegra_cpu_die, | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | }; |