Merge branch 'mti-next' of git://git.linux-mips.org/pub/scm/sjhill/linux-sjhill into mips-for-linux-next
This commit is contained in:
@@ -19,15 +19,16 @@ obj-$(CONFIG_CEVT_BCM1480) += cevt-bcm1480.o
|
||||
obj-$(CONFIG_CEVT_R4K) += cevt-r4k.o
|
||||
obj-$(CONFIG_MIPS_MT_SMTC) += cevt-smtc.o
|
||||
obj-$(CONFIG_CEVT_DS1287) += cevt-ds1287.o
|
||||
obj-$(CONFIG_CEVT_GIC) += cevt-gic.o
|
||||
obj-$(CONFIG_CEVT_GT641XX) += cevt-gt641xx.o
|
||||
obj-$(CONFIG_CEVT_SB1250) += cevt-sb1250.o
|
||||
obj-$(CONFIG_CEVT_TXX9) += cevt-txx9.o
|
||||
obj-$(CONFIG_CSRC_BCM1480) += csrc-bcm1480.o
|
||||
obj-$(CONFIG_CSRC_GIC) += csrc-gic.o
|
||||
obj-$(CONFIG_CSRC_IOASIC) += csrc-ioasic.o
|
||||
obj-$(CONFIG_CSRC_POWERTV) += csrc-powertv.o
|
||||
obj-$(CONFIG_CSRC_R4K) += csrc-r4k.o
|
||||
obj-$(CONFIG_CSRC_SB1250) += csrc-sb1250.o
|
||||
obj-$(CONFIG_CSRC_GIC) += csrc-gic.o
|
||||
obj-$(CONFIG_SYNC_R4K) += sync-r4k.o
|
||||
|
||||
obj-$(CONFIG_STACKTRACE) += stacktrace.o
|
||||
|
@@ -14,10 +14,186 @@
|
||||
#include <asm/cpu.h>
|
||||
#include <asm/cpu-features.h>
|
||||
#include <asm/fpu.h>
|
||||
#include <asm/fpu_emulator.h>
|
||||
#include <asm/inst.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
/*
|
||||
* Calculate and return exception PC in case of branch delay slot
|
||||
* for microMIPS and MIPS16e. It does not clear the ISA mode bit.
|
||||
*/
|
||||
int __isa_exception_epc(struct pt_regs *regs)
|
||||
{
|
||||
unsigned short inst;
|
||||
long epc = regs->cp0_epc;
|
||||
|
||||
/* Calculate exception PC in branch delay slot. */
|
||||
if (__get_user(inst, (u16 __user *) msk_isa16_mode(epc))) {
|
||||
/* This should never happen because delay slot was checked. */
|
||||
force_sig(SIGSEGV, current);
|
||||
return epc;
|
||||
}
|
||||
if (cpu_has_mips16) {
|
||||
if (((union mips16e_instruction)inst).ri.opcode
|
||||
== MIPS16e_jal_op)
|
||||
epc += 4;
|
||||
else
|
||||
epc += 2;
|
||||
} else if (mm_insn_16bit(inst))
|
||||
epc += 2;
|
||||
else
|
||||
epc += 4;
|
||||
|
||||
return epc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute return address and emulate branch in microMIPS mode after an
|
||||
* exception only. It does not handle compact branches/jumps and cannot
|
||||
* be used in interrupt context. (Compact branches/jumps do not cause
|
||||
* exceptions.)
|
||||
*/
|
||||
int __microMIPS_compute_return_epc(struct pt_regs *regs)
|
||||
{
|
||||
u16 __user *pc16;
|
||||
u16 halfword;
|
||||
unsigned int word;
|
||||
unsigned long contpc;
|
||||
struct mm_decoded_insn mminsn = { 0 };
|
||||
|
||||
mminsn.micro_mips_mode = 1;
|
||||
|
||||
/* This load never faults. */
|
||||
pc16 = (unsigned short __user *)msk_isa16_mode(regs->cp0_epc);
|
||||
__get_user(halfword, pc16);
|
||||
pc16++;
|
||||
contpc = regs->cp0_epc + 2;
|
||||
word = ((unsigned int)halfword << 16);
|
||||
mminsn.pc_inc = 2;
|
||||
|
||||
if (!mm_insn_16bit(halfword)) {
|
||||
__get_user(halfword, pc16);
|
||||
pc16++;
|
||||
contpc = regs->cp0_epc + 4;
|
||||
mminsn.pc_inc = 4;
|
||||
word |= halfword;
|
||||
}
|
||||
mminsn.insn = word;
|
||||
|
||||
if (get_user(halfword, pc16))
|
||||
goto sigsegv;
|
||||
mminsn.next_pc_inc = 2;
|
||||
word = ((unsigned int)halfword << 16);
|
||||
|
||||
if (!mm_insn_16bit(halfword)) {
|
||||
pc16++;
|
||||
if (get_user(halfword, pc16))
|
||||
goto sigsegv;
|
||||
mminsn.next_pc_inc = 4;
|
||||
word |= halfword;
|
||||
}
|
||||
mminsn.next_insn = word;
|
||||
|
||||
mm_isBranchInstr(regs, mminsn, &contpc);
|
||||
|
||||
regs->cp0_epc = contpc;
|
||||
|
||||
return 0;
|
||||
|
||||
sigsegv:
|
||||
force_sig(SIGSEGV, current);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute return address and emulate branch in MIPS16e mode after an
|
||||
* exception only. It does not handle compact branches/jumps and cannot
|
||||
* be used in interrupt context. (Compact branches/jumps do not cause
|
||||
* exceptions.)
|
||||
*/
|
||||
int __MIPS16e_compute_return_epc(struct pt_regs *regs)
|
||||
{
|
||||
u16 __user *addr;
|
||||
union mips16e_instruction inst;
|
||||
u16 inst2;
|
||||
u32 fullinst;
|
||||
long epc;
|
||||
|
||||
epc = regs->cp0_epc;
|
||||
|
||||
/* Read the instruction. */
|
||||
addr = (u16 __user *)msk_isa16_mode(epc);
|
||||
if (__get_user(inst.full, addr)) {
|
||||
force_sig(SIGSEGV, current);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
switch (inst.ri.opcode) {
|
||||
case MIPS16e_extend_op:
|
||||
regs->cp0_epc += 4;
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* JAL and JALX in MIPS16e mode
|
||||
*/
|
||||
case MIPS16e_jal_op:
|
||||
addr += 1;
|
||||
if (__get_user(inst2, addr)) {
|
||||
force_sig(SIGSEGV, current);
|
||||
return -EFAULT;
|
||||
}
|
||||
fullinst = ((unsigned)inst.full << 16) | inst2;
|
||||
regs->regs[31] = epc + 6;
|
||||
epc += 4;
|
||||
epc >>= 28;
|
||||
epc <<= 28;
|
||||
/*
|
||||
* JAL:5 X:1 TARGET[20-16]:5 TARGET[25:21]:5 TARGET[15:0]:16
|
||||
*
|
||||
* ......TARGET[15:0].................TARGET[20:16]...........
|
||||
* ......TARGET[25:21]
|
||||
*/
|
||||
epc |=
|
||||
((fullinst & 0xffff) << 2) | ((fullinst & 0x3e00000) >> 3) |
|
||||
((fullinst & 0x1f0000) << 7);
|
||||
if (!inst.jal.x)
|
||||
set_isa16_mode(epc); /* Set ISA mode bit. */
|
||||
regs->cp0_epc = epc;
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* J(AL)R(C)
|
||||
*/
|
||||
case MIPS16e_rr_op:
|
||||
if (inst.rr.func == MIPS16e_jr_func) {
|
||||
|
||||
if (inst.rr.ra)
|
||||
regs->cp0_epc = regs->regs[31];
|
||||
else
|
||||
regs->cp0_epc =
|
||||
regs->regs[reg16to32[inst.rr.rx]];
|
||||
|
||||
if (inst.rr.l) {
|
||||
if (inst.rr.nd)
|
||||
regs->regs[31] = epc + 2;
|
||||
else
|
||||
regs->regs[31] = epc + 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* All other cases have no branch delay slot and are 16-bits.
|
||||
* Branches do not cause an exception.
|
||||
*/
|
||||
regs->cp0_epc += 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* __compute_return_epc_for_insn - Computes the return address and do emulate
|
||||
* branch simulation, if required.
|
||||
@@ -129,6 +305,8 @@ int __compute_return_epc_for_insn(struct pt_regs *regs,
|
||||
epc <<= 28;
|
||||
epc |= (insn.j_format.target << 2);
|
||||
regs->cp0_epc = epc;
|
||||
if (insn.i_format.opcode == jalx_op)
|
||||
set_isa16_mode(regs->cp0_epc);
|
||||
break;
|
||||
|
||||
/*
|
||||
|
104
arch/mips/kernel/cevt-gic.c
Normal file
104
arch/mips/kernel/cevt-gic.c
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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 Imagination Technologies Ltd.
|
||||
*/
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/irq.h>
|
||||
|
||||
#include <asm/time.h>
|
||||
#include <asm/gic.h>
|
||||
#include <asm/mips-boards/maltaint.h>
|
||||
|
||||
DEFINE_PER_CPU(struct clock_event_device, gic_clockevent_device);
|
||||
int gic_timer_irq_installed;
|
||||
|
||||
|
||||
static int gic_next_event(unsigned long delta, struct clock_event_device *evt)
|
||||
{
|
||||
u64 cnt;
|
||||
int res;
|
||||
|
||||
cnt = gic_read_count();
|
||||
cnt += (u64)delta;
|
||||
gic_write_compare(cnt);
|
||||
res = ((int)(gic_read_count() - cnt) >= 0) ? -ETIME : 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
void gic_set_clock_mode(enum clock_event_mode mode,
|
||||
struct clock_event_device *evt)
|
||||
{
|
||||
/* Nothing to do ... */
|
||||
}
|
||||
|
||||
irqreturn_t gic_compare_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct clock_event_device *cd;
|
||||
int cpu = smp_processor_id();
|
||||
|
||||
gic_write_compare(gic_read_compare());
|
||||
cd = &per_cpu(gic_clockevent_device, cpu);
|
||||
cd->event_handler(cd);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
struct irqaction gic_compare_irqaction = {
|
||||
.handler = gic_compare_interrupt,
|
||||
.flags = IRQF_PERCPU | IRQF_TIMER,
|
||||
.name = "timer",
|
||||
};
|
||||
|
||||
|
||||
void gic_event_handler(struct clock_event_device *dev)
|
||||
{
|
||||
}
|
||||
|
||||
int __cpuinit gic_clockevent_init(void)
|
||||
{
|
||||
unsigned int cpu = smp_processor_id();
|
||||
struct clock_event_device *cd;
|
||||
unsigned int irq;
|
||||
|
||||
if (!cpu_has_counter || !gic_frequency)
|
||||
return -ENXIO;
|
||||
|
||||
irq = MIPS_GIC_IRQ_BASE;
|
||||
|
||||
cd = &per_cpu(gic_clockevent_device, cpu);
|
||||
|
||||
cd->name = "MIPS GIC";
|
||||
cd->features = CLOCK_EVT_FEAT_ONESHOT;
|
||||
|
||||
clockevent_set_clock(cd, gic_frequency);
|
||||
|
||||
/* Calculate the min / max delta */
|
||||
cd->max_delta_ns = clockevent_delta2ns(0x7fffffff, cd);
|
||||
cd->min_delta_ns = clockevent_delta2ns(0x300, cd);
|
||||
|
||||
cd->rating = 300;
|
||||
cd->irq = irq;
|
||||
cd->cpumask = cpumask_of(cpu);
|
||||
cd->set_next_event = gic_next_event;
|
||||
cd->set_mode = gic_set_clock_mode;
|
||||
cd->event_handler = gic_event_handler;
|
||||
|
||||
clockevents_register_device(cd);
|
||||
|
||||
GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_MAP), 0x80000002);
|
||||
GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_SMASK), GIC_VPE_SMASK_CMP_MSK);
|
||||
|
||||
if (gic_timer_irq_installed)
|
||||
return 0;
|
||||
|
||||
gic_timer_irq_installed = 1;
|
||||
|
||||
setup_irq(irq, &gic_compare_irqaction);
|
||||
irq_set_handler(irq, handle_percpu_irq);
|
||||
return 0;
|
||||
}
|
@@ -23,7 +23,6 @@
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_MIPS_MT_SMTC
|
||||
|
||||
static int mips_next_event(unsigned long delta,
|
||||
struct clock_event_device *evt)
|
||||
{
|
||||
@@ -49,7 +48,6 @@ DEFINE_PER_CPU(struct clock_event_device, mips_clockevent_device);
|
||||
int cp0_timer_irq_installed;
|
||||
|
||||
#ifndef CONFIG_MIPS_MT_SMTC
|
||||
|
||||
irqreturn_t c0_compare_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
const int r2 = cpu_has_mips_r2;
|
||||
@@ -74,6 +72,9 @@ irqreturn_t c0_compare_interrupt(int irq, void *dev_id)
|
||||
/* Clear Count/Compare Interrupt */
|
||||
write_c0_compare(read_c0_compare());
|
||||
cd = &per_cpu(mips_clockevent_device, cpu);
|
||||
#ifdef CONFIG_CEVT_GIC
|
||||
if (!gic_present)
|
||||
#endif
|
||||
cd->event_handler(cd);
|
||||
}
|
||||
|
||||
@@ -170,7 +171,6 @@ int c0_compare_int_usable(void)
|
||||
}
|
||||
|
||||
#ifndef CONFIG_MIPS_MT_SMTC
|
||||
|
||||
int __cpuinit r4k_clockevent_init(void)
|
||||
{
|
||||
unsigned int cpu = smp_processor_id();
|
||||
@@ -210,6 +210,9 @@ int __cpuinit r4k_clockevent_init(void)
|
||||
cd->set_mode = mips_set_clock_mode;
|
||||
cd->event_handler = mips_event_handler;
|
||||
|
||||
#ifdef CONFIG_CEVT_GIC
|
||||
if (!gic_present)
|
||||
#endif
|
||||
clockevents_register_device(cd);
|
||||
|
||||
if (cp0_timer_irq_installed)
|
||||
|
@@ -470,6 +470,9 @@ static inline unsigned int decode_config3(struct cpuinfo_mips *c)
|
||||
c->options |= MIPS_CPU_ULRI;
|
||||
if (config3 & MIPS_CONF3_ISA)
|
||||
c->options |= MIPS_CPU_MICROMIPS;
|
||||
#ifdef CONFIG_CPU_MICROMIPS
|
||||
write_c0_config3(read_c0_config3() | MIPS_CONF3_ISA_OE);
|
||||
#endif
|
||||
if (config3 & MIPS_CONF3_VZ)
|
||||
c->ases |= MIPS_ASE_VZ;
|
||||
|
||||
|
@@ -5,23 +5,14 @@
|
||||
*
|
||||
* Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved.
|
||||
*/
|
||||
#include <linux/clocksource.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/time.h>
|
||||
|
||||
#include <asm/time.h>
|
||||
#include <asm/gic.h>
|
||||
|
||||
static cycle_t gic_hpt_read(struct clocksource *cs)
|
||||
{
|
||||
unsigned int hi, hi2, lo;
|
||||
|
||||
do {
|
||||
GICREAD(GIC_REG(SHARED, GIC_SH_COUNTER_63_32), hi);
|
||||
GICREAD(GIC_REG(SHARED, GIC_SH_COUNTER_31_00), lo);
|
||||
GICREAD(GIC_REG(SHARED, GIC_SH_COUNTER_63_32), hi2);
|
||||
} while (hi2 != hi);
|
||||
|
||||
return (((cycle_t) hi) << 32) + lo;
|
||||
return gic_read_count();
|
||||
}
|
||||
|
||||
static struct clocksource gic_clocksource = {
|
||||
|
@@ -5,8 +5,8 @@
|
||||
*
|
||||
* Copyright (C) 1994 - 2000, 2001, 2003 Ralf Baechle
|
||||
* Copyright (C) 1999, 2000 Silicon Graphics, Inc.
|
||||
* Copyright (C) 2001 MIPS Technologies, Inc.
|
||||
* Copyright (C) 2002, 2007 Maciej W. Rozycki
|
||||
* Copyright (C) 2001, 2012 MIPS Technologies, Inc. All rights reserved.
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
|
||||
@@ -21,8 +21,10 @@
|
||||
#include <asm/war.h>
|
||||
#include <asm/thread_info.h>
|
||||
|
||||
#ifdef CONFIG_MIPS_MT_SMTC
|
||||
#define PANIC_PIC(msg) \
|
||||
.set push; \
|
||||
.set push; \
|
||||
.set nomicromips; \
|
||||
.set reorder; \
|
||||
PTR_LA a0,8f; \
|
||||
.set noat; \
|
||||
@@ -31,17 +33,10 @@
|
||||
9: b 9b; \
|
||||
.set pop; \
|
||||
TEXT(msg)
|
||||
#endif
|
||||
|
||||
__INIT
|
||||
|
||||
NESTED(except_vec0_generic, 0, sp)
|
||||
PANIC_PIC("Exception vector 0 called")
|
||||
END(except_vec0_generic)
|
||||
|
||||
NESTED(except_vec1_generic, 0, sp)
|
||||
PANIC_PIC("Exception vector 1 called")
|
||||
END(except_vec1_generic)
|
||||
|
||||
/*
|
||||
* General exception vector for all other CPUs.
|
||||
*
|
||||
@@ -138,12 +133,19 @@ LEAF(r4k_wait)
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
#ifdef CONFIG_CPU_MICROMIPS
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
#endif
|
||||
.set mips3
|
||||
wait
|
||||
/* end of rollback region (the region size must be power of two) */
|
||||
.set pop
|
||||
1:
|
||||
jr ra
|
||||
nop
|
||||
.set pop
|
||||
END(r4k_wait)
|
||||
|
||||
.macro BUILD_ROLLBACK_PROLOGUE handler
|
||||
@@ -201,7 +203,11 @@ NESTED(handle_int, PT_SIZE, sp)
|
||||
LONG_L s0, TI_REGS($28)
|
||||
LONG_S sp, TI_REGS($28)
|
||||
PTR_LA ra, ret_from_irq
|
||||
j plat_irq_dispatch
|
||||
PTR_LA v0, plat_irq_dispatch
|
||||
jr v0
|
||||
#ifdef CONFIG_CPU_MICROMIPS
|
||||
nop
|
||||
#endif
|
||||
END(handle_int)
|
||||
|
||||
__INIT
|
||||
@@ -222,11 +228,14 @@ NESTED(except_vec4, 0, sp)
|
||||
/*
|
||||
* EJTAG debug exception handler.
|
||||
* The EJTAG debug exception entry point is 0xbfc00480, which
|
||||
* normally is in the boot PROM, so the boot PROM must do a
|
||||
* normally is in the boot PROM, so the boot PROM must do an
|
||||
* unconditional jump to this vector.
|
||||
*/
|
||||
NESTED(except_vec_ejtag_debug, 0, sp)
|
||||
j ejtag_debug_handler
|
||||
#ifdef CONFIG_CPU_MICROMIPS
|
||||
nop
|
||||
#endif
|
||||
END(except_vec_ejtag_debug)
|
||||
|
||||
__FINIT
|
||||
@@ -251,9 +260,10 @@ NESTED(except_vec_vi, 0, sp)
|
||||
FEXPORT(except_vec_vi_mori)
|
||||
ori a0, $0, 0
|
||||
#endif /* CONFIG_MIPS_MT_SMTC */
|
||||
PTR_LA v1, except_vec_vi_handler
|
||||
FEXPORT(except_vec_vi_lui)
|
||||
lui v0, 0 /* Patched */
|
||||
j except_vec_vi_handler
|
||||
jr v1
|
||||
FEXPORT(except_vec_vi_ori)
|
||||
ori v0, 0 /* Patched */
|
||||
.set pop
|
||||
@@ -354,6 +364,9 @@ EXPORT(ejtag_debug_buffer)
|
||||
*/
|
||||
NESTED(except_vec_nmi, 0, sp)
|
||||
j nmi_handler
|
||||
#ifdef CONFIG_CPU_MICROMIPS
|
||||
nop
|
||||
#endif
|
||||
END(except_vec_nmi)
|
||||
|
||||
__FINIT
|
||||
@@ -480,7 +493,7 @@ NESTED(nmi_handler, PT_SIZE, sp)
|
||||
.set noreorder
|
||||
/* check if TLB contains a entry for EPC */
|
||||
MFC0 k1, CP0_ENTRYHI
|
||||
andi k1, 0xff /* ASID_MASK */
|
||||
andi k1, 0xff /* ASID_MASK patched at run-time!! */
|
||||
MFC0 k0, CP0_EPC
|
||||
PTR_SRL k0, _PAGE_SHIFT + 1
|
||||
PTR_SLL k0, _PAGE_SHIFT + 1
|
||||
@@ -500,13 +513,35 @@ NESTED(nmi_handler, PT_SIZE, sp)
|
||||
.set push
|
||||
.set noat
|
||||
.set noreorder
|
||||
/* 0x7c03e83b: rdhwr v1,$29 */
|
||||
/* MIPS32: 0x7c03e83b: rdhwr v1,$29 */
|
||||
/* microMIPS: 0x007d6b3c: rdhwr v1,$29 */
|
||||
MFC0 k1, CP0_EPC
|
||||
lui k0, 0x7c03
|
||||
lw k1, (k1)
|
||||
ori k0, 0xe83b
|
||||
.set reorder
|
||||
#if defined(CONFIG_CPU_MICROMIPS) || defined(CONFIG_CPU_MIPS32_R2) || defined(CONFIG_CPU_MIPS64_R2)
|
||||
and k0, k1, 1
|
||||
beqz k0, 1f
|
||||
xor k1, k0
|
||||
lhu k0, (k1)
|
||||
lhu k1, 2(k1)
|
||||
ins k1, k0, 16, 16
|
||||
lui k0, 0x007d
|
||||
b docheck
|
||||
ori k0, 0x6b3c
|
||||
1:
|
||||
lui k0, 0x7c03
|
||||
lw k1, (k1)
|
||||
ori k0, 0xe83b
|
||||
#else
|
||||
andi k0, k1, 1
|
||||
bnez k0, handle_ri
|
||||
lui k0, 0x7c03
|
||||
lw k1, (k1)
|
||||
ori k0, 0xe83b
|
||||
#endif
|
||||
.set reorder
|
||||
docheck:
|
||||
bne k0, k1, handle_ri /* if not ours */
|
||||
|
||||
isrdhwr:
|
||||
/* The insn is rdhwr. No need to check CAUSE.BD here. */
|
||||
get_saved_sp /* k1 := current_thread_info */
|
||||
.set noreorder
|
||||
|
@@ -10,6 +10,7 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/clocksource.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/gic.h>
|
||||
@@ -19,6 +20,8 @@
|
||||
#include <linux/hardirq.h>
|
||||
#include <asm-generic/bitops/find.h>
|
||||
|
||||
unsigned int gic_frequency;
|
||||
unsigned int gic_present;
|
||||
unsigned long _gic_base;
|
||||
unsigned int gic_irq_base;
|
||||
unsigned int gic_irq_flags[GIC_NUM_INTRS];
|
||||
@@ -30,6 +33,39 @@ 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];
|
||||
|
||||
#if defined(CONFIG_CSRC_GIC) || defined(CONFIG_CEVT_GIC)
|
||||
cycle_t gic_read_count(void)
|
||||
{
|
||||
unsigned int hi, hi2, lo;
|
||||
|
||||
do {
|
||||
GICREAD(GIC_REG(SHARED, GIC_SH_COUNTER_63_32), hi);
|
||||
GICREAD(GIC_REG(SHARED, GIC_SH_COUNTER_31_00), lo);
|
||||
GICREAD(GIC_REG(SHARED, GIC_SH_COUNTER_63_32), hi2);
|
||||
} while (hi2 != hi);
|
||||
|
||||
return (((cycle_t) hi) << 32) + lo;
|
||||
}
|
||||
|
||||
void gic_write_compare(cycle_t cnt)
|
||||
{
|
||||
GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_HI),
|
||||
(int)(cnt >> 32));
|
||||
GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_LO),
|
||||
(int)(cnt & 0xffffffff));
|
||||
}
|
||||
|
||||
cycle_t gic_read_compare(void)
|
||||
{
|
||||
unsigned int hi, lo;
|
||||
|
||||
GICREAD(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_HI), hi);
|
||||
GICREAD(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_LO), lo);
|
||||
|
||||
return (((cycle_t) hi) << 32) + lo;
|
||||
}
|
||||
#endif
|
||||
|
||||
unsigned int gic_get_timer_pending(void)
|
||||
{
|
||||
unsigned int vpe_pending;
|
||||
@@ -116,6 +152,17 @@ static void __init vpe_local_setup(unsigned int numvpes)
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int gic_compare_int(void)
|
||||
{
|
||||
unsigned int pending;
|
||||
|
||||
GICREAD(GIC_REG(VPE_LOCAL, GIC_VPE_PEND), pending);
|
||||
if (pending & GIC_VPE_PEND_CMP_MSK)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int gic_get_int(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
@@ -99,6 +99,10 @@ static int show_cpuinfo(struct seq_file *m, void *v)
|
||||
if (cpu_has_vz) seq_printf(m, "%s", " vz");
|
||||
seq_printf(m, "\n");
|
||||
|
||||
if (cpu_has_mmips) {
|
||||
seq_printf(m, "micromips kernel\t: %s\n",
|
||||
(read_c0_config3() & MIPS_CONF3_ISA_OE) ? "yes" : "no");
|
||||
}
|
||||
seq_printf(m, "shadow register sets\t: %d\n",
|
||||
cpu_data[n].srsets);
|
||||
seq_printf(m, "kscratch registers\t: %d\n",
|
||||
|
@@ -7,6 +7,7 @@
|
||||
* Copyright (C) 2005, 2006 by Ralf Baechle (ralf@linux-mips.org)
|
||||
* Copyright (C) 1999, 2000 Silicon Graphics, Inc.
|
||||
* Copyright (C) 2004 Thiemo Seufer
|
||||
* Copyright (C) 2013 Imagination Technologies Ltd.
|
||||
*/
|
||||
#include <linux/errno.h>
|
||||
#include <linux/sched.h>
|
||||
@@ -243,34 +244,115 @@ struct mips_frame_info {
|
||||
|
||||
static inline int is_ra_save_ins(union mips_instruction *ip)
|
||||
{
|
||||
#ifdef CONFIG_CPU_MICROMIPS
|
||||
union mips_instruction mmi;
|
||||
|
||||
/*
|
||||
* swsp ra,offset
|
||||
* swm16 reglist,offset(sp)
|
||||
* swm32 reglist,offset(sp)
|
||||
* sw32 ra,offset(sp)
|
||||
* jradiussp - NOT SUPPORTED
|
||||
*
|
||||
* microMIPS is way more fun...
|
||||
*/
|
||||
if (mm_insn_16bit(ip->halfword[0])) {
|
||||
mmi.word = (ip->halfword[0] << 16);
|
||||
return ((mmi.mm16_r5_format.opcode == mm_swsp16_op &&
|
||||
mmi.mm16_r5_format.rt == 31) ||
|
||||
(mmi.mm16_m_format.opcode == mm_pool16c_op &&
|
||||
mmi.mm16_m_format.func == mm_swm16_op));
|
||||
}
|
||||
else {
|
||||
mmi.halfword[0] = ip->halfword[1];
|
||||
mmi.halfword[1] = ip->halfword[0];
|
||||
return ((mmi.mm_m_format.opcode == mm_pool32b_op &&
|
||||
mmi.mm_m_format.rd > 9 &&
|
||||
mmi.mm_m_format.base == 29 &&
|
||||
mmi.mm_m_format.func == mm_swm32_func) ||
|
||||
(mmi.i_format.opcode == mm_sw32_op &&
|
||||
mmi.i_format.rs == 29 &&
|
||||
mmi.i_format.rt == 31));
|
||||
}
|
||||
#else
|
||||
/* sw / sd $ra, offset($sp) */
|
||||
return (ip->i_format.opcode == sw_op || ip->i_format.opcode == sd_op) &&
|
||||
ip->i_format.rs == 29 &&
|
||||
ip->i_format.rt == 31;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int is_jal_jalr_jr_ins(union mips_instruction *ip)
|
||||
{
|
||||
#ifdef CONFIG_CPU_MICROMIPS
|
||||
/*
|
||||
* jr16,jrc,jalr16,jalr16
|
||||
* jal
|
||||
* jalr/jr,jalr.hb/jr.hb,jalrs,jalrs.hb
|
||||
* jraddiusp - NOT SUPPORTED
|
||||
*
|
||||
* microMIPS is kind of more fun...
|
||||
*/
|
||||
union mips_instruction mmi;
|
||||
|
||||
mmi.word = (ip->halfword[0] << 16);
|
||||
|
||||
if ((mmi.mm16_r5_format.opcode == mm_pool16c_op &&
|
||||
(mmi.mm16_r5_format.rt & mm_jr16_op) == mm_jr16_op) ||
|
||||
ip->j_format.opcode == mm_jal32_op)
|
||||
return 1;
|
||||
if (ip->r_format.opcode != mm_pool32a_op ||
|
||||
ip->r_format.func != mm_pool32axf_op)
|
||||
return 0;
|
||||
return (((ip->u_format.uimmediate >> 6) & mm_jalr_op) == mm_jalr_op);
|
||||
#else
|
||||
if (ip->j_format.opcode == jal_op)
|
||||
return 1;
|
||||
if (ip->r_format.opcode != spec_op)
|
||||
return 0;
|
||||
return ip->r_format.func == jalr_op || ip->r_format.func == jr_op;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int is_sp_move_ins(union mips_instruction *ip)
|
||||
{
|
||||
#ifdef CONFIG_CPU_MICROMIPS
|
||||
/*
|
||||
* addiusp -imm
|
||||
* addius5 sp,-imm
|
||||
* addiu32 sp,sp,-imm
|
||||
* jradiussp - NOT SUPPORTED
|
||||
*
|
||||
* microMIPS is not more fun...
|
||||
*/
|
||||
if (mm_insn_16bit(ip->halfword[0])) {
|
||||
union mips_instruction mmi;
|
||||
|
||||
mmi.word = (ip->halfword[0] << 16);
|
||||
return ((mmi.mm16_r3_format.opcode == mm_pool16d_op &&
|
||||
mmi.mm16_r3_format.simmediate && mm_addiusp_func) ||
|
||||
(mmi.mm16_r5_format.opcode == mm_pool16d_op &&
|
||||
mmi.mm16_r5_format.rt == 29));
|
||||
}
|
||||
return (ip->mm_i_format.opcode == mm_addiu32_op &&
|
||||
ip->mm_i_format.rt == 29 && ip->mm_i_format.rs == 29);
|
||||
#else
|
||||
/* addiu/daddiu sp,sp,-imm */
|
||||
if (ip->i_format.rs != 29 || ip->i_format.rt != 29)
|
||||
return 0;
|
||||
if (ip->i_format.opcode == addiu_op || ip->i_format.opcode == daddiu_op)
|
||||
return 1;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_frame_info(struct mips_frame_info *info)
|
||||
{
|
||||
#ifdef CONFIG_CPU_MICROMIPS
|
||||
union mips_instruction *ip = (void *) (((char *) info->func) - 1);
|
||||
#else
|
||||
union mips_instruction *ip = info->func;
|
||||
#endif
|
||||
unsigned max_insns = info->func_size / sizeof(union mips_instruction);
|
||||
unsigned i;
|
||||
|
||||
@@ -290,7 +372,26 @@ static int get_frame_info(struct mips_frame_info *info)
|
||||
break;
|
||||
if (!info->frame_size) {
|
||||
if (is_sp_move_ins(ip))
|
||||
{
|
||||
#ifdef CONFIG_CPU_MICROMIPS
|
||||
if (mm_insn_16bit(ip->halfword[0]))
|
||||
{
|
||||
unsigned short tmp;
|
||||
|
||||
if (ip->halfword[0] & mm_addiusp_func)
|
||||
{
|
||||
tmp = (((ip->halfword[0] >> 1) & 0x1ff) << 2);
|
||||
info->frame_size = -(signed short)(tmp | ((tmp & 0x100) ? 0xfe00 : 0));
|
||||
} else {
|
||||
tmp = (ip->halfword[0] >> 1);
|
||||
info->frame_size = -(signed short)(tmp & 0xf);
|
||||
}
|
||||
ip = (void *) &ip->halfword[1];
|
||||
ip--;
|
||||
} else
|
||||
#endif
|
||||
info->frame_size = - ip->i_format.simmediate;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (info->pc_offset == -1 && is_ra_save_ins(ip)) {
|
||||
|
@@ -138,9 +138,18 @@ stackargs:
|
||||
5: jr t1
|
||||
sw t5, 16(sp) # argument #5 to ksp
|
||||
|
||||
#ifdef CONFIG_CPU_MICROMIPS
|
||||
sw t8, 28(sp) # argument #8 to ksp
|
||||
nop
|
||||
sw t7, 24(sp) # argument #7 to ksp
|
||||
nop
|
||||
sw t6, 20(sp) # argument #6 to ksp
|
||||
nop
|
||||
#else
|
||||
sw t8, 28(sp) # argument #8 to ksp
|
||||
sw t7, 24(sp) # argument #7 to ksp
|
||||
sw t6, 20(sp) # argument #6 to ksp
|
||||
#endif
|
||||
6: j stack_done # go back
|
||||
nop
|
||||
.set pop
|
||||
|
@@ -35,6 +35,7 @@
|
||||
#include <asm/war.h>
|
||||
#include <asm/vdso.h>
|
||||
#include <asm/dsp.h>
|
||||
#include <asm/inst.h>
|
||||
|
||||
#include "signal-common.h"
|
||||
|
||||
@@ -480,7 +481,15 @@ static void handle_signal(unsigned long sig, siginfo_t *info,
|
||||
sigset_t *oldset = sigmask_to_save();
|
||||
int ret;
|
||||
struct mips_abi *abi = current->thread.abi;
|
||||
#ifdef CONFIG_CPU_MICROMIPS
|
||||
void *vdso;
|
||||
unsigned int tmp = (unsigned int)current->mm->context.vdso;
|
||||
|
||||
set_isa16_mode(tmp);
|
||||
vdso = (void *)tmp;
|
||||
#else
|
||||
void *vdso = current->mm->context.vdso;
|
||||
#endif
|
||||
|
||||
if (regs->regs[0]) {
|
||||
switch(regs->regs[2]) {
|
||||
|
@@ -34,6 +34,7 @@
|
||||
#include <asm/mipsregs.h>
|
||||
#include <asm/mipsmtregs.h>
|
||||
#include <asm/mips_mt.h>
|
||||
#include <asm/gic.h>
|
||||
|
||||
static void __init smvp_copy_vpe_config(void)
|
||||
{
|
||||
@@ -151,8 +152,6 @@ static void vsmp_send_ipi_mask(const struct cpumask *mask, unsigned int action)
|
||||
static void __cpuinit vsmp_init_secondary(void)
|
||||
{
|
||||
#ifdef CONFIG_IRQ_GIC
|
||||
extern int gic_present;
|
||||
|
||||
/* This is Malta specific: IPI,performance and timer interrupts */
|
||||
if (gic_present)
|
||||
change_c0_status(ST0_IM, STATUSF_IP3 | STATUSF_IP4 |
|
||||
|
@@ -49,6 +49,9 @@ CAN WE PROVE THAT WE WON'T DO THIS IF INTS DISABLED??
|
||||
.text
|
||||
.align 5
|
||||
FEXPORT(__smtc_ipi_vector)
|
||||
#ifdef CONFIG_CPU_MICROMIPS
|
||||
nop
|
||||
#endif
|
||||
.set noat
|
||||
/* Disable thread scheduling to make Status update atomic */
|
||||
DMT 27 # dmt k1
|
||||
|
@@ -111,7 +111,7 @@ static int vpe0limit;
|
||||
static int ipibuffers;
|
||||
static int nostlb;
|
||||
static int asidmask;
|
||||
unsigned long smtc_asid_mask = 0xff;
|
||||
unsigned int smtc_asid_mask = 0xff;
|
||||
|
||||
static int __init vpe0tcs(char *str)
|
||||
{
|
||||
@@ -1395,7 +1395,7 @@ void smtc_get_new_mmu_context(struct mm_struct *mm, unsigned long cpu)
|
||||
asid = asid_cache(cpu);
|
||||
|
||||
do {
|
||||
if (!((asid += ASID_INC) & ASID_MASK) ) {
|
||||
if (!ASID_MASK(ASID_INC(asid))) {
|
||||
if (cpu_has_vtag_icache)
|
||||
flush_icache_all();
|
||||
/* Traverse all online CPUs (hack requires contiguous range) */
|
||||
@@ -1414,7 +1414,7 @@ void smtc_get_new_mmu_context(struct mm_struct *mm, unsigned long cpu)
|
||||
mips_ihb();
|
||||
}
|
||||
tcstat = read_tc_c0_tcstatus();
|
||||
smtc_live_asid[tlb][(tcstat & ASID_MASK)] |= (asiduse)(0x1 << i);
|
||||
smtc_live_asid[tlb][ASID_MASK(tcstat)] |= (asiduse)(0x1 << i);
|
||||
if (!prevhalt)
|
||||
write_tc_c0_tchalt(0);
|
||||
}
|
||||
@@ -1423,7 +1423,7 @@ void smtc_get_new_mmu_context(struct mm_struct *mm, unsigned long cpu)
|
||||
asid = ASID_FIRST_VERSION;
|
||||
local_flush_tlb_all(); /* start new asid cycle */
|
||||
}
|
||||
} while (smtc_live_asid[tlb][(asid & ASID_MASK)]);
|
||||
} while (smtc_live_asid[tlb][ASID_MASK(asid)]);
|
||||
|
||||
/*
|
||||
* SMTC shares the TLB within VPEs and possibly across all VPEs.
|
||||
@@ -1461,7 +1461,7 @@ void smtc_flush_tlb_asid(unsigned long asid)
|
||||
tlb_read();
|
||||
ehb();
|
||||
ehi = read_c0_entryhi();
|
||||
if ((ehi & ASID_MASK) == asid) {
|
||||
if (ASID_MASK(ehi) == asid) {
|
||||
/*
|
||||
* Invalidate only entries with specified ASID,
|
||||
* makiing sure all entries differ.
|
||||
|
@@ -8,8 +8,8 @@
|
||||
* Copyright (C) 1998 Ulf Carlsson
|
||||
* Copyright (C) 1999 Silicon Graphics, Inc.
|
||||
* Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com
|
||||
* Copyright (C) 2000, 01 MIPS Technologies, Inc.
|
||||
* Copyright (C) 2002, 2003, 2004, 2005, 2007 Maciej W. Rozycki
|
||||
* Copyright (C) 2000, 2001, 2012 MIPS Technologies, Inc. All rights reserved.
|
||||
*/
|
||||
#include <linux/bug.h>
|
||||
#include <linux/compiler.h>
|
||||
@@ -83,10 +83,6 @@ extern asmlinkage void handle_dsp(void);
|
||||
extern asmlinkage void handle_mcheck(void);
|
||||
extern asmlinkage void handle_reserved(void);
|
||||
|
||||
extern int fpu_emulator_cop1Handler(struct pt_regs *xcp,
|
||||
struct mips_fpu_struct *ctx, int has_fpu,
|
||||
void *__user *fault_addr);
|
||||
|
||||
void (*board_be_init)(void);
|
||||
int (*board_be_handler)(struct pt_regs *regs, int is_fixup);
|
||||
void (*board_nmi_handler_setup)(void);
|
||||
@@ -495,6 +491,12 @@ asmlinkage void do_be(struct pt_regs *regs)
|
||||
#define SYNC 0x0000000f
|
||||
#define RDHWR 0x0000003b
|
||||
|
||||
/* microMIPS definitions */
|
||||
#define MM_POOL32A_FUNC 0xfc00ffff
|
||||
#define MM_RDHWR 0x00006b3c
|
||||
#define MM_RS 0x001f0000
|
||||
#define MM_RT 0x03e00000
|
||||
|
||||
/*
|
||||
* The ll_bit is cleared by r*_switch.S
|
||||
*/
|
||||
@@ -609,42 +611,62 @@ static int simulate_llsc(struct pt_regs *regs, unsigned int opcode)
|
||||
* Simulate trapping 'rdhwr' instructions to provide user accessible
|
||||
* registers not implemented in hardware.
|
||||
*/
|
||||
static int simulate_rdhwr(struct pt_regs *regs, unsigned int opcode)
|
||||
static int simulate_rdhwr(struct pt_regs *regs, int rd, int rt)
|
||||
{
|
||||
struct thread_info *ti = task_thread_info(current);
|
||||
|
||||
perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS,
|
||||
1, regs, 0);
|
||||
switch (rd) {
|
||||
case 0: /* CPU number */
|
||||
regs->regs[rt] = smp_processor_id();
|
||||
return 0;
|
||||
case 1: /* SYNCI length */
|
||||
regs->regs[rt] = min(current_cpu_data.dcache.linesz,
|
||||
current_cpu_data.icache.linesz);
|
||||
return 0;
|
||||
case 2: /* Read count register */
|
||||
regs->regs[rt] = read_c0_count();
|
||||
return 0;
|
||||
case 3: /* Count register resolution */
|
||||
switch (current_cpu_data.cputype) {
|
||||
case CPU_20KC:
|
||||
case CPU_25KF:
|
||||
regs->regs[rt] = 1;
|
||||
break;
|
||||
default:
|
||||
regs->regs[rt] = 2;
|
||||
}
|
||||
return 0;
|
||||
case 29:
|
||||
regs->regs[rt] = ti->tp_value;
|
||||
return 0;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int simulate_rdhwr_normal(struct pt_regs *regs, unsigned int opcode)
|
||||
{
|
||||
if ((opcode & OPCODE) == SPEC3 && (opcode & FUNC) == RDHWR) {
|
||||
int rd = (opcode & RD) >> 11;
|
||||
int rt = (opcode & RT) >> 16;
|
||||
perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS,
|
||||
1, regs, 0);
|
||||
switch (rd) {
|
||||
case 0: /* CPU number */
|
||||
regs->regs[rt] = smp_processor_id();
|
||||
return 0;
|
||||
case 1: /* SYNCI length */
|
||||
regs->regs[rt] = min(current_cpu_data.dcache.linesz,
|
||||
current_cpu_data.icache.linesz);
|
||||
return 0;
|
||||
case 2: /* Read count register */
|
||||
regs->regs[rt] = read_c0_count();
|
||||
return 0;
|
||||
case 3: /* Count register resolution */
|
||||
switch (current_cpu_data.cputype) {
|
||||
case CPU_20KC:
|
||||
case CPU_25KF:
|
||||
regs->regs[rt] = 1;
|
||||
break;
|
||||
default:
|
||||
regs->regs[rt] = 2;
|
||||
}
|
||||
return 0;
|
||||
case 29:
|
||||
regs->regs[rt] = ti->tp_value;
|
||||
return 0;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
simulate_rdhwr(regs, rd, rt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Not ours. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int simulate_rdhwr_mm(struct pt_regs *regs, unsigned short opcode)
|
||||
{
|
||||
if ((opcode & MM_POOL32A_FUNC) == MM_RDHWR) {
|
||||
int rd = (opcode & MM_RS) >> 16;
|
||||
int rt = (opcode & MM_RT) >> 21;
|
||||
simulate_rdhwr(regs, rd, rt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Not ours. */
|
||||
@@ -675,7 +697,7 @@ asmlinkage void do_ov(struct pt_regs *regs)
|
||||
force_sig_info(SIGFPE, &info, current);
|
||||
}
|
||||
|
||||
static int process_fpemu_return(int sig, void __user *fault_addr)
|
||||
int process_fpemu_return(int sig, void __user *fault_addr)
|
||||
{
|
||||
if (sig == SIGSEGV || sig == SIGBUS) {
|
||||
struct siginfo si = {0};
|
||||
@@ -826,9 +848,29 @@ static void do_trap_or_bp(struct pt_regs *regs, unsigned int code,
|
||||
asmlinkage void do_bp(struct pt_regs *regs)
|
||||
{
|
||||
unsigned int opcode, bcode;
|
||||
unsigned long epc;
|
||||
u16 instr[2];
|
||||
|
||||
if (__get_user(opcode, (unsigned int __user *) exception_epc(regs)))
|
||||
goto out_sigsegv;
|
||||
if (get_isa16_mode(regs->cp0_epc)) {
|
||||
/* Calculate EPC. */
|
||||
epc = exception_epc(regs);
|
||||
if (cpu_has_mmips) {
|
||||
if ((__get_user(instr[0], (u16 __user *)msk_isa16_mode(epc)) ||
|
||||
(__get_user(instr[1], (u16 __user *)msk_isa16_mode(epc + 2)))))
|
||||
goto out_sigsegv;
|
||||
opcode = (instr[0] << 16) | instr[1];
|
||||
} else {
|
||||
/* MIPS16e mode */
|
||||
if (__get_user(instr[0], (u16 __user *)msk_isa16_mode(epc)))
|
||||
goto out_sigsegv;
|
||||
bcode = (instr[0] >> 6) & 0x3f;
|
||||
do_trap_or_bp(regs, bcode, "Break");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (__get_user(opcode, (unsigned int __user *) exception_epc(regs)))
|
||||
goto out_sigsegv;
|
||||
}
|
||||
|
||||
/*
|
||||
* There is the ancient bug in the MIPS assemblers that the break
|
||||
@@ -869,13 +911,22 @@ out_sigsegv:
|
||||
asmlinkage void do_tr(struct pt_regs *regs)
|
||||
{
|
||||
unsigned int opcode, tcode = 0;
|
||||
u16 instr[2];
|
||||
unsigned long epc = exception_epc(regs);
|
||||
|
||||
if (__get_user(opcode, (unsigned int __user *) exception_epc(regs)))
|
||||
goto out_sigsegv;
|
||||
if ((__get_user(instr[0], (u16 __user *)msk_isa16_mode(epc))) ||
|
||||
(__get_user(instr[1], (u16 __user *)msk_isa16_mode(epc + 2))))
|
||||
goto out_sigsegv;
|
||||
opcode = (instr[0] << 16) | instr[1];
|
||||
|
||||
/* Immediate versions don't provide a code. */
|
||||
if (!(opcode & OPCODE))
|
||||
tcode = ((opcode >> 6) & ((1 << 10) - 1));
|
||||
if (!(opcode & OPCODE)) {
|
||||
if (get_isa16_mode(regs->cp0_epc))
|
||||
/* microMIPS */
|
||||
tcode = (opcode >> 12) & 0x1f;
|
||||
else
|
||||
tcode = ((opcode >> 6) & ((1 << 10) - 1));
|
||||
}
|
||||
|
||||
do_trap_or_bp(regs, tcode, "Trap");
|
||||
return;
|
||||
@@ -888,6 +939,7 @@ asmlinkage void do_ri(struct pt_regs *regs)
|
||||
{
|
||||
unsigned int __user *epc = (unsigned int __user *)exception_epc(regs);
|
||||
unsigned long old_epc = regs->cp0_epc;
|
||||
unsigned long old31 = regs->regs[31];
|
||||
unsigned int opcode = 0;
|
||||
int status = -1;
|
||||
|
||||
@@ -900,23 +952,37 @@ asmlinkage void do_ri(struct pt_regs *regs)
|
||||
if (unlikely(compute_return_epc(regs) < 0))
|
||||
return;
|
||||
|
||||
if (unlikely(get_user(opcode, epc) < 0))
|
||||
status = SIGSEGV;
|
||||
if (get_isa16_mode(regs->cp0_epc)) {
|
||||
unsigned short mmop[2] = { 0 };
|
||||
|
||||
if (!cpu_has_llsc && status < 0)
|
||||
status = simulate_llsc(regs, opcode);
|
||||
if (unlikely(get_user(mmop[0], epc) < 0))
|
||||
status = SIGSEGV;
|
||||
if (unlikely(get_user(mmop[1], epc) < 0))
|
||||
status = SIGSEGV;
|
||||
opcode = (mmop[0] << 16) | mmop[1];
|
||||
|
||||
if (status < 0)
|
||||
status = simulate_rdhwr(regs, opcode);
|
||||
if (status < 0)
|
||||
status = simulate_rdhwr_mm(regs, opcode);
|
||||
} else {
|
||||
if (unlikely(get_user(opcode, epc) < 0))
|
||||
status = SIGSEGV;
|
||||
|
||||
if (status < 0)
|
||||
status = simulate_sync(regs, opcode);
|
||||
if (!cpu_has_llsc && status < 0)
|
||||
status = simulate_llsc(regs, opcode);
|
||||
|
||||
if (status < 0)
|
||||
status = simulate_rdhwr_normal(regs, opcode);
|
||||
|
||||
if (status < 0)
|
||||
status = simulate_sync(regs, opcode);
|
||||
}
|
||||
|
||||
if (status < 0)
|
||||
status = SIGILL;
|
||||
|
||||
if (unlikely(status > 0)) {
|
||||
regs->cp0_epc = old_epc; /* Undo skip-over. */
|
||||
regs->regs[31] = old31;
|
||||
force_sig(status, current);
|
||||
}
|
||||
}
|
||||
@@ -986,7 +1052,7 @@ static int default_cu2_call(struct notifier_block *nfb, unsigned long action,
|
||||
asmlinkage void do_cpu(struct pt_regs *regs)
|
||||
{
|
||||
unsigned int __user *epc;
|
||||
unsigned long old_epc;
|
||||
unsigned long old_epc, old31;
|
||||
unsigned int opcode;
|
||||
unsigned int cpid;
|
||||
int status;
|
||||
@@ -1000,26 +1066,41 @@ asmlinkage void do_cpu(struct pt_regs *regs)
|
||||
case 0:
|
||||
epc = (unsigned int __user *)exception_epc(regs);
|
||||
old_epc = regs->cp0_epc;
|
||||
old31 = regs->regs[31];
|
||||
opcode = 0;
|
||||
status = -1;
|
||||
|
||||
if (unlikely(compute_return_epc(regs) < 0))
|
||||
return;
|
||||
|
||||
if (unlikely(get_user(opcode, epc) < 0))
|
||||
status = SIGSEGV;
|
||||
if (get_isa16_mode(regs->cp0_epc)) {
|
||||
unsigned short mmop[2] = { 0 };
|
||||
|
||||
if (!cpu_has_llsc && status < 0)
|
||||
status = simulate_llsc(regs, opcode);
|
||||
if (unlikely(get_user(mmop[0], epc) < 0))
|
||||
status = SIGSEGV;
|
||||
if (unlikely(get_user(mmop[1], epc) < 0))
|
||||
status = SIGSEGV;
|
||||
opcode = (mmop[0] << 16) | mmop[1];
|
||||
|
||||
if (status < 0)
|
||||
status = simulate_rdhwr(regs, opcode);
|
||||
if (status < 0)
|
||||
status = simulate_rdhwr_mm(regs, opcode);
|
||||
} else {
|
||||
if (unlikely(get_user(opcode, epc) < 0))
|
||||
status = SIGSEGV;
|
||||
|
||||
if (!cpu_has_llsc && status < 0)
|
||||
status = simulate_llsc(regs, opcode);
|
||||
|
||||
if (status < 0)
|
||||
status = simulate_rdhwr_normal(regs, opcode);
|
||||
}
|
||||
|
||||
if (status < 0)
|
||||
status = SIGILL;
|
||||
|
||||
if (unlikely(status > 0)) {
|
||||
regs->cp0_epc = old_epc; /* Undo skip-over. */
|
||||
regs->regs[31] = old31;
|
||||
force_sig(status, current);
|
||||
}
|
||||
|
||||
@@ -1333,7 +1414,7 @@ asmlinkage void cache_parity_error(void)
|
||||
void ejtag_exception_handler(struct pt_regs *regs)
|
||||
{
|
||||
const int field = 2 * sizeof(unsigned long);
|
||||
unsigned long depc, old_epc;
|
||||
unsigned long depc, old_epc, old_ra;
|
||||
unsigned int debug;
|
||||
|
||||
printk(KERN_DEBUG "SDBBP EJTAG debug exception - not handled yet, just ignored!\n");
|
||||
@@ -1348,10 +1429,12 @@ void ejtag_exception_handler(struct pt_regs *regs)
|
||||
* calculation.
|
||||
*/
|
||||
old_epc = regs->cp0_epc;
|
||||
old_ra = regs->regs[31];
|
||||
regs->cp0_epc = depc;
|
||||
__compute_return_epc(regs);
|
||||
compute_return_epc(regs);
|
||||
depc = regs->cp0_epc;
|
||||
regs->cp0_epc = old_epc;
|
||||
regs->regs[31] = old_ra;
|
||||
} else
|
||||
depc += 4;
|
||||
write_c0_depc(depc);
|
||||
@@ -1390,10 +1473,27 @@ unsigned long vi_handlers[64];
|
||||
void __init *set_except_vector(int n, void *addr)
|
||||
{
|
||||
unsigned long handler = (unsigned long) addr;
|
||||
unsigned long old_handler = xchg(&exception_handlers[n], handler);
|
||||
unsigned long old_handler;
|
||||
|
||||
#ifdef CONFIG_CPU_MICROMIPS
|
||||
/*
|
||||
* Only the TLB handlers are cache aligned with an even
|
||||
* address. All other handlers are on an odd address and
|
||||
* require no modification. Otherwise, MIPS32 mode will
|
||||
* be entered when handling any TLB exceptions. That
|
||||
* would be bad...since we must stay in microMIPS mode.
|
||||
*/
|
||||
if (!(handler & 0x1))
|
||||
handler |= 1;
|
||||
#endif
|
||||
old_handler = xchg(&exception_handlers[n], handler);
|
||||
|
||||
if (n == 0 && cpu_has_divec) {
|
||||
#ifdef CONFIG_CPU_MICROMIPS
|
||||
unsigned long jump_mask = ~((1 << 27) - 1);
|
||||
#else
|
||||
unsigned long jump_mask = ~((1 << 28) - 1);
|
||||
#endif
|
||||
u32 *buf = (u32 *)(ebase + 0x200);
|
||||
unsigned int k0 = 26;
|
||||
if ((handler & jump_mask) == ((ebase + 0x200) & jump_mask)) {
|
||||
@@ -1420,17 +1520,18 @@ static void *set_vi_srs_handler(int n, vi_handler_t addr, int srs)
|
||||
unsigned long handler;
|
||||
unsigned long old_handler = vi_handlers[n];
|
||||
int srssets = current_cpu_data.srsets;
|
||||
u32 *w;
|
||||
u16 *h;
|
||||
unsigned char *b;
|
||||
|
||||
BUG_ON(!cpu_has_veic && !cpu_has_vint);
|
||||
BUG_ON((n < 0) && (n > 9));
|
||||
|
||||
if (addr == NULL) {
|
||||
handler = (unsigned long) do_default_vi;
|
||||
srs = 0;
|
||||
} else
|
||||
handler = (unsigned long) addr;
|
||||
vi_handlers[n] = (unsigned long) addr;
|
||||
vi_handlers[n] = handler;
|
||||
|
||||
b = (unsigned char *)(ebase + 0x200 + n*VECTORSPACING);
|
||||
|
||||
@@ -1449,9 +1550,8 @@ static void *set_vi_srs_handler(int n, vi_handler_t addr, int srs)
|
||||
if (srs == 0) {
|
||||
/*
|
||||
* If no shadow set is selected then use the default handler
|
||||
* that does normal register saving and a standard interrupt exit
|
||||
* that does normal register saving and standard interrupt exit
|
||||
*/
|
||||
|
||||
extern char except_vec_vi, except_vec_vi_lui;
|
||||
extern char except_vec_vi_ori, except_vec_vi_end;
|
||||
extern char rollback_except_vec_vi;
|
||||
@@ -1464,11 +1564,20 @@ static void *set_vi_srs_handler(int n, vi_handler_t addr, int srs)
|
||||
* Status.IM bit to be masked before going there.
|
||||
*/
|
||||
extern char except_vec_vi_mori;
|
||||
#if defined(CONFIG_CPU_MICROMIPS) || defined(CONFIG_CPU_BIG_ENDIAN)
|
||||
const int mori_offset = &except_vec_vi_mori - vec_start + 2;
|
||||
#else
|
||||
const int mori_offset = &except_vec_vi_mori - vec_start;
|
||||
#endif
|
||||
#endif /* CONFIG_MIPS_MT_SMTC */
|
||||
const int handler_len = &except_vec_vi_end - vec_start;
|
||||
#if defined(CONFIG_CPU_MICROMIPS) || defined(CONFIG_CPU_BIG_ENDIAN)
|
||||
const int lui_offset = &except_vec_vi_lui - vec_start + 2;
|
||||
const int ori_offset = &except_vec_vi_ori - vec_start + 2;
|
||||
#else
|
||||
const int lui_offset = &except_vec_vi_lui - vec_start;
|
||||
const int ori_offset = &except_vec_vi_ori - vec_start;
|
||||
#endif
|
||||
const int handler_len = &except_vec_vi_end - vec_start;
|
||||
|
||||
if (handler_len > VECTORSPACING) {
|
||||
/*
|
||||
@@ -1478,30 +1587,44 @@ static void *set_vi_srs_handler(int n, vi_handler_t addr, int srs)
|
||||
panic("VECTORSPACING too small");
|
||||
}
|
||||
|
||||
memcpy(b, vec_start, handler_len);
|
||||
set_handler(((unsigned long)b - ebase), vec_start,
|
||||
#ifdef CONFIG_CPU_MICROMIPS
|
||||
(handler_len - 1));
|
||||
#else
|
||||
handler_len);
|
||||
#endif
|
||||
#ifdef CONFIG_MIPS_MT_SMTC
|
||||
BUG_ON(n > 7); /* Vector index %d exceeds SMTC maximum. */
|
||||
|
||||
w = (u32 *)(b + mori_offset);
|
||||
*w = (*w & 0xffff0000) | (0x100 << n);
|
||||
h = (u16 *)(b + mori_offset);
|
||||
*h = (0x100 << n);
|
||||
#endif /* CONFIG_MIPS_MT_SMTC */
|
||||
w = (u32 *)(b + lui_offset);
|
||||
*w = (*w & 0xffff0000) | (((u32)handler >> 16) & 0xffff);
|
||||
w = (u32 *)(b + ori_offset);
|
||||
*w = (*w & 0xffff0000) | ((u32)handler & 0xffff);
|
||||
h = (u16 *)(b + lui_offset);
|
||||
*h = (handler >> 16) & 0xffff;
|
||||
h = (u16 *)(b + ori_offset);
|
||||
*h = (handler & 0xffff);
|
||||
local_flush_icache_range((unsigned long)b,
|
||||
(unsigned long)(b+handler_len));
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* In other cases jump directly to the interrupt handler
|
||||
*
|
||||
* It is the handlers responsibility to save registers if required
|
||||
* (eg hi/lo) and return from the exception using "eret"
|
||||
* In other cases jump directly to the interrupt handler. It
|
||||
* is the handler's responsibility to save registers if required
|
||||
* (eg hi/lo) and return from the exception using "eret".
|
||||
*/
|
||||
w = (u32 *)b;
|
||||
*w++ = 0x08000000 | (((u32)handler >> 2) & 0x03fffff); /* j handler */
|
||||
*w = 0;
|
||||
u32 insn;
|
||||
|
||||
h = (u16 *)b;
|
||||
/* j handler */
|
||||
#ifdef CONFIG_CPU_MICROMIPS
|
||||
insn = 0xd4000000 | (((u32)handler & 0x07ffffff) >> 1);
|
||||
#else
|
||||
insn = 0x08000000 | (((u32)handler & 0x0fffffff) >> 2);
|
||||
#endif
|
||||
h[0] = (insn >> 16) & 0xffff;
|
||||
h[1] = insn & 0xffff;
|
||||
h[2] = 0;
|
||||
h[3] = 0;
|
||||
local_flush_icache_range((unsigned long)b,
|
||||
(unsigned long)(b+8));
|
||||
}
|
||||
@@ -1546,6 +1669,7 @@ void __cpuinit per_cpu_trap_init(bool is_boot_cpu)
|
||||
unsigned int cpu = smp_processor_id();
|
||||
unsigned int status_set = ST0_CU0;
|
||||
unsigned int hwrena = cpu_hwrena_impl_bits;
|
||||
unsigned long asid = 0;
|
||||
#ifdef CONFIG_MIPS_MT_SMTC
|
||||
int secondaryTC = 0;
|
||||
int bootTC = (cpu == 0);
|
||||
@@ -1629,8 +1753,9 @@ void __cpuinit per_cpu_trap_init(bool is_boot_cpu)
|
||||
}
|
||||
#endif /* CONFIG_MIPS_MT_SMTC */
|
||||
|
||||
if (!cpu_data[cpu].asid_cache)
|
||||
cpu_data[cpu].asid_cache = ASID_FIRST_VERSION;
|
||||
asid = ASID_FIRST_VERSION;
|
||||
cpu_data[cpu].asid_cache = asid;
|
||||
TLBMISS_HANDLER_SETUP();
|
||||
|
||||
atomic_inc(&init_mm.mm_count);
|
||||
current->active_mm = &init_mm;
|
||||
@@ -1660,7 +1785,11 @@ void __cpuinit per_cpu_trap_init(bool is_boot_cpu)
|
||||
/* Install CPU exception handler */
|
||||
void __cpuinit set_handler(unsigned long offset, void *addr, unsigned long size)
|
||||
{
|
||||
#ifdef CONFIG_CPU_MICROMIPS
|
||||
memcpy((void *)(ebase + offset), ((unsigned char *)addr - 1), size);
|
||||
#else
|
||||
memcpy((void *)(ebase + offset), addr, size);
|
||||
#endif
|
||||
local_flush_icache_range(ebase + offset, ebase + offset + size);
|
||||
}
|
||||
|
||||
@@ -1694,8 +1823,9 @@ __setup("rdhwr_noopt", set_rdhwr_noopt);
|
||||
|
||||
void __init trap_init(void)
|
||||
{
|
||||
extern char except_vec3_generic, except_vec3_r4000;
|
||||
extern char except_vec3_generic;
|
||||
extern char except_vec4;
|
||||
extern char except_vec3_r4000;
|
||||
unsigned long i;
|
||||
int rollback;
|
||||
|
||||
@@ -1833,11 +1963,11 @@ void __init trap_init(void)
|
||||
|
||||
if (cpu_has_vce)
|
||||
/* Special exception: R4[04]00 uses also the divec space. */
|
||||
memcpy((void *)(ebase + 0x180), &except_vec3_r4000, 0x100);
|
||||
set_handler(0x180, &except_vec3_r4000, 0x100);
|
||||
else if (cpu_has_4kex)
|
||||
memcpy((void *)(ebase + 0x180), &except_vec3_generic, 0x80);
|
||||
set_handler(0x180, &except_vec3_generic, 0x80);
|
||||
else
|
||||
memcpy((void *)(ebase + 0x080), &except_vec3_generic, 0x80);
|
||||
set_handler(0x080, &except_vec3_generic, 0x80);
|
||||
|
||||
local_flush_icache_range(ebase, ebase + 0x400);
|
||||
flush_tlb_handlers();
|
||||
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user