Commit 7bcf7717 authored by Ralf Baechle's avatar Ralf Baechle

[MIPS] Implement clockevents for R4000-style cp0 count/compare interrupt

Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent 91a2fcc8
...@@ -335,6 +335,7 @@ config QEMU ...@@ -335,6 +335,7 @@ config QEMU
select GENERIC_ISA_DMA select GENERIC_ISA_DMA
select HAVE_STD_PC_SERIAL_PORT select HAVE_STD_PC_SERIAL_PORT
select I8259 select I8259
select IRQ_CPU
select ISA select ISA
select PCSPEAKER select PCSPEAKER
select SWAP_IO_SPACE select SWAP_IO_SPACE
...@@ -667,6 +668,10 @@ config GENERIC_CALIBRATE_DELAY ...@@ -667,6 +668,10 @@ config GENERIC_CALIBRATE_DELAY
bool bool
default y default y
config GENERIC_CLOCKEVENTS
bool
default y
config GENERIC_TIME config GENERIC_TIME
bool bool
default y default y
...@@ -901,6 +906,8 @@ config BOOT_ELF64 ...@@ -901,6 +906,8 @@ config BOOT_ELF64
menu "CPU selection" menu "CPU selection"
source "kernel/time/Kconfig"
choice choice
prompt "CPU type" prompt "CPU type"
default CPU_R4X00 default CPU_R4X00
......
...@@ -633,7 +633,7 @@ asmlinkage void plat_irq_dispatch(void) ...@@ -633,7 +633,7 @@ asmlinkage void plat_irq_dispatch(void)
unsigned int pending = read_c0_status() & read_c0_cause() & ST0_IM; unsigned int pending = read_c0_status() & read_c0_cause() & ST0_IM;
if (pending & CAUSEF_IP7) if (pending & CAUSEF_IP7)
ll_timer_interrupt(63); do_IRQ(63);
else if (pending & CAUSEF_IP2) else if (pending & CAUSEF_IP2)
intc0_req0_irqdispatch(); intc0_req0_irqdispatch();
else if (pending & CAUSEF_IP3) else if (pending & CAUSEF_IP3)
......
...@@ -69,7 +69,6 @@ CONFIG_SIBYTE_SB1xxx_SOC=y ...@@ -69,7 +69,6 @@ CONFIG_SIBYTE_SB1xxx_SOC=y
CONFIG_SIBYTE_CFE=y CONFIG_SIBYTE_CFE=y
# CONFIG_SIBYTE_CFE_CONSOLE is not set # CONFIG_SIBYTE_CFE_CONSOLE is not set
# CONFIG_SIBYTE_BUS_WATCHER is not set # CONFIG_SIBYTE_BUS_WATCHER is not set
# CONFIG_SIBYTE_SB1250_PROF is not set
# CONFIG_SIBYTE_TBPROF is not set # CONFIG_SIBYTE_TBPROF is not set
CONFIG_RWSEM_GENERIC_SPINLOCK=y CONFIG_RWSEM_GENERIC_SPINLOCK=y
# CONFIG_ARCH_HAS_ILOG2_U32 is not set # CONFIG_ARCH_HAS_ILOG2_U32 is not set
......
...@@ -70,7 +70,6 @@ CONFIG_SIBYTE_HAS_LDT=y ...@@ -70,7 +70,6 @@ CONFIG_SIBYTE_HAS_LDT=y
CONFIG_SIBYTE_CFE=y CONFIG_SIBYTE_CFE=y
# CONFIG_SIBYTE_CFE_CONSOLE is not set # CONFIG_SIBYTE_CFE_CONSOLE is not set
# CONFIG_SIBYTE_BUS_WATCHER is not set # CONFIG_SIBYTE_BUS_WATCHER is not set
# CONFIG_SIBYTE_SB1250_PROF is not set
# CONFIG_SIBYTE_TBPROF is not set # CONFIG_SIBYTE_TBPROF is not set
CONFIG_RWSEM_GENERIC_SPINLOCK=y CONFIG_RWSEM_GENERIC_SPINLOCK=y
# CONFIG_ARCH_HAS_ILOG2_U32 is not set # CONFIG_ARCH_HAS_ILOG2_U32 is not set
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/tick.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/stddef.h> #include <linux/stddef.h>
...@@ -52,6 +53,7 @@ void __noreturn cpu_idle(void) ...@@ -52,6 +53,7 @@ void __noreturn cpu_idle(void)
{ {
/* endless idle loop with no priority at all */ /* endless idle loop with no priority at all */
while (1) { while (1) {
tick_nohz_stop_sched_tick();
while (!need_resched()) { while (!need_resched()) {
#ifdef CONFIG_SMTC_IDLE_HOOK_DEBUG #ifdef CONFIG_SMTC_IDLE_HOOK_DEBUG
extern void smtc_idle_loop_hook(void); extern void smtc_idle_loop_hook(void);
...@@ -61,6 +63,7 @@ void __noreturn cpu_idle(void) ...@@ -61,6 +63,7 @@ void __noreturn cpu_idle(void)
if (cpu_wait) if (cpu_wait)
(*cpu_wait)(); (*cpu_wait)();
} }
tick_nohz_restart_sched_tick();
preempt_enable_no_resched(); preempt_enable_no_resched();
schedule(); schedule();
preempt_disable(); preempt_disable();
......
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#include <asm/system.h> #include <asm/system.h>
#include <asm/mmu_context.h> #include <asm/mmu_context.h>
#include <asm/smp.h> #include <asm/smp.h>
#include <asm/time.h>
#ifdef CONFIG_MIPS_MT_SMTC #ifdef CONFIG_MIPS_MT_SMTC
#include <asm/mipsmtregs.h> #include <asm/mipsmtregs.h>
...@@ -70,6 +71,7 @@ asmlinkage __cpuinit void start_secondary(void) ...@@ -70,6 +71,7 @@ asmlinkage __cpuinit void start_secondary(void)
cpu_probe(); cpu_probe();
cpu_report(); cpu_report();
per_cpu_trap_init(); per_cpu_trap_init();
mips_clockevent_init();
prom_init_secondary(); prom_init_secondary();
/* /*
......
...@@ -867,7 +867,7 @@ void ipi_decode(struct smtc_ipi *pipi) ...@@ -867,7 +867,7 @@ void ipi_decode(struct smtc_ipi *pipi)
#ifdef CONFIG_SMTC_IDLE_HOOK_DEBUG #ifdef CONFIG_SMTC_IDLE_HOOK_DEBUG
clock_hang_reported[dest_copy] = 0; clock_hang_reported[dest_copy] = 0;
#endif /* CONFIG_SMTC_IDLE_HOOK_DEBUG */ #endif /* CONFIG_SMTC_IDLE_HOOK_DEBUG */
local_timer_interrupt(0); local_timer_interrupt(0, NULL);
irq_exit(); irq_exit();
break; break;
case LINUX_SMP_IPI: case LINUX_SMP_IPI:
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
* Free Software Foundation; either version 2 of the License, or (at your * Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. * option) any later version.
*/ */
#include <linux/clockchips.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/init.h> #include <linux/init.h>
...@@ -34,6 +35,8 @@ ...@@ -34,6 +35,8 @@
#include <asm/sections.h> #include <asm/sections.h>
#include <asm/time.h> #include <asm/time.h>
#include <irq.h>
/* /*
* The integer part of the number of usecs per jiffy is taken from tick, * The integer part of the number of usecs per jiffy is taken from tick,
* but the fractional part is not recorded, so we calculate it using the * but the fractional part is not recorded, so we calculate it using the
...@@ -70,10 +73,6 @@ int update_persistent_clock(struct timespec now) ...@@ -70,10 +73,6 @@ int update_persistent_clock(struct timespec now)
/* how many counter cycles in a jiffy */ /* how many counter cycles in a jiffy */
static unsigned long cycles_per_jiffy __read_mostly; static unsigned long cycles_per_jiffy __read_mostly;
/* expirelo is the count value for next CPU timer interrupt */
static unsigned int expirelo;
/* /*
* Null timer ack for systems not needing one (e.g. i8254). * Null timer ack for systems not needing one (e.g. i8254).
*/ */
...@@ -92,18 +91,7 @@ static cycle_t null_hpt_read(void) ...@@ -92,18 +91,7 @@ static cycle_t null_hpt_read(void)
*/ */
static void c0_timer_ack(void) static void c0_timer_ack(void)
{ {
unsigned int count; write_c0_compare(read_c0_compare());
/* Ack this timer interrupt and set the next one. */
expirelo += cycles_per_jiffy;
write_c0_compare(expirelo);
/* Check to see if we have missed any timer interrupts. */
while (((count = read_c0_count()) - expirelo) < 0x7fffffff) {
/* missed_timer_count++; */
expirelo = count + cycles_per_jiffy;
write_c0_compare(expirelo);
}
} }
/* /*
...@@ -114,13 +102,6 @@ static cycle_t c0_hpt_read(void) ...@@ -114,13 +102,6 @@ static cycle_t c0_hpt_read(void)
return read_c0_count(); return read_c0_count();
} }
/* For use both as a high precision timer and an interrupt source. */
static void __init c0_hpt_timer_init(void)
{
expirelo = read_c0_count() + cycles_per_jiffy;
write_c0_compare(expirelo);
}
int (*mips_timer_state)(void); int (*mips_timer_state)(void);
void (*mips_timer_ack)(void); void (*mips_timer_ack)(void);
...@@ -140,35 +121,6 @@ void local_timer_interrupt(int irq, void *dev_id) ...@@ -140,35 +121,6 @@ void local_timer_interrupt(int irq, void *dev_id)
update_process_times(user_mode(get_irq_regs())); update_process_times(user_mode(get_irq_regs()));
} }
/*
* High-level timer interrupt service routines. This function
* is set as irqaction->handler and is invoked through do_IRQ.
*/
static irqreturn_t timer_interrupt(int irq, void *dev_id)
{
write_seqlock(&xtime_lock);
mips_timer_ack();
/*
* call the generic timer interrupt handling
*/
do_timer(1);
write_sequnlock(&xtime_lock);
/*
* In UP mode, we call local_timer_interrupt() to do profiling
* and process accouting.
*
* In SMP mode, local_timer_interrupt() is invoked by appropriate
* low-level local timer interrupt handler.
*/
local_timer_interrupt(irq, dev_id);
return IRQ_HANDLED;
}
int null_perf_irq(void) int null_perf_irq(void)
{ {
return 0; return 0;
...@@ -209,81 +161,6 @@ static inline int handle_perf_irq (int r2) ...@@ -209,81 +161,6 @@ static inline int handle_perf_irq (int r2)
!r2; !r2;
} }
void ll_timer_interrupt(int irq, void *dev_id)
{
int cpu = smp_processor_id();
#ifdef CONFIG_MIPS_MT_SMTC
/*
* In an SMTC system, one Count/Compare set exists per VPE.
* Which TC within a VPE gets the interrupt is essentially
* random - we only know that it shouldn't be one with
* IXMT set. Whichever TC gets the interrupt needs to
* send special interprocessor interrupts to the other
* TCs to make sure that they schedule, etc.
*
* That code is specific to the SMTC kernel, not to
* the a particular platform, so it's invoked from
* the general MIPS timer_interrupt routine.
*/
/*
* We could be here due to timer interrupt,
* perf counter overflow, or both.
*/
(void) handle_perf_irq(1);
if (read_c0_cause() & (1 << 30)) {
/*
* There are things we only want to do once per tick
* in an "MP" system. One TC of each VPE will take
* the actual timer interrupt. The others will get
* timer broadcast IPIs. We use whoever it is that takes
* the tick on VPE 0 to run the full timer_interrupt().
*/
if (cpu_data[cpu].vpe_id == 0) {
timer_interrupt(irq, NULL);
} else {
write_c0_compare(read_c0_count() +
(mips_hpt_frequency/HZ));
local_timer_interrupt(irq, dev_id);
}
smtc_timer_broadcast(cpu_data[cpu].vpe_id);
}
#else /* CONFIG_MIPS_MT_SMTC */
int r2 = cpu_has_mips_r2;
if (handle_perf_irq(r2))
return;
if (r2 && ((read_c0_cause() & (1 << 30)) == 0))
return;
if (cpu == 0) {
/*
* CPU 0 handles the global timer interrupt job and process
* accounting resets count/compare registers to trigger next
* timer int.
*/
timer_interrupt(irq, NULL);
} else {
/* Everyone else needs to reset the timer int here as
ll_local_timer_interrupt doesn't */
/*
* FIXME: need to cope with counter underflow.
* More support needs to be added to kernel/time for
* counter/timer interrupts on multiple CPU's
*/
write_c0_compare(read_c0_count() + (mips_hpt_frequency/HZ));
/*
* Other CPUs should do profiling and process accounting
*/
local_timer_interrupt(irq, dev_id);
}
#endif /* CONFIG_MIPS_MT_SMTC */
}
/* /*
* time_init() - it does the following things. * time_init() - it does the following things.
* *
...@@ -301,12 +178,6 @@ void ll_timer_interrupt(int irq, void *dev_id) ...@@ -301,12 +178,6 @@ void ll_timer_interrupt(int irq, void *dev_id)
unsigned int mips_hpt_frequency; unsigned int mips_hpt_frequency;
static struct irqaction timer_irqaction = {
.handler = timer_interrupt,
.flags = IRQF_DISABLED | IRQF_PERCPU,
.name = "timer",
};
static unsigned int __init calibrate_hpt(void) static unsigned int __init calibrate_hpt(void)
{ {
cycle_t frequency, hpt_start, hpt_end, hpt_count, hz; cycle_t frequency, hpt_start, hpt_end, hpt_count, hz;
...@@ -355,6 +226,65 @@ struct clocksource clocksource_mips = { ...@@ -355,6 +226,65 @@ struct clocksource clocksource_mips = {
.flags = CLOCK_SOURCE_IS_CONTINUOUS, .flags = CLOCK_SOURCE_IS_CONTINUOUS,
}; };
static int mips_next_event(unsigned long delta,
struct clock_event_device *evt)
{
unsigned int cnt;
cnt = read_c0_count();
cnt += delta;
write_c0_compare(cnt);
return ((long)(read_c0_count() - cnt ) > 0) ? -ETIME : 0;
}
static void mips_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{
/* Nothing to do ... */
}
struct clock_event_device mips_clockevent;
static struct clock_event_device *global_cd[NR_CPUS];
static int cp0_timer_irq_installed;
static irqreturn_t timer_interrupt(int irq, void *dev_id)
{
const int r2 = cpu_has_mips_r2;
struct clock_event_device *cd;
int cpu = smp_processor_id();
/*
* Suckage alert:
* Before R2 of the architecture there was no way to see if a
* performance counter interrupt was pending, so we have to run
* the performance counter interrupt handler anyway.
*/
if (handle_perf_irq(r2))
goto out;
/*
* The same applies to performance counter interrupts. But with the
* above we now know that the reason we got here must be a timer
* interrupt. Being the paranoiacs we are we check anyway.
*/
if (!r2 || (read_c0_cause() & (1 << 30))) {
c0_timer_ack();
cd = global_cd[cpu];
cd->event_handler(cd);
}
out:
return IRQ_HANDLED;
}
static struct irqaction timer_irqaction = {
.handler = timer_interrupt,
.flags = IRQF_DISABLED | IRQF_PERCPU,
.name = "timer",
};
static void __init init_mips_clocksource(void) static void __init init_mips_clocksource(void)
{ {
u64 temp; u64 temp;
...@@ -382,6 +312,56 @@ void __init __weak plat_time_init(void) ...@@ -382,6 +312,56 @@ void __init __weak plat_time_init(void)
{ {
} }
void __init __weak plat_timer_setup(struct irqaction *irq)
{
}
void __cpuinit mips_clockevent_init(void)
{
uint64_t mips_freq = mips_hpt_frequency;
unsigned int cpu = smp_processor_id();
struct clock_event_device *cd;
unsigned int irq = MIPS_CPU_IRQ_BASE + 7;
if (!cpu_has_counter)
return;
if (cpu == 0)
cd = &mips_clockevent;
else
cd = kzalloc(sizeof(*cd), GFP_ATOMIC);
if (!cd)
return; /* We're probably roadkill ... */
cd->name = "MIPS";
cd->features = CLOCK_EVT_FEAT_ONESHOT;
/* Calculate the min / max delta */
cd->mult = div_sc((unsigned long) mips_freq, NSEC_PER_SEC, 32);
cd->shift = 32;
cd->max_delta_ns = clockevent_delta2ns(0x7fffffff, cd);
cd->min_delta_ns = clockevent_delta2ns(0x30, cd);
cd->rating = 300;
cd->irq = irq;
cd->cpumask = cpumask_of_cpu(cpu);
cd->set_next_event = mips_next_event;
cd->set_mode = mips_set_mode;
global_cd[cpu] = cd;
clockevents_register_device(cd);
if (!cp0_timer_irq_installed) {
#ifdef CONFIG_MIPS_MT_SMTC
#define CPUCTR_IMASKBIT (0x100 << cp0_compare_irq)
setup_irq_smtc(irq, &timer_irqaction, CPUCTR_IMASKBIT);
#else
setup_irq(irq, &timer_irqaction);
#endif /* CONFIG_MIPS_MT_SMTC */
cp0_timer_irq_installed = 1;
}
}
void __init time_init(void) void __init time_init(void)
{ {
plat_time_init(); plat_time_init();
...@@ -407,11 +387,6 @@ void __init time_init(void) ...@@ -407,11 +387,6 @@ void __init time_init(void)
/* Calculate cache parameters. */ /* Calculate cache parameters. */
cycles_per_jiffy = cycles_per_jiffy =
(mips_hpt_frequency + HZ / 2) / HZ; (mips_hpt_frequency + HZ / 2) / HZ;
/*
* This sets up the high precision
* timer for the first interrupt.
*/
c0_hpt_timer_init();
} }
} }
if (!mips_hpt_frequency) if (!mips_hpt_frequency)
...@@ -421,6 +396,10 @@ void __init time_init(void) ...@@ -421,6 +396,10 @@ void __init time_init(void)
printk("Using %u.%03u MHz high precision timer.\n", printk("Using %u.%03u MHz high precision timer.\n",
((mips_hpt_frequency + 500) / 1000) / 1000, ((mips_hpt_frequency + 500) / 1000) / 1000,
((mips_hpt_frequency + 500) / 1000) % 1000); ((mips_hpt_frequency + 500) / 1000) % 1000);
#ifdef CONFIG_IRQ_CPU
setup_irq(MIPS_CPU_IRQ_BASE + 7, &timer_irqaction);
#endif
} }
if (!mips_timer_ack) if (!mips_timer_ack)
...@@ -441,4 +420,5 @@ void __init time_init(void) ...@@ -441,4 +420,5 @@ void __init time_init(void)
plat_timer_setup(&timer_irqaction); plat_timer_setup(&timer_irqaction);
init_mips_clocksource(); init_mips_clocksource();
mips_clockevent_init();
} }
...@@ -144,20 +144,20 @@ void __init plat_time_init(void) ...@@ -144,20 +144,20 @@ void __init plat_time_init(void)
mips_scroll_message(); mips_scroll_message();
} }
static irqreturn_t mips_perf_interrupt(int irq, void *dev_id) //static irqreturn_t mips_perf_interrupt(int irq, void *dev_id)
{ //{
return perf_irq(); // return perf_irq();
} //}
static struct irqaction perf_irqaction = { //static struct irqaction perf_irqaction = {
.handler = mips_perf_interrupt, // .handler = mips_perf_interrupt,
.flags = IRQF_DISABLED | IRQF_PERCPU, // .flags = IRQF_DISABLED | IRQF_PERCPU,
.name = "performance", // .name = "performance",
}; //};
void __init plat_perf_setup(void) void __init plat_perf_setup(void)
{ {
struct irqaction *irq = &perf_irqaction; // struct irqaction *irq = &perf_irqaction;
cp0_perfcount_irq = -1; cp0_perfcount_irq = -1;
...@@ -170,12 +170,6 @@ void __init plat_perf_setup(void) ...@@ -170,12 +170,6 @@ void __init plat_perf_setup(void)
if (cp0_perfcount_irq >= 0) { if (cp0_perfcount_irq >= 0) {
if (cpu_has_vint) if (cpu_has_vint)
set_vi_handler(cp0_perfcount_irq, mips_perf_dispatch); set_vi_handler(cp0_perfcount_irq, mips_perf_dispatch);
#ifdef CONFIG_MIPS_MT_SMTC
setup_irq_smtc(cp0_perfcount_irq, irq,
0x100 << cp0_perfcount_irq);
#else
setup_irq(cp0_perfcount_irq, irq);
#endif /* CONFIG_MIPS_MT_SMTC */
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
set_irq_handler(cp0_perfcount_irq, handle_percpu_irq); set_irq_handler(cp0_perfcount_irq, handle_percpu_irq);
#endif #endif
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#include <linux/linkage.h> #include <linux/linkage.h>
#include <asm/i8259.h> #include <asm/i8259.h>
#include <asm/irq_cpu.h>
#include <asm/mipsregs.h> #include <asm/mipsregs.h>
#include <asm/qemu.h> #include <asm/qemu.h>
#include <asm/system.h> #include <asm/system.h>
...@@ -12,7 +13,7 @@ asmlinkage void plat_irq_dispatch(void) ...@@ -12,7 +13,7 @@ asmlinkage void plat_irq_dispatch(void)
unsigned int pending = read_c0_status() & read_c0_cause(); unsigned int pending = read_c0_status() & read_c0_cause();
if (pending & 0x8000) { if (pending & 0x8000) {
ll_timer_interrupt(Q_COUNT_COMPARE_IRQ); do_IRQ(Q_COUNT_COMPARE_IRQ);
return; return;
} }
if (pending & 0x0400) { if (pending & 0x0400) {
...@@ -29,6 +30,7 @@ void __init arch_init_irq(void) ...@@ -29,6 +30,7 @@ void __init arch_init_irq(void)
{ {
mips_hpt_frequency = QEMU_C0_COUNTER_CLOCK; /* 100MHz */ mips_hpt_frequency = QEMU_C0_COUNTER_CLOCK; /* 100MHz */
mips_cpu_irq_init();
init_i8259_irqs(); init_i8259_irqs();
set_c0_status(0x8400); set_c0_status(0x8400);
} }
...@@ -17,7 +17,6 @@ void __init plat_timer_setup(struct irqaction *irq) ...@@ -17,7 +17,6 @@ void __init plat_timer_setup(struct irqaction *irq)
outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */ outb_p(0x34,0x43); /* binary, mode 2, LSB/MSB, ch 0 */
outb_p(LATCH & 0xff , 0x40); /* LSB */ outb_p(LATCH & 0xff , 0x40); /* LSB */
outb(LATCH >> 8 , 0x40); /* MSB */ outb(LATCH >> 8 , 0x40); /* MSB */
setup_irq(0, irq);
} }
void __init plat_mem_setup(void) void __init plat_mem_setup(void)
......
...@@ -242,7 +242,7 @@ asmlinkage void plat_irq_dispatch(void) ...@@ -242,7 +242,7 @@ asmlinkage void plat_irq_dispatch(void)
* First we check for r4k counter/timer IRQ. * First we check for r4k counter/timer IRQ.
*/ */
if (pending & CAUSEF_IP7) if (pending & CAUSEF_IP7)
ll_timer_interrupt(SGI_TIMER_IRQ, NULL); do_IRQ(SGI_TIMER_IRQ);
else if (pending & CAUSEF_IP2) else if (pending & CAUSEF_IP2)
indy_local0_irqdispatch(); indy_local0_irqdispatch();
else if (pending & CAUSEF_IP3) else if (pending & CAUSEF_IP3)
......
...@@ -457,7 +457,7 @@ static void ip32_irq4(void) ...@@ -457,7 +457,7 @@ static void ip32_irq4(void)
static void ip32_irq5(void) static void ip32_irq5(void)
{ {
ll_timer_interrupt(IP32_R4K_TIMER_IRQ); do_IRQ(IP32_R4K_TIMER_IRQ);
} }
asmlinkage void plat_irq_dispatch(void) asmlinkage void plat_irq_dispatch(void)
......
config SIBYTE_SB1250 config SIBYTE_SB1250
bool bool
select HW_HAS_PCI select HW_HAS_PCI
select IRQ_CPU
select SIBYTE_ENABLE_LDT_IF_PCI select SIBYTE_ENABLE_LDT_IF_PCI
select SIBYTE_HAS_ZBUS_PROFILING select SIBYTE_HAS_ZBUS_PROFILING
select SIBYTE_SB1xxx_SOC select SIBYTE_SB1xxx_SOC
...@@ -8,6 +9,7 @@ config SIBYTE_SB1250 ...@@ -8,6 +9,7 @@ config SIBYTE_SB1250
config SIBYTE_BCM1120 config SIBYTE_BCM1120
bool bool
select IRQ_CPU
select SIBYTE_BCM112X select SIBYTE_BCM112X
select SIBYTE_HAS_ZBUS_PROFILING select SIBYTE_HAS_ZBUS_PROFILING
select SIBYTE_SB1xxx_SOC select SIBYTE_SB1xxx_SOC
...@@ -15,6 +17,7 @@ config SIBYTE_BCM1120 ...@@ -15,6 +17,7 @@ config SIBYTE_BCM1120
config SIBYTE_BCM1125 config SIBYTE_BCM1125
bool bool
select HW_HAS_PCI select HW_HAS_PCI
select IRQ_CPU
select SIBYTE_BCM112X select SIBYTE_BCM112X
select SIBYTE_HAS_ZBUS_PROFILING select SIBYTE_HAS_ZBUS_PROFILING
select SIBYTE_SB1xxx_SOC select SIBYTE_SB1xxx_SOC
...@@ -22,6 +25,7 @@ config SIBYTE_BCM1125 ...@@ -22,6 +25,7 @@ config SIBYTE_BCM1125
config SIBYTE_BCM1125H config SIBYTE_BCM1125H
bool bool
select HW_HAS_PCI select HW_HAS_PCI
select IRQ_CPU
select SIBYTE_BCM112X select SIBYTE_BCM112X
select SIBYTE_ENABLE_LDT_IF_PCI select SIBYTE_ENABLE_LDT_IF_PCI
select SIBYTE_HAS_ZBUS_PROFILING select SIBYTE_HAS_ZBUS_PROFILING
...@@ -29,12 +33,14 @@ config SIBYTE_BCM1125H ...@@ -29,12 +33,14 @@ config SIBYTE_BCM1125H
config SIBYTE_BCM112X config SIBYTE_BCM112X
bool bool
select IRQ_CPU
select SIBYTE_SB1xxx_SOC select SIBYTE_SB1xxx_SOC
select SIBYTE_HAS_ZBUS_PROFILING select SIBYTE_HAS_ZBUS_PROFILING
config SIBYTE_BCM1x80 config SIBYTE_BCM1x80
bool bool
select HW_HAS_PCI select HW_HAS_PCI
select IRQ_CPU
select SIBYTE_HAS_ZBUS_PROFILING select SIBYTE_HAS_ZBUS_PROFILING
select SIBYTE_SB1xxx_SOC select SIBYTE_SB1xxx_SOC
select SYS_SUPPORTS_SMP select SYS_SUPPORTS_SMP
...@@ -42,6 +48,7 @@ config SIBYTE_BCM1x80 ...@@ -42,6 +48,7 @@ config SIBYTE_BCM1x80
config SIBYTE_BCM1x55 config SIBYTE_BCM1x55
bool bool
select HW_HAS_PCI select HW_HAS_PCI
select IRQ_CPU
select SIBYTE_SB1xxx_SOC select SIBYTE_SB1xxx_SOC
select SIBYTE_HAS_ZBUS_PROFILING select SIBYTE_HAS_ZBUS_PROFILING
select SYS_SUPPORTS_SMP select SYS_SUPPORTS_SMP
...@@ -49,6 +56,7 @@ config SIBYTE_BCM1x55 ...@@ -49,6 +56,7 @@ config SIBYTE_BCM1x55
config SIBYTE_SB1xxx_SOC config SIBYTE_SB1xxx_SOC
bool bool
select DMA_COHERENT select DMA_COHERENT
select IRQ_CPU
select SIBYTE_CFE select SIBYTE_CFE
select SWAP_IO_SPACE select SWAP_IO_SPACE
select SYS_SUPPORTS_32BIT_KERNEL select SYS_SUPPORTS_32BIT_KERNEL
...@@ -166,10 +174,6 @@ config SIBYTE_BW_TRACE ...@@ -166,10 +174,6 @@ config SIBYTE_BW_TRACE
buffer activity. Raw buffer data is dumped to console, and buffer activity. Raw buffer data is dumped to console, and
must be processed off-line. must be processed off-line.
config SIBYTE_SB1250_PROF
bool "Support for SB1/SOC profiling - SB1/SCD perf counters"
depends on SIBYTE_SB1xxx_SOC
config SIBYTE_TBPROF config SIBYTE_TBPROF
tristate "Support for ZBbus profiling" tristate "Support for ZBbus profiling"
depends on SIBYTE_HAS_ZBUS_PROFILING depends on SIBYTE_HAS_ZBUS_PROFILING
......
...@@ -450,7 +450,6 @@ static void bcm1480_kgdb_interrupt(void) ...@@ -450,7 +450,6 @@ static void bcm1480_kgdb_interrupt(void)
#endif /* CONFIG_KGDB */ #endif /* CONFIG_KGDB */
extern void bcm1480_timer_interrupt(void);
extern void bcm1480_mailbox_interrupt(void); extern void bcm1480_mailbox_interrupt(void);
asmlinkage void plat_irq_dispatch(void) asmlinkage void plat_irq_dispatch(void)
...@@ -470,8 +469,16 @@ asmlinkage void plat_irq_dispatch(void) ...@@ -470,8 +469,16 @@ asmlinkage void plat_irq_dispatch(void)
else else
#endif #endif
if (pending & CAUSEF_IP4) if (pending & CAUSEF_IP4) {
bcm1480_timer_interrupt(); int cpu = smp_processor_id();
int irq = K_BCM1480_INT_TIMER_0 + cpu;
/* Reset the timer */
__raw_writeq(M_SCD_TIMER_ENABLE|M_SCD_TIMER_MODE_CONTINUOUS,
IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)));
do_IRQ(irq);
}
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
else if (pending & CAUSEF_IP3) else if (pending & CAUSEF_IP3)
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include <asm/errno.h> #include <asm/errno.h>
#include <asm/signal.h> #include <asm/signal.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/time.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/sibyte/sb1250_regs.h> #include <asm/sibyte/sb1250_regs.h>
...@@ -399,18 +400,45 @@ static void sb1250_kgdb_interrupt(void) ...@@ -399,18 +400,45 @@ static void sb1250_kgdb_interrupt(void)
#endif /* CONFIG_KGDB */ #endif /* CONFIG_KGDB */
extern void sb1250_timer_interrupt(void); static inline void sb1250_timer_interrupt(void)
{
int cpu = smp_processor_id();
int irq = K_INT_TIMER_0 + cpu;
irq_enter();
kstat_this_cpu.irqs[irq]++;
write_seqlock(&xtime_lock);
/* ACK interrupt */
____raw_writeq(M_SCD_TIMER_ENABLE | M_SCD_TIMER_MODE_CONTINUOUS,
IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)));
/*
* call the generic timer interrupt handling
*/
do_timer(1);
write_sequnlock(&xtime_lock);
/*
* In UP mode, we call local_timer_interrupt() to do profiling
* and process accouting.
*
* In SMP mode, local_timer_interrupt() is invoked by appropriate
* low-level local timer interrupt handler.
*/
local_timer_interrupt(irq);
irq_exit();
}
extern void sb1250_mailbox_interrupt(void); extern void sb1250_mailbox_interrupt(void);
asmlinkage void plat_irq_dispatch(void) asmlinkage void plat_irq_dispatch(void)
{ {
unsigned int pending; unsigned int pending;
#ifdef CONFIG_SIBYTE_SB1250_PROF
/* Set compare to count to silence count/compare timer interrupts */
write_c0_compare(read_c0_count());
#endif
/* /*
* What a pain. We have to be really careful saving the upper 32 bits * What a pain. We have to be really careful saving the upper 32 bits
* of any * register across function calls if we don't want them * of any * register across function calls if we don't want them
...@@ -423,13 +451,9 @@ asmlinkage void plat_irq_dispatch(void) ...@@ -423,13 +451,9 @@ asmlinkage void plat_irq_dispatch(void)
pending = read_c0_cause() & read_c0_status() & ST0_IM; pending = read_c0_cause() & read_c0_status() & ST0_IM;
#ifdef CONFIG_SIBYTE_SB1250_PROF if (pending & CAUSEF_IP7) /* CPU performance counter interrupt */
if (pending & CAUSEF_IP7) /* Cpu performance counter interrupt */ do_IRQ(MIPS_CPU_IRQ_BASE + 7);
sbprof_cpu_intr(); else if (pending & CAUSEF_IP4)
else
#endif
if (pending & CAUSEF_IP4)
sb1250_timer_interrupt(); sb1250_timer_interrupt();
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
......
...@@ -116,18 +116,6 @@ void sb1250_time_init(void) ...@@ -116,18 +116,6 @@ void sb1250_time_init(void)
*/ */
} }
void sb1250_timer_interrupt(void)
{
int cpu = smp_processor_id();
int irq = K_INT_TIMER_0 + cpu;
/* ACK interrupt */
____raw_writeq(M_SCD_TIMER_ENABLE | M_SCD_TIMER_MODE_CONTINUOUS,
IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)));
ll_timer_interrupt(irq);
}
/* /*
* The HPT is free running from SB1250_HPT_VALUE down to 0 then starts over * The HPT is free running from SB1250_HPT_VALUE down to 0 then starts over
* again. * again.
......
...@@ -17,4 +17,6 @@ ...@@ -17,4 +17,6 @@
*/ */
#define NR_IRQS 256 #define NR_IRQS 256
#include_next <irq.h>
#endif /* __ASM_MACH_IP27_IRQ_H */ #endif /* __ASM_MACH_IP27_IRQ_H */
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
* Interrupt numbers * Interrupt numbers
*/ */
#define Q_PIC_IRQ_BASE 0 #define Q_PIC_IRQ_BASE 0
#define Q_COUNT_COMPARE_IRQ 16 #define Q_COUNT_COMPARE_IRQ 23
/* /*
* Qemu clock rate. Unlike on real MIPS this has no relation to the * Qemu clock rate. Unlike on real MIPS this has no relation to the
......
...@@ -48,11 +48,6 @@ extern void (*mips_timer_ack)(void); ...@@ -48,11 +48,6 @@ extern void (*mips_timer_ack)(void);
*/ */
extern struct clocksource clocksource_mips; extern struct clocksource clocksource_mips;
/*
* The low-level timer interrupt routine.
*/
extern void ll_timer_interrupt(int irq, void *dev_id);
/* /*
* profiling and process accouting is done separately in local_timer_interrupt * profiling and process accouting is done separately in local_timer_interrupt
*/ */
...@@ -78,4 +73,9 @@ extern unsigned int mips_hpt_frequency; ...@@ -78,4 +73,9 @@ extern unsigned int mips_hpt_frequency;
*/ */
extern int (*perf_irq)(void); extern int (*perf_irq)(void);
/*
* Initialize the calling CPU's compare interrupt as clockevent device
*/
extern void mips_clockevent_init(void);
#endif /* _ASM_TIME_H */ #endif /* _ASM_TIME_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment