1/* Copyright (C) 2007-2008 The Android Open Source Project
2**
3** This software is licensed under the terms of the GNU General Public
4** License version 2, as published by the Free Software Foundation, and
5** may be copied, distributed, and modified under those terms.
6**
7** This program is distributed in the hope that it will be useful,
8** but WITHOUT ANY WARRANTY; without even the implied warranty of
9** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10** GNU General Public License for more details.
11*/
12#include "cpu.h"
13#include "migration/qemu-file.h"
14#include "hw/android/goldfish/device.h"
15#include "hw/hw.h"
16#include "hw/power_supply.h"
17
18
19enum {
20	/* status register */
21	BATTERY_INT_STATUS	    = 0x00,
22	/* set this to enable IRQ */
23	BATTERY_INT_ENABLE	    = 0x04,
24
25	BATTERY_AC_ONLINE       = 0x08,
26	BATTERY_STATUS          = 0x0C,
27	BATTERY_HEALTH          = 0x10,
28	BATTERY_PRESENT         = 0x14,
29	BATTERY_CAPACITY        = 0x18,
30
31	BATTERY_STATUS_CHANGED	= 1U << 0,
32	AC_STATUS_CHANGED   	= 1U << 1,
33	BATTERY_INT_MASK        = BATTERY_STATUS_CHANGED | AC_STATUS_CHANGED,
34};
35
36
37struct goldfish_battery_state {
38    struct goldfish_device dev;
39    // IRQs
40    uint32_t int_status;
41    // irq enable mask for int_status
42    uint32_t int_enable;
43
44    int ac_online;
45    int status;
46    int health;
47    int present;
48    int capacity;
49
50    // the fields below are part of the device configuration
51    // and don't need to be saved to / restored from snapshots.
52    int hw_has_battery;
53};
54
55/* update this each time you update the battery_state struct */
56#define  BATTERY_STATE_SAVE_VERSION  1
57
58#define  QFIELD_STRUCT  struct goldfish_battery_state
59QFIELD_BEGIN(goldfish_battery_fields)
60    QFIELD_INT32(int_status),
61    QFIELD_INT32(int_enable),
62    QFIELD_INT32(ac_online),
63    QFIELD_INT32(status),
64    QFIELD_INT32(health),
65    QFIELD_INT32(present),
66    QFIELD_INT32(capacity),
67QFIELD_END
68
69static void  goldfish_battery_save(QEMUFile*  f, void* opaque)
70{
71    struct goldfish_battery_state*  s = opaque;
72
73    qemu_put_struct(f, goldfish_battery_fields, s);
74}
75
76static int   goldfish_battery_load(QEMUFile*  f, void*  opaque, int  version_id)
77{
78    struct goldfish_battery_state*  s = opaque;
79
80    if (version_id != BATTERY_STATE_SAVE_VERSION)
81        return -1;
82
83    return qemu_get_struct(f, goldfish_battery_fields, s);
84}
85
86static struct goldfish_battery_state *battery_state;
87
88static uint32_t goldfish_battery_read(void *opaque, hwaddr offset)
89{
90    uint32_t ret;
91    struct goldfish_battery_state *s = opaque;
92
93    switch(offset) {
94        case BATTERY_INT_STATUS:
95            // return current buffer status flags
96            ret = s->int_status & s->int_enable;
97            if (ret) {
98                goldfish_device_set_irq(&s->dev, 0, 0);
99                s->int_status = 0;
100            }
101            return ret;
102
103		case BATTERY_INT_ENABLE:
104		    return s->int_enable;
105		case BATTERY_AC_ONLINE:
106		    return s->ac_online;
107		case BATTERY_STATUS:
108		    return s->status;
109		case BATTERY_HEALTH:
110		    return s->health;
111		case BATTERY_PRESENT:
112		    return s->present;
113		case BATTERY_CAPACITY:
114		    return s->capacity;
115
116        default:
117            cpu_abort (cpu_single_env, "goldfish_battery_read: Bad offset %x\n", offset);
118            return 0;
119    }
120}
121
122static void goldfish_battery_write(void *opaque, hwaddr offset, uint32_t val)
123{
124    struct goldfish_battery_state *s = opaque;
125
126    switch(offset) {
127        case BATTERY_INT_ENABLE:
128            /* enable interrupts */
129            s->int_enable = val;
130//            s->int_status = (AUDIO_INT_WRITE_BUFFER_1_EMPTY | AUDIO_INT_WRITE_BUFFER_2_EMPTY);
131//            goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
132            break;
133
134        default:
135            cpu_abort (cpu_single_env, "goldfish_audio_write: Bad offset %x\n", offset);
136    }
137}
138
139static CPUReadMemoryFunc *goldfish_battery_readfn[] = {
140    goldfish_battery_read,
141    goldfish_battery_read,
142    goldfish_battery_read
143};
144
145
146static CPUWriteMemoryFunc *goldfish_battery_writefn[] = {
147    goldfish_battery_write,
148    goldfish_battery_write,
149    goldfish_battery_write
150};
151
152void goldfish_battery_init(int has_battery)
153{
154    struct goldfish_battery_state *s;
155
156    s = (struct goldfish_battery_state *)g_malloc0(sizeof(*s));
157    s->dev.name = "goldfish-battery";
158    s->dev.base = 0;    // will be allocated dynamically
159    s->dev.size = 0x1000;
160    s->dev.irq_count = 1;
161
162    // default values for the battery
163    s->ac_online = 1;
164    s->hw_has_battery = has_battery;
165    if (has_battery) {
166        s->status = POWER_SUPPLY_STATUS_CHARGING;
167        s->health = POWER_SUPPLY_HEALTH_GOOD;
168        s->present = 1;     // battery is present
169        s->capacity = 50;   // 50% charged
170    } else {
171        s->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
172        s->health = POWER_SUPPLY_HEALTH_DEAD;
173        s->present = 0;
174        s->capacity = 0;
175    }
176
177    battery_state = s;
178
179    goldfish_device_add(&s->dev, goldfish_battery_readfn, goldfish_battery_writefn, s);
180
181    register_savevm(NULL,
182                    "battery_state",
183                    0,
184                    BATTERY_STATE_SAVE_VERSION,
185                    goldfish_battery_save,
186                    goldfish_battery_load,
187                    s);
188}
189
190void goldfish_battery_set_prop(int ac, int property, int value)
191{
192    int new_status = (ac ? AC_STATUS_CHANGED : BATTERY_STATUS_CHANGED);
193
194    if (!battery_state || !battery_state->hw_has_battery)
195        return;
196
197    if (ac) {
198        switch (property) {
199            case POWER_SUPPLY_PROP_ONLINE:
200                battery_state->ac_online = value;
201                break;
202        }
203    } else {
204         switch (property) {
205            case POWER_SUPPLY_PROP_STATUS:
206                battery_state->status = value;
207                break;
208            case POWER_SUPPLY_PROP_HEALTH:
209                battery_state->health = value;
210                break;
211            case POWER_SUPPLY_PROP_PRESENT:
212                battery_state->present = value;
213                break;
214            case POWER_SUPPLY_PROP_CAPACITY:
215                battery_state->capacity = value;
216                break;
217        }
218    }
219
220    if (new_status != battery_state->int_status) {
221        battery_state->int_status |= new_status;
222        goldfish_device_set_irq(&battery_state->dev, 0, (battery_state->int_status & battery_state->int_enable));
223    }
224}
225
226void goldfish_battery_display(void (* callback)(void *data, const char* string), void *data)
227{
228    char          buffer[100];
229    const char*   value;
230
231    // Note: obviously, if there is no battery, the AC must always be on.
232    snprintf(buffer, sizeof buffer, "AC: %s\r\n",
233             (battery_state->ac_online) ? "online" : "offline");
234    callback(data, buffer);
235
236    switch (battery_state->status) {
237        case POWER_SUPPLY_STATUS_CHARGING:
238            value = "Charging";
239            break;
240        case POWER_SUPPLY_STATUS_DISCHARGING:
241            value = "Discharging";
242            break;
243        case POWER_SUPPLY_STATUS_NOT_CHARGING:
244            value = "Not charging";
245            break;
246        case POWER_SUPPLY_STATUS_FULL:
247            value = "Full";
248            break;
249        default:
250            value = "Unknown";
251    }
252    snprintf(buffer, sizeof buffer, "status: %s\r\n", value);
253    callback(data, buffer);
254
255    switch (battery_state->health) {
256        case POWER_SUPPLY_HEALTH_GOOD:
257            value = "Good";
258            break;
259        case POWER_SUPPLY_HEALTH_OVERHEAT:
260            value = "Overhead";
261            break;
262        case POWER_SUPPLY_HEALTH_DEAD:
263            value = "Dead";
264            break;
265        case POWER_SUPPLY_HEALTH_OVERVOLTAGE:
266            value = "Overvoltage";
267            break;
268        case POWER_SUPPLY_HEALTH_UNSPEC_FAILURE:
269            value = "Unspecified failure";
270            break;
271        default:
272            value = "Unknown";
273    }
274    snprintf(buffer, sizeof buffer, "health: %s\r\n", value);
275    callback(data, buffer);
276
277    snprintf(buffer, sizeof buffer, "present: %s\r\n",
278             (battery_state->present) ? "true" : "false");
279    callback(data, buffer);
280
281    snprintf(buffer, sizeof buffer, "capacity: %d\r\n", battery_state->capacity);
282    callback(data, buffer);
283}
284