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
17 enum {
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
35 struct 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
53 QFIELD_BEGIN(goldfish_battery_fields)
QFIELD_INT32(int_status)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),
61 QFIELD_END
62
63 static 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
goldfish_battery_load(QEMUFile * f,void * opaque,int version_id)70 static 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
80 static struct goldfish_battery_state *battery_state;
81
goldfish_battery_read(void * opaque,target_phys_addr_t offset)82 static 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
goldfish_battery_write(void * opaque,target_phys_addr_t offset,uint32_t val)116 static 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
133 static CPUReadMemoryFunc *goldfish_battery_readfn[] = {
134 goldfish_battery_read,
135 goldfish_battery_read,
136 goldfish_battery_read
137 };
138
139
140 static CPUWriteMemoryFunc *goldfish_battery_writefn[] = {
141 goldfish_battery_write,
142 goldfish_battery_write,
143 goldfish_battery_write
144 };
145
goldfish_battery_init()146 void 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
goldfish_battery_set_prop(int ac,int property,int value)171 void 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
goldfish_battery_display(void (* callback)(void * data,const char * string),void * data)204 void 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