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