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 "qemu_file.h"
13#include "goldfish_device.h"
14#include "power_supply.h"
15
16
17enum {
18	/* status register */
19	BATTERY_INT_STATUS	    = 0x00,
20	/* set this to enable IRQ */
21	BATTERY_INT_ENABLE	    = 0x04,
22
23	BATTERY_AC_ONLINE       = 0x08,
24	BATTERY_STATUS          = 0x0C,
25	BATTERY_HEALTH          = 0x10,
26	BATTERY_PRESENT         = 0x14,
27	BATTERY_CAPACITY        = 0x18,
28
29	BATTERY_STATUS_CHANGED	= 1U << 0,
30	AC_STATUS_CHANGED   	= 1U << 1,
31	BATTERY_INT_MASK        = BATTERY_STATUS_CHANGED | AC_STATUS_CHANGED,
32};
33
34
35struct goldfish_battery_state {
36    struct goldfish_device dev;
37    // IRQs
38    uint32_t int_status;
39    // irq enable mask for int_status
40    uint32_t int_enable;
41
42    int ac_online;
43    int status;
44    int health;
45    int present;
46    int capacity;
47};
48
49/* update this each time you update the battery_state struct */
50#define  BATTERY_STATE_SAVE_VERSION  1
51
52#define  QFIELD_STRUCT  struct goldfish_battery_state
53QFIELD_BEGIN(goldfish_battery_fields)
54    QFIELD_INT32(int_status),
55    QFIELD_INT32(int_enable),
56    QFIELD_INT32(ac_online),
57    QFIELD_INT32(status),
58    QFIELD_INT32(health),
59    QFIELD_INT32(present),
60    QFIELD_INT32(capacity),
61QFIELD_END
62
63static void  goldfish_battery_save(QEMUFile*  f, void* opaque)
64{
65    struct goldfish_battery_state*  s = opaque;
66
67    qemu_put_struct(f, goldfish_battery_fields, s);
68}
69
70static int   goldfish_battery_load(QEMUFile*  f, void*  opaque, int  version_id)
71{
72    struct goldfish_battery_state*  s = opaque;
73
74    if (version_id != BATTERY_STATE_SAVE_VERSION)
75        return -1;
76
77    return qemu_get_struct(f, goldfish_battery_fields, s);
78}
79
80static struct goldfish_battery_state *battery_state;
81
82static uint32_t goldfish_battery_read(void *opaque, target_phys_addr_t offset)
83{
84    uint32_t ret;
85    struct goldfish_battery_state *s = opaque;
86
87    switch(offset) {
88        case BATTERY_INT_STATUS:
89            // return current buffer status flags
90            ret = s->int_status & s->int_enable;
91            if (ret) {
92                goldfish_device_set_irq(&s->dev, 0, 0);
93                s->int_status = 0;
94            }
95            return ret;
96
97		case BATTERY_INT_ENABLE:
98		    return s->int_enable;
99		case BATTERY_AC_ONLINE:
100		    return s->ac_online;
101		case BATTERY_STATUS:
102		    return s->status;
103		case BATTERY_HEALTH:
104		    return s->health;
105		case BATTERY_PRESENT:
106		    return s->present;
107		case BATTERY_CAPACITY:
108		    return s->capacity;
109
110        default:
111            cpu_abort (cpu_single_env, "goldfish_battery_read: Bad offset %x\n", offset);
112            return 0;
113    }
114}
115
116static void goldfish_battery_write(void *opaque, target_phys_addr_t offset, uint32_t val)
117{
118    struct goldfish_battery_state *s = opaque;
119
120    switch(offset) {
121        case BATTERY_INT_ENABLE:
122            /* enable interrupts */
123            s->int_enable = val;
124//            s->int_status = (AUDIO_INT_WRITE_BUFFER_1_EMPTY | AUDIO_INT_WRITE_BUFFER_2_EMPTY);
125//            goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
126            break;
127
128        default:
129            cpu_abort (cpu_single_env, "goldfish_audio_write: Bad offset %x\n", offset);
130    }
131}
132
133static CPUReadMemoryFunc *goldfish_battery_readfn[] = {
134    goldfish_battery_read,
135    goldfish_battery_read,
136    goldfish_battery_read
137};
138
139
140static CPUWriteMemoryFunc *goldfish_battery_writefn[] = {
141    goldfish_battery_write,
142    goldfish_battery_write,
143    goldfish_battery_write
144};
145
146void goldfish_battery_init()
147{
148    struct goldfish_battery_state *s;
149
150    s = (struct goldfish_battery_state *)qemu_mallocz(sizeof(*s));
151    s->dev.name = "goldfish-battery";
152    s->dev.base = 0;    // will be allocated dynamically
153    s->dev.size = 0x1000;
154    s->dev.irq_count = 1;
155
156    // default values for the battery
157    s->ac_online = 1;
158    s->status = POWER_SUPPLY_STATUS_CHARGING;
159    s->health = POWER_SUPPLY_HEALTH_GOOD;
160    s->present = 1;     // battery is present
161    s->capacity = 50;   // 50% charged
162
163    battery_state = s;
164
165    goldfish_device_add(&s->dev, goldfish_battery_readfn, goldfish_battery_writefn, s);
166
167    register_savevm( "battery_state", 0, BATTERY_STATE_SAVE_VERSION,
168                     goldfish_battery_save, goldfish_battery_load, s);
169}
170
171void goldfish_battery_set_prop(int ac, int property, int value)
172{
173    int new_status = (ac ? AC_STATUS_CHANGED : BATTERY_STATUS_CHANGED);
174
175    if (ac) {
176        switch (property) {
177            case POWER_SUPPLY_PROP_ONLINE:
178                battery_state->ac_online = value;
179                break;
180        }
181    } else {
182         switch (property) {
183            case POWER_SUPPLY_PROP_STATUS:
184                battery_state->status = value;
185                break;
186            case POWER_SUPPLY_PROP_HEALTH:
187                battery_state->health = value;
188                break;
189            case POWER_SUPPLY_PROP_PRESENT:
190                battery_state->present = value;
191                break;
192            case POWER_SUPPLY_PROP_CAPACITY:
193                battery_state->capacity = value;
194                break;
195        }
196    }
197
198    if (new_status != battery_state->int_status) {
199        battery_state->int_status |= new_status;
200        goldfish_device_set_irq(&battery_state->dev, 0, (battery_state->int_status & battery_state->int_enable));
201    }
202}
203
204void goldfish_battery_display(void (* callback)(void *data, const char* string), void *data)
205{
206    char          buffer[100];
207    const char*   value;
208
209    sprintf(buffer, "AC: %s\r\n", (battery_state->ac_online ? "online" : "offline"));
210    callback(data, buffer);
211
212    switch (battery_state->status) {
213	    case POWER_SUPPLY_STATUS_CHARGING:
214	        value = "Charging";
215	        break;
216	    case POWER_SUPPLY_STATUS_DISCHARGING:
217	        value = "Discharging";
218	        break;
219	    case POWER_SUPPLY_STATUS_NOT_CHARGING:
220	        value = "Not charging";
221	        break;
222	    case POWER_SUPPLY_STATUS_FULL:
223	        value = "Full";
224	        break;
225        default:
226	        value = "Unknown";
227	        break;
228    }
229    sprintf(buffer, "status: %s\r\n", value);
230    callback(data, buffer);
231
232    switch (battery_state->health) {
233	    case POWER_SUPPLY_HEALTH_GOOD:
234	        value = "Good";
235	        break;
236	    case POWER_SUPPLY_HEALTH_OVERHEAT:
237	        value = "Overhead";
238	        break;
239	    case POWER_SUPPLY_HEALTH_DEAD:
240	        value = "Dead";
241	        break;
242	    case POWER_SUPPLY_HEALTH_OVERVOLTAGE:
243	        value = "Overvoltage";
244	        break;
245	    case POWER_SUPPLY_HEALTH_UNSPEC_FAILURE:
246	        value = "Unspecified failure";
247	        break;
248        default:
249	        value = "Unknown";
250	        break;
251    }
252    sprintf(buffer, "health: %s\r\n", value);
253    callback(data, buffer);
254
255    sprintf(buffer, "present: %s\r\n", (battery_state->present ? "true" : "false"));
256    callback(data, buffer);
257
258    sprintf(buffer, "capacity: %d\r\n", battery_state->capacity);
259    callback(data, buffer);
260}
261