summaryrefslogtreecommitdiff
path: root/polux/linux-2.6.10/arch/arm/mach-mse500/time.c
blob: 0fa9a2347ee23fa135416aecebaab02727453656 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/*
 * arch/arm/mach-mse500/time.c
 *
 * (C) Copyright 2007 Scaleo Chip
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/time.h>

#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/mach/time.h>
#include <asm/system.h>
#include <asm/arch/system.h>
#include <asm/arch/wdt.h>

#include <asm/arch/irqs.h>
#include <asm/arch/ips/timer.h>

/**
 * Function:     mse500_gettimeoffset
 * Parameters:   void
 * Purpose:      Returns number of microseconds since last timer interrupt.
 * Return Value: unsigned long
 */
static unsigned long mse500_gettimeoffset(void)
{
    volatile uint32_t timer_value;
    volatile uint32_t irq_pending;
    unsigned long us;

    //IRQs are disabled, check raw status to check if we've been interrupted
    do
    {
        irq_pending = TIMER1INTSTAT_1_VA;
        timer_value = TIMER1CURRENTVAL_1_VA;
    } while(irq_pending != TIMER1INTSTAT_1_VA); //Check if we've not been interrupted in the loop

    us = ((TIMER1LOADCOUNT_1_VA - timer_value) * 1000) / (TIMER_CLK / 1000);

    if(irq_pending)
    {
        //We're in a stable state where an irq is pending:
        //seems like timer interrupts are masked or disabled
        us += USEC_PER_SEC/HZ;
    }
    return us;
} /* mse500_gettimeoffset */

/**
 * Function:     mse500_timer_interrupt
 * Parameters:   int irq, void *dev_id, struct pt_regs *regs
 * Purpose:      IRQ handler for the timer.          
 * Return Value: irqreturn_t
 */
static irqreturn_t mse500_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
    volatile uint32_t dummy;

    //Check if it's our timer
    if(TIMER1INTSTAT_1_VA)
    {
        write_seqlock(&xtime_lock);
        //Clear interrupt
        dummy = TIMER1EOI_1_VA;
        //Update the Linux tick
        timer_tick(regs);
        write_sequnlock(&xtime_lock);

        if (!mse500_wdt_user_mode)
            mse500_wdt_refresh();

        return IRQ_HANDLED;
    }
    else
        return IRQ_NONE;
} /* mse500_timer_interrupt */


static struct irqaction mse500_timer_irq = {
	.name		= "MSE500 Timer Tick",
	.flags		= SA_INTERRUPT,
	.handler	= mse500_timer_interrupt
}; /* mse500_timer_irq */

/**
 * Function:     mse500_timer_init
 * Parameters:   void
 * Purpose:      Set up timer interrupt.        
 * Return Value: void
 */
void __init mse500_timer_init(void)
{
    uint32_t dummy;

    //Restart Watchdog that is refreshed under timer interrupt
    mse500_wdt_restart(2); //2 seconds for timeout

    /* Disable timer 1 */
    TIMER1CONTROLREG_1_VA = ~(TIMER_ENABLE_MASK | TIMER_MODE_MASK) & TIMER_INTMASK_MASK;

    /* Clear timer1 interrupt */
    dummy = TIMER1EOI_1_VA;

    /* Configure timer1 for tick timer (auto-reload mode with IT) */
    TIMER1CONTROLREG_1_VA = ~(TIMER_ENABLE_MASK | TIMER_INTMASK_MASK) & TIMER_MODE_MASK;

    /* Load the starting value for a 10ms tick timer */
    TIMER1LOADCOUNT_1_VA = (TIMER_CLK / HZ);

    /* Setup irq */
    setup_irq(INT_TIMER_1, &mse500_timer_irq);

    /* Enable the timer1 */
    TIMER1CONTROLREG_1_VA |= TIMER_ENABLE_MASK;
} /* mse500_timer_init */

struct sys_timer mse500_timer = {
	.init		= mse500_timer_init,
	.offset		= mse500_gettimeoffset,
}; /* mse500_timer */