Files
qemu-ios/hw/arm/ipod_touch_2g.c
T

453 lines
17 KiB
C

#include "qemu/osdep.h"
#include "qapi/error.h"
#include "hw/arm/boot.h"
#include "exec/address-spaces.h"
#include "hw/misc/unimp.h"
#include "hw/irq.h"
#include "sysemu/sysemu.h"
#include "sysemu/reset.h"
#include "hw/platform-bus.h"
#include "hw/block/flash.h"
#include "hw/qdev-clock.h"
#include "hw/arm/exynos4210.h"
#include "hw/arm/ipod_touch_2g.h"
#include "target/arm/cpregs.h"
#define VMSTATE_IT2G_CPREG(name) \
VMSTATE_UINT64(IT2G_CPREG_VAR_NAME(name), IPodTouchMachineState)
#define IT2G_CPREG_DEF(p_name, p_op0, p_op1, p_crn, p_crm, p_op2, p_access, p_reset) \
{ \
.cp = 15, \
.name = #p_name, .opc0 = p_op0, .crn = p_crn, .crm = p_crm, \
.opc1 = p_op1, .opc2 = p_op2, .access = p_access, .resetvalue = p_reset, \
.state = ARM_CP_STATE_AA32, .type = ARM_CP_OVERRIDE, \
.fieldoffset = offsetof(IPodTouchMachineState, IT2G_CPREG_VAR_NAME(p_name)) \
- offsetof(ARMCPU, env) \
}
static void allocate_ram(MemoryRegion *top, const char *name, uint32_t addr, uint32_t size)
{
MemoryRegion *sec = g_new(MemoryRegion, 1);
memory_region_init_ram(sec, NULL, name, size, &error_fatal);
memory_region_add_subregion(top, addr, sec);
}
static const ARMCPRegInfo it2g_cp_reginfo_tcg[] = {
IT2G_CPREG_DEF(REG0, 0, 0, 7, 6, 0, PL1_RW, 0),
IT2G_CPREG_DEF(REG1, 0, 0, 15, 2, 4, PL1_RW, 0),
IT2G_CPREG_DEF(REG1, 0, 0, 7, 14, 0, PL1_RW, 0),
IT2G_CPREG_DEF(REG1, 0, 0, 7, 10, 0, PL1_RW, 0),
};
static void ipod_touch_cpu_setup(MachineState *machine, MemoryRegion **sysmem, ARMCPU **cpu, AddressSpace **nsas)
{
Object *cpuobj = object_new(machine->cpu_type);
*cpu = ARM_CPU(cpuobj);
CPUState *cs = CPU(*cpu);
*sysmem = get_system_memory();
object_property_set_link(cpuobj, "memory", OBJECT(*sysmem), &error_abort);
object_property_set_bool(cpuobj, "has_el3", false, NULL);
object_property_set_bool(cpuobj, "has_el2", false, NULL);
object_property_set_bool(cpuobj, "realized", true, &error_fatal);
*nsas = cpu_get_address_space(cs, ARMASIdx_NS);
define_arm_cp_regs(*cpu, it2g_cp_reginfo_tcg);
object_unref(cpuobj);
}
static void ipod_touch_cpu_reset(void *opaque)
{
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE((MachineState *)opaque);
ARMCPU *cpu = nms->cpu;
CPUState *cs = CPU(cpu);
cpu_reset(cs);
//env->regs[0] = nms->kbootargs_pa;
//cpu_set_pc(CPU(cpu), 0xc00607ec);
cpu_set_pc(CPU(cpu), VROM_MEM_BASE);
//env->regs[0] = 0x9000000;
//cpu_set_pc(CPU(cpu), LLB_BASE + 0x100);
//cpu_set_pc(CPU(cpu), VROM_MEM_BASE);
}
static void ipod_touch_memory_setup(MachineState *machine, MemoryRegion *sysmem, AddressSpace *nsas)
{
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(machine);
allocate_ram(sysmem, "insecure_ram", INSECURE_RAM_MEM_BASE, 0x3000000);
allocate_ram(sysmem, "secure_ram", SECURE_RAM_MEM_BASE, 0x4B04000);
allocate_ram(sysmem, "iboot", IBOOT_MEM_BASE, 0x100000);
allocate_ram(sysmem, "llb", 0x22000000, 0x100000);
allocate_ram(sysmem, "sram1", SRAM1_MEM_BASE, 0x100000);
allocate_ram(sysmem, "framebuffer", FRAMEBUFFER_MEM_BASE, 0x400000);
allocate_ram(sysmem, "edgeic", EDGEIC_MEM_BASE, 0x1000);
allocate_ram(sysmem, "swi", SWI_MEM_BASE, 0x1000);
allocate_ram(sysmem, "h264", H264_MEM_BASE, 0x4000);
// load the bootrom (vrom)
uint8_t *file_data = NULL;
unsigned long fsize;
if (g_file_get_contents("/Users/martijndevos/Documents/ipod_touch_2g_emulation/bootrom_240_4", (char **)&file_data, &fsize, NULL)) {
allocate_ram(sysmem, "vrom", 0x0, 0x20000);
address_space_rw(nsas, VROM_MEM_BASE, MEMTXATTRS_UNSPECIFIED, (uint8_t *)file_data, fsize, 1);
}
}
static char *ipod_touch_get_nor_path(Object *obj, Error **errp)
{
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(obj);
return g_strdup(nms->nor_path);
}
static void ipod_touch_set_nor_path(Object *obj, const char *value, Error **errp)
{
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(obj);
g_strlcpy(nms->nor_path, value, sizeof(nms->nor_path));
}
static void ipod_touch_instance_init(Object *obj)
{
object_property_add_str(obj, "nor", ipod_touch_get_nor_path, ipod_touch_set_nor_path);
object_property_set_description(obj, "nor", "Path to the S5L8720 NOR image");
}
static inline qemu_irq s5l8900_get_irq(IPodTouchMachineState *s, int n)
{
return s->irq[n / S5L8720_VIC_SIZE][n % S5L8720_VIC_SIZE];
}
static uint32_t s5l8720_usb_hwcfg[] = {
0,
0x7a8f60d0,
0x082000e8,
0x01f08024
};
static void ipod_touch_machine_init(MachineState *machine)
{
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(machine);
MemoryRegion *sysmem;
AddressSpace *nsas;
ARMCPU *cpu;
ipod_touch_cpu_setup(machine, &sysmem, &cpu, &nsas);
// setup clock
nms->sysclk = clock_new(OBJECT(machine), "SYSCLK");
clock_set_hz(nms->sysclk, 12000000ULL);
nms->cpu = cpu;
nms->nsas = nsas;
// setup VICs
nms->irq = g_malloc0(sizeof(qemu_irq *) * 2);
DeviceState *dev = pl192_manual_init("vic0", qdev_get_gpio_in(DEVICE(nms->cpu), ARM_CPU_IRQ), qdev_get_gpio_in(DEVICE(nms->cpu), ARM_CPU_FIQ), NULL);
PL192State *s = PL192(dev);
nms->vic0 = s;
memory_region_add_subregion(sysmem, VIC0_MEM_BASE, &nms->vic0->iomem);
nms->irq[0] = g_malloc0(sizeof(qemu_irq) * 32);
for (int i = 0; i < 32; i++) { nms->irq[0][i] = qdev_get_gpio_in(dev, i); }
dev = pl192_manual_init("vic1", NULL);
s = PL192(dev);
nms->vic1 = s;
memory_region_add_subregion(sysmem, VIC1_MEM_BASE, &nms->vic1->iomem);
nms->irq[1] = g_malloc0(sizeof(qemu_irq) * 32);
for (int i = 0; i < 32; i++) { nms->irq[1][i] = qdev_get_gpio_in(dev, i); }
// // chain VICs together
nms->vic1->daisy = nms->vic0;
// init clock 0
dev = qdev_new("ipodtouch.clock");
IPodTouchClockState *clock0_state = IPOD_TOUCH_CLOCK(dev);
nms->clock0 = clock0_state;
memory_region_add_subregion(sysmem, CLOCK0_MEM_BASE, &clock0_state->iomem);
// init clock 1
dev = qdev_new("ipodtouch.clock");
IPodTouchClockState *clock1_state = IPOD_TOUCH_CLOCK(dev);
nms->clock1 = clock1_state;
memory_region_add_subregion(sysmem, CLOCK1_MEM_BASE, &clock1_state->iomem);
// init the timer
dev = qdev_new("ipodtouch.timer");
IPodTouchTimerState *timer_state = IPOD_TOUCH_TIMER(dev);
nms->timer1 = timer_state;
memory_region_add_subregion(sysmem, TIMER1_MEM_BASE, &timer_state->iomem);
SysBusDevice *busdev = SYS_BUS_DEVICE(dev);
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_TIMER1_IRQ));
//sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_TIMER1_IRQ - 1));
timer_state->sysclk = nms->sysclk;
// init sysic
dev = qdev_new("ipodtouch.sysic");
IPodTouchSYSICState *sysic_state = IPOD_TOUCH_SYSIC(dev);
nms->sysic = (IPodTouchSYSICState *) g_malloc0(sizeof(struct IPodTouchSYSICState));
memory_region_add_subregion(sysmem, SYSIC_MEM_BASE, &sysic_state->iomem);
busdev = SYS_BUS_DEVICE(dev);
for(int grp = 0; grp < GPIO_NUMINTGROUPS; grp++) {
sysbus_connect_irq(busdev, grp, s5l8900_get_irq(nms, S5L8900_GPIO_IRQS[grp]));
}
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_GPIO_G0_IRQ));
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_GPIO_G1_IRQ));
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_GPIO_G2_IRQ));
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_GPIO_G3_IRQ));
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_GPIO_G4_IRQ));
// init GPIO
dev = qdev_new("ipodtouch.gpio");
IPodTouchGPIOState *gpio_state = IPOD_TOUCH_GPIO(dev);
nms->gpio_state = gpio_state;
memory_region_add_subregion(sysmem, GPIO_MEM_BASE, &gpio_state->iomem);
// init SDIO
dev = qdev_new("ipodtouch.sdio");
IPodTouchSDIOState *sdio_state = IPOD_TOUCH_SDIO(dev);
nms->sdio_state = sdio_state;
memory_region_add_subregion(sysmem, SDIO_MEM_BASE, &sdio_state->iomem);
busdev = SYS_BUS_DEVICE(dev);
sysbus_realize(busdev, &error_fatal);
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_SDIO_IRQ));
dev = exynos4210_uart_create(UART0_MEM_BASE, 256, 0, serial_hd(0), nms->irq[0][24]);
if (!dev) {
printf("Failed to create uart0 device!\n");
abort();
}
dev = exynos4210_uart_create(UART1_MEM_BASE, 256, 1, serial_hd(1), nms->irq[0][25]);
if (!dev) {
printf("Failed to create uart1 device!\n");
abort();
}
dev = exynos4210_uart_create(UART2_MEM_BASE, 256, 2, serial_hd(2), nms->irq[0][26]);
if (!dev) {
printf("Failed to create uart2 device!\n");
abort();
}
dev = exynos4210_uart_create(UART3_MEM_BASE, 256, 3, serial_hd(3), nms->irq[0][27]);
if (!dev) {
printf("Failed to create uart3 device!\n");
abort();
}
// dev = exynos4210_uart_create(UART4_MEM_BASE, 256, 4, serial_hd(4), nms->irq[0][28]);
// if (!dev) {
// printf("Failed to create uart4 device!\n");
// abort();
// }
// init spis
set_spi_base(0);
dev = sysbus_create_simple("ipodtouch.spi", SPI0_MEM_BASE, s5l8900_get_irq(nms, S5L8720_SPI0_IRQ));
IPodTouchSPIState *spi0_state = IPOD_TOUCH_SPI(dev);
nms->spi0_state = spi0_state;
strcpy(spi0_state->nor->nor_path, nms->nor_path);
set_spi_base(1);
dev = sysbus_create_simple("ipodtouch.spi", SPI1_MEM_BASE, s5l8900_get_irq(nms, S5L8720_SPI1_IRQ));
IPodTouchSPIState *spi1_state = IPOD_TOUCH_SPI(dev);
nms->spi1_state = spi1_state;
//strcpy(spi1_state->nor->nor_path, nms->nor_path);
set_spi_base(2);
sysbus_create_simple("ipodtouch.spi", SPI2_MEM_BASE, s5l8900_get_irq(nms, S5L8720_SPI2_IRQ));
set_spi_base(3);
sysbus_create_simple("ipodtouch.spi", SPI3_MEM_BASE, s5l8900_get_irq(nms, S5L8720_SPI3_IRQ));
set_spi_base(4);
dev = sysbus_create_simple("ipodtouch.spi", SPI4_MEM_BASE, s5l8900_get_irq(nms, S5L8720_SPI4_IRQ));
IPodTouchSPIState *spi4_state = IPOD_TOUCH_SPI(dev);
spi4_state->mt->sysic = sysic_state;
spi4_state->mt->gpio_state = gpio_state;
nms->spi4_state = spi4_state;
// init the chip ID module
dev = qdev_new("ipodtouch.chipid");
IPodTouchChipIDState *chipid_state = IPOD_TOUCH_CHIPID(dev);
nms->chipid_state = chipid_state;
memory_region_add_subregion(sysmem, CHIPID_MEM_BASE, &chipid_state->iomem);
// init the TVOut instance
dev = qdev_new("ipodtouch.tvout");
IPodTouchTVOutState *tvout_state = IPOD_TOUCH_TVOUT(dev);
nms->tvout_state = tvout_state;
memory_region_add_subregion(sysmem, TVOUT_MIXER1_MEM_BASE, &tvout_state->mixer1_iomem);
memory_region_add_subregion(sysmem, TVOUT_MIXER2_MEM_BASE, &tvout_state->mixer2_iomem);
memory_region_add_subregion(sysmem, TVOUT_SDO_MEM_BASE, &tvout_state->sdo_iomem);
busdev = SYS_BUS_DEVICE(dev);
sysbus_realize(busdev, &error_fatal);
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_TVOUT_SDO_IRQ));
// init the unknown1 module
dev = qdev_new("ipodtouch.unknown1");
IPodTouchUnknown1State *unknown1_state = IPOD_TOUCH_UNKNOWN1(dev);
memory_region_add_subregion(sysmem, UNKNOWN1_MEM_BASE, &unknown1_state->iomem);
// init USB OTG
dev = ipod_touch_init_usb_otg(s5l8900_get_irq(nms, S5L8720_USB_OTG_IRQ), s5l8720_usb_hwcfg);
synopsys_usb_state *usb_otg = S5L8900USBOTG(dev);
nms->usb_otg = usb_otg;
memory_region_add_subregion(sysmem, USBOTG_MEM_BASE, &nms->usb_otg->iomem);
// init two pl080 DMAC0 devices
dev = qdev_new("pl080");
PL080State *pl080_1 = PL080(dev);
object_property_set_link(OBJECT(dev), "downstream", OBJECT(sysmem), &error_fatal);
memory_region_add_subregion(sysmem, DMAC0_MEM_BASE, &pl080_1->iomem1);
busdev = SYS_BUS_DEVICE(dev);
sysbus_realize(busdev, &error_fatal);
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_DMAC0_IRQ));
dev = qdev_new("pl080");
PL080State *pl080_2 = PL080(dev);
object_property_set_link(OBJECT(dev), "downstream", OBJECT(sysmem), &error_fatal);
memory_region_add_subregion(sysmem, DMAC1_0_MEM_BASE, &pl080_2->iomem1);
memory_region_add_subregion(sysmem, DMAC1_1_MEM_BASE, &pl080_2->iomem2);
busdev = SYS_BUS_DEVICE(dev);
sysbus_realize(busdev, &error_fatal);
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_DMAC1_IRQ));
// Init I2C0
dev = qdev_new("ipodtouch.i2c");
IPodTouchI2CState *i2c_state = IPOD_TOUCH_I2C(dev);
i2c_state->base = 0;
nms->i2c0_state = i2c_state;
busdev = SYS_BUS_DEVICE(dev);
memory_region_add_subregion(sysmem, I2C0_MEM_BASE, &i2c_state->iomem);
sysbus_realize(busdev, &error_fatal);
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_I2C0_IRQ));
// init the PMU
I2CSlave * pmu = i2c_slave_create_simple(i2c_state->bus, "pcf50633", 0x73);
// init the accelerometer
I2CSlave *accelerometer = i2c_slave_create_simple(i2c_state->bus, "lis302dl", 0x1D);
// Init I2C1
dev = qdev_new("ipodtouch.i2c");
i2c_state = IPOD_TOUCH_I2C(dev);
nms->i2c1_state = i2c_state;
i2c_state->base = 1;
busdev = SYS_BUS_DEVICE(dev);
memory_region_add_subregion(sysmem, I2C1_MEM_BASE, &i2c_state->iomem);
sysbus_realize(busdev, &error_fatal);
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_I2C1_IRQ));
// init the Mikey
I2CSlave *cd327mikey = i2c_slave_create_simple(i2c_state->bus, "cd3272mikey", 0x39);
// init the FMSS flash controller
dev = qdev_new("ipodtouch.fmss");
IPodTouchFMSSState *fmss_state = IPOD_TOUCH_FMSS(dev);
nms->fmss_state = fmss_state;
busdev = SYS_BUS_DEVICE(dev);
memory_region_add_subregion(sysmem, FMSS_MEM_BASE, &fmss_state->iomem);
sysbus_realize(busdev, &error_fatal);
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_FMSS_IRQ));
// init the USB module
dev = qdev_new("ipodtouch.usbphys");
IPodTouchUSBPhysState *usb_phys_state = IPOD_TOUCH_USB_PHYS(dev);
nms->usb_phys_state = usb_phys_state;
memory_region_add_subregion(sysmem, USBPHYS_MEM_BASE, &usb_phys_state->iomem);
ipod_touch_memory_setup(machine, sysmem, nsas);
// init the MIPI SDI controller
dev = qdev_new("ipodtouch.mipidsi");
IPodTouchMIPIDSIState *mipi_dsi_state = IPOD_TOUCH_MIPI_DSI(dev);
nms->mipi_dsi_state = mipi_dsi_state;
memory_region_add_subregion(sysmem, MIPI_DSI_MEM_BASE, &mipi_dsi_state->iomem);
// init LCD
dev = qdev_new("ipodtouch.lcd");
IPodTouchLCDState *lcd_state = IPOD_TOUCH_LCD(dev);
lcd_state->sysmem = sysmem;
lcd_state->mt = spi4_state->mt;
nms->lcd_state = lcd_state;
busdev = SYS_BUS_DEVICE(dev);
memory_region_add_subregion(sysmem, DISPLAY_MEM_BASE, &lcd_state->iomem);
sysbus_realize(busdev, &error_fatal);
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8720_LCD_IRQ));
// init scaler / CSC
dev = qdev_new("ipodtouch.scalercsc");
IPodTouchScalerCSCState *scaler_csc_state = IPOD_TOUCH_SCALER_CSC(dev);
nms->scaler_csc_state = scaler_csc_state;
busdev = SYS_BUS_DEVICE(dev);
memory_region_add_subregion(sysmem, SCALER_CSC_MEM_BASE, &scaler_csc_state->iomem);
sysbus_realize(busdev, &error_fatal);
// init SHA1 engine
dev = qdev_new("ipodtouch.sha1");
IPodTouchSHA1State *sha1_state = IPOD_TOUCH_SHA1(dev);
nms->sha1_state = sha1_state;
memory_region_add_subregion(sysmem, SHA1_MEM_BASE, &sha1_state->iomem);
// init AES engine
dev = qdev_new("ipodtouch.aes");
IPodTouchAESState *aes_state = IPOD_TOUCH_AES(dev);
nms->aes_state = aes_state;
memory_region_add_subregion(sysmem, AES_MEM_BASE, &aes_state->iomem);
// init PKE engine
dev = qdev_new("ipodtouch.pke");
IPodTouchPKEState *pke_state = IPOD_TOUCH_PKE(dev);
nms->pke_state = pke_state;
memory_region_add_subregion(sysmem, PKE_MEM_BASE, &pke_state->iomem);
// init block device engine
dev = qdev_new("ipodtouch.blockdevice");
IPodTouchBlockDeviceState *bdev_state = IPOD_TOUCH_BLOCK_DEVICE(dev);
nms->bdev_state = bdev_state;
memory_region_add_subregion(sysmem, BLOCK_DEVICE_MEM_BASE, &bdev_state->iomem);
// init the MBX
dev = qdev_new("ipodtouch.mbx");
IPodTouchMBXState *mbx_state = IPOD_TOUCH_MBX(dev);
nms->mbx_state = mbx_state;
memory_region_add_subregion(sysmem, MBX1_MEM_BASE, &mbx_state->iomem1);
memory_region_add_subregion(sysmem, MBX2_MEM_BASE, &mbx_state->iomem2);
qemu_register_reset(ipod_touch_cpu_reset, nms);
}
static void ipod_touch_machine_class_init(ObjectClass *klass, void *data)
{
MachineClass *mc = MACHINE_CLASS(klass);
mc->desc = "iPod Touch";
mc->init = ipod_touch_machine_init;
mc->max_cpus = 1;
mc->default_cpu_type = ARM_CPU_TYPE_NAME("arm1176");
}
static const TypeInfo ipod_touch_machine_info = {
.name = TYPE_IPOD_TOUCH_MACHINE,
.parent = TYPE_MACHINE,
.instance_size = sizeof(IPodTouchMachineState),
.class_size = sizeof(IPodTouchMachineClass),
.class_init = ipod_touch_machine_class_init,
.instance_init = ipod_touch_instance_init,
};
static void ipod_touch_machine_types(void)
{
type_register_static(&ipod_touch_machine_info);
}
type_init(ipod_touch_machine_types)