Skip to content

Commit

Permalink
Merge branch 'sh/smp'
Browse files Browse the repository at this point in the history
  • Loading branch information
Paul Mundt committed Apr 26, 2010
2 parents e60692b + e7dc951 commit 54b41b9
Show file tree
Hide file tree
Showing 15 changed files with 373 additions and 40 deletions.
7 changes: 7 additions & 0 deletions arch/sh/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,13 @@ config NR_CPUS
This is purely to save memory - each supported CPU adds
approximately eight kilobytes to the kernel image.

config HOTPLUG_CPU
bool "Support for hot-pluggable CPUs (EXPERIMENTAL)"
depends on SMP && HOTPLUG && EXPERIMENTAL
help
Say Y here to experiment with turning CPUs off and on. CPUs
can be controlled through /sys/devices/system/cpu.

source "kernel/Kconfig.preempt"

config GUSA
Expand Down
3 changes: 3 additions & 0 deletions arch/sh/boards/board-urquell.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <cpu/sh7786.h>
#include <asm/heartbeat.h>
#include <asm/sizes.h>
#include <asm/smp-ops.h>

/*
* bit 1234 5678
Expand Down Expand Up @@ -203,6 +204,8 @@ static void __init urquell_setup(char **cmdline_p)
printk(KERN_INFO "Renesas Technology Corp. Urquell support.\n");

pm_power_off = urquell_power_off;

register_smp_ops(&shx3_smp_ops);
}

/*
Expand Down
3 changes: 3 additions & 0 deletions arch/sh/boards/mach-sdk7786/setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <asm/heartbeat.h>
#include <asm/sizes.h>
#include <asm/reboot.h>
#include <asm/smp-ops.h>

static struct resource heartbeat_resource = {
.start = 0x07fff8b0,
Expand Down Expand Up @@ -189,6 +190,8 @@ static void __init sdk7786_setup(char **cmdline_p)

machine_ops.restart = sdk7786_restart;
pm_power_off = sdk7786_power_off;

register_smp_ops(&shx3_smp_ops);
}

/*
Expand Down
7 changes: 7 additions & 0 deletions arch/sh/boards/mach-x3proto/setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <linux/usb/r8a66597.h>
#include <linux/usb/m66592.h>
#include <asm/ilsel.h>
#include <asm/smp-ops.h>

static struct resource heartbeat_resources[] = {
[0] = {
Expand Down Expand Up @@ -152,7 +153,13 @@ static void __init x3proto_init_irq(void)
__raw_writel(__raw_readl(0xfe410000) | (1 << 21), 0xfe410000);
}

static void __init x3proto_setup(char **cmdline_p)
{
register_smp_ops(&shx3_smp_ops);
}

static struct sh_machine_vector mv_x3proto __initmv = {
.mv_name = "x3proto",
.mv_setup = x3proto_setup,
.mv_init_irq = x3proto_init_irq,
};
3 changes: 3 additions & 0 deletions arch/sh/include/asm/irq.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef __ASM_SH_IRQ_H
#define __ASM_SH_IRQ_H

#include <linux/cpumask.h>
#include <asm/machvec.h>

/*
Expand Down Expand Up @@ -50,6 +51,8 @@ static inline int generic_irq_demux(int irq)
#define irq_demux(irq) sh_mv.mv_irq_demux(irq)

void init_IRQ(void);
void migrate_irqs(void);

asmlinkage int do_IRQ(unsigned int irq, struct pt_regs *regs);

#ifdef CONFIG_IRQSTACKS
Expand Down
4 changes: 4 additions & 0 deletions arch/sh/include/asm/processor.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ struct sh_cpuinfo {
struct tlb_info itlb;
struct tlb_info dtlb;

#ifdef CONFIG_SMP
struct task_struct *idle;
#endif

unsigned long flags;
} __attribute__ ((aligned(L1_CACHE_BYTES)));

Expand Down
51 changes: 51 additions & 0 deletions arch/sh/include/asm/smp-ops.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#ifndef __ASM_SH_SMP_OPS_H
#define __ASM_SH_SMP_OPS_H

struct plat_smp_ops {
void (*smp_setup)(void);
unsigned int (*smp_processor_id)(void);
void (*prepare_cpus)(unsigned int max_cpus);
void (*start_cpu)(unsigned int cpu, unsigned long entry_point);
void (*send_ipi)(unsigned int cpu, unsigned int message);
int (*cpu_disable)(unsigned int cpu);
void (*cpu_die)(unsigned int cpu);
void (*play_dead)(void);
};

extern struct plat_smp_ops *mp_ops;
extern struct plat_smp_ops shx3_smp_ops;

#ifdef CONFIG_SMP

static inline void plat_smp_setup(void)
{
BUG_ON(!mp_ops);
mp_ops->smp_setup();
}

static inline void play_dead(void)
{
mp_ops->play_dead();
}

extern void register_smp_ops(struct plat_smp_ops *ops);

#else

static inline void plat_smp_setup(void)
{
/* UP, nothing to do ... */
}

static inline void register_smp_ops(struct plat_smp_ops *ops)
{
}

static inline void play_dead(void)
{
BUG();
}

#endif /* CONFIG_SMP */

#endif /* __ASM_SH_SMP_OPS_H */
40 changes: 32 additions & 8 deletions arch/sh/include/asm/smp.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@

#include <linux/bitops.h>
#include <linux/cpumask.h>
#include <asm/smp-ops.h>

#ifdef CONFIG_SMP

#include <linux/spinlock.h>
#include <asm/atomic.h>
#include <asm/current.h>
#include <asm/percpu.h>

#define raw_smp_processor_id() (current_thread_info()->cpu)
#define hard_smp_processor_id() plat_smp_processor_id()

/* Map from cpu id to sequential logical cpu number. */
extern int __cpu_number_map[NR_CPUS];
Expand All @@ -30,20 +31,43 @@ enum {
SMP_MSG_NR, /* must be last */
};

DECLARE_PER_CPU(int, cpu_state);

void smp_message_recv(unsigned int msg);
void smp_timer_broadcast(const struct cpumask *mask);

void local_timer_interrupt(void);
void local_timer_setup(unsigned int cpu);

void plat_smp_setup(void);
void plat_prepare_cpus(unsigned int max_cpus);
int plat_smp_processor_id(void);
void plat_start_cpu(unsigned int cpu, unsigned long entry_point);
void plat_send_ipi(unsigned int cpu, unsigned int message);
void local_timer_stop(unsigned int cpu);

void arch_send_call_function_single_ipi(int cpu);
extern void arch_send_call_function_ipi_mask(const struct cpumask *mask);
void arch_send_call_function_ipi_mask(const struct cpumask *mask);

void native_play_dead(void);
void native_cpu_die(unsigned int cpu);
int native_cpu_disable(unsigned int cpu);

#ifdef CONFIG_HOTPLUG_CPU
void play_dead_common(void);
extern int __cpu_disable(void);

static inline void __cpu_die(unsigned int cpu)
{
extern struct plat_smp_ops *mp_ops; /* private */

mp_ops->cpu_die(cpu);
}
#endif

static inline int hard_smp_processor_id(void)
{
extern struct plat_smp_ops *mp_ops; /* private */

if (!mp_ops)
return 0; /* boot CPU */

return mp_ops->smp_processor_id();
}

#else

Expand Down
70 changes: 64 additions & 6 deletions arch/sh/kernel/cpu/sh4a/smp-shx3.c
Original file line number Diff line number Diff line change
@@ -1,24 +1,30 @@
/*
* SH-X3 SMP
*
* Copyright (C) 2007 - 2008 Paul Mundt
* Copyright (C) 2007 - 2010 Paul Mundt
* Copyright (C) 2007 Magnus Damm
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/cpumask.h>
#include <linux/smp.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/cpu.h>
#include <asm/sections.h>

#define STBCR_REG(phys_id) (0xfe400004 | (phys_id << 12))
#define RESET_REG(phys_id) (0xfe400008 | (phys_id << 12))

#define STBCR_MSTP 0x00000001
#define STBCR_RESET 0x00000002
#define STBCR_SLEEP 0x00000004
#define STBCR_LTSLP 0x80000000

static irqreturn_t ipi_interrupt_handler(int irq, void *arg)
Expand All @@ -37,7 +43,7 @@ static irqreturn_t ipi_interrupt_handler(int irq, void *arg)
return IRQ_HANDLED;
}

void __init plat_smp_setup(void)
static void shx3_smp_setup(void)
{
unsigned int cpu = 0;
int i, num;
Expand All @@ -63,7 +69,7 @@ void __init plat_smp_setup(void)
printk(KERN_INFO "Detected %i available secondary CPU(s)\n", num);
}

void __init plat_prepare_cpus(unsigned int max_cpus)
static void shx3_prepare_cpus(unsigned int max_cpus)
{
int i;

Expand All @@ -74,9 +80,12 @@ void __init plat_prepare_cpus(unsigned int max_cpus)
for (i = 0; i < SMP_MSG_NR; i++)
request_irq(104 + i, ipi_interrupt_handler,
IRQF_DISABLED | IRQF_PERCPU, "IPI", (void *)(long)i);

for (i = 0; i < max_cpus; i++)
set_cpu_present(i, true);
}

void plat_start_cpu(unsigned int cpu, unsigned long entry_point)
static void shx3_start_cpu(unsigned int cpu, unsigned long entry_point)
{
if (__in_29bit_mode())
__raw_writel(entry_point, RESET_REG(cpu));
Expand All @@ -93,16 +102,65 @@ void plat_start_cpu(unsigned int cpu, unsigned long entry_point)
__raw_writel(STBCR_RESET | STBCR_LTSLP, STBCR_REG(cpu));
}

int plat_smp_processor_id(void)
static unsigned int shx3_smp_processor_id(void)
{
return __raw_readl(0xff000048); /* CPIDR */
}

void plat_send_ipi(unsigned int cpu, unsigned int message)
static void shx3_send_ipi(unsigned int cpu, unsigned int message)
{
unsigned long addr = 0xfe410070 + (cpu * 4);

BUG_ON(cpu >= 4);

__raw_writel(1 << (message << 2), addr); /* C0INTICI..CnINTICI */
}

static void shx3_update_boot_vector(unsigned int cpu)
{
__raw_writel(STBCR_MSTP, STBCR_REG(cpu));
while (!(__raw_readl(STBCR_REG(cpu)) & STBCR_MSTP))
cpu_relax();
__raw_writel(STBCR_RESET, STBCR_REG(cpu));
}

static int __cpuinit
shx3_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
{
unsigned int cpu = (unsigned int)hcpu;

switch (action) {
case CPU_UP_PREPARE:
shx3_update_boot_vector(cpu);
break;
case CPU_ONLINE:
pr_info("CPU %u is now online\n", cpu);
break;
case CPU_DEAD:
break;
}

return NOTIFY_OK;
}

static struct notifier_block __cpuinitdata shx3_cpu_notifier = {
.notifier_call = shx3_cpu_callback,
};

static int __cpuinit register_shx3_cpu_notifier(void)
{
register_hotcpu_notifier(&shx3_cpu_notifier);
return 0;
}
late_initcall(register_shx3_cpu_notifier);

struct plat_smp_ops shx3_smp_ops = {
.smp_setup = shx3_smp_setup,
.prepare_cpus = shx3_prepare_cpus,
.start_cpu = shx3_start_cpu,
.smp_processor_id = shx3_smp_processor_id,
.send_ipi = shx3_send_ipi,
.cpu_die = native_cpu_die,
.cpu_disable = native_cpu_disable,
.play_dead = native_play_dead,
};
8 changes: 6 additions & 2 deletions arch/sh/kernel/idle.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <asm/pgalloc.h>
#include <asm/system.h>
#include <asm/atomic.h>
#include <asm/smp.h>

void (*pm_idle)(void) = NULL;

Expand Down Expand Up @@ -89,10 +90,13 @@ void cpu_idle(void)
while (1) {
tick_nohz_stop_sched_tick(1);

while (!need_resched() && cpu_online(cpu)) {
while (!need_resched()) {
check_pgt_cache();
rmb();

if (cpu_is_offline(cpu))
play_dead();

local_irq_disable();
/* Don't trace irqs off for idle */
stop_critical_timings();
Expand Down Expand Up @@ -133,7 +137,7 @@ static void do_nothing(void *unused)
void stop_this_cpu(void *unused)
{
local_irq_disable();
cpu_clear(smp_processor_id(), cpu_online_map);
set_cpu_online(smp_processor_id(), false);

for (;;)
cpu_sleep();
Expand Down
Loading

0 comments on commit 54b41b9

Please sign in to comment.