1/* 2 * ARC700 Simulation-only Extensions for SMP 3 * 4 * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 * 10 * Vineet Gupta - 2012 : split off arch common and plat specific SMP 11 * Rajeshwar Ranga - 2007 : Interrupt Distribution Unit API's 12 */ 13 14#include <linux/smp.h> 15#include <linux/irq.h> 16#include <plat/smp.h> 17 18#define IDU_INTERRUPT_0 16 19 20static char smp_cpuinfo_buf[128]; 21 22/* 23 *------------------------------------------------------------------- 24 * Platform specific callbacks expected by arch SMP code 25 *------------------------------------------------------------------- 26 */ 27 28/* 29 * Master kick starting another CPU 30 */ 31static void iss_model_smp_wakeup_cpu(int cpu, unsigned long pc) 32{ 33 /* setup the start PC */ 34 write_aux_reg(ARC_AUX_XTL_REG_PARAM, pc); 35 36 /* Trigger WRITE_PC cmd for this cpu */ 37 write_aux_reg(ARC_AUX_XTL_REG_CMD, 38 (ARC_XTL_CMD_WRITE_PC | (cpu << 8))); 39 40 /* Take the cpu out of Halt */ 41 write_aux_reg(ARC_AUX_XTL_REG_CMD, 42 (ARC_XTL_CMD_CLEAR_HALT | (cpu << 8))); 43 44} 45 46static inline int get_hw_config_num_irq(void) 47{ 48 uint32_t val = read_aux_reg(ARC_REG_VECBASE_BCR); 49 50 switch (val & 0x03) { 51 case 0: 52 return 16; 53 case 1: 54 return 32; 55 case 2: 56 return 8; 57 default: 58 return 0; 59 } 60 61 return 0; 62} 63 64/* 65 * Any SMP specific init any CPU does when it comes up. 66 * Here we setup the CPU to enable Inter-Processor-Interrupts 67 * Called for each CPU 68 * -Master : init_IRQ() 69 * -Other(s) : start_kernel_secondary() 70 */ 71void iss_model_init_smp(unsigned int cpu) 72{ 73 /* Check if CPU is configured for more than 16 interrupts */ 74 if (NR_IRQS <= 16 || get_hw_config_num_irq() <= 16) 75 panic("[arcfpga] IRQ system can't support IDU IPI\n"); 76 77 idu_disable(); 78 79 /**************************************************************** 80 * IDU provides a set of Common IRQs, each of which can be dynamically 81 * attached to (1|many|all) CPUs. 82 * The Common IRQs [0-15] are mapped as CPU pvt [16-31] 83 * 84 * Here we use a simple 1:1 mapping: 85 * A CPU 'x' is wired to Common IRQ 'x'. 86 * So an IDU ASSERT on IRQ 'x' will trigger Interupt on CPU 'x', which 87 * makes up for our simple IPI plumbing. 88 * 89 * TBD: Have a dedicated multicast IRQ for sending IPIs to all CPUs 90 * w/o having to do one-at-a-time 91 ******************************************************************/ 92 93 /* 94 * Claim an IRQ which would trigger IPI on this CPU. 95 * In IDU parlance it involves setting up a cpu bitmask for the IRQ 96 * The bitmap here contains only 1 CPU (self). 97 */ 98 idu_irq_set_tgtcpu(cpu, 0x1 << cpu); 99 100 /* Set the IRQ destination to use the bitmask above */ 101 idu_irq_set_mode(cpu, 7, /* XXX: IDU_IRQ_MOD_TCPU_ALLRECP: ISS bug */ 102 IDU_IRQ_MODE_PULSE_TRIG); 103 104 idu_enable(); 105 106 /* Attach the arch-common IPI ISR to our IDU IRQ */ 107 smp_ipi_irq_setup(cpu, IDU_INTERRUPT_0 + cpu); 108} 109 110static void iss_model_ipi_send(int cpu) 111{ 112 idu_irq_assert(cpu); 113} 114 115static void iss_model_ipi_clear(int irq) 116{ 117 idu_irq_clear(IDU_INTERRUPT_0 + smp_processor_id()); 118} 119 120void iss_model_init_early_smp(void) 121{ 122#define IS_AVAIL1(var, str) ((var) ? str : "") 123 124 struct bcr_mp mp; 125 126 READ_BCR(ARC_REG_MP_BCR, mp); 127 128 sprintf(smp_cpuinfo_buf, "Extn [ISS-SMP]: v%d, arch(%d) %s %s %s\n", 129 mp.ver, mp.mp_arch, IS_AVAIL1(mp.scu, "SCU"), 130 IS_AVAIL1(mp.idu, "IDU"), IS_AVAIL1(mp.sdu, "SDU")); 131 132 plat_smp_ops.info = smp_cpuinfo_buf; 133 134 plat_smp_ops.cpu_kick = iss_model_smp_wakeup_cpu; 135 plat_smp_ops.ipi_send = iss_model_ipi_send; 136 plat_smp_ops.ipi_clear = iss_model_ipi_clear; 137} 138 139/* 140 *------------------------------------------------------------------- 141 * Low level Platform IPI Providers 142 *------------------------------------------------------------------- 143 */ 144 145/* Set the Mode for the Common IRQ */ 146void idu_irq_set_mode(uint8_t irq, uint8_t dest_mode, uint8_t trig_mode) 147{ 148 uint32_t par = IDU_IRQ_MODE_PARAM(dest_mode, trig_mode); 149 150 IDU_SET_PARAM(par); 151 IDU_SET_COMMAND(irq, IDU_IRQ_WMODE); 152} 153 154/* Set the target cpu Bitmask for Common IRQ */ 155void idu_irq_set_tgtcpu(uint8_t irq, uint32_t mask) 156{ 157 IDU_SET_PARAM(mask); 158 IDU_SET_COMMAND(irq, IDU_IRQ_WBITMASK); 159} 160 161/* Get the Interrupt Acknowledged status for IRQ (as CPU Bitmask) */ 162bool idu_irq_get_ack(uint8_t irq) 163{ 164 uint32_t val; 165 166 IDU_SET_COMMAND(irq, IDU_IRQ_ACK); 167 val = IDU_GET_PARAM(); 168 169 return val & (1 << irq); 170} 171 172/* 173 * Get the Interrupt Pending status for IRQ (as CPU Bitmask) 174 * -Pending means CPU has not yet noticed the IRQ (e.g. disabled) 175 * -After Interrupt has been taken, the IPI expcitily needs to be 176 * cleared, to be acknowledged. 177 */ 178bool idu_irq_get_pend(uint8_t irq) 179{ 180 uint32_t val; 181 182 IDU_SET_COMMAND(irq, IDU_IRQ_PEND); 183 val = IDU_GET_PARAM(); 184 185 return val & (1 << irq); 186} 187