2009/10/8 Gilles Chanteperdrix <[email protected]>

> Didenko Sergey wrote:
> > Dear Xenomai Experts,
> >
> > So far I did not progress too much.
> >
> > What I have now is that patched Linux is starting to boot as original
> > kernel and it hangs right after line
> > [42949373.240000] ata1: SATA max UDMA/133 irq 27
> > As you can see after line
> >
> > [42949373.230000] ?^<6>Xenomai: real-time nucleus v2.4.9.1 (Big Bad
> > Moon) loaded.
> >
> > System does not go to timer_interrupt() function any more.
>
> That is expected. Once Xenomai runs, it takes over the timer interrupt.
>
> >
> > What I'm worring about is that the system has 2 timers, one is
> > free-running with disabled interrupts, second one is interrupt-driven
> > clockevent timer.
>
> No problem. Use the clockevent timer for hardware timer, use the
> free-running counter for tsc emulation.
>
> >
> > =========================== Code - Start
> > (...)
> > =========================== Code - End
> >
> > According to HOWTO Xenomai needs free-running counter, in my case it is
> > Timer 0, but all functions are configured to work with Timer 1, which is
> > not free running.
> >
> > What would you suggest to do?
>
> No, the howto does not say that you need a free-running counter. It
> shows the example of a free-running counter and tell you to look at the
> integrator or s3cxxxx code if you want to know how to implement the
> I-pipe support for a decrementer. The reason being that I wrote the
> howto, and I never had to work with a decrementer.
>
>
If you never worked with decrementer, I do not want to use it if there is
any other way...
For example can I:
- Enable interrupts for free-running Timer0?
- Add one more timer with proper functionality for Xenomai (system has 2
more timers which are not initialized) ?

If you really want us to help you should show us the code that you
> added, not the code that we can already find in the linux kernel sources.
>

Of course I really want...

The code I added is under XENOMAI_SUPPORT_DSV definition

file time.c
=========================== Code - Start

#if XENOMAI_SUPPORT_DSV

#ifdef CONFIG_IPIPE
#ifdef CONFIG_NO_IDLE_HZ
#error "dynamic tick timer not yet supported with IPIPE"
#endif /* CONFIG_NO_IDLE_HZ */
#endif /* CONFIG_IPIPE */

#endif

/*
 * Timer block registers.
 */
#define TIMER_CTRL        (TIMER_VIRT_BASE + 0x0000)
#define  TIMER0_EN        0x0001
#define  TIMER0_RELOAD_EN    0x0002
#define  TIMER1_EN        0x0004
#define  TIMER1_RELOAD_EN    0x0008
#define TIMER0_RELOAD        (TIMER_VIRT_BASE + 0x0010)
#define TIMER0_VAL        (TIMER_VIRT_BASE + 0x0014)
#define TIMER1_RELOAD        (TIMER_VIRT_BASE + 0x0018)
#define TIMER1_VAL        (TIMER_VIRT_BASE + 0x001c)


#if XENOMAI_SUPPORT_DSV

#define XEN_PRINT(x) printk(x);
//#define XEN_PRINT(x)

/* Assume that 16 is Ok for MV 88F6290 platfrom - Need to be clarified */
#define MIN_OSCR_DELTA 16

/*Hardware timer IRQ number */
int __ipipe_mach_timerint = IRQ_MV88F6290_BRIDGE;
EXPORT_SYMBOL(__ipipe_mach_timerint);

/*Initialized to 0, it became non zero when the hardware timer is handled by
Xenomai. */
int __ipipe_mach_timerstolen = 0;
EXPORT_SYMBOL(__ipipe_mach_timerstolen);

/*
 * Count of hardware timer ticks between two timer interrupts, same thing as
the LATCH constant.
 */
unsigned int __ipipe_mach_ticks_per_jiffy = LATCH;

static int orion_timer_initialized = 0;

union tsc_reg {
#ifdef __BIG_ENDIAN
    struct {
        unsigned long high;
        unsigned long low;
    };
#else /* __LITTLE_ENDIAN */
    struct {
        unsigned long low;
        unsigned long high;
    };
#endif /* __LITTLE_ENDIAN */
    unsigned long long full;
};


#ifdef CONFIG_SMP
static union tsc_reg orion_tsc[NR_CPUS];

void __ipipe_mach_get_tscinfo(struct __ipipe_tscinfo *info)
{
    info->type = IPIPE_TSC_TYPE_NONE;
}

#else /* !CONFIG_SMP */
static union tsc_reg *orion_tsc;

// export the tsc to user-space

void __ipipe_mach_get_tscinfo(struct __ipipe_tscinfo *info)
{
    XEN_PRINT( "\n===[Break point] __ipipe_mach_get_tscinfo - IN")

    info->type = IPIPE_TSC_TYPE_FREERUNNING;
    info->u.fr.counter = (unsigned *) TIMER1_VAL;
    info->u.fr.mask = 0xffffffff;
    info->u.fr.tsc = &orion_tsc->full;
}
#endif /* !CONFIG_SMP */

/*
 * info->type indicates that the tsc is based on a free-running counter,
 * info->u.fr.counter is set to the PHYSICAL address of the free-running
counter,
 * info->u.fr.mask is a mask indicating which bits in the free-running
counter are valid.
 * info->u.fr.tsc is a pointer to the shared tsc area
 */

/* Acknowledge the hardware timer interrupt at hardware timer level.  */
void __ipipe_mach_acktimer(void)
{
    /*
     * ACK timer interrupt.
     */
    XEN_PRINT("?")

    writel(BRIDGE_INT_TIMER1_CLR, BRIDGE_CAUSE);
}

// Emulates the tsc;
notrace unsigned long long __ipipe_mach_get_tsc(void)
{
    XEN_PRINT("_")

    if (likely(orion_timer_initialized)) {
        union tsc_reg *local_tsc, result;
        unsigned long stamp;

        local_tsc = &orion_tsc[ipipe_processor_id()];

        __asm__ ("ldmia %1, %M0\n":
             "=r"(result.full): "r"(local_tsc), "m"(*local_tsc));

        barrier();

        stamp = readl(TIMER0_VAL);

        if (unlikely(stamp < result.low))
            /* 32 bit counter wrapped, increment high word. */
            result.high++;
        result.low = stamp;

        return result.full;
    }

        return 0;
}
EXPORT_SYMBOL(__ipipe_mach_get_tsc);

/*
 * Program the hardware timer to trig an interrupt in 'delay' hardware timer
ticks.
 */
void __ipipe_mach_set_dec(unsigned long delay)
{
    u32 u;

    XEN_PRINT("-")

    if (delay > MIN_OSCR_DELTA) {
        unsigned long flags;

        local_irq_save(flags);

                              //  OSCR             //OSMR0
        writel(delay + readl(TIMER1_VAL), TIMER1_RELOAD);

        //OIER |= OIER_E0;

        /*
         * Clear and enable clockevent timer interrupt.
         */
        writel(BRIDGE_INT_TIMER1_CLR, BRIDGE_CAUSE);

        u = readl(BRIDGE_MASK);
        u |= BRIDGE_INT_TIMER1;
        writel(u, BRIDGE_MASK);


        local_irq_restore(flags);
    } else
        ipipe_trigger_irq(IRQ_MV88F6290_BRIDGE);

}
EXPORT_SYMBOL(__ipipe_mach_set_dec);

#endif /* XENOMAI_SUPPORT_DSV */


/*
 * Clocksource handling.
 */
static cycle_t orion_clksrc_read(void)
{
    return 0xffffffff - readl(TIMER0_VAL);
}

static struct clocksource orion_clksrc = {
    .name        = "orion_clocksource",
    .shift        = 20,
    .rating        = 300,
    .read        = orion_clksrc_read,
    .mask        = CLOCKSOURCE_MASK(32),
    .flags        = CLOCK_SOURCE_IS_CONTINUOUS,
};


/*
 * Clockevent handling.
 */
static int
orion_clkevt_next_event(unsigned long delta, struct clock_event_device *dev)
{
    unsigned long flags;
    u32 u;

    XEN_PRINT( "\n===[Break point] orion_clkevt_next_event - IN")

    if (delta == 0)
        return -ETIME;

    local_irq_save(flags);

    /*
     * Clear and enable clockevent timer interrupt.
     */
    writel(BRIDGE_INT_TIMER1_CLR, BRIDGE_CAUSE);

    u = readl(BRIDGE_MASK);
    u |= BRIDGE_INT_TIMER1;
    writel(u, BRIDGE_MASK);

    /*
     * Setup new clockevent timer value.
     */
    writel(delta, TIMER1_VAL);

    /*
     * Enable the timer.
     */
    u = readl(TIMER_CTRL);
    u = (u & ~TIMER1_RELOAD_EN) | TIMER1_EN;
    writel(u, TIMER_CTRL);

    local_irq_restore(flags);

    return 0;
}

static void
orion_clkevt_mode(enum clock_event_mode mode, struct clock_event_device
*dev)
{
    unsigned long flags;
    u32 u;

    XEN_PRINT( "\n===[Break point] orion_clkevt_mode - IN")

    local_irq_save(flags);
    if (mode == CLOCK_EVT_MODE_PERIODIC) {
        /*
         * Setup timer to fire at 1/HZ intervals.
         */
        writel(__ipipe_mach_ticks_per_jiffy - 1, TIMER1_RELOAD);
        writel(__ipipe_mach_ticks_per_jiffy - 1, TIMER1_VAL);

        /*
         * Enable timer interrupt.
         */
        u = readl(BRIDGE_MASK);
        writel(u | BRIDGE_INT_TIMER1, BRIDGE_MASK);

        /*
         * Enable timer.
         */
        u = readl(TIMER_CTRL);
        writel(u | TIMER1_EN | TIMER1_RELOAD_EN, TIMER_CTRL);
    } else {
        /*
         * Disable timer.
         */
        u = readl(TIMER_CTRL);
        writel(u & ~TIMER1_EN, TIMER_CTRL);

        /*
         * Disable timer interrupt.
         */
        u = readl(BRIDGE_MASK);
        writel(u & ~BRIDGE_INT_TIMER1, BRIDGE_MASK);

        /*
         * ACK pending timer interrupt.
         */
        writel(BRIDGE_INT_TIMER1_CLR, BRIDGE_CAUSE);

    }
    local_irq_restore(flags);
}

static struct clock_event_device orion_clkevt = {
    .name        = "orion_tick",
    .features        = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC,
    .shift        = 32,
    .rating        = 300,
    .set_next_event    = orion_clkevt_next_event,
    .set_mode    = orion_clkevt_mode,
};

static irqreturn_t orion_timer_interrupt(int irq, void *dev_id)
{
    /*
     * ACK timer interrupt
     */
    writel(BRIDGE_INT_TIMER1_CLR, BRIDGE_CAUSE);

    XEN_PRINT( "^")

    /*
     * Call event handler.
     */
    orion_clkevt.event_handler(&orion_clkevt);

    return IRQ_HANDLED;
}

static struct irqaction orion_timer_irq = {
    .name        = "orion_tick",
    .flags        = IRQF_DISABLED | IRQF_TIMER,
    .handler    = orion_timer_interrupt
};

void __init orion_time_init(unsigned int irq, unsigned int tclk)
{
    u32 u;

    __ipipe_mach_ticks_per_jiffy = (tclk + HZ/2) / HZ;

    XEN_PRINT( "\n===[Break point] orion_time_init - IN")

    /*
     * Setup free-running clocksource timer (interrupts disabled)
     */
    writel(0xffffffff, TIMER0_VAL);
    writel(0xffffffff, TIMER0_RELOAD);
    u = readl(BRIDGE_MASK);
    writel(u & ~BRIDGE_INT_TIMER0, BRIDGE_MASK);
    u = readl(TIMER_CTRL);
    writel(u | TIMER0_EN | TIMER0_RELOAD_EN, TIMER_CTRL);
    orion_clksrc.mult = clocksource_hz2mult(tclk, orion_clksrc.shift);
    clocksource_register(&orion_clksrc);

    /*
     * Setup clockevent timer (interrupt-driven)
     */
    setup_irq(irq, &orion_timer_irq);
    orion_clkevt.mult = div_sc(tclk, NSEC_PER_SEC, orion_clkevt.shift);
    orion_clkevt.max_delta_ns = clockevent_delta2ns(0xfffffffe,
&orion_clkevt);
    orion_clkevt.min_delta_ns = clockevent_delta2ns(1, &orion_clkevt);
    orion_clkevt.cpumask = cpumask_of(0);
    clockevents_register_device(&orion_clkevt);

#if XENOMAI_SUPPORT_DSV

#ifdef CONFIG_IPIPE
#ifndef CONFIG_SMP
        orion_tsc = (union tsc_reg *) __ipipe_tsc_area;
        barrier();
#endif /* CONFIG_SMP */
        orion_timer_initialized = 1;
#endif /* CONFIG_IPIPE */

#endif /* XENOMAI_SUPPORT_DSV */


}

#if XENOMAI_SUPPORT_DSV

#ifdef CONFIG_IPIPE
int __ipipe_check_tickdev(const char *devname)
{
    return !strcmp(devname, orion_clkevt.name);

}
#endif /* CONFIG_IPIPE */

void __ipipe_mach_release_timer(void)
{
    XEN_PRINT( "\n===[Break point] __ipipe_mach_release_timer - IN")

        orion_clkevt_mode(orion_clkevt.mode, &orion_clkevt);
        if (orion_clkevt.mode == CLOCK_EVT_MODE_ONESHOT)
                orion_clkevt_next_event(LATCH, &orion_clkevt);
}
EXPORT_SYMBOL(__ipipe_mach_release_timer);

#endif

=========================== Code - End

BTW, how can I know how much the MIN_OSCR_DELTA value should be for MV
88F6290 platfrom?
_______________________________________________
Xenomai-help mailing list
[email protected]
https://mail.gna.org/listinfo/xenomai-help

Reply via email to