156 lines
4.7 KiB
C
156 lines
4.7 KiB
C
/* Régressions du D1759 sur le bus I²C réel de QEMU. */
|
|
#include "qemu/osdep.h"
|
|
#include "qemu/main-loop.h"
|
|
#include "qemu/option.h"
|
|
#include "sysemu/cpus.h"
|
|
#include "hw/arm/ipod_touch_pcf50633_pmu.c"
|
|
|
|
static QemuOptsList rtc_test_opts = {
|
|
.name = "rtc-test",
|
|
.head = QTAILQ_HEAD_INITIALIZER(rtc_test_opts.head),
|
|
.desc = {
|
|
{ .name = "base", .type = QEMU_OPT_STRING },
|
|
{ .name = "clock", .type = QEMU_OPT_STRING },
|
|
{ }
|
|
}
|
|
};
|
|
|
|
static I2CBus *bus;
|
|
static Pcf50633State *pmu;
|
|
static DeviceState *parent;
|
|
|
|
static void setup(const char *base, const char *clock)
|
|
{
|
|
QemuOpts *opts = qemu_opts_create(&rtc_test_opts, NULL, 0, &error_abort);
|
|
qemu_opt_set(opts, "base", base, &error_abort);
|
|
qemu_opt_set(opts, "clock", clock, &error_abort);
|
|
configure_rtc(opts);
|
|
qemu_opts_del(opts);
|
|
parent = qdev_new(TYPE_PCF50633);
|
|
bus = i2c_init_bus(parent, "rtc-test");
|
|
pmu = PCF50633(i2c_slave_create_simple(bus, TYPE_D1759, 0x73));
|
|
}
|
|
|
|
static void teardown(void)
|
|
{
|
|
qdev_unrealize(DEVICE(pmu));
|
|
object_unref(OBJECT(parent));
|
|
}
|
|
|
|
static void select_register(uint8_t reg, bool stop)
|
|
{
|
|
g_assert_cmpint(i2c_start_transfer(bus, 0x73, false), ==, 0);
|
|
g_assert_cmpint(i2c_send(bus, reg), ==, 0);
|
|
if (stop) {
|
|
i2c_end_transfer(bus);
|
|
}
|
|
g_assert_cmpint(i2c_start_transfer(bus, 0x73, true), ==, 0);
|
|
}
|
|
|
|
static uint32_t read_word(uint8_t reg, bool stop)
|
|
{
|
|
uint32_t value = 0;
|
|
select_register(reg, stop);
|
|
for (int i = 0; i < 4; i++) {
|
|
value |= (uint32_t)i2c_recv(bus) << (i * 8);
|
|
}
|
|
i2c_end_transfer(bus);
|
|
return value;
|
|
}
|
|
|
|
static void write_word(uint8_t reg, uint32_t value)
|
|
{
|
|
g_assert_cmpint(i2c_start_transfer(bus, 0x73, false), ==, 0);
|
|
g_assert_cmpint(i2c_send(bus, reg), ==, 0);
|
|
for (int i = 0; i < 4; i++) {
|
|
g_assert_cmpint(i2c_send(bus, value >> (i * 8)), ==, 0);
|
|
}
|
|
i2c_end_transfer(bus);
|
|
}
|
|
|
|
static void test_host_utc(void)
|
|
{
|
|
time_t before = time(NULL);
|
|
/* Le fuseau de l'hôte ne doit pas décaler le compteur Unix. */
|
|
setenv("TZ", "Pacific/Honolulu", 1);
|
|
tzset();
|
|
setup("utc", "host");
|
|
uint32_t value = read_word(0x5c, false);
|
|
g_assert_cmpuint(value, >=, before);
|
|
g_assert_cmpuint(value, <=, time(NULL));
|
|
g_assert_cmpuint(read_word(0x64, true), ==, 0);
|
|
teardown();
|
|
unsetenv("TZ");
|
|
tzset();
|
|
}
|
|
|
|
static void test_fixed_base(void)
|
|
{
|
|
setup("2024-02-29T23:59:59", "vm");
|
|
g_assert_cmpint(rtc_clock, ==, QEMU_CLOCK_VIRTUAL);
|
|
g_assert_cmpuint(read_word(0x5c, true), ==, 1709251199);
|
|
g_assert_cmpuint(read_word(0x5c, false), ==, 1709251199);
|
|
teardown();
|
|
}
|
|
|
|
static void test_snapshot_rollover(void)
|
|
{
|
|
setup("2024-02-29T23:59:59", "vm");
|
|
uint32_t boundaries[] = { 1709251199, 0x00ffffff, 0xffffffff };
|
|
for (size_t n = 0; n < G_N_ELEMENTS(boundaries); n++) {
|
|
pmu->rtc_tick_offset = boundaries[n];
|
|
select_register(0x5c, false);
|
|
uint32_t value = i2c_recv(bus);
|
|
/* Le temps change entre deux octets, après capture du premier. */
|
|
pmu->rtc_tick_offset++;
|
|
for (int i = 1; i < 4; i++) {
|
|
value |= (uint32_t)i2c_recv(bus) << (8 * i);
|
|
}
|
|
i2c_end_transfer(bus);
|
|
g_assert_cmpuint(value, ==, boundaries[n]);
|
|
g_assert_cmpuint(read_word(0x5c, true), ==, boundaries[n] + 1);
|
|
}
|
|
teardown();
|
|
}
|
|
|
|
static void test_offsets(void)
|
|
{
|
|
setup("2024-02-29T23:59:59", "vm");
|
|
uint32_t counter = read_word(0x5c, true);
|
|
uint32_t offsets[] = { 3600, (uint32_t)-86400, 0x12345678, 0 };
|
|
for (size_t i = 0; i < G_N_ELEMENTS(offsets); i++) {
|
|
write_word(0x64, offsets[i]);
|
|
g_assert_cmpuint(read_word(0x64, false), ==, offsets[i]);
|
|
g_assert_cmpuint(read_word(0x5c, true), ==, counter);
|
|
}
|
|
/* Un changement de date ne doit pas réécrire le compteur matériel. */
|
|
write_word(0x5c, 1234);
|
|
g_assert_cmpuint(read_word(0x5c, false), ==, counter);
|
|
teardown();
|
|
}
|
|
|
|
static void test_boot_registers(void)
|
|
{
|
|
setup("2024-02-29T23:59:59", "vm");
|
|
write_word(0x60, 0xffffffff);
|
|
g_assert_cmpuint(read_word(0x60, true), ==, 0);
|
|
g_assert_cmpuint(read_word(0x5c, false), ==, 1709251199);
|
|
g_assert_cmpuint(read_word(0x64, true), ==, 0);
|
|
teardown();
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
g_test_init(&argc, &argv, NULL);
|
|
module_call_init(MODULE_INIT_QOM);
|
|
qemu_init_main_loop(&error_abort);
|
|
qemu_init_cpu_loop();
|
|
qemu_mutex_lock_iothread();
|
|
g_test_add_func("/ipod-rtc/host-utc", test_host_utc);
|
|
g_test_add_func("/ipod-rtc/fixed-base", test_fixed_base);
|
|
g_test_add_func("/ipod-rtc/snapshot-rollover", test_snapshot_rollover);
|
|
g_test_add_func("/ipod-rtc/offsets", test_offsets);
|
|
g_test_add_func("/ipod-rtc/boot-registers", test_boot_registers);
|
|
return g_test_run();
|
|
}
|