summaryrefslogtreecommitdiffstats
path: root/firmware/target/arm/powermgmt-ascodec.c
blob: 1eaaf6ab30c9aee9f08bbc1284f811bb272fd341 (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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2009 by Michael Sevakis
 * Copyright (C) 2008 by Bertrik Sikken
 *
 * 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 "thread.h"
#include "as3514.h"
#include "ascodec.h"
#include "adc.h"
#include "powermgmt.h"
#include "power.h"
#include "usb-target.h"
#include "usb.h"

/*===========================================================================
 * These parameters may be defined per target:
 * 
 *   BATT_FULL_VOLTAGE - Upon connect a charge cycle begins if the reading is
 *                       lower than this value (millivolts).
 *
 * BATT_VAUTO_RECHARGE - While left plugged after cycle completion, the
 *                       charger restarts automatically if the reading drops
 *                       below this value (millivolts). Must be less than 
 *                       BATT_FULL_VOLTAGE.
 *
 *         ADC_BATTERY - ADC channel from which to read the battery voltage
 *
 *          BATT_CHG_V - Charger voltage regulation setting (as3514 regval)
 *
 *          BATT_CHG_I - Charger current regulation setting (as3514 regval)
 *
 * CHARGER_TOTAL_TIMER - Maximum allowed charging time (1/2-second steps)
 *===========================================================================
 */

/* This code assumes USB power input is not distinguishable from main
 * power and charger connect cannot wait for USB configuration before
 * considering USB charging available. Where they are distinguishable,
 * things get more complicated. */
static bool charger_close = false;  /* Shutting down? */
static int charger_total_timer = 0; /* Timeout in algorithm steps */

/* Current battery threshold for (re)charge:
 * First plugged = BATT_FULL_VOLTAGE
 * After charge cycle or non-start = BATT_VAUTO_RECHARGE
 */
static unsigned int batt_threshold = 0;

/* ADC should read 0x3ff=5.12V */
/* full-scale ADC readout (2^10) in millivolt */

/* Returns battery voltage from ADC [millivolts] */
unsigned int battery_adc_voltage(void)
{
    return (adc_read(ADC_BATTERY) * 5125 + 512) >> 10;
}

/* Returns true if the unit is charging the batteries. */
bool charging_state(void)
{
    return charge_state == CHARGING;
}

/* Reset the battery filter to a new voltage */
static void battery_voltage_sync(void)
{
    int i;
    unsigned int mv;

    for (i = 0, mv = 0; i < 5; i++)
        mv += battery_adc_voltage();

    reset_battery_filter(mv / 5);
}

/* Disable charger and minimize all settings. Reset timers, etc. */
static void disable_charger(void)
{
    ascodec_write(AS3514_IRQ_ENRD0, 0);
    ascodec_write(AS3514_CHARGER,
                  TMPSUP_OFF | CHG_I_50MA | CHG_V_3_90V | CHG_OFF);

    if (charge_state > DISCHARGING)
        charge_state = DISCHARGING; /* Not an error state already */

    charger_total_timer = 0;
    battery_voltage_sync();
}

/* Enable charger with specified settings. Start timers, etc. */
static void enable_charger(void)
{
    ascodec_write(AS3514_CHARGER, BATT_CHG_I | BATT_CHG_V);
    /* Watch for end of charge. Temperature supervision is handled in
     * hardware. Charger status can be read and has no interrupt enable. */
    ascodec_write(AS3514_IRQ_ENRD0, CHG_ENDOFCH);

    sleep(HZ/10); /* Allow charger turn-on time (it could be gradual). */

    ascodec_read(AS3514_IRQ_ENRD0); /* Clear out interrupts (important!) */

    charge_state = CHARGING;
    charger_total_timer = CHARGER_TOTAL_TIMER;
    battery_voltage_sync();
}

void powermgmt_init_target(void)
{
    /* Everything CHARGER, OFF! */
    ascodec_write(AS3514_IRQ_ENRD0, 0);
    ascodec_write(AS3514_CHARGER,
                  TMPSUP_OFF | CHG_I_50MA | CHG_V_3_90V | CHG_OFF);
}

static inline void charger_plugged(void)
{
    batt_threshold = BATT_FULL_VOLTAGE; /* Start with topped value. */
    battery_voltage_sync();
}

static inline void charger_control(void)
{
    switch (charge_state)
    {
    case DISCHARGING:
    {
        unsigned int millivolts;
        unsigned int thresh = batt_threshold;

        if (BATT_FULL_VOLTAGE == thresh)
        {
            /* Wait for CHG_status to be indicated. */
            if ((ascodec_read(AS3514_IRQ_ENRD0) & CHG_STATUS) == 0)
                break;

            batt_threshold = BATT_VAUTO_RECHARGE;
        }

        millivolts = battery_voltage();

        if (millivolts <= thresh)
            enable_charger();
        break;
        } /* DISCHARGING: */

    case CHARGING:
    {
        if ((ascodec_read(AS3514_IRQ_ENRD0) & CHG_ENDOFCH) == 0)
        {
            if (--charger_total_timer > 0)
                break;

            /* Timer ran out - require replug. */
            charge_state = CHARGE_STATE_ERROR;
        }
        /* else end of charge */

        disable_charger();
        break;
        } /* CHARGING: */

    default:
        /* DISABLED, ERROR */
        break;
    }
}

static inline void charger_unplugged(void)
{
    disable_charger();
    if (charge_state >= CHARGE_STATE_ERROR)
        charge_state = DISCHARGING; /* Reset error */
}

/* Main charging algorithm - called from powermgmt.c */
void charging_algorithm_step(void)
{
    switch (charger_input_state)
    {
    case NO_CHARGER:
        /* Nothing to do */
        break;

    case CHARGER_PLUGGED:
        charger_plugged();
        break;

    case CHARGER:
        charger_control();
        break;

    case CHARGER_UNPLUGGED:
        charger_unplugged();
        break;
    }

    if (charger_close)
    {
        /* Disable further charging and ack. */
        charge_state = CHARGE_STATE_DISABLED;
        disable_charger();
        charger_close = false;
    }
}

/* Disable the charger and prepare for poweroff - called off-thread so we
 * signal the charging thread to prepare to quit. */
void charging_algorithm_close(void)
{
    charger_close = true;

    /* Power management thread will set it false again. */
    while (charger_close)
        sleep(HZ/10);
}