| 
									
										
										
										
											2013-01-18 15:12:23 +05:30
										 |  |  | /*
 | 
					
						
							|  |  |  |  * ARC700 Simulation-only Extensions for SMP | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * 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. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  Vineet Gupta    - 2012 : split off arch common and plat specific SMP | 
					
						
							|  |  |  |  *  Rajeshwar Ranga - 2007 : Interrupt Distribution Unit API's | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/smp.h>
 | 
					
						
							| 
									
										
										
										
											2013-01-18 15:12:26 +05:30
										 |  |  | #include <linux/irq.h>
 | 
					
						
							|  |  |  | #include <plat/irq.h>
 | 
					
						
							| 
									
										
										
										
											2013-01-18 15:12:23 +05:30
										 |  |  | #include <plat/smp.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static char smp_cpuinfo_buf[128]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  *------------------------------------------------------------------- | 
					
						
							|  |  |  |  * Platform specific callbacks expected by arch SMP code | 
					
						
							|  |  |  |  *------------------------------------------------------------------- | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Master kick starting another CPU | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2013-01-18 15:12:26 +05:30
										 |  |  | static void iss_model_smp_wakeup_cpu(int cpu, unsigned long pc) | 
					
						
							| 
									
										
										
										
											2013-01-18 15:12:23 +05:30
										 |  |  | { | 
					
						
							|  |  |  | 	/* setup the start PC */ | 
					
						
							|  |  |  | 	write_aux_reg(ARC_AUX_XTL_REG_PARAM, pc); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Trigger WRITE_PC cmd for this cpu */ | 
					
						
							|  |  |  | 	write_aux_reg(ARC_AUX_XTL_REG_CMD, | 
					
						
							|  |  |  | 			(ARC_XTL_CMD_WRITE_PC | (cpu << 8))); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Take the cpu out of Halt */ | 
					
						
							|  |  |  | 	write_aux_reg(ARC_AUX_XTL_REG_CMD, | 
					
						
							|  |  |  | 			(ARC_XTL_CMD_CLEAR_HALT | (cpu << 8))); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Any SMP specific init any CPU does when it comes up. | 
					
						
							|  |  |  |  * Here we setup the CPU to enable Inter-Processor-Interrupts | 
					
						
							|  |  |  |  * Called for each CPU | 
					
						
							|  |  |  |  * -Master      : init_IRQ() | 
					
						
							|  |  |  |  * -Other(s)    : start_kernel_secondary() | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2013-01-23 16:32:48 +05:30
										 |  |  | void iss_model_init_smp(unsigned int cpu) | 
					
						
							| 
									
										
										
										
											2013-01-18 15:12:23 +05:30
										 |  |  | { | 
					
						
							|  |  |  | 	/* Check if CPU is configured for more than 16 interrupts */ | 
					
						
							|  |  |  | 	if (NR_IRQS <= 16 || get_hw_config_num_irq() <= 16) | 
					
						
							|  |  |  | 		panic("[arcfpga] IRQ system can't support IDU IPI\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	idu_disable(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/****************************************************************
 | 
					
						
							|  |  |  | 	 * IDU provides a set of Common IRQs, each of which can be dynamically | 
					
						
							|  |  |  | 	 * attached to (1|many|all) CPUs. | 
					
						
							|  |  |  | 	 * The Common IRQs [0-15] are mapped as CPU pvt [16-31] | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * Here we use a simple 1:1 mapping: | 
					
						
							|  |  |  | 	 * A CPU 'x' is wired to Common IRQ 'x'. | 
					
						
							|  |  |  | 	 * So an IDU ASSERT on IRQ 'x' will trigger Interupt on CPU 'x', which | 
					
						
							|  |  |  | 	 * makes up for our simple IPI plumbing. | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * TBD: Have a dedicated multicast IRQ for sending IPIs to all CPUs | 
					
						
							|  |  |  | 	 *      w/o having to do one-at-a-time | 
					
						
							|  |  |  | 	 ******************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Claim an IRQ which would trigger IPI on this CPU. | 
					
						
							|  |  |  | 	 * In IDU parlance it involves setting up a cpu bitmask for the IRQ | 
					
						
							|  |  |  | 	 * The bitmap here contains only 1 CPU (self). | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	idu_irq_set_tgtcpu(cpu, 0x1 << cpu); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Set the IRQ destination to use the bitmask above */ | 
					
						
							|  |  |  | 	idu_irq_set_mode(cpu, 7, /* XXX: IDU_IRQ_MOD_TCPU_ALLRECP: ISS bug */ | 
					
						
							|  |  |  | 			 IDU_IRQ_MODE_PULSE_TRIG); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	idu_enable(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Attach the arch-common IPI ISR to our IDU IRQ */ | 
					
						
							|  |  |  | 	smp_ipi_irq_setup(cpu, IDU_INTERRUPT_0 + cpu); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-18 15:12:26 +05:30
										 |  |  | static void iss_model_ipi_send(void *arg) | 
					
						
							| 
									
										
										
										
											2013-01-18 15:12:23 +05:30
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-01-18 15:12:26 +05:30
										 |  |  | 	struct cpumask *callmap = arg; | 
					
						
							| 
									
										
										
										
											2013-01-18 15:12:23 +05:30
										 |  |  | 	unsigned int cpu; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for_each_cpu(cpu, callmap) | 
					
						
							|  |  |  | 		idu_irq_assert(cpu); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-18 15:12:26 +05:30
										 |  |  | static void iss_model_ipi_clear(int cpu, int irq) | 
					
						
							| 
									
										
										
										
											2013-01-18 15:12:23 +05:30
										 |  |  | { | 
					
						
							|  |  |  | 	idu_irq_clear(IDU_INTERRUPT_0 + cpu); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-18 15:12:26 +05:30
										 |  |  | void iss_model_init_early_smp(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | #define IS_AVAIL1(var, str)    ((var) ? str : "")
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	struct bcr_mp mp; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	READ_BCR(ARC_REG_MP_BCR, mp); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sprintf(smp_cpuinfo_buf, "Extn [ISS-SMP]: v%d, arch(%d) %s %s %s\n", | 
					
						
							|  |  |  | 		mp.ver, mp.mp_arch, IS_AVAIL1(mp.scu, "SCU"), | 
					
						
							|  |  |  | 		IS_AVAIL1(mp.idu, "IDU"), IS_AVAIL1(mp.sdu, "SDU")); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	plat_smp_ops.info = smp_cpuinfo_buf; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	plat_smp_ops.cpu_kick = iss_model_smp_wakeup_cpu; | 
					
						
							|  |  |  | 	plat_smp_ops.ipi_send = iss_model_ipi_send; | 
					
						
							|  |  |  | 	plat_smp_ops.ipi_clear = iss_model_ipi_clear; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-18 15:12:23 +05:30
										 |  |  | /*
 | 
					
						
							|  |  |  |  *------------------------------------------------------------------- | 
					
						
							|  |  |  |  * Low level Platform IPI Providers | 
					
						
							|  |  |  |  *------------------------------------------------------------------- | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Set the Mode for the Common IRQ */ | 
					
						
							|  |  |  | void idu_irq_set_mode(uint8_t irq, uint8_t dest_mode, uint8_t trig_mode) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	uint32_t par = IDU_IRQ_MODE_PARAM(dest_mode, trig_mode); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	IDU_SET_PARAM(par); | 
					
						
							|  |  |  | 	IDU_SET_COMMAND(irq, IDU_IRQ_WMODE); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Set the target cpu Bitmask for Common IRQ */ | 
					
						
							|  |  |  | void idu_irq_set_tgtcpu(uint8_t irq, uint32_t mask) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	IDU_SET_PARAM(mask); | 
					
						
							|  |  |  | 	IDU_SET_COMMAND(irq, IDU_IRQ_WBITMASK); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Get the Interrupt Acknowledged status for IRQ (as CPU Bitmask) */ | 
					
						
							|  |  |  | bool idu_irq_get_ack(uint8_t irq) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	uint32_t val; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	IDU_SET_COMMAND(irq, IDU_IRQ_ACK); | 
					
						
							|  |  |  | 	val = IDU_GET_PARAM(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return val & (1 << irq); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Get the Interrupt Pending status for IRQ (as CPU Bitmask) | 
					
						
							|  |  |  |  * -Pending means CPU has not yet noticed the IRQ (e.g. disabled) | 
					
						
							|  |  |  |  * -After Interrupt has been taken, the IPI expcitily needs to be | 
					
						
							|  |  |  |  *  cleared, to be acknowledged. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | bool idu_irq_get_pend(uint8_t irq) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	uint32_t val; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	IDU_SET_COMMAND(irq, IDU_IRQ_PEND); | 
					
						
							|  |  |  | 	val = IDU_GET_PARAM(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return val & (1 << irq); | 
					
						
							|  |  |  | } |