aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGreg Hackmann <ghackmann@google.com>2013-11-13 15:56:08 -0800
committerDavid 'Digit' Turner <digit@google.com>2014-05-26 10:30:04 +0200
commitbe6c9427b401e2b333004899d57a9b501b3668fd (patch)
tree4963ebc469111e0dceac2eb5d5363495251b3d9f
parent8b536b4565cb3466ce0bf3e1b3557bd68b147400 (diff)
downloadqemu-arm-be6c9427b401e2b333004899d57a9b501b3668fd.tar.gz
import goldfish_battery from android
Signed-off-by: Greg Hackmann <ghackmann@google.com>
-rw-r--r--hw/misc/goldfish_battery.c260
1 files changed, 260 insertions, 0 deletions
diff --git a/hw/misc/goldfish_battery.c b/hw/misc/goldfish_battery.c
new file mode 100644
index 0000000000..c4988d78df
--- /dev/null
+++ b/hw/misc/goldfish_battery.c
@@ -0,0 +1,260 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+*/
+#include "qemu_file.h"
+#include "goldfish_device.h"
+#include "power_supply.h"
+
+
+enum {
+ /* status register */
+ BATTERY_INT_STATUS = 0x00,
+ /* set this to enable IRQ */
+ BATTERY_INT_ENABLE = 0x04,
+
+ BATTERY_AC_ONLINE = 0x08,
+ BATTERY_STATUS = 0x0C,
+ BATTERY_HEALTH = 0x10,
+ BATTERY_PRESENT = 0x14,
+ BATTERY_CAPACITY = 0x18,
+
+ BATTERY_STATUS_CHANGED = 1U << 0,
+ AC_STATUS_CHANGED = 1U << 1,
+ BATTERY_INT_MASK = BATTERY_STATUS_CHANGED | AC_STATUS_CHANGED,
+};
+
+
+struct goldfish_battery_state {
+ struct goldfish_device dev;
+ // IRQs
+ uint32_t int_status;
+ // irq enable mask for int_status
+ uint32_t int_enable;
+
+ int ac_online;
+ int status;
+ int health;
+ int present;
+ int capacity;
+};
+
+/* update this each time you update the battery_state struct */
+#define BATTERY_STATE_SAVE_VERSION 1
+
+#define QFIELD_STRUCT struct goldfish_battery_state
+QFIELD_BEGIN(goldfish_battery_fields)
+ QFIELD_INT32(int_status),
+ QFIELD_INT32(int_enable),
+ QFIELD_INT32(ac_online),
+ QFIELD_INT32(status),
+ QFIELD_INT32(health),
+ QFIELD_INT32(present),
+ QFIELD_INT32(capacity),
+QFIELD_END
+
+static void goldfish_battery_save(QEMUFile* f, void* opaque)
+{
+ struct goldfish_battery_state* s = opaque;
+
+ qemu_put_struct(f, goldfish_battery_fields, s);
+}
+
+static int goldfish_battery_load(QEMUFile* f, void* opaque, int version_id)
+{
+ struct goldfish_battery_state* s = opaque;
+
+ if (version_id != BATTERY_STATE_SAVE_VERSION)
+ return -1;
+
+ return qemu_get_struct(f, goldfish_battery_fields, s);
+}
+
+static struct goldfish_battery_state *battery_state;
+
+static uint32_t goldfish_battery_read(void *opaque, target_phys_addr_t offset)
+{
+ uint32_t ret;
+ struct goldfish_battery_state *s = opaque;
+
+ switch(offset) {
+ case BATTERY_INT_STATUS:
+ // return current buffer status flags
+ ret = s->int_status & s->int_enable;
+ if (ret) {
+ goldfish_device_set_irq(&s->dev, 0, 0);
+ s->int_status = 0;
+ }
+ return ret;
+
+ case BATTERY_INT_ENABLE:
+ return s->int_enable;
+ case BATTERY_AC_ONLINE:
+ return s->ac_online;
+ case BATTERY_STATUS:
+ return s->status;
+ case BATTERY_HEALTH:
+ return s->health;
+ case BATTERY_PRESENT:
+ return s->present;
+ case BATTERY_CAPACITY:
+ return s->capacity;
+
+ default:
+ cpu_abort (cpu_single_env, "goldfish_battery_read: Bad offset %x\n", offset);
+ return 0;
+ }
+}
+
+static void goldfish_battery_write(void *opaque, target_phys_addr_t offset, uint32_t val)
+{
+ struct goldfish_battery_state *s = opaque;
+
+ switch(offset) {
+ case BATTERY_INT_ENABLE:
+ /* enable interrupts */
+ s->int_enable = val;
+// s->int_status = (AUDIO_INT_WRITE_BUFFER_1_EMPTY | AUDIO_INT_WRITE_BUFFER_2_EMPTY);
+// goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
+ break;
+
+ default:
+ cpu_abort (cpu_single_env, "goldfish_audio_write: Bad offset %x\n", offset);
+ }
+}
+
+static CPUReadMemoryFunc *goldfish_battery_readfn[] = {
+ goldfish_battery_read,
+ goldfish_battery_read,
+ goldfish_battery_read
+};
+
+
+static CPUWriteMemoryFunc *goldfish_battery_writefn[] = {
+ goldfish_battery_write,
+ goldfish_battery_write,
+ goldfish_battery_write
+};
+
+void goldfish_battery_init()
+{
+ struct goldfish_battery_state *s;
+
+ s = (struct goldfish_battery_state *)qemu_mallocz(sizeof(*s));
+ s->dev.name = "goldfish-battery";
+ s->dev.base = 0; // will be allocated dynamically
+ s->dev.size = 0x1000;
+ s->dev.irq_count = 1;
+
+ // default values for the battery
+ s->ac_online = 1;
+ s->status = POWER_SUPPLY_STATUS_CHARGING;
+ s->health = POWER_SUPPLY_HEALTH_GOOD;
+ s->present = 1; // battery is present
+ s->capacity = 50; // 50% charged
+
+ battery_state = s;
+
+ goldfish_device_add(&s->dev, goldfish_battery_readfn, goldfish_battery_writefn, s);
+
+ register_savevm( "battery_state", 0, BATTERY_STATE_SAVE_VERSION,
+ goldfish_battery_save, goldfish_battery_load, s);
+}
+
+void goldfish_battery_set_prop(int ac, int property, int value)
+{
+ int new_status = (ac ? AC_STATUS_CHANGED : BATTERY_STATUS_CHANGED);
+
+ if (ac) {
+ switch (property) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ battery_state->ac_online = value;
+ break;
+ }
+ } else {
+ switch (property) {
+ case POWER_SUPPLY_PROP_STATUS:
+ battery_state->status = value;
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ battery_state->health = value;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ battery_state->present = value;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ battery_state->capacity = value;
+ break;
+ }
+ }
+
+ if (new_status != battery_state->int_status) {
+ battery_state->int_status |= new_status;
+ goldfish_device_set_irq(&battery_state->dev, 0, (battery_state->int_status & battery_state->int_enable));
+ }
+}
+
+void goldfish_battery_display(void (* callback)(void *data, const char* string), void *data)
+{
+ char buffer[100];
+ const char* value;
+
+ sprintf(buffer, "AC: %s\r\n", (battery_state->ac_online ? "online" : "offline"));
+ callback(data, buffer);
+
+ switch (battery_state->status) {
+ case POWER_SUPPLY_STATUS_CHARGING:
+ value = "Charging";
+ break;
+ case POWER_SUPPLY_STATUS_DISCHARGING:
+ value = "Discharging";
+ break;
+ case POWER_SUPPLY_STATUS_NOT_CHARGING:
+ value = "Not charging";
+ break;
+ case POWER_SUPPLY_STATUS_FULL:
+ value = "Full";
+ break;
+ default:
+ value = "Unknown";
+ break;
+ }
+ sprintf(buffer, "status: %s\r\n", value);
+ callback(data, buffer);
+
+ switch (battery_state->health) {
+ case POWER_SUPPLY_HEALTH_GOOD:
+ value = "Good";
+ break;
+ case POWER_SUPPLY_HEALTH_OVERHEAT:
+ value = "Overhead";
+ break;
+ case POWER_SUPPLY_HEALTH_DEAD:
+ value = "Dead";
+ break;
+ case POWER_SUPPLY_HEALTH_OVERVOLTAGE:
+ value = "Overvoltage";
+ break;
+ case POWER_SUPPLY_HEALTH_UNSPEC_FAILURE:
+ value = "Unspecified failure";
+ break;
+ default:
+ value = "Unknown";
+ break;
+ }
+ sprintf(buffer, "health: %s\r\n", value);
+ callback(data, buffer);
+
+ sprintf(buffer, "present: %s\r\n", (battery_state->present ? "true" : "false"));
+ callback(data, buffer);
+
+ sprintf(buffer, "capacity: %d\r\n", battery_state->capacity);
+ callback(data, buffer);
+}