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