Commit | Line | Data |
---|---|---|
41195d23 VG |
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> | |
e97ff121 | 15 | #include <linux/irq.h> |
41195d23 VG |
16 | #include <plat/smp.h> |
17 | ||
72f933e7 VG |
18 | #define IDU_INTERRUPT_0 16 |
19 | ||
41195d23 VG |
20 | static char smp_cpuinfo_buf[128]; |
21 | ||
22 | /* | |
23 | *------------------------------------------------------------------- | |
24 | * Platform specific callbacks expected by arch SMP code | |
25 | *------------------------------------------------------------------- | |
26 | */ | |
27 | ||
41195d23 VG |
28 | /* |
29 | * Master kick starting another CPU | |
30 | */ | |
b830cde5 | 31 | static void iss_model_smp_wakeup_cpu(int cpu, unsigned long pc) |
41195d23 VG |
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 | ||
9a091d9e VG |
46 | static 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 | ||
41195d23 VG |
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 | */ | |
877768c8 | 71 | void iss_model_init_smp(unsigned int cpu) |
41195d23 | 72 | { |
41195d23 VG |
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 | ||
ddf84433 | 110 | static void iss_model_ipi_send(int cpu) |
41195d23 | 111 | { |
ddf84433 | 112 | idu_irq_assert(cpu); |
41195d23 VG |
113 | } |
114 | ||
ccdaa6e0 | 115 | static void iss_model_ipi_clear(int irq) |
41195d23 | 116 | { |
ccdaa6e0 | 117 | idu_irq_clear(IDU_INTERRUPT_0 + smp_processor_id()); |
41195d23 VG |
118 | } |
119 | ||
b830cde5 VG |
120 | void 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 | ||
41195d23 VG |
139 | /* |
140 | *------------------------------------------------------------------- | |
141 | * Low level Platform IPI Providers | |
142 | *------------------------------------------------------------------- | |
143 | */ | |
144 | ||
145 | /* Set the Mode for the Common IRQ */ | |
146 | void 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 */ | |
155 | void 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) */ | |
162 | bool 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 | */ | |
178 | bool 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 | } |