summaryrefslogtreecommitdiff
path: root/firmware/target/arm/as3525/kernel-as3525.c
blob: ff489a86e87e201d5ef5f2b83109d8927bdbab6b (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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright © 2008 Rafaël Carré
 *
 * 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 "system.h"
#include "kernel.h"
#include "panic.h"
#include "timer.h"

#if INCREASED_SCROLLWHEEL_POLLING
#include "button-target.h"
/* The scrollwheel is polled every 5 ms (the tick tasks only every 10) */
static int poll_scrollwheel = 0;

static inline void do_scrollwheel(void)
{
    if (!poll_scrollwheel)
        call_tick_tasks();      /* Run through the list of tick tasks
                                 * (that includes reading the scrollwheel) */
    else
    {
        if (!button_hold())
            get_scrollwheel(); /* Read the scrollwheel */
    }

    poll_scrollwheel ^= 1;
}
#else
static inline void do_scrollwheel(void)
{
    call_tick_tasks();      /* Run through the list of tick tasks */
}
#endif

#if defined(SANSA_C200V2)
#include "backlight-target.h"

static int timer2_cycles_per_tick = 0;
static int timer2_cycles_pwmon = 0;
static int timer2_cycles_pwmoff = 0;
static int timer2_pwm_state = 0;
static int timer2_pwm_on = 0;

void _set_timer2_pwm_ratio(int ratio)
{
    int cycles = timer2_cycles_per_tick;

    /*
     * Rather arbitrary limits, but since the CPU
     * needs some to time in the interrupt handler
     * there sure is some limit.
     * More specifically, if the cycles needed to do
     * the pwm handling are more than the reloaded counter needs
     * to reach 0 again it will reload to the old value most
     * likely leading to a (slight) slowdown in tick rate.
     */

    if (ratio < 10) {
        /*
         * Permanent off, reduce interrupt rate to save power
         */
        TIMER2_BGLOAD = cycles;
        timer2_pwm_on = 0;
        _backlight_pwm(0);
        return;
    }

    if (ratio > 990) {
        /*
         * Permanent on, reduce interrupt rate to save power
         */
        TIMER2_BGLOAD = cycles;
        timer2_pwm_on = 0;
        _backlight_pwm(1);
        return;
    }

    timer2_cycles_pwmon = cycles*ratio/1000;
    timer2_cycles_pwmoff = cycles*(1000-ratio)/1000;

    if (timer2_pwm_on == 0) {
        timer2_pwm_state = 0;
        timer2_pwm_on = 1;
        TIMER2_BGLOAD = timer2_cycles_pwmoff;
    }
}

static void set_timer2_cycles_per_tick(int cycles)
{
    timer2_cycles_per_tick = cycles;
}

static inline void do_sw_pwm(void)
{
    if (!timer2_pwm_on) {
        do_scrollwheel();  /* Handle scrollwheel and tick tasks */
        TIMER2_INTCLR = 0;  /* clear interrupt */
        return;
    }

    timer2_pwm_state ^= 1;
    if (timer2_pwm_state) {
        TIMER2_BGLOAD = timer2_cycles_pwmoff;
        _backlight_pwm(1);
        /*
         * Always do scrollwheel and tick tasks during the longer cycle for safety,
         * since the short cycle can be quite short.
         * (minimum: 1us if ratio is 10 or 990 or 0.5us with scrollwheel,
         *  or just about 6000 clock cycles at 60MHz)
         */
        if (timer2_cycles_pwmon > timer2_cycles_pwmoff)
            do_scrollwheel();  /* Handle scrollwheel and tick tasks */
    } else {
        TIMER2_BGLOAD = timer2_cycles_pwmon;
        _backlight_pwm(0);
        if (!(timer2_cycles_pwmon > timer2_cycles_pwmoff))
            do_scrollwheel();  /* Handle scrollwheel and tick tasks */
    }

    TIMER2_INTCLR = 0;  /* clear interrupt */
}
#else
static inline void do_sw_pwm(void)
{
    do_scrollwheel();  /* Handle scrollwheel and tick tasks */
}

static void set_timer2_cycles_per_tick(int cycles)
{
    (void)cycles;
}
#endif


void INT_TIMER2(void)
{
    /*
     * Timer is stacked as follows:
     * Lowest layer: Software PWM (if configured)
     *   Alternates timer2 reload value to implement
     *   software pwm at 100Hz (no scrollwheel)
     *   or 200Hz (scrollwheel) with variable pulse width 1% to 99%
     * Middle layer: Scrollwheel handling (if configured, 200Hz)
     *   Alternate between polling scrollwheel and running tick
     *   tasks (includes scrollwheel polling).
     * Top layer: Run tick tasks at 100Hz
     */
    do_sw_pwm();

    TIMER2_INTCLR = 0;  /* clear interrupt */
}

void tick_start(unsigned int interval_in_ms)
{
    int cycles = KERNEL_TIMER_FREQ / 1000 * interval_in_ms;

    CGU_PERI |= CGU_TIMER2_CLOCK_ENABLE;    /* enable peripheral */
    VIC_INT_ENABLE = INTERRUPT_TIMER2;     /* enable interrupt */

    set_timer2_cycles_per_tick(cycles);
    TIMER2_LOAD = TIMER2_BGLOAD = cycles;   /* timer period */

    /* /!\ bit 4 (reserved) must not be modified
     * periodic mode, interrupt enabled, no prescale, 32 bits counter */
    TIMER2_CONTROL = (TIMER2_CONTROL & (1<<4)) |
                     TIMER_ENABLE |
                     TIMER_PERIODIC |
                     TIMER_INT_ENABLE |
                     TIMER_32_BIT;
}