blob: f9c83af584d0431f09d9cd8306c3c5e215bc3942 [file] [log] [blame]
/* 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 "cpu.h"
#include "migration/qemu-file.h"
#include "hw/android/goldfish/device.h"
#include "hw/hw.h"
#include "hw/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;
// the fields below are part of the device configuration
// and don't need to be saved to / restored from snapshots.
int hw_has_battery;
};
/* 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;
if (qemu_get_struct(f, goldfish_battery_fields, s) < 0)
return -1;
goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
return 0;
}
static struct goldfish_battery_state *battery_state;
static uint32_t goldfish_battery_read(void *opaque, hwaddr 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(current_cpu,
"goldfish_battery_read: Bad offset %" HWADDR_PRIx "\n",
offset);
return 0;
}
}
static void goldfish_battery_write(void *opaque, hwaddr 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(current_cpu,
"goldfish_audio_write: Bad offset %" HWADDR_PRIx "\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(int has_battery)
{
struct goldfish_battery_state *s;
s = (struct goldfish_battery_state *)g_malloc0(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->hw_has_battery = has_battery;
if (has_battery) {
s->status = POWER_SUPPLY_STATUS_CHARGING;
s->health = POWER_SUPPLY_HEALTH_GOOD;
s->present = 1; // battery is present
s->capacity = 50; // 50% charged
} else {
s->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
s->health = POWER_SUPPLY_HEALTH_DEAD;
s->present = 0;
s->capacity = 0;
}
battery_state = s;
goldfish_device_add(&s->dev, goldfish_battery_readfn, goldfish_battery_writefn, s);
register_savevm(NULL,
"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 (!battery_state || !battery_state->hw_has_battery)
return;
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;
// Note: obviously, if there is no battery, the AC must always be on.
snprintf(buffer, sizeof 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";
}
snprintf(buffer, sizeof 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";
}
snprintf(buffer, sizeof buffer, "health: %s\r\n", value);
callback(data, buffer);
snprintf(buffer, sizeof buffer, "present: %s\r\n",
(battery_state->present) ? "true" : "false");
callback(data, buffer);
snprintf(buffer, sizeof buffer, "capacity: %d\r\n", battery_state->capacity);
callback(data, buffer);
}