Linux MIPS Architecture development
 help / color / mirror / Atom feed
From: Sergei Shtylyov <sshtylyov@ru.mvista.com>
To: Franck <vagabon.xyz@gmail.com>
Cc: linux-mips <linux-mips@linux-mips.org>,
	Ralf Baechle <ralf@linux-mips.org>,
	Atsushi Nemoto <anemo@mba.ocn.ne.jp>
Subject: Re: [RFC Implement clockevents/clocksource for R4000-style cp0 timer [take #3]
Date: Wed, 27 Jun 2007 22:07:04 +0400	[thread overview]
Message-ID: <4682A748.2000108@ru.mvista.com> (raw)
In-Reply-To: <467F8C34.7080904@innova-card.com>

Hello.

Franck Bui-Huu wrote:

> This patch is an attempt to add clocksource/clockevent support for
> platforms that use R4000-style cp0 timer as the tick source of the
> system.

[...]

> It implements clockevent/clocksource support for cp0 counter in a new
> file "hpt-cp0.c" but let the platform code to initialize it if it
> chooses to use it. Therefore there's no more generic code which tries
> to guess what the platform wants to use as tick source because in
> practice only the platform code can drive the initialization of all
> timer devices and interrupts properly.

> Take #3 includes minor changes (Thanks to Sergei Shtylyov's feedbacks)


> Changes since take #2:
> ---------------------

>    - clocksource rating does not depend on cp0-count frequency anymore
>      since a clocksource should be selected for its stability first,
>      not its precision.

>    - Rename CP0_HPT_TIMER into CP0_CLOCKS but I'm still not sure. If

    Why not just CP0_TIMER? Hm, CP0_HPT_TIMER was even redundant... :-)

>      someone can come with a better idea that would be nice. BTW
>      hpt-cp0.c file should be renamed into clock-cp0.c too.

    I suggest timer-cp0.c ot cp0-timer.c

> TODO:
> -----

>    - There are still few places to fix that uses 'mips_hpt_frequency'
>      global.

>    - If this patch is accepted, fix all platforms that use cp0
>      counter.

> Please have a look,

> 		Franck

> -- 8< --

> Subject: [PATCH] Implement clockevents/clocksource for R4000-style cp0 timer

> Signed-off-by: Franck Bui-Huu <fbuihuu@gmail.com>
> ---
>  arch/mips/Kconfig          |    9 +
>  arch/mips/kernel/Makefile  |    2 +
>  arch/mips/kernel/hpt-cp0.c |  259 +++++++++++++++++++++++++++
>  arch/mips/kernel/process.c |    3 +
>  arch/mips/kernel/smp.c     |    2 +
>  arch/mips/kernel/time.c    |  416 ++++----------------------------------------
>  include/asm-mips/hpt.h     |   36 ++++

    That HPT just doesn't want to die. :-)

[...]
> diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
> index 7bcf38d..a994af1 100644
[...]
> @@ -1741,6 +1749,7 @@ config HZ
>  	default 1000 if HZ_1000
>  	default 1024 if HZ_1024
>  
> +source "kernel/time/Kconfig"
>  source "kernel/Kconfig.preempt"

    I would have put this part into a separate patch, along with tickless mode
hooks in arch/mips/kernel/process.c...

> diff --git a/arch/mips/kernel/hpt-cp0.c b/arch/mips/kernel/hpt-cp0.c
> new file mode 100644
> index 0000000..e2defcd
> --- /dev/null
> +++ b/arch/mips/kernel/hpt-cp0.c
> @@ -0,0 +1,259 @@
> +/*
> + * hpt-cp0.c - CP0 clock driver
> + *
> + * Copyright (C) 2007,  Franck Bui-Huu <fbuihuu@gmail.com>
> + *
> + * This code is released under the GNU General Public License,
> + * Version 2 (GPL v2).
> + */
> +#include <linux/kernel_stat.h>
> +#include <linux/spinlock.h>
> +#include <linux/clockchips.h>
> +#include <linux/clocksource.h>
> +
> +#include <asm/time.h>
> +#include <asm/hpt.h>
> +
> +
> +#define CP0_CLOCK_NAME	"cp0-count"
> +
> +static unsigned (*get_freq)(int cpu) __initdata;
> +static irqreturn_t (*perf_handler)(int irq, void *dev_id) __read_mostly;
> +
> +/*
> + * cp0 clocks can be disabled by boot command line
> + */
> +static int disable_clockevent __initdata;
> +static int disable_clocksource __initdata;
> +
> +static int __init cp0_clock_setup(char *arg)
> +{
> +	if (arg == NULL)
> +		return -EINVAL;
> +
> +	if (!strcmp(arg, "disable_event"))
> +		disable_clockevent = 1;
> +	else if (!strcmp(arg, "disable_source"))
> +		disable_clocksource = 1;
> +	else if (!strcmp(arg, "disable_both")) {
> +		disable_clocksource = 1;
> +		disable_clockevent = 1;

    How about "noevent", "nosource", and "none"?

> +	}
> +	return 0;
> +}
> +early_param("cp0_clock", cp0_clock_setup);
> +
> +/*
> + * cp0 count/compare operations.
> + */
> +static void cp0_count_ack(void)
> +{
> +	write_c0_compare(read_c0_compare());
> +}
> +
> +static cycle_t cp0_count_read(void)
> +{
> +        return read_c0_count();

    "Entab" spaces here please.

> +}
> +
> +/*
> + * Clocksource device. Its rating should not depend on its frequency:
> + * stability is a feature more valuable for a clock source.
> + */
> +struct clocksource cp0_clocksource = {
> +	.name		= CP0_CLOCK_NAME,
> +	.rating		= 200,

    Perhaps we even need to rise the rating to 300 or 400 -- according to what
<linux/clocksource.h> says...

> +	.mask		= CLOCKSOURCE_MASK(32),
> +	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
> +	.read		= cp0_count_read,
> +};
> +
> +static void __init setup_cp0_clocksource(void)
> +{
> +	int cpu = smp_processor_id();
> +	unsigned freq = get_freq(cpu);

    Might fold these 2 lines into:

unsigned freq = get_freq(smp_processor_id());

> +	unsigned shift = 0;

    Unneeded initializer.

> +	u64 mult;
> +
> +	for (shift = 32; shift > 0; shift--) {
> +		mult = (u64)NSEC_PER_SEC << shift;
> +		do_div(mult, freq);
> +		if ((mult >> 32) == 0)
> +			break;
> +	}
> +
> +	cp0_clocksource.shift = shift;
> +	cp0_clocksource.mult = mult;
> +
> +	clocksource_register(&cp0_clocksource);
> +}
> +
> +/*
> + * High precision timer functions
> + */
> +static int cp0_set_next_event(unsigned long delta,
> +				   struct clock_event_device *evt)
> +{
> +	unsigned int cnt;
> +
> +	BUG_ON(evt->mode != CLOCK_EVT_MODE_ONESHOT);
> +
> +	/* interrupt ack is done by setting up the next event */

    We acknowledge interrupt independently from this code anyway, so the 
comment seems misplaced.

> +	cnt = read_c0_count();
> +	cnt += delta;

   Could be merge into one statement...

> +	write_c0_compare(cnt);
> +
> +	return ((long)(read_c0_count() - cnt) > 0L) ? -ETIME : 0;
> +}
> +
> +static void cp0_set_mode(enum clock_event_mode mode,
> +			     struct clock_event_device *evt)
> +{
> +	switch (mode) {
> +	case CLOCK_EVT_MODE_UNUSED:
> +	case CLOCK_EVT_MODE_SHUTDOWN:
> +		/*
> +		 * For now, we don't disable cp0 hpt interrupts. So we

    A reference to hpt needs to be killed.

> +		 * leave them enabled, and ignore them in this mode.
> +		 * Therefore we will get one useless but also harmless
> +		 * interrupt every 2^32 cycles...
> +		 */
> +		cp0_count_ack();
> +		break;
> +	case CLOCK_EVT_MODE_ONESHOT:
> +		/* nothing to do */
> +		break;
> +	case CLOCK_EVT_MODE_PERIODIC:
> +		BUG();
> +	};
> +}
> +
> +static struct clock_event_device cp0_clockevent __initdata = {
> +	.name		= CP0_CLOCK_NAME,
> +	.mode		= CLOCK_EVT_MODE_UNUSED,

    Needless intialization, that constant is 0 anyway, so this field will just 
get zeroed implicitly.

> +	.features	= CLOCK_EVT_FEAT_ONESHOT,
> +	.shift		= 32,
> +	.set_mode	= cp0_set_mode,
> +	.set_next_event	= cp0_set_next_event,
> +	.irq		= -1,
> +};
> +
> +static DEFINE_PER_CPU(struct clock_event_device, cp0_clock_events);
> +
> +void __init setup_cp0_clockevent(void)
> +{
> +	struct clock_event_device *evt;
> +	int cpu = smp_processor_id();
> +	unsigned freq;
> +
> +	if (disable_clockevent)
> +		return;
> +
> +	evt = &__get_cpu_var(cp0_clock_events);

    Could use per_cpu() here, as we already have "sampled" smp_processor_id().

> +
> +	memcpy(evt, &cp0_clockevent, sizeof(*evt));
> +
> +	freq = get_freq(cpu);
> +
> +	evt->rating = 200 + freq/10000000;
> +	evt->mult = div_sc(freq, NSEC_PER_SEC, evt->shift);
> +	evt->cpumask = cpumask_of_cpu(cpu);
> +
> +	evt->max_delta_ns = clockevent_delta2ns(0x7fffffff, evt);
> +	evt->min_delta_ns = clockevent_delta2ns(0x10, evt);
> +
> +	clockevents_register_device(evt);
> +
> +	printk("Using %u.%03u MHz CP0 high precision timer on CPU #%d.\n",

    The "high precision " part should disapper.

> +	       ((freq + 500) / 1000) / 1000,
> +	       ((freq + 500) / 1000) % 1000,
> +		cpu);
> +}
> +
> +static irqreturn_t cp0_clockevent_interrupt(int irq, void *dev_id)
> +{
> +	const int r2 = cpu_has_mips_r2;
> +	struct clock_event_device *evt;
> +
[...]
> +	/*
> +	 * 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))) {
> +		evt = &__get_cpu_var(cp0_clock_events);

    I'd think 'evt' should be declared in this block too, not at the function 
level.

> +
> +		/*
> +		 * We can get interrupts whereas the hpt clock event

    A reference to hpt needs to be killed there too...

> +		 * device has been disabled since we can't shut it
> +		 * down. So always ack the timer.
> +		 */
> +		cp0_count_ack();
> +
> +		switch (evt->mode) {
> +		case CLOCK_EVT_MODE_ONESHOT:
> +			evt->event_handler(evt);
> +			break;
> +		case CLOCK_EVT_MODE_UNUSED:
> +		case CLOCK_EVT_MODE_SHUTDOWN:
> +		case CLOCK_EVT_MODE_PERIODIC:
> +			/* nothing */;
> +		}

    Isn't this over-engineered? Why use *switch*' where the simple *if* would 
suffice?

> +	}
> +out:
> +	return IRQ_HANDLED;
> +}
> +
> +static struct irqaction cp0_clockevent_irqaction = {
> +	.handler	= cp0_clockevent_interrupt,
> +	.flags		= IRQF_DISABLED | IRQF_PERCPU,
> +	.name		= CP0_CLOCK_NAME,
> +};
> +
> +
> +/*
> + * This function is used by platforms which use the hpt as clock
> + * source and timer.
> + */
> +int __init setup_cp0_clocks(struct cp0_clock_info *info)
> +{

    Erm, do we need this function at all after we have separate setups for 
clock source/event?  Just move all the override checks there.

> +	if (!cpu_has_counter)
> +		goto disable_all;
> +	if (info->get_freq == NULL)
> +		goto disable_all;
> +
> +	get_freq = info->get_freq;
> +	perf_handler = info->perf_handler;

    I seriously don't understand why are you expecting this to be passed by 
the platform code... :-/  Currently this is not so -- since it's the Oprofile 
code that handles the performance interrupt.

> +
> +	if (info->irq == 0)
> +		disable_clockevent = 1;
> +
> +	if (!disable_clocksource)
> +		setup_cp0_clocksource();
> +	if (!disable_clockevent) {
> +		setup_cp0_clockevent();
> +		setup_irq(info->irq, &cp0_clockevent_irqaction);

    Erm... why not include setup_irq() into setup_cp0_clockevent()? This way, 
it'll be autonomous and callable by the platfrom code on its own... but there 
would remain perf_irq and get_freq problems. Wouldn't it be better to declare 
get_cp0_timer_freq as a pointer ot function and let the platform just fill it 
before calling setup?

> +	}
> +	return 0;
> +
> +disable_all:
> +	disable_clocksource = disable_clockevent = 1;
> +	return -EINVAL;
> +}
> diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c
> index 6bdfb5a..23b8858 100644
> --- a/arch/mips/kernel/process.c
> +++ b/arch/mips/kernel/process.c
> @@ -25,6 +25,7 @@
>  #include <linux/init.h>
>  #include <linux/completion.h>
>  #include <linux/kallsyms.h>
> +#include <linux/tick.h>
>  
>  #include <asm/bootinfo.h>
>  #include <asm/cpu.h>
> @@ -50,6 +51,7 @@ ATTRIB_NORET void cpu_idle(void)
>  {
>  	/* endless idle loop with no priority at all */
>  	while (1) {
> +		tick_nohz_stop_sched_tick();
>  		while (!need_resched()) {
>  #ifdef CONFIG_SMTC_IDLE_HOOK_DEBUG
>  			extern void smtc_idle_loop_hook(void);
> @@ -59,6 +61,7 @@ ATTRIB_NORET void cpu_idle(void)
>  			if (cpu_wait)
>  				(*cpu_wait)();
>  		}
> +		tick_nohz_restart_sched_tick();
>  		preempt_enable_no_resched();
>  		schedule();
>  		preempt_disable();
> diff --git a/arch/mips/kernel/time.c b/arch/mips/kernel/time.c
> index 72df0bf..0cc8363 100644
> --- a/arch/mips/kernel/time.c
> +++ b/arch/mips/kernel/time.c
[...]
> + * If you don't know timer 'X' frequency and have another timer 'Y'
> + * that flips at HZ rate, you can use this helper to determinate the
> + * timer 'X' freq.
>   */
> -
> -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)
> +unsigned __init calibrate_timer(cycle_t (*x_read)(void),
> +				int (*y_state)(void))

    Hm, with those x/y it looks a bit like joystick code. :-)

> diff --git a/include/asm-mips/hpt.h b/include/asm-mips/hpt.h
> new file mode 100644
> index 0000000..f0acab3
> --- /dev/null
> +++ b/include/asm-mips/hpt.h
> @@ -0,0 +1,36 @@
> +#ifndef _ASM_HPT_H
> +#define _ASM_HPT_H
> +
> +#ifdef CONFIG_CP0_CLOCKS
> +
> +struct cp0_clock_info {
> +	/*
> +	 * This is the irq num of the cp0 count/compare timer.
> +	 */
> +	int irq;
> +
> +	/*
> +	 * This mandartory hook is called to get the frequency of
> +	 * the running processor.
> +	 */
> +	unsigned (*get_freq)(int cpu);

    Creating a global variable looks like a better idea...

> +
> +	/*
> +	 * The performance counter overflow irq may be shared with the
> +	 * hpt interrupt. In that case this handler will be called
> +	 * during a hpt interrupt.
> +	 */
> +	irqreturn_t (*perf_handler)(int irq, void *dev_id);

    The only issue is that the platform code may have no idea what it should 
be...  I think we need to stick to the old approach here.

> +};

    ... this would leave us with only IRQ field and eliminate the need for 
this structure.

> +
> +extern int setup_cp0_clocks(struct cp0_clock_info *info);
> +extern void setup_cp0_clockevent(void);
> +
> +#else
> +
> +static inline void setup_cp0_clockevent(void) {}
> +
> +#endif	/* CONFIG_CP0_CLOCKS */
> +
> +#endif	/* _ASM_HPT_H */

    The remaining declaration might be joined to time.h, thus making this 
whole header unneeded... well, that's up to you.

WBR, Sergei

  reply	other threads:[~2007-06-27 18:05 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-06-25  9:34 [RFC Implement clockevents/clocksource for R4000-style cp0 timer [take #3] Franck Bui-Huu
2007-06-27 18:07 ` Sergei Shtylyov [this message]
2007-06-29  9:30   ` Franck Bui-Huu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4682A748.2000108@ru.mvista.com \
    --to=sshtylyov@ru.mvista.com \
    --cc=anemo@mba.ocn.ne.jp \
    --cc=linux-mips@linux-mips.org \
    --cc=ralf@linux-mips.org \
    --cc=vagabon.xyz@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox