| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright 2010 Tilera Corporation. 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 | 
					
						
							|  |  |  |  *   as published by the Free Software Foundation, version 2. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *   This program is distributed in the hope that it will be useful, but | 
					
						
							|  |  |  |  *   WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | 
					
						
							|  |  |  |  *   NON INFRINGEMENT.  See the GNU General Public License for | 
					
						
							|  |  |  |  *   more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * TILE SMP support routines. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/smp.h>
 | 
					
						
							| 
									
										
										
										
											2010-06-25 16:41:11 -04:00
										 |  |  | #include <linux/interrupt.h>
 | 
					
						
							|  |  |  | #include <linux/io.h>
 | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | #include <linux/irq.h>
 | 
					
						
							| 
									
										
										
										
											2010-06-25 16:41:11 -04:00
										 |  |  | #include <linux/module.h>
 | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | #include <asm/cacheflush.h>
 | 
					
						
							| 
									
										
										
										
											2013-08-09 15:08:57 -04:00
										 |  |  | #include <asm/homecache.h>
 | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-15 16:29:02 -04:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * We write to width and height with a single store in head_NN.S, | 
					
						
							|  |  |  |  * so make the variable aligned to "long". | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | HV_Topology smp_topology __write_once __aligned(sizeof(long)); | 
					
						
							| 
									
										
										
										
											2010-06-25 16:41:11 -04:00
										 |  |  | EXPORT_SYMBOL(smp_topology); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if CHIP_HAS_IPI()
 | 
					
						
							|  |  |  | static unsigned long __iomem *ipi_mappings[NR_CPUS]; | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Top-level send_IPI*() functions to send messages to other cpus. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Set by smp_send_stop() to avoid recursive panics. */ | 
					
						
							|  |  |  | static int stopping_cpus; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-28 13:32:14 -05:00
										 |  |  | static void __send_IPI_many(HV_Recipient *recip, int nrecip, int tag) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int sent = 0; | 
					
						
							|  |  |  | 	while (sent < nrecip) { | 
					
						
							|  |  |  | 		int rc = hv_send_message(recip, nrecip, | 
					
						
							|  |  |  | 					 (HV_VirtAddr)&tag, sizeof(tag)); | 
					
						
							|  |  |  | 		if (rc < 0) { | 
					
						
							|  |  |  | 			if (!stopping_cpus)  /* avoid recursive panic */ | 
					
						
							|  |  |  | 				panic("hv_send_message returned %d", rc); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		WARN_ONCE(rc == 0, "hv_send_message() returned zero\n"); | 
					
						
							|  |  |  | 		sent += rc; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | void send_IPI_single(int cpu, int tag) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	HV_Recipient recip = { | 
					
						
							|  |  |  | 		.y = cpu / smp_width, | 
					
						
							|  |  |  | 		.x = cpu % smp_width, | 
					
						
							|  |  |  | 		.state = HV_TO_BE_SENT | 
					
						
							|  |  |  | 	}; | 
					
						
							| 
									
										
										
										
											2011-02-28 13:32:14 -05:00
										 |  |  | 	__send_IPI_many(&recip, 1, tag); | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void send_IPI_many(const struct cpumask *mask, int tag) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	HV_Recipient recip[NR_CPUS]; | 
					
						
							| 
									
										
										
										
											2011-02-28 13:32:14 -05:00
										 |  |  | 	int cpu; | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | 	int nrecip = 0; | 
					
						
							|  |  |  | 	int my_cpu = smp_processor_id(); | 
					
						
							|  |  |  | 	for_each_cpu(cpu, mask) { | 
					
						
							|  |  |  | 		HV_Recipient *r; | 
					
						
							|  |  |  | 		BUG_ON(cpu == my_cpu); | 
					
						
							|  |  |  | 		r = &recip[nrecip++]; | 
					
						
							|  |  |  | 		r->y = cpu / smp_width; | 
					
						
							|  |  |  | 		r->x = cpu % smp_width; | 
					
						
							|  |  |  | 		r->state = HV_TO_BE_SENT; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-02-28 13:32:14 -05:00
										 |  |  | 	__send_IPI_many(recip, nrecip, tag); | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void send_IPI_allbutself(int tag) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct cpumask mask; | 
					
						
							|  |  |  | 	cpumask_copy(&mask, cpu_online_mask); | 
					
						
							|  |  |  | 	cpumask_clear_cpu(smp_processor_id(), &mask); | 
					
						
							|  |  |  | 	send_IPI_many(&mask, tag); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Functions related to starting/stopping cpus. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Handler to start the current cpu. */ | 
					
						
							|  |  |  | static void smp_start_cpu_interrupt(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	get_irq_regs()->pc = start_cpu_function_addr; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Handler to stop the current cpu. */ | 
					
						
							|  |  |  | static void smp_stop_cpu_interrupt(void) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2010-11-01 15:24:29 -04:00
										 |  |  | 	arch_local_irq_disable_all(); | 
					
						
							| 
									
										
										
										
											2013-08-07 11:36:54 -04:00
										 |  |  | 	set_cpu_online(smp_processor_id(), 0); | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | 	for (;;) | 
					
						
							| 
									
										
										
										
											2012-03-29 15:57:18 -04:00
										 |  |  | 		asm("nap; nop"); | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* This function calls the 'stop' function on all other CPUs in the system. */ | 
					
						
							|  |  |  | void smp_send_stop(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	stopping_cpus = 1; | 
					
						
							|  |  |  | 	send_IPI_allbutself(MSG_TAG_STOP_CPU); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-29 15:59:11 -04:00
										 |  |  | /* On panic, just wait; we may get an smp_send_stop() later on. */ | 
					
						
							|  |  |  | void panic_smp_self_stop(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	while (1) | 
					
						
							|  |  |  | 		asm("nap; nop"); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Dispatch code called from hv_message_intr() for HV_MSG_TILE hv messages. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void evaluate_message(int tag) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	switch (tag) { | 
					
						
							|  |  |  | 	case MSG_TAG_START_CPU: /* Start up a cpu */ | 
					
						
							|  |  |  | 		smp_start_cpu_interrupt(); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case MSG_TAG_STOP_CPU: /* Sent to shut down slave CPU's */ | 
					
						
							|  |  |  | 		smp_stop_cpu_interrupt(); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case MSG_TAG_CALL_FUNCTION_MANY: /* Call function on cpumask */ | 
					
						
							|  |  |  | 		generic_smp_call_function_interrupt(); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case MSG_TAG_CALL_FUNCTION_SINGLE: /* Call function on one other CPU */ | 
					
						
							|  |  |  | 		generic_smp_call_function_single_interrupt(); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		panic("Unknown IPI message tag %d", tag); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * flush_icache_range() code uses smp_call_function(). | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct ipi_flush { | 
					
						
							|  |  |  | 	unsigned long start; | 
					
						
							|  |  |  | 	unsigned long end; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void ipi_flush_icache_range(void *info) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ipi_flush *flush = (struct ipi_flush *) info; | 
					
						
							|  |  |  | 	__flush_icache_range(flush->start, flush->end); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void flush_icache_range(unsigned long start, unsigned long end) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ipi_flush flush = { start, end }; | 
					
						
							| 
									
										
										
										
											2013-08-09 15:08:57 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* If invoked with irqs disabled, we can not issue IPIs. */ | 
					
						
							|  |  |  | 	if (irqs_disabled()) | 
					
						
							|  |  |  | 		flush_remote(0, HV_FLUSH_EVICT_L1I, NULL, 0, 0, 0, | 
					
						
							|  |  |  | 			NULL, NULL, 0); | 
					
						
							|  |  |  | 	else { | 
					
						
							|  |  |  | 		preempt_disable(); | 
					
						
							|  |  |  | 		on_each_cpu(ipi_flush_icache_range, &flush, 1); | 
					
						
							|  |  |  | 		preempt_enable(); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-25 16:41:11 -04:00
										 |  |  | /* Called when smp_send_reschedule() triggers IRQ_RESCHEDULE. */ | 
					
						
							|  |  |  | static irqreturn_t handle_reschedule_ipi(int irq, void *token) | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | { | 
					
						
							|  |  |  | 	__get_cpu_var(irq_stat).irq_resched_count++; | 
					
						
							| 
									
										
										
										
											2011-04-05 17:23:39 +02:00
										 |  |  | 	scheduler_ipi(); | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return IRQ_HANDLED; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-25 16:41:11 -04:00
										 |  |  | static struct irqaction resched_action = { | 
					
						
							|  |  |  | 	.handler = handle_reschedule_ipi, | 
					
						
							|  |  |  | 	.name = "resched", | 
					
						
							|  |  |  | 	.dev_id = handle_reschedule_ipi /* unique token */, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void __init ipi_init(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | #if CHIP_HAS_IPI()
 | 
					
						
							|  |  |  | 	int cpu; | 
					
						
							|  |  |  | 	/* Map IPI trigger MMIO addresses. */ | 
					
						
							|  |  |  | 	for_each_possible_cpu(cpu) { | 
					
						
							|  |  |  | 		HV_Coord tile; | 
					
						
							|  |  |  | 		HV_PTE pte; | 
					
						
							|  |  |  | 		unsigned long offset; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		tile.x = cpu_x(cpu); | 
					
						
							|  |  |  | 		tile.y = cpu_y(cpu); | 
					
						
							| 
									
										
										
										
											2010-10-14 16:23:03 -04:00
										 |  |  | 		if (hv_get_ipi_pte(tile, KERNEL_PL, &pte) != 0) | 
					
						
							| 
									
										
										
										
											2010-06-25 16:41:11 -04:00
										 |  |  | 			panic("Failed to initialize IPI for cpu %d\n", cpu); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-29 13:58:43 -04:00
										 |  |  | 		offset = PFN_PHYS(pte_pfn(pte)); | 
					
						
							| 
									
										
										
										
											2010-06-25 16:41:11 -04:00
										 |  |  | 		ipi_mappings[cpu] = ioremap_prot(offset, PAGE_SIZE, pte); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Bind handle_reschedule_ipi() to IRQ_RESCHEDULE. */ | 
					
						
							|  |  |  | 	tile_irq_activate(IRQ_RESCHEDULE, TILE_IRQ_PERCPU); | 
					
						
							|  |  |  | 	BUG_ON(setup_irq(IRQ_RESCHEDULE, &resched_action)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if CHIP_HAS_IPI()
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void smp_send_reschedule(int cpu) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	WARN_ON(cpu_is_offline(cpu)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * We just want to do an MMIO store.  The traditional writeq() | 
					
						
							|  |  |  | 	 * functions aren't really correct here, since they're always | 
					
						
							|  |  |  | 	 * directed at the PCI shim.  For now, just do a raw store, | 
					
						
							|  |  |  | 	 * casting away the __iomem attribute. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	((unsigned long __force *)ipi_mappings[cpu])[IRQ_RESCHEDULE] = 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | void smp_send_reschedule(int cpu) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	HV_Coord coord; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	WARN_ON(cpu_is_offline(cpu)); | 
					
						
							| 
									
										
										
										
											2010-06-25 16:41:11 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	coord.y = cpu_y(cpu); | 
					
						
							|  |  |  | 	coord.x = cpu_x(cpu); | 
					
						
							| 
									
										
										
										
											2010-05-28 23:09:12 -04:00
										 |  |  | 	hv_trigger_ipi(coord, IRQ_RESCHEDULE); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2010-06-25 16:41:11 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | #endif /* CHIP_HAS_IPI() */
 |