mirror of
https://github.com/followmsi/android_kernel_google_msm.git
synced 2024-11-06 23:17:41 +00:00
[MIPS] Implement clockevents for R4000-style cp0 count/compare interrupt
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
This commit is contained in:
parent
91a2fcc886
commit
7bcf7717b6
20 changed files with 210 additions and 200 deletions
|
@ -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
|
||||||
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
|
||||||
|
|
||||||
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)
|
||||||
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
|
||||||
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
|
||||||
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 @@
|
||||||
#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)
|
||||||
{
|
{
|
||||||
/* 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)
|
||||||
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 @@
|
||||||
#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)
|
||||||
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)
|
||||||
#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 @@
|
||||||
* 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 @@
|
||||||
#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)
|
||||||
/* 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)
|
||||||
*/
|
*/
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
!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)
|
||||||
|
|
||||||
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 = {
|
||||||
.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)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
/* 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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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 @@
|
||||||
#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)
|
||||||
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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
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)
|
||||||
* 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)
|
||||||
|
|
||||||
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)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
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
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
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
|
||||||
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
|
||||||
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)
|
||||||
|
|
||||||
#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)
|
||||||
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 @@
|
||||||
#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)
|
||||||
|
|
||||||
#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)
|
||||||
|
|
||||||
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)
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
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 @@
|
||||||
*/
|
*/
|
||||||
#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 @@
|
||||||
* 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);
|
||||||
*/
|
*/
|
||||||
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;
|
||||||
*/
|
*/
|
||||||
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 */
|
||||||
|
|
Loading…
Reference in a new issue