lkml.org 
[lkml]   [2011]   [Mar]   [8]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCHv6 2/5] picoxcell: support for the common system timers
    Date
    The picoXcell devices have 4 timers capable of generating interrupts
    when they reach a predefined value and restarting and a freerunning RTC.
    Use one of the interrupt capable timers as the clockevent_device and the
    RTC for the clocksource and sched_clock().

    v2:
    - use clocksource_register_hz() and
    clockevents_calc_mult_shift() rather than specifying .mult and
    .shift.

    v3:
    - Incorporate feedback from RMK.
    - Don't emulate oneshot mode in the timers, let the generic
    clockevents layer do it.
    - Provide a better sched_clock() based on plat-nomadik's.
    - Convert to __raw_ io accessors.

    v4:
    - Use the fixed_sched_clock helpers for sched_clock()
    implementation as provided by RMK and remove redunant use of
    __init.

    Signed-off-by: Jamie Iles <jamie@jamieiles.com>
    ---
    arch/arm/mach-picoxcell/Makefile | 3 +-
    arch/arm/mach-picoxcell/picoxcell_core.c | 1 +
    arch/arm/mach-picoxcell/picoxcell_core.h | 3 +
    arch/arm/mach-picoxcell/time.c | 222 ++++++++++++++++++++++++++++++
    4 files changed, 228 insertions(+), 1 deletions(-)
    create mode 100644 arch/arm/mach-picoxcell/time.c

    diff --git a/arch/arm/mach-picoxcell/Makefile b/arch/arm/mach-picoxcell/Makefile
    index 6afe388..493ec0e 100644
    --- a/arch/arm/mach-picoxcell/Makefile
    +++ b/arch/arm/mach-picoxcell/Makefile
    @@ -1 +1,2 @@
    -obj-y := picoxcell_core.o io.o axi2cfg.o
    +obj-y := picoxcell_core.o io.o axi2cfg.o \
    + time.o
    diff --git a/arch/arm/mach-picoxcell/picoxcell_core.c b/arch/arm/mach-picoxcell/picoxcell_core.c
    index a0db3fd..a54d981 100644
    --- a/arch/arm/mach-picoxcell/picoxcell_core.c
    +++ b/arch/arm/mach-picoxcell/picoxcell_core.c
    @@ -97,6 +97,7 @@ static void report_chipinfo(void)
    void __init picoxcell_init_early(void)
    {
    axi2cfg_init();
    + picoxcell_sched_clock_init();
    }

    void __init picoxcell_core_init(void)
    diff --git a/arch/arm/mach-picoxcell/picoxcell_core.h b/arch/arm/mach-picoxcell/picoxcell_core.h
    index 299c512..50cf842 100644
    --- a/arch/arm/mach-picoxcell/picoxcell_core.h
    +++ b/arch/arm/mach-picoxcell/picoxcell_core.h
    @@ -13,10 +13,13 @@
    #define __ASM_ARCH_PICOXCELL_CORE_H__

    struct picoxcell_soc;
    +struct sys_timer;

    extern void picoxcell_init_early(void);
    extern void picoxcell_core_init(void);
    extern void picoxcell_init_irq(void);
    extern void picoxcell_map_io(void);
    +extern struct sys_timer picoxcell_sys_timer;
    +extern void picoxcell_sched_clock_init(void);

    #endif /* __ASM_ARCH_PICOXCELL_CORE_H__ */
    diff --git a/arch/arm/mach-picoxcell/time.c b/arch/arm/mach-picoxcell/time.c
    new file mode 100644
    index 0000000..66fe86d
    --- /dev/null
    +++ b/arch/arm/mach-picoxcell/time.c
    @@ -0,0 +1,222 @@
    +/*
    + * Copyright (c) 2010 Picochip Ltd., Jamie Iles
    + *
    + * This program is free software; you can redistribute it and/or modify
    + * it under the terms of the GNU General Public License version 2 as
    + * published by the Free Software Foundation.
    + *
    + * All enquiries to support@picochip.com
    + */
    +#include <linux/clockchips.h>
    +#include <linux/clocksource.h>
    +#include <linux/cnt32_to_63.h>
    +#include <linux/interrupt.h>
    +#include <linux/irq.h>
    +
    +#include <asm/mach/time.h>
    +#include <asm/sched_clock.h>
    +
    +#include <mach/hardware.h>
    +
    +#include "picoxcell_core.h"
    +#include "soc.h"
    +
    +enum timer_id {
    + TIMER_ID_CLOCKEVENT,
    + TIMER_ID_CLOCKSOURCE,
    + NR_TIMERS,
    +};
    +
    +struct timer_instance {
    + void __iomem *base;
    + struct irqaction irqaction;
    +};
    +
    +/*
    + * We expect to have 2 timers - a freerunning one for the clock source and a
    + * periodic/oneshot one for the clock_event_device.
    + */
    +static struct timer_instance timers[NR_TIMERS];
    +
    +static void timer_set_mode(enum clock_event_mode mode,
    + struct clock_event_device *clk)
    +{
    + struct timer_instance *timer = &timers[TIMER_ID_CLOCKEVENT];
    + unsigned long load_count = DIV_ROUND_UP(CLOCK_TICK_RATE, HZ);
    +
    + switch (mode) {
    + case CLOCK_EVT_MODE_PERIODIC:
    + /*
    + * By default, use the kernel tick rate. The reload value can
    + * be changed with the timer_set_next_event() function.
    + */
    + __raw_writel(load_count,
    + timer->base + TIMER_LOAD_COUNT_REG_OFFSET);
    + __raw_writel(TIMER_ENABLE | TIMER_MODE,
    + timer->base + TIMER_CONTROL_REG_OFFSET);
    + break;
    +
    + case CLOCK_EVT_MODE_ONESHOT:
    + case CLOCK_EVT_MODE_UNUSED:
    + case CLOCK_EVT_MODE_SHUTDOWN:
    + default:
    + __raw_writel(0, timer->base + TIMER_CONTROL_REG_OFFSET);
    + break;
    + }
    +}
    +
    +static int timer_set_next_event(unsigned long evt,
    + struct clock_event_device *clk)
    +{
    + struct timer_instance *timer = &timers[TIMER_ID_CLOCKEVENT];
    +
    + /* Disable the timer, write the new event then enable it. */
    + __raw_writel(0, timer->base + TIMER_CONTROL_REG_OFFSET);
    + __raw_writel(evt, timer->base + TIMER_LOAD_COUNT_REG_OFFSET);
    + __raw_writel(TIMER_ENABLE | TIMER_MODE,
    + timer->base + TIMER_CONTROL_REG_OFFSET);
    +
    + return 0;
    +}
    +
    +static struct clock_event_device clockevent_picoxcell = {
    + .features = CLOCK_EVT_FEAT_PERIODIC |
    + CLOCK_EVT_FEAT_ONESHOT,
    + .set_next_event = timer_set_next_event,
    + .set_mode = timer_set_mode,
    +};
    +
    +static irqreturn_t timer_interrupt(int irq, void *dev_id)
    +{
    + struct timer_instance *timer = &timers[TIMER_ID_CLOCKEVENT];
    +
    + /* Clear the interrupt. */
    + __raw_readl(timer->base + TIMER_EOI_REG_OFFSET);
    +
    + clockevent_picoxcell.event_handler(&clockevent_picoxcell);
    +
    + return IRQ_HANDLED;
    +}
    +
    +#define PICOXCELL_MIN_RANGE 4
    +
    +static void picoxcell_clockevent_init(struct picoxcell_soc *soc)
    +{
    + struct timer_instance *inst = &timers[TIMER_ID_CLOCKEVENT];
    + const struct picoxcell_timer *timer = NULL;
    + int i;
    +
    + for (i = 0; i < soc->nr_timers; ++i)
    + if (soc->timers[i].type == TIMER_TYPE_TIMER) {
    + timer = &soc->timers[i];
    + break;
    + }
    +
    + BUG_ON(!timer);
    +
    + /* Configure the interrupt for this timer. */
    + inst->irqaction.name = timer->name;
    + inst->irqaction.handler = timer_interrupt;
    + inst->irqaction.flags = IRQF_DISABLED | IRQF_TIMER;
    + inst->base = ioremap(timer->base, TIMER_SPACING);
    +
    + clockevent_picoxcell.name = timer->name;
    + clockevents_calc_mult_shift(&clockevent_picoxcell, CLOCK_TICK_RATE,
    + PICOXCELL_MIN_RANGE);
    + clockevent_picoxcell.max_delta_ns =
    + clockevent_delta2ns(0xfffffffe, &clockevent_picoxcell);
    + clockevent_picoxcell.min_delta_ns = 50000;
    + clockevent_picoxcell.cpumask = cpumask_of(0);
    +
    + /* Start with the timer disabled and the interrupt enabled. */
    + __raw_writel(0, inst->base + TIMER_CONTROL_REG_OFFSET);
    + setup_irq(timer->irq, &inst->irqaction);
    +
    + clockevents_register_device(&clockevent_picoxcell);
    +}
    +
    +static cycle_t picoxcell_rtc_get_cycles(struct clocksource *cs)
    +{
    + struct timer_instance *inst = &timers[TIMER_ID_CLOCKSOURCE];
    +
    + return __raw_readl(inst->base + RTCLK_CCV_REG_OFFSET);
    +}
    +
    +static struct clocksource clocksource_picoxcell = {
    + .name = "rtc",
    + .rating = 300,
    + .read = picoxcell_rtc_get_cycles,
    + .mask = CLOCKSOURCE_MASK(32),
    + .flags = CLOCK_SOURCE_IS_CONTINUOUS,
    +};
    +
    +static void picoxcell_clocksource_init(struct picoxcell_soc *soc)
    +{
    + const struct picoxcell_timer *timer = NULL;
    + int i;
    +
    + for (i = 0; i < soc->nr_timers; ++i)
    + if (soc->timers[i].type == TIMER_TYPE_RTC) {
    + timer = &soc->timers[i];
    + break;
    + }
    +
    + BUG_ON(!timer);
    +
    + timers[TIMER_ID_CLOCKSOURCE].base = ioremap(timer->base, SZ_4K);
    +
    + /* The RTC is always running. We don't need to do any initialization. */
    + clocksource_picoxcell.read = picoxcell_rtc_get_cycles;
    + clocksource_register_hz(&clocksource_picoxcell, CLOCK_TICK_RATE);
    +}
    +
    +static void __init picoxcell_timer_init(void)
    +{
    + struct picoxcell_soc *soc = picoxcell_get_soc();
    +
    + picoxcell_clocksource_init(soc);
    + picoxcell_clockevent_init(soc);
    +}
    +
    +struct sys_timer picoxcell_sys_timer = {
    + .init = picoxcell_timer_init,
    +};
    +
    +/*
    + * picoxcell's sched_clock implementation. It has a resolution of 5ns
    + * (200MHz).
    + */
    +static DEFINE_CLOCK_DATA(cd);
    +
    +/*
    + * Constants generated by:
    + * clocks_calc_mult_shift(m, s, 200000000, NSEC_PER_SEC, 0);
    + */
    +#define SC_MULT 2684354560LU
    +#define SC_SHIFT 29
    +
    +unsigned long long notrace sched_clock(void)
    +{
    + u32 cyc = __raw_readl(IO_ADDRESS(PICOXCELL_RTCLK_BASE) +
    + RTCLK_CCV_REG_OFFSET);
    + return cyc_to_fixed_sched_clock(&cd, cyc, (u32)~0, SC_MULT, SC_SHIFT);
    +}
    +
    +static void notrace picoxcell_update_sched_clock(void)
    +{
    + u32 cyc = __raw_readl(IO_ADDRESS(PICOXCELL_RTCLK_BASE) +
    + RTCLK_CCV_REG_OFFSET);
    + update_sched_clock(&cd, cyc, (u32)~0);
    +}
    +
    +void __init picoxcell_sched_clock_init(void)
    +{
    + /*
    + * Reset the RTC. We don't know how long the RTC has been running for
    + * in the bootloader.
    + */
    + __raw_writel(0, IO_ADDRESS(PICOXCELL_RTCLK_BASE +
    + RTCLK_SET_REG_OFFSET));
    + init_fixed_sched_clock(&cd, picoxcell_update_sched_clock, 32,
    + CLOCK_TICK_RATE, SC_MULT, SC_SHIFT);
    +}
    --
    1.7.4


    \
     
     \ /
      Last update: 2011-03-08 16:09    [W:0.039 / U:119.120 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site