From 41195d236e84458bebd4fdc218610a92231ac791 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:23 +0530 Subject: 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 Cc: Arnd Bergmann Cc: Thomas Gleixner Cc: Rajeshwar Ranga Cc: Noam Camus Cc: Gilad Ben-Yossef --- arch/arc/plat-arcfpga/include/plat/smp.h | 115 +++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 arch/arc/plat-arcfpga/include/plat/smp.h (limited to 'arch/arc/plat-arcfpga/include/plat/smp.h') 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 @@ +/* + * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Rajeshwar Ranga: Interrupt Distribution Unit API's + */ + +#ifndef __PLAT_ARCFPGA_SMP_H +#define __PLAT_ARCFPGA_SMP_H + +#ifdef CONFIG_SMP + +#include +#include + +#define ARC_AUX_IDU_REG_CMD 0x2000 +#define ARC_AUX_IDU_REG_PARAM 0x2001 + +#define ARC_AUX_XTL_REG_CMD 0x2002 +#define ARC_AUX_XTL_REG_PARAM 0x2003 + +#define ARC_REG_MP_BCR 0x2021 + +#define ARC_XTL_CMD_WRITE_PC 0x04 +#define ARC_XTL_CMD_CLEAR_HALT 0x02 + +/* + * Build Configuration Register which identifies the sub-components + */ +struct bcr_mp { +#ifdef CONFIG_CPU_BIG_ENDIAN + unsigned int mp_arch:16, pad:5, sdu:1, idu:1, scu:1, ver:8; +#else + unsigned int ver:8, scu:1, idu:1, sdu:1, pad:5, mp_arch:16; +#endif +}; + +/* IDU supports 256 common interrupts */ +#define NR_IDU_IRQS 256 + +/* + * The Aux Regs layout is same bit-by-bit in both BE/LE modes. + * However when casted as a bitfield encoded "C" struct, gcc treats it as + * memory, generating different code for BE/LE, requiring strcture adj (see + * include/asm/arcregs.h) + * + * However when manually "carving" the value for a Aux, no special handling + * of BE is needed because of the property discribed above + */ +#define IDU_SET_COMMAND(irq, cmd) \ +do { \ + uint32_t __val; \ + __val = (((irq & 0xFF) << 8) | (cmd & 0xFF)); \ + write_aux_reg(ARC_AUX_IDU_REG_CMD, __val); \ +} while (0) + +#define IDU_SET_PARAM(par) write_aux_reg(ARC_AUX_IDU_REG_PARAM, par) +#define IDU_GET_PARAM() read_aux_reg(ARC_AUX_IDU_REG_PARAM) + +/* IDU Commands */ +#define IDU_DISABLE 0x00 +#define IDU_ENABLE 0x01 +#define IDU_IRQ_CLEAR 0x02 +#define IDU_IRQ_ASSERT 0x03 +#define IDU_IRQ_WMODE 0x04 +#define IDU_IRQ_STATUS 0x05 +#define IDU_IRQ_ACK 0x06 +#define IDU_IRQ_PEND 0x07 +#define IDU_IRQ_RMODE 0x08 +#define IDU_IRQ_WBITMASK 0x09 +#define IDU_IRQ_RBITMASK 0x0A + +#define idu_enable() IDU_SET_COMMAND(0, IDU_ENABLE) +#define idu_disable() IDU_SET_COMMAND(0, IDU_DISABLE) + +#define idu_irq_assert(irq) IDU_SET_COMMAND((irq), IDU_IRQ_ASSERT) +#define idu_irq_clear(irq) IDU_SET_COMMAND((irq), IDU_IRQ_CLEAR) + +/* IDU Interrupt Mode - Destination Encoding */ +#define IDU_IRQ_MOD_DISABLE 0x00 +#define IDU_IRQ_MOD_ROUND_RECP 0x01 +#define IDU_IRQ_MOD_TCPU_FIRSTRECP 0x02 +#define IDU_IRQ_MOD_TCPU_ALLRECP 0x03 + +/* IDU Interrupt Mode - Triggering Mode */ +#define IDU_IRQ_MODE_LEVEL_TRIG 0x00 +#define IDU_IRQ_MODE_PULSE_TRIG 0x01 + +#define IDU_IRQ_MODE_PARAM(dest_mode, trig_mode) \ + (((trig_mode & 0x01) << 15) | (dest_mode & 0xFF)) + +struct idu_irq_config { + uint8_t irq; + uint8_t dest_mode; + uint8_t trig_mode; +}; + +struct idu_irq_status { + uint8_t irq; + bool enabled; + bool status; + bool ack; + bool pend; + uint8_t next_rr; +}; + +extern void idu_irq_set_tgtcpu(uint8_t irq, uint32_t mask); +extern void idu_irq_set_mode(uint8_t irq, uint8_t dest_mode, uint8_t trig_mode); + +#endif /* CONFIG_SMP */ + +#endif -- cgit v1.2.3 From 877768c84d6ca8f7dedafff0e44615a12e82f8f4 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Wed, 23 Jan 2013 16:32:48 +0530 Subject: ARC: [Review] Multi-platform image #3: switch to board callback -platform API is retired and instead callbacks are used Signed-off-by: Vineet Gupta Cc: Arnd Bergmann Acked-by: Arnd Bergmann --- arch/arc/include/asm/irq.h | 1 - arch/arc/include/asm/smp.h | 5 ---- arch/arc/kernel/irq.c | 2 -- arch/arc/kernel/setup.c | 5 ---- arch/arc/kernel/smp.c | 1 - arch/arc/plat-arcfpga/include/plat/irq.h | 2 ++ arch/arc/plat-arcfpga/include/plat/smp.h | 2 ++ arch/arc/plat-arcfpga/irq.c | 2 +- arch/arc/plat-arcfpga/platform.c | 50 +++++++++++++++++++++++++++----- arch/arc/plat-arcfpga/smp.c | 4 +-- 10 files changed, 48 insertions(+), 26 deletions(-) (limited to 'arch/arc/plat-arcfpga/include/plat/smp.h') diff --git a/arch/arc/include/asm/irq.h b/arch/arc/include/asm/irq.h index c91b2e4344a1..f1b318d92877 100644 --- a/arch/arc/include/asm/irq.h +++ b/arch/arc/include/asm/irq.h @@ -17,7 +17,6 @@ #include extern void __init arc_init_IRQ(void); -extern void __init plat_init_IRQ(void); extern int __init get_hw_config_num_irq(void); void __cpuinit arc_local_timer_setup(unsigned int cpu); diff --git a/arch/arc/include/asm/smp.h b/arch/arc/include/asm/smp.h index f91f1946272f..0cff3a548136 100644 --- a/arch/arc/include/asm/smp.h +++ b/arch/arc/include/asm/smp.h @@ -46,10 +46,6 @@ extern int smp_ipi_irq_setup(int cpu, int irq); * arc_platform_smp_cpuinfo: * returns a string containing info for /proc/cpuinfo * - * arc_platform_smp_init_cpu: - * Called from start_kernel_secondary to do any CPU local setup - * such as starting a timer, setting up IPI etc - * * arc_platform_smp_wait_to_boot: * Called from early bootup code for non-Master CPUs to "park" them * @@ -64,7 +60,6 @@ extern int smp_ipi_irq_setup(int cpu, int irq); * Takes @cpu which got IPI at @irq to do any IPI clearing */ extern const char *arc_platform_smp_cpuinfo(void); -extern void arc_platform_smp_init_cpu(void); extern void arc_platform_smp_wait_to_boot(int cpu); extern void arc_platform_smp_wakeup_cpu(int cpu, unsigned long pc); extern void arc_platform_ipi_send(const struct cpumask *callmap); diff --git a/arch/arc/kernel/irq.c b/arch/arc/kernel/irq.c index 1198168850e8..551c10dff481 100644 --- a/arch/arc/kernel/irq.c +++ b/arch/arc/kernel/irq.c @@ -124,7 +124,6 @@ void __init init_onchip_IRQ(void) void __init init_IRQ(void) { init_onchip_IRQ(); - plat_init_IRQ(); /* Any external intc can be setup here */ if (machine_desc->init_irq) @@ -132,7 +131,6 @@ void __init init_IRQ(void) #ifdef CONFIG_SMP /* Master CPU can initialize it's side of IPI */ - arc_platform_smp_init_cpu(); if (machine_desc->init_smp) machine_desc->init_smp(smp_processor_id()); #endif diff --git a/arch/arc/kernel/setup.c b/arch/arc/kernel/setup.c index 20273b89e545..e591c6ae88a6 100644 --- a/arch/arc/kernel/setup.c +++ b/arch/arc/kernel/setup.c @@ -319,10 +319,6 @@ void __init setup_processor(void) arc_chk_fpu(); } -void __init __attribute__((weak)) arc_platform_early_init(void) -{ -} - void __init setup_arch(char **cmdline_p) { #ifdef CONFIG_CMDLINE_UBOOT @@ -347,7 +343,6 @@ void __init setup_arch(char **cmdline_p) parse_early_param(); /* Platform/board specific: e.g. early console registration */ - arc_platform_early_init(); if (machine_desc->init_early) machine_desc->init_early(); diff --git a/arch/arc/kernel/smp.c b/arch/arc/kernel/smp.c index ea15f073452f..8ee010fdbc18 100644 --- a/arch/arc/kernel/smp.c +++ b/arch/arc/kernel/smp.c @@ -127,7 +127,6 @@ void __cpuinit start_kernel_secondary(void) pr_info("## CPU%u LIVE ##: Executing Code...\n", cpu); - arc_platform_smp_init_cpu(); if (machine_desc->init_smp) machine_desc->init_smp(smp_processor_id()); diff --git a/arch/arc/plat-arcfpga/include/plat/irq.h b/arch/arc/plat-arcfpga/include/plat/irq.h index 255b90e973ee..6515df232f21 100644 --- a/arch/arc/plat-arcfpga/include/plat/irq.h +++ b/arch/arc/plat-arcfpga/include/plat/irq.h @@ -32,4 +32,6 @@ #define IDU_INTERRUPT_0 16 #endif +extern void __init plat_fpga_init_IRQ(void); + #endif diff --git a/arch/arc/plat-arcfpga/include/plat/smp.h b/arch/arc/plat-arcfpga/include/plat/smp.h index 8c5e46c01b15..27822ac8155e 100644 --- a/arch/arc/plat-arcfpga/include/plat/smp.h +++ b/arch/arc/plat-arcfpga/include/plat/smp.h @@ -110,6 +110,8 @@ struct idu_irq_status { extern void idu_irq_set_tgtcpu(uint8_t irq, uint32_t mask); extern void idu_irq_set_mode(uint8_t irq, uint8_t dest_mode, uint8_t trig_mode); +extern void iss_model_init_smp(unsigned int cpu); + #endif /* CONFIG_SMP */ #endif diff --git a/arch/arc/plat-arcfpga/irq.c b/arch/arc/plat-arcfpga/irq.c index 590edd174c47..0ea43c26f16a 100644 --- a/arch/arc/plat-arcfpga/irq.c +++ b/arch/arc/plat-arcfpga/irq.c @@ -11,7 +11,7 @@ #include #include -void __init plat_init_IRQ(void) +void __init plat_fpga_init_IRQ(void) { /* * SMP Hack because UART IRQ hardwired to cpu0 (boot-cpu) but if the diff --git a/arch/arc/plat-arcfpga/platform.c b/arch/arc/plat-arcfpga/platform.c index b7f63e3f3cae..ac85d6927334 100644 --- a/arch/arc/plat-arcfpga/platform.c +++ b/arch/arc/plat-arcfpga/platform.c @@ -18,7 +18,9 @@ #include #include #include +#include #include +#include /*-----------------------BVCI Latency Unit -----------------------------*/ @@ -153,10 +155,7 @@ static void arc_fpga_serial_init(void) #endif } -/* - * Early Platform Initialization called from setup_arch() - */ -void __init arc_platform_early_init(void) +static void __init plat_fpga_early_init(void) { pr_info("[plat-arcfpga]: registering early dev resources\n"); @@ -172,7 +171,7 @@ static struct of_dev_auxdata plat_auxdata_lookup[] __initdata = { {} }; -int __init fpga_plat_init(void) +static void __init plat_fpga_populate_dev(void) { pr_info("[plat-arcfpga]: registering device resources\n"); @@ -182,7 +181,42 @@ int __init fpga_plat_init(void) */ of_platform_populate(NULL, of_default_bus_match_table, plat_auxdata_lookup, NULL); - - return 0; } -arch_initcall(fpga_plat_init); + +/*----------------------- Machine Descriptions ------------------------------ + * + * Machine description is simply a set of platform/board specific callbacks + * This is not directly related to DeviceTree based dynamic device creation, + * however as part of early device tree scan, we also select the right + * callback set, by matching the DT compatible name. + */ + +static const char *aa4_compat[] __initdata = { + "snps,arc-angel4", + NULL, +}; + +MACHINE_START(ANGEL4, "angel4") + .dt_compat = aa4_compat, + .init_early = plat_fpga_early_init, + .init_machine = plat_fpga_populate_dev, + .init_irq = plat_fpga_init_IRQ, +#ifdef CONFIG_SMP + .init_smp = iss_model_init_smp, +#endif +MACHINE_END + +static const char *ml509_compat[] __initdata = { + "snps,arc-ml509", + NULL, +}; + +MACHINE_START(ML509, "ml509") + .dt_compat = ml509_compat, + .init_early = plat_fpga_early_init, + .init_machine = plat_fpga_populate_dev, + .init_irq = plat_fpga_init_IRQ, +#ifdef CONFIG_SMP + .init_smp = iss_model_init_smp, +#endif +MACHINE_END diff --git a/arch/arc/plat-arcfpga/smp.c b/arch/arc/plat-arcfpga/smp.c index a95fcdb29033..fec18793bbe0 100644 --- a/arch/arc/plat-arcfpga/smp.c +++ b/arch/arc/plat-arcfpga/smp.c @@ -63,10 +63,8 @@ void arc_platform_smp_wakeup_cpu(int cpu, unsigned long pc) * -Master : init_IRQ() * -Other(s) : start_kernel_secondary() */ -void arc_platform_smp_init_cpu(void) +void iss_model_init_smp(unsigned int cpu) { - int cpu = smp_processor_id(); - /* Check if CPU is configured for more than 16 interrupts */ if (NR_IRQS <= 16 || get_hw_config_num_irq() <= 16) panic("[arcfpga] IRQ system can't support IDU IPI\n"); -- cgit v1.2.3 From b830cde5a486d1157105fe928dfa0ddcb9f1b840 Mon Sep 17 00:00:00 2001 From: Vineet Gupta Date: Fri, 18 Jan 2013 15:12:26 +0530 Subject: ARC: [Review] Multi-platform image #8: platform registers SMP callbacks Platforms export their SMP callbacks by populating arc_smp_ops. The population itself needs to be done pretty early, from init_early callback. Signed-off-by: Vineet Gupta Cc: Arnd Bergmann --- arch/arc/plat-arcfpga/include/plat/smp.h | 1 + arch/arc/plat-arcfpga/platform.c | 4 ++++ arch/arc/plat-arcfpga/smp.c | 41 ++++++++++++++++++-------------- 3 files changed, 28 insertions(+), 18 deletions(-) (limited to 'arch/arc/plat-arcfpga/include/plat/smp.h') diff --git a/arch/arc/plat-arcfpga/include/plat/smp.h b/arch/arc/plat-arcfpga/include/plat/smp.h index 27822ac8155e..c09eb4cfc77c 100644 --- a/arch/arc/plat-arcfpga/include/plat/smp.h +++ b/arch/arc/plat-arcfpga/include/plat/smp.h @@ -111,6 +111,7 @@ extern void idu_irq_set_tgtcpu(uint8_t irq, uint32_t mask); extern void idu_irq_set_mode(uint8_t irq, uint8_t dest_mode, uint8_t trig_mode); extern void iss_model_init_smp(unsigned int cpu); +extern void iss_model_init_early_smp(void); #endif /* CONFIG_SMP */ diff --git a/arch/arc/plat-arcfpga/platform.c b/arch/arc/plat-arcfpga/platform.c index 4024f10a39ca..4e20a1a5104d 100644 --- a/arch/arc/plat-arcfpga/platform.c +++ b/arch/arc/plat-arcfpga/platform.c @@ -162,6 +162,10 @@ static void __init plat_fpga_early_init(void) setup_bvci_lat_unit(); arc_fpga_serial_init(); + +#ifdef CONFIG_SMP + iss_model_init_early_smp(); +#endif } static struct of_dev_auxdata plat_auxdata_lookup[] __initdata = { diff --git a/arch/arc/plat-arcfpga/smp.c b/arch/arc/plat-arcfpga/smp.c index 68a53b153d03..91b55349a5f8 100644 --- a/arch/arc/plat-arcfpga/smp.c +++ b/arch/arc/plat-arcfpga/smp.c @@ -24,25 +24,10 @@ static char smp_cpuinfo_buf[128]; *------------------------------------------------------------------- */ -const char *arc_platform_smp_cpuinfo(void) -{ -#define IS_AVAIL1(var, str) ((var) ? str : "") - - struct bcr_mp mp; - - READ_BCR(ARC_REG_MP_BCR, mp); - - sprintf(smp_cpuinfo_buf, "Extn [700-SMP]: v%d, arch(%d) %s %s %s\n", - mp.ver, mp.mp_arch, IS_AVAIL1(mp.scu, "SCU"), - IS_AVAIL1(mp.idu, "IDU"), IS_AVAIL1(mp.sdu, "SDU")); - - return smp_cpuinfo_buf; -} - /* * Master kick starting another CPU */ -void arc_platform_smp_wakeup_cpu(int cpu, unsigned long pc) +static void iss_model_smp_wakeup_cpu(int cpu, unsigned long pc) { /* setup the start PC */ write_aux_reg(ARC_AUX_XTL_REG_PARAM, pc); @@ -103,19 +88,39 @@ void iss_model_init_smp(unsigned int cpu) smp_ipi_irq_setup(cpu, IDU_INTERRUPT_0 + cpu); } -void arc_platform_ipi_send(const struct cpumask *callmap) +static void iss_model_ipi_send(void *arg) { + struct cpumask *callmap = arg; unsigned int cpu; for_each_cpu(cpu, callmap) idu_irq_assert(cpu); } -void arc_platform_ipi_clear(int cpu, int irq) +static void iss_model_ipi_clear(int cpu, int irq) { idu_irq_clear(IDU_INTERRUPT_0 + cpu); } +void iss_model_init_early_smp(void) +{ +#define IS_AVAIL1(var, str) ((var) ? str : "") + + struct bcr_mp mp; + + READ_BCR(ARC_REG_MP_BCR, mp); + + sprintf(smp_cpuinfo_buf, "Extn [ISS-SMP]: v%d, arch(%d) %s %s %s\n", + mp.ver, mp.mp_arch, IS_AVAIL1(mp.scu, "SCU"), + IS_AVAIL1(mp.idu, "IDU"), IS_AVAIL1(mp.sdu, "SDU")); + + plat_smp_ops.info = smp_cpuinfo_buf; + + plat_smp_ops.cpu_kick = iss_model_smp_wakeup_cpu; + plat_smp_ops.ipi_send = iss_model_ipi_send; + plat_smp_ops.ipi_clear = iss_model_ipi_clear; +} + /* *------------------------------------------------------------------- * Low level Platform IPI Providers -- cgit v1.2.3