MIPS: Octeon: Add kexec and kdump support
[ralf@linux-mips.org: Original patch by Maxim Uvarov <muvarov@gmail.com> with plenty of further shining, polishing, debugging and testing by me.] Signed-off-by: Maxim Uvarov <muvarov@gmail.com> Cc: linux-mips@linux-mips.org Cc: kexec@lists.infradead.org Cc: horms@verge.net.au Patchwork: https://patchwork.linux-mips.org/patch/1026/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
This commit is contained in:
		
					parent
					
						
							
								7aa1c8f47e
							
						
					
				
			
			
				commit
				
					
						abe77f90dc
					
				
			
		
					 6 changed files with 335 additions and 9 deletions
				
			
		|  | @ -192,6 +192,10 @@ endif | |||
| #
 | ||||
| include $(srctree)/arch/mips/Kbuild.platforms | ||||
| 
 | ||||
| ifdef CONFIG_PHYSICAL_START | ||||
| load-y                                  = $(CONFIG_PHYSICAL_START) | ||||
| endif | ||||
| 
 | ||||
| cflags-y			+= -I$(srctree)/arch/mips/include/asm/mach-generic | ||||
| drivers-$(CONFIG_PCI)		+= arch/mips/pci/ | ||||
| 
 | ||||
|  |  | |||
|  | @ -688,3 +688,8 @@ int64_t cvmx_bootmem_phy_named_block_alloc(uint64_t size, uint64_t min_addr, | |||
| 		cvmx_spinlock_unlock((cvmx_spinlock_t *)&(cvmx_bootmem_desc->lock)); | ||||
| 	return addr_allocated; | ||||
| } | ||||
| 
 | ||||
| struct cvmx_bootmem_desc *cvmx_bootmem_get_desc(void) | ||||
| { | ||||
| 	return cvmx_bootmem_desc; | ||||
| } | ||||
|  |  | |||
|  | @ -25,6 +25,7 @@ | |||
| #include <linux/serial_8250.h> | ||||
| #include <linux/of_fdt.h> | ||||
| #include <linux/libfdt.h> | ||||
| #include <linux/kexec.h> | ||||
| 
 | ||||
| #include <asm/processor.h> | ||||
| #include <asm/reboot.h> | ||||
|  | @ -58,11 +59,208 @@ struct octeon_boot_descriptor *octeon_boot_desc_ptr; | |||
| struct cvmx_bootinfo *octeon_bootinfo; | ||||
| EXPORT_SYMBOL(octeon_bootinfo); | ||||
| 
 | ||||
| static unsigned long long RESERVE_LOW_MEM = 0ull; | ||||
| #ifdef CONFIG_KEXEC | ||||
| #ifdef CONFIG_SMP | ||||
| /*
 | ||||
|  * Wait for relocation code is prepared and send | ||||
|  * secondary CPUs to spin until kernel is relocated. | ||||
|  */ | ||||
| static void octeon_kexec_smp_down(void *ignored) | ||||
| { | ||||
| 	int cpu = smp_processor_id(); | ||||
| 
 | ||||
| 	local_irq_disable(); | ||||
| 	set_cpu_online(cpu, false); | ||||
| 	while (!atomic_read(&kexec_ready_to_reboot)) | ||||
| 		cpu_relax(); | ||||
| 
 | ||||
| 	asm volatile ( | ||||
| 	"	sync						\n" | ||||
| 	"	synci	($0)					\n"); | ||||
| 
 | ||||
| 	relocated_kexec_smp_wait(NULL); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #define OCTEON_DDR0_BASE    (0x0ULL) | ||||
| #define OCTEON_DDR0_SIZE    (0x010000000ULL) | ||||
| #define OCTEON_DDR1_BASE    (0x410000000ULL) | ||||
| #define OCTEON_DDR1_SIZE    (0x010000000ULL) | ||||
| #define OCTEON_DDR2_BASE    (0x020000000ULL) | ||||
| #define OCTEON_DDR2_SIZE    (0x3e0000000ULL) | ||||
| #define OCTEON_MAX_PHY_MEM_SIZE (16*1024*1024*1024ULL) | ||||
| 
 | ||||
| static struct kimage *kimage_ptr; | ||||
| 
 | ||||
| static void kexec_bootmem_init(uint64_t mem_size, uint32_t low_reserved_bytes) | ||||
| { | ||||
| 	int64_t addr; | ||||
| 	struct cvmx_bootmem_desc *bootmem_desc; | ||||
| 
 | ||||
| 	bootmem_desc = cvmx_bootmem_get_desc(); | ||||
| 
 | ||||
| 	if (mem_size > OCTEON_MAX_PHY_MEM_SIZE) { | ||||
| 		mem_size = OCTEON_MAX_PHY_MEM_SIZE; | ||||
| 		pr_err("Error: requested memory too large," | ||||
| 		       "truncating to maximum size\n"); | ||||
| 	} | ||||
| 
 | ||||
| 	bootmem_desc->major_version = CVMX_BOOTMEM_DESC_MAJ_VER; | ||||
| 	bootmem_desc->minor_version = CVMX_BOOTMEM_DESC_MIN_VER; | ||||
| 
 | ||||
| 	addr = (OCTEON_DDR0_BASE + RESERVE_LOW_MEM + low_reserved_bytes); | ||||
| 	bootmem_desc->head_addr = 0; | ||||
| 
 | ||||
| 	if (mem_size <= OCTEON_DDR0_SIZE) { | ||||
| 		__cvmx_bootmem_phy_free(addr, | ||||
| 				mem_size - RESERVE_LOW_MEM - | ||||
| 				low_reserved_bytes, 0); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	__cvmx_bootmem_phy_free(addr, | ||||
| 			OCTEON_DDR0_SIZE - RESERVE_LOW_MEM - | ||||
| 			low_reserved_bytes, 0); | ||||
| 
 | ||||
| 	mem_size -= OCTEON_DDR0_SIZE; | ||||
| 
 | ||||
| 	if (mem_size > OCTEON_DDR1_SIZE) { | ||||
| 		__cvmx_bootmem_phy_free(OCTEON_DDR1_BASE, OCTEON_DDR1_SIZE, 0); | ||||
| 		__cvmx_bootmem_phy_free(OCTEON_DDR2_BASE, | ||||
| 				mem_size - OCTEON_DDR1_SIZE, 0); | ||||
| 	} else | ||||
| 		__cvmx_bootmem_phy_free(OCTEON_DDR1_BASE, mem_size, 0); | ||||
| } | ||||
| 
 | ||||
| static int octeon_kexec_prepare(struct kimage *image) | ||||
| { | ||||
| 	int i; | ||||
| 	char *bootloader = "kexec"; | ||||
| 
 | ||||
| 	octeon_boot_desc_ptr->argc = 0; | ||||
| 	for (i = 0; i < image->nr_segments; i++) { | ||||
| 		if (!strncmp(bootloader, (char *)image->segment[i].buf, | ||||
| 				strlen(bootloader))) { | ||||
| 			/*
 | ||||
| 			 * convert command line string to array | ||||
| 			 * of parameters (as bootloader does). | ||||
| 			 */ | ||||
| 			int argc = 0, offt; | ||||
| 			char *str = (char *)image->segment[i].buf; | ||||
| 			char *ptr = strchr(str, ' '); | ||||
| 			while (ptr && (OCTEON_ARGV_MAX_ARGS > argc)) { | ||||
| 				*ptr = '\0'; | ||||
| 				if (ptr[1] != ' ') { | ||||
| 					offt = (int)(ptr - str + 1); | ||||
| 					octeon_boot_desc_ptr->argv[argc] = | ||||
| 						image->segment[i].mem + offt; | ||||
| 					argc++; | ||||
| 				} | ||||
| 				ptr = strchr(ptr + 1, ' '); | ||||
| 			} | ||||
| 			octeon_boot_desc_ptr->argc = argc; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Information about segments will be needed during pre-boot memory | ||||
| 	 * initialization. | ||||
| 	 */ | ||||
| 	kimage_ptr = image; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void octeon_generic_shutdown(void) | ||||
| { | ||||
| 	int cpu, i; | ||||
| 	struct cvmx_bootmem_desc *bootmem_desc; | ||||
| 	void *named_block_array_ptr; | ||||
| 
 | ||||
| 	bootmem_desc = cvmx_bootmem_get_desc(); | ||||
| 	named_block_array_ptr = | ||||
| 		cvmx_phys_to_ptr(bootmem_desc->named_block_array_addr); | ||||
| 
 | ||||
| #ifdef CONFIG_SMP | ||||
| 	/* disable watchdogs */ | ||||
| 	for_each_online_cpu(cpu) | ||||
| 		cvmx_write_csr(CVMX_CIU_WDOGX(cpu_logical_map(cpu)), 0); | ||||
| #else | ||||
| 	cvmx_write_csr(CVMX_CIU_WDOGX(cvmx_get_core_num()), 0); | ||||
| #endif | ||||
| 	if (kimage_ptr != kexec_crash_image) { | ||||
| 		memset(named_block_array_ptr, | ||||
| 			0x0, | ||||
| 			CVMX_BOOTMEM_NUM_NAMED_BLOCKS * | ||||
| 			sizeof(struct cvmx_bootmem_named_block_desc)); | ||||
| 		/*
 | ||||
| 		 * Mark all memory (except low 0x100000 bytes) as free. | ||||
| 		 * It is the same thing that bootloader does. | ||||
| 		 */ | ||||
| 		kexec_bootmem_init(octeon_bootinfo->dram_size*1024ULL*1024ULL, | ||||
| 				0x100000); | ||||
| 		/*
 | ||||
| 		 * Allocate all segments to avoid their corruption during boot. | ||||
| 		 */ | ||||
| 		for (i = 0; i < kimage_ptr->nr_segments; i++) | ||||
| 			cvmx_bootmem_alloc_address( | ||||
| 				kimage_ptr->segment[i].memsz + 2*PAGE_SIZE, | ||||
| 				kimage_ptr->segment[i].mem - PAGE_SIZE, | ||||
| 				PAGE_SIZE); | ||||
| 	} else { | ||||
| 		/*
 | ||||
| 		 * Do not mark all memory as free. Free only named sections | ||||
| 		 * leaving the rest of memory unchanged. | ||||
| 		 */ | ||||
| 		struct cvmx_bootmem_named_block_desc *ptr = | ||||
| 			(struct cvmx_bootmem_named_block_desc *) | ||||
| 			named_block_array_ptr; | ||||
| 
 | ||||
| 		for (i = 0; i < bootmem_desc->named_block_num_blocks; i++) | ||||
| 			if (ptr[i].size) | ||||
| 				cvmx_bootmem_free_named(ptr[i].name); | ||||
| 	} | ||||
| 	kexec_args[2] = 1UL; /* running on octeon_main_processor */ | ||||
| 	kexec_args[3] = (unsigned long)octeon_boot_desc_ptr; | ||||
| #ifdef CONFIG_SMP | ||||
| 	secondary_kexec_args[2] = 0UL; /* running on secondary cpu */ | ||||
| 	secondary_kexec_args[3] = (unsigned long)octeon_boot_desc_ptr; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| static void octeon_shutdown(void) | ||||
| { | ||||
| 	octeon_generic_shutdown(); | ||||
| #ifdef CONFIG_SMP | ||||
| 	smp_call_function(octeon_kexec_smp_down, NULL, 0); | ||||
| 	smp_wmb(); | ||||
| 	while (num_online_cpus() > 1) { | ||||
| 		cpu_relax(); | ||||
| 		mdelay(1); | ||||
| 	} | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| static void octeon_crash_shutdown(struct pt_regs *regs) | ||||
| { | ||||
| 	octeon_generic_shutdown(); | ||||
| 	default_machine_crash_shutdown(regs); | ||||
| } | ||||
| 
 | ||||
| #endif /* CONFIG_KEXEC */ | ||||
| 
 | ||||
| #ifdef CONFIG_CAVIUM_RESERVE32 | ||||
| uint64_t octeon_reserve32_memory; | ||||
| EXPORT_SYMBOL(octeon_reserve32_memory); | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONFIG_KEXEC | ||||
| /* crashkernel cmdline parameter is parsed _after_ memory setup
 | ||||
|  * we also parse it here (workaround for EHB5200) */ | ||||
| static uint64_t crashk_size, crashk_base; | ||||
| #endif | ||||
| 
 | ||||
| static int octeon_uart; | ||||
| 
 | ||||
| extern asmlinkage void handle_int(void); | ||||
|  | @ -417,6 +615,8 @@ void octeon_user_io_init(void) | |||
| void __init prom_init(void) | ||||
| { | ||||
| 	struct cvmx_sysinfo *sysinfo; | ||||
| 	const char *arg; | ||||
| 	char *p; | ||||
| 	int i; | ||||
| 	int argc; | ||||
| #ifdef CONFIG_CAVIUM_RESERVE32 | ||||
|  | @ -568,6 +768,15 @@ void __init prom_init(void) | |||
| 	if (octeon_is_simulation()) | ||||
| 		MAX_MEMORY = 64ull << 20; | ||||
| 
 | ||||
| 	arg = strstr(arcs_cmdline, "mem="); | ||||
| 	if (arg) { | ||||
| 		MAX_MEMORY = memparse(arg + 4, &p); | ||||
| 		if (MAX_MEMORY == 0) | ||||
| 			MAX_MEMORY = 32ull << 30; | ||||
| 		if (*p == '@') | ||||
| 			RESERVE_LOW_MEM = memparse(p + 1, &p); | ||||
| 	} | ||||
| 
 | ||||
| 	arcs_cmdline[0] = 0; | ||||
| 	argc = octeon_boot_desc_ptr->argc; | ||||
| 	for (i = 0; i < argc; i++) { | ||||
|  | @ -575,15 +784,29 @@ void __init prom_init(void) | |||
| 			cvmx_phys_to_ptr(octeon_boot_desc_ptr->argv[i]); | ||||
| 		if ((strncmp(arg, "MEM=", 4) == 0) || | ||||
| 		    (strncmp(arg, "mem=", 4) == 0)) { | ||||
| 			sscanf(arg + 4, "%llu", &MAX_MEMORY); | ||||
| 			MAX_MEMORY <<= 20; | ||||
| 			MAX_MEMORY = memparse(arg + 4, &p); | ||||
| 			if (MAX_MEMORY == 0) | ||||
| 				MAX_MEMORY = 32ull << 30; | ||||
| 			if (*p == '@') | ||||
| 				RESERVE_LOW_MEM = memparse(p + 1, &p); | ||||
| 		} else if (strcmp(arg, "ecc_verbose") == 0) { | ||||
| #ifdef CONFIG_CAVIUM_REPORT_SINGLE_BIT_ECC | ||||
| 			__cvmx_interrupt_ecc_report_single_bit_errors = 1; | ||||
| 			pr_notice("Reporting of single bit ECC errors is " | ||||
| 				  "turned on\n"); | ||||
| #endif | ||||
| #ifdef CONFIG_KEXEC | ||||
| 		} else if (strncmp(arg, "crashkernel=", 12) == 0) { | ||||
| 			crashk_size = memparse(arg+12, &p); | ||||
| 			if (*p == '@') | ||||
| 				crashk_base = memparse(p+1, &p); | ||||
| 			strcat(arcs_cmdline, " "); | ||||
| 			strcat(arcs_cmdline, arg); | ||||
| 			/*
 | ||||
| 			 * To do: switch parsing to new style, something like: | ||||
| 			 * parse_crashkernel(arg, sysinfo->system_dram_size, | ||||
| 			 * 		  &crashk_size, &crashk_base); | ||||
| 			 */ | ||||
| #endif | ||||
| 		} else if (strlen(arcs_cmdline) + strlen(arg) + 1 < | ||||
| 			   sizeof(arcs_cmdline) - 1) { | ||||
|  | @ -619,11 +842,18 @@ void __init prom_init(void) | |||
| 	_machine_restart = octeon_restart; | ||||
| 	_machine_halt = octeon_halt; | ||||
| 
 | ||||
| #ifdef CONFIG_KEXEC | ||||
| 	_machine_kexec_shutdown = octeon_shutdown; | ||||
| 	_machine_crash_shutdown = octeon_crash_shutdown; | ||||
| 	_machine_kexec_prepare = octeon_kexec_prepare; | ||||
| #endif | ||||
| 
 | ||||
| 	octeon_user_io_init(); | ||||
| 	register_smp_ops(&octeon_smp_ops); | ||||
| } | ||||
| 
 | ||||
| /* Exclude a single page from the regions obtained in plat_mem_setup. */ | ||||
| #ifndef CONFIG_CRASH_DUMP | ||||
| static __init void memory_exclude_page(u64 addr, u64 *mem, u64 *size) | ||||
| { | ||||
| 	if (addr > *mem && addr < *mem + *size) { | ||||
|  | @ -638,14 +868,21 @@ static __init void memory_exclude_page(u64 addr, u64 *mem, u64 *size) | |||
| 		*size -= PAGE_SIZE; | ||||
| 	} | ||||
| } | ||||
| #endif /* CONFIG_CRASH_DUMP */ | ||||
| 
 | ||||
| void __init plat_mem_setup(void) | ||||
| { | ||||
| 	uint64_t mem_alloc_size; | ||||
| 	uint64_t total; | ||||
| 	uint64_t crashk_end; | ||||
| #ifndef CONFIG_CRASH_DUMP | ||||
| 	int64_t memory; | ||||
| 	uint64_t kernel_start; | ||||
| 	uint64_t kernel_size; | ||||
| #endif | ||||
| 
 | ||||
| 	total = 0; | ||||
| 	crashk_end = 0; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * The Mips memory init uses the first memory location for | ||||
|  | @ -658,6 +895,17 @@ void __init plat_mem_setup(void) | |||
| 	if (mem_alloc_size > MAX_MEMORY) | ||||
| 		mem_alloc_size = MAX_MEMORY; | ||||
| 
 | ||||
| /* Crashkernel ignores bootmem list. It relies on mem=X@Y option */ | ||||
| #ifdef CONFIG_CRASH_DUMP | ||||
| 	add_memory_region(RESERVE_LOW_MEM, MAX_MEMORY, BOOT_MEM_RAM); | ||||
| 	total += MAX_MEMORY; | ||||
| #else | ||||
| #ifdef CONFIG_KEXEC | ||||
| 	if (crashk_size > 0) { | ||||
| 		add_memory_region(crashk_base, crashk_size, BOOT_MEM_RAM); | ||||
| 		crashk_end = crashk_base + crashk_size; | ||||
| 	} | ||||
| #endif | ||||
| 	/*
 | ||||
| 	 * When allocating memory, we want incrementing addresses from | ||||
| 	 * bootmem_alloc so the code in add_memory_region can merge | ||||
|  | @ -672,6 +920,9 @@ void __init plat_mem_setup(void) | |||
| 						CVMX_BOOTMEM_FLAG_NO_LOCKING); | ||||
| 		if (memory >= 0) { | ||||
| 			u64 size = mem_alloc_size; | ||||
| #ifdef CONFIG_KEXEC | ||||
| 			uint64_t end; | ||||
| #endif | ||||
| 
 | ||||
| 			/*
 | ||||
| 			 * exclude a page at the beginning and end of | ||||
|  | @ -684,20 +935,67 @@ void __init plat_mem_setup(void) | |||
| 			memory_exclude_page(CVMX_PCIE_BAR1_PHYS_BASE + | ||||
| 					    CVMX_PCIE_BAR1_PHYS_SIZE, | ||||
| 					    &memory, &size); | ||||
| #ifdef CONFIG_KEXEC | ||||
| 			end = memory + mem_alloc_size; | ||||
| 
 | ||||
| 			/*
 | ||||
| 			 * This function automatically merges address | ||||
| 			 * regions next to each other if they are | ||||
| 			 * received in incrementing order. | ||||
| 			 * This function automatically merges address regions | ||||
| 			 * next to each other if they are received in | ||||
| 			 * incrementing order | ||||
| 			 */ | ||||
| 			if (size) | ||||
| 				add_memory_region(memory, size, BOOT_MEM_RAM); | ||||
| 			if (memory < crashk_base && end >  crashk_end) { | ||||
| 				/* region is fully in */ | ||||
| 				add_memory_region(memory, | ||||
| 						  crashk_base - memory, | ||||
| 						  BOOT_MEM_RAM); | ||||
| 				total += crashk_base - memory; | ||||
| 				add_memory_region(crashk_end, | ||||
| 						  end - crashk_end, | ||||
| 						  BOOT_MEM_RAM); | ||||
| 				total += end - crashk_end; | ||||
| 				continue; | ||||
| 			} | ||||
| 
 | ||||
| 			if (memory >= crashk_base && end <= crashk_end) | ||||
| 				/*
 | ||||
| 				 * Entire memory region is within the new | ||||
| 				 *  kernel's memory, ignore it. | ||||
| 				 */ | ||||
| 				continue; | ||||
| 
 | ||||
| 			if (memory > crashk_base && memory < crashk_end && | ||||
| 			    end > crashk_end) { | ||||
| 				/*
 | ||||
| 				 * Overlap with the beginning of the region, | ||||
| 				 * reserve the beginning. | ||||
| 				  */ | ||||
| 				mem_alloc_size -= crashk_end - memory; | ||||
| 				memory = crashk_end; | ||||
| 			} else if (memory < crashk_base && end > crashk_base && | ||||
| 				   end < crashk_end) | ||||
| 				/*
 | ||||
| 				 * Overlap with the beginning of the region, | ||||
| 				 * chop of end. | ||||
| 				 */ | ||||
| 				mem_alloc_size -= end - crashk_base; | ||||
| #endif | ||||
| 			add_memory_region(memory, mem_alloc_size, BOOT_MEM_RAM); | ||||
| 			total += mem_alloc_size; | ||||
| 			/* Recovering mem_alloc_size */ | ||||
| 			mem_alloc_size = 4 << 20; | ||||
| 		} else { | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	cvmx_bootmem_unlock(); | ||||
| 	/* Add the memory region for the kernel. */ | ||||
| 	kernel_start = (unsigned long) _text; | ||||
| 	kernel_size = ALIGN(_end - _text, 0x100000); | ||||
| 
 | ||||
| 	/* Adjust for physical offset. */ | ||||
| 	kernel_start &= ~0xffffffff80000000ULL; | ||||
| 	add_memory_region(kernel_start, kernel_size, BOOT_MEM_RAM); | ||||
| #endif /* CONFIG_CRASH_DUMP */ | ||||
| 
 | ||||
| #ifdef CONFIG_CAVIUM_RESERVE32 | ||||
| 	/*
 | ||||
|  |  | |||
|  | @ -370,4 +370,6 @@ void cvmx_bootmem_lock(void); | |||
|  */ | ||||
| void cvmx_bootmem_unlock(void); | ||||
| 
 | ||||
| extern struct cvmx_bootmem_desc *cvmx_bootmem_get_desc(void); | ||||
| 
 | ||||
| #endif /*   __CVMX_BOOTMEM_H__ */ | ||||
|  |  | |||
|  | @ -3,8 +3,6 @@ | |||
| #include <linux/crash_dump.h> | ||||
| #include <asm/uaccess.h> | ||||
| 
 | ||||
| unsigned long long elfcorehdr_addr = ELFCORE_ADDR_MAX; | ||||
| 
 | ||||
| static int __init parse_savemaxmem(char *p) | ||||
| { | ||||
| 	if (p) | ||||
|  |  | |||
|  | @ -78,7 +78,19 @@ done: | |||
| 	LONG_S		zero,(t0) | ||||
| #endif | ||||
| 
 | ||||
| #ifdef CONFIG_CPU_CAVIUM_OCTEON | ||||
| 	/* We need to flush I-cache before jumping to new kernel. | ||||
| 	 * Unfortunatelly, this code is cpu-specific. | ||||
| 	 */ | ||||
| 	.set push
 | ||||
| 	.set noreorder
 | ||||
| 	syncw | ||||
| 	syncw | ||||
| 	synci		0($0) | ||||
| 	.set pop
 | ||||
| #else | ||||
| 	sync | ||||
| #endif | ||||
| 	/* jump to kexec_start_address */ | ||||
| 	j		s1 | ||||
| 	END(relocate_new_kernel) | ||||
|  | @ -110,7 +122,14 @@ LEAF(kexec_smp_wait) | |||
| 1:	LONG_L		s0, (t0) | ||||
| 	bne		s0, zero,1b | ||||
| 
 | ||||
| #ifdef CONFIG_CPU_CAVIUM_OCTEON | ||||
| 	.set push
 | ||||
| 	.set noreorder
 | ||||
| 	synci		0($0) | ||||
| 	.set pop
 | ||||
| #else | ||||
| 	sync | ||||
| #endif | ||||
| 	j		s1 | ||||
| 	END(kexec_smp_wait) | ||||
| #endif | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Ralf Baechle
				Ralf Baechle