aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arc/plat-arcfpga
diff options
context:
space:
mode:
authorVineet Gupta <vgupta@synopsys.com>2013-01-18 04:42:23 -0500
committerVineet Gupta <vgupta@synopsys.com>2013-02-15 12:46:02 -0500
commit41195d236e84458bebd4fdc218610a92231ac791 (patch)
treec0049630c1a21a071c9c942086041029ebdf2866 /arch/arc/plat-arcfpga
parent0ef88a54aa341f754707414500158addbf35c780 (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/Kconfig14
-rw-r--r--arch/arc/plat-arcfpga/Makefile1
-rw-r--r--arch/arc/plat-arcfpga/include/plat/irq.h10
-rw-r--r--arch/arc/plat-arcfpga/include/plat/smp.h115
-rw-r--r--arch/arc/plat-arcfpga/irq.c10
-rw-r--r--arch/arc/plat-arcfpga/smp.c167
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
14config ARC_BOARD_ANGEL4 14config 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
25config 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
24endchoice 38endchoice
25 39
26config ARC_SERIAL_BAUD 40config 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
9obj-y := platform.o irq.o 9obj-y := platform.o irq.o
10obj-$(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 */
33struct 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) \
54do { \
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
95struct idu_irq_config {
96 uint8_t irq;
97 uint8_t dest_mode;
98 uint8_t trig_mode;
99};
100
101struct 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
110extern void idu_irq_set_tgtcpu(uint8_t irq, uint32_t mask);
111extern 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
13void __init plat_init_IRQ(void) 14void __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
18static char smp_cpuinfo_buf[128];
19
20/*
21 *-------------------------------------------------------------------
22 * Platform specific callbacks expected by arch SMP code
23 *-------------------------------------------------------------------
24 */
25
26const 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 */
44void 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 */
66void 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
107void 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
115void 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 */
127void 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 */
136void 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) */
143bool 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 */
159bool 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}