Files
QEMU-S5L8950X/hw/misc/s5l8950x-i2c.c
T
Yaya48 5d9a60a926 hw/arm: add authenticated A6 IMG3 boot lab
Model the A6 crypto, interrupt, USB, and platform blocks needed to boot SecureROM through iBSS into iBEC Recovery.

Add local lab identity, IMG3, and APTicket tooling, patched macOS recovery utilities, UART and GDB access, and English end-user documentation.
2026-09-01 09:51:49 -07:00

116 lines
3.1 KiB
C

/*
* Apple S5L8950X I2C controller (minimal polled-transfer model)
*
* Early iBoot uses the three controllers while bringing up board devices.
* The target devices are not modelled yet, so reads return zero, but command
* FIFO writes complete synchronously and without a NAK. This is sufficient
* to preserve the firmware's normal timeout/error paths while allowing the
* boot chain to continue through board discovery.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
#include "hw/arm/s5l8950x.h"
#include "hw/core/irq.h"
#include "hw/core/sysbus.h"
#include "qom/object.h"
#define S5L8950X_I2C_REGION_SIZE 0x1000
#define S5L8950X_I2C_STATUS 0x14
#define S5L8950X_I2C_STATUS_DONE BIT(27)
OBJECT_DECLARE_SIMPLE_TYPE(S5L8950XI2CState, S5L8950X_I2C)
struct S5L8950XI2CState {
SysBusDevice parent_obj;
MemoryRegion iomem;
qemu_irq irq;
uint32_t regs[S5L8950X_I2C_REGION_SIZE / sizeof(uint32_t)];
};
static uint64_t s5l8950x_i2c_read(void *opaque, hwaddr offset,
unsigned size)
{
S5L8950XI2CState *s = opaque;
uint32_t value = s->regs[offset / sizeof(uint32_t)];
if (offset == S5L8950X_I2C_STATUS) {
value |= S5L8950X_I2C_STATUS_DONE;
}
return value;
}
static void s5l8950x_i2c_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
S5L8950XI2CState *s = opaque;
if (offset == S5L8950X_I2C_STATUS) {
s->regs[offset / sizeof(uint32_t)] &= ~value;
if (!(s->regs[offset / sizeof(uint32_t)] &
S5L8950X_I2C_STATUS_DONE)) {
qemu_irq_lower(s->irq);
}
return;
}
s->regs[offset / sizeof(uint32_t)] = value;
if (offset == 0) {
s->regs[S5L8950X_I2C_STATUS / sizeof(uint32_t)] |=
S5L8950X_I2C_STATUS_DONE;
qemu_irq_raise(s->irq);
}
}
static const MemoryRegionOps s5l8950x_i2c_ops = {
.read = s5l8950x_i2c_read,
.write = s5l8950x_i2c_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 4,
},
};
static void s5l8950x_i2c_reset(DeviceState *dev)
{
S5L8950XI2CState *s = S5L8950X_I2C(dev);
memset(s->regs, 0, sizeof(s->regs));
qemu_irq_lower(s->irq);
}
static void s5l8950x_i2c_init(Object *obj)
{
S5L8950XI2CState *s = S5L8950X_I2C(obj);
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
memory_region_init_io(&s->iomem, obj, &s5l8950x_i2c_ops, s,
TYPE_S5L8950X_I2C, S5L8950X_I2C_REGION_SIZE);
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq);
}
static void s5l8950x_i2c_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
device_class_set_legacy_reset(dc, s5l8950x_i2c_reset);
}
static const TypeInfo s5l8950x_i2c_info = {
.name = TYPE_S5L8950X_I2C,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(S5L8950XI2CState),
.instance_init = s5l8950x_i2c_init,
.class_init = s5l8950x_i2c_class_init,
};
static void s5l8950x_i2c_register_types(void)
{
type_register_static(&s5l8950x_i2c_info);
}
type_init(s5l8950x_i2c_register_types)