diff mbox

[v2,05/14] ARM: integrator: use clocksource_of_init for sp804

Message ID 1363108124-17484-6-git-send-email-haojian.zhuang@linaro.org
State Superseded
Headers show

Commit Message

Haojian Zhuang March 12, 2013, 5:08 p.m. UTC
Remove all code to parse sp804 in integrator platform driver. Use
clocksource_of_init() instead since these code are implemented in sp804
driver already.

Signed-off-by: Haojian Zhuang <haojian.zhuang@linaro.org>
---
 arch/arm/boot/dts/integrator.dtsi        |    3 +++
 arch/arm/boot/dts/integratorap.dts       |   15 +++++------
 arch/arm/boot/dts/integratorcp.dts       |    9 +++----
 arch/arm/mach-integrator/Kconfig         |    3 +++
 arch/arm/mach-integrator/integrator_ap.c |   41 +-----------------------------
 arch/arm/mach-integrator/integrator_cp.c |   35 ++-----------------------
 6 files changed, 20 insertions(+), 86 deletions(-)

Comments

Arnd Bergmann March 12, 2013, 6:54 p.m. UTC | #1
On Tuesday 12 March 2013, Haojian Zhuang wrote:
> 
> -       aliases {
> -               arm,timer-primary = &timer2;
> -               arm,timer-secondary = &timer1;
> -       };
> -
>         chosen {
>                 bootargs = "root=/dev/ram0 console=ttyAMA0,38400n8 earlyprintk";
>         };
> @@ -29,10 +24,14 @@
>  
>         timer1: timer@13000100 {
>                 compatible = "arm,sp804", "arm,primecell";
> +               arm,sp804-clockevent = <0>;
> +               status = "ok";
>         };

I would prefer to keep the old way of doing this. The "aliases" node is
meant for configuration, while the timer device itself should not care
whether it is used as a clockevent or clocksource or some other device.

We could probably change the identifiers in the aliases to
"linux,clocksource" and "linux,clockevents" instead of
"arm,timer-primary" and "arm,timer-secondary".

	Arnd
Rob Herring March 12, 2013, 7:15 p.m. UTC | #2
On 03/12/2013 12:08 PM, Haojian Zhuang wrote:
> Remove all code to parse sp804 in integrator platform driver. Use
> clocksource_of_init() instead since these code are implemented in sp804
> driver already.
> 
> Signed-off-by: Haojian Zhuang <haojian.zhuang@linaro.org>
> ---
>  arch/arm/boot/dts/integrator.dtsi        |    3 +++
>  arch/arm/boot/dts/integratorap.dts       |   15 +++++------
>  arch/arm/boot/dts/integratorcp.dts       |    9 +++----
>  arch/arm/mach-integrator/Kconfig         |    3 +++
>  arch/arm/mach-integrator/integrator_ap.c |   41 +-----------------------------
>  arch/arm/mach-integrator/integrator_cp.c |   35 ++-----------------------
>  6 files changed, 20 insertions(+), 86 deletions(-)


> diff --git a/arch/arm/boot/dts/integratorap.dts b/arch/arm/boot/dts/integratorap.dts
> index c9c3fa3..70e321c 100644
> --- a/arch/arm/boot/dts/integratorap.dts
> +++ b/arch/arm/boot/dts/integratorap.dts
> @@ -9,11 +9,6 @@
>  	model = "ARM Integrator/AP";
>  	compatible = "arm,integrator-ap";
>  
> -	aliases {
> -		arm,timer-primary = &timer2;
> -		arm,timer-secondary = &timer1;
> -	};
> -
>  	chosen {
>  		bootargs = "root=/dev/ram0 console=ttyAM0,38400n8 earlyprintk";
>  	};
> @@ -24,15 +19,19 @@
>  	};
>  
>  	timer0: timer@13000000 {
> -		compatible = "arm,integrator-timer";
> +		compatible = "arm,sp804", "arm,primecell";

You are breaking existing dtb's changing this, but this is wrong for
other reasons. The integrator does not have an SP804. It is the same
programming model, but is a single timer and not the dual timer. So
having a different compatible string is the correct way. I doubt it has
the primecell ID registers which is what "arm,primecell" indicates.

>  	};
>  
>  	timer1: timer@13000100 {
> -		compatible = "arm,integrator-timer";
> +		compatible = "arm,sp804", "arm,primecell";
> +		arm,sp804-clockevent = <0>;

I don't like this nor the old way with aliases. We should describe the
h/w features of the timer to determine what to use it for. AFAICT, all 3
timers are identical on integrator and it does not matter which one
Linux picks for clocksource vs. clockevent.

Rob
Arnd Bergmann March 12, 2013, 7:33 p.m. UTC | #3
On Tuesday 12 March 2013, Rob Herring wrote:
> On 03/12/2013 12:08 PM, Haojian Zhuang wrote:

> You are breaking existing dtb's changing this, but this is wrong for
> other reasons. The integrator does not have an SP804. It is the same
> programming model, but is a single timer and not the dual timer. So
> having a different compatible string is the correct way. I doubt it has
> the primecell ID registers which is what "arm,primecell" indicates.

At least the qemu model has the primecell ID only for actual sp804 but
not for the integrator, see http://git.qemu.org/?p=qemu.git;a=blob;f=hw/arm_timer.c

> >  	};
> >  
> >  	timer1: timer@13000100 {
> > -		compatible = "arm,integrator-timer";
> > +		compatible = "arm,sp804", "arm,primecell";
> > +		arm,sp804-clockevent = <0>;
> 
> I don't like this nor the old way with aliases. We should describe the
> h/w features of the timer to determine what to use it for. AFAICT, all 3
> timers are identical on integrator and it does not matter which one
> Linux picks for clocksource vs. clockevent.

Interesting point. I remember we had a discussion about this when the
initial binding integrator code was merged, but I don't remember what
the intent was for doing it like this. Probably just minimizing the
changes to the existing code.

	Arnd
Rob Herring March 12, 2013, 8:52 p.m. UTC | #4
On 03/12/2013 02:33 PM, Arnd Bergmann wrote:
> On Tuesday 12 March 2013, Rob Herring wrote:
>> On 03/12/2013 12:08 PM, Haojian Zhuang wrote:
> 
>> You are breaking existing dtb's changing this, but this is wrong for
>> other reasons. The integrator does not have an SP804. It is the same
>> programming model, but is a single timer and not the dual timer. So
>> having a different compatible string is the correct way. I doubt it has
>> the primecell ID registers which is what "arm,primecell" indicates.
> 
> At least the qemu model has the primecell ID only for actual sp804 but
> not for the integrator, see http://git.qemu.org/?p=qemu.git;a=blob;f=hw/arm_timer.c

Right. I think I added that to qemu...

Since the primecell ID registers are defined to be at 0xFF? offset, it
is quite impossible for the integrator timers to have the ID registers
within their 0x100 address space.

> 
>>>  	};
>>>  
>>>  	timer1: timer@13000100 {
>>> -		compatible = "arm,integrator-timer";
>>> +		compatible = "arm,sp804", "arm,primecell";
>>> +		arm,sp804-clockevent = <0>;
>>
>> I don't like this nor the old way with aliases. We should describe the
>> h/w features of the timer to determine what to use it for. AFAICT, all 3
>> timers are identical on integrator and it does not matter which one
>> Linux picks for clocksource vs. clockevent.
> 
> Interesting point. I remember we had a discussion about this when the
> initial binding integrator code was merged, but I don't remember what
> the intent was for doing it like this. Probably just minimizing the
> changes to the existing code.

Re-reading the thread on this for Integrator, I don't think any
conclusion was really made, but it got merged. I don't think we should
a) change the existing alias names or b) expand their usage. OMAP is a
good example of lots of timers and using h/w properties to select
particular instances based on properties. I think on the ARM boards the
selection has been pretty arbitrary. I traced the commit history back
for the timer code and could not find any evidence to the contrary
except on VExpress which has broken sp804 on the core tile.

Another part is sched_clock selection. I use the sp804 on highbank, but
the ARM boards have their 24MHz counter. Also, if I'm on midway, then I
want to use the arch timers even though the sp804 is still there. My
sched_clock patches yesterday attempt to address that. Any comments on
what characteristics should be used to select timer would be helpful.
Higher frequency and/or bits is better, but to what limit in frequency?

Rob
Haojian Zhuang March 13, 2013, 2 a.m. UTC | #5
On 13 March 2013 02:54, Arnd Bergmann <arnd@arndb.de> wrote:
> On Tuesday 12 March 2013, Haojian Zhuang wrote:
>>
>> -       aliases {
>> -               arm,timer-primary = &timer2;
>> -               arm,timer-secondary = &timer1;
>> -       };
>> -
>>         chosen {
>>                 bootargs = "root=/dev/ram0 console=ttyAMA0,38400n8 earlyprintk";
>>         };
>> @@ -29,10 +24,14 @@
>>
>>         timer1: timer@13000100 {
>>                 compatible = "arm,sp804", "arm,primecell";
>> +               arm,sp804-clockevent = <0>;
>> +               status = "ok";
>>         };
>
> I would prefer to keep the old way of doing this. The "aliases" node is
> meant for configuration, while the timer device itself should not care
> whether it is used as a clockevent or clocksource or some other device.
>
> We could probably change the identifiers in the aliases to
> "linux,clocksource" and "linux,clockevents" instead of
> "arm,timer-primary" and "arm,timer-secondary".
>
>         Arnd

SP804 is a dual timer. Either TIMER1 or TIMER2 in the same SP804
could be configured as clock source. And either TIMER1 or TIMER2 in
the same SP804 could be configured as clock event.


Integratorap/cp is always using TIMER1 of SP804-A as clocksource,
and TIMER1 of SP804-B as clockevent. This usage case always
drop TIMER2 usage.

I tried to add arm,sp804-clocksource to specify whether TIMER1 or
TIMER2 is configured as clocksource. For example, realview is using
TIMER2 of SP804-B as clockevent and TIMER1 of SP804-A as
clocksource.

If we decide to support the property like "arm,sp804-clocksource",
we could get the TIMER offset & clock source information at the same
time. Then alias configuration is duplicated.

If we decide to support alais configuration like "linux,clocksource",
we could get clock source/event information. But we don't know which
TIMER is used in the same SP804. We still need one property to
identify it. And people are also using both TIMERs in the same SP804,
like vexpress v2m.

Which method do we choose?

Regards
Haojian
Haojian Zhuang March 13, 2013, 2:04 a.m. UTC | #6
On 13 March 2013 04:52, Rob Herring <robherring2@gmail.com> wrote:
> On 03/12/2013 02:33 PM, Arnd Bergmann wrote:
>> On Tuesday 12 March 2013, Rob Herring wrote:
>>> On 03/12/2013 12:08 PM, Haojian Zhuang wrote:
>
> Since the primecell ID registers are defined to be at 0xFF? offset, it
> is quite impossible for the integrator timers to have the ID registers
> within their 0x100 address space.
>
I'm sorry that I don't know this. I can add "arm,integrator-timer" into
compatible table of sp804 driver. I think that we could resolve this
issue. What's your opinion?

>>
>>>>     };
>>>>
>>>>     timer1: timer@13000100 {
>>>> -           compatible = "arm,integrator-timer";
>>>> +           compatible = "arm,sp804", "arm,primecell";
>>>> +           arm,sp804-clockevent = <0>;
>>>
>>> I don't like this nor the old way with aliases. We should describe the
>>> h/w features of the timer to determine what to use it for. AFAICT, all 3
>>> timers are identical on integrator and it does not matter which one
>>> Linux picks for clocksource vs. clockevent.
>>
>> Interesting point. I remember we had a discussion about this when the
>> initial binding integrator code was merged, but I don't remember what
>> the intent was for doing it like this. Probably just minimizing the
>> changes to the existing code.
>
> Re-reading the thread on this for Integrator, I don't think any
> conclusion was really made, but it got merged. I don't think we should
> a) change the existing alias names or b) expand their usage. OMAP is a
> good example of lots of timers and using h/w properties to select
> particular instances based on properties. I think on the ARM boards the
> selection has been pretty arbitrary. I traced the commit history back
> for the timer code and could not find any evidence to the contrary
> except on VExpress which has broken sp804 on the core tile.
>
> Another part is sched_clock selection. I use the sp804 on highbank, but
> the ARM boards have their 24MHz counter. Also, if I'm on midway, then I
> want to use the arch timers even though the sp804 is still there. My
> sched_clock patches yesterday attempt to address that. Any comments on
> what characteristics should be used to select timer would be helpful.
> Higher frequency and/or bits is better, but to what limit in frequency?
>
> Rob
>
Linus Walleij March 13, 2013, 5:25 a.m. UTC | #7
On Tue, Mar 12, 2013 at 7:54 PM, Arnd Bergmann <arnd@arndb.de> wrote:
> On Tuesday 12 March 2013, Haojian Zhuang wrote:

>>         timer1: timer@13000100 {
>>                 compatible = "arm,sp804", "arm,primecell";
>> +               arm,sp804-clockevent = <0>;
>> +               status = "ok";
>>         };
>
> I would prefer to keep the old way of doing this. The "aliases" node is
> meant for configuration, while the timer device itself should not care
> whether it is used as a clockevent or clocksource or some other device.
>
> We could probably change the identifiers in the aliases to
> "linux,clocksource" and "linux,clockevents" instead of
> "arm,timer-primary" and "arm,timer-secondary".

I named it like that exactly to be OS-neutral.

The idea is that what timer is to be used for primary and secondary
may have implications on things like power consumption and
one timer may be broken in the HW etc. That is not a Linux-specific
thing.

A bit hard to do things right here :-/

Yours,
Linus Walleij
Linus Walleij March 13, 2013, 6:35 a.m. UTC | #8
On Tue, Mar 12, 2013 at 6:08 PM, Haojian Zhuang
<haojian.zhuang@linaro.org> wrote:

> Remove all code to parse sp804 in integrator platform driver. Use
> clocksource_of_init() instead since these code are implemented in sp804
> driver already.

(...)
> --- a/arch/arm/boot/dts/integratorap.dts
> +++ b/arch/arm/boot/dts/integratorap.dts
>         timer0: timer@13000000 {
> -               compatible = "arm,integrator-timer";
> +               compatible = "arm,sp804", "arm,primecell";
>         };
>
>         timer1: timer@13000100 {
> -               compatible = "arm,integrator-timer";
> +               compatible = "arm,sp804", "arm,primecell";
> +               arm,sp804-clockevent = <0>;
> +               status = "ok";
>         };
>
>         timer2: timer@13000200 {
> -               compatible = "arm,integrator-timer";
> +               compatible = "arm,sp804", "arm,primecell";
> +               arm,sp804-clocksource = <0>;
> +               status = "ok";
>         };

This is wrong. The Integrator/AP timer is not an SP804.
It may look similar but it's a totally different kind of beast.

What is needed is to break out the timer code from
integrator_ap.c and move it to drivers/clocksource,
but wait with that until you fixed the SP804 in the
Integrator/CP, which is probably what you want to
prioritize.

(...)
> --- a/arch/arm/mach-integrator/Kconfig
> +++ b/arch/arm/mach-integrator/Kconfig
(...)
> -static void __init ap_of_timer_init(void)
> -{
> -       struct device_node *node;
> -       const char *path;
> -       void __iomem *base;
> -       int err;
> -       int irq;
> -       struct clk *clk;
> -       unsigned long rate;
> -
> -       clk = clk_get_sys("ap_timer", NULL);

As you see you are not changing this clock lookup either, which
will never work.

Please restrict the patch to altering Integrator/CP.

Yours,
Linus Walleij
Linus Walleij March 13, 2013, 6:41 a.m. UTC | #9
On Wed, Mar 13, 2013 at 3:04 AM, Haojian Zhuang
<haojian.zhuang@linaro.org> wrote:
> On 13 March 2013 04:52, Rob Herring <robherring2@gmail.com> wrote:
>> On 03/12/2013 02:33 PM, Arnd Bergmann wrote:
>>> On Tuesday 12 March 2013, Rob Herring wrote:
>>>> On 03/12/2013 12:08 PM, Haojian Zhuang wrote:
>>
>> Since the primecell ID registers are defined to be at 0xFF? offset, it
>> is quite impossible for the integrator timers to have the ID registers
>> within their 0x100 address space.
>>
> I'm sorry that I don't know this. I can add "arm,integrator-timer" into
> compatible table of sp804 driver. I think that we could resolve this
> issue. What's your opinion?

As per the other discussion:

The Integrator/AP has a timer which is different from the SP804.

The Integrator/CP has an SP804.

Please restrict the patch set to Integrator/CP.

Yours,
Linus Walleij
Haojian Zhuang March 13, 2013, 7:09 a.m. UTC | #10
On 13 March 2013 14:41, Linus Walleij <linus.walleij@linaro.org> wrote:
> On Wed, Mar 13, 2013 at 3:04 AM, Haojian Zhuang
> <haojian.zhuang@linaro.org> wrote:
>> On 13 March 2013 04:52, Rob Herring <robherring2@gmail.com> wrote:
>>> On 03/12/2013 02:33 PM, Arnd Bergmann wrote:
>>>> On Tuesday 12 March 2013, Rob Herring wrote:
>>>>> On 03/12/2013 12:08 PM, Haojian Zhuang wrote:
>>>
>>> Since the primecell ID registers are defined to be at 0xFF? offset, it
>>> is quite impossible for the integrator timers to have the ID registers
>>> within their 0x100 address space.
>>>
>> I'm sorry that I don't know this. I can add "arm,integrator-timer" into
>> compatible table of sp804 driver. I think that we could resolve this
>> issue. What's your opinion?
>
> As per the other discussion:
>
> The Integrator/AP has a timer which is different from the SP804.
>
> The Integrator/CP has an SP804.
>
> Please restrict the patch set to Integrator/CP.
>
> Yours,
> Linus Walleij

According to Rob's comment, I thought that only prime-cell ID registers
are not contained in integrator-timer controller.

Yes, I missed to include lookup to handle integrator-ap timer. I think that
we can use a string name that are binded to different compatible name.
Code is in below.

#define SP804_TIMER       0
#define INTEGRATOR_TIMER     1
#define ID_BUF_SIZE         16

match table:
{.compatible = "arm,sp804", .data = SP804_TIMER, },
{.compaitlbe = "arm,integrator-timer", .data = INTEGRATOR_TIMER, },

sp804_get_clock_rate():
    char dev_id_buf[ID_BUF_SIZE];
    if (match->data)
        snprintf(dev_id_buf, ID_BUF_SIZE, "ap_timer");
    else
        snprintf(dev_id_buf, ID_BUF_SIZE, "sp804");
    clk = clk_get_sys(dev_id_buf, name);

sp804_dt_init_clk():
    char dev_id_buf[ID_BUF_SIZE];
    if (match->data)
        snprintf(dev_id_buf, ID_BUF_SIZE, "ap_timer");
    else
        snprintf(dev_id_buf, ID_BUF_SIZE, "sp804");
    lookup = clkdev_alloc(clk, name, dev_id_buf);

I think it won't break out code any more. Since most code are same, we
could handle it in the same driver.

Regards
Haojian
Arnd Bergmann March 13, 2013, 8:43 a.m. UTC | #11
On Wednesday 13 March 2013, Haojian Zhuang wrote:
> match table:
> {.compatible = "arm,sp804", .data = SP804_TIMER, },
> {.compaitlbe = "arm,integrator-timer", .data = INTEGRATOR_TIMER, },
> 
> sp804_get_clock_rate():
>     char dev_id_buf[ID_BUF_SIZE];
>     if (match->data)
>         snprintf(dev_id_buf, ID_BUF_SIZE, "ap_timer");
>     else
>         snprintf(dev_id_buf, ID_BUF_SIZE, "sp804");
>     clk = clk_get_sys(dev_id_buf, name);
> 
> sp804_dt_init_clk():
>     char dev_id_buf[ID_BUF_SIZE];
>     if (match->data)
>         snprintf(dev_id_buf, ID_BUF_SIZE, "ap_timer");
>     else
>         snprintf(dev_id_buf, ID_BUF_SIZE, "sp804");
>     lookup = clkdev_alloc(clk, name, dev_id_buf);
> 
> I think it won't break out code any more. Since most code are same, we
> could handle it in the same driver.
> 

The data member of struct of_device_id is a pointer, you could
point that directly to a const string containing the name, if that
is the only difference, or to another structure that describes
the other parameters.

	Arnd
Linus Walleij March 13, 2013, 9 a.m. UTC | #12
On Wed, Mar 13, 2013 at 8:09 AM, Haojian Zhuang
<haojian.zhuang@linaro.org> wrote:
> On 13 March 2013 14:41, Linus Walleij <linus.walleij@linaro.org> wrote:

>> Please restrict the patch set to Integrator/CP.
>
> According to Rob's comment, I thought that only prime-cell ID registers
> are not contained in integrator-timer controller.

No, you have missed the most important aspect of all.

The Integrator/AP timer is only 16 bit. Look:

static void integrator_clocksource_init(unsigned long inrate,
					void __iomem *base)
{
(...)
	clocksource_mmio_init(base + TIMER_VALUE, "timer2",
			rate, 200, 16, clocksource_mmio_readl_down);
(...)
}

Compare:

void __init __sp804_clocksource_and_sched_clock_init(void __iomem *base,
						     const char *name,
						     int use_sched_clock)
{
(...)
	clocksource_mmio_init(base + TIMER_VALUE, name,
		rate, 200, 32, clocksource_mmio_readl_down);
(...)
}

I had the idea of merging the drivers in the past so I'm
positive to the change from a larger perspective.
But then you also have to take the bit-width precision
into account.

If you apply the patch, the Integrator/AP will appear
to work, then if you leave it dormant until the counter
wraps around it will hang or screw up the timeline.

Another part of the problem I see is that the code in the
SP804 driver is lacking features found in the code for the
driver inside integrator_ap.c, I detail these below...

> Yes, I missed to include lookup to handle integrator-ap timer. I think that
> we can use a string name that are binded to different compatible name.
> Code is in below.
(...)
> sp804_get_clock_rate():
>     char dev_id_buf[ID_BUF_SIZE];
>     if (match->data)
>         snprintf(dev_id_buf, ID_BUF_SIZE, "ap_timer");
>     else
>         snprintf(dev_id_buf, ID_BUF_SIZE, "sp804");
>     clk = clk_get_sys(dev_id_buf, name);
>
> sp804_dt_init_clk():
>     char dev_id_buf[ID_BUF_SIZE];
>     if (match->data)
>         snprintf(dev_id_buf, ID_BUF_SIZE, "ap_timer");
>     else
>         snprintf(dev_id_buf, ID_BUF_SIZE, "sp804");
>     lookup = clkdev_alloc(clk, name, dev_id_buf);

If this is what you want to attain, I think it would be better to
just patch the clock lookups in
drivers/clk/clk-integrator.c and rename the "ap_timer"
lookup to "sp804" and skip all the above.

> I think it won't break out code any more. Since most code are same, we
> could handle it in the same driver.

So it will definately break stuff. Being 16bit is not all,
here are other differences:

static u32 notrace integrator_read_sched_clock(void)
{
	return -readl((void __iomem *) TIMER2_VA_BASE + TIMER_VALUE);
}

static void integrator_clocksource_init(unsigned long inrate,
					void __iomem *base)
{
(...)
	setup_sched_clock(integrator_read_sched_clock, 16, rate);
}

Since you do not call even integrator_clocksource_init() anymore
after this, you are breaking sched_clock() for the Integrator/AP.
The SP804 doesn't even have a sched_clock option.
You make it fall back to using jiffies.

So you would have to add this to the SP804 driver, and make
sure sched_clock() is registered for the "arm,integrator"
compatible string.

Then the Integrator/AP timer has this code in the
integrator_clockevent_init() function:

	clkevt_base = base;
	/* Calculate and program a divisor */
	if (rate > 0x100000 * HZ) {
		rate /= 256;
		ctrl |= TIMER_CTRL_DIV256;
	} else if (rate > 0x10000 * HZ) {
		rate /= 16;
		ctrl |= TIMER_CTRL_DIV16;
	}

It appears the SP804 has no code to handle this
divisor/prescaler at all. Which means that you regress
the Integrator/AP again.

It appears (from arm_timer.h) that the SP804 actually
has this prescaler, but then first make a separate patch
to make the SP804 driver use that divisor (like above) so
you don't degrade the quality of the AP timer. The prescaler
make it possible to let the system sleep for longer times
when using NO_HZ so it's *pretty* important (and it will
increas the quality of all SP804-based machines as well).

As you surely understand, since the counter on the
Integrator/AP is only 16 bit this is very important, since
we don't want it to wake up too often.

Then the clocksource init function has this:

	if (rate >= 1500000) {
		rate /= 16;
		ctrl |= TIMER_CTRL_DIV16;
	}

Here I'm more uncertain. As I know from extensive
discussions with John and Thomas, there are many many
factors to decide the clocksource. Basically the above lines
limits the precision of the clocksource in exchange for a
longer time until wrap-around. Again, the 16bit nature of the
Integrator/AP timer is a factor here.

Yours,
Linus Walleij
Linus Walleij March 13, 2013, 9:03 a.m. UTC | #13
On Tue, Mar 12, 2013 at 8:33 PM, Arnd Bergmann <arnd@arndb.de> wrote:
> On Tuesday 12 March 2013, Rob Herring wrote:
>> On 03/12/2013 12:08 PM, Haojian Zhuang wrote:
>
>> You are breaking existing dtb's changing this, but this is wrong for
>> other reasons. The integrator does not have an SP804. It is the same
>> programming model, but is a single timer and not the dual timer. So
>> having a different compatible string is the correct way. I doubt it has
>> the primecell ID registers which is what "arm,primecell" indicates.
>
> At least the qemu model has the primecell ID only for actual sp804 but
> not for the integrator, see http://git.qemu.org/?p=qemu.git;a=blob;f=hw/arm_timer.c

That is the for the Integrator/CP model right?

Part of the problem with the current patch series is assuming
similarity between the Integrator/AP and Integrator/CP.

The hardware (VHDL code) for the Integrator/CP SP804 block
is certainly based on the Integrator/AP (unnamed "integrator timer")
but many changes were made, the most drastic extending the
counters from 16 to 32 bits.

Yours,
Linus Walleij
Rob Herring March 13, 2013, 1:56 p.m. UTC | #14
On 03/13/2013 01:41 AM, Linus Walleij wrote:
> On Wed, Mar 13, 2013 at 3:04 AM, Haojian Zhuang
> <haojian.zhuang@linaro.org> wrote:
>> On 13 March 2013 04:52, Rob Herring <robherring2@gmail.com> wrote:
>>> On 03/12/2013 02:33 PM, Arnd Bergmann wrote:
>>>> On Tuesday 12 March 2013, Rob Herring wrote:
>>>>> On 03/12/2013 12:08 PM, Haojian Zhuang wrote:
>>>
>>> Since the primecell ID registers are defined to be at 0xFF? offset, it
>>> is quite impossible for the integrator timers to have the ID registers
>>> within their 0x100 address space.
>>>
>> I'm sorry that I don't know this. I can add "arm,integrator-timer" into
>> compatible table of sp804 driver. I think that we could resolve this
>> issue. What's your opinion?
> 
> As per the other discussion:
> 
> The Integrator/AP has a timer which is different from the SP804.
> 
> The Integrator/CP has an SP804.

Not quite. I was wrong that they are the same, The AP has 16-bit timers
and the CP has 32-bit timers. While the individual timer on the AP is
the same programming model as the sp804, it is still not the sp804 block.

Rob

> 
> Please restrict the patch set to Integrator/CP.
> 
> Yours,
> Linus Walleij
>
Russell King - ARM Linux March 15, 2013, 11:54 a.m. UTC | #15
On Tue, Mar 12, 2013 at 02:15:09PM -0500, Rob Herring wrote:
> You are breaking existing dtb's changing this, but this is wrong for
> other reasons. The integrator does not have an SP804. It is the same
> programming model,

No, it is not the same programming model.  It is a subset of the model.
Integrator/AP is only 16-bit, and needs to have a prescaler configured
according to the clockrate.  Integrator/CP and later platforms have
SP804, which looks the same, but is 32-bit instead.

If you look at arch/arm/include/asm/hardware/arm_timer.h, you will see
that I fully documented what registers and bits are present on each
platform of AP, CP, Versatile/AB,PB and Realview.

Note that even through the CP and Versatile platforms are both called
SP804, there are subtle differences between the implementations.
Russell King - ARM Linux March 15, 2013, 12:15 p.m. UTC | #16
On Wed, Mar 13, 2013 at 10:00:14AM +0100, Linus Walleij wrote:
> Then the Integrator/AP timer has this code in the
> integrator_clockevent_init() function:
> 
> 	clkevt_base = base;
> 	/* Calculate and program a divisor */
> 	if (rate > 0x100000 * HZ) {
> 		rate /= 256;
> 		ctrl |= TIMER_CTRL_DIV256;
> 	} else if (rate > 0x10000 * HZ) {
> 		rate /= 16;
> 		ctrl |= TIMER_CTRL_DIV16;
> 	}
> 
> It appears the SP804 has no code to handle this
> divisor/prescaler at all. Which means that you regress
> the Integrator/AP again.
> 
> It appears (from arm_timer.h) that the SP804 actually
> has this prescaler, but then first make a separate patch
> to make the SP804 driver use that divisor (like above) so
> you don't degrade the quality of the AP timer. The prescaler
> make it possible to let the system sleep for longer times
> when using NO_HZ so it's *pretty* important (and it will
> increas the quality of all SP804-based machines as well).
> 
> As you surely understand, since the counter on the
> Integrator/AP is only 16 bit this is very important, since
> we don't want it to wake up too often.
> 
> Then the clocksource init function has this:
> 
> 	if (rate >= 1500000) {
> 		rate /= 16;
> 		ctrl |= TIMER_CTRL_DIV16;
> 	}
> 
> Here I'm more uncertain. As I know from extensive
> discussions with John and Thomas, there are many many
> factors to decide the clocksource. Basically the above lines
> limits the precision of the clocksource in exchange for a
> longer time until wrap-around. Again, the 16bit nature of the
> Integrator/AP timer is a factor here.

Integrator/AP selects the prescaler based on the fact that it is only a
16-bit counter, which has to be clocked at a rate which gives us our
desired periodic tick interval.

SP804 does not do this because, being a 32-bit counter, it is completely
unnecessary to use the prescaler - and using the prescaler will result
in loss of fine-grained timing accuracy.  Making use of the prescaler
there is absurd - the timer will run to 4000 odd seconds before wrapping
with a prescaler of 1.

You end up needing 64-bit arithmetic if you try to apply the Integrator/AP
logic to SP804 - it becomes this:

 	if (rate > 0x1000000000 * HZ) {
 		rate /= 256;
 		ctrl |= TIMER_CTRL_DIV256;
 	} else if (rate > 0x100000000 * HZ) {
 		rate /= 16;
 		ctrl |= TIMER_CTRL_DIV16;
 	}

or more precisely:
	if (rate > HZ * 0x100 * ((unsigned long long)wrap + 1)) {
...
 	} else if (rate > HZ * 0x10 * ((unsigned long long)wrap + 1)) {
...

You need that wrap value anyway for clockevents_config_and_register.
Linus Walleij March 15, 2013, 1:59 p.m. UTC | #17
On Fri, Mar 15, 2013 at 1:15 PM, Russell King - ARM Linux
<linux@arm.linux.org.uk> wrote:

> Integrator/AP selects the prescaler based on the fact that it is only a
> 16-bit counter, which has to be clocked at a rate which gives us our
> desired periodic tick interval.

Ah! I knew there was some simple explanation behind this.

Thanks Russell!
Linus Walleij
diff mbox

Patch

diff --git a/arch/arm/boot/dts/integrator.dtsi b/arch/arm/boot/dts/integrator.dtsi
index 813b91d..749ac21 100644
--- a/arch/arm/boot/dts/integrator.dtsi
+++ b/arch/arm/boot/dts/integrator.dtsi
@@ -9,18 +9,21 @@ 
 		reg = <0x13000000 0x100>;
 		interrupt-parent = <&pic>;
 		interrupts = <5>;
+		status = "disabled";
 	};
 
 	timer@13000100 {
 		reg = <0x13000100 0x100>;
 		interrupt-parent = <&pic>;
 		interrupts = <6>;
+		status = "disabled";
 	};
 
 	timer@13000200 {
 		reg = <0x13000200 0x100>;
 		interrupt-parent = <&pic>;
 		interrupts = <7>;
+		status = "disabled";
 	};
 
 	pic@14000000 {
diff --git a/arch/arm/boot/dts/integratorap.dts b/arch/arm/boot/dts/integratorap.dts
index c9c3fa3..70e321c 100644
--- a/arch/arm/boot/dts/integratorap.dts
+++ b/arch/arm/boot/dts/integratorap.dts
@@ -9,11 +9,6 @@ 
 	model = "ARM Integrator/AP";
 	compatible = "arm,integrator-ap";
 
-	aliases {
-		arm,timer-primary = &timer2;
-		arm,timer-secondary = &timer1;
-	};
-
 	chosen {
 		bootargs = "root=/dev/ram0 console=ttyAM0,38400n8 earlyprintk";
 	};
@@ -24,15 +19,19 @@ 
 	};
 
 	timer0: timer@13000000 {
-		compatible = "arm,integrator-timer";
+		compatible = "arm,sp804", "arm,primecell";
 	};
 
 	timer1: timer@13000100 {
-		compatible = "arm,integrator-timer";
+		compatible = "arm,sp804", "arm,primecell";
+		arm,sp804-clockevent = <0>;
+		status = "ok";
 	};
 
 	timer2: timer@13000200 {
-		compatible = "arm,integrator-timer";
+		compatible = "arm,sp804", "arm,primecell";
+		arm,sp804-clocksource = <0>;
+		status = "ok";
 	};
 
 	pic: pic@14000000 {
diff --git a/arch/arm/boot/dts/integratorcp.dts b/arch/arm/boot/dts/integratorcp.dts
index 8b11939..19b2e3e 100644
--- a/arch/arm/boot/dts/integratorcp.dts
+++ b/arch/arm/boot/dts/integratorcp.dts
@@ -9,11 +9,6 @@ 
 	model = "ARM Integrator/CP";
 	compatible = "arm,integrator-cp";
 
-	aliases {
-		arm,timer-primary = &timer2;
-		arm,timer-secondary = &timer1;
-	};
-
 	chosen {
 		bootargs = "root=/dev/ram0 console=ttyAMA0,38400n8 earlyprintk";
 	};
@@ -29,10 +24,14 @@ 
 
 	timer1: timer@13000100 {
 		compatible = "arm,sp804", "arm,primecell";
+		arm,sp804-clockevent = <0>;
+		status = "ok";
 	};
 
 	timer2: timer@13000200 {
 		compatible = "arm,sp804", "arm,primecell";
+		arm,sp804-clocksource = <0>;
+		status = "ok";
 	};
 
 	pic: pic@14000000 {
diff --git a/arch/arm/mach-integrator/Kconfig b/arch/arm/mach-integrator/Kconfig
index abeff25..031f43a 100644
--- a/arch/arm/mach-integrator/Kconfig
+++ b/arch/arm/mach-integrator/Kconfig
@@ -5,6 +5,7 @@  menu "Integrator Options"
 config ARCH_INTEGRATOR_AP
 	bool "Support Integrator/AP and Integrator/PP2 platforms"
 	select CLKSRC_MMIO
+	select CLKSRC_OF
 	select MIGHT_HAVE_PCI
 	select SERIAL_AMBA_PL010
 	select SERIAL_AMBA_PL010_CONSOLE
@@ -15,6 +16,8 @@  config ARCH_INTEGRATOR_AP
 
 config ARCH_INTEGRATOR_CP
 	bool "Support Integrator/CP platform"
+	select CLKSRC_MMIO
+	select CLKSRC_OF
 	select ARCH_CINTEGRATOR
 	select ARM_TIMER_SP804
 	select PLAT_VERSATILE_CLCD
diff --git a/arch/arm/mach-integrator/integrator_ap.c b/arch/arm/mach-integrator/integrator_ap.c
index c2112ff..2266944 100644
--- a/arch/arm/mach-integrator/integrator_ap.c
+++ b/arch/arm/mach-integrator/integrator_ap.c
@@ -425,45 +425,6 @@  void __init ap_init_early(void)
 
 #ifdef CONFIG_OF
 
-static void __init ap_of_timer_init(void)
-{
-	struct device_node *node;
-	const char *path;
-	void __iomem *base;
-	int err;
-	int irq;
-	struct clk *clk;
-	unsigned long rate;
-
-	clk = clk_get_sys("ap_timer", NULL);
-	BUG_ON(IS_ERR(clk));
-	clk_prepare_enable(clk);
-	rate = clk_get_rate(clk);
-
-	err = of_property_read_string(of_aliases,
-				"arm,timer-primary", &path);
-	if (WARN_ON(err))
-		return;
-	node = of_find_node_by_path(path);
-	base = of_iomap(node, 0);
-	if (WARN_ON(!base))
-		return;
-	writel(0, base + TIMER_CTRL);
-	integrator_clocksource_init(rate, base);
-
-	err = of_property_read_string(of_aliases,
-				"arm,timer-secondary", &path);
-	if (WARN_ON(err))
-		return;
-	node = of_find_node_by_path(path);
-	base = of_iomap(node, 0);
-	if (WARN_ON(!base))
-		return;
-	irq = irq_of_parse_and_map(node, 0);
-	writel(0, base + TIMER_CTRL);
-	integrator_clockevent_init(rate, base, irq);
-}
-
 static const struct of_device_id fpga_irq_of_match[] __initconst = {
 	{ .compatible = "arm,versatile-fpga-irq", .data = fpga_irq_of_init, },
 	{ /* Sentinel */ }
@@ -582,7 +543,7 @@  DT_MACHINE_START(INTEGRATOR_AP_DT, "ARM Integrator/AP (Device Tree)")
 	.init_early	= ap_init_early,
 	.init_irq	= ap_init_irq_of,
 	.handle_irq	= fpga_handle_irq,
-	.init_time	= ap_of_timer_init,
+	.init_time	= clocksource_of_init,
 	.init_machine	= ap_init_of,
 	.restart	= integrator_restart,
 	.dt_compat      = ap_dt_board_compat,
diff --git a/arch/arm/mach-integrator/integrator_cp.c b/arch/arm/mach-integrator/integrator_cp.c
index 40373ec..6e1c340 100644
--- a/arch/arm/mach-integrator/integrator_cp.c
+++ b/arch/arm/mach-integrator/integrator_cp.c
@@ -28,6 +28,7 @@ 
 #include <linux/of_address.h>
 #include <linux/of_platform.h>
 #include <linux/sys_soc.h>
+#include <linux/clocksource.h>
 #include <clocksource/arm_timer.h>
 #include <clocksource/timer-sp.h>
 
@@ -251,38 +252,6 @@  static void __init intcp_init_early(void)
 
 #ifdef CONFIG_OF
 
-static void __init cp_of_timer_init(void)
-{
-	struct device_node *node;
-	const char *path;
-	void __iomem *base;
-	int err;
-	int irq;
-
-	err = of_property_read_string(of_aliases,
-				"arm,timer-primary", &path);
-	if (WARN_ON(err))
-		return;
-	node = of_find_node_by_path(path);
-	base = of_iomap(node, 0);
-	if (WARN_ON(!base))
-		return;
-	writel(0, base + TIMER_CTRL);
-	sp804_clocksource_init(base, node->name);
-
-	err = of_property_read_string(of_aliases,
-				"arm,timer-secondary", &path);
-	if (WARN_ON(err))
-		return;
-	node = of_find_node_by_path(path);
-	base = of_iomap(node, 0);
-	if (WARN_ON(!base))
-		return;
-	irq = irq_of_parse_and_map(node, 0);
-	writel(0, base + TIMER_CTRL);
-	sp804_clockevents_init(base, irq, node->name);
-}
-
 static const struct of_device_id fpga_irq_of_match[] __initconst = {
 	{ .compatible = "arm,versatile-fpga-irq", .data = fpga_irq_of_init, },
 	{ /* Sentinel */ }
@@ -386,7 +355,7 @@  DT_MACHINE_START(INTEGRATOR_CP_DT, "ARM Integrator/CP (Device Tree)")
 	.init_early	= intcp_init_early,
 	.init_irq	= intcp_init_irq_of,
 	.handle_irq	= fpga_handle_irq,
-	.init_time	= cp_of_timer_init,
+	.init_time	= clocksource_of_init,
 	.init_machine	= intcp_init_of,
 	.restart	= integrator_restart,
 	.dt_compat      = intcp_dt_board_compat,