Commit 0a4908e1 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'upstream' of git://ftp.linux-mips.org/pub/scm/upstream-linus

* 'upstream' of git://ftp.linux-mips.org/pub/scm/upstream-linus:
  [MIPS] Delete totally outdated Documentation/mips/time.README
  [MIPS] Kill duplicated setup_irq() for cp0 timer
  [MIPS] Sibyte: Finish conversion to modern time APIs.
  [MIPS] time: Helpers to compute clocksource/event shift and mult values.
  [MIPS] SMTC: Build fix.
  [MIPS] time: Delete dead code.
  [MIPS] MIPSsim: Strip defconfig file to the bones.
parents 2843483d 6f75aaa7
......@@ -4,5 +4,3 @@ AU1xxx_IDE.README
- README for MIPS AU1XXX IDE driver.
GT64120.README
- README for dir with info on MIPS boards using GT-64120 or GT-64120A.
time.README
- README for MIPS time services.
README for MIPS time services
Jun Sun
jsun@mvista.com or jsun@junsun.net
ABOUT
-----
This file describes the new arch/mips/kernel/time.c, related files and the
services they provide.
If you are short in patience and just want to know how to use time.c for a
new board or convert an existing board, go to the last section.
FILES, COMPATABILITY AND CONFIGS
---------------------------------
The old arch/mips/kernel/time.c is renamed to old-time.c.
A new time.c is put there, together with include/asm-mips/time.h.
Two configs variables are introduced, CONFIG_OLD_TIME_C and CONFIG_NEW_TIME_C.
So we allow boards using
1) old time.c (CONFIG_OLD_TIME_C)
2) new time.c (CONFIG_NEW_TIME_C)
3) neither (their own private time.c)
However, it is expected every board will move to the new time.c in the near
future.
WHAT THE NEW CODE PROVIDES?
---------------------------
The new time code provide the following services:
a) Implements functions required by Linux common code:
time_init
b) provides an abstraction of RTC and null RTC implementation as default.
extern unsigned long (*rtc_get_time)(void);
extern int (*rtc_set_time)(unsigned long);
c) high-level and low-level timer interrupt routines where the timer
interrupt source may or may not be the CPU timer. The high-level
routine is dispatched through do_IRQ() while the low-level is
dispatched in assemably code (usually int-handler.S)
WHAT THE NEW CODE REQUIRES?
---------------------------
For the new code to work properly, each board implementation needs to supply
the following functions or values:
a) board_time_init - a function pointer. Invoked at the beginnig of
time_init(). It is optional.
1. (optional) set up RTC routines
2. (optional) calibrate and set the mips_hpt_frequency
b) plat_timer_setup - a function pointer. Invoked at the end of time_init()
1. (optional) over-ride any decisions made in time_init()
2. set up the irqaction for timer interrupt.
3. enable the timer interrupt
c) (optional) board-specific RTC routines.
d) (optional) mips_hpt_frequency - It must be definied if the board
is using CPU counter for timer interrupt.
PORTING GUIDE
-------------
Step 1: decide how you like to implement the time services.
a) does this board have a RTC? If yes, implement the two RTC funcs.
b) does the CPU have counter/compare registers?
If the answer is no, you need a timer to provide the timer interrupt
at 100 HZ speed.
c) The following sub steps assume your CPU has counter register.
Do you plan to use the CPU counter register as the timer interrupt
or use an exnternal timer?
In order to use CPU counter register as the timer interrupt source, you
must know the counter speed (mips_hpt_frequency). It is usually the
same as the CPU speed or an integral divisor of it.
d) decide on whether you want to use high-level or low-level timer
interrupt routines. The low-level one is presumably faster, but should
not make too mcuh difference.
Step 2: the machine setup() function
If you supply board_time_init(), set the function poointer.
Step 3: implement rtc routines, board_time_init() and plat_timer_setup()
if needed.
board_time_init() -
a) (optional) set up RTC routines,
b) (optional) calibrate and set the mips_hpt_frequency
(only needed if you intended to use cpu counter as timer interrupt
source)
plat_timer_setup() -
a) (optional) over-write any choices made above by time_init().
b) machine specific code should setup the timer irqaction.
c) enable the timer interrupt
If the RTC chip is a common chip, I suggest the routines are put under
arch/mips/libs. For example, for DS1386 chip, one would create
rtc-ds1386.c under arch/mips/lib directory. Add the following line to
the arch/mips/lib/Makefile:
obj-$(CONFIG_DDB5476) += rtc-ds1386.o
Step 4: if you are using low-level timer interrupt, change your interrupt
dispathcing code to check for timer interrupt and jump to
ll_timer_interrupt() directly if one is detected.
Step 5: Modify arch/mips/config.in and add CONFIG_NEW_TIME_C to your machine.
Modify the appropriate defconfig if applicable.
Final notes:
For some tricky cases, you may need to add your own wrapper functions
for some of the functions in time.c.
For example, you may define your own timer interrupt routine, which does
some of its own processing and then calls timer_interrupt().
You can also over-ride any of the built-in functions (RTC routines
and/or timer interrupt routine).
PORTING NOTES FOR SMP
----------------------
If you have a SMP box, things are slightly more complicated.
The time service running every jiffy is logically divided into two parts:
1) the one for the whole system (defined in timer_interrupt())
2) the one that should run for each CPU (defined in local_timer_interrupt())
You need to decide on your timer interrupt sources.
case 1) - whole system has only one timer interrupt delivered to one CPU
In this case, you set up timer interrupt as in UP systems. In addtion,
you need to set emulate_local_timer_interrupt to 1 so that other
CPUs get to call local_timer_interrupt().
THIS IS CURRENTLY NOT IMPLEMNETED. However, it is rather easy to write
one should such a need arise. You simply make a IPI call.
case 2) - each CPU has a separate timer interrupt
In this case, you need to set up IRQ such that each of them will
call local_timer_interrupt(). In addition, you need to arrange
one and only one of them to call timer_interrupt().
You can also do the low-level version of those interrupt routines,
following similar dispatching routes described above.
......@@ -46,10 +46,3 @@ void __init plat_time_init(void)
/* Set MIPS counter frequency for fixed_rate_gettimeoffset() */
mips_hpt_frequency = hz;
}
void __init
plat_timer_setup(struct irqaction *irq)
{
/* Enable the timer interrupt */
setup_irq(7, irq);
}
This diff is collapsed.
......@@ -104,12 +104,6 @@ void __init plat_time_init(void)
mips_hpt_frequency = (bus_frequency * (4 + reg)) / 4 / 2;
}
void __init plat_timer_setup(struct irqaction *irq)
{
/* we are using the cpu counter for timer interrupts */
setup_irq(CPU_IRQ_BASE + 7, irq);
}
static void markeins_board_init(void);
extern void markeins_irq_setup(void);
......
......@@ -10,6 +10,7 @@
#include <linux/interrupt.h>
#include <linux/percpu.h>
#include <asm/smtc_ipi.h>
#include <asm/time.h>
static int mips_next_event(unsigned long delta,
......
......@@ -39,17 +39,6 @@
#include <irq.h>
/*
* 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
* initial value of HZ. This aids systems where tick isn't really an
* integer (e.g. for HZ = 128).
*/
#define USECS_PER_JIFFY TICK_SIZE
#define USECS_PER_JIFFY_FRAC ((unsigned long)(u32)((1000000ULL << 32) / HZ))
#define TICK_SIZE (tick_nsec / 1000)
/*
* forward reference
*/
......@@ -182,84 +171,59 @@ struct clocksource clocksource_mips = {
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static void __init init_mips_clocksource(void)
void __init clocksource_set_clock(struct clocksource *cs, unsigned int clock)
{
u64 temp;
u32 shift;
if (!mips_hpt_frequency || clocksource_mips.read == null_hpt_read)
return;
/* Calclate a somewhat reasonable rating value */
clocksource_mips.rating = 200 + mips_hpt_frequency / 10000000;
/* Find a shift value */
for (shift = 32; shift > 0; shift--) {
temp = (u64) NSEC_PER_SEC << shift;
do_div(temp, mips_hpt_frequency);
do_div(temp, clock);
if ((temp >> 32) == 0)
break;
}
clocksource_mips.shift = shift;
clocksource_mips.mult = (u32)temp;
clocksource_register(&clocksource_mips);
cs->shift = shift;
cs->mult = (u32) temp;
}
void __init __weak plat_time_init(void)
void __cpuinit clockevent_set_clock(struct clock_event_device *cd,
unsigned int clock)
{
u64 temp;
u32 shift;
/* Find a shift value */
for (shift = 32; shift > 0; shift--) {
temp = (u64) NSEC_PER_SEC << shift;
do_div(temp, clock);
if ((temp >> 32) == 0)
break;
}
cd->shift = shift;
cd->mult = (u32) temp;
}
void __init __weak plat_timer_setup(struct irqaction *irq)
static void __init init_mips_clocksource(void)
{
}
if (!mips_hpt_frequency || clocksource_mips.read == null_hpt_read)
return;
#ifdef CONFIG_MIPS_MT_SMTC
DEFINE_PER_CPU(struct clock_event_device, smtc_dummy_clockevent_device);
/* Calclate a somewhat reasonable rating value */
clocksource_mips.rating = 200 + mips_hpt_frequency / 10000000;
static void smtc_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{
clocksource_set_clock(&clocksource_mips, mips_hpt_frequency);
clocksource_register(&clocksource_mips);
}
static void mips_broadcast(cpumask_t mask)
void __init __weak plat_time_init(void)
{
unsigned int cpu;
for_each_cpu_mask(cpu, mask)
smtc_send_ipi(cpu, SMTC_CLOCK_TICK, 0);
}
static void setup_smtc_dummy_clockevent_device(void)
void __init __weak plat_timer_setup(struct irqaction *irq)
{
//uint64_t mips_freq = mips_hpt_^frequency;
unsigned int cpu = smp_processor_id();
struct clock_event_device *cd;
cd = &per_cpu(smtc_dummy_clockevent_device, cpu);
cd->name = "SMTC";
cd->features = CLOCK_EVT_FEAT_DUMMY;
/* Calculate the min / max delta */
cd->mult = 0; //div_sc((unsigned long) mips_freq, NSEC_PER_SEC, 32);
cd->shift = 0; //32;
cd->max_delta_ns = 0; //clockevent_delta2ns(0x7fffffff, cd);
cd->min_delta_ns = 0; //clockevent_delta2ns(0x30, cd);
cd->rating = 200;
cd->irq = 17; //-1;
// if (cpu)
// cd->cpumask = CPU_MASK_ALL; // cpumask_of_cpu(cpu);
// else
cd->cpumask = cpumask_of_cpu(cpu);
cd->set_mode = smtc_set_mode;
cd->broadcast = mips_broadcast;
clockevents_register_device(cd);
}
#endif
void __init time_init(void)
{
......
......@@ -53,11 +53,6 @@ unsigned long bus_clock;
unsigned int memsize;
unsigned int highmemsize = 0;
void __init plat_timer_setup(struct irqaction *irq)
{
setup_irq(MIPS_CPU_IRQ_BASE + 7, irq);
}
void __init plat_time_init(void)
{
/* setup mips r4k timer */
......
......@@ -86,8 +86,5 @@ void __init plat_timer_setup(struct irqaction *irq)
#ifdef CONFIG_IRQ_MSP_CIC
/* we are using the vpe0 counter for timer interrupts */
setup_irq(MSP_INT_VPE0_TIMER, irq);
#else
/* we are using the mips counter for timer interrupts */
setup_irq(MSP_INT_TIMER, irq);
#endif
}
......@@ -137,11 +137,6 @@ int rtc_mips_set_time(unsigned long tim)
return 0;
}
void __init plat_timer_setup(struct irqaction *irq)
{
setup_irq(7, irq);
}
void __init plat_time_init(void)
{
mips_hpt_frequency = cpu_clock_freq / 2;
......
......@@ -69,8 +69,9 @@ void bcm1480_smp_init(void)
void bcm1480_smp_finish(void)
{
extern void bcm1480_time_init(void);
bcm1480_time_init();
extern void sb1480_clockevent_init(void);
sb1480_clockevent_init();
local_irq_enable();
}
......
......@@ -27,9 +27,8 @@
*/
#include <linux/clockchips.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/percpu.h>
#include <linux/spinlock.h>
#include <linux/kernel_stat.h>
#include <asm/irq.h>
#include <asm/addrspace.h>
......@@ -101,25 +100,36 @@ static void sibyte_set_mode(enum clock_event_mode mode,
break;
case CLOCK_EVT_MODE_UNUSED: /* shuddup gcc */
case CLOCK_EVT_MODE_RESUME:
;
}
}
struct clock_event_device sibyte_hpt_clockevent = {
.name = "bcm1480-counter",
.features = CLOCK_EVT_FEAT_PERIODIC,
.set_mode = sibyte_set_mode,
.shift = 32,
.irq = 0,
};
static int sibyte_next_event(unsigned long delta, struct clock_event_device *cd)
{
unsigned int cpu = smp_processor_id();
void __iomem *timer_init;
unsigned int cnt;
int res;
timer_init = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_INIT));
cnt = __raw_readq(timer_init);
cnt += delta;
__raw_writeq(cnt, timer_init);
res = ((long)(__raw_readq(timer_init) - cnt ) > 0) ? -ETIME : 0;
return res;
}
static DEFINE_PER_CPU(struct clock_event_device, sibyte_hpt_clockevent);
static irqreturn_t sibyte_counter_handler(int irq, void *dev_id)
{
struct clock_event_device *cd = &sibyte_hpt_clockevent;
unsigned int cpu = smp_processor_id();
struct clock_event_device *cd = &per_cpu(sibyte_hpt_clockevent, cpu);
/* Reset the timer */
__raw_writeq(M_SCD_TIMER_ENABLE|M_SCD_TIMER_MODE_CONTINUOUS,
__raw_writeq(M_SCD_TIMER_ENABLE | M_SCD_TIMER_MODE_CONTINUOUS,
IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG)));
cd->event_handler(cd);
......@@ -140,24 +150,21 @@ static struct irqaction sibyte_counter_irqaction = {
* called directly from irq_handler.S when IP[4] is set during an
* interrupt
*/
static void __init sb1480_clockevent_init(void)
void __cpuinit sb1480_clockevent_init(void)
{
unsigned int cpu = smp_processor_id();
unsigned int irq = K_BCM1480_INT_TIMER_0 + cpu;
struct clock_event_device *cd = &per_cpu(sibyte_hpt_clockevent, cpu);
setup_irq(irq, &sibyte_counter_irqaction);
}
cd->name = "bcm1480-counter";
cd->features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_MODE_ONESHOT;
cd->set_next_event = sibyte_next_event;
cd->set_mode = sibyte_set_mode;
cd->irq = irq;
clockevent_set_clock(cd, BCM1480_HPT_VALUE);
void bcm1480_timer_interrupt(void)
{
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)));
ll_timer_interrupt(irq);
setup_irq(irq, &sibyte_counter_irqaction);
}
static cycle_t bcm1480_hpt_read(void)
......@@ -168,9 +175,26 @@ static cycle_t bcm1480_hpt_read(void)
return (jiffies + 1) * (BCM1480_HPT_VALUE / HZ) - count;
}
struct clocksource bcm1480_clocksource = {
.name = "MIPS",
.rating = 200,
.read = bcm1480_hpt_read,
.mask = CLOCKSOURCE_MASK(32),
.shift = 32,
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
void __init sb1480_clocksource_init(void)
{
struct clocksource *cs = &bcm1480_clocksource;
clocksource_set_clock(cs, BCM1480_HPT_VALUE);
clocksource_register(cs);
}
void __init bcm1480_hpt_setup(void)
{
clocksource_mips.read = bcm1480_hpt_read;
mips_hpt_frequency = BCM1480_HPT_VALUE;
sb1480_clocksource_init();
sb1480_clockevent_init();
}
......@@ -400,43 +400,11 @@ static void sb1250_kgdb_interrupt(void)
#endif /* CONFIG_KGDB */
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);
asmlinkage void plat_irq_dispatch(void)
{
unsigned int cpu = smp_processor_id();
unsigned int pending;
/*
......@@ -454,7 +422,7 @@ asmlinkage void plat_irq_dispatch(void)
if (pending & CAUSEF_IP7) /* CPU performance counter interrupt */
do_IRQ(MIPS_CPU_IRQ_BASE + 7);
else if (pending & CAUSEF_IP4)
sb1250_timer_interrupt();
do_IRQ(K_INT_TIMER_0 + cpu); /* sb1250_timer_interrupt() */
#ifdef CONFIG_SMP
else if (pending & CAUSEF_IP3)
......
......@@ -57,8 +57,9 @@ void sb1250_smp_init(void)
void sb1250_smp_finish(void)
{
extern void sb1250_time_init(void);
sb1250_time_init();
extern void sb1250_clockevent_init(void);
sb1250_clockevent_init();
local_irq_enable();
}
......
......@@ -100,6 +100,7 @@ static void sibyte_set_mode(enum clock_event_mode mode,
break;
case CLOCK_EVT_MODE_UNUSED: /* shuddup gcc */
case CLOCK_EVT_MODE_RESUME:
;
}
}
......@@ -144,79 +145,7 @@ static struct irqaction sibyte_irqaction = {
.name = "timer",
};
/*
* The general purpose timer ticks at 1 Mhz independent if
* the rest of the system
*/
static void sibyte_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{
unsigned int cpu = smp_processor_id();
void __iomem *timer_cfg, *timer_init;
timer_cfg = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_CFG));
timer_init = IOADDR(A_SCD_TIMER_REGISTER(cpu, R_SCD_TIMER_INIT));
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC:
__raw_writeq(0, timer_cfg);
__raw_writeq((V_SCD_TIMER_FREQ / HZ) - 1, timer_init);
__raw_writeq(M_SCD_TIMER_ENABLE | M_SCD_TIMER_MODE_CONTINUOUS,
timer_cfg);
break;
case CLOCK_EVT_MODE_ONESHOT:
/* Stop the timer until we actually program a shot */
case CLOCK_EVT_MODE_SHUTDOWN:
__raw_writeq(0, timer_cfg);
break;
case CLOCK_EVT_MODE_UNUSED: /* shuddup gcc */
;
}