Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 39b8d525 authored by Ralf Baechle's avatar Ralf Baechle
Browse files

[MIPS] Add support for MIPS CMP platform.

parent 30840244
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -221,6 +221,7 @@ config MIPS_MALTA
	select DMA_NONCOHERENT
	select GENERIC_ISA_DMA
	select IRQ_CPU
	select IRQ_GIC
	select HW_HAS_PCI
	select I8253
	select I8259
@@ -840,6 +841,9 @@ config MIPS_NILE4
config MIPS_DISABLE_OBSOLETE_IDE
	bool

config SYNC_R4K
	bool

config NO_IOPORT
	def_bool n

@@ -909,6 +913,9 @@ config IRQ_TXX9
config IRQ_GT641XX
	bool

config IRQ_GIC
	bool

config MIPS_BOARDS_GEN
	bool

@@ -1811,6 +1818,17 @@ config NR_CPUS
	  performance should round up your number of processors to the next
	  power of two.

config MIPS_CMP
	bool "MIPS CMP framework support"
	depends on SMP
	select SYNC_R4K
	select SYS_SUPPORTS_SCHED_SMT
	select WEAK_ORDERING
	default n
	help
	  This is a placeholder option for the GCMP work. It will need to
	  be handled differently...

source "kernel/time/Kconfig"

#
+3 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@ obj-$(CONFIG_CEVT_TXX9) += cevt-txx9.o
obj-$(CONFIG_CSRC_BCM1480)	+= csrc-bcm1480.o
obj-$(CONFIG_CSRC_R4K)		+= csrc-r4k.o
obj-$(CONFIG_CSRC_SB1250)	+= csrc-sb1250.o
obj-$(CONFIG_SYNC_R4K)		+= sync-r4k.o

binfmt_irix-objs	:= irixelf.o irixinv.o irixioctl.o irixsig.o	\
			   irix5sys.o sysirix.o
@@ -50,6 +51,7 @@ obj-$(CONFIG_MIPS_MT) += mips-mt.o
obj-$(CONFIG_MIPS_MT_FPAFF)	+= mips-mt-fpaff.o
obj-$(CONFIG_MIPS_MT_SMTC)	+= smtc.o smtc-asm.o smtc-proc.o
obj-$(CONFIG_MIPS_MT_SMP)	+= smp-mt.o
obj-$(CONFIG_MIPS_CMP)		+= smp-cmp.o
obj-$(CONFIG_CPU_MIPSR2)	+= spram.o

obj-$(CONFIG_MIPS_APSP_KSPD)	+= kspd.o
@@ -63,6 +65,7 @@ obj-$(CONFIG_IRQ_CPU_RM9K) += irq-rm9000.o
obj-$(CONFIG_MIPS_BOARDS_GEN)	+= irq-msc01.o
obj-$(CONFIG_IRQ_TXX9)		+= irq_txx9.o
obj-$(CONFIG_IRQ_GT641XX)	+= irq-gt641xx.o
obj-$(CONFIG_IRQ_GIC)		+= irq-gic.o

obj-$(CONFIG_32BIT)		+= scall32-o32.o
obj-$(CONFIG_64BIT)		+= scall64-64.o
+5 −0
Original line number Diff line number Diff line
@@ -169,6 +169,7 @@ static inline void check_wait(void)

	case CPU_24K:
	case CPU_34K:
	case CPU_1004K:
		cpu_wait = r4k_wait;
		if (read_c0_config7() & MIPS_CONF7_WII)
			cpu_wait = r4k_wait_irqoff;
@@ -717,6 +718,9 @@ static inline void cpu_probe_mips(struct cpuinfo_mips *c)
	case PRID_IMP_74K:
		c->cputype = CPU_74K;
		break;
	case PRID_IMP_1004K:
		c->cputype = CPU_1004K;
		break;
	}

	spram_config();
@@ -884,6 +888,7 @@ static __cpuinit const char *cpu_to_name(struct cpuinfo_mips *c)
	case CPU_24K:		name = "MIPS 24K"; break;
	case CPU_25KF:		name = "MIPS 25Kf"; break;
	case CPU_34K:		name = "MIPS 34K"; break;
	case CPU_1004K:		name = "MIPS 1004K"; break;
	case CPU_74K:		name = "MIPS 74K"; break;
	case CPU_VR4111:	name = "NEC VR4111"; break;
	case CPU_VR4121:	name = "NEC VR4121"; break;
+295 −0
Original line number Diff line number Diff line
#undef DEBUG

#include <linux/bitmap.h>
#include <linux/init.h>

#include <asm/io.h>
#include <asm/gic.h>
#include <asm/gcmpregs.h>
#include <asm/mips-boards/maltaint.h>
#include <asm/irq.h>
#include <linux/hardirq.h>
#include <asm-generic/bitops/find.h>


static unsigned long _gic_base;
static unsigned int _irqbase, _mapsize, numvpes, numintrs;
static struct gic_intr_map *_intrmap;

static struct gic_pcpu_mask pcpu_masks[NR_CPUS];
static struct gic_pending_regs pending_regs[NR_CPUS];
static struct gic_intrmask_regs intrmask_regs[NR_CPUS];

#define gic_wedgeb2bok 0	/*
				 * Can GIC handle b2b writes to wedge register?
				 */
#if gic_wedgeb2bok == 0
static DEFINE_SPINLOCK(gic_wedgeb2b_lock);
#endif

void gic_send_ipi(unsigned int intr)
{
#if gic_wedgeb2bok == 0
	unsigned long flags;
#endif
	pr_debug("CPU%d: %s status %08x\n", smp_processor_id(), __func__,
		 read_c0_status());
	if (!gic_wedgeb2bok)
		spin_lock_irqsave(&gic_wedgeb2b_lock, flags);
	GICWRITE(GIC_REG(SHARED, GIC_SH_WEDGE), 0x80000000 | intr);
	if (!gic_wedgeb2bok) {
		(void) GIC_REG(SHARED, GIC_SH_CONFIG);
		spin_unlock_irqrestore(&gic_wedgeb2b_lock, flags);
	}
}

/* This is Malta specific and needs to be exported */
static void vpe_local_setup(unsigned int numvpes)
{
	int i;
	unsigned long timer_interrupt = 5, perf_interrupt = 5;
	unsigned int vpe_ctl;

	/*
	 * Setup the default performance counter timer interrupts
	 * for all VPEs
	 */
	for (i = 0; i < numvpes; i++) {
		GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_OTHER_ADDR), i);

		/* Are Interrupts locally routable? */
		GICREAD(GIC_REG(VPE_OTHER, GIC_VPE_CTL), vpe_ctl);
		if (vpe_ctl & GIC_VPE_CTL_TIMER_RTBL_MSK)
			GICWRITE(GIC_REG(VPE_OTHER, GIC_VPE_TIMER_MAP),
				 GIC_MAP_TO_PIN_MSK | timer_interrupt);

		if (vpe_ctl & GIC_VPE_CTL_PERFCNT_RTBL_MSK)
			GICWRITE(GIC_REG(VPE_OTHER, GIC_VPE_PERFCTR_MAP),
				 GIC_MAP_TO_PIN_MSK | perf_interrupt);
	}
}

unsigned int gic_get_int(void)
{
	unsigned int i;
	unsigned long *pending, *intrmask, *pcpu_mask;
	unsigned long *pending_abs, *intrmask_abs;

	/* Get per-cpu bitmaps */
	pending = pending_regs[smp_processor_id()].pending;
	intrmask = intrmask_regs[smp_processor_id()].intrmask;
	pcpu_mask = pcpu_masks[smp_processor_id()].pcpu_mask;

	pending_abs = (unsigned long *) GIC_REG_ABS_ADDR(SHARED,
							 GIC_SH_PEND_31_0_OFS);
	intrmask_abs = (unsigned long *) GIC_REG_ABS_ADDR(SHARED,
							  GIC_SH_MASK_31_0_OFS);

	for (i = 0; i < BITS_TO_LONGS(GIC_NUM_INTRS); i++) {
		GICREAD(*pending_abs, pending[i]);
		GICREAD(*intrmask_abs, intrmask[i]);
		pending_abs++;
		intrmask_abs++;
	}

	bitmap_and(pending, pending, intrmask, GIC_NUM_INTRS);
	bitmap_and(pending, pending, pcpu_mask, GIC_NUM_INTRS);

	i = find_first_bit(pending, GIC_NUM_INTRS);

	pr_debug("CPU%d: %s pend=%d\n", smp_processor_id(), __func__, i);

	return i;
}

static unsigned int gic_irq_startup(unsigned int irq)
{
	pr_debug("CPU%d: %s: irq%d\n", smp_processor_id(), __func__, irq);
	irq -= _irqbase;
	/* FIXME: this is wrong for !GICISWORDLITTLEENDIAN */
	GICWRITE(GIC_REG_ADDR(SHARED, (GIC_SH_SMASK_31_0_OFS + (irq / 32))),
		 1 << (irq % 32));
	return 0;
}

static void gic_irq_ack(unsigned int irq)
{
#if gic_wedgeb2bok == 0
	unsigned long flags;
#endif
	pr_debug("CPU%d: %s: irq%d\n", smp_processor_id(), __func__, irq);
	irq -= _irqbase;
	GICWRITE(GIC_REG_ADDR(SHARED, (GIC_SH_RMASK_31_0_OFS + (irq / 32))),
		 1 << (irq % 32));

	if (_intrmap[irq].trigtype == GIC_TRIG_EDGE) {
		if (!gic_wedgeb2bok)
			spin_lock_irqsave(&gic_wedgeb2b_lock, flags);
		GICWRITE(GIC_REG(SHARED, GIC_SH_WEDGE), irq);
		if (!gic_wedgeb2bok) {
			(void) GIC_REG(SHARED, GIC_SH_CONFIG);
			spin_unlock_irqrestore(&gic_wedgeb2b_lock, flags);
		}
	}
}

static void gic_mask_irq(unsigned int irq)
{
	pr_debug("CPU%d: %s: irq%d\n", smp_processor_id(), __func__, irq);
	irq -= _irqbase;
	/* FIXME: this is wrong for !GICISWORDLITTLEENDIAN */
	GICWRITE(GIC_REG_ADDR(SHARED, (GIC_SH_RMASK_31_0_OFS + (irq / 32))),
		 1 << (irq % 32));
}

static void gic_unmask_irq(unsigned int irq)
{
	pr_debug("CPU%d: %s: irq%d\n", smp_processor_id(), __func__, irq);
	irq -= _irqbase;
	/* FIXME: this is wrong for !GICISWORDLITTLEENDIAN */
	GICWRITE(GIC_REG_ADDR(SHARED, (GIC_SH_SMASK_31_0_OFS + (irq / 32))),
		 1 << (irq % 32));
}

#ifdef CONFIG_SMP

static DEFINE_SPINLOCK(gic_lock);

static void gic_set_affinity(unsigned int irq, cpumask_t cpumask)
{
	cpumask_t	tmp = CPU_MASK_NONE;
	unsigned long	flags;
	int		i;

	pr_debug(KERN_DEBUG "%s called\n", __func__);
	irq -= _irqbase;

	cpus_and(tmp, cpumask, cpu_online_map);
	if (cpus_empty(tmp))
		return;

	/* Assumption : cpumask refers to a single CPU */
	spin_lock_irqsave(&gic_lock, flags);
	for (;;) {
		/* Re-route this IRQ */
		GIC_SH_MAP_TO_VPE_SMASK(irq, first_cpu(tmp));

		/*
		 * FIXME: assumption that _intrmap is ordered and has no holes
		 */

		/* Update the intr_map */
		_intrmap[irq].cpunum = first_cpu(tmp);

		/* Update the pcpu_masks */
		for (i = 0; i < NR_CPUS; i++)
			clear_bit(irq, pcpu_masks[i].pcpu_mask);
		set_bit(irq, pcpu_masks[first_cpu(tmp)].pcpu_mask);

	}
	irq_desc[irq].affinity = cpumask;
	spin_unlock_irqrestore(&gic_lock, flags);

}
#endif

static struct irq_chip gic_irq_controller = {
	.name		=	"MIPS GIC",
	.startup	=	gic_irq_startup,
	.ack		=	gic_irq_ack,
	.mask		=	gic_mask_irq,
	.mask_ack	=	gic_mask_irq,
	.unmask		=	gic_unmask_irq,
	.eoi		=	gic_unmask_irq,
#ifdef CONFIG_SMP
	.set_affinity	=	gic_set_affinity,
#endif
};

static void __init setup_intr(unsigned int intr, unsigned int cpu,
	unsigned int pin, unsigned int polarity, unsigned int trigtype)
{
	/* Setup Intr to Pin mapping */
	if (pin & GIC_MAP_TO_NMI_MSK) {
		GICWRITE(GIC_REG_ADDR(SHARED, GIC_SH_MAP_TO_PIN(intr)), pin);
		/* FIXME: hack to route NMI to all cpu's */
		for (cpu = 0; cpu < NR_CPUS; cpu += 32) {
			GICWRITE(GIC_REG_ADDR(SHARED,
					  GIC_SH_MAP_TO_VPE_REG_OFF(intr, cpu)),
				 0xffffffff);
		}
	} else {
		GICWRITE(GIC_REG_ADDR(SHARED, GIC_SH_MAP_TO_PIN(intr)),
			 GIC_MAP_TO_PIN_MSK | pin);
		/* Setup Intr to CPU mapping */
		GIC_SH_MAP_TO_VPE_SMASK(intr, cpu);
	}

	/* Setup Intr Polarity */
	GIC_SET_POLARITY(intr, polarity);

	/* Setup Intr Trigger Type */
	GIC_SET_TRIGGER(intr, trigtype);

	/* Init Intr Masks */
	GIC_SET_INTR_MASK(intr, 0);
}

static void __init gic_basic_init(void)
{
	unsigned int i, cpu;

	/* Setup defaults */
	for (i = 0; i < GIC_NUM_INTRS; i++) {
		GIC_SET_POLARITY(i, GIC_POL_POS);
		GIC_SET_TRIGGER(i, GIC_TRIG_LEVEL);
		GIC_SET_INTR_MASK(i, 0);
	}

	/* Setup specifics */
	for (i = 0; i < _mapsize; i++) {
		cpu = _intrmap[i].cpunum;
		if (cpu == X)
			continue;

		setup_intr(_intrmap[i].intrnum,
				_intrmap[i].cpunum,
				_intrmap[i].pin,
				_intrmap[i].polarity,
				_intrmap[i].trigtype);
		/* Initialise per-cpu Interrupt software masks */
		if (_intrmap[i].ipiflag)
			set_bit(_intrmap[i].intrnum, pcpu_masks[cpu].pcpu_mask);
	}

	vpe_local_setup(numvpes);

	for (i = _irqbase; i < (_irqbase + numintrs); i++)
		set_irq_chip(i, &gic_irq_controller);
}

void __init gic_init(unsigned long gic_base_addr,
		     unsigned long gic_addrspace_size,
		     struct gic_intr_map *intr_map, unsigned int intr_map_size,
		     unsigned int irqbase)
{
	unsigned int gicconfig;

	_gic_base = (unsigned long) ioremap_nocache(gic_base_addr,
						    gic_addrspace_size);
	_irqbase = irqbase;
	_intrmap = intr_map;
	_mapsize = intr_map_size;

	GICREAD(GIC_REG(SHARED, GIC_SH_CONFIG), gicconfig);
	numintrs = (gicconfig & GIC_SH_CONFIG_NUMINTRS_MSK) >>
		   GIC_SH_CONFIG_NUMINTRS_SHF;
	numintrs = ((numintrs + 1) * 8);

	numvpes = (gicconfig & GIC_SH_CONFIG_NUMVPES_MSK) >>
		  GIC_SH_CONFIG_NUMVPES_SHF;

	pr_debug("%s called\n", __func__);

	gic_basic_init();
}
+265 −0
Original line number Diff line number Diff line
/*
 *  This program is free software; you can distribute it and/or modify it
 *  under the terms of the GNU General Public License (Version 2) as
 *  published by the Free Software Foundation.
 *
 *  This program is distributed in the hope 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.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
 *
 * Copyright (C) 2007 MIPS Technologies, Inc.
 *    Chris Dearman (chris@mips.com)
 */

#undef DEBUG

#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/cpumask.h>
#include <linux/interrupt.h>
#include <linux/compiler.h>

#include <asm/atomic.h>
#include <asm/cacheflush.h>
#include <asm/cpu.h>
#include <asm/processor.h>
#include <asm/system.h>
#include <asm/hardirq.h>
#include <asm/mmu_context.h>
#include <asm/smp.h>
#include <asm/time.h>
#include <asm/mipsregs.h>
#include <asm/mipsmtregs.h>
#include <asm/mips_mt.h>

/*
 * Crude manipulation of the CPU masks to control which
 * which CPU's are brought online during initialisation
 *
 * Beware... this needs to be called after CPU discovery
 * but before CPU bringup
 */
static int __init allowcpus(char *str)
{
	cpumask_t cpu_allow_map;
	char buf[256];
	int len;

	cpus_clear(cpu_allow_map);
	if (cpulist_parse(str, cpu_allow_map) == 0) {
		cpu_set(0, cpu_allow_map);
		cpus_and(cpu_possible_map, cpu_possible_map, cpu_allow_map);
		len = cpulist_scnprintf(buf, sizeof(buf)-1, cpu_possible_map);
		buf[len] = '\0';
		pr_debug("Allowable CPUs: %s\n", buf);
		return 1;
	} else
		return 0;
}
__setup("allowcpus=", allowcpus);

static void ipi_call_function(unsigned int cpu)
{
	unsigned int action = 0;

	pr_debug("CPU%d: %s cpu %d status %08x\n",
		 smp_processor_id(), __func__, cpu, read_c0_status());

	switch (cpu) {
	case 0:
		action = GIC_IPI_EXT_INTR_CALLFNC_VPE0;
		break;
	case 1:
		action = GIC_IPI_EXT_INTR_CALLFNC_VPE1;
		break;
	case 2:
		action = GIC_IPI_EXT_INTR_CALLFNC_VPE2;
		break;
	case 3:
		action = GIC_IPI_EXT_INTR_CALLFNC_VPE3;
		break;
	}
	gic_send_ipi(action);
}


static void ipi_resched(unsigned int cpu)
{
	unsigned int action = 0;

	pr_debug("CPU%d: %s cpu %d status %08x\n",
		 smp_processor_id(), __func__, cpu, read_c0_status());

	switch (cpu) {
	case 0:
		action = GIC_IPI_EXT_INTR_RESCHED_VPE0;
		break;
	case 1:
		action = GIC_IPI_EXT_INTR_RESCHED_VPE1;
		break;
	case 2:
		action = GIC_IPI_EXT_INTR_RESCHED_VPE2;
		break;
	case 3:
		action = GIC_IPI_EXT_INTR_RESCHED_VPE3;
		break;
	}
	gic_send_ipi(action);
}

/*
 * FIXME: This isn't restricted to CMP
 * The SMVP kernel could use GIC interrupts if available
 */
void cmp_send_ipi_single(int cpu, unsigned int action)
{
	unsigned long flags;

	local_irq_save(flags);

	switch (action) {
	case SMP_CALL_FUNCTION:
		ipi_call_function(cpu);
		break;

	case SMP_RESCHEDULE_YOURSELF:
		ipi_resched(cpu);
		break;
	}

	local_irq_restore(flags);
}

static void cmp_send_ipi_mask(cpumask_t mask, unsigned int action)
{
	unsigned int i;

	for_each_cpu_mask(i, mask)
		cmp_send_ipi_single(i, action);
}

static void cmp_init_secondary(void)
{
	struct cpuinfo_mips *c = &current_cpu_data;

	/* Assume GIC is present */
	change_c0_status(ST0_IM, STATUSF_IP3 | STATUSF_IP4 | STATUSF_IP6 |
				 STATUSF_IP7);

	/* Enable per-cpu interrupts: platform specific */

	c->core = (read_c0_ebase() >> 1) & 0xff;
#if defined(CONFIG_MIPS_MT_SMP) || defined(CONFIG_MIPS_MT_SMTC)
	c->vpe_id = (read_c0_tcbind() >> TCBIND_CURVPE_SHIFT) & TCBIND_CURVPE;
#endif
#ifdef CONFIG_MIPS_MT_SMTC
	c->tc_id  = (read_c0_tcbind() >> TCBIND_CURTC_SHIFT) & TCBIND_CURTC;
#endif
}

static void cmp_smp_finish(void)
{
	pr_debug("SMPCMP: CPU%d: %s\n", smp_processor_id(), __func__);

	/* CDFIXME: remove this? */
	write_c0_compare(read_c0_count() + (8 * mips_hpt_frequency / HZ));

#ifdef CONFIG_MIPS_MT_FPAFF
	/* If we have an FPU, enroll ourselves in the FPU-full mask */
	if (cpu_has_fpu)
		cpu_set(smp_processor_id(), mt_fpu_cpumask);
#endif /* CONFIG_MIPS_MT_FPAFF */

	local_irq_enable();
}

static void cmp_cpus_done(void)
{
	pr_debug("SMPCMP: CPU%d: %s\n", smp_processor_id(), __func__);
}

/*
 * Setup the PC, SP, and GP of a secondary processor and start it running
 * smp_bootstrap is the place to resume from
 * __KSTK_TOS(idle) is apparently the stack pointer
 * (unsigned long)idle->thread_info the gp
 */
static void cmp_boot_secondary(int cpu, struct task_struct *idle)
{
	struct thread_info *gp = task_thread_info(idle);
	unsigned long sp = __KSTK_TOS(idle);
	unsigned long pc = (unsigned long)&smp_bootstrap;
	unsigned long a0 = 0;

	pr_debug("SMPCMP: CPU%d: %s cpu %d\n", smp_processor_id(),
		__func__, cpu);

#if 0
	/* Needed? */
	flush_icache_range((unsigned long)gp,
			   (unsigned long)(gp + sizeof(struct thread_info)));
#endif

	amon_cpu_start(cpu, pc, sp, gp, a0);
}

/*
 * Common setup before any secondaries are started
 */
void __init cmp_smp_setup(void)
{
	int i;
	int ncpu = 0;

	pr_debug("SMPCMP: CPU%d: %s\n", smp_processor_id(), __func__);

#ifdef CONFIG_MIPS_MT_FPAFF
	/* If we have an FPU, enroll ourselves in the FPU-full mask */
	if (cpu_has_fpu)
		cpu_set(0, mt_fpu_cpumask);
#endif /* CONFIG_MIPS_MT_FPAFF */

	for (i = 1; i < NR_CPUS; i++) {
		if (amon_cpu_avail(i)) {
			cpu_set(i, phys_cpu_present_map);
			__cpu_number_map[i]	= ++ncpu;
			__cpu_logical_map[ncpu]	= i;
		}
	}

	if (cpu_has_mipsmt) {
		unsigned int nvpe, mvpconf0 = read_c0_mvpconf0();

		nvpe = ((mvpconf0 & MVPCONF0_PTC) >> MVPCONF0_PTC_SHIFT) + 1;
		smp_num_siblings = nvpe;
	}
	pr_info("Detected %i available secondary CPU(s)\n", ncpu);
}

void __init cmp_prepare_cpus(unsigned int max_cpus)
{
	pr_debug("SMPCMP: CPU%d: %s max_cpus=%d\n",
		 smp_processor_id(), __func__, max_cpus);

	/*
	 * FIXME: some of these options are per-system, some per-core and
	 * some per-cpu
	 */
	mips_mt_set_cpuoptions();
}

struct plat_smp_ops cmp_smp_ops = {
	.send_ipi_single	= cmp_send_ipi_single,
	.send_ipi_mask		= cmp_send_ipi_mask,
	.init_secondary		= cmp_init_secondary,
	.smp_finish		= cmp_smp_finish,
	.cpus_done		= cmp_cpus_done,
	.boot_secondary		= cmp_boot_secondary,
	.smp_setup		= cmp_smp_setup,
	.prepare_cpus		= cmp_prepare_cpus,
};
Loading