提交 300459d5 编写于 作者: H Huacai Chen 提交者: Ralf Baechle

MIPS: Loongson 3: Add Loongson-3 SMP support

IPI registers of Loongson-3 include IPI_SET, IPI_CLEAR, IPI_STATUS,
IPI_EN and IPI_MAILBOX_BUF. Each bit of IPI_STATUS indicate a type of
IPI and IPI_EN indicate whether the IPI is enabled. The sender write 1
to IPI_SET bits generate IPIs in IPI_STATUS, and receiver write 1 to
bits of IPI_CLEAR to clear IPIs. IPI_MAILBOX_BUF are used to deliver
more information about IPIs.

Why we change code in arch/mips/loongson/common/setup.c?

If without this change, when SMP configured, system cannot boot since
it hang at printk() in cgroup_init_early(). The root cause is:

console_trylock()
  \-->down_trylock(&console_sem)
    \-->raw_spin_unlock_irqrestore(&sem->lock, flags)
      \-->_raw_spin_unlock_irqrestore()(SMP/UP have different versions)
        \-->__raw_spin_unlock_irqrestore()  (following is the SMP case)
          \-->do_raw_spin_unlock()
            \-->arch_spin_unlock()
              \-->nudge_writes()
                \-->mb()
                  \-->wbflush()
                    \-->__wbflush()

In previous code __wbflush() is initialized in plat_mem_setup(), but
cgroup_init_early() is called before plat_mem_setup(). Therefore, In
this patch we make changes to avoid boot failure.
Signed-off-by: NHuacai Chen <chenhc@lemote.com>
Signed-off-by: NHongliang Tao <taohl@lemote.com>
Signed-off-by: NHua Yan <yanh@lemote.com>
Tested-by: NAlex Smith <alex.smith@imgtec.com>
Reviewed-by: NAlex Smith <alex.smith@imgtec.com>
Cc: John Crispin <john@phrozen.org>
Cc: Steven J. Hill <Steven.Hill@imgtec.com>
Cc: Aurelien Jarno <aurelien@aurel32.net>
Cc: linux-mips@linux-mips.org
Cc: Fuxin Zhang <zhangfx@lemote.com>
Cc: Zhangjin Wu <wuzhangjin@gmail.com>
Patchwork: https://patchwork.linux-mips.org/patch/6638Signed-off-by: NRalf Baechle <ralf@linux-mips.org>
上级 0e476d91
master alk-4.19.24 alk-4.19.30 alk-4.19.34 alk-4.19.36 alk-4.19.43 alk-4.19.48 alk-4.19.57 ck-4.19.67 ck-4.19.81 ck-4.19.91 github/fork/deepanshu1422/fix-typo-in-comment github/fork/haosdent/fix-typo linux-next v4.19.91 v4.19.90 v4.19.89 v4.19.88 v4.19.87 v4.19.86 v4.19.85 v4.19.84 v4.19.83 v4.19.82 v4.19.81 v4.19.80 v4.19.79 v4.19.78 v4.19.77 v4.19.76 v4.19.75 v4.19.74 v4.19.73 v4.19.72 v4.19.71 v4.19.70 v4.19.69 v4.19.68 v4.19.67 v4.19.66 v4.19.65 v4.19.64 v4.19.63 v4.19.62 v4.19.61 v4.19.60 v4.19.59 v4.19.58 v4.19.57 v4.19.56 v4.19.55 v4.19.54 v4.19.53 v4.19.52 v4.19.51 v4.19.50 v4.19.49 v4.19.48 v4.19.47 v4.19.46 v4.19.45 v4.19.44 v4.19.43 v4.19.42 v4.19.41 v4.19.40 v4.19.39 v4.19.38 v4.19.37 v4.19.36 v4.19.35 v4.19.34 v4.19.33 v4.19.32 v4.19.31 v4.19.30 v4.19.29 v4.19.28 v4.19.27 v4.19.26 v4.19.25 v4.19.24 v4.19.23 v4.19.22 v4.19.21 v4.19.20 v4.19.19 v4.19.18 v4.19.17 v4.19.16 v4.19.15 v4.19.14 v4.19.13 v4.19.12 v4.19.11 v4.19.10 v4.19.9 v4.19.8 v4.19.7 v4.19.6 v4.19.5 v4.19.4 v4.19.3 v4.19.2 v4.19.1 v4.19 v4.19-rc8 v4.19-rc7 v4.19-rc6 v4.19-rc5 v4.19-rc4 v4.19-rc3 v4.19-rc2 v4.19-rc1 ck-release-21 ck-release-20 ck-release-19.2 ck-release-19.1 ck-release-19 ck-release-18 ck-release-17.2 ck-release-17.1 ck-release-17 ck-release-16 ck-release-15.1 ck-release-15 ck-release-14 ck-release-13.2 ck-release-13 ck-release-12 ck-release-11 ck-release-10 ck-release-9 ck-release-7 alk-release-15 alk-release-14 alk-release-13.2 alk-release-13 alk-release-12 alk-release-11 alk-release-10 alk-release-9 alk-release-7
无相关合并请求
......@@ -37,5 +37,7 @@
#endif
extern void loongson3_ipi_interrupt(struct pt_regs *regs);
#include_next <irq.h>
#endif /* __ASM_MACH_LOONGSON_IRQ_H_ */
......@@ -27,6 +27,7 @@ extern void mach_prepare_shutdown(void);
/* environment arguments from bootloader */
extern u32 cpu_clock_freq;
extern u32 memsize, highmemsize;
extern struct plat_smp_ops loongson3_smp_ops;
/* loongson-specific command line, env and memory initialization */
extern void __init prom_init_memory(void);
......
......@@ -9,6 +9,7 @@
*/
#include <linux/bootmem.h>
#include <asm/smp-ops.h>
#include <loongson.h>
......@@ -33,6 +34,7 @@ void __init prom_init(void)
/*init the uart base address */
prom_init_uart_base();
register_smp_ops(&loongson3_smp_ops);
}
void __init prom_free_prom_memory(void)
......
......@@ -18,9 +18,6 @@
#include <linux/screen_info.h>
#endif
void (*__wbflush)(void);
EXPORT_SYMBOL(__wbflush);
static void wbflush_loongson(void)
{
asm(".set\tpush\n\t"
......@@ -32,10 +29,11 @@ static void wbflush_loongson(void)
".set mips0\n\t");
}
void (*__wbflush)(void) = wbflush_loongson;
EXPORT_SYMBOL(__wbflush);
void __init plat_mem_setup(void)
{
__wbflush = wbflush_loongson;
#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
conswitchp = &vga_con;
......
......@@ -2,3 +2,5 @@
# Makefile for Loongson-3 family machines
#
obj-y += irq.o
obj-$(CONFIG_SMP) += smp.o
......@@ -26,6 +26,10 @@ void mach_irq_dispatch(unsigned int pending)
{
if (pending & CAUSEF_IP7)
do_IRQ(LOONGSON_TIMER_IRQ);
#if defined(CONFIG_SMP)
else if (pending & CAUSEF_IP6)
loongson3_ipi_interrupt(NULL);
#endif
else if (pending & CAUSEF_IP3)
ht_irqdispatch();
else if (pending & CAUSEF_IP2)
......@@ -45,10 +49,26 @@ static inline void mask_loongson_irq(struct irq_data *d)
{
clear_c0_status(0x100 << (d->irq - MIPS_CPU_IRQ_BASE));
irq_disable_hazard();
/* Workaround: UART IRQ may deliver to any core */
if (d->irq == LOONGSON_UART_IRQ) {
int cpu = smp_processor_id();
LOONGSON_INT_ROUTER_INTENCLR = 1 << 10;
LOONGSON_INT_ROUTER_LPC = 0x10 + (1<<cpu);
}
}
static inline void unmask_loongson_irq(struct irq_data *d)
{
/* Workaround: UART IRQ may deliver to any core */
if (d->irq == LOONGSON_UART_IRQ) {
int cpu = smp_processor_id();
LOONGSON_INT_ROUTER_INTENSET = 1 << 10;
LOONGSON_INT_ROUTER_LPC = 0x10 + (1<<cpu);
}
set_c0_status(0x100 << (d->irq - MIPS_CPU_IRQ_BASE));
irq_enable_hazard();
}
......
/*
* Copyright (C) 2010, 2011, 2012, Lemote, Inc.
* Author: Chen Huacai, chenhc@lemote.com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/init.h>
#include <linux/cpu.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <linux/cpufreq.h>
#include <asm/processor.h>
#include <asm/time.h>
#include <asm/clock.h>
#include <asm/tlbflush.h>
#include <loongson.h>
#include "smp.h"
/* read a 32bit value from ipi register */
#define loongson3_ipi_read32(addr) readl(addr)
/* read a 64bit value from ipi register */
#define loongson3_ipi_read64(addr) readq(addr)
/* write a 32bit value to ipi register */
#define loongson3_ipi_write32(action, addr) \
do { \
writel(action, addr); \
__wbflush(); \
} while (0)
/* write a 64bit value to ipi register */
#define loongson3_ipi_write64(action, addr) \
do { \
writeq(action, addr); \
__wbflush(); \
} while (0)
static void *ipi_set0_regs[] = {
(void *)(SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + SET0),
(void *)(SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + SET0),
(void *)(SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + SET0),
(void *)(SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + SET0),
(void *)(SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + SET0),
(void *)(SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + SET0),
(void *)(SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + SET0),
(void *)(SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + SET0),
(void *)(SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + SET0),
(void *)(SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + SET0),
(void *)(SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + SET0),
(void *)(SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + SET0),
(void *)(SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + SET0),
(void *)(SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + SET0),
(void *)(SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + SET0),
(void *)(SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + SET0),
};
static void *ipi_clear0_regs[] = {
(void *)(SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + CLEAR0),
(void *)(SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + CLEAR0),
(void *)(SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + CLEAR0),
(void *)(SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + CLEAR0),
(void *)(SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + CLEAR0),
(void *)(SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + CLEAR0),
(void *)(SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + CLEAR0),
(void *)(SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + CLEAR0),
(void *)(SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + CLEAR0),
(void *)(SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + CLEAR0),
(void *)(SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + CLEAR0),
(void *)(SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + CLEAR0),
(void *)(SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + CLEAR0),
(void *)(SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + CLEAR0),
(void *)(SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + CLEAR0),
(void *)(SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + CLEAR0),
};
static void *ipi_status0_regs[] = {
(void *)(SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + STATUS0),
(void *)(SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + STATUS0),
(void *)(SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + STATUS0),
(void *)(SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + STATUS0),
(void *)(SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + STATUS0),
(void *)(SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + STATUS0),
(void *)(SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + STATUS0),
(void *)(SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + STATUS0),
(void *)(SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + STATUS0),
(void *)(SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + STATUS0),
(void *)(SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + STATUS0),
(void *)(SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + STATUS0),
(void *)(SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + STATUS0),
(void *)(SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + STATUS0),
(void *)(SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + STATUS0),
(void *)(SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + STATUS0),
};
static void *ipi_en0_regs[] = {
(void *)(SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + EN0),
(void *)(SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + EN0),
(void *)(SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + EN0),
(void *)(SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + EN0),
(void *)(SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + EN0),
(void *)(SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + EN0),
(void *)(SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + EN0),
(void *)(SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + EN0),
(void *)(SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + EN0),
(void *)(SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + EN0),
(void *)(SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + EN0),
(void *)(SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + EN0),
(void *)(SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + EN0),
(void *)(SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + EN0),
(void *)(SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + EN0),
(void *)(SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + EN0),
};
static void *ipi_mailbox_buf[] = {
(void *)(SMP_CORE_GROUP0_BASE + SMP_CORE0_OFFSET + BUF),
(void *)(SMP_CORE_GROUP0_BASE + SMP_CORE1_OFFSET + BUF),
(void *)(SMP_CORE_GROUP0_BASE + SMP_CORE2_OFFSET + BUF),
(void *)(SMP_CORE_GROUP0_BASE + SMP_CORE3_OFFSET + BUF),
(void *)(SMP_CORE_GROUP1_BASE + SMP_CORE0_OFFSET + BUF),
(void *)(SMP_CORE_GROUP1_BASE + SMP_CORE1_OFFSET + BUF),
(void *)(SMP_CORE_GROUP1_BASE + SMP_CORE2_OFFSET + BUF),
(void *)(SMP_CORE_GROUP1_BASE + SMP_CORE3_OFFSET + BUF),
(void *)(SMP_CORE_GROUP2_BASE + SMP_CORE0_OFFSET + BUF),
(void *)(SMP_CORE_GROUP2_BASE + SMP_CORE1_OFFSET + BUF),
(void *)(SMP_CORE_GROUP2_BASE + SMP_CORE2_OFFSET + BUF),
(void *)(SMP_CORE_GROUP2_BASE + SMP_CORE3_OFFSET + BUF),
(void *)(SMP_CORE_GROUP3_BASE + SMP_CORE0_OFFSET + BUF),
(void *)(SMP_CORE_GROUP3_BASE + SMP_CORE1_OFFSET + BUF),
(void *)(SMP_CORE_GROUP3_BASE + SMP_CORE2_OFFSET + BUF),
(void *)(SMP_CORE_GROUP3_BASE + SMP_CORE3_OFFSET + BUF),
};
/*
* Simple enough, just poke the appropriate ipi register
*/
static void loongson3_send_ipi_single(int cpu, unsigned int action)
{
loongson3_ipi_write32((u32)action, ipi_set0_regs[cpu]);
}
static void
loongson3_send_ipi_mask(const struct cpumask *mask, unsigned int action)
{
unsigned int i;
for_each_cpu(i, mask)
loongson3_ipi_write32((u32)action, ipi_set0_regs[i]);
}
void loongson3_ipi_interrupt(struct pt_regs *regs)
{
int cpu = smp_processor_id();
unsigned int action;
/* Load the ipi register to figure out what we're supposed to do */
action = loongson3_ipi_read32(ipi_status0_regs[cpu]);
/* Clear the ipi register to clear the interrupt */
loongson3_ipi_write32((u32)action, ipi_clear0_regs[cpu]);
if (action & SMP_RESCHEDULE_YOURSELF)
scheduler_ipi();
if (action & SMP_CALL_FUNCTION)
smp_call_function_interrupt();
}
/*
* SMP init and finish on secondary CPUs
*/
static void loongson3_init_secondary(void)
{
int i;
unsigned int imask = STATUSF_IP7 | STATUSF_IP6 |
STATUSF_IP3 | STATUSF_IP2;
/* Set interrupt mask, but don't enable */
change_c0_status(ST0_IM, imask);
for (i = 0; i < loongson_sysconf.nr_cpus; i++)
loongson3_ipi_write32(0xffffffff, ipi_en0_regs[i]);
}
static void loongson3_smp_finish(void)
{
write_c0_compare(read_c0_count() + mips_hpt_frequency/HZ);
local_irq_enable();
loongson3_ipi_write64(0,
(void *)(ipi_mailbox_buf[smp_processor_id()]+0x0));
pr_info("CPU#%d finished, CP0_ST=%x\n",
smp_processor_id(), read_c0_status());
}
static void __init loongson3_smp_setup(void)
{
int i, num;
init_cpu_possible(cpu_none_mask);
set_cpu_possible(0, true);
__cpu_number_map[0] = 0;
__cpu_logical_map[0] = 0;
/* For unified kernel, NR_CPUS is the maximum possible value,
* loongson_sysconf.nr_cpus is the really present value */
for (i = 1, num = 0; i < loongson_sysconf.nr_cpus; i++) {
set_cpu_possible(i, true);
__cpu_number_map[i] = ++num;
__cpu_logical_map[num] = i;
}
pr_info("Detected %i available secondary CPU(s)\n", num);
}
static void __init loongson3_prepare_cpus(unsigned int max_cpus)
{
}
/*
* Setup the PC, SP, and GP of a secondary processor and start it runing!
*/
static void loongson3_boot_secondary(int cpu, struct task_struct *idle)
{
unsigned long startargs[4];
pr_info("Booting CPU#%d...\n", cpu);
/* startargs[] are initial PC, SP and GP for secondary CPU */
startargs[0] = (unsigned long)&smp_bootstrap;
startargs[1] = (unsigned long)__KSTK_TOS(idle);
startargs[2] = (unsigned long)task_thread_info(idle);
startargs[3] = 0;
pr_debug("CPU#%d, func_pc=%lx, sp=%lx, gp=%lx\n",
cpu, startargs[0], startargs[1], startargs[2]);
loongson3_ipi_write64(startargs[3], (void *)(ipi_mailbox_buf[cpu]+0x18));
loongson3_ipi_write64(startargs[2], (void *)(ipi_mailbox_buf[cpu]+0x10));
loongson3_ipi_write64(startargs[1], (void *)(ipi_mailbox_buf[cpu]+0x8));
loongson3_ipi_write64(startargs[0], (void *)(ipi_mailbox_buf[cpu]+0x0));
}
/*
* Final cleanup after all secondaries booted
*/
static void __init loongson3_cpus_done(void)
{
}
struct plat_smp_ops loongson3_smp_ops = {
.send_ipi_single = loongson3_send_ipi_single,
.send_ipi_mask = loongson3_send_ipi_mask,
.init_secondary = loongson3_init_secondary,
.smp_finish = loongson3_smp_finish,
.cpus_done = loongson3_cpus_done,
.boot_secondary = loongson3_boot_secondary,
.smp_setup = loongson3_smp_setup,
.prepare_cpus = loongson3_prepare_cpus,
};
#ifndef __LOONGSON_SMP_H_
#define __LOONGSON_SMP_H_
/* for Loongson-3A smp support */
/* 4 groups(nodes) in maximum in numa case */
#define SMP_CORE_GROUP0_BASE 0x900000003ff01000
#define SMP_CORE_GROUP1_BASE 0x900010003ff01000
#define SMP_CORE_GROUP2_BASE 0x900020003ff01000
#define SMP_CORE_GROUP3_BASE 0x900030003ff01000
/* 4 cores in each group(node) */
#define SMP_CORE0_OFFSET 0x000
#define SMP_CORE1_OFFSET 0x100
#define SMP_CORE2_OFFSET 0x200
#define SMP_CORE3_OFFSET 0x300
/* ipi registers offsets */
#define STATUS0 0x00
#define EN0 0x04
#define SET0 0x08
#define CLEAR0 0x0c
#define STATUS1 0x10
#define MASK1 0x14
#define SET1 0x18
#define CLEAR1 0x1c
#define BUF 0x20
#endif
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册
反馈
建议
客服 返回
顶部