[MIPS] Add support for MIPS CMP platform.
Signed-off-by: Chris Dearman <chris@mips.com> Signed-off-by: Atsushi Nemoto <anemo@mba.ocn.ne.jp> Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
This commit is contained in:
@@ -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
|
||||
|
@@ -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
arch/mips/kernel/irq-gic.c
Normal file
295
arch/mips/kernel/irq-gic.c
Normal file
@@ -0,0 +1,295 @@
|
||||
#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
arch/mips/kernel/smp-cmp.c
Normal file
265
arch/mips/kernel/smp-cmp.c
Normal file
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* 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 = ¤t_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,
|
||||
};
|
@@ -36,63 +36,7 @@
|
||||
#include <asm/mipsmtregs.h>
|
||||
#include <asm/mips_mt.h>
|
||||
|
||||
#define MIPS_CPU_IPI_RESCHED_IRQ 0
|
||||
#define MIPS_CPU_IPI_CALL_IRQ 1
|
||||
|
||||
static int cpu_ipi_resched_irq, cpu_ipi_call_irq;
|
||||
|
||||
#if 0
|
||||
static void dump_mtregisters(int vpe, int tc)
|
||||
{
|
||||
printk("vpe %d tc %d\n", vpe, tc);
|
||||
|
||||
settc(tc);
|
||||
|
||||
printk(" c0 status 0x%lx\n", read_vpe_c0_status());
|
||||
printk(" vpecontrol 0x%lx\n", read_vpe_c0_vpecontrol());
|
||||
printk(" vpeconf0 0x%lx\n", read_vpe_c0_vpeconf0());
|
||||
printk(" tcstatus 0x%lx\n", read_tc_c0_tcstatus());
|
||||
printk(" tcrestart 0x%lx\n", read_tc_c0_tcrestart());
|
||||
printk(" tcbind 0x%lx\n", read_tc_c0_tcbind());
|
||||
printk(" tchalt 0x%lx\n", read_tc_c0_tchalt());
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ipi_resched_dispatch(void)
|
||||
{
|
||||
do_IRQ(MIPS_CPU_IRQ_BASE + MIPS_CPU_IPI_RESCHED_IRQ);
|
||||
}
|
||||
|
||||
static void ipi_call_dispatch(void)
|
||||
{
|
||||
do_IRQ(MIPS_CPU_IRQ_BASE + MIPS_CPU_IPI_CALL_IRQ);
|
||||
}
|
||||
|
||||
static irqreturn_t ipi_resched_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t ipi_call_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
smp_call_function_interrupt();
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static struct irqaction irq_resched = {
|
||||
.handler = ipi_resched_interrupt,
|
||||
.flags = IRQF_DISABLED|IRQF_PERCPU,
|
||||
.name = "IPI_resched"
|
||||
};
|
||||
|
||||
static struct irqaction irq_call = {
|
||||
.handler = ipi_call_interrupt,
|
||||
.flags = IRQF_DISABLED|IRQF_PERCPU,
|
||||
.name = "IPI_call"
|
||||
};
|
||||
|
||||
static void __init smp_copy_vpe_config(void)
|
||||
static void __init smvp_copy_vpe_config(void)
|
||||
{
|
||||
write_vpe_c0_status(
|
||||
(read_c0_status() & ~(ST0_IM | ST0_IE | ST0_KSU)) | ST0_CU0);
|
||||
@@ -109,7 +53,7 @@ static void __init smp_copy_vpe_config(void)
|
||||
write_vpe_c0_count(read_c0_count());
|
||||
}
|
||||
|
||||
static unsigned int __init smp_vpe_init(unsigned int tc, unsigned int mvpconf0,
|
||||
static unsigned int __init smvp_vpe_init(unsigned int tc, unsigned int mvpconf0,
|
||||
unsigned int ncpu)
|
||||
{
|
||||
if (tc > ((mvpconf0 & MVPCONF0_PVPE) >> MVPCONF0_PVPE_SHIFT))
|
||||
@@ -135,12 +79,12 @@ static unsigned int __init smp_vpe_init(unsigned int tc, unsigned int mvpconf0,
|
||||
write_vpe_c0_vpecontrol(read_vpe_c0_vpecontrol() & ~VPECONTROL_TE);
|
||||
|
||||
if (tc != 0)
|
||||
smp_copy_vpe_config();
|
||||
smvp_copy_vpe_config();
|
||||
|
||||
return ncpu;
|
||||
}
|
||||
|
||||
static void __init smp_tc_init(unsigned int tc, unsigned int mvpconf0)
|
||||
static void __init smvp_tc_init(unsigned int tc, unsigned int mvpconf0)
|
||||
{
|
||||
unsigned long tmp;
|
||||
|
||||
@@ -207,15 +151,20 @@ static void vsmp_send_ipi_mask(cpumask_t mask, unsigned int action)
|
||||
|
||||
static void __cpuinit vsmp_init_secondary(void)
|
||||
{
|
||||
/* Enable per-cpu interrupts */
|
||||
extern int gic_present;
|
||||
|
||||
/* This is Malta specific: IPI,performance and timer inetrrupts */
|
||||
write_c0_status((read_c0_status() & ~ST0_IM ) |
|
||||
(STATUSF_IP0 | STATUSF_IP1 | STATUSF_IP6 | STATUSF_IP7));
|
||||
if (gic_present)
|
||||
change_c0_status(ST0_IM, STATUSF_IP3 | STATUSF_IP4 |
|
||||
STATUSF_IP6 | STATUSF_IP7);
|
||||
else
|
||||
change_c0_status(ST0_IM, STATUSF_IP0 | STATUSF_IP1 |
|
||||
STATUSF_IP6 | STATUSF_IP7);
|
||||
}
|
||||
|
||||
static void __cpuinit vsmp_smp_finish(void)
|
||||
{
|
||||
/* CDFIXME: remove this? */
|
||||
write_c0_compare(read_c0_count() + (8* mips_hpt_frequency/HZ));
|
||||
|
||||
#ifdef CONFIG_MIPS_MT_FPAFF
|
||||
@@ -276,7 +225,7 @@ static void __cpuinit vsmp_boot_secondary(int cpu, struct task_struct *idle)
|
||||
/*
|
||||
* Common setup before any secondaries are started
|
||||
* Make sure all CPU's are in a sensible state before we boot any of the
|
||||
* secondarys
|
||||
* secondaries
|
||||
*/
|
||||
static void __init vsmp_smp_setup(void)
|
||||
{
|
||||
@@ -309,8 +258,8 @@ static void __init vsmp_smp_setup(void)
|
||||
for (tc = 0; tc <= ntc; tc++) {
|
||||
settc(tc);
|
||||
|
||||
smp_tc_init(tc, mvpconf0);
|
||||
ncpu = smp_vpe_init(tc, mvpconf0, ncpu);
|
||||
smvp_tc_init(tc, mvpconf0);
|
||||
ncpu = smvp_vpe_init(tc, mvpconf0, ncpu);
|
||||
}
|
||||
|
||||
/* Release config state */
|
||||
@@ -324,21 +273,6 @@ static void __init vsmp_smp_setup(void)
|
||||
static void __init vsmp_prepare_cpus(unsigned int max_cpus)
|
||||
{
|
||||
mips_mt_set_cpuoptions();
|
||||
|
||||
/* set up ipi interrupts */
|
||||
if (cpu_has_vint) {
|
||||
set_vi_handler(MIPS_CPU_IPI_RESCHED_IRQ, ipi_resched_dispatch);
|
||||
set_vi_handler(MIPS_CPU_IPI_CALL_IRQ, ipi_call_dispatch);
|
||||
}
|
||||
|
||||
cpu_ipi_resched_irq = MIPS_CPU_IRQ_BASE + MIPS_CPU_IPI_RESCHED_IRQ;
|
||||
cpu_ipi_call_irq = MIPS_CPU_IRQ_BASE + MIPS_CPU_IPI_CALL_IRQ;
|
||||
|
||||
setup_irq(cpu_ipi_resched_irq, &irq_resched);
|
||||
setup_irq(cpu_ipi_call_irq, &irq_call);
|
||||
|
||||
set_irq_handler(cpu_ipi_resched_irq, handle_percpu_irq);
|
||||
set_irq_handler(cpu_ipi_call_irq, handle_percpu_irq);
|
||||
}
|
||||
|
||||
struct plat_smp_ops vsmp_smp_ops = {
|
||||
|
@@ -35,6 +35,7 @@
|
||||
#include <asm/atomic.h>
|
||||
#include <asm/cpu.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/r4k-timer.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include <asm/time.h>
|
||||
@@ -125,6 +126,8 @@ asmlinkage __cpuinit void start_secondary(void)
|
||||
|
||||
cpu_set(cpu, cpu_callin_map);
|
||||
|
||||
synchronise_count_slave();
|
||||
|
||||
cpu_idle();
|
||||
}
|
||||
|
||||
@@ -287,6 +290,7 @@ void smp_send_stop(void)
|
||||
void __init smp_cpus_done(unsigned int max_cpus)
|
||||
{
|
||||
mp_ops->cpus_done();
|
||||
synchronise_count_master();
|
||||
}
|
||||
|
||||
/* called from main before smp_init() */
|
||||
|
@@ -331,7 +331,8 @@ static void smtc_tc_setup(int vpe, int tc, int cpu)
|
||||
/* In general, all TCs should have the same cpu_data indications */
|
||||
memcpy(&cpu_data[cpu], &cpu_data[0], sizeof(struct cpuinfo_mips));
|
||||
/* For 34Kf, start with TC/CPU 0 as sole owner of single FPU context */
|
||||
if (cpu_data[0].cputype == CPU_34K)
|
||||
if (cpu_data[0].cputype == CPU_34K ||
|
||||
cpu_data[0].cputype == CPU_1004K)
|
||||
cpu_data[cpu].options &= ~MIPS_CPU_FPU;
|
||||
cpu_data[cpu].vpe_id = vpe;
|
||||
cpu_data[cpu].tc_id = tc;
|
||||
|
159
arch/mips/kernel/sync-r4k.c
Normal file
159
arch/mips/kernel/sync-r4k.c
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Count register synchronisation.
|
||||
*
|
||||
* All CPUs will have their count registers synchronised to the CPU0 expirelo
|
||||
* value. This can cause a small timewarp for CPU0. All other CPU's should
|
||||
* not have done anything significant (but they may have had interrupts
|
||||
* enabled briefly - prom_smp_finish() should not be responsible for enabling
|
||||
* interrupts...)
|
||||
*
|
||||
* FIXME: broken for SMTC
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/irqflags.h>
|
||||
#include <linux/r4k-timer.h>
|
||||
|
||||
#include <asm/atomic.h>
|
||||
#include <asm/barrier.h>
|
||||
#include <asm/cpumask.h>
|
||||
#include <asm/mipsregs.h>
|
||||
|
||||
static atomic_t __initdata count_start_flag = ATOMIC_INIT(0);
|
||||
static atomic_t __initdata count_count_start = ATOMIC_INIT(0);
|
||||
static atomic_t __initdata count_count_stop = ATOMIC_INIT(0);
|
||||
|
||||
#define COUNTON 100
|
||||
#define NR_LOOPS 5
|
||||
|
||||
void __init synchronise_count_master(void)
|
||||
{
|
||||
int i;
|
||||
unsigned long flags;
|
||||
unsigned int initcount;
|
||||
int nslaves;
|
||||
|
||||
#ifdef CONFIG_MIPS_MT_SMTC
|
||||
/*
|
||||
* SMTC needs to synchronise per VPE, not per CPU
|
||||
* ignore for now
|
||||
*/
|
||||
return;
|
||||
#endif
|
||||
|
||||
pr_info("Checking COUNT synchronization across %u CPUs: ",
|
||||
num_online_cpus());
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
/*
|
||||
* Notify the slaves that it's time to start
|
||||
*/
|
||||
atomic_set(&count_start_flag, 1);
|
||||
smp_wmb();
|
||||
|
||||
/* Count will be initialised to expirelo for all CPU's */
|
||||
initcount = expirelo;
|
||||
|
||||
/*
|
||||
* We loop a few times to get a primed instruction cache,
|
||||
* then the last pass is more or less synchronised and
|
||||
* the master and slaves each set their cycle counters to a known
|
||||
* value all at once. This reduces the chance of having random offsets
|
||||
* between the processors, and guarantees that the maximum
|
||||
* delay between the cycle counters is never bigger than
|
||||
* the latency of information-passing (cachelines) between
|
||||
* two CPUs.
|
||||
*/
|
||||
|
||||
nslaves = num_online_cpus()-1;
|
||||
for (i = 0; i < NR_LOOPS; i++) {
|
||||
/* slaves loop on '!= ncpus' */
|
||||
while (atomic_read(&count_count_start) != nslaves)
|
||||
mb();
|
||||
atomic_set(&count_count_stop, 0);
|
||||
smp_wmb();
|
||||
|
||||
/* this lets the slaves write their count register */
|
||||
atomic_inc(&count_count_start);
|
||||
|
||||
/*
|
||||
* Everyone initialises count in the last loop:
|
||||
*/
|
||||
if (i == NR_LOOPS-1)
|
||||
write_c0_count(initcount);
|
||||
|
||||
/*
|
||||
* Wait for all slaves to leave the synchronization point:
|
||||
*/
|
||||
while (atomic_read(&count_count_stop) != nslaves)
|
||||
mb();
|
||||
atomic_set(&count_count_start, 0);
|
||||
smp_wmb();
|
||||
atomic_inc(&count_count_stop);
|
||||
}
|
||||
/* Arrange for an interrupt in a short while */
|
||||
write_c0_compare(read_c0_count() + COUNTON);
|
||||
|
||||
local_irq_restore(flags);
|
||||
|
||||
/*
|
||||
* i386 code reported the skew here, but the
|
||||
* count registers were almost certainly out of sync
|
||||
* so no point in alarming people
|
||||
*/
|
||||
printk("done.\n");
|
||||
}
|
||||
|
||||
void __init synchronise_count_slave(void)
|
||||
{
|
||||
int i;
|
||||
unsigned long flags;
|
||||
unsigned int initcount;
|
||||
int ncpus;
|
||||
|
||||
#ifdef CONFIG_MIPS_MT_SMTC
|
||||
/*
|
||||
* SMTC needs to synchronise per VPE, not per CPU
|
||||
* ignore for now
|
||||
*/
|
||||
return;
|
||||
#endif
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
/*
|
||||
* Not every cpu is online at the time this gets called,
|
||||
* so we first wait for the master to say everyone is ready
|
||||
*/
|
||||
|
||||
while (!atomic_read(&count_start_flag))
|
||||
mb();
|
||||
|
||||
/* Count will be initialised to expirelo for all CPU's */
|
||||
initcount = expirelo;
|
||||
|
||||
ncpus = num_online_cpus();
|
||||
for (i = 0; i < NR_LOOPS; i++) {
|
||||
atomic_inc(&count_count_start);
|
||||
while (atomic_read(&count_count_start) != ncpus)
|
||||
mb();
|
||||
|
||||
/*
|
||||
* Everyone initialises count in the last loop:
|
||||
*/
|
||||
if (i == NR_LOOPS-1)
|
||||
write_c0_count(initcount);
|
||||
|
||||
atomic_inc(&count_count_stop);
|
||||
while (atomic_read(&count_count_stop) != ncpus)
|
||||
mb();
|
||||
}
|
||||
/* Arrange for an interrupt in a short while */
|
||||
write_c0_compare(read_c0_count() + COUNTON);
|
||||
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
#undef NR_LOOPS
|
||||
#endif
|
@@ -22,6 +22,7 @@
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/bootmem.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ptrace.h>
|
||||
|
||||
#include <asm/bootinfo.h>
|
||||
#include <asm/branch.h>
|
||||
@@ -80,19 +81,22 @@ void (*board_bind_eic_interrupt)(int irq, int regset);
|
||||
|
||||
static void show_raw_backtrace(unsigned long reg29)
|
||||
{
|
||||
unsigned long *sp = (unsigned long *)reg29;
|
||||
unsigned long *sp = (unsigned long *)(reg29 & ~3);
|
||||
unsigned long addr;
|
||||
|
||||
printk("Call Trace:");
|
||||
#ifdef CONFIG_KALLSYMS
|
||||
printk("\n");
|
||||
#endif
|
||||
while (!kstack_end(sp)) {
|
||||
addr = *sp++;
|
||||
if (__kernel_text_address(addr))
|
||||
print_ip_sym(addr);
|
||||
#define IS_KVA01(a) ((((unsigned int)a) & 0xc0000000) == 0x80000000)
|
||||
if (IS_KVA01(sp)) {
|
||||
while (!kstack_end(sp)) {
|
||||
addr = *sp++;
|
||||
if (__kernel_text_address(addr))
|
||||
print_ip_sym(addr);
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KALLSYMS
|
||||
@@ -192,16 +196,19 @@ EXPORT_SYMBOL(dump_stack);
|
||||
static void show_code(unsigned int __user *pc)
|
||||
{
|
||||
long i;
|
||||
unsigned short __user *pc16 = NULL;
|
||||
|
||||
printk("\nCode:");
|
||||
|
||||
if ((unsigned long)pc & 1)
|
||||
pc16 = (unsigned short __user *)((unsigned long)pc & ~1);
|
||||
for(i = -3 ; i < 6 ; i++) {
|
||||
unsigned int insn;
|
||||
if (__get_user(insn, pc + i)) {
|
||||
if (pc16 ? __get_user(insn, pc16 + i) : __get_user(insn, pc + i)) {
|
||||
printk(" (Bad address in epc)\n");
|
||||
break;
|
||||
}
|
||||
printk("%c%08x%c", (i?' ':'<'), insn, (i?' ':'>'));
|
||||
printk("%c%0*x%c", (i?' ':'<'), pc16 ? 4 : 8, insn, (i?' ':'>'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,10 +318,21 @@ void show_regs(struct pt_regs *regs)
|
||||
|
||||
void show_registers(const struct pt_regs *regs)
|
||||
{
|
||||
const int field = 2 * sizeof(unsigned long);
|
||||
|
||||
__show_regs(regs);
|
||||
print_modules();
|
||||
printk("Process %s (pid: %d, threadinfo=%p, task=%p)\n",
|
||||
current->comm, task_pid_nr(current), current_thread_info(), current);
|
||||
printk("Process %s (pid: %d, threadinfo=%p, task=%p, tls=%0*lx)\n",
|
||||
current->comm, current->pid, current_thread_info(), current,
|
||||
field, current_thread_info()->tp_value);
|
||||
if (cpu_has_userlocal) {
|
||||
unsigned long tls;
|
||||
|
||||
tls = read_c0_userlocal();
|
||||
if (tls != current_thread_info()->tp_value)
|
||||
printk("*HwTLS: %0*lx\n", field, tls);
|
||||
}
|
||||
|
||||
show_stacktrace(current, regs);
|
||||
show_code((unsigned int __user *) regs->cp0_epc);
|
||||
printk("\n");
|
||||
@@ -985,6 +1003,21 @@ asmlinkage void do_reserved(struct pt_regs *regs)
|
||||
(regs->cp0_cause & 0x7f) >> 2);
|
||||
}
|
||||
|
||||
static int __initdata l1parity = 1;
|
||||
static int __init nol1parity(char *s)
|
||||
{
|
||||
l1parity = 0;
|
||||
return 1;
|
||||
}
|
||||
__setup("nol1par", nol1parity);
|
||||
static int __initdata l2parity = 1;
|
||||
static int __init nol2parity(char *s)
|
||||
{
|
||||
l2parity = 0;
|
||||
return 1;
|
||||
}
|
||||
__setup("nol2par", nol2parity);
|
||||
|
||||
/*
|
||||
* Some MIPS CPUs can enable/disable for cache parity detection, but do
|
||||
* it different ways.
|
||||
@@ -994,6 +1027,62 @@ static inline void parity_protection_init(void)
|
||||
switch (current_cpu_type()) {
|
||||
case CPU_24K:
|
||||
case CPU_34K:
|
||||
case CPU_74K:
|
||||
case CPU_1004K:
|
||||
{
|
||||
#define ERRCTL_PE 0x80000000
|
||||
#define ERRCTL_L2P 0x00800000
|
||||
unsigned long errctl;
|
||||
unsigned int l1parity_present, l2parity_present;
|
||||
|
||||
errctl = read_c0_ecc();
|
||||
errctl &= ~(ERRCTL_PE|ERRCTL_L2P);
|
||||
|
||||
/* probe L1 parity support */
|
||||
write_c0_ecc(errctl | ERRCTL_PE);
|
||||
back_to_back_c0_hazard();
|
||||
l1parity_present = (read_c0_ecc() & ERRCTL_PE);
|
||||
|
||||
/* probe L2 parity support */
|
||||
write_c0_ecc(errctl|ERRCTL_L2P);
|
||||
back_to_back_c0_hazard();
|
||||
l2parity_present = (read_c0_ecc() & ERRCTL_L2P);
|
||||
|
||||
if (l1parity_present && l2parity_present) {
|
||||
if (l1parity)
|
||||
errctl |= ERRCTL_PE;
|
||||
if (l1parity ^ l2parity)
|
||||
errctl |= ERRCTL_L2P;
|
||||
} else if (l1parity_present) {
|
||||
if (l1parity)
|
||||
errctl |= ERRCTL_PE;
|
||||
} else if (l2parity_present) {
|
||||
if (l2parity)
|
||||
errctl |= ERRCTL_L2P;
|
||||
} else {
|
||||
/* No parity available */
|
||||
}
|
||||
|
||||
printk(KERN_INFO "Writing ErrCtl register=%08lx\n", errctl);
|
||||
|
||||
write_c0_ecc(errctl);
|
||||
back_to_back_c0_hazard();
|
||||
errctl = read_c0_ecc();
|
||||
printk(KERN_INFO "Readback ErrCtl register=%08lx\n", errctl);
|
||||
|
||||
if (l1parity_present)
|
||||
printk(KERN_INFO "Cache parity protection %sabled\n",
|
||||
(errctl & ERRCTL_PE) ? "en" : "dis");
|
||||
|
||||
if (l2parity_present) {
|
||||
if (l1parity_present && l1parity)
|
||||
errctl ^= ERRCTL_L2P;
|
||||
printk(KERN_INFO "L2 cache parity protection %sabled\n",
|
||||
(errctl & ERRCTL_L2P) ? "en" : "dis");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CPU_5KC:
|
||||
write_c0_ecc(0x80000000);
|
||||
back_to_back_c0_hazard();
|
||||
@@ -1353,7 +1442,6 @@ void __cpuinit per_cpu_trap_init(void)
|
||||
change_c0_status(ST0_CU|ST0_MX|ST0_RE|ST0_FR|ST0_BEV|ST0_TS|ST0_KX|ST0_SX|ST0_UX,
|
||||
status_set);
|
||||
|
||||
#ifdef CONFIG_CPU_MIPSR2
|
||||
if (cpu_has_mips_r2) {
|
||||
unsigned int enable = 0x0000000f;
|
||||
|
||||
@@ -1362,7 +1450,6 @@ void __cpuinit per_cpu_trap_init(void)
|
||||
|
||||
write_c0_hwrena(enable);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MIPS_MT_SMTC
|
||||
if (!secondaryTC) {
|
||||
|
Reference in New Issue
Block a user