diff options
| -rw-r--r-- | arch/mips/include/asm/mach-paravirt/cpu-feature-overrides.h | 36 | ||||
| -rw-r--r-- | arch/mips/include/asm/mach-paravirt/irq.h | 19 | ||||
| -rw-r--r-- | arch/mips/include/asm/mach-paravirt/kernel-entry-init.h | 50 | ||||
| -rw-r--r-- | arch/mips/include/asm/mach-paravirt/war.h | 25 | ||||
| -rw-r--r-- | arch/mips/mm/tlbex.c | 8 | ||||
| -rw-r--r-- | arch/mips/paravirt/Makefile | 14 | ||||
| -rw-r--r-- | arch/mips/paravirt/Platform | 8 | ||||
| -rw-r--r-- | arch/mips/paravirt/paravirt-irq.c | 368 | ||||
| -rw-r--r-- | arch/mips/paravirt/paravirt-smp.c | 148 | ||||
| -rw-r--r-- | arch/mips/paravirt/serial.c | 40 | ||||
| -rw-r--r-- | arch/mips/paravirt/setup.c | 67 | 
11 files changed, 777 insertions, 6 deletions
| diff --git a/arch/mips/include/asm/mach-paravirt/cpu-feature-overrides.h b/arch/mips/include/asm/mach-paravirt/cpu-feature-overrides.h new file mode 100644 index 000000000000..725e1ed83f6a --- /dev/null +++ b/arch/mips/include/asm/mach-paravirt/cpu-feature-overrides.h @@ -0,0 +1,36 @@ +/* + * 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. + * + * Copyright (C) 2013 Cavium, Inc. + */ +#ifndef __ASM_MACH_PARAVIRT_CPU_FEATURE_OVERRIDES_H +#define __ASM_MACH_PARAVIRT_CPU_FEATURE_OVERRIDES_H + +#define cpu_has_4kex		1 +#define cpu_has_3k_cache	0 +#define cpu_has_tx39_cache	0 +#define cpu_has_counter		1 +#define cpu_has_llsc		1 +/* + * We Disable LL/SC on non SMP systems as it is faster to disable + * interrupts for atomic access than a LL/SC. + */ +#ifdef CONFIG_SMP +# define kernel_uses_llsc	1 +#else +# define kernel_uses_llsc	0 +#endif + +#ifdef CONFIG_CPU_CAVIUM_OCTEON +#define cpu_dcache_line_size()	128 +#define cpu_icache_line_size()	128 +#define cpu_has_octeon_cache	1 +#define cpu_has_4k_cache	0 +#else +#define cpu_has_octeon_cache	0 +#define cpu_has_4k_cache	1 +#endif + +#endif /* __ASM_MACH_PARAVIRT_CPU_FEATURE_OVERRIDES_H */ diff --git a/arch/mips/include/asm/mach-paravirt/irq.h b/arch/mips/include/asm/mach-paravirt/irq.h new file mode 100644 index 000000000000..9b4d35eca977 --- /dev/null +++ b/arch/mips/include/asm/mach-paravirt/irq.h @@ -0,0 +1,19 @@ +/* + * 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. + * + * Copyright (C) 2013 Cavium, Inc. + */ +#ifndef __ASM_MACH_PARAVIRT_IRQ_H__ +#define  __ASM_MACH_PARAVIRT_IRQ_H__ + +#define NR_IRQS 64 +#define MIPS_CPU_IRQ_BASE 1 + +#define MIPS_IRQ_PCIA (MIPS_CPU_IRQ_BASE + 8) + +#define MIPS_IRQ_MBOX0 (MIPS_CPU_IRQ_BASE + 32) +#define MIPS_IRQ_MBOX1 (MIPS_CPU_IRQ_BASE + 33) + +#endif /* __ASM_MACH_PARAVIRT_IRQ_H__ */ diff --git a/arch/mips/include/asm/mach-paravirt/kernel-entry-init.h b/arch/mips/include/asm/mach-paravirt/kernel-entry-init.h new file mode 100644 index 000000000000..2f82bfa3a773 --- /dev/null +++ b/arch/mips/include/asm/mach-paravirt/kernel-entry-init.h @@ -0,0 +1,50 @@ +/* + * 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. + * + * Copyright (C) 2013 Cavium, Inc + */ +#ifndef __ASM_MACH_PARAVIRT_KERNEL_ENTRY_H +#define __ASM_MACH_PARAVIRT_KERNEL_ENTRY_H + +#define CP0_EBASE $15, 1 + +	.macro  kernel_entry_setup +	mfc0	t0, CP0_EBASE +	andi	t0, t0, 0x3ff		# CPUNum +	beqz	t0, 1f +	# CPUs other than zero goto smp_bootstrap +	j	smp_bootstrap + +1: +	.endm + +/* + * Do SMP slave processor setup necessary before we can safely execute + * C code. + */ +	.macro  smp_slave_setup +	mfc0	t0, CP0_EBASE +	andi	t0, t0, 0x3ff		# CPUNum +	slti	t1, t0, NR_CPUS +	bnez	t1, 1f +2: +	di +	wait +	b	2b			# Unknown CPU, loop forever. +1: +	PTR_LA	t1, paravirt_smp_sp +	PTR_SLL	t0, PTR_SCALESHIFT +	PTR_ADDU t1, t1, t0 +3: +	PTR_L	sp, 0(t1) +	beqz	sp, 3b			# Spin until told to proceed. + +	PTR_LA	t1, paravirt_smp_gp +	PTR_ADDU t1, t1, t0 +	sync +	PTR_L	gp, 0(t1) +	.endm + +#endif /* __ASM_MACH_PARAVIRT_KERNEL_ENTRY_H */ diff --git a/arch/mips/include/asm/mach-paravirt/war.h b/arch/mips/include/asm/mach-paravirt/war.h new file mode 100644 index 000000000000..36d3afb98451 --- /dev/null +++ b/arch/mips/include/asm/mach-paravirt/war.h @@ -0,0 +1,25 @@ +/* + * 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. + * + * Copyright (C) 2002, 2004, 2007 by Ralf Baechle <ralf@linux-mips.org> + * Copyright (C) 2013 Cavium Networks <support@caviumnetworks.com> + */ +#ifndef __ASM_MIPS_MACH_PARAVIRT_WAR_H +#define __ASM_MIPS_MACH_PARAVIRT_WAR_H + +#define R4600_V1_INDEX_ICACHEOP_WAR	0 +#define R4600_V1_HIT_CACHEOP_WAR	0 +#define R4600_V2_HIT_CACHEOP_WAR	0 +#define R5432_CP0_INTERRUPT_WAR		0 +#define BCM1250_M3_WAR			0 +#define SIBYTE_1956_WAR			0 +#define MIPS4K_ICACHE_REFILL_WAR	0 +#define MIPS_CACHE_SYNC_WAR		0 +#define TX49XX_ICACHE_INDEX_INV_WAR	0 +#define ICACHE_REFILLS_WORKAROUND_WAR	0 +#define R10000_LLSC_WAR			0 +#define MIPS34K_MISSED_ITLB_WAR		0 + +#endif /* __ASM_MIPS_MACH_PARAVIRT_WAR_H */ diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c index af91f3ed1a82..e80e10bafc83 100644 --- a/arch/mips/mm/tlbex.c +++ b/arch/mips/mm/tlbex.c @@ -1250,17 +1250,13 @@ static void build_r4000_tlb_refill_handler(void)  	unsigned int final_len;  	struct mips_huge_tlb_info htlb_info __maybe_unused;  	enum vmalloc64_mode vmalloc_mode __maybe_unused; -#ifdef CONFIG_64BIT -	bool is64bit = true; -#else -	bool is64bit = false; -#endif +  	memset(tlb_handler, 0, sizeof(tlb_handler));  	memset(labels, 0, sizeof(labels));  	memset(relocs, 0, sizeof(relocs));  	memset(final_handler, 0, sizeof(final_handler)); -	if (is64bit && (scratch_reg >= 0 || scratchpad_available()) && use_bbit_insns()) { +	if (IS_ENABLED(CONFIG_64BIT) && (scratch_reg >= 0 || scratchpad_available()) && use_bbit_insns()) {  		htlb_info = build_fast_tlb_refill_handler(&p, &l, &r, K0, K1,  							  scratch_reg);  		vmalloc_mode = refill_scratch; diff --git a/arch/mips/paravirt/Makefile b/arch/mips/paravirt/Makefile new file mode 100644 index 000000000000..5023af733a35 --- /dev/null +++ b/arch/mips/paravirt/Makefile @@ -0,0 +1,14 @@ +# +# Makefile for MIPS para-virtualized specific kernel interface routines +# under Linux. +# +# 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. +# +# Copyright (C) 2013 Cavium, Inc. +# + +obj-y := setup.o serial.o paravirt-irq.o + +obj-$(CONFIG_SMP)		+= paravirt-smp.o diff --git a/arch/mips/paravirt/Platform b/arch/mips/paravirt/Platform new file mode 100644 index 000000000000..7e76ef25ea17 --- /dev/null +++ b/arch/mips/paravirt/Platform @@ -0,0 +1,8 @@ +# +# Generic para-virtualized guest. +# +platform-$(CONFIG_MIPS_PARAVIRT)	+= paravirt/ +cflags-$(CONFIG_MIPS_PARAVIRT)		+=				\ +		-I$(srctree)/arch/mips/include/asm/mach-paravirt + +load-$(CONFIG_MIPS_PARAVIRT)	= 0xffffffff80010000 diff --git a/arch/mips/paravirt/paravirt-irq.c b/arch/mips/paravirt/paravirt-irq.c new file mode 100644 index 000000000000..8987b06c9de9 --- /dev/null +++ b/arch/mips/paravirt/paravirt-irq.c @@ -0,0 +1,368 @@ +/* + * 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. + * + * Copyright (C) 2013 Cavium, Inc. + */ + +#include <linux/interrupt.h> +#include <linux/cpumask.h> +#include <linux/kernel.h> +#include <linux/mutex.h> + +#include <asm/io.h> + +#define MBOX_BITS_PER_CPU 2 + +static int cpunum_for_cpu(int cpu) +{ +#ifdef CONFIG_SMP +	return cpu_logical_map(cpu); +#else +	return get_ebase_cpunum(); +#endif +} + +struct core_chip_data { +	struct mutex core_irq_mutex; +	bool current_en; +	bool desired_en; +	u8 bit; +}; + +static struct core_chip_data irq_core_chip_data[8]; + +static void irq_core_ack(struct irq_data *data) +{ +	struct core_chip_data *cd = irq_data_get_irq_chip_data(data); +	unsigned int bit = cd->bit; + +	/* +	 * We don't need to disable IRQs to make these atomic since +	 * they are already disabled earlier in the low level +	 * interrupt code. +	 */ +	clear_c0_status(0x100 << bit); +	/* The two user interrupts must be cleared manually. */ +	if (bit < 2) +		clear_c0_cause(0x100 << bit); +} + +static void irq_core_eoi(struct irq_data *data) +{ +	struct core_chip_data *cd = irq_data_get_irq_chip_data(data); + +	/* +	 * We don't need to disable IRQs to make these atomic since +	 * they are already disabled earlier in the low level +	 * interrupt code. +	 */ +	set_c0_status(0x100 << cd->bit); +} + +static void irq_core_set_enable_local(void *arg) +{ +	struct irq_data *data = arg; +	struct core_chip_data *cd = irq_data_get_irq_chip_data(data); +	unsigned int mask = 0x100 << cd->bit; + +	/* +	 * Interrupts are already disabled, so these are atomic. +	 */ +	if (cd->desired_en) +		set_c0_status(mask); +	else +		clear_c0_status(mask); + +} + +static void irq_core_disable(struct irq_data *data) +{ +	struct core_chip_data *cd = irq_data_get_irq_chip_data(data); +	cd->desired_en = false; +} + +static void irq_core_enable(struct irq_data *data) +{ +	struct core_chip_data *cd = irq_data_get_irq_chip_data(data); +	cd->desired_en = true; +} + +static void irq_core_bus_lock(struct irq_data *data) +{ +	struct core_chip_data *cd = irq_data_get_irq_chip_data(data); + +	mutex_lock(&cd->core_irq_mutex); +} + +static void irq_core_bus_sync_unlock(struct irq_data *data) +{ +	struct core_chip_data *cd = irq_data_get_irq_chip_data(data); + +	if (cd->desired_en != cd->current_en) { +		on_each_cpu(irq_core_set_enable_local, data, 1); +		cd->current_en = cd->desired_en; +	} + +	mutex_unlock(&cd->core_irq_mutex); +} + +static struct irq_chip irq_chip_core = { +	.name = "Core", +	.irq_enable = irq_core_enable, +	.irq_disable = irq_core_disable, +	.irq_ack = irq_core_ack, +	.irq_eoi = irq_core_eoi, +	.irq_bus_lock = irq_core_bus_lock, +	.irq_bus_sync_unlock = irq_core_bus_sync_unlock, + +	.irq_cpu_online = irq_core_eoi, +	.irq_cpu_offline = irq_core_ack, +	.flags = IRQCHIP_ONOFFLINE_ENABLED, +}; + +static void __init irq_init_core(void) +{ +	int i; +	int irq; +	struct core_chip_data *cd; + +	/* Start with a clean slate */ +	clear_c0_status(ST0_IM); +	clear_c0_cause(CAUSEF_IP0 | CAUSEF_IP1); + +	for (i = 0; i < ARRAY_SIZE(irq_core_chip_data); i++) { +		cd = irq_core_chip_data + i; +		cd->current_en = false; +		cd->desired_en = false; +		cd->bit = i; +		mutex_init(&cd->core_irq_mutex); + +		irq = MIPS_CPU_IRQ_BASE + i; + +		switch (i) { +		case 0: /* SW0 */ +		case 1: /* SW1 */ +		case 5: /* IP5 */ +		case 6: /* IP6 */ +		case 7: /* IP7 */ +			irq_set_chip_data(irq, cd); +			irq_set_chip_and_handler(irq, &irq_chip_core, +						 handle_percpu_irq); +			break; +		default: +			break; +		} +	} +} + +static void __iomem *mips_irq_chip; +#define MIPS_IRQ_CHIP_NUM_BITS 0 +#define MIPS_IRQ_CHIP_REGS 8 + +static int mips_irq_cpu_stride; +static int mips_irq_chip_reg_raw; +static int mips_irq_chip_reg_src; +static int mips_irq_chip_reg_en; +static int mips_irq_chip_reg_raw_w1s; +static int mips_irq_chip_reg_raw_w1c; +static int mips_irq_chip_reg_en_w1s; +static int mips_irq_chip_reg_en_w1c; + +static void irq_pci_enable(struct irq_data *data) +{ +	u32 mask = 1u << data->irq; + +	__raw_writel(mask, mips_irq_chip + mips_irq_chip_reg_en_w1s); +} + +static void irq_pci_disable(struct irq_data *data) +{ +	u32 mask = 1u << data->irq; + +	__raw_writel(mask, mips_irq_chip + mips_irq_chip_reg_en_w1c); +} + +static void irq_pci_ack(struct irq_data *data) +{ +} + +static void irq_pci_mask(struct irq_data *data) +{ +	u32 mask = 1u << data->irq; + +	__raw_writel(mask, mips_irq_chip + mips_irq_chip_reg_en_w1c); +} + +static void irq_pci_unmask(struct irq_data *data) +{ +	u32 mask = 1u << data->irq; + +	__raw_writel(mask, mips_irq_chip + mips_irq_chip_reg_en_w1s); +} + +static struct irq_chip irq_chip_pci = { +	.name = "PCI", +	.irq_enable = irq_pci_enable, +	.irq_disable = irq_pci_disable, +	.irq_ack = irq_pci_ack, +	.irq_mask = irq_pci_mask, +	.irq_unmask = irq_pci_unmask, +}; + +static void irq_mbox_all(struct irq_data *data,  void __iomem *base) +{ +	int cpu; +	unsigned int mbox = data->irq - MIPS_IRQ_MBOX0; +	u32 mask; + +	WARN_ON(mbox >= MBOX_BITS_PER_CPU); + +	for_each_online_cpu(cpu) { +		unsigned int cpuid = cpunum_for_cpu(cpu); +		mask = 1 << (cpuid * MBOX_BITS_PER_CPU + mbox); +		__raw_writel(mask, base + (cpuid * mips_irq_cpu_stride)); +	} +} + +static void irq_mbox_enable(struct irq_data *data) +{ +	irq_mbox_all(data, mips_irq_chip + mips_irq_chip_reg_en_w1s + sizeof(u32)); +} + +static void irq_mbox_disable(struct irq_data *data) +{ +	irq_mbox_all(data, mips_irq_chip + mips_irq_chip_reg_en_w1c + sizeof(u32)); +} + +static void irq_mbox_ack(struct irq_data *data) +{ +	u32 mask; +	unsigned int mbox = data->irq - MIPS_IRQ_MBOX0; + +	WARN_ON(mbox >= MBOX_BITS_PER_CPU); + +	mask = 1 << (get_ebase_cpunum() * MBOX_BITS_PER_CPU + mbox); +	__raw_writel(mask, mips_irq_chip + mips_irq_chip_reg_raw_w1c + sizeof(u32)); +} + +void irq_mbox_ipi(int cpu, unsigned int actions) +{ +	unsigned int cpuid = cpunum_for_cpu(cpu); +	u32 mask; + +	WARN_ON(actions >= (1 << MBOX_BITS_PER_CPU)); + +	mask = actions << (cpuid * MBOX_BITS_PER_CPU); +	__raw_writel(mask, mips_irq_chip + mips_irq_chip_reg_raw_w1s + sizeof(u32)); +} + +static void irq_mbox_cpu_onoffline(struct irq_data *data,  void __iomem *base) +{ +	unsigned int mbox = data->irq - MIPS_IRQ_MBOX0; +	unsigned int cpuid = get_ebase_cpunum(); +	u32 mask; + +	WARN_ON(mbox >= MBOX_BITS_PER_CPU); + +	mask = 1 << (cpuid * MBOX_BITS_PER_CPU + mbox); +	__raw_writel(mask, base + (cpuid * mips_irq_cpu_stride)); + +} + +static void irq_mbox_cpu_online(struct irq_data *data) +{ +	irq_mbox_cpu_onoffline(data, mips_irq_chip + mips_irq_chip_reg_en_w1s + sizeof(u32)); +} + +static void irq_mbox_cpu_offline(struct irq_data *data) +{ +	irq_mbox_cpu_onoffline(data, mips_irq_chip + mips_irq_chip_reg_en_w1c + sizeof(u32)); +} + +static struct irq_chip irq_chip_mbox = { +	.name = "MBOX", +	.irq_enable = irq_mbox_enable, +	.irq_disable = irq_mbox_disable, +	.irq_ack = irq_mbox_ack, +	.irq_cpu_online = irq_mbox_cpu_online, +	.irq_cpu_offline = irq_mbox_cpu_offline, +	.flags = IRQCHIP_ONOFFLINE_ENABLED, +}; + +static void __init irq_pci_init(void) +{ +	int i, stride; +	u32 num_bits; + +	mips_irq_chip = ioremap(0x1e010000, 4096); + +	num_bits = __raw_readl(mips_irq_chip + MIPS_IRQ_CHIP_NUM_BITS); +	stride = 8 * (1 + ((num_bits - 1) / 64)); + + +	pr_notice("mips_irq_chip: %u bits, reg stride: %d\n", num_bits, stride); +	mips_irq_chip_reg_raw		= MIPS_IRQ_CHIP_REGS + 0 * stride; +	mips_irq_chip_reg_raw_w1s	= MIPS_IRQ_CHIP_REGS + 1 * stride; +	mips_irq_chip_reg_raw_w1c	= MIPS_IRQ_CHIP_REGS + 2 * stride; +	mips_irq_chip_reg_src		= MIPS_IRQ_CHIP_REGS + 3 * stride; +	mips_irq_chip_reg_en		= MIPS_IRQ_CHIP_REGS + 4 * stride; +	mips_irq_chip_reg_en_w1s	= MIPS_IRQ_CHIP_REGS + 5 * stride; +	mips_irq_chip_reg_en_w1c	= MIPS_IRQ_CHIP_REGS + 6 * stride; +	mips_irq_cpu_stride		= stride * 4; + +	for (i = 0; i < 4; i++) +		irq_set_chip_and_handler(i + MIPS_IRQ_PCIA, &irq_chip_pci, handle_level_irq); + +	for (i = 0; i < 2; i++) +		irq_set_chip_and_handler(i + MIPS_IRQ_MBOX0, &irq_chip_mbox, handle_percpu_irq); + + +	set_c0_status(STATUSF_IP2); +} + +static void irq_pci_dispatch(void) +{ +	unsigned int cpuid = get_ebase_cpunum(); +	u32 en; + +	en = __raw_readl(mips_irq_chip + mips_irq_chip_reg_src + +			(cpuid * mips_irq_cpu_stride)); + +	if (!en) { +		en = __raw_readl(mips_irq_chip + mips_irq_chip_reg_src + (cpuid * mips_irq_cpu_stride) + sizeof(u32)); +		en = (en >> (2 * cpuid)) & 3; + +		if (!en) +			spurious_interrupt(); +		else +			do_IRQ(__ffs(en) + MIPS_IRQ_MBOX0);	/* MBOX type */ +	} else { +		do_IRQ(__ffs(en)); +	} +} + + +void __init arch_init_irq(void) +{ +	irq_init_core(); +	irq_pci_init(); +} + +asmlinkage void plat_irq_dispatch(void) +{ +	unsigned int pending = read_c0_cause() & read_c0_status() & ST0_IM; +	int ip; + +	if (unlikely(!pending)) { +		spurious_interrupt(); +		return; +	} + +	ip = ffs(pending) - 1 - STATUSB_IP0; +	if (ip == 2) +		irq_pci_dispatch(); +	else +		do_IRQ(MIPS_CPU_IRQ_BASE + ip); +} diff --git a/arch/mips/paravirt/paravirt-smp.c b/arch/mips/paravirt/paravirt-smp.c new file mode 100644 index 000000000000..73a123e08c28 --- /dev/null +++ b/arch/mips/paravirt/paravirt-smp.c @@ -0,0 +1,148 @@ +/* + * 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. + * + * Copyright (C) 2013 Cavium, Inc. + */ + +#include <linux/interrupt.h> +#include <linux/cpumask.h> +#include <linux/kernel.h> +#include <linux/sched.h> + +#include <asm/mipsregs.h> +#include <asm/setup.h> +#include <asm/time.h> +#include <asm/smp.h> + +/* + * Writing the sp releases the CPU, so writes must be ordered, gp + * first, then sp. + */ +unsigned long paravirt_smp_sp[NR_CPUS]; +unsigned long paravirt_smp_gp[NR_CPUS]; + +static int numcpus = 1; + +static int __init set_numcpus(char *str) +{ +	int newval; + +	if (get_option(&str, &newval)) { +		if (newval < 1 || newval >= NR_CPUS) +			goto bad; +		numcpus = newval; +		return 0; +	} +bad: +	return -EINVAL; +} +early_param("numcpus", set_numcpus); + + +static void paravirt_smp_setup(void) +{ +	int id; +	unsigned int cpunum = get_ebase_cpunum(); + +	if (WARN_ON(cpunum >= NR_CPUS)) +		return; + +	/* The present CPUs are initially just the boot cpu (CPU 0). */ +	for (id = 0; id < NR_CPUS; id++) { +		set_cpu_possible(id, id == 0); +		set_cpu_present(id, id == 0); +	} +	__cpu_number_map[cpunum] = 0; +	__cpu_logical_map[0] = cpunum; + +	for (id = 0; id < numcpus; id++) { +		set_cpu_possible(id, true); +		set_cpu_present(id, true); +		__cpu_number_map[id] = id; +		__cpu_logical_map[id] = id; +	} +} + +void irq_mbox_ipi(int cpu, unsigned int actions); +static void paravirt_send_ipi_single(int cpu, unsigned int action) +{ +	irq_mbox_ipi(cpu, action); +} + +static void paravirt_send_ipi_mask(const struct cpumask *mask, unsigned int action) +{ +	unsigned int cpu; + +	for_each_cpu_mask(cpu, *mask) +		paravirt_send_ipi_single(cpu, action); +} + +static void paravirt_init_secondary(void) +{ +	unsigned int sr; + +	sr = set_c0_status(ST0_BEV); +	write_c0_ebase((u32)ebase); + +	sr |= STATUSF_IP2; /* Interrupt controller on IP2 */ +	write_c0_status(sr); + +	irq_cpu_online(); +} + +static void paravirt_smp_finish(void) +{ +	/* to generate the first CPU timer interrupt */ +	write_c0_compare(read_c0_count() + mips_hpt_frequency / HZ); +	local_irq_enable(); +} + +static void paravirt_cpus_done(void) +{ +} + +static void paravirt_boot_secondary(int cpu, struct task_struct *idle) +{ +	paravirt_smp_gp[cpu] = (unsigned long)task_thread_info(idle); +	smp_wmb(); +	paravirt_smp_sp[cpu] = __KSTK_TOS(idle); +} + +static irqreturn_t paravirt_reched_interrupt(int irq, void *dev_id) +{ +	scheduler_ipi(); +	return IRQ_HANDLED; +} + +static irqreturn_t paravirt_function_interrupt(int irq, void *dev_id) +{ +	smp_call_function_interrupt(); +	return IRQ_HANDLED; +} + +static void paravirt_prepare_cpus(unsigned int max_cpus) +{ +	if (request_irq(MIPS_IRQ_MBOX0, paravirt_reched_interrupt, +			IRQF_PERCPU | IRQF_NO_THREAD, "Scheduler", +			paravirt_reched_interrupt)) { +		panic("Cannot request_irq for SchedulerIPI"); +	} +	if (request_irq(MIPS_IRQ_MBOX1, paravirt_function_interrupt, +			IRQF_PERCPU | IRQF_NO_THREAD, "SMP-Call", +			paravirt_function_interrupt)) { +		panic("Cannot request_irq for SMP-Call"); +	} +} + +struct plat_smp_ops paravirt_smp_ops = { +	.send_ipi_single	= paravirt_send_ipi_single, +	.send_ipi_mask		= paravirt_send_ipi_mask, +	.init_secondary		= paravirt_init_secondary, +	.smp_finish		= paravirt_smp_finish, +	.cpus_done		= paravirt_cpus_done, +	.boot_secondary		= paravirt_boot_secondary, +	.smp_setup		= paravirt_smp_setup, +	.prepare_cpus		= paravirt_prepare_cpus, +}; diff --git a/arch/mips/paravirt/serial.c b/arch/mips/paravirt/serial.c new file mode 100644 index 000000000000..02b665c02272 --- /dev/null +++ b/arch/mips/paravirt/serial.c @@ -0,0 +1,40 @@ +/* + * 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. + * + * Copyright (C) 2013 Cavium, Inc. + */ + +#include <linux/kernel.h> +#include <linux/virtio_console.h> +#include <linux/kvm_para.h> + +/* + * Emit one character to the boot console. + */ +int prom_putchar(char c) +{ +	kvm_hypercall3(KVM_HC_MIPS_CONSOLE_OUTPUT, 0 /*  port 0 */, +		(unsigned long)&c, 1 /* len == 1 */); + +	return 1; +} + +#ifdef CONFIG_VIRTIO_CONSOLE +static int paravirt_put_chars(u32 vtermno, const char *buf, int count) +{ +	kvm_hypercall3(KVM_HC_MIPS_CONSOLE_OUTPUT, vtermno, +		(unsigned long)buf, count); + +	return count; +} + +static int __init paravirt_cons_init(void) +{ +	virtio_cons_early_init(paravirt_put_chars); +	return 0; +} +core_initcall(paravirt_cons_init); + +#endif diff --git a/arch/mips/paravirt/setup.c b/arch/mips/paravirt/setup.c new file mode 100644 index 000000000000..cb8448b373a7 --- /dev/null +++ b/arch/mips/paravirt/setup.c @@ -0,0 +1,67 @@ +/* + * 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. + * + * Copyright (C) 2013 Cavium, Inc. + */ + +#include <linux/kernel.h> +#include <linux/kvm_para.h> + +#include <asm/reboot.h> +#include <asm/bootinfo.h> +#include <asm/smp-ops.h> +#include <asm/time.h> + +extern struct plat_smp_ops paravirt_smp_ops; + +const char *get_system_type(void) +{ +	return "MIPS Para-Virtualized Guest"; +} + +void __init plat_time_init(void) +{ +	mips_hpt_frequency = kvm_hypercall0(KVM_HC_MIPS_GET_CLOCK_FREQ); + +	preset_lpj = mips_hpt_frequency / (2 * HZ); +} + +static void pv_machine_halt(void) +{ +	kvm_hypercall0(KVM_HC_MIPS_EXIT_VM); +} + +/* + * Early entry point for arch setup + */ +void __init prom_init(void) +{ +	int i; +	int argc = fw_arg0; +	char **argv = (char **)fw_arg1; + +#ifdef CONFIG_32BIT +	set_io_port_base(KSEG1ADDR(0x1e000000)); +#else /* CONFIG_64BIT */ +	set_io_port_base(PHYS_TO_XKSEG_UNCACHED(0x1e000000)); +#endif + +	for (i = 0; i < argc; i++) { +		strlcat(arcs_cmdline, argv[i], COMMAND_LINE_SIZE); +		if (i < argc - 1) +			strlcat(arcs_cmdline, " ", COMMAND_LINE_SIZE); +	} +	_machine_halt = pv_machine_halt; +	register_smp_ops(¶virt_smp_ops); +} + +void __init plat_mem_setup(void) +{ +	/* Do nothing, the "mem=???" parser handles our memory. */ +} + +void __init prom_free_prom_memory(void) +{ +} | 
