OK, this is what I've come up with. Dunno whether it's right or not -- the object model is decoupled from the memory model, so there's no straightforward way to override just a few of the registers.
At this stage this is just for comment, as I don't really have that much of a clue about how the object/class hierarchy is meant to work. --- hw/arm/kzm.c | 3 + hw/misc/arm_l2x0.c | 129 ++++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 96 insertions(+), 36 deletions(-) Index: qemu/hw/arm/kzm.c =================================================================== --- qemu.orig/hw/arm/kzm.c 2013-08-07 11:21:48.864692846 +1000 +++ qemu/hw/arm/kzm.c 2013-08-07 11:22:40.292983604 +1000 @@ -33,6 +33,7 @@ * 0x1fffc000-0x1fffffff RAM EMULATED * 0x20000000-0x2fffffff Reserved IGNORED * 0x30000000-0x7fffffff I.MX31 Internal Register Space + * 0x30000000-0x30000fff L2 Cache Controller PARTIALLY EMULATED * 0x43f00000 IO_AREA0 * 0x43f90000 UART1 EMULATED * 0x43f94000 UART2 EMULATED @@ -134,6 +135,8 @@ static void kzm_init(QEMUMachineInitArgs DEVICE_NATIVE_ENDIAN); } + sysbus_create_varargs("imx_l2cc", 0x30000000, NULL); + kzm_binfo.ram_size = ram_size; kzm_binfo.kernel_filename = kernel_filename; kzm_binfo.kernel_cmdline = kernel_cmdline; Index: qemu/hw/misc/arm_l2x0.c =================================================================== --- qemu.orig/hw/misc/arm_l2x0.c 2013-08-07 11:21:48.864692846 +1000 +++ qemu/hw/misc/arm_l2x0.c 2013-08-07 11:21:48.860692824 +1000 @@ -21,7 +21,9 @@ #include "hw/sysbus.h" /* L2C-310 r3p2 */ -#define CACHE_ID 0x410000c8 +#define PL310_CACHE_ID 0x410000c8 +/* L2CC from Freescale */ +#define IMX_PL2CC_CACHE_ID 0xD5000041 #define TYPE_ARM_L2X0 "l2x0" #define ARM_L2X0(obj) OBJECT_CHECK(L2x0State, (obj), TYPE_ARM_L2X0) @@ -30,6 +32,7 @@ typedef struct L2x0State { SysBusDevice parent_obj; MemoryRegion iomem; + uint32_t cache_id; uint32_t cache_type; uint32_t ctrl; uint32_t aux_ctrl; @@ -66,7 +69,7 @@ static uint64_t l2x0_priv_read(void *opa } switch (offset) { case 0: - return CACHE_ID; + return s->cache_id; case 0x4: /* aux_ctrl values affect cache_type values */ cache_data = (s->aux_ctrl & (7 << 17)) >> 15; @@ -78,23 +81,25 @@ static uint64_t l2x0_priv_read(void *opa return s->aux_ctrl; case 0x108: return s->tag_ctrl; - case 0x10C: - return s->data_ctrl; - case 0xC00: - return s->filter_start; - case 0xC04: - return s->filter_end; case 0xF40: return 0; - case 0xF60: - return 0; - case 0xF80: - return 0; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "l2x0_priv_read: Bad offset %x\n", (int)offset); - break; } + if (s->cache_id == PL310_CACHE_ID) { + switch (offset) { + case 0x10C: + return s->data_ctrl; + case 0xC00: + return s->filter_start; + case 0xC04: + return s->filter_end; + case 0xF60: + return 0; + case 0xF80: + return 0; + } + } + qemu_log_mask(LOG_GUEST_ERROR, + "l2x0_priv_read: Bad offset %x\n", (int)offset); return 0; } @@ -107,6 +112,7 @@ static void l2x0_priv_write(void *opaque /* ignore */ return; } + switch (offset) { case 0x100: s->ctrl = value & 1; @@ -114,29 +120,32 @@ static void l2x0_priv_write(void *opaque case 0x104: s->aux_ctrl = value; break; - case 0x108: - s->tag_ctrl = value; - break; - case 0x10C: - s->data_ctrl = value; - break; - case 0xC00: - s->filter_start = value; - break; - case 0xC04: - s->filter_end = value; - break; case 0xF40: return; - case 0xF60: - return; - case 0xF80: - return; - default: - qemu_log_mask(LOG_GUEST_ERROR, - "l2x0_priv_write: Bad offset %x\n", (int)offset); - break; } + + if (s->cache_id == PL310_CACHE_ID) { + switch (offset) { + case 0x108: + s->tag_ctrl = value; + break; + case 0x10C: + s->data_ctrl = value; + break; + case 0xC00: + s->filter_start = value; + break; + case 0xC04: + s->filter_end = value; + break; + case 0xF60: + return; + case 0xF80: + return; + } + } + qemu_log_mask(LOG_GUEST_ERROR, + "l2x0_priv_write: Bad offset %x\n", (int)offset); } static void l2x0_priv_reset(DeviceState *dev) @@ -184,16 +193,64 @@ static void l2x0_class_init(ObjectClass dc->reset = l2x0_priv_reset; } +static void l2x0_init(Object *obj) +{ + L2x0State *s = ARM_L2X0(obj); + + s->cache_id = PL310_CACHE_ID; +} + static const TypeInfo l2x0_info = { .name = TYPE_ARM_L2X0, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(L2x0State), + .instance_init = l2x0_init, .class_init = l2x0_class_init, }; +/******************************** + * The i.MX31 L2CC is a subset of the PL310 implemented above. + */ + +static void imx_l2cc_init(Object *obj) { + L2x0State *s = ARM_L2X0(obj); + + s->cache_id = IMX_PL2CC_CACHE_ID; +} + + +static void imx_l2cc_priv_reset(DeviceState *dev) +{ + L2x0State *s = ARM_L2X0(dev); + + s->ctrl = 0; + s->aux_ctrl = 0xE4020FFF; +} + + +/* + * I assume the parent class's init function has already been called. + */ +static void imx_l2cc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = imx_l2cc_priv_reset; +} + + +static const TypeInfo imx_l2cc_info = { + .name = "imx_l2cc", + .parent = TYPE_ARM_L2X0, + .instance_init = imx_l2cc_init, + .class_init = imx_l2cc_class_init, +}; + + static void l2x0_register_types(void) { type_register_static(&l2x0_info); + type_register_static(&imx_l2cc_info); } type_init(l2x0_register_types)