diff options
author | Vineet Gupta <vgupta@synopsys.com> | 2013-01-18 04:42:23 -0500 |
---|---|---|
committer | Vineet Gupta <vgupta@synopsys.com> | 2013-02-15 12:46:02 -0500 |
commit | 41195d236e84458bebd4fdc218610a92231ac791 (patch) | |
tree | c0049630c1a21a071c9c942086041029ebdf2866 /arch/arc/plat-arcfpga | |
parent | 0ef88a54aa341f754707414500158addbf35c780 (diff) |
ARC: SMP support
ARC common code to enable a SMP system + ISS provided SMP extensions.
ARC700 natively lacks SMP support, hence some of the core features are
are only enabled if SoCs have the necessary h/w pixie-dust. This
includes:
-Inter Processor Interrupts (IPI)
-Cache coherency
-load-locked/store-conditional
...
The low level exception handling would be completely broken in SMP
because we don't have hardware assisted stack switching. Thus a fair bit
of this code is repurposing the MMU_SCRATCH reg for event handler
prologues to keep them re-entrant.
Many thanks to Rajeshwar Ranga for his initial "major" contributions to
SMP Port (back in 2008), and to Noam Camus and Gilad Ben-Yossef for help
with resurrecting that in 3.2 kernel (2012).
Note that this platform code is again singleton design pattern - so
multiple SMP platforms won't build at the moment - this deficiency is
addressed in subsequent patches within this series.
Signed-off-by: Vineet Gupta <vgupta@synopsys.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Rajeshwar Ranga <rajeshwar.ranga@gmail.com>
Cc: Noam Camus <noamc@ezchip.com>
Cc: Gilad Ben-Yossef <gilad@benyossef.com>
Diffstat (limited to 'arch/arc/plat-arcfpga')
-rw-r--r-- | arch/arc/plat-arcfpga/Kconfig | 14 | ||||
-rw-r--r-- | arch/arc/plat-arcfpga/Makefile | 1 | ||||
-rw-r--r-- | arch/arc/plat-arcfpga/include/plat/irq.h | 10 | ||||
-rw-r--r-- | arch/arc/plat-arcfpga/include/plat/smp.h | 115 | ||||
-rw-r--r-- | arch/arc/plat-arcfpga/irq.c | 10 | ||||
-rw-r--r-- | arch/arc/plat-arcfpga/smp.c | 167 |
6 files changed, 316 insertions, 1 deletions
diff --git a/arch/arc/plat-arcfpga/Kconfig b/arch/arc/plat-arcfpga/Kconfig index 7af3a4e7f7d2..38752bfb91e0 100644 --- a/arch/arc/plat-arcfpga/Kconfig +++ b/arch/arc/plat-arcfpga/Kconfig | |||
@@ -13,6 +13,7 @@ choice | |||
13 | 13 | ||
14 | config ARC_BOARD_ANGEL4 | 14 | config ARC_BOARD_ANGEL4 |
15 | bool "ARC Angel4" | 15 | bool "ARC Angel4" |
16 | select ISS_SMP_EXTN if SMP | ||
16 | help | 17 | help |
17 | ARC Angel4 FPGA Ref Platform (Xilinx Virtex Based) | 18 | ARC Angel4 FPGA Ref Platform (Xilinx Virtex Based) |
18 | 19 | ||
@@ -21,6 +22,19 @@ config ARC_BOARD_ML509 | |||
21 | help | 22 | help |
22 | ARC ML509 FPGA Ref Platform (Xilinx Virtex-5 Based) | 23 | ARC ML509 FPGA Ref Platform (Xilinx Virtex-5 Based) |
23 | 24 | ||
25 | config ISS_SMP_EXTN | ||
26 | bool "ARC SMP Extensions (ISS Models only)" | ||
27 | default n | ||
28 | depends on SMP | ||
29 | select ARC_HAS_COH_RTSC | ||
30 | help | ||
31 | SMP Extensions to ARC700, in a "simulation only" Model, supported in | ||
32 | ARC ISS (Instruction Set Simulator). | ||
33 | The SMP extensions include: | ||
34 | -IDU (Interrupt Distribution Unit) | ||
35 | -XTL (To enable CPU start/stop/set-PC for another CPU) | ||
36 | It doesn't provide coherent Caches and/or Atomic Ops (LLOCK/SCOND) | ||
37 | |||
24 | endchoice | 38 | endchoice |
25 | 39 | ||
26 | config ARC_SERIAL_BAUD | 40 | config ARC_SERIAL_BAUD |
diff --git a/arch/arc/plat-arcfpga/Makefile b/arch/arc/plat-arcfpga/Makefile index 385eb9d83b63..2a828bec8212 100644 --- a/arch/arc/plat-arcfpga/Makefile +++ b/arch/arc/plat-arcfpga/Makefile | |||
@@ -7,3 +7,4 @@ | |||
7 | # | 7 | # |
8 | 8 | ||
9 | obj-y := platform.o irq.o | 9 | obj-y := platform.o irq.o |
10 | obj-$(CONFIG_SMP) += smp.o | ||
diff --git a/arch/arc/plat-arcfpga/include/plat/irq.h b/arch/arc/plat-arcfpga/include/plat/irq.h index b34e08734c65..255b90e973ee 100644 --- a/arch/arc/plat-arcfpga/include/plat/irq.h +++ b/arch/arc/plat-arcfpga/include/plat/irq.h | |||
@@ -12,7 +12,11 @@ | |||
12 | #ifndef __PLAT_IRQ_H | 12 | #ifndef __PLAT_IRQ_H |
13 | #define __PLAT_IRQ_H | 13 | #define __PLAT_IRQ_H |
14 | 14 | ||
15 | #define NR_IRQS 16 | 15 | #ifdef CONFIG_SMP |
16 | #define NR_IRQS 32 | ||
17 | #else | ||
18 | #define NR_IRQS 16 | ||
19 | #endif | ||
16 | 20 | ||
17 | #define UART0_IRQ 5 | 21 | #define UART0_IRQ 5 |
18 | #define UART1_IRQ 10 | 22 | #define UART1_IRQ 10 |
@@ -24,4 +28,8 @@ | |||
24 | #define PCI_IRQ 14 | 28 | #define PCI_IRQ 14 |
25 | #define PS2_IRQ 15 | 29 | #define PS2_IRQ 15 |
26 | 30 | ||
31 | #ifdef CONFIG_SMP | ||
32 | #define IDU_INTERRUPT_0 16 | ||
33 | #endif | ||
34 | |||
27 | #endif | 35 | #endif |
diff --git a/arch/arc/plat-arcfpga/include/plat/smp.h b/arch/arc/plat-arcfpga/include/plat/smp.h new file mode 100644 index 000000000000..8c5e46c01b15 --- /dev/null +++ b/arch/arc/plat-arcfpga/include/plat/smp.h | |||
@@ -0,0 +1,115 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | * Rajeshwar Ranga: Interrupt Distribution Unit API's | ||
9 | */ | ||
10 | |||
11 | #ifndef __PLAT_ARCFPGA_SMP_H | ||
12 | #define __PLAT_ARCFPGA_SMP_H | ||
13 | |||
14 | #ifdef CONFIG_SMP | ||
15 | |||
16 | #include <linux/types.h> | ||
17 | #include <asm/arcregs.h> | ||
18 | |||
19 | #define ARC_AUX_IDU_REG_CMD 0x2000 | ||
20 | #define ARC_AUX_IDU_REG_PARAM 0x2001 | ||
21 | |||
22 | #define ARC_AUX_XTL_REG_CMD 0x2002 | ||
23 | #define ARC_AUX_XTL_REG_PARAM 0x2003 | ||
24 | |||
25 | #define ARC_REG_MP_BCR 0x2021 | ||
26 | |||
27 | #define ARC_XTL_CMD_WRITE_PC 0x04 | ||
28 | #define ARC_XTL_CMD_CLEAR_HALT 0x02 | ||
29 | |||
30 | /* | ||
31 | * Build Configuration Register which identifies the sub-components | ||
32 | */ | ||
33 | struct bcr_mp { | ||
34 | #ifdef CONFIG_CPU_BIG_ENDIAN | ||
35 | unsigned int mp_arch:16, pad:5, sdu:1, idu:1, scu:1, ver:8; | ||
36 | #else | ||
37 | unsigned int ver:8, scu:1, idu:1, sdu:1, pad:5, mp_arch:16; | ||
38 | #endif | ||
39 | }; | ||
40 | |||
41 | /* IDU supports 256 common interrupts */ | ||
42 | #define NR_IDU_IRQS 256 | ||
43 | |||
44 | /* | ||
45 | * The Aux Regs layout is same bit-by-bit in both BE/LE modes. | ||
46 | * However when casted as a bitfield encoded "C" struct, gcc treats it as | ||
47 | * memory, generating different code for BE/LE, requiring strcture adj (see | ||
48 | * include/asm/arcregs.h) | ||
49 | * | ||
50 | * However when manually "carving" the value for a Aux, no special handling | ||
51 | * of BE is needed because of the property discribed above | ||
52 | */ | ||
53 | #define IDU_SET_COMMAND(irq, cmd) \ | ||
54 | do { \ | ||
55 | uint32_t __val; \ | ||
56 | __val = (((irq & 0xFF) << 8) | (cmd & 0xFF)); \ | ||
57 | write_aux_reg(ARC_AUX_IDU_REG_CMD, __val); \ | ||
58 | } while (0) | ||
59 | |||
60 | #define IDU_SET_PARAM(par) write_aux_reg(ARC_AUX_IDU_REG_PARAM, par) | ||
61 | #define IDU_GET_PARAM() read_aux_reg(ARC_AUX_IDU_REG_PARAM) | ||
62 | |||
63 | /* IDU Commands */ | ||
64 | #define IDU_DISABLE 0x00 | ||
65 | #define IDU_ENABLE 0x01 | ||
66 | #define IDU_IRQ_CLEAR 0x02 | ||
67 | #define IDU_IRQ_ASSERT 0x03 | ||
68 | #define IDU_IRQ_WMODE 0x04 | ||
69 | #define IDU_IRQ_STATUS 0x05 | ||
70 | #define IDU_IRQ_ACK 0x06 | ||
71 | #define IDU_IRQ_PEND 0x07 | ||
72 | #define IDU_IRQ_RMODE 0x08 | ||
73 | #define IDU_IRQ_WBITMASK 0x09 | ||
74 | #define IDU_IRQ_RBITMASK 0x0A | ||
75 | |||
76 | #define idu_enable() IDU_SET_COMMAND(0, IDU_ENABLE) | ||
77 | #define idu_disable() IDU_SET_COMMAND(0, IDU_DISABLE) | ||
78 | |||
79 | #define idu_irq_assert(irq) IDU_SET_COMMAND((irq), IDU_IRQ_ASSERT) | ||
80 | #define idu_irq_clear(irq) IDU_SET_COMMAND((irq), IDU_IRQ_CLEAR) | ||
81 | |||
82 | /* IDU Interrupt Mode - Destination Encoding */ | ||
83 | #define IDU_IRQ_MOD_DISABLE 0x00 | ||
84 | #define IDU_IRQ_MOD_ROUND_RECP 0x01 | ||
85 | #define IDU_IRQ_MOD_TCPU_FIRSTRECP 0x02 | ||
86 | #define IDU_IRQ_MOD_TCPU_ALLRECP 0x03 | ||
87 | |||
88 | /* IDU Interrupt Mode - Triggering Mode */ | ||
89 | #define IDU_IRQ_MODE_LEVEL_TRIG 0x00 | ||
90 | #define IDU_IRQ_MODE_PULSE_TRIG 0x01 | ||
91 | |||
92 | #define IDU_IRQ_MODE_PARAM(dest_mode, trig_mode) \ | ||
93 | (((trig_mode & 0x01) << 15) | (dest_mode & 0xFF)) | ||
94 | |||
95 | struct idu_irq_config { | ||
96 | uint8_t irq; | ||
97 | uint8_t dest_mode; | ||
98 | uint8_t trig_mode; | ||
99 | }; | ||
100 | |||
101 | struct idu_irq_status { | ||
102 | uint8_t irq; | ||
103 | bool enabled; | ||
104 | bool status; | ||
105 | bool ack; | ||
106 | bool pend; | ||
107 | uint8_t next_rr; | ||
108 | }; | ||
109 | |||
110 | extern void idu_irq_set_tgtcpu(uint8_t irq, uint32_t mask); | ||
111 | extern void idu_irq_set_mode(uint8_t irq, uint8_t dest_mode, uint8_t trig_mode); | ||
112 | |||
113 | #endif /* CONFIG_SMP */ | ||
114 | |||
115 | #endif | ||
diff --git a/arch/arc/plat-arcfpga/irq.c b/arch/arc/plat-arcfpga/irq.c index ed726360b9f6..590edd174c47 100644 --- a/arch/arc/plat-arcfpga/irq.c +++ b/arch/arc/plat-arcfpga/irq.c | |||
@@ -9,7 +9,17 @@ | |||
9 | */ | 9 | */ |
10 | 10 | ||
11 | #include <linux/interrupt.h> | 11 | #include <linux/interrupt.h> |
12 | #include <asm/irq.h> | ||
12 | 13 | ||
13 | void __init plat_init_IRQ(void) | 14 | void __init plat_init_IRQ(void) |
14 | { | 15 | { |
16 | /* | ||
17 | * SMP Hack because UART IRQ hardwired to cpu0 (boot-cpu) but if the | ||
18 | * request_irq() comes from any other CPU, the low level IRQ unamsking | ||
19 | * essential for getting Interrupts won't be enabled on cpu0, locking | ||
20 | * up the UART state machine. | ||
21 | */ | ||
22 | #ifdef CONFIG_SMP | ||
23 | arch_unmask_irq(UART0_IRQ); | ||
24 | #endif | ||
15 | } | 25 | } |
diff --git a/arch/arc/plat-arcfpga/smp.c b/arch/arc/plat-arcfpga/smp.c new file mode 100644 index 000000000000..a95fcdb29033 --- /dev/null +++ b/arch/arc/plat-arcfpga/smp.c | |||
@@ -0,0 +1,167 @@ | |||
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 <asm/irq.h> | ||
16 | #include <plat/smp.h> | ||
17 | |||
18 | static char smp_cpuinfo_buf[128]; | ||
19 | |||
20 | /* | ||
21 | *------------------------------------------------------------------- | ||
22 | * Platform specific callbacks expected by arch SMP code | ||
23 | *------------------------------------------------------------------- | ||
24 | */ | ||
25 | |||
26 | const char *arc_platform_smp_cpuinfo(void) | ||
27 | { | ||
28 | #define IS_AVAIL1(var, str) ((var) ? str : "") | ||
29 | |||
30 | struct bcr_mp mp; | ||
31 | |||
32 | READ_BCR(ARC_REG_MP_BCR, mp); | ||
33 | |||
34 | sprintf(smp_cpuinfo_buf, "Extn [700-SMP]: v%d, arch(%d) %s %s %s\n", | ||
35 | mp.ver, mp.mp_arch, IS_AVAIL1(mp.scu, "SCU"), | ||
36 | IS_AVAIL1(mp.idu, "IDU"), IS_AVAIL1(mp.sdu, "SDU")); | ||
37 | |||
38 | return smp_cpuinfo_buf; | ||
39 | } | ||
40 | |||
41 | /* | ||
42 | * Master kick starting another CPU | ||
43 | */ | ||
44 | void arc_platform_smp_wakeup_cpu(int cpu, unsigned long pc) | ||
45 | { | ||
46 | /* setup the start PC */ | ||
47 | write_aux_reg(ARC_AUX_XTL_REG_PARAM, pc); | ||
48 | |||
49 | /* Trigger WRITE_PC cmd for this cpu */ | ||
50 | write_aux_reg(ARC_AUX_XTL_REG_CMD, | ||
51 | (ARC_XTL_CMD_WRITE_PC | (cpu << 8))); | ||
52 | |||
53 | /* Take the cpu out of Halt */ | ||
54 | write_aux_reg(ARC_AUX_XTL_REG_CMD, | ||
55 | (ARC_XTL_CMD_CLEAR_HALT | (cpu << 8))); | ||
56 | |||
57 | } | ||
58 | |||
59 | /* | ||
60 | * Any SMP specific init any CPU does when it comes up. | ||
61 | * Here we setup the CPU to enable Inter-Processor-Interrupts | ||
62 | * Called for each CPU | ||
63 | * -Master : init_IRQ() | ||
64 | * -Other(s) : start_kernel_secondary() | ||
65 | */ | ||
66 | void arc_platform_smp_init_cpu(void) | ||
67 | { | ||
68 | int cpu = smp_processor_id(); | ||
69 | |||
70 | /* Check if CPU is configured for more than 16 interrupts */ | ||
71 | if (NR_IRQS <= 16 || get_hw_config_num_irq() <= 16) | ||
72 | panic("[arcfpga] IRQ system can't support IDU IPI\n"); | ||
73 | |||
74 | idu_disable(); | ||
75 | |||
76 | /**************************************************************** | ||
77 | * IDU provides a set of Common IRQs, each of which can be dynamically | ||
78 | * attached to (1|many|all) CPUs. | ||
79 | * The Common IRQs [0-15] are mapped as CPU pvt [16-31] | ||
80 | * | ||
81 | * Here we use a simple 1:1 mapping: | ||
82 | * A CPU 'x' is wired to Common IRQ 'x'. | ||
83 | * So an IDU ASSERT on IRQ 'x' will trigger Interupt on CPU 'x', which | ||
84 | * makes up for our simple IPI plumbing. | ||
85 | * | ||
86 | * TBD: Have a dedicated multicast IRQ for sending IPIs to all CPUs | ||
87 | * w/o having to do one-at-a-time | ||
88 | ******************************************************************/ | ||
89 | |||
90 | /* | ||
91 | * Claim an IRQ which would trigger IPI on this CPU. | ||
92 | * In IDU parlance it involves setting up a cpu bitmask for the IRQ | ||
93 | * The bitmap here contains only 1 CPU (self). | ||
94 | */ | ||
95 | idu_irq_set_tgtcpu(cpu, 0x1 << cpu); | ||
96 | |||
97 | /* Set the IRQ destination to use the bitmask above */ | ||
98 | idu_irq_set_mode(cpu, 7, /* XXX: IDU_IRQ_MOD_TCPU_ALLRECP: ISS bug */ | ||
99 | IDU_IRQ_MODE_PULSE_TRIG); | ||
100 | |||
101 | idu_enable(); | ||
102 | |||
103 | /* Attach the arch-common IPI ISR to our IDU IRQ */ | ||
104 | smp_ipi_irq_setup(cpu, IDU_INTERRUPT_0 + cpu); | ||
105 | } | ||
106 | |||
107 | void arc_platform_ipi_send(const struct cpumask *callmap) | ||
108 | { | ||
109 | unsigned int cpu; | ||
110 | |||
111 | for_each_cpu(cpu, callmap) | ||
112 | idu_irq_assert(cpu); | ||
113 | } | ||
114 | |||
115 | void arc_platform_ipi_clear(int cpu, int irq) | ||
116 | { | ||
117 | idu_irq_clear(IDU_INTERRUPT_0 + cpu); | ||
118 | } | ||
119 | |||
120 | /* | ||
121 | *------------------------------------------------------------------- | ||
122 | * Low level Platform IPI Providers | ||
123 | *------------------------------------------------------------------- | ||
124 | */ | ||
125 | |||
126 | /* Set the Mode for the Common IRQ */ | ||
127 | void idu_irq_set_mode(uint8_t irq, uint8_t dest_mode, uint8_t trig_mode) | ||
128 | { | ||
129 | uint32_t par = IDU_IRQ_MODE_PARAM(dest_mode, trig_mode); | ||
130 | |||
131 | IDU_SET_PARAM(par); | ||
132 | IDU_SET_COMMAND(irq, IDU_IRQ_WMODE); | ||
133 | } | ||
134 | |||
135 | /* Set the target cpu Bitmask for Common IRQ */ | ||
136 | void idu_irq_set_tgtcpu(uint8_t irq, uint32_t mask) | ||
137 | { | ||
138 | IDU_SET_PARAM(mask); | ||
139 | IDU_SET_COMMAND(irq, IDU_IRQ_WBITMASK); | ||
140 | } | ||
141 | |||
142 | /* Get the Interrupt Acknowledged status for IRQ (as CPU Bitmask) */ | ||
143 | bool idu_irq_get_ack(uint8_t irq) | ||
144 | { | ||
145 | uint32_t val; | ||
146 | |||
147 | IDU_SET_COMMAND(irq, IDU_IRQ_ACK); | ||
148 | val = IDU_GET_PARAM(); | ||
149 | |||
150 | return val & (1 << irq); | ||
151 | } | ||
152 | |||
153 | /* | ||
154 | * Get the Interrupt Pending status for IRQ (as CPU Bitmask) | ||
155 | * -Pending means CPU has not yet noticed the IRQ (e.g. disabled) | ||
156 | * -After Interrupt has been taken, the IPI expcitily needs to be | ||
157 | * cleared, to be acknowledged. | ||
158 | */ | ||
159 | bool idu_irq_get_pend(uint8_t irq) | ||
160 | { | ||
161 | uint32_t val; | ||
162 | |||
163 | IDU_SET_COMMAND(irq, IDU_IRQ_PEND); | ||
164 | val = IDU_GET_PARAM(); | ||
165 | |||
166 | return val & (1 << irq); | ||
167 | } | ||