summaryrefslogtreecommitdiffstats
path: root/firmware/target/arm/s3c2440/gigabeat-fx/timer-meg-fx.c
blob: 4565f79751e03e29c3658d2bf4c43ca224a972db (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
/***************************************************************************
*             __________               __   ___.
*   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
*   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
*   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
*   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
*                     \/            \/     \/    \/            \/
* $Id$
*
* Copyright (C) 2007 by Michael Sevakis
*
* 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "config.h"
#include "cpu.h"
#include "system.h"
#include "timer.h"
#include "logf.h"

/* GPB0/TOUT0 should already have been configured as output so that pin
   should not be a functional pin and TIMER0 output unseen there */
void TIMER0(void)
{
    if (pfn_timer != NULL)
        pfn_timer();

    SRCPND = TIMER0_MASK;
    INTPND = TIMER0_MASK;
}

static void stop_timer(void)
{
    /* mask interrupt */
    INTMSK |= TIMER0_MASK;

    /* stop any running TIMER0 */
    TCON &= ~(1 << 0);

    /* clear pending */
    SRCPND = TIMER0_MASK;
    INTPND = TIMER0_MASK;
}

bool timer_set(long cycles, bool start)
{
    bool retval = false;

    /* Find the minimum factor that puts the counter in range 1-65535 */
    unsigned int prescaler = (cycles + 65534) / 65535;

    /* Maximum divider setting is x / 256 / 16 = x / 4096 - min divider
       is x / 2 however */
    if (prescaler <= 2048)
    {
        int oldlevel;
        unsigned int divider;

        if (start && pfn_unregister != NULL)
        {
            pfn_unregister();
            pfn_unregister = NULL;
        }

        oldlevel = disable_irq_save();

        TCMPB0 = 0;
        TCNTB0 = (unsigned int)cycles / prescaler;

        /* Max prescale is 255+1 */
        for (divider = 0; prescaler > 256; prescaler >>= 1, divider++);

        TCFG0 = (TCFG0 & ~0xff) | (prescaler - 1);
        TCFG1 = (TCFG1 & ~0xf) | divider;

        restore_irq(oldlevel);

        retval = true;
    }

    return retval;
}

bool timer_start(void)
{
    bool retval = true;

    int oldstatus = disable_interrupt_save(IRQ_FIQ_STATUS);

    stop_timer();

    /* neurosis - make sure something didn't set GPB0 to TOUT0 */
    if ((GPBCON & 0x3) != 0x2)
    {
        /* manual update: on (to reset count) */
        TCON |= (1 << 1);
        /* dead zone: off, inverter: off, manual off */
        TCON &= ~((1 << 4) | (1 << 2) | (1 << 1));
        /* interval mode (auto reload): on */
        TCON |= (1 << 3);
        /* start timer */
        TCON |= (1 << 0);
        /* unmask interrupt */
        INTMSK &= ~TIMER0_MASK;
    }

    if (!(TCON & (1 << 0)))
    {
        /* timer could not be started due to config error */
        logf("Timer error: GPB0 set to TOUT0");
        retval = false;
    }

    restore_interrupt(oldstatus);

    return retval;
}

void timer_stop(void)
{
    int oldstatus = disable_interrupt_save(IRQ_FIQ_STATUS);
    stop_timer();
    restore_interrupt(oldstatus);
}