Compare commits
99
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
501c85c4f8 | ||
|
|
8acdcfc48a | ||
|
|
b775f691fd | ||
|
|
d1b409d869 | ||
|
|
fde3f83c08 | ||
|
|
8905df5bac | ||
|
|
0766bdf7c2 | ||
|
|
7385a19248 | ||
|
|
744efa0bcd | ||
|
|
82fab923c7 | ||
|
|
9fa9bbdb6c | ||
|
|
01bdf8560f | ||
|
|
a5ac6115bc | ||
|
|
beaf938da3 | ||
|
|
c7e9406b3c | ||
|
|
0b62d22a3c | ||
|
|
52395b0875 | ||
|
|
0c313bf18f | ||
|
|
d2236805ff | ||
|
|
21a27376b7 | ||
|
|
c4927a1fa2 | ||
|
|
20d7aa739a | ||
|
|
89214ac6f7 | ||
|
|
51aaa2b3ef | ||
|
|
0f67f8b76c | ||
|
|
e273e9ad77 | ||
|
|
906d257436 | ||
|
|
fac58206ce | ||
|
|
17f9c55b07 | ||
|
|
eb29b5ecc6 | ||
|
|
72816559dc | ||
|
|
5beeaab61b | ||
|
|
76773a441e | ||
|
|
d6d52a81af | ||
|
|
9f3e33e3cf | ||
|
|
5a767d09b5 | ||
|
|
c3b7e709ff | ||
|
|
d71ff21fe7 | ||
|
|
e32c75d630 | ||
|
|
9040190818 | ||
|
|
9e9787d0dd | ||
|
|
9b6a0e0b1c | ||
|
|
13ffc18445 | ||
|
|
1226cf8de1 | ||
|
|
6bf7ee73cc | ||
|
|
f4d8d60530 | ||
|
|
52adb32e07 | ||
|
|
0b241ee0b6 | ||
|
|
f59f20f60e | ||
|
|
d9380bddc9 | ||
|
|
55b2ddb796 | ||
|
|
58ca7abc51 | ||
|
|
d5b4b64df3 | ||
|
|
025b02c2d9 | ||
|
|
19014a5ea2 | ||
|
|
7ebda9567e | ||
|
|
6e47473564 | ||
|
|
dbfdb9f7e7 | ||
|
|
beb1335a71 | ||
|
|
b4743e1139 | ||
|
|
68edc52052 | ||
|
|
b66eef5008 | ||
|
|
fa15006fbb | ||
|
|
7e2bb1c0b0 | ||
|
|
75a3670bd9 | ||
|
|
dc89d3b2bd | ||
|
|
56a309d200 | ||
|
|
88187bb6b6 | ||
|
|
a6c3de5e45 | ||
|
|
7475713599 | ||
|
|
df09005a18 | ||
|
|
d86dddc5be | ||
|
|
c333b6490a | ||
|
|
486848c749 | ||
|
|
1d633a1518 | ||
|
|
5703c933c0 | ||
|
|
609e0cadee | ||
|
|
59c417d6af | ||
|
|
d84e295289 | ||
|
|
c3954c2d4e | ||
|
|
7dde682632 | ||
|
|
3193772219 | ||
|
|
bacfdbbd68 | ||
|
|
d4369e46a0 | ||
|
|
3fc8d202b4 | ||
|
|
2a1917ca1a | ||
|
|
0e84b2429d | ||
|
|
de8154086f | ||
|
|
5d7f4d44b7 | ||
|
|
659aa3fefc | ||
|
|
d4fb06366d | ||
|
|
7f7b4749a5 | ||
|
|
35490d71f7 | ||
|
|
e3cda1b547 | ||
|
|
58247580d8 | ||
|
|
4eb76cd379 | ||
|
|
f11cbe0ec9 | ||
|
|
3d39becbc4 | ||
|
|
94963a4588 |
No files matched your search
@@ -42,3 +42,4 @@ CONFIG_FSL_IMX6UL=y
|
||||
CONFIG_SEMIHOSTING=y
|
||||
CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
|
||||
CONFIG_ALLWINNER_H3=y
|
||||
CONFIG_IPOD_TOUCH=y
|
||||
@@ -1599,3 +1599,54 @@ void AES_decrypt(const unsigned char *in, unsigned char *out,
|
||||
}
|
||||
|
||||
#endif /* AES_ASM */
|
||||
|
||||
void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
|
||||
const unsigned long length, const AES_KEY *key,
|
||||
unsigned char *ivec, const int enc)
|
||||
{
|
||||
|
||||
unsigned long n;
|
||||
unsigned long len = length;
|
||||
unsigned char tmp[AES_BLOCK_SIZE];
|
||||
|
||||
assert(in && out && key && ivec);
|
||||
|
||||
if (enc) {
|
||||
while (len >= AES_BLOCK_SIZE) {
|
||||
for(n=0; n < AES_BLOCK_SIZE; ++n)
|
||||
tmp[n] = in[n] ^ ivec[n];
|
||||
AES_encrypt(tmp, out, key);
|
||||
memcpy(ivec, out, AES_BLOCK_SIZE);
|
||||
len -= AES_BLOCK_SIZE;
|
||||
in += AES_BLOCK_SIZE;
|
||||
out += AES_BLOCK_SIZE;
|
||||
}
|
||||
if (len) {
|
||||
for(n=0; n < len; ++n)
|
||||
tmp[n] = in[n] ^ ivec[n];
|
||||
for(n=len; n < AES_BLOCK_SIZE; ++n)
|
||||
tmp[n] = ivec[n];
|
||||
AES_encrypt(tmp, tmp, key);
|
||||
memcpy(out, tmp, AES_BLOCK_SIZE);
|
||||
memcpy(ivec, tmp, AES_BLOCK_SIZE);
|
||||
}
|
||||
} else {
|
||||
while (len >= AES_BLOCK_SIZE) {
|
||||
memcpy(tmp, in, AES_BLOCK_SIZE);
|
||||
AES_decrypt(in, out, key);
|
||||
for(n=0; n < AES_BLOCK_SIZE; ++n)
|
||||
out[n] ^= ivec[n];
|
||||
memcpy(ivec, tmp, AES_BLOCK_SIZE);
|
||||
len -= AES_BLOCK_SIZE;
|
||||
in += AES_BLOCK_SIZE;
|
||||
out += AES_BLOCK_SIZE;
|
||||
}
|
||||
if (len) {
|
||||
memcpy(tmp, in, AES_BLOCK_SIZE);
|
||||
AES_decrypt(tmp, tmp, key);
|
||||
for(n=0; n < len; ++n)
|
||||
out[n] = tmp[n] ^ ivec[n];
|
||||
memcpy(ivec, tmp, AES_BLOCK_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
BIN
Binary file not shown.
Binary file not shown.
@@ -549,3 +549,8 @@ config ARMSSE
|
||||
select UNIMP
|
||||
select SSE_COUNTER
|
||||
select SSE_TIMER
|
||||
|
||||
config IPOD_TOUCH
|
||||
bool
|
||||
select IPOD_TOUCH
|
||||
select PL192
|
||||
@@ -0,0 +1,678 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu-common.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/ipod_touch.h"
|
||||
#include "hw/arm/exynos4210.h"
|
||||
#include "hw/dma/pl080.h"
|
||||
#include <openssl/aes.h>
|
||||
|
||||
static uint64_t tvout_workaround_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tvout_workaround_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const MemoryRegionOps tvout_workaround_ops = {
|
||||
.read = tvout_workaround_read,
|
||||
.write = tvout_workaround_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
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 uint32_t align_64k_high(uint32_t addr)
|
||||
{
|
||||
return (addr + 0xffffull) & ~0xffffull;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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), IBOOT_BASE);
|
||||
//env->regs[0] = 0x9000000;
|
||||
//cpu_set_pc(CPU(cpu), LLB_BASE + 0x100);
|
||||
//cpu_set_pc(CPU(cpu), VROM_MEM_BASE);
|
||||
}
|
||||
|
||||
/*
|
||||
USB PHYS
|
||||
*/
|
||||
static uint64_t s5l8900_usb_phys_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
s5l8900_usb_phys_s *s = opaque;
|
||||
|
||||
switch(addr)
|
||||
{
|
||||
case 0x0: // OPHYPWR
|
||||
return s->usb_ophypwr;
|
||||
|
||||
case 0x4: // OPHYCLK
|
||||
return s->usb_ophyclk;
|
||||
|
||||
case 0x8: // ORSTCON
|
||||
return s->usb_orstcon;
|
||||
|
||||
case 0x20: // OPHYTUNE
|
||||
return s->usb_ophytune;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "%s: read invalid location 0x%08x\n", __func__, addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void s5l8900_usb_phys_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
s5l8900_usb_phys_s *s = opaque;
|
||||
|
||||
switch(addr)
|
||||
{
|
||||
case 0x0: // OPHYPWR
|
||||
s->usb_ophypwr = val;
|
||||
return;
|
||||
|
||||
case 0x4: // OPHYCLK
|
||||
s->usb_ophyclk = val;
|
||||
return;
|
||||
|
||||
case 0x8: // ORSTCON
|
||||
s->usb_orstcon = val;
|
||||
return;
|
||||
|
||||
case 0x20: // OPHYTUNE
|
||||
s->usb_ophytune = val;
|
||||
return;
|
||||
|
||||
default:
|
||||
//hw_error("%s: write invalid location 0x%08x.\n", __func__, offset);
|
||||
fprintf(stderr, "%s: write invalid location 0x%08x\n", __func__, addr);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps usb_phys_ops = {
|
||||
.read = s5l8900_usb_phys_read,
|
||||
.write = s5l8900_usb_phys_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
/*
|
||||
MBX
|
||||
*/
|
||||
static uint64_t s5l8900_mbx_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: read from location 0x%08x\n", __func__, addr);
|
||||
switch(addr)
|
||||
{
|
||||
case 0x12c:
|
||||
return 0x100;
|
||||
case 0xf00:
|
||||
return (1 << 0x18) | 0x10000; // seems to be some kind of identifier
|
||||
case 0x1020:
|
||||
return 0x10000;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void s5l8900_mbx_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, val, addr);
|
||||
// do nothing
|
||||
}
|
||||
|
||||
static const MemoryRegionOps mbx_ops = {
|
||||
.read = s5l8900_mbx_read,
|
||||
.write = s5l8900_mbx_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_memory_setup(MachineState *machine, MemoryRegion *sysmem, AddressSpace *nsas)
|
||||
{
|
||||
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(machine);
|
||||
DriveInfo *dinfo;
|
||||
|
||||
allocate_ram(sysmem, "sram1", SRAM1_MEM_BASE, 0x10000);
|
||||
|
||||
// allocate UART ram
|
||||
allocate_ram(sysmem, "ram", RAM_MEM_BASE, 0x8000000);
|
||||
|
||||
// load the bootrom (vrom)
|
||||
uint8_t *file_data = NULL;
|
||||
unsigned long fsize;
|
||||
if (g_file_get_contents(nms->bootrom_path, (char **)&file_data, &fsize, NULL)) {
|
||||
allocate_ram(sysmem, "vrom", VROM_MEM_BASE, 0x10000);
|
||||
address_space_rw(nsas, VROM_MEM_BASE, MEMTXATTRS_UNSPECIFIED, (uint8_t *)file_data, fsize, 1);
|
||||
}
|
||||
|
||||
// patch the address table to point to our own routines
|
||||
uint32_t *data = malloc(4);
|
||||
data[0] = LLB_BASE + 0x80;
|
||||
address_space_rw(nsas, 0x2000008c, MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, 4, 1);
|
||||
data[0] = LLB_BASE + 0x100;
|
||||
address_space_rw(nsas, 0x20000090, MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, 4, 1);
|
||||
|
||||
// load iBoot
|
||||
file_data = NULL;
|
||||
if (g_file_get_contents(nms->iboot_path, (char **)&file_data, &fsize, NULL)) {
|
||||
allocate_ram(sysmem, "iboot", IBOOT_BASE, 0x400000);
|
||||
address_space_rw(nsas, IBOOT_BASE, MEMTXATTRS_UNSPECIFIED, (uint8_t *)file_data, fsize, 1);
|
||||
}
|
||||
|
||||
// // load LLB
|
||||
// file_data = NULL;
|
||||
// if (g_file_get_contents("/Users/martijndevos/Documents/ipod_touch_emulation/LLB.n45ap.RELEASE", (char **)&file_data, &fsize, NULL)) {
|
||||
// allocate_ram(sysmem, "llb", LLB_BASE, align_64k_high(fsize));
|
||||
// address_space_rw(nsas, LLB_BASE, MEMTXATTRS_UNSPECIFIED, (uint8_t *)file_data, fsize, 1);
|
||||
// }
|
||||
|
||||
allocate_ram(sysmem, "edgeic", EDGEIC_MEM_BASE, 0x1000);
|
||||
allocate_ram(sysmem, "watchdog", WATCHDOG_MEM_BASE, align_64k_high(0x1));
|
||||
|
||||
allocate_ram(sysmem, "iis0", IIS0_MEM_BASE, align_64k_high(0x1));
|
||||
allocate_ram(sysmem, "iis1", IIS1_MEM_BASE, align_64k_high(0x1));
|
||||
allocate_ram(sysmem, "iis2", IIS2_MEM_BASE, align_64k_high(0x1));
|
||||
|
||||
allocate_ram(sysmem, "mpvd", MPVD_MEM_BASE, 0x70000);
|
||||
allocate_ram(sysmem, "h264bpd", H264BPD_MEM_BASE, 4096);
|
||||
|
||||
allocate_ram(sysmem, "framebuffer", FRAMEBUFFER_MEM_BASE, align_64k_high(4 * 320 * 480));
|
||||
|
||||
// setup 1MB NOR
|
||||
dinfo = drive_get(IF_PFLASH, 0, 0);
|
||||
if (!dinfo) {
|
||||
printf("A NOR image must be given with the -pflash parameter\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
if(!pflash_cfi02_register(NOR_MEM_BASE, "nor", 1024 * 1024, dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, 4096, 1, 2, 0x00bf, 0x273f, 0x0, 0x0, 0x555, 0x2aa, 0)) {
|
||||
printf("Error registering NOR flash!\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static char *ipod_touch_get_bootrom_path(Object *obj, Error **errp)
|
||||
{
|
||||
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(obj);
|
||||
return g_strdup(nms->bootrom_path);
|
||||
}
|
||||
|
||||
static void ipod_touch_set_bootrom_path(Object *obj, const char *value, Error **errp)
|
||||
{
|
||||
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(obj);
|
||||
g_strlcpy(nms->bootrom_path, value, sizeof(nms->bootrom_path));
|
||||
}
|
||||
|
||||
static char *ipod_touch_get_iboot_path(Object *obj, Error **errp)
|
||||
{
|
||||
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(obj);
|
||||
return g_strdup(nms->iboot_path);
|
||||
}
|
||||
|
||||
static void ipod_touch_set_iboot_path(Object *obj, const char *value, Error **errp)
|
||||
{
|
||||
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(obj);
|
||||
g_strlcpy(nms->iboot_path, value, sizeof(nms->iboot_path));
|
||||
}
|
||||
|
||||
static char *ipod_touch_get_nand_path(Object *obj, Error **errp)
|
||||
{
|
||||
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(obj);
|
||||
return g_strdup(nms->nand_path);
|
||||
}
|
||||
|
||||
static void ipod_touch_set_nand_path(Object *obj, const char *value, Error **errp)
|
||||
{
|
||||
IPodTouchMachineState *nms = IPOD_TOUCH_MACHINE(obj);
|
||||
g_strlcpy(nms->nand_path, value, sizeof(nms->nand_path));
|
||||
}
|
||||
|
||||
static void ipod_touch_instance_init(Object *obj)
|
||||
{
|
||||
object_property_add_str(obj, "bootrom", ipod_touch_get_bootrom_path, ipod_touch_set_bootrom_path);
|
||||
object_property_set_description(obj, "bootrom", "Path to the S5L8900 bootrom binary");
|
||||
|
||||
object_property_add_str(obj, "iboot", ipod_touch_get_iboot_path, ipod_touch_set_iboot_path);
|
||||
object_property_set_description(obj, "iboot", "Path to the iBoot binary");
|
||||
|
||||
object_property_add_str(obj, "nand", ipod_touch_get_nand_path, ipod_touch_set_nand_path);
|
||||
object_property_set_description(obj, "nand", "Path to the NAND files");
|
||||
}
|
||||
|
||||
static inline qemu_irq s5l8900_get_irq(IPodTouchMachineState *s, int n)
|
||||
{
|
||||
return s->irq[n / S5L8900_VIC_SIZE][n % S5L8900_VIC_SIZE];
|
||||
}
|
||||
|
||||
static uint32_t s5l8900_usb_hwcfg[] = {
|
||||
0,
|
||||
0x7a8f60d0,
|
||||
0x082000e8,
|
||||
0x01f08024
|
||||
};
|
||||
|
||||
static void ipod_touch_key_event(void *opaque, int keycode)
|
||||
{
|
||||
bool do_irq = false;
|
||||
int gpio_group = 0, gpio_selector = 0;
|
||||
|
||||
IPodTouchMultitouchState *s = (IPodTouchMultitouchState *)opaque;
|
||||
if(keycode == 25 || keycode == 153) {
|
||||
// power button
|
||||
gpio_group = GPIO_BUTTON_POWER_IRQ / NUM_GPIO_PINS;
|
||||
gpio_selector = GPIO_BUTTON_POWER_IRQ % NUM_GPIO_PINS;
|
||||
|
||||
if(keycode == 25 && (s->gpio_state->gpio_state & (1 << (GPIO_BUTTON_POWER & 0xf))) == 0) {
|
||||
s->gpio_state->gpio_state |= (1 << (GPIO_BUTTON_POWER & 0xf));
|
||||
do_irq = true;
|
||||
}
|
||||
else if(keycode == 153) {
|
||||
s->gpio_state->gpio_state &= ~(1 << (GPIO_BUTTON_POWER & 0xf));
|
||||
do_irq = true;
|
||||
}
|
||||
}
|
||||
else if(keycode == 35 || keycode == 163) {
|
||||
// home button
|
||||
gpio_group = GPIO_BUTTON_HOME_IRQ / NUM_GPIO_PINS;
|
||||
gpio_selector = GPIO_BUTTON_HOME_IRQ % NUM_GPIO_PINS;
|
||||
|
||||
if(keycode == 35 && (s->gpio_state->gpio_state & (1 << (GPIO_BUTTON_HOME & 0xf))) == 0) {
|
||||
s->gpio_state->gpio_state |= (1 << (GPIO_BUTTON_HOME & 0xf));
|
||||
do_irq = true;
|
||||
}
|
||||
else if(keycode == 163) {
|
||||
s->gpio_state->gpio_state &= ~(1 << (GPIO_BUTTON_HOME & 0xf));
|
||||
do_irq = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(do_irq) {
|
||||
s->sysic->gpio_int_status[gpio_group] |= (1 << gpio_selector);
|
||||
qemu_irq_raise(s->sysic->gpio_irqs[gpio_group]);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// 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, S5L8900_TIMER1_IRQ));
|
||||
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));
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_GPIO_G5_IRQ));
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_GPIO_G6_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);
|
||||
|
||||
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);
|
||||
sysbus_create_simple("s5l8900spi", SPI0_MEM_BASE, s5l8900_get_irq(nms, S5L8900_SPI0_IRQ));
|
||||
|
||||
set_spi_base(1);
|
||||
sysbus_create_simple("s5l8900spi", SPI1_MEM_BASE, s5l8900_get_irq(nms, S5L8900_SPI1_IRQ));
|
||||
|
||||
set_spi_base(2);
|
||||
dev = sysbus_create_simple("s5l8900spi", SPI2_MEM_BASE, s5l8900_get_irq(nms, S5L8900_SPI2_IRQ));
|
||||
S5L8900SPIState *spi2_state = S5L8900SPI(dev);
|
||||
spi2_state->mt->sysic = sysic_state;
|
||||
spi2_state->mt->gpio_state = gpio_state;
|
||||
nms->spi2_state = spi2_state;
|
||||
|
||||
ipod_touch_memory_setup(machine, sysmem, nsas);
|
||||
|
||||
// init LCD
|
||||
dev = qdev_new("ipodtouch.lcd");
|
||||
IPodTouchLCDState *lcd_state = IPOD_TOUCH_LCD(dev);
|
||||
lcd_state->sysmem = sysmem;
|
||||
lcd_state->mt = spi2_state->mt;
|
||||
nms->lcd_state = lcd_state;
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_LCD_IRQ));
|
||||
memory_region_add_subregion(sysmem, DISPLAY_MEM_BASE, &lcd_state->iomem);
|
||||
sysbus_realize(busdev, &error_fatal);
|
||||
|
||||
// init AES engine
|
||||
dev = qdev_new("ipodtouch.aes");
|
||||
S5L8900AESState *aes_state = IPOD_TOUCH_AES(dev);
|
||||
nms->aes_state = aes_state;
|
||||
memory_region_add_subregion(sysmem, AES_MEM_BASE, &aes_state->iomem);
|
||||
|
||||
// init SHA1 engine
|
||||
dev = qdev_new("ipodtouch.sha1");
|
||||
S5L8900SHA1State *sha1_state = IPOD_TOUCH_SHA1(dev);
|
||||
nms->sha1_state = sha1_state;
|
||||
memory_region_add_subregion(sysmem, SHA1_MEM_BASE, &sha1_state->iomem);
|
||||
|
||||
// init 8900 engine
|
||||
MemoryRegion *iomem = g_new(MemoryRegion, 1);
|
||||
memory_region_init_io(iomem, OBJECT(s), &engine_8900_ops, nsas, "8900engine", 0x100);
|
||||
memory_region_add_subregion(sysmem, ENGINE_8900_MEM_BASE, iomem);
|
||||
|
||||
// init NAND flash
|
||||
dev = qdev_new("itnand");
|
||||
ITNandState *nand_state = ITNAND(dev);
|
||||
nand_state->nand_path = &nms->nand_path;
|
||||
nms->nand_state = nand_state;
|
||||
memory_region_add_subregion(sysmem, NAND_MEM_BASE, &nand_state->iomem);
|
||||
|
||||
// init NAND ECC module
|
||||
dev = qdev_new("itnand_ecc");
|
||||
ITNandECCState *nand_ecc_state = ITNANDECC(dev);
|
||||
nms->nand_ecc_state = nand_ecc_state;
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_NAND_ECC_IRQ));
|
||||
memory_region_add_subregion(sysmem, NAND_ECC_MEM_BASE, &nand_ecc_state->iomem);
|
||||
|
||||
// init USB OTG
|
||||
dev = ipod_touch_init_usb_otg(nms->irq[0][13], s5l8900_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 USB PHYS
|
||||
s5l8900_usb_phys_s *usb_state = malloc(sizeof(s5l8900_usb_phys_s));
|
||||
nms->usb_phys = usb_state;
|
||||
usb_state->usb_ophypwr = 0;
|
||||
usb_state->usb_ophyclk = 0;
|
||||
usb_state->usb_orstcon = 0;
|
||||
usb_state->usb_ophytune = 0;
|
||||
|
||||
iomem = g_new(MemoryRegion, 1);
|
||||
memory_region_init_io(iomem, OBJECT(s), &usb_phys_ops, usb_state, "usbphys", 0x40);
|
||||
memory_region_add_subregion(sysmem, USBPHYS_MEM_BASE, iomem);
|
||||
|
||||
// init 8900 OPS
|
||||
allocate_ram(sysmem, "8900ops", LLB_BASE, 0x1000);
|
||||
|
||||
// patch the instructions related to 8900 decryption
|
||||
uint32_t *data = malloc(sizeof(uint32_t) * 2);
|
||||
data[0] = 0xe3b00001; // MOVS R0, #1
|
||||
data[1] = 0xe12fff1e; // BX LR
|
||||
address_space_rw(nsas, LLB_BASE + 0x80, MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, sizeof(uint32_t) * 2, 1);
|
||||
|
||||
/*
|
||||
load the decryption logic in memory. These bytes correspond to the following ARMv6 instructions:
|
||||
|
||||
LDR r1,[pc,#0x100]
|
||||
STR r0,[r1]
|
||||
MOVS R0, #1
|
||||
BX lr
|
||||
*/
|
||||
data = malloc(sizeof(uint32_t) * 4);
|
||||
data[0] = 0xE59F1100; // LDR r1,[pc,#0x100]
|
||||
data[1] = 0xE5810000; // STR r0,[r1]
|
||||
data[2] = 0xE3B00001; // MOVS R0, #1
|
||||
data[3] = 0xE12FFF1E; // BX lr
|
||||
address_space_rw(nsas, LLB_BASE + 0x100, MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, sizeof(uint32_t) * 4, 1);
|
||||
|
||||
// contains some constants
|
||||
data = malloc(4);
|
||||
data[0] = ENGINE_8900_MEM_BASE; // engine base address
|
||||
address_space_rw(nsas, LLB_BASE + 0x208, MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, 4, 1);
|
||||
|
||||
// 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->iomem);
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
sysbus_realize(busdev, &error_fatal);
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_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_MEM_BASE, &pl080_2->iomem);
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
sysbus_realize(busdev, &error_fatal);
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_DMAC1_IRQ));
|
||||
|
||||
// Init I2C
|
||||
dev = qdev_new("ipodtouch.i2c");
|
||||
IPodTouchI2CState *i2c_state = IPOD_TOUCH_I2C(dev);
|
||||
nms->i2c0_state = i2c_state;
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_I2C0_IRQ));
|
||||
memory_region_add_subregion(sysmem, I2C0_MEM_BASE, &i2c_state->iomem);
|
||||
|
||||
// init the accelerometer
|
||||
I2CSlave *accelerometer = i2c_slave_create_simple(i2c_state->bus, "lis302dl", 0x1D);
|
||||
|
||||
dev = qdev_new("ipodtouch.i2c");
|
||||
i2c_state = IPOD_TOUCH_I2C(dev);
|
||||
nms->i2c1_state = i2c_state;
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_I2C1_IRQ));
|
||||
memory_region_add_subregion(sysmem, I2C1_MEM_BASE, &i2c_state->iomem);
|
||||
|
||||
// init the PMU
|
||||
I2CSlave *pmu = i2c_slave_create_simple(i2c_state->bus, "pcf50633", 0x73);
|
||||
|
||||
// init the ADM
|
||||
dev = qdev_new("ipodtouch.adm");
|
||||
IPodTouchADMState *adm_state = IPOD_TOUCH_ADM(dev);
|
||||
adm_state->nand_state = nand_state;
|
||||
nms->adm_state = adm_state;
|
||||
object_property_set_link(OBJECT(dev), "downstream", OBJECT(sysmem), &error_fatal);
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
sysbus_realize(busdev, &error_fatal);
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_ADM_IRQ));
|
||||
memory_region_add_subregion(sysmem, ADM_MEM_BASE, &adm_state->iomem);
|
||||
|
||||
// init MBX
|
||||
iomem = g_new(MemoryRegion, 1);
|
||||
memory_region_init_io(iomem, OBJECT(nms), &mbx_ops, NULL, "mbx", 0x1000000);
|
||||
memory_region_add_subregion(sysmem, MBX_MEM_BASE, iomem);
|
||||
|
||||
// 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 instances
|
||||
dev = qdev_new("ipodtouch.tvout");
|
||||
IPodTouchTVOutState *tvout_state = IPOD_TOUCH_TVOUT(dev);
|
||||
tvout_state->index = 1;
|
||||
nms->tvout1_state = tvout_state;
|
||||
memory_region_add_subregion(sysmem, TVOUT1_MEM_BASE, &tvout_state->iomem);
|
||||
|
||||
dev = qdev_new("ipodtouch.tvout");
|
||||
tvout_state = IPOD_TOUCH_TVOUT(dev);
|
||||
tvout_state->index = 2;
|
||||
nms->tvout2_state = tvout_state;
|
||||
memory_region_add_subregion(sysmem, TVOUT2_MEM_BASE, &tvout_state->iomem);
|
||||
busdev = SYS_BUS_DEVICE(dev);
|
||||
sysbus_connect_irq(busdev, 0, s5l8900_get_irq(nms, S5L8900_TVOUT_SDO_IRQ));
|
||||
|
||||
dev = qdev_new("ipodtouch.tvout");
|
||||
tvout_state = IPOD_TOUCH_TVOUT(dev);
|
||||
tvout_state->index = 3;
|
||||
nms->tvout3_state = tvout_state;
|
||||
memory_region_add_subregion(sysmem, TVOUT3_MEM_BASE, &tvout_state->iomem);
|
||||
|
||||
// setup workaround for TVOut
|
||||
iomem = g_new(MemoryRegion, 1);
|
||||
memory_region_init_io(iomem, OBJECT(nms), &tvout_workaround_ops, NULL, "tvoutworkaround", 0x4);
|
||||
memory_region_add_subregion(sysmem, TVOUT_WORKAROUND_MEM_BASE, iomem);
|
||||
|
||||
qemu_register_reset(ipod_touch_cpu_reset, nms);
|
||||
|
||||
qemu_add_kbd_event_handler(ipod_touch_key_event, spi2_state->mt);
|
||||
}
|
||||
|
||||
static void ipod_touch_machine_class_init(ObjectClass *obj, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(obj);
|
||||
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)
|
||||
@@ -0,0 +1 @@
|
||||
#include "hw/arm/ipod_touch_8900_engine.h"
|
||||
@@ -0,0 +1,276 @@
|
||||
#include "hw/arm/ipod_touch_adm.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "hw/arm/ipod_touch_nand.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
static void set_bank(ITNandState *s, uint8_t activate_bank) {
|
||||
for(int bank = 0; bank < 8; bank++) {
|
||||
// clear bit, toggle if it is active
|
||||
s->fmctrl0 &= ~(1 << (bank + 1));
|
||||
if(bank == activate_bank) {
|
||||
s->fmctrl0 ^= 1 << (bank + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t ipod_touch_adm_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
//IPodTouchADMState *s = (IPodTouchADMState *)opaque;
|
||||
|
||||
//fprintf(stderr, "s5l8900_adm_read(): offset = 0x%08x\n", offset);
|
||||
|
||||
switch (offset) {
|
||||
case ADM_CTRL: // this seems to be the control register
|
||||
return 0x2; // this seems to indicate that the device is ready
|
||||
case ADM_CTRL2:
|
||||
return 0x10; // indicates that a upload event has been finished
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_adm_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
|
||||
{
|
||||
IPodTouchADMState *s = (IPodTouchADMState *)opaque;
|
||||
|
||||
//fprintf(stderr, "s5l8900_adm_write: offset = 0x%08x, val = 0x%08x\n", offset, value);
|
||||
|
||||
switch(offset) {
|
||||
case ADM_CTRL:
|
||||
if(value == 0x3) {
|
||||
// some kind of start-up command?
|
||||
// write some bits to data2_sec_addr to indicate that the device is started
|
||||
uint32_t *data = (uint32_t *) malloc(sizeof(uint32_t));
|
||||
data[0] = 0x50;
|
||||
address_space_rw(&s->downstream_as, s->data2_sec_addr, MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, 4, 1);
|
||||
|
||||
// dunno, write some bytes to data4_sec_addr to indicate that the NAND banks are ready
|
||||
data = (uint32_t *) malloc(sizeof(uint32_t) * 8);
|
||||
for(int i = 0; i < 8; i++) {
|
||||
data[i] = NAND_CHIP_ID;
|
||||
}
|
||||
|
||||
address_space_rw(&s->downstream_as, s->data3_sec_addr, MEMTXATTRS_UNSPECIFIED, (uint8_t *)data, 8 * sizeof(uint32_t), 1);
|
||||
}
|
||||
break;
|
||||
case ADM_CTRL2:
|
||||
if(value == 0x2) {
|
||||
// read the command and initialize the right device
|
||||
uint32_t page;
|
||||
uint16_t num_pages;
|
||||
uint8_t bank;
|
||||
uint32_t *buf = malloc(80);
|
||||
address_space_read(&s->downstream_as, s->data2_sec_addr + 0x1104 + 0x24, MEMTXATTRS_UNSPECIFIED, buf, 80);
|
||||
int cmd = *buf;
|
||||
// printf("Setting command: 0x%08x\n", cmd);
|
||||
// for(int i = 0; i < 20; i++) {
|
||||
// printf("0x%08x ", buf[i]);
|
||||
// }
|
||||
// printf("\n");
|
||||
switch(cmd) {
|
||||
case 0x200:
|
||||
// read multiple pages simultaneously from the same bank
|
||||
s->nand_state->reading_multiple_pages = true;
|
||||
buf = malloc(2);
|
||||
address_space_read(&s->downstream_as, s->data2_sec_addr + 0x1104 + 0x28, MEMTXATTRS_UNSPECIFIED, buf, 2);
|
||||
num_pages = *(uint16_t*)buf;
|
||||
num_pages = ( (((num_pages) >> 8) & 0x00FF) | (((num_pages) << 8) & 0xFF00) ); // swap endianness
|
||||
//printf("Reading %d pages at once, ", num_pages);
|
||||
|
||||
buf = malloc(4);
|
||||
address_space_read(&s->downstream_as, s->data2_sec_addr + 0x1104 + 0x244, MEMTXATTRS_UNSPECIFIED, buf, 4);
|
||||
page = *buf;
|
||||
page = ((page>>24)&0xff) | // move byte 3 to byte 0
|
||||
((page<<8)&0xff0000) | // move byte 1 to byte 2
|
||||
((page>>8)&0xff00) | // move byte 2 to byte 1
|
||||
((page<<24)&0xff000000); // byte 0 to byte 3
|
||||
//printf("starting with page %d\n", page);
|
||||
|
||||
uint16_t ops = num_pages / 8;
|
||||
for(int op = 0; op < ops; op++) {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
s->nand_state->pages_to_read[op * 8 + i] = page;
|
||||
s->nand_state->banks_to_read[op * 8 + i] = i;
|
||||
}
|
||||
page++;
|
||||
}
|
||||
|
||||
s->nand_state->fmdnum = (num_pages * 0x800);
|
||||
s->nand_state->cur_bank_reading = -1;
|
||||
|
||||
for(int i = 0; i < num_pages; i++) {
|
||||
uint8_t *sbuf = malloc(0xC);
|
||||
sbuf[10] = 0xFF;
|
||||
address_space_rw(&s->downstream_as, s->data3_sec_addr + i * 0xC, MEMTXATTRS_UNSPECIFIED, sbuf, 0xC, 1);
|
||||
}
|
||||
|
||||
break;
|
||||
case 0x300:
|
||||
// seems to be the NAND read command, read the page(s) + bank and instruct the flash device
|
||||
s->nand_state->reading_multiple_pages = false;
|
||||
buf = malloc(2);
|
||||
address_space_read(&s->downstream_as, s->data2_sec_addr + 0x1104 + 0x28, MEMTXATTRS_UNSPECIFIED, buf, 2);
|
||||
num_pages = *(uint16_t*)buf;
|
||||
num_pages = ( (((num_pages) >> 8) & 0x00FF) | (((num_pages) << 8) & 0xFF00) ); // swap endianness
|
||||
if(num_pages == 1) {
|
||||
// TODO this can probably be refactored to re-use the logic to read multiple pages!
|
||||
address_space_read(&s->downstream_as, s->data2_sec_addr + 0x1104 + 0x44, MEMTXATTRS_UNSPECIFIED, buf, 1);
|
||||
bank = *(uint8_t*)buf;
|
||||
|
||||
buf = malloc(4);
|
||||
address_space_read(&s->downstream_as, s->data2_sec_addr + 0x1104 + 0x244, MEMTXATTRS_UNSPECIFIED, buf, 4);
|
||||
page = *buf;
|
||||
page = ((page>>24)&0xff) | // move byte 3 to byte 0
|
||||
((page<<8)&0xff0000) | // move byte 1 to byte 2
|
||||
((page>>8)&0xff00) | // move byte 2 to byte 1
|
||||
((page<<24)&0xff000000); // byte 0 to byte 3
|
||||
//printf("Reading single page: %d (bank: %d)\n", page, bank);
|
||||
|
||||
// set the bank, page, and operation.
|
||||
set_bank(s->nand_state, bank);
|
||||
memory_region_dispatch_write(&s->nand_state->iomem, NAND_FMDNUM, 0x800 - 1, MO_32, MEMTXATTRS_UNSPECIFIED);
|
||||
memory_region_dispatch_write(&s->nand_state->iomem, NAND_FMADDR0, page << 16, MO_32, MEMTXATTRS_UNSPECIFIED);
|
||||
memory_region_dispatch_write(&s->nand_state->iomem, NAND_FMADDR1, (page >> 16) & 0xFF, MO_32, MEMTXATTRS_UNSPECIFIED);
|
||||
memory_region_dispatch_write(&s->nand_state->iomem, NAND_CMD, NAND_CMD_READ, MO_32, MEMTXATTRS_UNSPECIFIED);
|
||||
|
||||
// write the spare of the page to the 3rd data section
|
||||
nand_set_buffered_page(s->nand_state, page);
|
||||
address_space_rw(&s->downstream_as, s->data3_sec_addr, MEMTXATTRS_UNSPECIFIED, (uint8_t *)s->nand_state->page_spare_buffer, NAND_BYTES_PER_SPARE, 1);
|
||||
}
|
||||
else if(num_pages > 1) {
|
||||
// read scattered pages
|
||||
// printf("Reading %d scattered pages\n", num_pages);
|
||||
s->nand_state->reading_multiple_pages = true;
|
||||
for(int i = 0; i < num_pages; i++) {
|
||||
buf = malloc(4);
|
||||
address_space_read(&s->downstream_as, s->data2_sec_addr + 0x1104 + 0x244 + 4 * i, MEMTXATTRS_UNSPECIFIED, buf, 4);
|
||||
page = *buf;
|
||||
page = ((page>>24)&0xff) | // move byte 3 to byte 0
|
||||
((page<<8)&0xff0000) | // move byte 1 to byte 2
|
||||
((page>>8)&0xff00) | // move byte 2 to byte 1
|
||||
((page<<24)&0xff000000); // byte 0 to byte 3
|
||||
|
||||
buf = malloc(1);
|
||||
address_space_read(&s->downstream_as, s->data2_sec_addr + 0x1104 + 0x44 + i, MEMTXATTRS_UNSPECIFIED, buf, 1);
|
||||
bank = *(uint8_t*)buf;
|
||||
// printf("Page: %d, bank: %d\n", page, bank);
|
||||
|
||||
s->nand_state->pages_to_read[i] = page;
|
||||
s->nand_state->banks_to_read[i] = bank;
|
||||
}
|
||||
|
||||
s->nand_state->fmdnum = (num_pages * 0x800);
|
||||
s->nand_state->cur_bank_reading = -1;
|
||||
|
||||
for(int i = 0; i < num_pages; i++) {
|
||||
uint8_t *sbuf = malloc(0xC);
|
||||
sbuf[10] = 0xFF;
|
||||
address_space_rw(&s->downstream_as, s->data3_sec_addr + i * 0xC, MEMTXATTRS_UNSPECIFIED, sbuf, 0xC, 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x500:
|
||||
// writing a page
|
||||
buf = malloc(1);
|
||||
address_space_read(&s->downstream_as, s->data2_sec_addr + 0x1104 + 0x44, MEMTXATTRS_UNSPECIFIED, buf, 1);
|
||||
bank = *(uint8_t*)buf;
|
||||
|
||||
buf = malloc(4);
|
||||
address_space_read(&s->downstream_as, s->data2_sec_addr + 0x1104 + 0x244, MEMTXATTRS_UNSPECIFIED, buf, 4);
|
||||
page = *buf;
|
||||
page = ((page>>24)&0xff) | // move byte 3 to byte 0
|
||||
((page<<8)&0xff0000) | // move byte 1 to byte 2
|
||||
((page>>8)&0xff00) | // move byte 2 to byte 1
|
||||
((page<<24)&0xff000000); // byte 0 to byte 3
|
||||
|
||||
// set the bank, page, and operation.
|
||||
//printf("Activating bank for writing: %d, page: %d\n", bank, page);
|
||||
set_bank(s->nand_state, bank);
|
||||
nand_set_buffered_page(s->nand_state, page);
|
||||
s->nand_state->fmdnum = NAND_BYTES_PER_PAGE;
|
||||
s->nand_state->is_writing = true;
|
||||
break;
|
||||
default:
|
||||
printf("Unrecognized ADM command: %d\n", cmd);
|
||||
break;
|
||||
}
|
||||
qemu_irq_raise(s->irq);
|
||||
}
|
||||
if((value & 0x2) == 0) {
|
||||
qemu_irq_lower(s->irq);
|
||||
}
|
||||
break;
|
||||
case ADM_CODE_SEC_ADDR:
|
||||
s->code_sec_addr = value;
|
||||
break;
|
||||
case ADM_DATA1_SEC_ADDR:
|
||||
s->data1_sec_addr = value;
|
||||
break;
|
||||
case ADM_DATA2_SEC_ADDR:
|
||||
s->data2_sec_addr = value;
|
||||
break;
|
||||
case ADM_DATA3_SEC_ADDR:
|
||||
s->data3_sec_addr = value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ipod_touch_adm_ops = {
|
||||
.read = ipod_touch_adm_read,
|
||||
.write = ipod_touch_adm_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_adm_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
IPodTouchADMState *s = IPOD_TOUCH_ADM(dev);
|
||||
|
||||
if (!s->downstream) {
|
||||
error_setg(errp, "ADM 'downstream' link not set");
|
||||
return;
|
||||
}
|
||||
|
||||
address_space_init(&s->downstream_as, s->downstream, "adm-downstream");
|
||||
}
|
||||
|
||||
static Property adm_properties[] = {
|
||||
DEFINE_PROP_LINK("downstream", IPodTouchADMState, downstream,
|
||||
TYPE_MEMORY_REGION, MemoryRegion *),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void ipod_touch_adm_init(Object *obj)
|
||||
{
|
||||
IPodTouchADMState *s = IPOD_TOUCH_ADM(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &ipod_touch_adm_ops, s, TYPE_IPOD_TOUCH_ADM, 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
}
|
||||
|
||||
static void ipod_touch_adm_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
dc->realize = ipod_touch_adm_realize;
|
||||
device_class_set_props(dc, adm_properties);
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_adm_type_info = {
|
||||
.name = TYPE_IPOD_TOUCH_ADM,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchADMState),
|
||||
.instance_init = ipod_touch_adm_init,
|
||||
.class_init = ipod_touch_adm_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_adm_register_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_adm_type_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_adm_register_types)
|
||||
@@ -0,0 +1,135 @@
|
||||
#include "hw/arm/ipod_touch_aes.h"
|
||||
|
||||
static uint64_t s5l8900_aes_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
struct S5L8900AESState *aesop = (struct S5L8900AESState *)opaque;
|
||||
|
||||
switch(offset) {
|
||||
case AES_STATUS:
|
||||
return aesop->status;
|
||||
default:
|
||||
//fprintf(stderr, "%s: UNMAPPED AES_ADDR @ offset 0x%08x\n", __FUNCTION__, offset);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void s5l8900_aes_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
|
||||
{
|
||||
struct S5L8900AESState *aesop = (struct S5L8900AESState *)opaque;
|
||||
static uint8_t keylenop = 0;
|
||||
|
||||
uint8_t *inbuf;
|
||||
uint8_t *buf;
|
||||
|
||||
// fprintf(stderr, "%s: offset 0x%08x value 0x%08x\n", __FUNCTION__, offset, value);
|
||||
|
||||
switch(offset) {
|
||||
case AES_GO:
|
||||
inbuf = (uint8_t *)malloc(aesop->insize);
|
||||
cpu_physical_memory_read((aesop->inaddr - 0x80000000), inbuf, aesop->insize);
|
||||
|
||||
switch(aesop->keytype) {
|
||||
case AESGID:
|
||||
fprintf(stderr, "%s: No support for GID key\n", __func__);
|
||||
return;
|
||||
case AESUID:
|
||||
AES_set_decrypt_key(key_uid, sizeof(key_uid) * 8, &aesop->decryptKey);
|
||||
break;
|
||||
case AESCustom:
|
||||
AES_set_decrypt_key((uint8_t *)aesop->custkey, 0x20 * 8, &aesop->decryptKey);
|
||||
break;
|
||||
}
|
||||
|
||||
buf = (uint8_t *) malloc(aesop->insize);
|
||||
|
||||
AES_cbc_encrypt(inbuf, buf, aesop->insize, &aesop->decryptKey, (uint8_t *)aesop->ivec, aesop->operation);
|
||||
|
||||
cpu_physical_memory_write((aesop->outaddr - 0x80000000), buf, aesop->insize);
|
||||
memset(aesop->custkey, 0, 0x20);
|
||||
memset(aesop->ivec, 0, 0x10);
|
||||
free(inbuf);
|
||||
free(buf);
|
||||
keylenop = 0;
|
||||
aesop->outsize = aesop->insize;
|
||||
aesop->status = 0xf;
|
||||
break;
|
||||
case AES_KEYLEN:
|
||||
if(keylenop == 1) {
|
||||
aesop->operation = value;
|
||||
}
|
||||
keylenop++;
|
||||
aesop->keylen = value;
|
||||
break;
|
||||
case AES_INADDR:
|
||||
aesop->inaddr = value;
|
||||
break;
|
||||
case AES_INSIZE:
|
||||
aesop->insize = value;
|
||||
break;
|
||||
case AES_OUTSIZE:
|
||||
aesop->outsize = value;
|
||||
break;
|
||||
case AES_OUTADDR:
|
||||
aesop->outaddr = value;
|
||||
break;
|
||||
case AES_TYPE:
|
||||
aesop->keytype = value;
|
||||
break;
|
||||
case AES_KEY_REG ... ((AES_KEY_REG + AES_KEYSIZE) - 1):
|
||||
{
|
||||
uint8_t idx = (offset - AES_KEY_REG) / 4;
|
||||
aesop->custkey[idx] |= value;
|
||||
break;
|
||||
}
|
||||
case AES_IV_REG ... ((AES_IV_REG + AES_IVSIZE) -1 ):
|
||||
{
|
||||
uint8_t idx = (offset - AES_IV_REG) / 4;
|
||||
aesop->ivec[idx] |= value;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
//fprintf(stderr, "%s: UNMAPPED AES_ADDR @ offset 0x%08x - 0x%08x\n", __FUNCTION__, offset, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps aes_ops = {
|
||||
.read = s5l8900_aes_read,
|
||||
.write = s5l8900_aes_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void s5l8900_aes_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
S5L8900AESState *s = IPOD_TOUCH_AES(dev);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &aes_ops, s, "aes", 0x100);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
|
||||
memset(&s->custkey, 0, 8 * sizeof(uint32_t));
|
||||
memset(&s->ivec, 0, 4 * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
static void s5l8900_aes_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_aes_info = {
|
||||
.name = TYPE_IPOD_TOUCH_AES,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(S5L8900AESState),
|
||||
.instance_init = s5l8900_aes_init,
|
||||
.class_init = s5l8900_aes_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_machine_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_aes_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_machine_types)
|
||||
@@ -0,0 +1,50 @@
|
||||
#include "hw/arm/ipod_touch_chipid.h"
|
||||
|
||||
static uint64_t ipod_touch_chipid_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: offset = 0x%08x\n", __func__, addr);
|
||||
|
||||
switch (addr) {
|
||||
case 0x4:
|
||||
return (CHIP_REVISION << 24);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ipod_touch_chipid_ops = {
|
||||
.read = ipod_touch_chipid_read,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_chipid_init(Object *obj)
|
||||
{
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
IPodTouchChipIDState *s = IPOD_TOUCH_CHIPID(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &ipod_touch_chipid_ops, s, TYPE_IPOD_TOUCH_CHIPID, 0x10);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
}
|
||||
|
||||
static void ipod_touch_chipid_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_chipid_type_info = {
|
||||
.name = TYPE_IPOD_TOUCH_CHIPID,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchChipIDState),
|
||||
.instance_init = ipod_touch_chipid_init,
|
||||
.class_init = ipod_touch_chipid_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_chipid_register_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_chipid_type_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_chipid_register_types)
|
||||
@@ -0,0 +1,112 @@
|
||||
#include "hw/arm/ipod_touch_clock.h"
|
||||
|
||||
static void s5l8900_clock1_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
IPodTouchClockState *s = (struct IPodTouchClockState *) opaque;
|
||||
|
||||
switch (addr) {
|
||||
case CLOCK1_PLLLOCK:
|
||||
s->plllock = val;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t s5l8900_clock1_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
IPodTouchClockState *s = (struct IPodTouchClockState *) opaque;
|
||||
|
||||
switch (addr) {
|
||||
case CLOCK1_CONFIG0:
|
||||
return s->config0;
|
||||
case CLOCK1_CONFIG1:
|
||||
return s->config1;
|
||||
case CLOCK1_CONFIG2:
|
||||
return s->config2;
|
||||
case CLOCK1_PLL0CON:
|
||||
return s->pll0con;
|
||||
case CLOCK1_PLL1CON:
|
||||
return s->pll1con;
|
||||
case CLOCK1_PLL2CON:
|
||||
return s->pll2con;
|
||||
case CLOCK1_PLL3CON:
|
||||
return s->pll3con;
|
||||
case CLOCK1_PLLLOCK:
|
||||
return s->plllock;
|
||||
case CLOCK1_PLLMODE:
|
||||
return 0x000a003a; // extracted from iPod Touch
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps clock1_ops = {
|
||||
.read = s5l8900_clock1_read,
|
||||
.write = s5l8900_clock1_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void s5l8900_clock_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
IPodTouchClockState *s = IPOD_TOUCH_CLOCK(dev);
|
||||
|
||||
s->plllock = 1 | 2 | 4 | 8;
|
||||
|
||||
uint64_t config0 = 0x0;
|
||||
config0 |= (1 << 12); // set the clock PPL to index 1
|
||||
config0 |= (1 << 24); // indicate that we have a memory divisor
|
||||
config0 |= (2 << 16); // set the memory divisor to two
|
||||
s->config0 = config0;
|
||||
|
||||
uint64_t config1 = 0x0;
|
||||
config1 |= (1 << 12); // set the bus PPL to index 1
|
||||
config1 |= (1 << 24); // indicate that we have a bus divisor
|
||||
config1 |= (3 << 16); // set the bus divisor to three
|
||||
config1 |= (1 << 8); // indicate that unknown has a divisor
|
||||
config1 |= 3; // set the unknown divisor 1 to 3
|
||||
config1 |= (0 << 4); // set the unknown divisor 2 to 0
|
||||
config1 |= (1 << 20); // set the peripheral factor to 1
|
||||
|
||||
config1 |= (1 << 14); // unknown
|
||||
config1 |= (1 << 28); // set some PPL index to 1
|
||||
config1 |= (1 << 30); // unknown
|
||||
s->config1 = config1;
|
||||
|
||||
uint64_t config2 = 0x0;
|
||||
config2 |= (3 << 28); // set the peripheral PPL to index 3
|
||||
config2 |= (1 << 24); // indicate that the display has a divisor
|
||||
config2 |= (1 << 16); // set the display divisor to 1
|
||||
s->config2 = config2;
|
||||
|
||||
// set the PLL configurations (MDIV, PDIV, SDIV)
|
||||
s->pll0con = (80 << 8) | (8 << 24) | 0;
|
||||
s->pll1con = (103 << 8) | (6 << 24) | 0;
|
||||
s->pll2con = (156 << 8) | (53 << 24) | 2;
|
||||
s->pll3con = (72 << 8) | (8 << 24) | 1;
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &clock1_ops, s, "clock", 0x1000);
|
||||
}
|
||||
|
||||
static void s5l8900_clock_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_clock_info = {
|
||||
.name = TYPE_IPOD_TOUCH_CLOCK,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchClockState),
|
||||
.instance_init = s5l8900_clock_init,
|
||||
.class_init = s5l8900_clock_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_machine_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_clock_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_machine_types)
|
||||
@@ -0,0 +1,62 @@
|
||||
#include "hw/arm/ipod_touch_gpio.h"
|
||||
|
||||
static void s5l8900_gpio_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, value, addr);
|
||||
IPodTouchGPIOState *s = (struct IPodTouchGPIOState *) opaque;
|
||||
|
||||
switch(addr) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t s5l8900_gpio_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: read from location 0x%08x\n", __func__, addr);
|
||||
IPodTouchGPIOState *s = (struct IPodTouchGPIOState *) opaque;
|
||||
|
||||
switch(addr) {
|
||||
case 0x2c4:
|
||||
return s->gpio_state;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps gpio_ops = {
|
||||
.read = s5l8900_gpio_read,
|
||||
.write = s5l8900_gpio_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void s5l8900_gpio_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
IPodTouchGPIOState *s = IPOD_TOUCH_GPIO(dev);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &gpio_ops, s, "gpio", 0x10000);
|
||||
}
|
||||
|
||||
static void s5l8900_gpio_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_gpio_info = {
|
||||
.name = TYPE_IPOD_TOUCH_GPIO,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchGPIOState),
|
||||
.instance_init = s5l8900_gpio_init,
|
||||
.class_init = s5l8900_gpio_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_machine_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_gpio_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_machine_types)
|
||||
@@ -0,0 +1,301 @@
|
||||
#include "hw/arm/ipod_touch_lcd.h"
|
||||
#include "ui/pixel_ops.h"
|
||||
#include "ui/console.h"
|
||||
#include "hw/display/framebuffer.h"
|
||||
|
||||
static uint64_t s5l8900_lcd_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
// fprintf(stderr, "%s: read from location 0x%08x\n", __func__, addr);
|
||||
|
||||
IPodTouchLCDState *s = (IPodTouchLCDState *)opaque;
|
||||
switch(addr)
|
||||
{
|
||||
case 0x4:
|
||||
return s->lcd_con;
|
||||
case 0x8:
|
||||
return s->lcd_con2;
|
||||
|
||||
case 0x14:
|
||||
return s->unknown1;
|
||||
case 0x18:
|
||||
return s->unknown2;
|
||||
|
||||
case 0x20:
|
||||
return s->wnd_con;
|
||||
|
||||
case 0x200:
|
||||
return s->vid_con0;
|
||||
case 0x204:
|
||||
return s->vid_con1;
|
||||
|
||||
case 0x20C:
|
||||
return s->vidt_con0;
|
||||
case 0x210:
|
||||
return s->vidt_con1;
|
||||
case 0x214:
|
||||
return s->vidt_con2;
|
||||
case 0x218:
|
||||
return s->vidt_con3;
|
||||
|
||||
case 0x58:
|
||||
return s->w1_hspan;
|
||||
case 0x5c:
|
||||
return s->w1_display_depth_info;
|
||||
case 0x60:
|
||||
return s->w1_framebuffer_base;
|
||||
case 0x64:
|
||||
return s->w1_display_resolution_info;
|
||||
case 0x68:
|
||||
return s->w1_qlen;
|
||||
|
||||
case 0x70:
|
||||
return s->w2_hspan;
|
||||
case 0x74:
|
||||
return s->w2_display_depth_info;
|
||||
case 0x78:
|
||||
return s->w2_framebuffer_base;
|
||||
case 0x7c:
|
||||
return s->w2_display_resolution_info;
|
||||
case 0x80:
|
||||
return s->w2_qlen;
|
||||
default:
|
||||
// hw_error("%s: read invalid location 0x%08x.\n", __func__, addr);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void s5l8900_lcd_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
IPodTouchLCDState *s = (IPodTouchLCDState *)opaque;
|
||||
// fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, val, addr);
|
||||
|
||||
switch(addr) {
|
||||
case 0x4:
|
||||
s->lcd_con = val;
|
||||
break;
|
||||
case 0x8:
|
||||
s->lcd_con2 = val;
|
||||
break;
|
||||
|
||||
case 0x14:
|
||||
s->unknown1 = val;
|
||||
break;
|
||||
case 0x18:
|
||||
s->unknown2 = val;
|
||||
qemu_irq_lower(s->irq);
|
||||
break;
|
||||
|
||||
case 0x20:
|
||||
s->wnd_con = val;
|
||||
break;
|
||||
|
||||
case 0x200:
|
||||
s->vid_con0 = val;
|
||||
break;
|
||||
case 0x204:
|
||||
s->vid_con1 = val;
|
||||
break;
|
||||
|
||||
case 0x20C:
|
||||
s->vidt_con0 = val;
|
||||
break;
|
||||
case 0x210:
|
||||
s->vidt_con1 = val;
|
||||
break;
|
||||
case 0x214:
|
||||
s->vidt_con2 = val;
|
||||
break;
|
||||
case 0x218:
|
||||
s->vidt_con3 = val;
|
||||
break;
|
||||
|
||||
case 0x58:
|
||||
s->w1_hspan = val;
|
||||
break;
|
||||
case 0x5c:
|
||||
s->w1_display_depth_info = val;
|
||||
break;
|
||||
case 0x60:
|
||||
s->w1_framebuffer_base = val;
|
||||
break;
|
||||
case 0x64:
|
||||
s->w1_display_resolution_info = val;
|
||||
break;
|
||||
case 0x68:
|
||||
s->w1_qlen = val;
|
||||
break;
|
||||
|
||||
case 0x70:
|
||||
s->w2_hspan = val;
|
||||
break;
|
||||
case 0x74:
|
||||
s->w2_display_depth_info = val;
|
||||
break;
|
||||
case 0x78:
|
||||
s->w2_framebuffer_base = val;
|
||||
break;
|
||||
case 0x7c:
|
||||
s->w2_display_resolution_info = val;
|
||||
break;
|
||||
case 0x80:
|
||||
s->w2_qlen = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void lcd_invalidate(void *opaque)
|
||||
{
|
||||
IPodTouchLCDState *s = opaque;
|
||||
s->invalidate = 1;
|
||||
}
|
||||
|
||||
static void draw_line32_32(void *opaque, uint8_t *d, const uint8_t *s, int width, int deststep)
|
||||
{
|
||||
uint8_t r, g, b;
|
||||
|
||||
do {
|
||||
//v = lduw_le_p((void *) s);
|
||||
//printf("V: %d\n", *s);
|
||||
b = s[0];
|
||||
g = s[1];
|
||||
r = s[2];
|
||||
//printf("R: %d, G: %d, B: %d\n", r, g, b);
|
||||
((uint32_t *) d)[0] = rgb_to_pixel32(r, g, b);
|
||||
s += 4;
|
||||
d += 4;
|
||||
} while (-- width != 0);
|
||||
}
|
||||
|
||||
static void lcd_refresh(void *opaque)
|
||||
{
|
||||
//fprintf(stderr, "%s: refreshing LCD screen\n", __func__);
|
||||
|
||||
IPodTouchLCDState *lcd = (IPodTouchLCDState *) opaque;
|
||||
DisplaySurface *surface = qemu_console_surface(lcd->con);
|
||||
drawfn draw_line;
|
||||
int src_width, dest_width;
|
||||
int height, first, last;
|
||||
int width, linesize;
|
||||
|
||||
if (!lcd || !lcd->con || !surface_bits_per_pixel(surface))
|
||||
return;
|
||||
|
||||
dest_width = 4;
|
||||
draw_line = draw_line32_32;
|
||||
|
||||
/* Resolution */
|
||||
first = last = 0;
|
||||
width = 320;
|
||||
height = 480;
|
||||
lcd->invalidate = 1;
|
||||
|
||||
src_width = 4 * width;
|
||||
linesize = surface_stride(surface);
|
||||
|
||||
if(lcd->invalidate) {
|
||||
framebuffer_update_memory_section(&lcd->fbsection, lcd->sysmem, lcd->w1_framebuffer_base, height, 4 * width);
|
||||
}
|
||||
|
||||
framebuffer_update_display(surface, &lcd->fbsection,
|
||||
width, height,
|
||||
src_width, /* Length of source line, in bytes. */
|
||||
linesize, /* Bytes between adjacent horizontal output pixels. */
|
||||
dest_width, /* Bytes between adjacent vertical output pixels. */
|
||||
lcd->invalidate,
|
||||
draw_line, NULL,
|
||||
&first, &last);
|
||||
if (first >= 0) {
|
||||
dpy_gfx_update(lcd->con, 0, first, width, last - first + 1);
|
||||
}
|
||||
lcd->invalidate = 0;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps lcd_ops = {
|
||||
.read = s5l8900_lcd_read,
|
||||
.write = s5l8900_lcd_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static const GraphicHwOps s5l8900_gfx_ops = {
|
||||
.invalidate = lcd_invalidate,
|
||||
.gfx_update = lcd_refresh,
|
||||
};
|
||||
|
||||
static void ipod_touch_lcd_mouse_event(void *opaque, int x, int y, int z, int buttons_state)
|
||||
{
|
||||
//printf("CLICKY %d %d %d %d\n", x, y, z, buttons_state);
|
||||
|
||||
// convert x and y to fractional numbers
|
||||
float fx = x / pow(2, 15);
|
||||
float fy = 1 - y / pow(2, 15);
|
||||
|
||||
IPodTouchLCDState *lcd = (IPodTouchLCDState *) opaque;
|
||||
lcd->mt->prev_touch_x = lcd->mt->touch_x;
|
||||
lcd->mt->prev_touch_y = lcd->mt->touch_y;
|
||||
lcd->mt->touch_x = fx;
|
||||
lcd->mt->touch_y = fy;
|
||||
|
||||
if(buttons_state && !lcd->mt->touch_down) {
|
||||
ipod_touch_multitouch_on_touch(lcd->mt);
|
||||
}
|
||||
else if(!buttons_state && lcd->mt->touch_down) {
|
||||
ipod_touch_multitouch_on_release(lcd->mt);
|
||||
}
|
||||
}
|
||||
|
||||
static void refresh_timer_tick(void *opaque)
|
||||
{
|
||||
IPodTouchLCDState *s = (IPodTouchLCDState *)opaque;
|
||||
|
||||
qemu_irq_raise(s->irq);
|
||||
|
||||
timer_mod(s->refresh_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + NANOSECONDS_PER_SECOND / LCD_REFRESH_RATE_FREQUENCY);
|
||||
}
|
||||
|
||||
static void s5l8900_lcd_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
IPodTouchLCDState *s = IPOD_TOUCH_LCD(dev);
|
||||
s->con = graphic_console_init(dev, 0, &s5l8900_gfx_ops, s);
|
||||
qemu_console_resize(s->con, 320, 480);
|
||||
|
||||
// add mouse handler
|
||||
qemu_add_mouse_event_handler(ipod_touch_lcd_mouse_event, s, 1, "iPod Touch Touchscreen");
|
||||
|
||||
// initialize the refresh timer
|
||||
s->refresh_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, refresh_timer_tick, s);
|
||||
timer_mod(s->refresh_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + NANOSECONDS_PER_SECOND / LCD_REFRESH_RATE_FREQUENCY);
|
||||
}
|
||||
|
||||
static void s5l8900_lcd_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
IPodTouchLCDState *s = IPOD_TOUCH_LCD(dev);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &lcd_ops, s, "lcd", 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
}
|
||||
|
||||
static void s5l8900_lcd_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = s5l8900_lcd_realize;
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_lcd_info = {
|
||||
.name = TYPE_IPOD_TOUCH_LCD,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchLCDState),
|
||||
.instance_init = s5l8900_lcd_init,
|
||||
.class_init = s5l8900_lcd_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_machine_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_lcd_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_machine_types)
|
||||
@@ -0,0 +1,63 @@
|
||||
#include "hw/arm/ipod_touch_lcd_panel.h"
|
||||
|
||||
static uint32_t ipod_touch_lcd_panel_transfer(SSIPeripheral *dev, uint32_t value)
|
||||
{
|
||||
IPodTouchLCDPanelState *s = IPOD_TOUCH_LCD_PANEL(dev);
|
||||
|
||||
if(!s->cur_cmd && (value == 0x95 || value == 0xDA || value == 0xDB || value == 0xDC)) {
|
||||
// this is a command -> set it
|
||||
s->cur_cmd = value;
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
if(s->cur_cmd) {
|
||||
uint32_t res = 0;
|
||||
switch(s->cur_cmd) {
|
||||
case 0x95:
|
||||
res = 0x1;
|
||||
break;
|
||||
case 0xDA:
|
||||
res = 0x71;
|
||||
break;
|
||||
case 0xDB:
|
||||
res = 0xC2;
|
||||
break;
|
||||
case 0xDC:
|
||||
res = 0x0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
s->cur_cmd = 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
static void ipod_touch_lcd_panel_realize(SSIPeripheral *d, Error **errp)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void ipod_touch_lcd_panel_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass);
|
||||
k->realize = ipod_touch_lcd_panel_realize;
|
||||
k->transfer = ipod_touch_lcd_panel_transfer;
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_lcd_panel_type_info = {
|
||||
.name = TYPE_IPOD_TOUCH_LCD_PANEL,
|
||||
.parent = TYPE_SSI_PERIPHERAL,
|
||||
.instance_size = sizeof(IPodTouchLCDPanelState),
|
||||
.class_init = ipod_touch_lcd_panel_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_lcd_panel_register_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_lcd_panel_type_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_lcd_panel_register_types)
|
||||
@@ -0,0 +1,55 @@
|
||||
#include "hw/arm/ipod_touch_lis302dl.h"
|
||||
|
||||
static int lis302dl_event(I2CSlave *i2c, enum i2c_event event)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t lis302dl_recv(I2CSlave *i2c)
|
||||
{
|
||||
LIS302DLState *s = LIS302DL(i2c);
|
||||
//printf("Reading accelerometer register %d\n", s->cmd);
|
||||
|
||||
switch(s->cmd) {
|
||||
case ACCEL_WHOAMI:
|
||||
return ACCEL_WHOAMI_VALUE; // whoami value
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int lis302dl_send(I2CSlave *i2c, uint8_t data)
|
||||
{
|
||||
LIS302DLState *s = LIS302DL(i2c);
|
||||
s->cmd = data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lis302dl_init(Object *obj)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void lis302dl_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
|
||||
|
||||
k->event = lis302dl_event;
|
||||
k->recv = lis302dl_recv;
|
||||
k->send = lis302dl_send;
|
||||
}
|
||||
|
||||
static const TypeInfo lis302dl_info = {
|
||||
.name = TYPE_LIS302DL,
|
||||
.parent = TYPE_I2C_SLAVE,
|
||||
.instance_init = lis302dl_init,
|
||||
.instance_size = sizeof(LIS302DLState),
|
||||
.class_init = lis302dl_class_init,
|
||||
};
|
||||
|
||||
static void lis302dl_register_types(void)
|
||||
{
|
||||
type_register_static(&lis302dl_info);
|
||||
}
|
||||
|
||||
type_init(lis302dl_register_types)
|
||||
@@ -0,0 +1,418 @@
|
||||
#include "hw/arm/ipod_touch_multitouch.h"
|
||||
|
||||
static void prepare_interface_version_response(IPodTouchMultitouchState *s) {
|
||||
memset(s->out_buffer + 1, 0, 15);
|
||||
|
||||
// set the interface version
|
||||
s->out_buffer[2] = MT_INTERFACE_VERSION;
|
||||
|
||||
// set the max packet size
|
||||
s->out_buffer[3] = (MT_MAX_PACKET_SIZE & 0xFF);
|
||||
s->out_buffer[4] = (MT_MAX_PACKET_SIZE >> 8) & 0xFF;
|
||||
|
||||
// compute and set the checksum
|
||||
uint32_t checksum = 0;
|
||||
for(int i = 0; i < 14; i++) {
|
||||
checksum += s->out_buffer[i];
|
||||
}
|
||||
|
||||
s->out_buffer[14] = (checksum & 0xFF);
|
||||
s->out_buffer[15] = (checksum >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
static void prepare_cmd_status_response(IPodTouchMultitouchState *s) {
|
||||
memset(s->out_buffer + 1, 0, 15);
|
||||
|
||||
// TODO we should probably set some CMD status here
|
||||
|
||||
// compute and set the checksum
|
||||
uint32_t checksum = 0;
|
||||
for(int i = 0; i < 14; i++) {
|
||||
checksum += s->out_buffer[i];
|
||||
}
|
||||
|
||||
s->out_buffer[14] = (checksum & 0xFF);
|
||||
s->out_buffer[15] = (checksum >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
static void prepare_report_info_response(IPodTouchMultitouchState *s, uint8_t report_id) {
|
||||
memset(s->out_buffer + 1, 0, 15);
|
||||
|
||||
// set the error
|
||||
s->out_buffer[2] = 0;
|
||||
|
||||
// set the report length
|
||||
uint32_t report_length = 0;
|
||||
if(report_id == MT_REPORT_UNKNOWN1) {
|
||||
report_length = MT_REPORT_UNKNOWN1_SIZE;
|
||||
}
|
||||
else if(report_id == MT_REPORT_FAMILY_ID) {
|
||||
report_length = MT_REPORT_FAMILY_ID_SIZE;
|
||||
}
|
||||
else if(report_id == MT_REPORT_SENSOR_INFO) {
|
||||
report_length = MT_REPORT_SENSOR_INFO_SIZE;
|
||||
}
|
||||
else if(report_id == MT_REPORT_SENSOR_REGION_DESC) {
|
||||
report_length = MT_REPORT_SENSOR_REGION_DESC_SIZE;
|
||||
}
|
||||
else if(report_id == MT_REPORT_SENSOR_REGION_PARAM) {
|
||||
report_length = MT_REPORT_SENSOR_REGION_PARAM_SIZE;
|
||||
}
|
||||
else if(report_id == MT_REPORT_SENSOR_DIMENSIONS) {
|
||||
report_length = MT_REPORT_SENSOR_DIMENSIONS_SIZE;
|
||||
}
|
||||
else {
|
||||
hw_error("Unknown report ID 0x%02x\n", report_id);
|
||||
}
|
||||
|
||||
s->out_buffer[3] = (report_length & 0xFF);
|
||||
s->out_buffer[4] = (report_length >> 8) & 0xFF;
|
||||
|
||||
// compute and set the checksum
|
||||
uint32_t checksum = 0;
|
||||
for(int i = 0; i < 14; i++) {
|
||||
checksum += s->out_buffer[i];
|
||||
}
|
||||
|
||||
s->out_buffer[14] = (checksum & 0xFF);
|
||||
s->out_buffer[15] = (checksum >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
static void prepare_short_control_response(IPodTouchMultitouchState *s, uint8_t report_id) {
|
||||
memset(s->out_buffer + 1, 0, 15);
|
||||
|
||||
if(report_id == MT_REPORT_FAMILY_ID) {
|
||||
s->out_buffer[3] = MT_FAMILY_ID;
|
||||
}
|
||||
else if(report_id == MT_REPORT_SENSOR_INFO) {
|
||||
s->out_buffer[3] = MT_ENDIANNESS;
|
||||
s->out_buffer[4] = MT_SENSOR_ROWS;
|
||||
s->out_buffer[5] = MT_SENSOR_COLUMNS;
|
||||
s->out_buffer[6] = (MT_BCD_VERSION & 0xFF);
|
||||
s->out_buffer[7] = (MT_BCD_VERSION >> 8) & 0xFF;
|
||||
}
|
||||
else if(report_id == MT_REPORT_SENSOR_REGION_DESC) {
|
||||
s->out_buffer[3] = MT_SENSOR_REGION_DESC;
|
||||
}
|
||||
else if(report_id == MT_REPORT_SENSOR_REGION_PARAM) {
|
||||
s->out_buffer[3] = MT_SENSOR_REGION_PARAM;
|
||||
}
|
||||
else if(report_id == MT_REPORT_SENSOR_DIMENSIONS) {
|
||||
uint32_t *ob_int32 = (uint32_t *)&s->out_buffer[3];
|
||||
ob_int32[0] = MT_SENSOR_SURFACE_WIDTH;
|
||||
ob_int32[1] = MT_SENSOR_SURFACE_HEIGHT;
|
||||
}
|
||||
else {
|
||||
hw_error("Unknown report ID 0x%02x\n", report_id);
|
||||
}
|
||||
|
||||
// compute and set the checksum
|
||||
uint32_t checksum = 0;
|
||||
for(int i = 0; i < 14; i++) {
|
||||
checksum += s->out_buffer[i];
|
||||
}
|
||||
|
||||
s->out_buffer[14] = (checksum & 0xFF);
|
||||
s->out_buffer[15] = (checksum >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
static uint32_t ipod_touch_multitouch_transfer(SSIPeripheral *dev, uint32_t value)
|
||||
{
|
||||
IPodTouchMultitouchState *s = IPOD_TOUCH_MULTITOUCH(dev);
|
||||
|
||||
//printf("<MULTITOUCH> Got value: 0x%02x\n", value);
|
||||
|
||||
if(s->cur_cmd == 0) {
|
||||
//printf("Starting command 0x%02x\n", value);
|
||||
// we're currently not in a command - start a new command
|
||||
s->cur_cmd = value;
|
||||
s->out_buffer = malloc(0x100);
|
||||
s->out_buffer[0] = value; // the response header
|
||||
s->buf_ind = 0;
|
||||
s->in_buffer = malloc(0x100);
|
||||
s->in_buffer_ind = 0;
|
||||
|
||||
if(value == 0x18) { // filler packet??
|
||||
s->buf_size = 2;
|
||||
s->out_buffer[1] = 0xE1;
|
||||
}
|
||||
else if(value == 0x1A) { // HBPP ACK
|
||||
s->buf_size = 2;
|
||||
if(s->hbpp_atn_ack_response[0] == 0 && s->hbpp_atn_ack_response[1] == 0) {
|
||||
// return the default ACK response
|
||||
s->out_buffer[0] = 0x4B;
|
||||
s->out_buffer[1] = 0xC1;
|
||||
}
|
||||
else {
|
||||
s->out_buffer[0] = s->hbpp_atn_ack_response[0];
|
||||
s->out_buffer[1] = s->hbpp_atn_ack_response[1];
|
||||
}
|
||||
|
||||
}
|
||||
else if(value == 0x1C) { // read register
|
||||
s->buf_size = 8;
|
||||
memset(s->out_buffer, 0, 8); // just return zeros
|
||||
}
|
||||
else if(value == 0x1D) { // execute
|
||||
s->buf_size = 12;
|
||||
memset(s->out_buffer, 0, 12); // just return zeros
|
||||
}
|
||||
else if(value == 0x1F) { // calibration
|
||||
s->buf_size = 2;
|
||||
s->out_buffer[1] = 0x0;
|
||||
}
|
||||
else if(value == 0x1E) { // write register
|
||||
s->buf_size = 16;
|
||||
memset(s->out_buffer, 0, 16); // just return zeros
|
||||
}
|
||||
else if(value == 0x1F) { // calibration
|
||||
s->buf_size = 2;
|
||||
s->out_buffer[1] = 0x0;
|
||||
}
|
||||
else if(value == MT_CMD_HBPP_DATA_PACKET) {
|
||||
s->buf_size = 20; // should be enough initially, until we get the packet length
|
||||
memset(s->out_buffer + 1, 0, 20 - 1); // just return zeros
|
||||
}
|
||||
else if(value == 0x47) { // unknown command, probably used to clear the interrupt
|
||||
s->buf_size = 2;
|
||||
}
|
||||
else if(value == MT_CMD_GET_CMD_STATUS) {
|
||||
s->buf_size = 16;
|
||||
prepare_cmd_status_response(s);
|
||||
}
|
||||
else if(value == MT_CMD_GET_INTERFACE_VERSION) {
|
||||
s->buf_size = 16;
|
||||
prepare_interface_version_response(s);
|
||||
}
|
||||
else if(value == MT_CMD_GET_REPORT_INFO) {
|
||||
s->buf_size = 16;
|
||||
}
|
||||
else if(value == MT_CMD_SHORT_CONTROL_WRITE) {
|
||||
s->buf_size = 16;
|
||||
}
|
||||
else if(value == MT_CMD_SHORT_CONTROL_READ) {
|
||||
s->buf_size = 16;
|
||||
}
|
||||
else if(value == MT_CMD_FRAME_READ) {
|
||||
printf("Will read frame!\n");
|
||||
s->buf_size = sizeof(MTFrame);
|
||||
free(s->out_buffer);
|
||||
s->out_buffer = (uint8_t *) s->next_frame;
|
||||
}
|
||||
else {
|
||||
hw_error("Unknown command 0x%02x!", value);
|
||||
}
|
||||
}
|
||||
|
||||
s->in_buffer[s->in_buffer_ind] = value;
|
||||
s->in_buffer_ind++;
|
||||
|
||||
if(s->cur_cmd == MT_CMD_HBPP_DATA_PACKET && s->in_buffer_ind == 10) {
|
||||
// verify the header checksum
|
||||
uint32_t checksum = 0;
|
||||
for(int i = 2; i < 8; i++) {
|
||||
checksum += s->in_buffer[i];
|
||||
}
|
||||
|
||||
if(checksum != (s->in_buffer[8] << 8 | s->in_buffer[9])) {
|
||||
hw_error("HBPP data header checksum doesn't match!");
|
||||
}
|
||||
|
||||
uint32_t data_len = (s->in_buffer[2] << 10) | (s->in_buffer[3] << 2) + 5;
|
||||
// extend the lengths of the in/out buffers
|
||||
free(s->in_buffer);
|
||||
s->in_buffer = malloc(data_len + 0x10);
|
||||
|
||||
free(s->out_buffer);
|
||||
s->out_buffer = malloc(data_len);
|
||||
memset(s->out_buffer, 0, data_len);
|
||||
s->buf_size = data_len;
|
||||
s->buf_ind = 0;
|
||||
}
|
||||
else if(s->cur_cmd == MT_CMD_GET_REPORT_INFO && s->in_buffer_ind == 2) {
|
||||
prepare_report_info_response(s, s->in_buffer[1]);
|
||||
}
|
||||
else if(s->cur_cmd == MT_CMD_SHORT_CONTROL_WRITE && s->in_buffer_ind == 16) {
|
||||
// TODO we should persist the report here!
|
||||
}
|
||||
else if(s->cur_cmd == MT_CMD_SHORT_CONTROL_READ && s->in_buffer_ind == 2) {
|
||||
prepare_short_control_response(s, s->in_buffer[1]);
|
||||
}
|
||||
|
||||
// TODO process register writes!
|
||||
|
||||
uint8_t ret_val = s->out_buffer[s->buf_ind];
|
||||
s->buf_ind++;
|
||||
|
||||
//printf("<MULTITOUCH> Got value: 0x%02x, returning 0x%02x (index: %d, buffer length: %d)\n", value, ret_val, s->buf_ind, s->buf_size);
|
||||
|
||||
if(s->buf_ind == s->buf_size) {
|
||||
//printf("Finished command 0x%02x\n", s->cur_cmd);
|
||||
|
||||
if(s->cur_cmd == 0x1E) {
|
||||
// make sure we return a success status on the next HBPP ACK
|
||||
s->hbpp_atn_ack_response[0] = 0x4A;
|
||||
s->hbpp_atn_ack_response[1] = 0xD1;
|
||||
}
|
||||
|
||||
// we're done with the command
|
||||
s->cur_cmd = 0;
|
||||
s->buf_size = 0;
|
||||
//free(s->out_buffer);
|
||||
//free(s->in_buffer);
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
static MTFrame *get_frame(IPodTouchMultitouchState *s, uint8_t event, float x, float y, uint16_t radius1, uint16_t radius2, uint16_t radius3, uint16_t contactDensity) {
|
||||
MTFrame *frame = calloc(sizeof(MTFrame), sizeof(uint8_t *));
|
||||
|
||||
uint16_t data_len = sizeof(MTFrameHeader) + sizeof(FingerData) + 2;
|
||||
|
||||
/// create the frame length packet
|
||||
frame->frame_length.cmd = MT_CMD_FRAME_READ;
|
||||
frame->frame_length.length1 = (data_len & 0xFF);
|
||||
frame->frame_length.length2 = (data_len >> 8) & 0xFF;
|
||||
|
||||
uint16_t checksum = 0;
|
||||
for(int i = 0; i < 14; i++) {
|
||||
checksum += ((uint8_t *) &frame->frame_length)[i];
|
||||
}
|
||||
frame->frame_length.checksum1 = (checksum & 0xFF);
|
||||
frame->frame_length.checksum2 = (checksum >> 8) & 0xFF;
|
||||
|
||||
// create the frame packet
|
||||
frame->frame_packet.cmd = MT_CMD_FRAME_READ;
|
||||
frame->frame_packet.length1 = (data_len & 0xFF);
|
||||
frame->frame_packet.length2 = (data_len >> 8) & 0xFF;
|
||||
|
||||
checksum = 0;
|
||||
for(int i = 0; i < 4; i++) {
|
||||
checksum += ((uint8_t *) &frame->frame_length)[i];
|
||||
}
|
||||
|
||||
// the first five bytes have to sum up to 0.
|
||||
frame->frame_packet.checksum_pad = 0xFF - (checksum & 0xFF) + 1;
|
||||
|
||||
frame->frame_packet.header.type = MT_FRAME_TYPE_PATH;
|
||||
frame->frame_packet.header.frameNum = s->frame_counter;
|
||||
frame->frame_packet.header.headerLen = sizeof(MTFrameHeader);
|
||||
uint64_t elapsed_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / 1000000;
|
||||
frame->frame_packet.header.timestamp = elapsed_ns;
|
||||
frame->frame_packet.header.numFingers = 1;
|
||||
frame->frame_packet.header.fingerDataLen = sizeof(FingerData);
|
||||
|
||||
// create the finger data
|
||||
frame->finger_data.id = 1;
|
||||
frame->finger_data.event = event;
|
||||
frame->finger_data.unk_2 = 2;
|
||||
frame->finger_data.unk_3 = 1;
|
||||
|
||||
// compute the velocity
|
||||
int diff_x = (int)((x - s->prev_touch_x) * MT_INTERNAL_SENSOR_SURFACE_WIDTH);
|
||||
int diff_y = (int)((x - s->prev_touch_y) * MT_INTERNAL_SENSOR_SURFACE_HEIGHT);
|
||||
|
||||
// Add 1 to elapsed_ns to prevent crash with error "Floating point exception"
|
||||
// due to click fast on emulator
|
||||
frame->finger_data.velX = diff_x / (elapsed_ns + 1 - s->last_frame_timestamp) * 1000;
|
||||
frame->finger_data.velY = diff_y / (elapsed_ns + 1 - s->last_frame_timestamp) * 1000;
|
||||
|
||||
frame->finger_data.x = (int)(x * MT_INTERNAL_SENSOR_SURFACE_WIDTH);
|
||||
frame->finger_data.y = (int)(y * MT_INTERNAL_SENSOR_SURFACE_HEIGHT);
|
||||
frame->finger_data.radius1 = radius1;
|
||||
frame->finger_data.radius2 = radius2;
|
||||
frame->finger_data.radius3 = radius3;
|
||||
frame->finger_data.angle = 19317;
|
||||
frame->finger_data.contactDensity = contactDensity; // seems to be a medium press
|
||||
|
||||
// compute the checksum over the frame data.
|
||||
checksum = 0;
|
||||
for(int i = 0; i < data_len - 2; i++) {
|
||||
checksum += ((uint8_t *) &frame->frame_packet.header)[i];
|
||||
}
|
||||
frame->checksum1 = (checksum & 0xFF);
|
||||
frame->checksum2 = (checksum >> 8) & 0xFF;
|
||||
|
||||
s->last_frame_timestamp = elapsed_ns;
|
||||
s->frame_counter += 1;
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
static void ipod_touch_multitouch_inform_frame_ready(IPodTouchMultitouchState *s) {
|
||||
s->sysic->gpio_int_status[4] |= (1 << 27); // the multitouch interrupt bit is in group 4 (32 interrupts per group), and the 27th of the 4th group
|
||||
qemu_irq_raise(s->sysic->gpio_irqs[4]);
|
||||
}
|
||||
|
||||
void ipod_touch_multitouch_on_touch(IPodTouchMultitouchState *s) {
|
||||
s->touch_down = true;
|
||||
|
||||
s->next_frame = get_frame(s, MT_EVENT_TOUCH_START, s->touch_x, s->touch_y, 100, 660, 580, 150);
|
||||
ipod_touch_multitouch_inform_frame_ready(s);
|
||||
|
||||
timer_mod(s->touch_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + NANOSECONDS_PER_SECOND / 10);
|
||||
}
|
||||
|
||||
void ipod_touch_multitouch_on_release(IPodTouchMultitouchState *s) {
|
||||
s->next_frame = get_frame(s, MT_EVENT_TOUCH_ENDED, s->touch_x, s->touch_y, 0, 0, 0, 0);
|
||||
s->touch_down = false;
|
||||
ipod_touch_multitouch_inform_frame_ready(s);
|
||||
|
||||
timer_del(s->touch_timer);
|
||||
timer_mod(s->touch_end_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + NANOSECONDS_PER_SECOND / 10);
|
||||
}
|
||||
|
||||
static void touch_timer_tick(void *opaque)
|
||||
{
|
||||
IPodTouchMultitouchState *s = (IPodTouchMultitouchState *)opaque;
|
||||
|
||||
s->next_frame = get_frame(s, MT_EVENT_TOUCH_MOVED, s->touch_x, s->touch_y, 100, 660, 580, 150);
|
||||
ipod_touch_multitouch_inform_frame_ready(s);
|
||||
|
||||
if(s->touch_down) {
|
||||
// reschedule the timer
|
||||
timer_mod(s->touch_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + NANOSECONDS_PER_SECOND / 10);
|
||||
}
|
||||
}
|
||||
|
||||
static void touch_end_timer_tick(void *opaque)
|
||||
{
|
||||
IPodTouchMultitouchState *s = (IPodTouchMultitouchState *)opaque;
|
||||
s->next_frame = get_frame(s, MT_EVENT_TOUCH_FULL_END, s->touch_x, s->touch_y, 0, 0, 0, 0);
|
||||
s->touch_down = false;
|
||||
ipod_touch_multitouch_inform_frame_ready(s);
|
||||
}
|
||||
|
||||
static void ipod_touch_multitouch_realize(SSIPeripheral *d, Error **errp)
|
||||
{
|
||||
IPodTouchMultitouchState *s = IPOD_TOUCH_MULTITOUCH(d);
|
||||
memset(s->hbpp_atn_ack_response, 0, 2);
|
||||
s->touch_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, touch_timer_tick, s);
|
||||
s->touch_end_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, touch_end_timer_tick, s);
|
||||
|
||||
s->prev_touch_x = 0;
|
||||
s->prev_touch_y = 0;
|
||||
s->last_frame_timestamp = 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_multitouch_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass);
|
||||
k->realize = ipod_touch_multitouch_realize;
|
||||
k->transfer = ipod_touch_multitouch_transfer;
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_multitouch_type_info = {
|
||||
.name = TYPE_IPOD_TOUCH_MULTITOUCH,
|
||||
.parent = TYPE_SSI_PERIPHERAL,
|
||||
.instance_size = sizeof(IPodTouchMultitouchState),
|
||||
.class_init = ipod_touch_multitouch_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_multitouch_register_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_multitouch_type_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_multitouch_register_types)
|
||||
@@ -0,0 +1,246 @@
|
||||
#include "hw/arm/ipod_touch_nand.h"
|
||||
|
||||
static int get_bank(ITNandState *s) {
|
||||
uint32_t bank_bitmap = (s->fmctrl0 >> 1) & 0xFF;
|
||||
for(int bank = 0; bank < NAND_NUM_BANKS; bank++) {
|
||||
if((bank_bitmap & (1 << bank)) != 0) {
|
||||
return bank;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void set_bank(ITNandState *s, uint32_t activate_bank) {
|
||||
for(int bank = 0; bank < 8; bank++) {
|
||||
// clear bit, toggle if it is active
|
||||
s->fmctrl0 &= ~(1 << (bank + 1));
|
||||
if(bank == activate_bank) {
|
||||
s->fmctrl0 ^= 1 << (bank + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void nand_set_buffered_page(ITNandState *s, uint32_t page) {
|
||||
uint32_t bank = get_bank(s);
|
||||
if(bank == -1) {
|
||||
hw_error("Active bank not set while nand_read with page %d is called (reading multiple pages: %d)!", page, s->reading_multiple_pages);
|
||||
}
|
||||
|
||||
if(bank != s->buffered_bank || page != s->buffered_page) {
|
||||
// refresh the buffered page
|
||||
uint32_t vpn = page * 8 + bank;
|
||||
char filename[200];
|
||||
sprintf(filename, "%s/bank%d/%d.page", s->nand_path, bank, page);
|
||||
struct stat st = {0};
|
||||
if (stat(filename, &st) == -1) {
|
||||
// page storage does not exist - initialize an empty buffer
|
||||
memset(s->page_buffer, 0, NAND_BYTES_PER_PAGE);
|
||||
memset(s->page_spare_buffer, 0, NAND_BYTES_PER_SPARE);
|
||||
s->page_spare_buffer[0xA] = 0xFF; // make sure we add the FTL mark to an empty page
|
||||
}
|
||||
else {
|
||||
FILE *f = fopen(filename, "rb");
|
||||
if (f == NULL) { hw_error("Unable to read file!"); }
|
||||
fread(s->page_buffer, sizeof(char), NAND_BYTES_PER_PAGE, f);
|
||||
fread(s->page_spare_buffer, sizeof(char), NAND_BYTES_PER_SPARE, f);
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
s->buffered_page = page;
|
||||
s->buffered_bank = bank;
|
||||
// printf("Buffered bank: %d, page: %d\n", s->buffered_bank, s->buffered_page);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t itnand_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
ITNandState *s = (ITNandState *) opaque;
|
||||
if(s->reading_multiple_pages) {
|
||||
//fprintf(stderr, "%s: reading from 0x%08x\n", __func__, addr);
|
||||
}
|
||||
|
||||
switch (addr) {
|
||||
case NAND_FMCTRL0:
|
||||
return s->fmctrl0;
|
||||
case NAND_FMFIFO:
|
||||
if(s->cmd == NAND_CMD_ID) {
|
||||
return NAND_CHIP_ID;
|
||||
}
|
||||
else if(s->cmd == NAND_CMD_READSTATUS) {
|
||||
return (1 << 6);
|
||||
}
|
||||
else {
|
||||
uint32_t read_val = 0;
|
||||
if(s->reading_multiple_pages) {
|
||||
// which bank are we at?
|
||||
if(s->fmdnum % 0x800 == 0) {
|
||||
s->cur_bank_reading += 1;
|
||||
//printf("WILL TURN TO BANK %d (cnt: %d)\n", s->cur_bank_reading, s->fmdnum);
|
||||
set_bank(s, s->banks_to_read[s->cur_bank_reading]);
|
||||
}
|
||||
|
||||
// compute the offset in the page
|
||||
uint32_t page_offset = s->fmdnum % 0x800;
|
||||
if(page_offset == 0) { page_offset = 0x800; }
|
||||
nand_set_buffered_page(s, s->pages_to_read[s->cur_bank_reading]);
|
||||
//printf("Reading page %d\n", s->pages_to_read[s->cur_bank_reading]);
|
||||
read_val = ((uint32_t *)s->page_buffer)[(NAND_BYTES_PER_PAGE - page_offset) / 4];
|
||||
//printf("FMDNUM: %d, offset: %d\n", s->fmdnum, (NAND_BYTES_PER_PAGE - page_offset) / 4);
|
||||
//printf("Page offset: %d, bytes: 0x%08x\n", page_offset, read_val);
|
||||
}
|
||||
else {
|
||||
uint32_t page = (s->fmaddr1 << 16) | (s->fmaddr0 >> 16);
|
||||
nand_set_buffered_page(s, page);
|
||||
//printf("Reading page %d\n", page);
|
||||
|
||||
if(s->reading_spare) {
|
||||
read_val = ((uint32_t *)s->page_spare_buffer)[(NAND_BYTES_PER_SPARE - s->fmdnum - 1) / 4];
|
||||
} else {
|
||||
read_val = ((uint32_t *)s->page_buffer)[(NAND_BYTES_PER_PAGE - s->fmdnum - 1) / 4];
|
||||
}
|
||||
}
|
||||
s->fmdnum -= 4;
|
||||
return read_val;
|
||||
}
|
||||
|
||||
case NAND_FMCSTAT:
|
||||
return (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8) | (1 << 9) | (1 << 10) | (1 << 11) | (1 << 12); // this indicates that everything is ready, including our eight banks
|
||||
case NAND_RSCTRL:
|
||||
return s->rsctrl;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void itnand_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
ITNandState *s = (ITNandState *) opaque;
|
||||
if(s->reading_multiple_pages) {
|
||||
//fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, val, addr);
|
||||
}
|
||||
|
||||
|
||||
switch(addr) {
|
||||
case NAND_FMCTRL0:
|
||||
s->fmctrl0 = val;
|
||||
break;
|
||||
case NAND_FMCTRL1:
|
||||
s->fmctrl1 = val;
|
||||
break;
|
||||
case NAND_FMADDR0:
|
||||
s->fmaddr0 = val;
|
||||
break;
|
||||
case NAND_FMADDR1:
|
||||
s->fmaddr1 = val;
|
||||
break;
|
||||
case NAND_FMANUM:
|
||||
s->fmanum = val;
|
||||
break;
|
||||
case NAND_CMD:
|
||||
s->cmd = val;
|
||||
break;
|
||||
case NAND_FMDNUM:
|
||||
if(val == NAND_BYTES_PER_SPARE - 1) {
|
||||
s->reading_spare = 1;
|
||||
} else {
|
||||
s->reading_spare = 0;
|
||||
}
|
||||
s->fmdnum = val;
|
||||
break;
|
||||
case NAND_FMFIFO:
|
||||
if(!s->is_writing) {
|
||||
// printf("%s: NAND_FMFIFO writing while not in writing mode!\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
//printf("Setting offset %d: %d\n", s->fmdnum, (NAND_BYTES_PER_PAGE - s->fmdnum) / 4);
|
||||
((uint32_t *)s->page_buffer)[(NAND_BYTES_PER_PAGE - s->fmdnum) / 4] = val;
|
||||
s->fmdnum -= 4;
|
||||
|
||||
if(s->fmdnum == 0) {
|
||||
// we're done!
|
||||
s->is_writing = false;
|
||||
|
||||
// flush the page buffer to the disk
|
||||
uint32_t vpn = s->buffered_page * 8 + s->buffered_bank;
|
||||
//printf("Flushing page %d, bank %d, vpn %d\n", s->buffered_page, s->buffered_bank, vpn);
|
||||
qemu_mutex_lock(&s->lock);
|
||||
qemu_mutex_unlock(&s->lock);
|
||||
{
|
||||
char filename[200];
|
||||
sprintf(filename, "%s/bank%d/%d_new.page", s->nand_path, s->buffered_bank, s->buffered_page);
|
||||
FILE *f = fopen(filename, "wb");
|
||||
if (f == NULL) { hw_error("Unable to read file!"); }
|
||||
fwrite(s->page_buffer, sizeof(char), NAND_BYTES_PER_PAGE, f);
|
||||
fwrite(s->page_spare_buffer, sizeof(char), NAND_BYTES_PER_SPARE, f);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NAND_RSCTRL:
|
||||
s->rsctrl = val;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps nand_ops = {
|
||||
.read = itnand_read,
|
||||
.write = itnand_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void itnand_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
ITNandState *s = ITNAND(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &nand_ops, s, "nand", 0x1000);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
|
||||
s->page_buffer = (uint8_t *)malloc(NAND_BYTES_PER_PAGE);
|
||||
s->page_spare_buffer = (uint8_t *)malloc(NAND_BYTES_PER_SPARE);
|
||||
s->buffered_page = -1;
|
||||
s->buffered_bank = -1;
|
||||
|
||||
qemu_mutex_init(&s->lock);
|
||||
}
|
||||
|
||||
static void itnand_reset(DeviceState *d)
|
||||
{
|
||||
ITNandState *s = (ITNandState *) d;
|
||||
|
||||
s->fmctrl0 = 0;
|
||||
s->fmctrl1 = 0;
|
||||
s->fmaddr0 = 0;
|
||||
s->fmaddr1 = 0;
|
||||
s->fmanum = 0;
|
||||
s->fmdnum = 0;
|
||||
s->rsctrl = 0;
|
||||
s->cmd = 0;
|
||||
s->reading_spare = 0;
|
||||
s->buffered_page = -1;
|
||||
}
|
||||
|
||||
static void itnand_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
dc->reset = itnand_reset;
|
||||
}
|
||||
|
||||
static const TypeInfo itnand_info = {
|
||||
.name = TYPE_ITNAND,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(ITNandState),
|
||||
.instance_init = itnand_init,
|
||||
.class_init = itnand_class_init,
|
||||
};
|
||||
|
||||
static void itnand_register_types(void)
|
||||
{
|
||||
type_register_static(&itnand_info);
|
||||
}
|
||||
|
||||
type_init(itnand_register_types)
|
||||
@@ -0,0 +1,77 @@
|
||||
#include "hw/arm/ipod_touch_nand_ecc.h"
|
||||
|
||||
static uint64_t itnand_ecc_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
ITNandECCState *s = (ITNandECCState *) opaque;
|
||||
//fprintf(stderr, "%s: reading from 0x%08x\n", __func__, addr);
|
||||
|
||||
switch (addr) {
|
||||
case NANDECC_STATUS:
|
||||
return 0; // TODO: for now, we assume that all ECC operations are successful
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void itnand_ecc_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
ITNandECCState *s = (ITNandECCState *) opaque;
|
||||
|
||||
switch(addr) {
|
||||
case NANDECC_START:
|
||||
qemu_irq_raise(s->irq);
|
||||
break;
|
||||
case NANDECC_CLEARINT:
|
||||
qemu_irq_lower(s->irq);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps nand_ecc_ops = {
|
||||
.read = itnand_ecc_read,
|
||||
.write = itnand_ecc_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void itnand_ecc_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
ITNandECCState *s = ITNANDECC(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &nand_ecc_ops, s, "nandecc", 0x100);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
}
|
||||
|
||||
static void itnand_ecc_reset(DeviceState *d)
|
||||
{
|
||||
ITNandECCState *s = (ITNandECCState *) d;
|
||||
|
||||
s->data_addr = 0;
|
||||
s->ecc_addr = 0;
|
||||
s->status = 0;
|
||||
s->setup = 0;
|
||||
}
|
||||
|
||||
static void itnand_ecc_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
dc->reset = itnand_ecc_reset;
|
||||
}
|
||||
|
||||
static const TypeInfo itnand_ecc_info = {
|
||||
.name = TYPE_ITNANDECC,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(ITNandECCState),
|
||||
.instance_init = itnand_ecc_init,
|
||||
.class_init = itnand_ecc_class_init,
|
||||
};
|
||||
|
||||
static void itnand_ecc_register_types(void)
|
||||
{
|
||||
type_register_static(&itnand_ecc_info);
|
||||
}
|
||||
|
||||
type_init(itnand_ecc_register_types)
|
||||
@@ -0,0 +1,104 @@
|
||||
#include "hw/arm/ipod_touch_pcf50633_pmu.h"
|
||||
|
||||
static int pcf50633_event(I2CSlave *i2c, enum i2c_event event)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int int_to_bcd(int value) {
|
||||
int shift = 0;
|
||||
int res = 0;
|
||||
while (value > 0) {
|
||||
res |= (value % 10) << (shift++ << 2);
|
||||
value /= 10;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static uint8_t pcf50633_recv(I2CSlave *i2c)
|
||||
{
|
||||
Pcf50633State *s = PCF50633(i2c);
|
||||
printf("Reading PMU register %d\n", s->cmd);
|
||||
|
||||
time_t t = time(NULL);
|
||||
struct tm tm = *localtime(&t);
|
||||
|
||||
int res = 0;
|
||||
|
||||
switch(s->cmd) {
|
||||
case PMU_MBCS1:
|
||||
res = 0; // battery power source
|
||||
break;
|
||||
case PMU_ADCC1:
|
||||
res = 0; // battery charge voltage
|
||||
break;
|
||||
case PMU_RTCSC: // seconds
|
||||
res = int_to_bcd(tm.tm_sec);
|
||||
break;
|
||||
case PMU_RTCMN: // minutes
|
||||
res = int_to_bcd(tm.tm_min);
|
||||
break;
|
||||
case PMU_RTCHR: // hours
|
||||
res = int_to_bcd(tm.tm_hour);
|
||||
break;
|
||||
case PMU_RTCDT: // days
|
||||
res = int_to_bcd(tm.tm_mday);
|
||||
break;
|
||||
case PMU_RTCMT: // month
|
||||
res = int_to_bcd(tm.tm_mon + 1);
|
||||
break;
|
||||
case PMU_RTCYR: // year
|
||||
res = int_to_bcd(tm.tm_year - 100); // the year counts from 1900
|
||||
break;
|
||||
case 0x67:
|
||||
res = 1; // whether we should enable debug UARTS
|
||||
break;
|
||||
case 0x69:
|
||||
res = 0; // boot count error/panic
|
||||
break;
|
||||
case 0x76:
|
||||
res = 0; // unknown register
|
||||
break;
|
||||
default:
|
||||
res = 0;
|
||||
}
|
||||
|
||||
s->cmd += 1;
|
||||
return res;
|
||||
}
|
||||
|
||||
static int pcf50633_send(I2CSlave *i2c, uint8_t data)
|
||||
{
|
||||
Pcf50633State *s = PCF50633(i2c);
|
||||
s->cmd = data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pcf50633_init(Object *obj)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void pcf50633_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
|
||||
|
||||
k->event = pcf50633_event;
|
||||
k->recv = pcf50633_recv;
|
||||
k->send = pcf50633_send;
|
||||
}
|
||||
|
||||
static const TypeInfo pcf50633_info = {
|
||||
.name = TYPE_PCF50633,
|
||||
.parent = TYPE_I2C_SLAVE,
|
||||
.instance_init = pcf50633_init,
|
||||
.instance_size = sizeof(Pcf50633State),
|
||||
.class_init = pcf50633_class_init,
|
||||
};
|
||||
|
||||
static void pcf50633_register_types(void)
|
||||
{
|
||||
type_register_static(&pcf50633_info);
|
||||
}
|
||||
|
||||
type_init(pcf50633_register_types)
|
||||
@@ -0,0 +1,105 @@
|
||||
#include "hw/arm/ipod_touch_sdio.h"
|
||||
|
||||
void sdio_exec_cmd(IPodTouchSDIOState *s)
|
||||
{
|
||||
uint32_t addr = (s->arg >> 9) & 0x1ffff;
|
||||
if(addr == (1 << 8)) {
|
||||
// reading slot 1 - make sure there is a device here
|
||||
//s->resp0 = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void ipod_touch_sdio_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
|
||||
{
|
||||
fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, value, addr);
|
||||
|
||||
IPodTouchSDIOState *s = (struct IPodTouchSDIOState *) opaque;
|
||||
|
||||
switch(addr) {
|
||||
case SDIO_CMD:
|
||||
s->cmd = value;
|
||||
if(value & (1 << 31)) {
|
||||
sdio_exec_cmd(s);
|
||||
}
|
||||
break;
|
||||
case SDIO_ARGU:
|
||||
s->arg = value;
|
||||
break;
|
||||
case SDIO_CSR:
|
||||
s->csr = value;
|
||||
break;
|
||||
case SDIO_IRQMASK:
|
||||
s->irq_mask = value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t ipod_touch_sdio_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
fprintf(stderr, "%s: offset = 0x%08x\n", __func__, addr);
|
||||
|
||||
IPodTouchSDIOState *s = (struct IPodTouchSDIOState *) opaque;
|
||||
|
||||
switch (addr) {
|
||||
case SDIO_CMD:
|
||||
return s->cmd;
|
||||
case SDIO_ARGU:
|
||||
return s->arg;
|
||||
case SDIO_DSTA:
|
||||
return (1 << 0) | (1 << 4) ; // 0x1 indicates that the SDIO is ready for a CMD, (1 << 4) that the command is complete
|
||||
case SDIO_RESP0:
|
||||
return s->resp0;
|
||||
case SDIO_RESP1:
|
||||
return s->resp1;
|
||||
case SDIO_RESP2:
|
||||
return s->resp2;
|
||||
case SDIO_RESP3:
|
||||
return s->resp3;
|
||||
case SDIO_CSR:
|
||||
return s->csr;
|
||||
case SDIO_IRQMASK:
|
||||
return s->irq_mask;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ipod_touch_sdio_ops = {
|
||||
.read = ipod_touch_sdio_read,
|
||||
.write = ipod_touch_sdio_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_sdio_init(Object *obj)
|
||||
{
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
IPodTouchSDIOState *s = IPOD_TOUCH_SDIO(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &ipod_touch_sdio_ops, s, TYPE_IPOD_TOUCH_SDIO, 4096);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
}
|
||||
|
||||
static void ipod_touch_sdio_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_sdio_type_info = {
|
||||
.name = TYPE_IPOD_TOUCH_SDIO,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchSDIOState),
|
||||
.instance_init = ipod_touch_sdio_init,
|
||||
.class_init = ipod_touch_sdio_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_sdio_register_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_sdio_type_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_sdio_register_types)
|
||||
@@ -0,0 +1,153 @@
|
||||
#include "hw/arm/ipod_touch_sha1.h"
|
||||
|
||||
static uint64_t swapLong(void *X) {
|
||||
uint64_t x = (uint64_t) X;
|
||||
x = (x & 0x00000000FFFFFFFF) << 32 | (x & 0xFFFFFFFF00000000) >> 32;
|
||||
x = (x & 0x0000FFFF0000FFFF) << 16 | (x & 0xFFFF0000FFFF0000) >> 16;
|
||||
x = (x & 0x00FF00FF00FF00FF) << 8 | (x & 0xFF00FF00FF00FF00) >> 8;
|
||||
return x;
|
||||
}
|
||||
|
||||
static void flush_hw_buffer(S5L8900SHA1State *s) {
|
||||
// Flush the hardware buffer to the state buffer and clear the buffer.
|
||||
memcpy(s->buffer + s->buffer_ind, (uint8_t *)s->hw_buffer, 0x40);
|
||||
memset(s->hw_buffer, 0, 0x40);
|
||||
s->hw_buffer_dirty = false;
|
||||
s->buffer_ind += 0x40;
|
||||
}
|
||||
|
||||
static void sha1_reset(S5L8900SHA1State *s)
|
||||
{
|
||||
s->config = 0;
|
||||
s->memory_start = 0;
|
||||
s->memory_mode = 0;
|
||||
s->insize = 0;
|
||||
memset(&s->buffer, 0, SHA1_BUFFER_SIZE);
|
||||
memset(&s->hw_buffer, 0, 0x10 * sizeof(uint32_t));
|
||||
s->buffer_ind = 0;
|
||||
memset(&s->hashout, 0, 0x14);
|
||||
s->hw_buffer_dirty = false;
|
||||
s->hash_computed = false;
|
||||
}
|
||||
|
||||
static uint64_t s5l8900_sha1_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
S5L8900SHA1State *s = (S5L8900SHA1State *)opaque;
|
||||
|
||||
//fprintf(stderr, "%s: offset 0x%08x\n", __FUNCTION__, offset);
|
||||
|
||||
switch(offset) {
|
||||
case SHA_CONFIG:
|
||||
return s->config;
|
||||
case SHA_RESET:
|
||||
return 0;
|
||||
case SHA_MEMORY_START:
|
||||
return s->memory_start;
|
||||
case SHA_MEMORY_MODE:
|
||||
return s->memory_mode;
|
||||
case SHA_INSIZE:
|
||||
return s->insize;
|
||||
/* Hash result ouput */
|
||||
case 0x20 ... 0x34:
|
||||
//fprintf(stderr, "Hash out %08x\n", *(uint32_t *)&s->hashout[offset - 0x20]);
|
||||
if(!s->hash_computed) {
|
||||
// lazy compute the final hash by inspecting the last eight bytes of the buffer, which contains the length of the input data.
|
||||
uint64_t data_length = swapLong(((uint64_t *)s->buffer)[s->buffer_ind / 8 - 1]) / 8;
|
||||
|
||||
SHA_CTX ctx;
|
||||
SHA1_Init(&ctx);
|
||||
SHA1_Update(&ctx, s->buffer, data_length);
|
||||
SHA1_Final(s->hashout, &ctx);
|
||||
s->hash_computed = true;
|
||||
}
|
||||
|
||||
return *(uint32_t *)&s->hashout[offset - 0x20];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void s5l8900_sha1_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
|
||||
{
|
||||
S5L8900SHA1State *s = (S5L8900SHA1State *)opaque;
|
||||
|
||||
//fprintf(stderr, "%s: offset 0x%08x value 0x%08x\n", __FUNCTION__, offset, value);
|
||||
|
||||
switch(offset) {
|
||||
case SHA_CONFIG:
|
||||
if(value == 0x2 || value == 0xa)
|
||||
{
|
||||
if(s->hw_buffer_dirty) {
|
||||
flush_hw_buffer(s);
|
||||
}
|
||||
|
||||
if(s->memory_mode)
|
||||
{
|
||||
// we are in memory mode - gradually add the memory to the buffer
|
||||
for(int i = 0; i < s->insize / 0x40; i++) {
|
||||
cpu_physical_memory_read(s->memory_start + i * 0x40, s->buffer + s->buffer_ind, 0x40);
|
||||
s->buffer_ind += 0x40;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s->config = value;
|
||||
}
|
||||
break;
|
||||
case SHA_RESET:
|
||||
sha1_reset(s);
|
||||
break;
|
||||
case SHA_MEMORY_START:
|
||||
s->memory_start = value;
|
||||
break;
|
||||
case SHA_MEMORY_MODE:
|
||||
s->memory_mode = value;
|
||||
break;
|
||||
case SHA_INSIZE:
|
||||
assert(value <= SHA1_BUFFER_SIZE);
|
||||
s->insize = value;
|
||||
break;
|
||||
case 0x40 ... 0x7c:
|
||||
// write to the hardware buffer
|
||||
s->hw_buffer[(offset - 0x40) / 4] |= value;
|
||||
s->hw_buffer_dirty = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps sha1_ops = {
|
||||
.read = s5l8900_sha1_read,
|
||||
.write = s5l8900_sha1_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void s5l8900_sha1_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
S5L8900SHA1State *s = IPOD_TOUCH_SHA1(dev);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &sha1_ops, s, "sha1", 0x100);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
|
||||
sha1_reset(s);
|
||||
}
|
||||
|
||||
static void s5l8900_sha1_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_sha1_info = {
|
||||
.name = TYPE_IPOD_TOUCH_SHA1,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(S5L8900SHA1State),
|
||||
.instance_init = s5l8900_sha1_init,
|
||||
.class_init = s5l8900_sha1_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_machine_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_sha1_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_machine_types)
|
||||
@@ -0,0 +1,312 @@
|
||||
/*
|
||||
* S5L8900 SPI Emulation
|
||||
*
|
||||
* This code is based on:
|
||||
* - https://github.com/TrungNguyen1909/qemu-t8030/blob/master/hw/ssi/apple_spi.c (by TrungNguyen1909)
|
||||
* - https://github.com/danzatt/QEMU-s5l89xx-port/blob/master/hw/s5l8900_spi.c (by cmw)
|
||||
*/
|
||||
|
||||
#include "hw/arm/ipod_touch_spi.h"
|
||||
|
||||
static int apple_spi_word_size(S5L8900SPIState *s)
|
||||
{
|
||||
switch (R_CFG_WORD_SIZE(REG(s, R_CFG))) {
|
||||
case R_CFG_WORD_SIZE_8B:
|
||||
return 1;
|
||||
case R_CFG_WORD_SIZE_16B:
|
||||
return 2;
|
||||
case R_CFG_WORD_SIZE_32B:
|
||||
return 4;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void apple_spi_update_xfer_tx(S5L8900SPIState *s)
|
||||
{
|
||||
if (fifo8_is_empty(&s->tx_fifo)) {
|
||||
REG(s, R_STATUS) |= R_STATUS_TXEMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
static void apple_spi_update_xfer_rx(S5L8900SPIState *s)
|
||||
{
|
||||
if (!fifo8_is_empty(&s->rx_fifo)) {
|
||||
REG(s, R_STATUS) |= R_STATUS_RXREADY;
|
||||
}
|
||||
}
|
||||
|
||||
static void apple_spi_update_irq(S5L8900SPIState *s)
|
||||
{
|
||||
uint32_t irq = 0;
|
||||
uint32_t mask = 0;
|
||||
|
||||
if (REG(s, R_CFG) & R_CFG_IE_RXREADY) {
|
||||
mask |= R_STATUS_RXREADY;
|
||||
}
|
||||
if (REG(s, R_CFG) & R_CFG_IE_TXEMPTY) {
|
||||
mask |= R_STATUS_TXEMPTY;
|
||||
}
|
||||
if (REG(s, R_CFG) & R_CFG_IE_COMPLETE) {
|
||||
mask |= R_STATUS_COMPLETE;
|
||||
}
|
||||
|
||||
if (REG(s, R_STATUS) & mask) {
|
||||
irq = 1;
|
||||
}
|
||||
if (irq != s->last_irq) {
|
||||
s->last_irq = irq;
|
||||
qemu_set_irq(s->irq, irq);
|
||||
}
|
||||
}
|
||||
|
||||
static void apple_spi_update_cs(S5L8900SPIState *s)
|
||||
{
|
||||
BusState *b = BUS(s->spi);
|
||||
BusChild *kid = QTAILQ_FIRST(&b->children);
|
||||
if (kid) {
|
||||
// TODO GPIO not properly setup yet
|
||||
//qemu_set_irq(qdev_get_gpio_in_named(kid->child, SSI_GPIO_CS, 0), (REG(s, R_PIN) & R_PIN_CS) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void apple_spi_cs_set(void *opaque, int pin, int level)
|
||||
{
|
||||
S5L8900SPIState *s = S5L8900SPI(opaque);
|
||||
if (level) {
|
||||
REG(s, R_PIN) |= R_PIN_CS;
|
||||
} else {
|
||||
REG(s, R_PIN) &= ~R_PIN_CS;
|
||||
}
|
||||
apple_spi_update_cs(s);
|
||||
}
|
||||
|
||||
static void apple_spi_run(S5L8900SPIState *s)
|
||||
{
|
||||
uint32_t tx;
|
||||
uint32_t rx;
|
||||
|
||||
if (!(REG(s, R_CTRL) & R_CTRL_RUN)) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (!fifo8_is_empty(&s->tx_fifo)) {
|
||||
tx = (uint32_t)fifo8_pop(&s->tx_fifo);
|
||||
rx = ssi_transfer(s->spi, tx);
|
||||
apple_spi_update_xfer_tx(s);
|
||||
if (REG(s, R_RXCNT) > 0) {
|
||||
if (fifo8_is_full(&s->rx_fifo)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: rx overflow\n", __func__);
|
||||
REG(s, R_STATUS) |= R_STATUS_RXOVERFLOW;
|
||||
} else {
|
||||
fifo8_push(&s->rx_fifo, (uint8_t)rx);
|
||||
REG(s, R_RXCNT)--;
|
||||
apple_spi_update_xfer_rx(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fetch the remaining bytes by sending sentinel bytes.
|
||||
while (!fifo8_is_full(&s->rx_fifo) && (REG(s, R_RXCNT) > 0) && (REG(s, R_CFG) & R_CFG_AGD)) {
|
||||
rx = ssi_transfer(s->spi, 0xff);
|
||||
if (fifo8_is_full(&s->rx_fifo)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: rx overflow\n", __func__);
|
||||
REG(s, R_STATUS) |= R_STATUS_RXOVERFLOW;
|
||||
break;
|
||||
} else {
|
||||
fifo8_push(&s->rx_fifo, (uint8_t)rx);
|
||||
REG(s, R_RXCNT)--;
|
||||
apple_spi_update_xfer_rx(s);
|
||||
}
|
||||
}
|
||||
if (REG(s, R_RXCNT) == 0 && fifo8_is_empty(&s->tx_fifo)) {
|
||||
REG(s, R_STATUS) |= R_STATUS_COMPLETE;
|
||||
REG(s, R_CTRL) &= ~R_CTRL_RUN;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t s5l8900_spi_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
S5L8900SPIState *s = S5L8900SPI(opaque);
|
||||
//fprintf(stderr, "%s (base %d): read from location 0x%08x\n", __func__, s->base, addr);
|
||||
|
||||
uint32_t r;
|
||||
bool run = false;
|
||||
|
||||
r = s->regs[addr >> 2];
|
||||
switch (addr) {
|
||||
case R_RXDATA: {
|
||||
const uint8_t *buf = NULL;
|
||||
int word_size = apple_spi_word_size(s);
|
||||
uint32_t num = 0;
|
||||
if (fifo8_is_empty(&s->rx_fifo)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: rx underflow\n", __func__);
|
||||
r = 0;
|
||||
break;
|
||||
}
|
||||
buf = fifo8_pop_buf(&s->rx_fifo, word_size, &num);
|
||||
memcpy(&r, buf, num);
|
||||
if (fifo8_is_empty(&s->rx_fifo)) {
|
||||
run = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case R_STATUS: {
|
||||
int val = 0;
|
||||
val |= fifo8_num_used(&s->tx_fifo) << R_STATUS_TXFIFO_SHIFT;
|
||||
val |= fifo8_num_used(&s->rx_fifo) << R_STATUS_RXFIFO_SHIFT;
|
||||
val &= (R_STATUS_TXFIFO_MASK | R_STATUS_RXFIFO_MASK);
|
||||
r &= ~(R_STATUS_TXFIFO_MASK | R_STATUS_RXFIFO_MASK);
|
||||
r |= val;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (run) {
|
||||
apple_spi_run(s);
|
||||
}
|
||||
apple_spi_update_irq(s);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void s5l8900_spi_write(void *opaque, hwaddr addr, uint64_t data, unsigned size)
|
||||
{
|
||||
S5L8900SPIState *s = S5L8900SPI(opaque);
|
||||
//fprintf(stderr, "%s (base %d): writing 0x%08x to 0x%08x\n", __func__, s->base, data, addr);
|
||||
|
||||
uint32_t r = data;
|
||||
uint32_t *mmio = ®(s, addr);
|
||||
uint32_t old = *mmio;
|
||||
bool cs_flg = false;
|
||||
bool run = false;
|
||||
|
||||
switch (addr) {
|
||||
case R_CTRL:
|
||||
if (r & R_CTRL_TX_RESET) {
|
||||
fifo8_reset(&s->tx_fifo);
|
||||
}
|
||||
if (r & R_CTRL_RX_RESET) {
|
||||
fifo8_reset(&s->rx_fifo);
|
||||
}
|
||||
if (r & R_CTRL_RUN && !fifo8_is_empty(&s->tx_fifo)) {
|
||||
run = true;
|
||||
}
|
||||
break;
|
||||
case R_STATUS:
|
||||
r = old & (~r);
|
||||
run = true;
|
||||
break;
|
||||
case R_PIN:
|
||||
cs_flg = true;
|
||||
break;
|
||||
case R_TXDATA ... R_TXDATA + 3: {
|
||||
int word_size = apple_spi_word_size(s);
|
||||
if ((fifo8_is_full(&s->tx_fifo))
|
||||
|| (fifo8_num_free(&s->tx_fifo) < word_size)) {
|
||||
hw_error("OVERFLOW: %d\n", fifo8_num_free(&s->tx_fifo));
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: tx overflow\n", __func__);
|
||||
r = 0;
|
||||
break;
|
||||
}
|
||||
fifo8_push_all(&s->tx_fifo, (uint8_t *)&r, word_size);
|
||||
break;
|
||||
case R_CFG:
|
||||
run = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
*mmio = r;
|
||||
if (cs_flg) {
|
||||
apple_spi_update_cs(s);
|
||||
}
|
||||
if (run) {
|
||||
apple_spi_run(s);
|
||||
}
|
||||
apple_spi_update_irq(s);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps spi_ops = {
|
||||
.read = s5l8900_spi_read,
|
||||
.write = s5l8900_spi_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void s5l8900_spi_reset(DeviceState *d)
|
||||
{
|
||||
S5L8900SPIState *s = (S5L8900SPIState *)d;
|
||||
memset(s->regs, 0, sizeof(s->regs));
|
||||
fifo8_reset(&s->tx_fifo);
|
||||
fifo8_reset(&s->rx_fifo);
|
||||
}
|
||||
|
||||
static uint32_t base_addr = 0;
|
||||
|
||||
void set_spi_base(uint32_t base)
|
||||
{
|
||||
base_addr = base;
|
||||
}
|
||||
|
||||
static void s5l8900_spi_realize(DeviceState *dev, struct Error **errp)
|
||||
{
|
||||
S5L8900SPIState *s = S5L8900SPI(dev);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||
|
||||
char bus_name[32] = { 0 };
|
||||
snprintf(bus_name, sizeof(bus_name), "%s.bus", dev->id);
|
||||
s->spi = ssi_create_bus(dev, (const char *)bus_name);
|
||||
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
sysbus_init_irq(sbd, &s->cs_line);
|
||||
qdev_init_gpio_in_named(dev, apple_spi_cs_set, SSI_GPIO_CS, 1);
|
||||
char name[5];
|
||||
snprintf(name, 5, "spi%d", base_addr);
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &spi_ops, s, name, 0x100);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
s->base = base_addr;
|
||||
|
||||
fifo8_create(&s->tx_fifo, R_FIFO_DEPTH);
|
||||
fifo8_create(&s->rx_fifo, R_FIFO_DEPTH);
|
||||
|
||||
// create the peripheral
|
||||
switch(s->base) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
ssi_create_peripheral(s->spi, TYPE_IPOD_TOUCH_LCD_PANEL);
|
||||
break;
|
||||
case 2:
|
||||
{
|
||||
DeviceState *dev = ssi_create_peripheral(s->spi, TYPE_IPOD_TOUCH_MULTITOUCH);
|
||||
IPodTouchMultitouchState *mt = IPOD_TOUCH_MULTITOUCH(dev);
|
||||
s->mt = mt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void s5l8900_spi_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
dc->realize = s5l8900_spi_realize;
|
||||
dc->reset = s5l8900_spi_reset;
|
||||
}
|
||||
|
||||
static const TypeInfo s5l8900_spi_info = {
|
||||
.name = TYPE_S5L8900SPI,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(S5L8900SPIState),
|
||||
.class_init = s5l8900_spi_class_init,
|
||||
};
|
||||
|
||||
static void s5l8900_spi_register_types(void)
|
||||
{
|
||||
type_register_static(&s5l8900_spi_info);
|
||||
}
|
||||
|
||||
type_init(s5l8900_spi_register_types)
|
||||
@@ -0,0 +1,127 @@
|
||||
#include "hw/arm/ipod_touch_sysic.h"
|
||||
|
||||
static uint64_t ipod_touch_sysic_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
IPodTouchSYSICState *s = (IPodTouchSYSICState *) opaque;
|
||||
|
||||
//fprintf(stderr, "%s: offset = 0x%08x\n", __func__, addr);
|
||||
|
||||
switch (addr) {
|
||||
case POWER_ID:
|
||||
//return (3 << 24); //for older iboots
|
||||
return (2 << 0x18);
|
||||
case POWER_SETSTATE:
|
||||
case POWER_STATE:
|
||||
return s->power_state;
|
||||
case 0x7a:
|
||||
case 0x7c:
|
||||
return 1;
|
||||
case GPIO_INTLEVEL ... (GPIO_INTLEVEL + GPIO_NUMINTGROUPS * 4):
|
||||
{
|
||||
uint8_t group = (addr - GPIO_INTLEVEL) / 4;
|
||||
return s->gpio_int_level[group];
|
||||
}
|
||||
case GPIO_INTSTAT ... (GPIO_INTSTAT + GPIO_NUMINTGROUPS * 4):
|
||||
{
|
||||
uint8_t group = (addr - GPIO_INTSTAT) / 4;
|
||||
return s->gpio_int_status[group];
|
||||
}
|
||||
case GPIO_INTEN ... (GPIO_INTEN + GPIO_NUMINTGROUPS * 4):
|
||||
{
|
||||
uint8_t group = (addr - GPIO_INTEN) / 4;
|
||||
return s->gpio_int_enabled[group];
|
||||
}
|
||||
case GPIO_INTTYPE ... (GPIO_INTTYPE + GPIO_NUMINTGROUPS * 4):
|
||||
{
|
||||
uint8_t group = (addr - GPIO_INTTYPE) / 4;
|
||||
return s->gpio_int_type[group];
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipod_touch_sysic_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
|
||||
{
|
||||
IPodTouchSYSICState *s = (IPodTouchSYSICState *) opaque;
|
||||
|
||||
//fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, val, addr);
|
||||
|
||||
switch (addr) {
|
||||
case POWER_ONCTRL:
|
||||
if((val & 0x20) != 0 || (val & 0x4) != 0 || (val & POWER_ID_ADM) != 0) { break; } // make sure that we do not record the 'on' state of some devices so it appears like they are turned on immediately.
|
||||
s->power_state = val;
|
||||
break;
|
||||
case POWER_OFFCTRL:
|
||||
s->power_state = val;
|
||||
break;
|
||||
case GPIO_INTLEVEL ... (GPIO_INTLEVEL + GPIO_NUMINTGROUPS * 4):
|
||||
{
|
||||
break;
|
||||
}
|
||||
case GPIO_INTSTAT ... (GPIO_INTSTAT + GPIO_NUMINTGROUPS * 4):
|
||||
{
|
||||
uint8_t group = (addr - GPIO_INTSTAT) / 4;
|
||||
|
||||
// acknowledge the interrupts and clear the corresponding bits
|
||||
s->gpio_int_status[group] = s->gpio_int_status[group] & ~val;
|
||||
|
||||
qemu_irq_lower(s->gpio_irqs[group]);
|
||||
|
||||
break;
|
||||
}
|
||||
case GPIO_INTEN ... (GPIO_INTEN + GPIO_NUMINTGROUPS * 4):
|
||||
{
|
||||
uint8_t group = (addr - GPIO_INTEN) / 4;
|
||||
s->gpio_int_enabled[group] = val;
|
||||
break;
|
||||
}
|
||||
case GPIO_INTTYPE ... (GPIO_INTTYPE + GPIO_NUMINTGROUPS * 4):
|
||||
{
|
||||
uint8_t group = (addr - GPIO_INTTYPE) / 4;
|
||||
s->gpio_int_type[group] = val;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ipod_touch_sysic_ops = {
|
||||
.read = ipod_touch_sysic_read,
|
||||
.write = ipod_touch_sysic_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_sysic_init(Object *obj)
|
||||
{
|
||||
IPodTouchSYSICState *s = IPOD_TOUCH_SYSIC(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &ipod_touch_sysic_ops, s, TYPE_IPOD_TOUCH_SYSIC, 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
for(int grp = 0; grp < GPIO_NUMINTGROUPS; grp++) {
|
||||
sysbus_init_irq(sbd, &s->gpio_irqs[grp]);
|
||||
}
|
||||
}
|
||||
|
||||
static void ipod_touch_sysic_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_sysic_type_info = {
|
||||
.name = TYPE_IPOD_TOUCH_SYSIC,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchSYSICState),
|
||||
.instance_init = ipod_touch_sysic_init,
|
||||
.class_init = ipod_touch_sysic_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_sysic_register_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_sysic_type_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_sysic_register_types)
|
||||
@@ -0,0 +1,143 @@
|
||||
#include "hw/arm/ipod_touch_timer.h"
|
||||
|
||||
static void s5l8900_st_update(IPodTouchTimerState *s)
|
||||
{
|
||||
s->freq_out = 1000000000 / 100;
|
||||
s->tick_interval = /* bcount1 * get_ticks / freq + ((bcount2 * get_ticks / freq)*/
|
||||
muldiv64((s->bcount1 < 1000) ? 1000 : s->bcount1, NANOSECONDS_PER_SECOND, s->freq_out);
|
||||
s->next_planned_tick = 0;
|
||||
}
|
||||
|
||||
static void s5l8900_st_set_timer(IPodTouchTimerState *s)
|
||||
{
|
||||
uint64_t last = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - s->base_time;
|
||||
|
||||
s->next_planned_tick = last + (s->tick_interval - last % s->tick_interval);
|
||||
timer_mod(s->st_timer, s->next_planned_tick + s->base_time);
|
||||
s->last_tick = last;
|
||||
}
|
||||
|
||||
static void s5l8900_st_tick(void *opaque)
|
||||
{
|
||||
IPodTouchTimerState *s = (IPodTouchTimerState *)opaque;
|
||||
|
||||
if (s->status & TIMER_STATE_START) {
|
||||
//fprintf(stderr, "%s: Raising irq\n", __func__);
|
||||
qemu_irq_raise(s->irq);
|
||||
|
||||
/* schedule next interrupt */
|
||||
if(!(s->status & TIMER_STATE_MANUALUPDATE)) {
|
||||
s5l8900_st_set_timer(s);
|
||||
}
|
||||
} else {
|
||||
s->next_planned_tick = 0;
|
||||
s->last_tick = 0;
|
||||
timer_del(s->st_timer);
|
||||
}
|
||||
}
|
||||
|
||||
static void s5l8900_timer1_write(void *opaque, hwaddr addr, uint64_t value, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, value, addr);
|
||||
IPodTouchTimerState *s = (struct IPodTouchTimerState *) opaque;
|
||||
|
||||
switch(addr){
|
||||
|
||||
case TIMER_IRQSTAT:
|
||||
s->irqstat = value;
|
||||
return;
|
||||
case TIMER_IRQLATCH:
|
||||
//fprintf(stderr, "%s: lowering irq\n", __func__);
|
||||
qemu_irq_lower(s->irq);
|
||||
return;
|
||||
case TIMER_4 + TIMER_CONFIG:
|
||||
s5l8900_st_update(s);
|
||||
s->config = value;
|
||||
break;
|
||||
case TIMER_4 + TIMER_STATE:
|
||||
if (value & TIMER_STATE_START) {
|
||||
s->base_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
s5l8900_st_update(s);
|
||||
s5l8900_st_set_timer(s);
|
||||
} else if (value == TIMER_STATE_STOP) {
|
||||
timer_del(s->st_timer);
|
||||
}
|
||||
s->status = value;
|
||||
break;
|
||||
case TIMER_4 + TIMER_COUNT_BUFFER:
|
||||
s->bcount1 = s->bcreload = value;
|
||||
break;
|
||||
case TIMER_4 + TIMER_COUNT_BUFFER2:
|
||||
s->bcount2 = value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t s5l8900_timer1_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
//fprintf(stderr, "%s: read from location 0x%08x\n", __func__, addr);
|
||||
IPodTouchTimerState *s = (struct IPodTouchTimerState *) opaque;
|
||||
uint64_t elapsed_ns, ticks;
|
||||
|
||||
switch (addr) {
|
||||
case TIMER_TICKSHIGH: // needs to be fixed so that read from low first works as well
|
||||
|
||||
elapsed_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / 2; // the timer ticks twice as slow as the CPU frequency in the kernel
|
||||
ticks = clock_ns_to_ticks(s->sysclk, elapsed_ns);
|
||||
//printf("TICKS: %lld\n", ticks);
|
||||
s->ticks_high = (ticks >> 32);
|
||||
s->ticks_low = (ticks & 0xFFFFFFFF);
|
||||
return s->ticks_high;
|
||||
case TIMER_TICKSLOW:
|
||||
return s->ticks_low;
|
||||
case TIMER_IRQSTAT:
|
||||
return ~0; // s->irqstat;
|
||||
case TIMER_IRQLATCH:
|
||||
return 0xffffffff;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps timer1_ops = {
|
||||
.read = s5l8900_timer1_read,
|
||||
.write = s5l8900_timer1_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void s5l8900_timer_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
DeviceState *dev = DEVICE(sbd);
|
||||
IPodTouchTimerState *s = IPOD_TOUCH_TIMER(dev);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &timer1_ops, s, "timer1", 0x10001);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
|
||||
s->base_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
s->st_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, s5l8900_st_tick, s);
|
||||
}
|
||||
|
||||
static void s5l8900_timer_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_timer_info = {
|
||||
.name = TYPE_IPOD_TOUCH_TIMER,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchTimerState),
|
||||
.instance_init = s5l8900_timer_init,
|
||||
.class_init = s5l8900_timer_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_machine_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_timer_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_machine_types)
|
||||
@@ -0,0 +1,68 @@
|
||||
#include "hw/arm/ipod_touch_tvout.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
static uint64_t ipod_touch_tvout_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
IPodTouchTVOutState *s = (IPodTouchTVOutState *)opaque;
|
||||
|
||||
//fprintf(stderr, "%s (%d): offset = 0x%08x\n", __func__, s->index, offset);
|
||||
|
||||
// switch(offset) {
|
||||
// case SDO_IRQ:
|
||||
// return s->sdo_irq_reg;
|
||||
// }
|
||||
|
||||
return s->data[offset];
|
||||
}
|
||||
|
||||
static void ipod_touch_tvout_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
|
||||
{
|
||||
IPodTouchTVOutState *s = (IPodTouchTVOutState *)opaque;
|
||||
|
||||
//fprintf(stderr, "%s (%d): writing 0x%08x to 0x%08x\n", __func__, s->index, value, offset);
|
||||
s->data[offset] = value;
|
||||
// switch(offset) {
|
||||
// case SDO_IRQ:
|
||||
// s->sdo_irq_reg = value;
|
||||
// break;
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ipod_touch_tvout_ops = {
|
||||
.read = ipod_touch_tvout_read,
|
||||
.write = ipod_touch_tvout_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_tvout_init(Object *obj)
|
||||
{
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
IPodTouchTVOutState *s = IPOD_TOUCH_TVOUT(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &ipod_touch_tvout_ops, s, "tvout", 4096);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
}
|
||||
|
||||
static void ipod_touch_tvout_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_tvout_type_info = {
|
||||
.name = TYPE_IPOD_TOUCH_TVOUT,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchTVOutState),
|
||||
.instance_init = ipod_touch_tvout_init,
|
||||
.class_init = ipod_touch_tvout_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_tvout_register_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_tvout_type_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_tvout_register_types)
|
||||
@@ -0,0 +1,617 @@
|
||||
/*
|
||||
* Synopsys DesignWareCore for USB OTG.
|
||||
*
|
||||
* Copyright (c) 2011 Richard Ian Taylor.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "hw/platform-bus.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/arm/ipod_touch_usb_otg.h"
|
||||
|
||||
static inline size_t synopsys_usb_tx_fifo_start(synopsys_usb_state *_state, uint32_t _fifo)
|
||||
{
|
||||
if(_fifo == 0)
|
||||
return _state->gnptxfsiz >> 16;
|
||||
else
|
||||
return _state->dptxfsiz[_fifo-1] >> 16;
|
||||
}
|
||||
|
||||
static inline size_t synopsys_usb_tx_fifo_size(synopsys_usb_state *_state, uint32_t _fifo)
|
||||
{
|
||||
if(_fifo == 0)
|
||||
return _state->gnptxfsiz & 0xFFFF;
|
||||
else
|
||||
return _state->dptxfsiz[_fifo-1] & 0xFFFF;
|
||||
}
|
||||
|
||||
static void synopsys_usb_update_irq(synopsys_usb_state *_state)
|
||||
{
|
||||
_state->daintsts = 0;
|
||||
_state->gintsts &=~ (GINTMSK_OEP | GINTMSK_INEP | GINTMSK_OTG);
|
||||
|
||||
if(_state->gotgint)
|
||||
_state->gintsts |= GINTMSK_OTG;
|
||||
|
||||
int i;
|
||||
for(i = 0; i < USB_NUM_ENDPOINTS; i++)
|
||||
{
|
||||
if(_state->out_eps[i].interrupt_status & _state->doepmsk)
|
||||
{
|
||||
_state->daintsts |= 1 << (i+DAINT_OUT_SHIFT);
|
||||
if(_state->daintmsk & (1 << (i+DAINT_OUT_SHIFT)))
|
||||
_state->gintsts |= GINTMSK_OEP;
|
||||
}
|
||||
|
||||
if(_state->in_eps[i].interrupt_status & _state->diepmsk)
|
||||
{
|
||||
_state->daintsts |= 1 << (i+DAINT_IN_SHIFT);
|
||||
if(_state->daintmsk & (1 << (i+DAINT_IN_SHIFT)))
|
||||
_state->gintsts |= GINTMSK_INEP;
|
||||
}
|
||||
}
|
||||
|
||||
if((_state->pcgcctl & 3) == 0 && _state->gintmsk & _state->gintsts)
|
||||
{
|
||||
//printf("USB: IRQ triggered 0x%08x & 0x%08x.\n", _state->gintsts, _state->gintmsk);
|
||||
qemu_irq_raise(_state->irq);
|
||||
}
|
||||
else
|
||||
qemu_irq_lower(_state->irq);
|
||||
}
|
||||
|
||||
static void synopsys_usb_update_ep(synopsys_usb_state *_state, synopsys_usb_ep_state *_ep)
|
||||
{
|
||||
if(_ep->control & USB_EPCON_SETNAK)
|
||||
{
|
||||
_ep->control |= USB_EPCON_NAKSTS;
|
||||
_ep->interrupt_status |= USB_EPINT_INEPNakEff;
|
||||
_ep->control &=~ USB_EPCON_SETNAK;
|
||||
}
|
||||
|
||||
if(_ep->control & USB_EPCON_DISABLE)
|
||||
{
|
||||
_ep->interrupt_status |= USB_EPINT_EPDisbld;
|
||||
_ep->control &=~ (USB_EPCON_DISABLE | USB_EPCON_ENABLE);
|
||||
}
|
||||
}
|
||||
|
||||
static void synopsys_usb_update_in_ep(synopsys_usb_state *_state, uint8_t _ep)
|
||||
{
|
||||
synopsys_usb_ep_state *eps = &_state->in_eps[_ep];
|
||||
synopsys_usb_update_ep(_state, eps);
|
||||
|
||||
if(eps->control & USB_EPCON_ENABLE)
|
||||
;//printf("USB: IN transfer queued on %d.\n", _ep);
|
||||
}
|
||||
|
||||
static void synopsys_usb_update_out_ep(synopsys_usb_state *_state, uint8_t _ep)
|
||||
{
|
||||
synopsys_usb_ep_state *eps = &_state->out_eps[_ep];
|
||||
synopsys_usb_update_ep(_state, eps);
|
||||
|
||||
if(eps->control & USB_EPCON_ENABLE)
|
||||
;//printf("USB: OUT transfer queued on %d.\n", _ep);
|
||||
}
|
||||
|
||||
static uint32_t synopsys_usb_in_ep_read(synopsys_usb_state *_state, uint8_t _ep, hwaddr _addr)
|
||||
{
|
||||
if(_ep >= USB_NUM_ENDPOINTS)
|
||||
{
|
||||
hw_error("usb_synopsys: Tried to read from disabled EP %d.\n", _ep);
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (_addr)
|
||||
{
|
||||
case 0x00:
|
||||
return _state->in_eps[_ep].control;
|
||||
|
||||
case 0x08:
|
||||
return _state->in_eps[_ep].interrupt_status;
|
||||
|
||||
case 0x10:
|
||||
return _state->in_eps[_ep].tx_size;
|
||||
|
||||
case 0x14:
|
||||
return _state->in_eps[_ep].dma_address;
|
||||
|
||||
case 0x1C:
|
||||
return _state->in_eps[_ep].dma_buffer;
|
||||
|
||||
default:
|
||||
hw_error("usb_synopsys: bad ep read offset 0x" TARGET_FMT_plx "\n", _addr);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t synopsys_usb_out_ep_read(synopsys_usb_state *_state, int _ep, hwaddr _addr)
|
||||
{
|
||||
if(_ep >= USB_NUM_ENDPOINTS)
|
||||
{
|
||||
hw_error("usb_synopsys: Tried to read from disabled EP %d.\n", _ep);
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (_addr)
|
||||
{
|
||||
case 0x00:
|
||||
return _state->out_eps[_ep].control;
|
||||
|
||||
case 0x08:
|
||||
return _state->out_eps[_ep].interrupt_status;
|
||||
|
||||
case 0x10:
|
||||
return _state->out_eps[_ep].tx_size;
|
||||
|
||||
case 0x14:
|
||||
return _state->out_eps[_ep].dma_address;
|
||||
|
||||
case 0x1C:
|
||||
return _state->out_eps[_ep].dma_buffer;
|
||||
|
||||
default:
|
||||
hw_error("usb_synopsys: bad ep read offset 0x" TARGET_FMT_plx "\n", _addr);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint64_t synopsys_usb_read(void *opaque, hwaddr _addr, unsigned size)
|
||||
{
|
||||
synopsys_usb_state *state = (synopsys_usb_state *)opaque;
|
||||
|
||||
//printf("USB: Read 0x%08x.\n", _addr);
|
||||
|
||||
switch(_addr)
|
||||
{
|
||||
case PCGCCTL:
|
||||
return state->pcgcctl;
|
||||
|
||||
case GOTGCTL:
|
||||
return state->gotgctl;
|
||||
|
||||
case GOTGINT:
|
||||
return state->gotgint;
|
||||
|
||||
case GRSTCTL:
|
||||
return state->grstctl;
|
||||
|
||||
case GHWCFG1:
|
||||
return state->ghwcfg1;
|
||||
|
||||
case GHWCFG2:
|
||||
return state->ghwcfg2;
|
||||
|
||||
case GHWCFG3:
|
||||
return state->ghwcfg3;
|
||||
|
||||
case GHWCFG4:
|
||||
return state->ghwcfg4;
|
||||
|
||||
case GAHBCFG:
|
||||
return state->gahbcfg;
|
||||
|
||||
case GUSBCFG:
|
||||
return state->gusbcfg;
|
||||
|
||||
case GINTMSK:
|
||||
return state->gintmsk;
|
||||
|
||||
case GINTSTS:
|
||||
return state->gintsts;
|
||||
|
||||
case DIEPMSK:
|
||||
return state->diepmsk;
|
||||
|
||||
case DOEPMSK:
|
||||
return state->doepmsk;
|
||||
|
||||
case DAINTMSK:
|
||||
return state->daintmsk;
|
||||
|
||||
case DAINTSTS:
|
||||
return state->daintsts;
|
||||
|
||||
case DCTL:
|
||||
return state->dctl;
|
||||
|
||||
case DCFG:
|
||||
return state->dcfg;
|
||||
|
||||
case DSTS:
|
||||
return state->dsts;
|
||||
|
||||
case GRXSTSR:
|
||||
case GRXSTSP:
|
||||
return 0; // TODO: Do something about this?
|
||||
|
||||
case GNPTXFSTS:
|
||||
return 0xFFFFFFFF;
|
||||
|
||||
case GRXFSIZ:
|
||||
return state->grxfsiz;
|
||||
|
||||
case GNPTXFSIZ:
|
||||
return state->gnptxfsiz;
|
||||
|
||||
case DIEPTXF(1) ... DIEPTXF(USB_NUM_FIFOS+1):
|
||||
_addr -= DIEPTXF(1);
|
||||
_addr >>= 2;
|
||||
return state->dptxfsiz[_addr];
|
||||
|
||||
case USB_INREGS ... (USB_INREGS + USB_EPREGS_SIZE - 4):
|
||||
_addr -= USB_INREGS;
|
||||
return synopsys_usb_in_ep_read(state, _addr >> 5, _addr & 0x1f);
|
||||
|
||||
case USB_OUTREGS ... (USB_OUTREGS + USB_EPREGS_SIZE - 4):
|
||||
_addr -= USB_OUTREGS;
|
||||
return synopsys_usb_out_ep_read(state, _addr >> 5, _addr & 0x1f);
|
||||
|
||||
case USB_FIFO_START ... USB_FIFO_END-4:
|
||||
_addr -= USB_FIFO_START;
|
||||
return *((uint32_t*)(&state->fifos[_addr]));
|
||||
|
||||
default:
|
||||
hw_error("USB: Unhandled read address 0x%08x!\n", _addr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void synopsys_usb_in_ep_write(synopsys_usb_state *_state, int _ep, hwaddr _addr, uint32_t _val)
|
||||
{
|
||||
if(_ep >= USB_NUM_ENDPOINTS)
|
||||
{
|
||||
hw_error("usb_synopsys: Wrote to disabled EP %d.\n", _ep);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (_addr)
|
||||
{
|
||||
case 0x00:
|
||||
_state->in_eps[_ep].control = _val;
|
||||
synopsys_usb_update_in_ep(_state, _ep);
|
||||
return;
|
||||
|
||||
case 0x08:
|
||||
_state->in_eps[_ep].interrupt_status &=~ _val;
|
||||
synopsys_usb_update_irq(_state);
|
||||
return;
|
||||
|
||||
case 0x10:
|
||||
_state->in_eps[_ep].tx_size = _val;
|
||||
return;
|
||||
|
||||
case 0x14:
|
||||
_state->in_eps[_ep].dma_address = _val;
|
||||
return;
|
||||
|
||||
case 0x1C:
|
||||
_state->in_eps[_ep].dma_buffer = _val;
|
||||
return;
|
||||
|
||||
default:
|
||||
hw_error("usb_synopsys: bad ep write offset 0x" TARGET_FMT_plx "\n", _addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void synopsys_usb_out_ep_write(synopsys_usb_state *_state, int _ep, hwaddr _addr, uint32_t _val)
|
||||
{
|
||||
if(_ep >= USB_NUM_ENDPOINTS)
|
||||
{
|
||||
hw_error("usb_synopsys: Wrote to disabled EP %d.\n", _ep);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (_addr)
|
||||
{
|
||||
case 0x00:
|
||||
_state->out_eps[_ep].control = _val;
|
||||
synopsys_usb_update_out_ep(_state, _ep);
|
||||
return;
|
||||
|
||||
case 0x08:
|
||||
_state->out_eps[_ep].interrupt_status &=~ _val;
|
||||
synopsys_usb_update_irq(_state);
|
||||
return;
|
||||
|
||||
case 0x10:
|
||||
_state->out_eps[_ep].tx_size = _val;
|
||||
return;
|
||||
|
||||
case 0x14:
|
||||
_state->out_eps[_ep].dma_address = _val;
|
||||
return;
|
||||
|
||||
case 0x1C:
|
||||
_state->out_eps[_ep].dma_buffer = _val;
|
||||
return;
|
||||
|
||||
default:
|
||||
hw_error("usb_synopsys: bad ep write offset 0x" TARGET_FMT_plx "\n", _addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void synopsys_usb_write(void *opaque, hwaddr _addr, uint64_t _val, unsigned size)
|
||||
{
|
||||
synopsys_usb_state *state = (synopsys_usb_state *)opaque;
|
||||
|
||||
//printf("USB: Write 0x%08x to 0x%08x.\n", _val, _addr);
|
||||
|
||||
switch(_addr)
|
||||
{
|
||||
case PCGCCTL:
|
||||
state->pcgcctl = _val;
|
||||
synopsys_usb_update_irq(state);
|
||||
return;
|
||||
|
||||
case GOTGCTL:
|
||||
state->gotgctl = _val;
|
||||
break;
|
||||
|
||||
case GOTGINT:
|
||||
state->gotgint &=~ _val;
|
||||
synopsys_usb_update_irq(state);
|
||||
return;
|
||||
|
||||
case GRSTCTL:
|
||||
if(_val & GRSTCTL_CORESOFTRESET)
|
||||
{
|
||||
state->grstctl = GRSTCTL_CORESOFTRESET;
|
||||
|
||||
// Do reset stuff
|
||||
// if(state->server_host)
|
||||
// {
|
||||
// tcp_usb_cleanup(&state->tcp_state);
|
||||
// tcp_usb_init(&state->tcp_state, synopsys_usb_tcp_callback, NULL, state);
|
||||
|
||||
// printf("Connecting to USB server at %s:%d...\n",
|
||||
// state->server_host, state->server_port);
|
||||
|
||||
// int ret = tcp_usb_connect(&state->tcp_state, state->server_host, state->server_port);
|
||||
// if(ret < 0)
|
||||
// hw_error("Failed to connect to USB server (%d).\n", ret);
|
||||
|
||||
// printf("Connected to USB server.\n");
|
||||
// }
|
||||
|
||||
state->grstctl &= ~GRSTCTL_CORESOFTRESET;
|
||||
state->grstctl |= GRSTCTL_AHBIDLE;
|
||||
state->gintsts |= GINTMSK_RESET;
|
||||
synopsys_usb_update_irq(state);
|
||||
}
|
||||
else if(_val == 0)
|
||||
state->grstctl = _val;
|
||||
|
||||
return;
|
||||
|
||||
case GINTMSK:
|
||||
state->gintmsk = _val;
|
||||
synopsys_usb_update_irq(state);
|
||||
break;
|
||||
|
||||
case GINTSTS:
|
||||
state->gintsts &=~ _val;
|
||||
synopsys_usb_update_irq(state);
|
||||
return;
|
||||
|
||||
case DOEPMSK:
|
||||
state->doepmsk = _val;
|
||||
synopsys_usb_update_irq(state);
|
||||
return;
|
||||
|
||||
case DIEPMSK:
|
||||
state->diepmsk = _val;
|
||||
synopsys_usb_update_irq(state);
|
||||
return;
|
||||
|
||||
case DAINTMSK:
|
||||
state->daintmsk = _val;
|
||||
synopsys_usb_update_irq(state);
|
||||
return;
|
||||
|
||||
case DAINTSTS:
|
||||
state->daintsts &=~ _val;
|
||||
synopsys_usb_update_irq(state);
|
||||
return;
|
||||
|
||||
case GAHBCFG:
|
||||
state->gahbcfg = _val;
|
||||
return;
|
||||
|
||||
case GUSBCFG:
|
||||
state->gusbcfg = _val;
|
||||
return;
|
||||
|
||||
case DCTL:
|
||||
if((_val & DCTL_SGNPINNAK) != (state->dctl & DCTL_SGNPINNAK)
|
||||
&& (_val & DCTL_SGNPINNAK))
|
||||
{
|
||||
state->gintsts |= GINTMSK_GINNAKEFF;
|
||||
_val &=~ DCTL_SGNPINNAK;
|
||||
}
|
||||
|
||||
if((_val & DCTL_SGOUTNAK) != (state->dctl & DCTL_SGOUTNAK)
|
||||
&& (_val & DCTL_SGOUTNAK))
|
||||
{
|
||||
state->gintsts |= GINTMSK_GOUTNAKEFF;
|
||||
_val &=~ DCTL_SGOUTNAK;
|
||||
}
|
||||
|
||||
state->dctl = _val;
|
||||
synopsys_usb_update_irq(state);
|
||||
return;
|
||||
|
||||
case DCFG:
|
||||
//printf("USB: dcfg = 0x%08x.\n", _val);
|
||||
state->dcfg = _val;
|
||||
return;
|
||||
|
||||
case GRXFSIZ:
|
||||
state->grxfsiz = _val;
|
||||
return;
|
||||
|
||||
case GNPTXFSIZ:
|
||||
state->gnptxfsiz = _val;
|
||||
return;
|
||||
|
||||
case DIEPTXF(1) ... DIEPTXF(USB_NUM_FIFOS+1):
|
||||
_addr -= DIEPTXF(1);
|
||||
_addr >>= 2;
|
||||
state->dptxfsiz[_addr] = _val;
|
||||
return;
|
||||
|
||||
case USB_INREGS ... (USB_INREGS + USB_EPREGS_SIZE - 4):
|
||||
_addr -= USB_INREGS;
|
||||
synopsys_usb_in_ep_write(state, _addr >> 5, _addr & 0x1f, _val);
|
||||
return;
|
||||
|
||||
case USB_OUTREGS ... (USB_OUTREGS + USB_EPREGS_SIZE - 4):
|
||||
_addr -= USB_OUTREGS;
|
||||
synopsys_usb_out_ep_write(state, _addr >> 5, _addr & 0x1f, _val);
|
||||
return;
|
||||
|
||||
case USB_FIFO_START ... USB_FIFO_END-4:
|
||||
_addr -= USB_FIFO_START;
|
||||
*((uint32_t*)(&state->fifos[_addr])) = _val;
|
||||
return;
|
||||
|
||||
default:
|
||||
hw_error("USB: Unhandled write address 0x%08x!\n", _addr);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps usb_otg_ops = {
|
||||
.read = synopsys_usb_read,
|
||||
.write = synopsys_usb_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void s5l8900_usb_otg_reset(DeviceState *d)
|
||||
{
|
||||
synopsys_usb_state *state = S5L8900USBOTG(d);
|
||||
|
||||
printf("USB: cfg = 0x%08x, 0x%08x, 0x%08x, 0x%08x.\n",
|
||||
state->ghwcfg1,
|
||||
state->ghwcfg2,
|
||||
state->ghwcfg3,
|
||||
state->ghwcfg4);
|
||||
|
||||
state->pcgcctl = 3;
|
||||
|
||||
state->gahbcfg = 0;
|
||||
state->gusbcfg = 0;
|
||||
|
||||
state->dctl = 0;
|
||||
state->dcfg = 0;
|
||||
state->dsts = 0;
|
||||
|
||||
state->gotgctl = 0;
|
||||
state->gotgint = 0;
|
||||
|
||||
state->gintmsk = 0;
|
||||
state->gintsts = 0;
|
||||
|
||||
state->daintmsk = 0;
|
||||
state->daintsts = 0;
|
||||
|
||||
state->diepmsk = 0;
|
||||
state->doepmsk = 0;
|
||||
|
||||
state->grxfsiz = 0x100;
|
||||
state->gnptxfsiz = (0x100 << 16) | 0x100;
|
||||
|
||||
uint32_t counter = 0x200;
|
||||
int i;
|
||||
for(i = 0; i < USB_NUM_FIFOS; i++)
|
||||
{
|
||||
state->dptxfsiz[i] = (counter << 16) | 0x100;
|
||||
counter += 0x100;
|
||||
}
|
||||
|
||||
for(i = 0; i < USB_NUM_ENDPOINTS; i++)
|
||||
{
|
||||
synopsys_usb_ep_state *in = &state->in_eps[i];
|
||||
in->control = 0;
|
||||
in->dma_address = 0;
|
||||
in->fifo = 0;
|
||||
in->tx_size = 0;
|
||||
|
||||
synopsys_usb_ep_state *out = &state->out_eps[i];
|
||||
out->control = 0;
|
||||
out->dma_address = 0;
|
||||
out->fifo = 0;
|
||||
out->tx_size = 0;
|
||||
}
|
||||
|
||||
synopsys_usb_update_irq(state);
|
||||
}
|
||||
|
||||
static void s5l8900_usb_otg_init1(Object *obj)
|
||||
{
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
synopsys_usb_state *s = S5L8900USBOTG(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &usb_otg_ops, s, "usb_otg", 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
}
|
||||
|
||||
// Helper for adding to a machine
|
||||
DeviceState *ipod_touch_init_usb_otg(qemu_irq _irq, uint32_t _hwcfg[4])
|
||||
{
|
||||
DeviceState *dev = qdev_new(TYPE_S5L8900USBOTG);
|
||||
synopsys_usb_state *state = S5L8900USBOTG(dev);
|
||||
|
||||
state->ghwcfg1 = _hwcfg[0];
|
||||
state->ghwcfg2 = _hwcfg[1];
|
||||
state->ghwcfg3 = _hwcfg[2];
|
||||
state->ghwcfg4 = _hwcfg[3];
|
||||
|
||||
SysBusDevice *sdev = SYS_BUS_DEVICE(dev);
|
||||
sysbus_connect_irq(sdev, 0, _irq);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
static void s5l8900_usb_otg_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
dc->reset = s5l8900_usb_otg_reset;
|
||||
}
|
||||
|
||||
static const TypeInfo s5l8900_usb_otg_info = {
|
||||
.name = TYPE_S5L8900USBOTG,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(synopsys_usb_state),
|
||||
.instance_init = s5l8900_usb_otg_init1,
|
||||
.class_init = s5l8900_usb_otg_class_init,
|
||||
};
|
||||
|
||||
static void s5l8900_usb_otg_register_types(void)
|
||||
{
|
||||
type_register_static(&s5l8900_usb_otg_info);
|
||||
}
|
||||
|
||||
type_init(s5l8900_usb_otg_register_types)
|
||||
@@ -58,5 +58,6 @@ arm_ss.add(when: 'CONFIG_FSL_IMX7', if_true: files('fsl-imx7.c', 'mcimx7d-sabre.
|
||||
arm_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmu-common.c', 'smmuv3.c'))
|
||||
arm_ss.add(when: 'CONFIG_FSL_IMX6UL', if_true: files('fsl-imx6ul.c', 'mcimx6ul-evk.c'))
|
||||
arm_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_soc.c'))
|
||||
arm_ss.add(when: 'CONFIG_IPOD_TOUCH', if_true: files('ipod_touch.c', 'ipod_touch_spi.c', 'ipod_touch_sysic.c', 'ipod_touch_aes.c', 'ipod_touch_sha1.c', 'ipod_touch_usb_otg.c', 'ipod_touch_8900_engine.c', 'ipod_touch_nand.c', 'ipod_touch_nand_ecc.c', 'ipod_touch_pcf50633_pmu.c', 'ipod_touch_adm.c', 'ipod_touch_chipid.c', 'ipod_touch_tvout.c', 'ipod_touch_lcd_panel.c', 'ipod_touch_multitouch.c', 'ipod_touch_lcd.c', 'ipod_touch_lis302dl.c', 'ipod_touch_aes.c', 'ipod_touch_sha1.c', 'ipod_touch_timer.c', 'ipod_touch_clock.c', 'ipod_touch_gpio.c', 'ipod_touch_sdio.c'))
|
||||
|
||||
hw_arch += {'arm': arm_ss}
|
||||
+17
-5
@@ -83,6 +83,8 @@ static void pl080_update(PL080State *s)
|
||||
bool tclevel = (s->tc_int & s->tc_mask);
|
||||
bool errlevel = (s->err_int & s->err_mask);
|
||||
|
||||
//printf("(DMA) Setting irq: %d\n", errlevel || tclevel);
|
||||
|
||||
qemu_set_irq(s->interr, errlevel);
|
||||
qemu_set_irq(s->inttc, tclevel);
|
||||
qemu_set_irq(s->irq, errlevel || tclevel);
|
||||
@@ -142,12 +144,12 @@ again:
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
if ((req & (1u << dest_id)) == 0)
|
||||
size = 0;
|
||||
// if ((req & (1u << dest_id)) == 0)
|
||||
// size = 0;
|
||||
break;
|
||||
case 2:
|
||||
if ((req & (1u << src_id)) == 0)
|
||||
size = 0;
|
||||
// if ((req & (1u << src_id)) == 0)
|
||||
// size = 0;
|
||||
break;
|
||||
case 3:
|
||||
if ((req & (1u << src_id)) == 0
|
||||
@@ -155,8 +157,9 @@ again:
|
||||
size = 0;
|
||||
break;
|
||||
}
|
||||
if (!size)
|
||||
if (!size) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Transfer one element. */
|
||||
/* ??? Should transfer multiple elements for a burst request. */
|
||||
@@ -179,6 +182,7 @@ again:
|
||||
ch->dest += swidth;
|
||||
}
|
||||
|
||||
//printf("Transfer size: %d, destination: 0x%08x\n", size, ch->dest);
|
||||
size--;
|
||||
ch->ctrl = (ch->ctrl & 0xfffff000) | size;
|
||||
if (size == 0) {
|
||||
@@ -204,6 +208,7 @@ again:
|
||||
ch->conf &= ~PL080_CCONF_E;
|
||||
}
|
||||
if (ch->ctrl & PL080_CCTRL_I) {
|
||||
//printf("Setting interrupt status of channel %d\n", c);
|
||||
s->tc_int |= 1 << c;
|
||||
}
|
||||
}
|
||||
@@ -212,6 +217,7 @@ again:
|
||||
if (--s->running)
|
||||
s->running = 1;
|
||||
}
|
||||
pl080_update(s);
|
||||
}
|
||||
|
||||
static uint64_t pl080_read(void *opaque, hwaddr offset,
|
||||
@@ -289,24 +295,30 @@ static void pl080_write(void *opaque, hwaddr offset,
|
||||
PL080State *s = (PL080State *)opaque;
|
||||
int i;
|
||||
|
||||
//fprintf(stderr, "%s: writing 0x%08x to 0x%08x\n", __func__, value, offset);
|
||||
|
||||
if (offset >= 0x100 && offset < 0x200) {
|
||||
i = (offset & 0xe0) >> 5;
|
||||
if (i >= s->nchannels)
|
||||
goto bad_offset;
|
||||
switch ((offset >> 2) & 7) {
|
||||
case 0: /* SrcAddr */
|
||||
//printf("%s: setting source address of channel %d to 0x%08x\n", __func__, i, value);
|
||||
s->chan[i].src = value;
|
||||
break;
|
||||
case 1: /* DestAddr */
|
||||
//printf("%s: setting destination address of channel %d to 0x%08x\n", __func__, i, value);
|
||||
s->chan[i].dest = value;
|
||||
break;
|
||||
case 2: /* LLI */
|
||||
s->chan[i].lli = value;
|
||||
break;
|
||||
case 3: /* Control */
|
||||
//printf("%s: setting control of channel %d to 0x%08x (transfer size: %d)\n", __func__, i, value, value & 0xfff);
|
||||
s->chan[i].ctrl = value;
|
||||
break;
|
||||
case 4: /* Configuration */
|
||||
//printf("%s: setting configuration of channel %d to 0x%08x\n", __func__, i, value);
|
||||
s->chan[i].conf = value;
|
||||
pl080_run(s);
|
||||
break;
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* IPod Touch I2C Bus Serial Interface Emulation
|
||||
*
|
||||
* Copyright (C) 2012 Samsung Electronics Co Ltd.
|
||||
* Maksim Kozlov, <m.kozlov@samsung.com>
|
||||
* Igor Mitsyanko, <i.mitsyanko@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "hw/i2c/ipod_touch_i2c.h"
|
||||
|
||||
static void s5l8900_i2c_update(IPodTouchI2CState *s)
|
||||
{
|
||||
uint16_t level;
|
||||
level = (s->status & S5L8900_IICSTAT_START) &&
|
||||
(s->control & S5L8900_IICCON_IRQEN);
|
||||
|
||||
if (s->control & S5L8900_IICCON_IRQPEND)
|
||||
level = 0;
|
||||
|
||||
qemu_irq_raise(s->irq);
|
||||
}
|
||||
|
||||
static int s5l8900_i2c_receive(IPodTouchI2CState *s)
|
||||
{
|
||||
int r;
|
||||
r = i2c_recv(s->bus);
|
||||
s5l8900_i2c_update(s);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int s5l8900_i2c_send(IPodTouchI2CState *s, uint8_t data)
|
||||
{
|
||||
if (!(s->status & S5L8900_IICSTAT_LASTBIT)) {
|
||||
s->status |= S5L8900_IICCON_ACKEN;
|
||||
s->data = data;
|
||||
s->iicreg20 |= 0x100;
|
||||
i2c_send(s->bus, s->data);
|
||||
}
|
||||
s5l8900_i2c_update(s);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* I2C read function */
|
||||
static uint64_t ipod_touch_i2c_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
IPodTouchI2CState *s = (IPodTouchI2CState *)opaque;
|
||||
|
||||
//fprintf(stderr, "s5l8900_i2c_read(): offset = 0x%08x\n", offset);
|
||||
|
||||
switch (offset) {
|
||||
case I2CCON:
|
||||
return s->control;
|
||||
case I2CSTAT:
|
||||
return s->status;
|
||||
case I2CADD:
|
||||
return s->address;
|
||||
case I2CDS:
|
||||
s->iicreg20 |= 0x100;
|
||||
s->data = s5l8900_i2c_receive(s);
|
||||
return s->data;
|
||||
case I2CLC:
|
||||
return s->line_ctrl;
|
||||
case IICREG20:
|
||||
{
|
||||
// clear the flags
|
||||
uint32_t tmp_reg20 = s->iicreg20;
|
||||
s->iicreg20 &= ~0x100;
|
||||
s->iicreg20 &= ~0x2000;
|
||||
return tmp_reg20;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
//hw_error("s5l8900.i2c: bad read offset 0x" TARGET_FMT_plx "\n", offset);
|
||||
//fprintf(stderr, "%s: bad read offset 0x%08x\n", __func__, offset);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* I2C write function */
|
||||
static void ipod_touch_i2c_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
|
||||
{
|
||||
IPodTouchI2CState *s = (IPodTouchI2CState *)opaque;
|
||||
int mode;
|
||||
|
||||
//fprintf(stderr, "s5l8900_i2c_write: offset = 0x%08x, val = 0x%08x\n", offset, value);
|
||||
|
||||
qemu_irq_lower(s->irq);
|
||||
|
||||
switch (offset) {
|
||||
case I2CCON:
|
||||
if(value & ~(S5L8900_IICCON_ACKEN)) {
|
||||
s->iicreg20 |= 0x100;
|
||||
}
|
||||
if((value & 0x10) && (s->status == 0x90)) {
|
||||
s->iicreg20 |= 0x2000;
|
||||
}
|
||||
s->control = value & 0xff;
|
||||
|
||||
qemu_irq_raise(s->irq);
|
||||
break;
|
||||
|
||||
case I2CSTAT:
|
||||
/* We have to make sure we don't miss an end transfer */
|
||||
if((!s->active) && ((s->status >> 6) != ((value >> 6)))) {
|
||||
s->status = value & 0xff;
|
||||
/* If they toggle the tx bit then we have to force an end transfer before mode update */
|
||||
} else if((s->active) && ((s->status >> 6) != ((value >> 6)))) {
|
||||
i2c_end_transfer(s->bus);
|
||||
s->active=0;
|
||||
s->status = value & 0xff;
|
||||
s->status |= S5L8900_IICSTAT_TXRXEN;
|
||||
break;
|
||||
}
|
||||
mode = (s->status >> 6) & 0x3;
|
||||
if (value & S5L8900_IICSTAT_TXRXEN) {
|
||||
/* IIC-bus data output enable/disable bit */
|
||||
switch(mode) {
|
||||
case SR_MODE:
|
||||
s->data = s5l8900_i2c_receive(s);
|
||||
break;
|
||||
case ST_MODE:
|
||||
s->data = s5l8900_i2c_receive(s);
|
||||
break;
|
||||
case MR_MODE:
|
||||
if (value & S5L8900_IICSTAT_START) {
|
||||
/* START condition */
|
||||
s->status &= ~S5L8900_IICSTAT_LASTBIT;
|
||||
|
||||
s->iicreg20 |= 0x100;
|
||||
s->active = 1;
|
||||
i2c_start_transfer(s->bus, s->data >> 1, 1);
|
||||
} else {
|
||||
i2c_end_transfer(s->bus);
|
||||
s->active = 0;
|
||||
s->status |= S5L8900_IICSTAT_TXRXEN;
|
||||
}
|
||||
break;
|
||||
case MT_MODE:
|
||||
if (value & S5L8900_IICSTAT_START) {
|
||||
/* START condition */
|
||||
s->status &= ~S5L8900_IICSTAT_LASTBIT;
|
||||
|
||||
s->iicreg20 |= 0x100;
|
||||
s->active = 1;
|
||||
i2c_start_transfer(s->bus, s->data >> 1, 0);
|
||||
} else {
|
||||
i2c_end_transfer(s->bus);
|
||||
s->active = 0;
|
||||
s->status |= S5L8900_IICSTAT_TXRXEN;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
s5l8900_i2c_update(s);
|
||||
break;
|
||||
|
||||
case I2CADD:
|
||||
s->address = value & 0xff;
|
||||
break;
|
||||
|
||||
case I2CDS:
|
||||
s5l8900_i2c_send(s, value & 0xff);
|
||||
break;
|
||||
|
||||
case I2CLC:
|
||||
s->line_ctrl = value & 0xff;
|
||||
break;
|
||||
|
||||
case IICREG20:
|
||||
//s->iicreg20 &= ~value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
//hw_error("s5l8900.i2c: bad write offset 0x" TARGET_FMT_plx "\n", offset);
|
||||
//fprintf(stderr, "%s: bad write offset 0x%08x\n", __func__, offset);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ipod_touch_i2c_ops = {
|
||||
.read = ipod_touch_i2c_read,
|
||||
.write = ipod_touch_i2c_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ipod_touch_i2c_init(Object *obj)
|
||||
{
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
IPodTouchI2CState *s = IPOD_TOUCH_I2C(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
memory_region_init_io(&s->iomem, obj, &ipod_touch_i2c_ops, s, TYPE_IPOD_TOUCH_I2C, 0x100);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
s->bus = i2c_init_bus(dev, "i2c");
|
||||
}
|
||||
|
||||
static void ipod_touch_i2c_reset(DeviceState *d)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void ipod_touch_i2c_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
dc->reset = ipod_touch_i2c_reset;
|
||||
}
|
||||
|
||||
static const TypeInfo ipod_touch_i2c_type_info = {
|
||||
.name = TYPE_IPOD_TOUCH_I2C,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(IPodTouchI2CState),
|
||||
.instance_init = ipod_touch_i2c_init,
|
||||
.class_init = ipod_touch_i2c_class_init,
|
||||
};
|
||||
|
||||
static void ipod_touch_i2c_register_types(void)
|
||||
{
|
||||
type_register_static(&ipod_touch_i2c_type_info);
|
||||
}
|
||||
|
||||
type_init(ipod_touch_i2c_register_types)
|
||||
@@ -16,4 +16,5 @@ i2c_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_i2c.c'))
|
||||
i2c_ss.add(when: 'CONFIG_PPC4XX', if_true: files('ppc4xx_i2c.c'))
|
||||
i2c_ss.add(when: 'CONFIG_PCA954X', if_true: files('i2c_mux_pca954x.c'))
|
||||
i2c_ss.add(when: 'CONFIG_PMBUS', if_true: files('pmbus_device.c'))
|
||||
arm_ss.add(when: 'CONFIG_IPOD_TOUCH', if_true: files('ipod_touch_i2c.c'))
|
||||
softmmu_ss.add_all(when: 'CONFIG_I2C', if_true: i2c_ss)
|
||||
@@ -8,6 +8,9 @@ config I8259
|
||||
config PL190
|
||||
bool
|
||||
|
||||
config PL192
|
||||
bool
|
||||
|
||||
config IOAPIC
|
||||
bool
|
||||
select I8259
|
||||
|
||||
@@ -19,6 +19,7 @@ softmmu_ss.add(when: 'CONFIG_IMX', if_true: files('imx_avic.c', 'imx_gpcv2.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_IOAPIC', if_true: files('ioapic_common.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_OPENPIC', if_true: files('openpic.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_PL190', if_true: files('pl190.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_PL192', if_true: files('pl192.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_REALVIEW', if_true: files('realview_gic.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_intctl.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_XILINX', if_true: files('xilinx_intc.c'))
|
||||
|
||||
+439
@@ -0,0 +1,439 @@
|
||||
/*
|
||||
* ARM PrimeCell PL192 Vector Interrupt Controller
|
||||
*
|
||||
* Copyright (c) 2009 Samsung Electronics.
|
||||
* Contributed by Kirill Batuzov <batuzovk@ispras.ru>
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/hw.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/intc/pl192.h"
|
||||
|
||||
extern CPUState *getMainCpuEnv(void);
|
||||
|
||||
|
||||
static void pl192_update(PL192State *);
|
||||
|
||||
static void pl192_raise(PL192State *s, int is_fiq)
|
||||
{
|
||||
if (is_fiq) {
|
||||
if (s->fiq) {
|
||||
/* Raise parent FIQ */
|
||||
qemu_irq_raise(s->fiq);
|
||||
} else {
|
||||
if (s->daisy) {
|
||||
/* FIQ is directly propagated through daisy chain */
|
||||
pl192_raise(s->daisy, is_fiq);
|
||||
} else {
|
||||
fprintf(stderr, "pl192i cannot raise FIQ. This usually means that "
|
||||
"initialization was done incorrectly.\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (s->irq) {
|
||||
/* Raise parent IRQ */
|
||||
qemu_irq_raise(s->irq);
|
||||
} else {
|
||||
if (s->daisy) {
|
||||
/* Setup daisy input of the next chained contorller and force
|
||||
it to update it's state */
|
||||
s->daisy->daisy_vectaddr = s->address;
|
||||
s->daisy->daisy_callback = s;
|
||||
s->daisy->daisy_input = 1;
|
||||
pl192_update(s->daisy);
|
||||
} else {
|
||||
// TODO Needs urgent fixing!
|
||||
hw_error("pl192: cannot raise IRQ. This usually means that initialization was done incorrectly.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void pl192_lower(PL192State *s, int is_fiq)
|
||||
{
|
||||
/* Lower parrent interrupt if there is one */
|
||||
if (is_fiq && s->fiq) {
|
||||
qemu_irq_lower(s->fiq);
|
||||
}
|
||||
if (!is_fiq && s->irq) {
|
||||
qemu_irq_lower(s->irq);
|
||||
}
|
||||
/* Propagate to the previous controller in chain if needed */
|
||||
if (s->daisy) {
|
||||
if (!is_fiq) {
|
||||
s->daisy->daisy_input = 0;
|
||||
pl192_update(s->daisy);
|
||||
} else {
|
||||
pl192_lower(s->daisy, is_fiq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Find interrupt of the highest priority */
|
||||
static uint32_t pl192_priority_sorter(PL192State *s)
|
||||
{
|
||||
int i;
|
||||
uint32_t prio_irq[PL192_PRIO_LEVELS];
|
||||
|
||||
for (i = 0; i < PL192_PRIO_LEVELS; i++) {
|
||||
prio_irq[i] = PL192_NO_IRQ;
|
||||
}
|
||||
if (s->daisy_input) {
|
||||
prio_irq[s->daisy_priority] = PL192_DAISY_IRQ;
|
||||
}
|
||||
for (i = PL192_INT_SOURCES - 1; i >= 0; i--) {
|
||||
if (s->irq_status & (1 << i)) {
|
||||
prio_irq[s->vect_priority[i]] = i;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < PL192_PRIO_LEVELS; i++) {
|
||||
if ((s->sw_priority_mask & (1 << i)) &&
|
||||
prio_irq[i] <= PL192_DAISY_IRQ) {
|
||||
return prio_irq[i];
|
||||
}
|
||||
}
|
||||
return PL192_NO_IRQ;
|
||||
}
|
||||
|
||||
static void pl192_update(PL192State *s)
|
||||
{
|
||||
/* TODO: does SOFTINT affects IRQ_STATUS??? */
|
||||
s->irq_status = (s->rawintr | s->softint) & s->intenable & ~s->intselect;
|
||||
s->fiq_status = (s->rawintr | s->softint) & s->intenable & s->intselect;
|
||||
if (s->fiq_status) {
|
||||
pl192_raise(s, 1);
|
||||
} else {
|
||||
pl192_lower(s, 1);
|
||||
}
|
||||
if (s->irq_status || s->daisy_input) {
|
||||
s->current_highest = pl192_priority_sorter(s);
|
||||
if (s->current_highest < PL192_INT_SOURCES) {
|
||||
s->address = s->vect_addr[s->current_highest];
|
||||
} else {
|
||||
s->address = s->daisy_vectaddr;
|
||||
}
|
||||
if (s->current_highest != s->current) {
|
||||
if (s->current_highest < PL192_INT_SOURCES) {
|
||||
if (s->vect_priority[s->current_highest] >= s->priority) {
|
||||
return ;
|
||||
}
|
||||
}
|
||||
if (s->current_highest == PL192_DAISY_IRQ) {
|
||||
if (s->daisy_priority >= s->priority) {
|
||||
return ;
|
||||
}
|
||||
}
|
||||
if (s->current_highest <= PL192_DAISY_IRQ) {
|
||||
pl192_raise(s, 0);
|
||||
} else {
|
||||
pl192_lower(s, 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s->current_highest = PL192_NO_IRQ;
|
||||
pl192_lower(s, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Set priority level when an interrupt have been acknoledged by CPU.
|
||||
Also save interrupt id and priority to stack so it can be restored
|
||||
lately. */
|
||||
static inline void pl192_mask_priority(PL192State *s)
|
||||
{
|
||||
if (s->stack_i >= PL192_INT_SOURCES) {
|
||||
hw_error("pl192: internal error (trying to mask when there are no more sources)\n");
|
||||
}
|
||||
s->stack_i++;
|
||||
if (s->current == PL192_DAISY_IRQ) {
|
||||
s->priority = s->daisy_priority;
|
||||
} else {
|
||||
s->priority = s->vect_priority[s->current];
|
||||
}
|
||||
s->priority_stack[s->stack_i] = s->priority;
|
||||
s->irq_stack[s->stack_i] = s->current;
|
||||
}
|
||||
|
||||
/* Set priority level when interrupt have been successfully processed by CPU.
|
||||
Also restore previous interrupt id and priority level. */
|
||||
static inline void pl192_unmask_priority(PL192State *s)
|
||||
{
|
||||
if (s->stack_i < 1) {
|
||||
return; // simply ignore this event
|
||||
//hw_error("pl192: internal error (mask stack insufficient)\n");
|
||||
}
|
||||
s->stack_i--;
|
||||
s->priority = s->priority_stack[s->stack_i];
|
||||
s->current = s->irq_stack[s->stack_i];
|
||||
}
|
||||
|
||||
/* IRQ was acknoledged by CPU. Update controller state accordingly */
|
||||
static uint32_t pl192_irq_ack(PL192State *s)
|
||||
{
|
||||
int is_daisy = (s->current_highest == PL192_DAISY_IRQ);
|
||||
uint32_t res = s->address;
|
||||
|
||||
s->current = s->current_highest;
|
||||
pl192_mask_priority(s);
|
||||
if (is_daisy) {
|
||||
pl192_mask_priority(s->daisy_callback);
|
||||
}
|
||||
pl192_update(s);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* IRQ was processed by CPU. Update controller state accrodingly */
|
||||
static void pl192_irq_fin(PL192State *s)
|
||||
{
|
||||
int is_daisy = (s->current == PL192_DAISY_IRQ);
|
||||
|
||||
pl192_unmask_priority(s);
|
||||
if (is_daisy) {
|
||||
pl192_unmask_priority(s->daisy_callback);
|
||||
}
|
||||
pl192_update(s);
|
||||
|
||||
/* hmm is this right?*/
|
||||
/*
|
||||
if (s->current == PL192_NO_IRQ && (s->current_highest >= PL192_INT_SOURCES)) {
|
||||
pl192_lower(s, 0);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
static uint64_t pl192_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
PL192State *s = (PL192State *) opaque;
|
||||
|
||||
if (offset & 3) {
|
||||
fprintf(stderr, "pl192: bad read offset (1) " TARGET_FMT_plx "\n", offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (offset >= 0xfe0 && offset < 0x1000) {
|
||||
unsigned char pl192_id[] = { 0x92, 0x11, 0x04, 0x00, 0x0D, 0xF0, 0x05, 0xB1 };
|
||||
return pl192_id[(offset - 0xfe0) >> 2];
|
||||
}
|
||||
if (offset >= 0x100 && offset < 0x180) {
|
||||
return s->vect_addr[(offset - 0x100) >> 2];
|
||||
}
|
||||
if (offset >= 0x200 && offset < 0x280) {
|
||||
return s->vect_priority[(offset - 0x200) >> 2];
|
||||
}
|
||||
|
||||
switch (offset) {
|
||||
case PL192_IRQSTATUS:
|
||||
//fprintf(stderr, "%s: irqstatus 0x%08x\n", __FUNCTION__, s->irq_status);
|
||||
return s->irq_status;
|
||||
case PL192_FIQSTATUS:
|
||||
return s->fiq_status;
|
||||
case PL192_RAWINTR:
|
||||
return s->rawintr;
|
||||
case PL192_INTSELECT:
|
||||
return s->intselect;
|
||||
case PL192_INTENABLE:
|
||||
return s->intenable;
|
||||
case PL192_SOFTINT:
|
||||
return s->softint;
|
||||
case PL192_PROTECTION:
|
||||
return s->protection;
|
||||
case PL192_SWPRIORITYMASK:
|
||||
return s->sw_priority_mask;
|
||||
case PL192_PRIORITYDAISY:
|
||||
return s->daisy_priority;
|
||||
case PL192_INTENCLEAR:
|
||||
return 0;
|
||||
case PL192_SOFTINTCLEAR:
|
||||
fprintf(stderr, "pl192: attempt to read write-only register (offset = "
|
||||
TARGET_FMT_plx ")\n", offset);
|
||||
case PL192_VECTADDR:
|
||||
return pl192_irq_ack(s);
|
||||
/* Workaround for kernel code using PL190 */
|
||||
case PL190_ITCR:
|
||||
case PL190_VECTADDR:
|
||||
case PL190_DEFVECTADDR:
|
||||
return 0;
|
||||
default:
|
||||
fprintf(stderr, "pl192: bad read offset (2) " TARGET_FMT_plx "\n", offset);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void pl192_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
|
||||
{
|
||||
PL192State *s = (PL192State *) opaque;
|
||||
|
||||
if (offset & 3) {
|
||||
hw_error("pl192: bad write offset (1) " TARGET_FMT_plx "\n", offset);
|
||||
}
|
||||
|
||||
if (offset >= 0xfe0 && offset < 0x1000) {
|
||||
hw_error("pl192: attempt to write to a read-only register (offset = "
|
||||
TARGET_FMT_plx ")\n", offset);
|
||||
}
|
||||
if (offset >= 0x100 && offset < 0x180) {
|
||||
s->vect_addr[(offset - 0x100) >> 2] = value;
|
||||
pl192_update(s);
|
||||
return;
|
||||
}
|
||||
if (offset >= 0x200 && offset < 0x280) {
|
||||
s->vect_priority[(offset - 0x200) >> 2] = value & 0xf;
|
||||
pl192_update(s);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (offset) {
|
||||
case PL192_IRQSTATUS:
|
||||
/* This is a readonly register, but linux tries to write to it
|
||||
anyway. Ignore the write. */
|
||||
return;
|
||||
case PL192_FIQSTATUS:
|
||||
case PL192_RAWINTR:
|
||||
hw_error("pl192: attempt to write to a read-only register (offset = "
|
||||
TARGET_FMT_plx ")\n", offset);
|
||||
break;
|
||||
case PL192_INTSELECT:
|
||||
s->intselect = value;
|
||||
break;
|
||||
case PL192_INTENABLE:
|
||||
s->intenable |= value;
|
||||
break;
|
||||
case PL192_INTENCLEAR:
|
||||
s->intenable &= ~value;
|
||||
break;
|
||||
case PL192_SOFTINT:
|
||||
s->softint |= value;
|
||||
break;
|
||||
case PL192_SOFTINTCLEAR:
|
||||
s->softint &= ~value;
|
||||
break;
|
||||
case PL192_PROTECTION:
|
||||
/* TODO: implement protection */
|
||||
s->protection = value & 1;
|
||||
break;
|
||||
case PL192_SWPRIORITYMASK:
|
||||
s->sw_priority_mask = value & 0xffff;
|
||||
break;
|
||||
case PL192_PRIORITYDAISY:
|
||||
s->daisy_priority = value & 0xf;
|
||||
break;
|
||||
case PL192_VECTADDR:
|
||||
pl192_irq_fin(s);
|
||||
return;
|
||||
case PL190_ITCR:
|
||||
case PL190_VECTADDR:
|
||||
case PL190_DEFVECTADDR:
|
||||
/* NB: This thing is not present here, but linux wants to write it */
|
||||
/* Ignore written value */
|
||||
return;
|
||||
default:
|
||||
fprintf(stderr, "pl192: bad write offset (2) " TARGET_FMT_plx "\n", offset);
|
||||
return;
|
||||
}
|
||||
|
||||
pl192_update(s);
|
||||
}
|
||||
|
||||
static void pl192_irq_handler(void *opaque, int irq, int level)
|
||||
{
|
||||
PL192State *s = (PL192State *) opaque;
|
||||
|
||||
if (level) {
|
||||
s->rawintr |= 1 << irq;
|
||||
} else {
|
||||
s->rawintr &= ~(1 << irq);
|
||||
}
|
||||
pl192_update(opaque);
|
||||
}
|
||||
|
||||
static void pl192_reset(DeviceState *d)
|
||||
{
|
||||
PL192State *s = PL192(d);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PL192_INT_SOURCES; i++) {
|
||||
s->vect_priority[i] = 0xf;
|
||||
}
|
||||
s->sw_priority_mask = 0xffff;
|
||||
s->daisy_priority = 0xf;
|
||||
s->current = PL192_NO_IRQ;
|
||||
s->current_highest = PL192_NO_IRQ;
|
||||
s->stack_i = 0;
|
||||
s->priority_stack[0] = 0x10;
|
||||
s->irq_stack[0] = PL192_NO_IRQ;
|
||||
s->priority = 0x10;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps pl192_ops = {
|
||||
.read = pl192_read,
|
||||
.write = pl192_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
DeviceState *pl192_manual_init(char *mem_name, ...)
|
||||
{
|
||||
DeviceState *dev = qdev_new(TYPE_PL192);
|
||||
PL192State *s = PL192(dev);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
|
||||
va_list va;
|
||||
qemu_irq irq;
|
||||
|
||||
memory_region_init_io(&s->iomem, OBJECT(s), &pl192_ops, s, mem_name, 0x1000);
|
||||
sysbus_init_mmio(sbd, &s->iomem);
|
||||
qdev_init_gpio_in(dev, pl192_irq_handler, PL192_INT_SOURCES);
|
||||
sysbus_init_irq(sbd, &s->irq);
|
||||
sysbus_init_irq(sbd, &s->fiq);
|
||||
sysbus_realize_and_unref(sbd, &error_fatal);
|
||||
|
||||
va_start(va, mem_name);
|
||||
int n = 0;
|
||||
while (1) {
|
||||
irq = va_arg(va, qemu_irq);
|
||||
if (!irq) {
|
||||
break;
|
||||
}
|
||||
sysbus_connect_irq(sbd, n, irq);
|
||||
n++;
|
||||
}
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
static void pl192_init(Object *obj)
|
||||
{
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
PL192State *s = PL192(obj);
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
|
||||
//memory_region_init_io(&s->iomem, obj, &pl192_ops, s, "pl192", 0x1000);
|
||||
//sysbus_init_mmio(sbd, &s->iomem);
|
||||
//qdev_init_gpio_in(dev, pl192_irq_handler, PL192_INT_SOURCES);
|
||||
//sysbus_init_irq(sbd, s->irq);
|
||||
//sysbus_init_irq(sbd, s->fiq);
|
||||
}
|
||||
|
||||
static void pl192_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->reset = pl192_reset;
|
||||
//dc->vmsd = &vmstate_pl192;
|
||||
// TODO save VM
|
||||
}
|
||||
|
||||
static const TypeInfo pl192_info = {
|
||||
.name = TYPE_PL192,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(PL192State),
|
||||
.instance_init = pl192_init,
|
||||
.class_init = pl192_class_init,
|
||||
};
|
||||
|
||||
static void pl192_register_types(void)
|
||||
{
|
||||
type_register_static(&pl192_info);
|
||||
}
|
||||
|
||||
type_init(pl192_register_types)
|
||||
@@ -16,6 +16,7 @@ typedef struct aes_key_st AES_KEY;
|
||||
#define AES_set_decrypt_key QEMU_AES_set_decrypt_key
|
||||
#define AES_encrypt QEMU_AES_encrypt
|
||||
#define AES_decrypt QEMU_AES_decrypt
|
||||
#define AES_cbc_encrypt QEMU_AES_cbc_encrypt
|
||||
|
||||
int AES_set_encrypt_key(const unsigned char *userKey, const int bits,
|
||||
AES_KEY *key);
|
||||
@@ -26,6 +27,9 @@ void AES_encrypt(const unsigned char *in, unsigned char *out,
|
||||
const AES_KEY *key);
|
||||
void AES_decrypt(const unsigned char *in, unsigned char *out,
|
||||
const AES_KEY *key);
|
||||
void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
|
||||
const unsigned long length, const AES_KEY *key,
|
||||
unsigned char *ivec, const int enc);
|
||||
|
||||
extern const uint8_t AES_sbox[256];
|
||||
extern const uint8_t AES_isbox[256];
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
#ifndef HW_ARM_IPOD_TOUCH_H
|
||||
#define HW_ARM_IPOD_TOUCH_H
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "exec/hwaddr.h"
|
||||
#include "ui/console.h"
|
||||
#include "hw/boards.h"
|
||||
#include "hw/arm/boot.h"
|
||||
#include "hw/intc/pl192.h"
|
||||
#include "hw/arm/ipod_touch_timer.h"
|
||||
#include "hw/arm/ipod_touch_clock.h"
|
||||
#include "hw/arm/ipod_touch_spi.h"
|
||||
#include "hw/arm/ipod_touch_aes.h"
|
||||
#include "hw/arm/ipod_touch_sha1.h"
|
||||
#include "hw/arm/ipod_touch_8900_engine.h"
|
||||
#include "hw/arm/ipod_touch_usb_otg.h"
|
||||
#include "hw/arm/ipod_touch_nand.h"
|
||||
#include "hw/arm/ipod_touch_nand_ecc.h"
|
||||
#include "hw/arm/ipod_touch_pcf50633_pmu.h"
|
||||
#include "hw/arm/ipod_touch_adm.h"
|
||||
#include "hw/arm/ipod_touch_sysic.h"
|
||||
#include "hw/arm/ipod_touch_chipid.h"
|
||||
#include "hw/arm/ipod_touch_tvout.h"
|
||||
#include "hw/arm/ipod_touch_lcd.h"
|
||||
#include "hw/arm/ipod_touch_gpio.h"
|
||||
#include "hw/arm/ipod_touch_sdio.h"
|
||||
#include "hw/i2c/ipod_touch_i2c.h"
|
||||
#include "cpu.h"
|
||||
|
||||
#define TYPE_IPOD_TOUCH "iPod-Touch"
|
||||
|
||||
#define TYPE_IPOD_TOUCH_MACHINE MACHINE_TYPE_NAME(TYPE_IPOD_TOUCH)
|
||||
#define IPOD_TOUCH_MACHINE(obj) \
|
||||
OBJECT_CHECK(IPodTouchMachineState, (obj), TYPE_IPOD_TOUCH_MACHINE)
|
||||
|
||||
// VIC
|
||||
#define S5L8900_VIC_N 2
|
||||
#define S5L8900_VIC_SIZE 32
|
||||
|
||||
#define S5L8900_TIMER1_IRQ 0x7
|
||||
|
||||
#define S5L8900_SPI0_IRQ 0x9
|
||||
#define S5L8900_SPI1_IRQ 0xA
|
||||
#define S5L8900_SPI2_IRQ 0xB
|
||||
|
||||
#define S5L8900_LCD_IRQ 0xD
|
||||
|
||||
#define S5L8900_DMAC0_IRQ 0x10
|
||||
#define S5L8900_DMAC1_IRQ 0x11
|
||||
|
||||
#define S5L8900_I2C0_IRQ 0x15
|
||||
#define S5L8900_I2C1_IRQ 0x16
|
||||
|
||||
#define S5L8900_ADM_IRQ 0x25
|
||||
|
||||
#define S5L8900_NAND_ECC_IRQ 0x2B
|
||||
|
||||
#define S5L8900_TVOUT_SDO_IRQ 0x1E
|
||||
#define S5L8900_TVOUT_MIXER_IRQ 0x26
|
||||
|
||||
// GPIO interrupts
|
||||
#define S5L8900_GPIO_G0_IRQ 0x21
|
||||
#define S5L8900_GPIO_G1_IRQ 0x20
|
||||
#define S5L8900_GPIO_G2_IRQ 0x1F
|
||||
#define S5L8900_GPIO_G3_IRQ 0x03
|
||||
#define S5L8900_GPIO_G4_IRQ 0x02
|
||||
#define S5L8900_GPIO_G5_IRQ 0x01
|
||||
#define S5L8900_GPIO_G6_IRQ 0x00
|
||||
|
||||
const int S5L8900_GPIO_IRQS[7] = { S5L8900_GPIO_G0_IRQ, S5L8900_GPIO_G1_IRQ, S5L8900_GPIO_G2_IRQ, S5L8900_GPIO_G3_IRQ, S5L8900_GPIO_G4_IRQ, S5L8900_GPIO_G5_IRQ, S5L8900_GPIO_G6_IRQ };
|
||||
|
||||
// memory addresses
|
||||
#define IPOD_TOUCH_PHYS_BASE (0xc0000000)
|
||||
#define IBOOT_BASE 0x18000000
|
||||
#define LLB_BASE 0x22000000
|
||||
#define SRAM1_MEM_BASE 0x22020000
|
||||
#define CLOCK0_MEM_BASE 0x38100000
|
||||
#define CLOCK1_MEM_BASE 0x3C500000
|
||||
#define SYSIC_MEM_BASE 0x39A00000
|
||||
#define VIC0_MEM_BASE 0x38E00000
|
||||
#define VIC1_MEM_BASE 0x38E01000
|
||||
#define EDGEIC_MEM_BASE 0x38E02000
|
||||
#define IIS0_MEM_BASE 0x3D400000
|
||||
#define IIS1_MEM_BASE 0x3CD00000
|
||||
#define IIS2_MEM_BASE 0x3CA00000
|
||||
#define NOR_MEM_BASE 0x24000000
|
||||
#define TIMER1_MEM_BASE 0x3E200000
|
||||
#define USBOTG_MEM_BASE 0x38400000
|
||||
#define USBPHYS_MEM_BASE 0x3C400000
|
||||
#define GPIO_MEM_BASE 0x3E400000
|
||||
#define I2C0_MEM_BASE 0x3C600000
|
||||
#define I2C1_MEM_BASE 0x3C900000
|
||||
#define SPI0_MEM_BASE 0x3C300000
|
||||
#define SPI1_MEM_BASE 0x3CE00000
|
||||
#define SPI2_MEM_BASE 0x3D200000
|
||||
#define WATCHDOG_MEM_BASE 0x3E300000
|
||||
#define DISPLAY_MEM_BASE 0x38900000
|
||||
#define CHIPID_MEM_BASE 0x3e500000
|
||||
#define RAM_MEM_BASE 0x8000000
|
||||
#define SHA1_MEM_BASE 0x38000000
|
||||
#define AES_MEM_BASE 0x38C00000
|
||||
#define VROM_MEM_BASE 0x20000000
|
||||
#define NAND_MEM_BASE 0x38A00000
|
||||
#define NAND_ECC_MEM_BASE 0x38F00000
|
||||
#define DMAC0_MEM_BASE 0x38200000
|
||||
#define DMAC1_MEM_BASE 0x39900000
|
||||
#define UART0_MEM_BASE 0x3CC00000
|
||||
#define UART1_MEM_BASE 0x3CC04000
|
||||
#define UART2_MEM_BASE 0x3CC08000
|
||||
#define UART3_MEM_BASE 0x3CC0C000
|
||||
#define UART4_MEM_BASE 0x3CC10000
|
||||
#define ADM_MEM_BASE 0x38800000
|
||||
#define MBX_MEM_BASE 0x3B000000
|
||||
#define ENGINE_8900_MEM_BASE 0x3F000000
|
||||
#define KERNELCACHE_BASE 0x9000000
|
||||
#define SDIO_MEM_BASE 0x38d00000
|
||||
#define FRAMEBUFFER_MEM_BASE 0xfe00000
|
||||
#define TVOUT1_MEM_BASE 0x39100000
|
||||
#define TVOUT2_MEM_BASE 0x39200000
|
||||
#define TVOUT3_MEM_BASE 0x39300000
|
||||
#define MPVD_MEM_BASE 0x39600000
|
||||
#define H264BPD_MEM_BASE 0x39800000
|
||||
|
||||
#define TVOUT_WORKAROUND_MEM_BASE 0x8a25960 // workaround for TV Out, to make sure that it can be correctly deallocated without reverse engineering the entire TVOut protocol
|
||||
|
||||
typedef struct {
|
||||
MachineClass parent;
|
||||
} IPodTouchMachineClass;
|
||||
|
||||
typedef struct s5l8900_usb_phys_s
|
||||
{
|
||||
uint32_t usb_ophypwr;
|
||||
uint32_t usb_ophyclk;
|
||||
uint32_t usb_orstcon;
|
||||
uint32_t usb_ophytune;
|
||||
|
||||
} s5l8900_usb_phys_s;
|
||||
|
||||
typedef struct {
|
||||
MachineState parent;
|
||||
qemu_irq **irq;
|
||||
PL192State *vic0;
|
||||
PL192State *vic1;
|
||||
synopsys_usb_state *usb_otg;
|
||||
IPodTouchClockState *clock0;
|
||||
IPodTouchClockState *clock1;
|
||||
IPodTouchTimerState *timer1;
|
||||
IPodTouchSYSICState *sysic;
|
||||
S5L8900SPIState *spi2_state;
|
||||
s5l8900_usb_phys_s *usb_phys;
|
||||
S5L8900AESState *aes_state;
|
||||
S5L8900SHA1State *sha1_state;
|
||||
ITNandState *nand_state;
|
||||
ITNandECCState *nand_ecc_state;
|
||||
IPodTouchI2CState *i2c0_state;
|
||||
IPodTouchI2CState *i2c1_state;
|
||||
IPodTouchADMState *adm_state;
|
||||
IPodTouchChipIDState *chipid_state;
|
||||
IPodTouchTVOutState *tvout1_state;
|
||||
IPodTouchTVOutState *tvout2_state;
|
||||
IPodTouchTVOutState *tvout3_state;
|
||||
IPodTouchLCDState *lcd_state;
|
||||
IPodTouchGPIOState *gpio_state;
|
||||
IPodTouchSDIOState *sdio_state;
|
||||
Clock *sysclk;
|
||||
uint32_t kpc_pa;
|
||||
uint32_t kbootargs_pa;
|
||||
uint32_t uart_mmio_pa;
|
||||
ARMCPU *cpu;
|
||||
char bootrom_path[1024];
|
||||
char iboot_path[1024];
|
||||
char nand_path[1024];
|
||||
} IPodTouchMachineState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,112 @@
|
||||
#ifndef HW_ARM_IPOD_TOUCH_8900_ENGINE_H
|
||||
#define HW_ARM_IPOD_TOUCH_8900_ENGINE_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "hw/hw.h"
|
||||
#include "exec/hwaddr.h"
|
||||
#include "exec/memory.h"
|
||||
#include <openssl/aes.h>
|
||||
|
||||
typedef struct {
|
||||
unsigned char magic[ 4 ];
|
||||
unsigned char version[ 3 ];
|
||||
unsigned char encrypted;
|
||||
unsigned char unknownNull[4];
|
||||
unsigned int sizeOfData;
|
||||
unsigned int footerSignatureOffset;
|
||||
unsigned int footerCertOffset;
|
||||
unsigned int footerCertLen;
|
||||
unsigned char key1[ 32 ];
|
||||
unsigned char unknownVersion[ 4 ];
|
||||
unsigned char key2[ 16 ];
|
||||
unsigned char padding[ 1968 ];
|
||||
} header8900;
|
||||
|
||||
static uint64_t s5l8900_8900_engine_read(void *opaque, hwaddr offset, unsigned size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void convert_hex_key_to_bin(char *str, uint8_t *bytes, int maxlen)
|
||||
{
|
||||
int slen = strlen(str);
|
||||
int bytelen = maxlen;
|
||||
int rpos, wpos = 0;
|
||||
|
||||
for(rpos = 0; rpos < bytelen; rpos++) {
|
||||
sscanf(&str[rpos*2], "%02hhx", &bytes[wpos++]);
|
||||
}
|
||||
}
|
||||
|
||||
static void s5l8900_8900_engine_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
|
||||
{
|
||||
AES_KEY ctx, aes_decrypt_key;
|
||||
uint8_t aes_key[16];
|
||||
|
||||
unsigned char ramdiskKey[33] = "188458A6D15034DFE386F23B61D43774";
|
||||
unsigned char ramdiskiv[1];
|
||||
|
||||
unsigned char keybuf[16];
|
||||
unsigned char iv[AES_BLOCK_SIZE];
|
||||
int encrypted;
|
||||
off_t data_begin, data_current, data_end, data_len;
|
||||
|
||||
if(offset != 0x0) { return; }
|
||||
|
||||
printf("Reading 8900 header with length %d at address 0x%08x\n", sizeof(header8900), value);
|
||||
|
||||
AddressSpace *nsas = (AddressSpace *)opaque;
|
||||
header8900 *header = malloc(sizeof(header8900));
|
||||
address_space_rw(nsas, value, MEMTXATTRS_UNSPECIFIED, (uint8_t *)header, sizeof(header8900), 0);
|
||||
|
||||
if( (header->magic[0] != 0x38) && // 8
|
||||
(header->magic[1] != 0x39) && // 9
|
||||
(header->magic[2] != 0x30) && // 0
|
||||
(header->magic[3] != 0x30)) // 0
|
||||
{
|
||||
printf("Bad 8900 magic\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(header->encrypted == 0x03) { encrypted = 1; }
|
||||
else if( header->encrypted = 0x04 ) { encrypted = 0; }
|
||||
|
||||
data_begin = sizeof(header8900);
|
||||
data_len = header->sizeOfData;
|
||||
|
||||
printf("Will decrypt 8900 image at address 0x%08x (len: %d bytes)\n", value, data_len);
|
||||
|
||||
// read the data into a buffer
|
||||
uint8_t *inbuf = (uint8_t *)malloc(data_len);
|
||||
address_space_rw(nsas, value + sizeof(header8900), MEMTXATTRS_UNSPECIFIED, (uint8_t *)inbuf, data_len, 0);
|
||||
uint8_t *outbuf = (uint8_t *)malloc(data_len);
|
||||
uint8_t *intbuf = (uint8_t *)malloc(AES_BLOCK_SIZE);
|
||||
|
||||
convert_hex_key_to_bin(ramdiskKey, aes_key, 16);
|
||||
AES_set_decrypt_key(aes_key, 128, &aes_decrypt_key);
|
||||
memset(iv, 0, AES_BLOCK_SIZE);
|
||||
|
||||
data_current = 0;
|
||||
while(data_current < data_len)
|
||||
{
|
||||
AES_cbc_encrypt(inbuf + data_current, intbuf, AES_BLOCK_SIZE, &aes_decrypt_key, iv, 0);
|
||||
memcpy(outbuf + data_current, intbuf, AES_BLOCK_SIZE);
|
||||
data_current = data_current + AES_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
// write the decrypted output buffer
|
||||
address_space_rw(nsas, value + sizeof(header8900), MEMTXATTRS_UNSPECIFIED, (uint8_t *)outbuf, data_len, 1);
|
||||
|
||||
free(header);
|
||||
free(inbuf);
|
||||
free(outbuf);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps engine_8900_ops = {
|
||||
.read = s5l8900_8900_engine_read,
|
||||
.write = s5l8900_8900_engine_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef IPOD_TOUCH_ADM_H
|
||||
#define IPOD_TOUCH_ADM_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/arm/ipod_touch_nand.h"
|
||||
|
||||
#define TYPE_IPOD_TOUCH_ADM "ipodtouch.adm"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchADMState, IPOD_TOUCH_ADM)
|
||||
|
||||
#define ADM_CTRL 0x0
|
||||
#define ADM_CTRL2 0x4
|
||||
#define ADM_CODE_SEC_ADDR 0x50
|
||||
#define ADM_DATA1_SEC_ADDR 0x84
|
||||
#define ADM_DATA2_SEC_ADDR 0x88
|
||||
#define ADM_DATA3_SEC_ADDR 0x8C
|
||||
|
||||
typedef struct IPodTouchADMState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
MemoryRegion iomem;
|
||||
qemu_irq irq;
|
||||
|
||||
uint32_t code_sec_addr;
|
||||
uint32_t data1_sec_addr;
|
||||
uint32_t data2_sec_addr;
|
||||
uint32_t data3_sec_addr;
|
||||
|
||||
MemoryRegion *downstream;
|
||||
AddressSpace downstream_as;
|
||||
|
||||
ITNandState *nand_state;
|
||||
} IPodTouchADMState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,70 @@
|
||||
#ifndef HW_ARM_IPOD_TOUCH_AES_H
|
||||
#define HW_ARM_IPOD_TOUCH_AES_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "hw/platform-bus.h"
|
||||
#include "hw/hw.h"
|
||||
#include "exec/hwaddr.h"
|
||||
#include "exec/memory.h"
|
||||
#include <openssl/aes.h>
|
||||
|
||||
#define TYPE_IPOD_TOUCH_AES "ipodtouch.aes"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(S5L8900AESState, IPOD_TOUCH_AES)
|
||||
|
||||
#define key_uid ((uint8_t[]){0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF})
|
||||
|
||||
#define AES_128_CBC_BLOCK_SIZE 64
|
||||
#define AES_CONTROL 0x0
|
||||
#define AES_GO 0x4
|
||||
#define AES_UNKREG0 0x8
|
||||
#define AES_STATUS 0xC
|
||||
#define AES_UNKREG1 0x10
|
||||
#define AES_KEYLEN 0x14
|
||||
#define AES_INSIZE 0x18
|
||||
#define AES_INADDR 0x20
|
||||
#define AES_OUTSIZE 0x24
|
||||
#define AES_OUTADDR 0x28
|
||||
#define AES_AUXSIZE 0x2C
|
||||
#define AES_AUXADDR 0x30
|
||||
#define AES_SIZE3 0x34
|
||||
#define AES_KEY_REG 0x4C
|
||||
#define AES_TYPE 0x6C
|
||||
#define AES_IV_REG 0x74
|
||||
#define AES_KEYSIZE 0x20
|
||||
#define AES_IVSIZE 0x10
|
||||
|
||||
typedef enum AESKeyType {
|
||||
AESCustom = 0,
|
||||
AESGID = 1,
|
||||
AESUID = 2
|
||||
} AESKeyType;
|
||||
|
||||
typedef enum AESKeyLen {
|
||||
AES128 = 0,
|
||||
AES192 = 1,
|
||||
AES256 = 2
|
||||
} AESKeyLen;
|
||||
|
||||
typedef struct S5L8900AESState
|
||||
{
|
||||
SysBusDevice busdev;
|
||||
MemoryRegion iomem;
|
||||
AES_KEY decryptKey;
|
||||
uint32_t ivec[4];
|
||||
uint32_t insize;
|
||||
uint32_t inaddr;
|
||||
uint32_t outsize;
|
||||
uint32_t outaddr;
|
||||
uint32_t auxaddr;
|
||||
uint32_t keytype;
|
||||
uint32_t status;
|
||||
uint32_t ctrl;
|
||||
uint32_t unkreg0;
|
||||
uint32_t unkreg1;
|
||||
uint32_t operation;
|
||||
uint32_t keylen;
|
||||
uint32_t custkey[8];
|
||||
} S5L8900AESState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef HW_ARM_IPOD_TOUCH_CHIPID_H
|
||||
#define HW_ARM_IPOD_TOUCH_CHIPID_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
#define TYPE_IPOD_TOUCH_CHIPID "ipodtouch.chipid"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchChipIDState, IPOD_TOUCH_CHIPID)
|
||||
|
||||
#define CHIP_REVISION 0x2
|
||||
|
||||
typedef struct IPodTouchChipIDState {
|
||||
SysBusDevice busdev;
|
||||
MemoryRegion iomem;
|
||||
} IPodTouchChipIDState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,39 @@
|
||||
#ifndef IPOD_TOUCH_CLOCK_H
|
||||
#define IPOD_TOUCH_CLOCK_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/clock.h"
|
||||
|
||||
#define TYPE_IPOD_TOUCH_CLOCK "ipodtouch.clock"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchClockState, IPOD_TOUCH_CLOCK)
|
||||
|
||||
#define CLOCK1_CONFIG0 0x0
|
||||
#define CLOCK1_CONFIG1 0x4
|
||||
#define CLOCK1_CONFIG2 0x8
|
||||
#define CLOCK1_PLL0CON 0x20
|
||||
#define CLOCK1_PLL1CON 0x24
|
||||
#define CLOCK1_PLL2CON 0x28
|
||||
#define CLOCK1_PLL3CON 0x2C
|
||||
#define CLOCK1_PLLLOCK 0x40
|
||||
#define CLOCK1_PLLMODE 0x44
|
||||
|
||||
typedef struct IPodTouchClockState
|
||||
{
|
||||
SysBusDevice busdev;
|
||||
MemoryRegion iomem;
|
||||
uint32_t config0;
|
||||
uint32_t config1;
|
||||
uint32_t config2;
|
||||
uint32_t pll0con;
|
||||
uint32_t pll1con;
|
||||
uint32_t pll2con;
|
||||
uint32_t pll3con;
|
||||
uint32_t plllock;
|
||||
|
||||
} IPodTouchClockState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef IPOD_TOUCH_GPIO_H
|
||||
#define IPOD_TOUCH_GPIO_H
|
||||
|
||||
#include <math.h>
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
#define TYPE_IPOD_TOUCH_GPIO "ipodtouch.gpio"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchGPIOState, IPOD_TOUCH_GPIO)
|
||||
|
||||
#define GPIO_BUTTON_POWER 0x1605
|
||||
#define GPIO_BUTTON_HOME 0x1606
|
||||
|
||||
#define GPIO_BUTTON_POWER_IRQ 0x2D
|
||||
#define GPIO_BUTTON_HOME_IRQ 0x2E
|
||||
|
||||
#define NUM_GPIO_PINS 0x20
|
||||
|
||||
typedef struct IPodTouchGPIOState
|
||||
{
|
||||
SysBusDevice parent_obj;
|
||||
MemoryRegion iomem;
|
||||
uint32_t gpio_state;
|
||||
} IPodTouchGPIOState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,57 @@
|
||||
#ifndef IPOD_TOUCH_LCD_H
|
||||
#define IPOD_TOUCH_LCD_H
|
||||
|
||||
#include <math.h>
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/arm/ipod_touch_multitouch.h"
|
||||
|
||||
#define TYPE_IPOD_TOUCH_LCD "ipodtouch.lcd"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchLCDState, IPOD_TOUCH_LCD)
|
||||
|
||||
#define LCD_REFRESH_RATE_FREQUENCY 10
|
||||
|
||||
typedef struct IPodTouchLCDState
|
||||
{
|
||||
SysBusDevice parent_obj;
|
||||
MemoryRegion *sysmem;
|
||||
MemoryRegion iomem;
|
||||
QemuConsole *con;
|
||||
IPodTouchMultitouchState *mt;
|
||||
int invalidate;
|
||||
MemoryRegionSection fbsection;
|
||||
qemu_irq irq;
|
||||
uint32_t lcd_con;
|
||||
uint32_t lcd_con2;
|
||||
uint32_t unknown1;
|
||||
uint32_t unknown2;
|
||||
|
||||
uint32_t wnd_con;
|
||||
|
||||
uint32_t vid_con0;
|
||||
uint32_t vid_con1;
|
||||
|
||||
uint32_t vidt_con0;
|
||||
uint32_t vidt_con1;
|
||||
uint32_t vidt_con2;
|
||||
uint32_t vidt_con3;
|
||||
|
||||
uint32_t w1_hspan;
|
||||
uint32_t w1_framebuffer_base;
|
||||
uint32_t w1_display_resolution_info;
|
||||
uint32_t w1_display_depth_info;
|
||||
uint32_t w1_qlen;
|
||||
|
||||
uint32_t w2_hspan;
|
||||
uint32_t w2_framebuffer_base;
|
||||
uint32_t w2_display_resolution_info;
|
||||
uint32_t w2_display_depth_info;
|
||||
uint32_t w2_qlen;
|
||||
|
||||
QEMUTimer *refresh_timer;
|
||||
} IPodTouchLCDState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef IPOD_TOUCH_LCD_PANEL_H
|
||||
#define IPOD_TOUCH_LCD_PANEL_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/ssi/ssi.h"
|
||||
|
||||
#define TYPE_IPOD_TOUCH_LCD_PANEL "ipodtouch.lcdpanel"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchLCDPanelState, IPOD_TOUCH_LCD_PANEL)
|
||||
|
||||
typedef struct IPodTouchLCDPanelState {
|
||||
SSIPeripheral ssidev;
|
||||
uint32_t cur_cmd;
|
||||
} IPodTouchLCDPanelState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef HW_LIS302DL_H
|
||||
#define HW_LIS302DL_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/i2c/i2c.h"
|
||||
#include "hw/irq.h"
|
||||
|
||||
#define TYPE_LIS302DL "lis302dl"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(LIS302DLState, LIS302DL)
|
||||
|
||||
#define ACCEL_WHOAMI 0x0F
|
||||
|
||||
#define ACCEL_WHOAMI_VALUE 0x3B
|
||||
|
||||
typedef struct LIS302DLState {
|
||||
I2CSlave i2c;
|
||||
uint32_t cmd;
|
||||
} LIS302DLState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,161 @@
|
||||
#ifndef IPOD_TOUCH_MULTITOUCH_H
|
||||
#define IPOD_TOUCH_MULTITOUCH_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/ssi/ssi.h"
|
||||
#include "hw/arm/ipod_touch_sysic.h"
|
||||
#include "hw/arm/ipod_touch_gpio.h"
|
||||
|
||||
#define TYPE_IPOD_TOUCH_MULTITOUCH "ipodtouch.multitouch"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchMultitouchState, IPOD_TOUCH_MULTITOUCH)
|
||||
|
||||
#define MT_INTERFACE_VERSION 0x1
|
||||
#define MT_FAMILY_ID 81
|
||||
#define MT_ENDIANNESS 0x1
|
||||
#define MT_SENSOR_ROWS 15
|
||||
#define MT_SENSOR_COLUMNS 10
|
||||
#define MT_BCD_VERSION 51
|
||||
#define MT_SENSOR_REGION_DESC 0x0
|
||||
#define MT_SENSOR_REGION_PARAM 0x0
|
||||
#define MT_MAX_PACKET_SIZE 0x294 // 660
|
||||
#define MT_SENSOR_SURFACE_WIDTH 5000
|
||||
#define MT_SENSOR_SURFACE_HEIGHT 7500
|
||||
|
||||
// internal surface width/height
|
||||
#define MT_INTERNAL_SENSOR_SURFACE_WIDTH (9000 - MT_SENSOR_SURFACE_WIDTH) * 84 / 73
|
||||
#define MT_INTERNAL_SENSOR_SURFACE_HEIGHT (13850 - MT_SENSOR_SURFACE_HEIGHT) * 84 / 73
|
||||
|
||||
// report IDs
|
||||
#define MT_REPORT_UNKNOWN1 0x70
|
||||
#define MT_REPORT_FAMILY_ID 0xD1
|
||||
#define MT_REPORT_SENSOR_INFO 0xD3
|
||||
#define MT_REPORT_SENSOR_REGION_DESC 0xD0
|
||||
#define MT_REPORT_SENSOR_REGION_PARAM 0xA1
|
||||
#define MT_REPORT_SENSOR_DIMENSIONS 0xD9
|
||||
|
||||
// report sizes
|
||||
#define MT_REPORT_UNKNOWN1_SIZE 0x1
|
||||
#define MT_REPORT_FAMILY_ID_SIZE 0x1
|
||||
#define MT_REPORT_SENSOR_INFO_SIZE 0x5
|
||||
#define MT_REPORT_SENSOR_REGION_DESC_SIZE 0x1
|
||||
#define MT_REPORT_SENSOR_REGION_PARAM_SIZE 0x1
|
||||
#define MT_REPORT_SENSOR_DIMENSIONS_SIZE 0x8
|
||||
|
||||
#define MT_CMD_HBPP_DATA_PACKET 0x30
|
||||
#define MT_CMD_GET_CMD_STATUS 0xE1
|
||||
#define MT_CMD_GET_INTERFACE_VERSION 0xE2
|
||||
#define MT_CMD_GET_REPORT_INFO 0xE3
|
||||
#define MT_CMD_SHORT_CONTROL_WRITE 0xE4
|
||||
#define MT_CMD_SHORT_CONTROL_READ 0xE6
|
||||
#define MT_CMD_FRAME_READ 0xEA
|
||||
|
||||
// frame types
|
||||
#define MT_FRAME_TYPE_PATH 0x44
|
||||
|
||||
// frame event types
|
||||
#define MT_EVENT_TOUCH_FULL_END 0x0
|
||||
#define MT_EVENT_TOUCH_START 0x3
|
||||
#define MT_EVENT_TOUCH_MOVED 0x4
|
||||
#define MT_EVENT_TOUCH_ENDED 0x7
|
||||
|
||||
typedef struct MTFrameLengthPacket
|
||||
{
|
||||
uint8_t cmd;
|
||||
uint8_t length1;
|
||||
uint8_t length2;
|
||||
uint8_t unused[11];
|
||||
uint8_t checksum1;
|
||||
uint8_t checksum2;
|
||||
} __attribute__((__packed__)) MTFrameLengthPacket;
|
||||
|
||||
typedef struct MTFrameHeader
|
||||
{
|
||||
uint8_t type;
|
||||
uint8_t frameNum;
|
||||
uint8_t headerLen;
|
||||
uint8_t unk_3;
|
||||
uint32_t timestamp;
|
||||
uint8_t unk_8;
|
||||
uint8_t unk_9;
|
||||
uint8_t unk_A;
|
||||
uint8_t unk_B;
|
||||
uint16_t unk_C;
|
||||
uint16_t isImage;
|
||||
|
||||
uint8_t numFingers;
|
||||
uint8_t fingerDataLen;
|
||||
uint16_t unk_12;
|
||||
uint16_t unk_14;
|
||||
uint16_t unk_16;
|
||||
} __attribute__((__packed__)) MTFrameHeader;
|
||||
|
||||
typedef struct MTFramePacket
|
||||
{
|
||||
uint8_t cmd;
|
||||
uint8_t unused1;
|
||||
uint8_t length1;
|
||||
uint8_t length2;
|
||||
uint8_t checksum_pad;
|
||||
MTFrameHeader header;
|
||||
} __attribute__((__packed__)) MTFramePacket;
|
||||
|
||||
typedef struct FingerData
|
||||
{
|
||||
uint8_t id;
|
||||
uint8_t event;
|
||||
uint8_t unk_2;
|
||||
uint8_t unk_3;
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
int16_t velX;
|
||||
int16_t velY;
|
||||
uint16_t radius2;
|
||||
uint16_t radius3;
|
||||
uint16_t angle;
|
||||
uint16_t radius1;
|
||||
uint16_t contactDensity;
|
||||
uint16_t unk_16;
|
||||
uint16_t unk_18;
|
||||
uint16_t unk_1A;
|
||||
} __attribute__((__packed__)) FingerData;
|
||||
|
||||
typedef struct MTFrame {
|
||||
MTFrameLengthPacket frame_length;
|
||||
MTFramePacket frame_packet;
|
||||
FingerData finger_data; // TODO we assume one finger for now
|
||||
uint8_t checksum1;
|
||||
uint8_t checksum2;
|
||||
} __attribute__((__packed__)) MTFrame;
|
||||
|
||||
typedef struct IPodTouchMultitouchState {
|
||||
SSIPeripheral ssidev;
|
||||
uint8_t cur_cmd;
|
||||
uint8_t *out_buffer;
|
||||
uint8_t *in_buffer;
|
||||
uint32_t buf_size;
|
||||
uint32_t buf_ind;
|
||||
uint32_t in_buffer_ind;
|
||||
uint8_t hbpp_atn_ack_response[2];
|
||||
MTFrame *next_frame;
|
||||
uint32_t frame_counter;
|
||||
bool touch_down;
|
||||
QEMUTimer *touch_timer;
|
||||
QEMUTimer *touch_end_timer;
|
||||
IPodTouchSYSICState *sysic;
|
||||
IPodTouchGPIOState *gpio_state;
|
||||
float touch_x;
|
||||
float touch_y;
|
||||
float prev_touch_x;
|
||||
float prev_touch_y;
|
||||
uint64_t last_frame_timestamp;
|
||||
} IPodTouchMultitouchState;
|
||||
|
||||
void ipod_touch_multitouch_on_touch(IPodTouchMultitouchState *s);
|
||||
void ipod_touch_multitouch_on_release(IPodTouchMultitouchState *s);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,67 @@
|
||||
#ifndef HW_ARM_IPOD_TOUCH_NAND_H
|
||||
#define HW_ARM_IPOD_TOUCH_NAND_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "hw/platform-bus.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/irq.h"
|
||||
#include "qemu/lockable.h"
|
||||
|
||||
#define NAND_NUM_BANKS 8
|
||||
#define NAND_BYTES_PER_PAGE 2048
|
||||
#define NAND_BYTES_PER_SPARE 64
|
||||
|
||||
#define NAND_CHIP_ID 0xA514D3AD
|
||||
|
||||
#define NAND_FMCTRL0 0x0
|
||||
#define NAND_FMCTRL1 0x4
|
||||
#define NAND_CMD 0x8
|
||||
#define NAND_FMADDR0 0xC
|
||||
#define NAND_FMADDR1 0x10
|
||||
#define NAND_FMANUM 0x2C
|
||||
#define NAND_FMDNUM 0x30
|
||||
#define NAND_FMCSTAT 0x48
|
||||
#define NAND_FMFIFO 0x80
|
||||
#define NAND_RSCTRL 0x100
|
||||
|
||||
#define NAND_CMD_ID 0x90
|
||||
#define NAND_CMD_READ 0x30
|
||||
#define NAND_CMD_READSTATUS 0x70
|
||||
|
||||
#define FILESYSTEM_START_VPN 206851
|
||||
#define FILESYSTEM_NUM_PAGES 132854
|
||||
|
||||
#define TYPE_ITNAND "itnand"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(ITNandState, ITNAND)
|
||||
|
||||
typedef struct ITNandState {
|
||||
SysBusDevice busdev;
|
||||
MemoryRegion iomem;
|
||||
uint32_t fmctrl0;
|
||||
uint32_t fmctrl1;
|
||||
uint32_t fmaddr0;
|
||||
uint32_t fmaddr1;
|
||||
uint32_t fmanum;
|
||||
uint32_t fmdnum;
|
||||
uint32_t rsctrl;
|
||||
uint32_t cmd;
|
||||
uint8_t reading_spare;
|
||||
qemu_irq irq;
|
||||
|
||||
uint8_t *page_buffer;
|
||||
uint8_t *page_spare_buffer;
|
||||
uint32_t buffered_bank;
|
||||
uint32_t buffered_page;
|
||||
bool reading_multiple_pages;
|
||||
uint32_t cur_bank_reading;
|
||||
uint32_t banks_to_read[512]; // used when in multiple page read mode
|
||||
uint32_t pages_to_read[512]; // used when in multiple page read mode
|
||||
bool is_writing;
|
||||
QemuMutex lock;
|
||||
char *nand_path;
|
||||
} ITNandState;
|
||||
|
||||
void nand_set_buffered_page(ITNandState *s, uint32_t page);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef HW_ARM_IPOD_TOUCH_NAND_ECC_H
|
||||
#define HW_ARM_IPOD_TOUCH_NAND_ECC_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "hw/platform-bus.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/irq.h"
|
||||
|
||||
#define NANDECC_DATA 0x4
|
||||
#define NANDECC_ECC 0x8
|
||||
#define NANDECC_START 0xC
|
||||
#define NANDECC_STATUS 0x10
|
||||
#define NANDECC_SETUP 0x14
|
||||
#define NANDECC_CLEARINT 0x40
|
||||
|
||||
#define TYPE_ITNANDECC "itnand_ecc"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(ITNandECCState, ITNANDECC)
|
||||
|
||||
typedef struct ITNandECCState {
|
||||
SysBusDevice busdev;
|
||||
MemoryRegion iomem;
|
||||
uint32_t data_addr;
|
||||
uint32_t ecc_addr;
|
||||
uint32_t status;
|
||||
uint32_t setup;
|
||||
qemu_irq irq;
|
||||
} ITNandECCState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef HW_PCF50633_PMU_H
|
||||
#define HW_PCF50633_PMU_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/i2c/i2c.h"
|
||||
#include "hw/irq.h"
|
||||
#include "time.h"
|
||||
|
||||
#define TYPE_PCF50633 "pcf50633"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(Pcf50633State, PCF50633)
|
||||
|
||||
#define PMU_MBCS1 0x4B
|
||||
#define PMU_ADCC1 0x57
|
||||
|
||||
// RTC registers
|
||||
#define PMU_RTCSC 0x59
|
||||
#define PMU_RTCMN 0x5A
|
||||
#define PMU_RTCHR 0x5B
|
||||
#define PMU_RTCWD 0x5C
|
||||
#define PMU_RTCDT 0x5D
|
||||
#define PMU_RTCMT 0x5E
|
||||
#define PMU_RTCYR 0x5F
|
||||
|
||||
typedef struct Pcf50633State {
|
||||
I2CSlave i2c;
|
||||
uint32_t cmd;
|
||||
} Pcf50633State;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef IPOD_TOUCH_SDIO_H
|
||||
#define IPOD_TOUCH_SDIO_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
#define TYPE_IPOD_TOUCH_SDIO "ipodtouch.sdio"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchSDIOState, IPOD_TOUCH_SDIO)
|
||||
|
||||
#define SDIO_CMD 0x8
|
||||
#define SDIO_ARGU 0xC
|
||||
#define SDIO_DSTA 0x18
|
||||
#define SDIO_RESP0 0x20
|
||||
#define SDIO_RESP1 0x24
|
||||
#define SDIO_RESP2 0x28
|
||||
#define SDIO_RESP3 0x2C
|
||||
#define SDIO_CSR 0x34
|
||||
#define SDIO_IRQMASK 0x3C
|
||||
|
||||
typedef struct IPodTouchSDIOState
|
||||
{
|
||||
SysBusDevice parent_obj;
|
||||
MemoryRegion iomem;
|
||||
|
||||
uint32_t cmd;
|
||||
uint32_t arg;
|
||||
uint32_t csr;
|
||||
uint32_t resp0;
|
||||
uint32_t resp1;
|
||||
uint32_t resp2;
|
||||
uint32_t resp3;
|
||||
uint32_t irq_mask;
|
||||
} IPodTouchSDIOState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,39 @@
|
||||
#ifndef HW_ARM_IPOD_TOUCH_SHA1_H
|
||||
#define HW_ARM_IPOD_TOUCH_SHA1_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "hw/platform-bus.h"
|
||||
#include "hw/hw.h"
|
||||
#include "exec/hwaddr.h"
|
||||
#include "exec/memory.h"
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#define TYPE_IPOD_TOUCH_SHA1 "ipodtouch.sha1"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(S5L8900SHA1State, IPOD_TOUCH_SHA1)
|
||||
|
||||
#define SHA1_BUFFER_SIZE 1024 * 1024
|
||||
|
||||
#define SHA_CONFIG 0x0
|
||||
#define SHA_RESET 0x4
|
||||
#define SHA_MEMORY_MODE 0x80 // whether we read from the memory
|
||||
#define SHA_MEMORY_START 0x84
|
||||
#define SHA_INSIZE 0x8c
|
||||
|
||||
typedef struct S5L8900SHA1State {
|
||||
SysBusDevice busdev;
|
||||
MemoryRegion iomem;
|
||||
uint32_t config;
|
||||
uint32_t memory_start;
|
||||
uint32_t memory_mode;
|
||||
uint32_t insize;
|
||||
uint8_t buffer[SHA1_BUFFER_SIZE];
|
||||
uint32_t hw_buffer[0x10]; // hardware buffer
|
||||
uint32_t buffer_ind;
|
||||
uint8_t hashout[0x14];
|
||||
bool hw_buffer_dirty;
|
||||
bool hash_computed;
|
||||
SHA_CTX ctx;
|
||||
} S5L8900SHA1State;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* S5L8900 SPI Emulation
|
||||
*
|
||||
* This code is based on:
|
||||
* - https://github.com/TrungNguyen1909/qemu-t8030/blob/master/hw/ssi/apple_spi.c (by TrungNguyen1909)
|
||||
* - https://github.com/danzatt/QEMU-s5l89xx-port/blob/master/hw/s5l8900_spi.c (by cmw)
|
||||
*/
|
||||
|
||||
#ifndef HW_ARM_IPOD_TOUCH_SPI_H
|
||||
#define HW_ARM_IPOD_TOUCH_SPI_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/fifo8.h"
|
||||
#include "qemu/log.h"
|
||||
#include "hw/platform-bus.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/ssi/ssi.h"
|
||||
#include "hw/arm/ipod_touch_lcd_panel.h"
|
||||
#include "hw/arm/ipod_touch_multitouch.h"
|
||||
|
||||
#define TYPE_S5L8900SPI "s5l8900spi"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(S5L8900SPIState, S5L8900SPI)
|
||||
|
||||
#define R_CTRL 0x000
|
||||
#define R_CTRL_RUN (1 << 0)
|
||||
#define R_CTRL_TX_RESET (1 << 2)
|
||||
#define R_CTRL_RX_RESET (1 << 3)
|
||||
|
||||
#define R_CFG 0x004
|
||||
#define R_CFG_AGD (1 << 0)
|
||||
#define R_CFG_CPHA (1 << 1)
|
||||
#define R_CFG_CPOL (1 << 2)
|
||||
#define R_CFG_MODE(_x) (((_x) >> 5) & 0x3)
|
||||
#define R_CFG_MODE_POLLED 0
|
||||
#define R_CFG_MODE_IRQ 1
|
||||
#define R_CFG_MODE_DMA 2
|
||||
#define R_CFG_IE_RXREADY (1 << 7)
|
||||
#define R_CFG_IE_TXEMPTY (1 << 8)
|
||||
#define R_CFG_WORD_SIZE(_x) (((_x) >> 13) & 0x3)
|
||||
#define R_CFG_WORD_SIZE_8B 0
|
||||
#define R_CFG_WORD_SIZE_16B 1
|
||||
#define R_CFG_WORD_SIZE_32B 2
|
||||
#define R_CFG_IE_COMPLETE (1 << 21)
|
||||
|
||||
#define R_STATUS 0x008
|
||||
#define R_STATUS_RXREADY (1 << 0)
|
||||
#define R_STATUS_TXEMPTY (1 << 1)
|
||||
#define R_STATUS_RXOVERFLOW (1 << 3)
|
||||
#define R_STATUS_COMPLETE (1 << 22)
|
||||
#define R_STATUS_TXFIFO_SHIFT (4)
|
||||
#define R_STATUS_TXFIFO_MASK (31 << R_STATUS_TXFIFO_SHIFT)
|
||||
#define R_STATUS_RXFIFO_SHIFT (8)
|
||||
#define R_STATUS_RXFIFO_MASK (31 << R_STATUS_RXFIFO_SHIFT)
|
||||
|
||||
#define R_PIN 0x00c
|
||||
#define R_PIN_CS (1 << 1)
|
||||
|
||||
#define R_TXDATA 0x010
|
||||
#define R_RXDATA 0x020
|
||||
#define R_CLKDIV 0x030
|
||||
#define R_CLKDIV_MAX 0x7ff
|
||||
#define R_RXCNT 0x034
|
||||
#define R_WORD_DELAY 0x038
|
||||
#define R_TXCNT 0x04c
|
||||
|
||||
#define R_FIFO_DEPTH 50000
|
||||
|
||||
#define REG(_s,_v) ((_s)->regs[(_v)>>2])
|
||||
#define MMIO_SIZE (0x4000)
|
||||
|
||||
|
||||
typedef struct S5L8900SPIState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
MemoryRegion iomem;
|
||||
SSIBus *spi;
|
||||
IPodTouchMultitouchState *mt;
|
||||
|
||||
qemu_irq irq;
|
||||
uint32_t last_irq;
|
||||
qemu_irq cs_line;
|
||||
|
||||
Fifo8 rx_fifo;
|
||||
Fifo8 tx_fifo;
|
||||
uint32_t regs[MMIO_SIZE >> 2];
|
||||
uint32_t mmio_size;
|
||||
uint8_t base;
|
||||
} S5L8900SPIState;
|
||||
|
||||
void set_spi_base(uint32_t base);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef IPOD_TOUCH_SYSIC_H
|
||||
#define IPOD_TOUCH_SYSIC_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/irq.h"
|
||||
|
||||
#define TYPE_IPOD_TOUCH_SYSIC "ipodtouch.sysic"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchSYSICState, IPOD_TOUCH_SYSIC)
|
||||
|
||||
#define POWER_ID 0x44
|
||||
#define POWER_ONCTRL 0xC
|
||||
#define POWER_OFFCTRL 0x10
|
||||
#define POWER_SETSTATE 0x8
|
||||
#define POWER_STATE 0x14 // seems to be toggled by writing a 1 to the right device ID - cleared to 0 when the device has started.
|
||||
|
||||
#define POWER_ID_ADM 0x10
|
||||
|
||||
// the GPIO IC is part of the system controller
|
||||
#define GPIO_INTLEVEL 0x80
|
||||
#define GPIO_INTSTAT 0xA0
|
||||
#define GPIO_INTEN 0xC0
|
||||
#define GPIO_INTTYPE 0xE0
|
||||
|
||||
#define GPIO_NUMINTGROUPS 7
|
||||
|
||||
typedef struct IPodTouchSYSICState {
|
||||
SysBusDevice parent_obj;
|
||||
MemoryRegion iomem;
|
||||
qemu_irq gpio_irqs[GPIO_NUMINTGROUPS];
|
||||
uint32_t power_state;
|
||||
|
||||
// GPIO
|
||||
uint32_t gpio_int_level[GPIO_NUMINTGROUPS];
|
||||
uint32_t gpio_int_status[GPIO_NUMINTGROUPS];
|
||||
uint32_t gpio_int_enabled[GPIO_NUMINTGROUPS];
|
||||
uint32_t gpio_int_type[GPIO_NUMINTGROUPS];
|
||||
} IPodTouchSYSICState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,52 @@
|
||||
#ifndef IPOD_TOUCH_TIMER_H
|
||||
#define IPOD_TOUCH_TIMER_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/clock.h"
|
||||
|
||||
#define TYPE_IPOD_TOUCH_TIMER "ipodtouch.timer"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchTimerState, IPOD_TOUCH_TIMER)
|
||||
|
||||
#define TIMER_IRQSTAT 0x10000
|
||||
#define TIMER_IRQLATCH 0xF8
|
||||
#define TIMER_TICKSHIGH 0x80
|
||||
#define TIMER_TICKSLOW 0x84
|
||||
#define TIMER_STATE_START 1
|
||||
#define TIMER_STATE_STOP 0
|
||||
#define TIMER_STATE_MANUALUPDATE 2
|
||||
#define NUM_TIMERS 7
|
||||
#define TIMER_4 0xA0
|
||||
#define TIMER_CONFIG 0
|
||||
#define TIMER_STATE 0x4
|
||||
#define TIMER_COUNT_BUFFER 0x8
|
||||
#define TIMER_COUNT_BUFFER2 0xC
|
||||
|
||||
typedef struct IPodTouchTimerState
|
||||
{
|
||||
SysBusDevice busdev;
|
||||
MemoryRegion iomem;
|
||||
uint32_t ticks_high;
|
||||
uint32_t ticks_low;
|
||||
uint32_t status;
|
||||
uint32_t config;
|
||||
uint32_t bcount1;
|
||||
uint32_t bcount2;
|
||||
uint32_t prescaler;
|
||||
uint32_t irqstat;
|
||||
QEMUTimer *st_timer;
|
||||
Clock *sysclk;
|
||||
uint32_t bcreload;
|
||||
uint32_t freq_out;
|
||||
uint64_t tick_interval;
|
||||
uint64_t last_tick;
|
||||
uint64_t next_planned_tick;
|
||||
uint64_t base_time;
|
||||
qemu_irq irq;
|
||||
|
||||
} IPodTouchTimerState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef IPOD_TOUCH_TVOUT_H
|
||||
#define IPOD_TOUCH_TVOUT_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/irq.h"
|
||||
|
||||
#define SDO_IRQ 0x280
|
||||
|
||||
#define TYPE_IPOD_TOUCH_TVOUT "ipodtouch.tvout"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchTVOutState, IPOD_TOUCH_TVOUT)
|
||||
|
||||
typedef struct IPodTouchTVOutState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
MemoryRegion iomem;
|
||||
qemu_irq irq;
|
||||
uint32_t index;
|
||||
|
||||
//uint32_t sdo_irq_reg;
|
||||
uint32_t data[4096];
|
||||
} IPodTouchTVOutState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,267 @@
|
||||
#ifndef HW_ARM_IPOD_TOUCH_USB_OTG_H
|
||||
#define HW_ARM_IPOD_TOUCH_USB_OTG_H
|
||||
|
||||
#include "hw/irq.h"
|
||||
#include "hw/usb.h"
|
||||
|
||||
#define DEVICE_NAME "usb_synopsys"
|
||||
|
||||
#define TYPE_S5L8900USBOTG "s5l8900usbotg"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(synopsys_usb_state, S5L8900USBOTG)
|
||||
|
||||
// Maximums supported by OIB
|
||||
#define USB_NUM_ENDPOINTS 8
|
||||
#define USB_NUM_FIFOS 16
|
||||
|
||||
#define RX_FIFO_DEPTH 0x1C0
|
||||
#define TX_FIFO_DEPTH 0x1C0
|
||||
#define TX_FIFO_STARTADDR 0x200
|
||||
#define PERIODIC_TX_FIFO_STARTADDR 0x21B
|
||||
#define PERIODIC_TX_FIFO_DEPTH 0x100
|
||||
|
||||
// Registers
|
||||
#define GOTGCTL 0x0
|
||||
#define GOTGINT 0x4
|
||||
#define GAHBCFG 0x8
|
||||
#define GUSBCFG 0xC
|
||||
#define GRSTCTL 0x10
|
||||
#define GINTSTS 0x14
|
||||
#define GINTMSK 0x18
|
||||
#define GRXSTSR 0x1C
|
||||
#define GRXSTSP 0x20
|
||||
#define GRXFSIZ 0x24
|
||||
#define GNPTXFSIZ 0x28
|
||||
#define GNPTXFSTS 0x2C
|
||||
#define GHWCFG1 0x44
|
||||
#define GHWCFG2 0x48
|
||||
#define GHWCFG3 0x4C
|
||||
#define GHWCFG4 0x50
|
||||
#define DIEPTXF(x) (0x100 + (4*(x)))
|
||||
#define DCFG 0x800
|
||||
#define DCTL 0x804
|
||||
#define DSTS 0x808
|
||||
#define DIEPMSK 0x810
|
||||
#define DOEPMSK 0x814
|
||||
#define DAINTSTS 0x818
|
||||
#define DAINTMSK 0x81C
|
||||
#define DTKNQR1 0x820
|
||||
#define DTKNQR2 0x824
|
||||
#define DTKNQR3 0x830
|
||||
#define DTKNQR4 0x834
|
||||
#define USB_INREGS 0x900
|
||||
#define USB_OUTREGS 0xB00
|
||||
#define USB_EPREGS_SIZE 0x200
|
||||
|
||||
#define USB_FIFO_START 0x1000
|
||||
#define USB_FIFO_SIZE (0x100*(USB_NUM_FIFOS+1))
|
||||
#define USB_FIFO_END (USB_FIFO_START+USB_FIFO_SIZE)
|
||||
|
||||
#define PCGCCTL 0xE00
|
||||
|
||||
#define PCGCCTL_ONOFF_MASK 3 // bits 0, 1
|
||||
#define PCGCCTL_ON 0
|
||||
#define PCGCCTL_OFF 1
|
||||
|
||||
#define GOTGCTL_BSESSIONVALID (1 << 19)
|
||||
#define GOTGCTL_SESSIONREQUEST (1 << 1)
|
||||
|
||||
#define GAHBCFG_DMAEN (1 << 5)
|
||||
#define GAHBCFG_BSTLEN_SINGLE (0 << 1)
|
||||
#define GAHBCFG_BSTLEN_INCR (1 << 1)
|
||||
#define GAHBCFG_BSTLEN_INCR4 (3 << 1)
|
||||
#define GAHBCFG_BSTLEN_INCR8 (5 << 1)
|
||||
#define GAHBCFG_BSTLEN_INCR16 (7 << 1)
|
||||
#define GAHBCFG_MASKINT 0x1
|
||||
|
||||
#define GUSBCFG_TURNAROUND_MASK 0xF
|
||||
#define GUSBCFG_TURNAROUND_SHIFT 10
|
||||
#define GUSBCFG_HNPENABLE (1 << 9)
|
||||
#define GUSBCFG_SRPENABLE (1 << 8)
|
||||
#define GUSBCFG_PHYIF16BIT (1 << 3)
|
||||
#define USB_UNKNOWNREG1_START 0x1708
|
||||
|
||||
#define GHWCFG2_TKNDEPTH_SHIFT 26
|
||||
#define GHWCFG2_TKNDEPTH_MASK 0xF
|
||||
#define GHWCFG2_NUM_ENDPOINTS_SHIFT 10
|
||||
#define GHWCFG2_NUM_ENDPOINTS_MASK 0xf
|
||||
|
||||
#define GHWCFG4_DED_FIFO_EN (1 << 25)
|
||||
|
||||
#define GRSTCTL_AHBIDLE (1 << 31)
|
||||
#define GRSTCTL_TXFFLUSH (1 << 5)
|
||||
#define GRSTCTL_TXFFNUM_SHIFT 6
|
||||
#define GRSTCTL_TXFFNUM_MASK 0x1f
|
||||
#define GRSTCTL_CORESOFTRESET 0x1
|
||||
#define GRSTCTL_TKNFLUSH 3
|
||||
|
||||
#define GINTMSK_NONE 0x0
|
||||
#define GINTMSK_OTG (1 << 2)
|
||||
#define GINTMSK_SOF (1 << 3)
|
||||
#define GINTMSK_GINNAKEFF (1 << 6)
|
||||
#define GINTMSK_GOUTNAKEFF (1 << 7)
|
||||
#define GINTMSK_SUSPEND (1 << 11)
|
||||
#define GINTMSK_RESET (1 << 12)
|
||||
#define GINTMSK_ENUMDONE (1 << 13)
|
||||
#define GINTMSK_EPMIS (1 << 17)
|
||||
#define GINTMSK_INEP (1 << 18)
|
||||
#define GINTMSK_OEP (1 << 19)
|
||||
#define GINTMSK_DISCONNECT (1 << 29)
|
||||
#define GINTMSK_RESUME (1 << 31)
|
||||
|
||||
#define GOTGINT_SESENDDET (1 << 2)
|
||||
|
||||
#define FIFO_DEPTH_SHIFT 16
|
||||
|
||||
#define GNPTXFSTS_GET_TXQSPCAVAIL(x) GET_BITS(x, 16, 8)
|
||||
|
||||
#define GHWCFG4_DED_FIFO_EN (1 << 25)
|
||||
|
||||
#define DAINT_ALL 0xFFFFFFFF
|
||||
#define DAINT_NONE 0
|
||||
#define DAINT_OUT_SHIFT 16
|
||||
#define DAINT_IN_SHIFT 0
|
||||
|
||||
#define DCTL_SFTDISCONNECT 0x2
|
||||
#define DCTL_PROGRAMDONE (1 << 11)
|
||||
#define DCTL_CGOUTNAK (1 << 10)
|
||||
#define DCTL_SGOUTNAK (1 << 9)
|
||||
#define DCTL_CGNPINNAK (1 << 8)
|
||||
#define DCTL_SGNPINNAK (1 << 7)
|
||||
|
||||
#define DSTS_GET_SPEED(x) GET_BITS(x, 1, 2)
|
||||
|
||||
#define DCFG_NZSTSOUTHSHK (1 << 2)
|
||||
#define DCFG_EPMSCNT (1 << 18)
|
||||
#define DCFG_HISPEED 0x0
|
||||
#define DCFG_FULLSPEED 0x1
|
||||
#define DCFG_DEVICEADDR_UNSHIFTED_MASK 0x7F
|
||||
#define DCFG_DEVICEADDR_SHIFT 4
|
||||
#define DCFG_DEVICEADDRMSK (DCFG_DEVICEADDR_UNSHIFTED_MASK << DCFG_DEVICEADDR_SHIFT)
|
||||
#define DCFG_ACTIVE_EP_COUNT_MASK 0x1f
|
||||
#define DCFG_ACTIVE_EP_COUNT_SHIFT 18
|
||||
|
||||
#define DOEPTSIZ0_SUPCNT_MASK 0x3
|
||||
#define DOEPTSIZ0_SUPCNT_SHIFT 29
|
||||
#define DOEPTSIZ0_PKTCNT_MASK 0x1
|
||||
#define DEPTSIZ0_XFERSIZ_MASK 0x7F
|
||||
#define DIEPTSIZ_MC_MASK 0x3
|
||||
#define DIEPTSIZ_MC_SHIFT 29
|
||||
#define DEPTSIZ_PKTCNT_MASK 0x3FF
|
||||
#define DEPTSIZ_PKTCNT_SHIFT 19
|
||||
#define DEPTSIZ_XFERSIZ_MASK 0x1FFFF
|
||||
|
||||
// ENDPOINT_DIRECTIONS register has two bits per endpoint. 0, 1 for endpoint 0. 1, 2 for end point 1, etc.
|
||||
#define USB_EP_DIRECTION(ep) (USBDirection)(2-((GET_REG(USB + GHWCFG1) >> ((ep) * 2)) & 0x3))
|
||||
#define USB_ENDPOINT_DIRECTIONS_BIDIR 0
|
||||
#define USB_ENDPOINT_DIRECTIONS_IN 1
|
||||
#define USB_ENDPOINT_DIRECTIONS_OUT 2
|
||||
|
||||
#define USB_START_DELAYUS 10000
|
||||
#define USB_SFTDISCONNECT_DELAYUS 4000
|
||||
#define USB_ONOFFSTART_DELAYUS 100
|
||||
#define USB_RESETWAITFINISH_DELAYUS 1000
|
||||
#define USB_SFTCONNECT_DELAYUS 250
|
||||
#define USB_PROGRAMDONE_DELAYUS 10
|
||||
|
||||
#define USB_EPCON_ENABLE (1 << 31)
|
||||
#define USB_EPCON_DISABLE (1 << 30)
|
||||
#define USB_EPCON_SETD0PID (1 << 28)
|
||||
#define USB_EPCON_SETNAK (1 << 27)
|
||||
#define USB_EPCON_CLEARNAK (1 << 26)
|
||||
#define USB_EPCON_TXFNUM_MASK 0xf
|
||||
#define USB_EPCON_TXFNUM_SHIFT 22
|
||||
#define USB_EPCON_STALL (1 << 21)
|
||||
#define USB_EPCON_TYPE_MASK 0x3
|
||||
#define USB_EPCON_TYPE_SHIFT 18
|
||||
#define USB_EPCON_NAKSTS (1 << 17)
|
||||
#define USB_EPCON_ACTIVE (1 << 15)
|
||||
#define USB_EPCON_NEXTEP_MASK 0xF
|
||||
#define USB_EPCON_NEXTEP_SHIFT 11
|
||||
#define USB_EPCON_MPS_MASK 0x7FF
|
||||
|
||||
#define USB_EPINT_INEPNakEff 0x40
|
||||
#define USB_EPINT_INTknEPMis 0x20
|
||||
#define USB_EPINT_INTknTXFEmp 0x10
|
||||
#define USB_EPINT_TimeOUT 0x8
|
||||
#define USB_EPINT_AHBErr 0x4
|
||||
#define USB_EPINT_EPDisbld 0x2
|
||||
#define USB_EPINT_XferCompl 0x1
|
||||
|
||||
#define USB_EPINT_Back2BackSetup (1 << 6)
|
||||
#define USB_EPINT_OUTTknEPDis 0x10
|
||||
#define USB_EPINT_SetUp 0x8
|
||||
#define USB_EPINT_EpDisbld 0x1
|
||||
#define USB_EPINT_NONE 0
|
||||
#define USB_EPINT_ALL 0xFFFFFFFF
|
||||
|
||||
#define USB_2_0 0x0200
|
||||
|
||||
#define USB_HIGHSPEED 0
|
||||
#define USB_FULLSPEED 1
|
||||
#define USB_LOWSPEED 2
|
||||
#define USB_FULLSPEED_48_MHZ 3
|
||||
|
||||
#define USB_CONTROLEP 0
|
||||
|
||||
typedef struct _synopsys_usb_ep_state
|
||||
{
|
||||
uint32_t control;
|
||||
uint32_t tx_size;
|
||||
uint32_t fifo;
|
||||
uint32_t interrupt_status;
|
||||
|
||||
hwaddr dma_address;
|
||||
hwaddr dma_buffer;
|
||||
|
||||
} synopsys_usb_ep_state;
|
||||
|
||||
typedef struct synopsys_usb_state
|
||||
{
|
||||
SysBusDevice busdev;
|
||||
MemoryRegion iomem;
|
||||
qemu_irq irq;
|
||||
|
||||
char *server_host;
|
||||
uint32_t server_port;
|
||||
//tcp_usb_state_t tcp_state;
|
||||
|
||||
uint32_t pcgcctl;
|
||||
|
||||
uint32_t ghwcfg1;
|
||||
uint32_t ghwcfg2;
|
||||
uint32_t ghwcfg3;
|
||||
uint32_t ghwcfg4;
|
||||
|
||||
uint32_t gahbcfg;
|
||||
uint32_t gusbcfg;
|
||||
|
||||
uint32_t grxfsiz;
|
||||
uint32_t gnptxfsiz;
|
||||
|
||||
uint32_t gotgctl;
|
||||
uint32_t gotgint;
|
||||
uint32_t grstctl;
|
||||
uint32_t gintmsk;
|
||||
uint32_t gintsts;
|
||||
|
||||
uint32_t dptxfsiz[USB_NUM_FIFOS];
|
||||
|
||||
uint32_t dctl;
|
||||
uint32_t dcfg;
|
||||
uint32_t dsts;
|
||||
|
||||
uint32_t daintmsk;
|
||||
uint32_t daintsts;
|
||||
uint32_t diepmsk;
|
||||
uint32_t doepmsk;
|
||||
|
||||
synopsys_usb_ep_state in_eps[USB_NUM_ENDPOINTS];
|
||||
synopsys_usb_ep_state out_eps[USB_NUM_ENDPOINTS];
|
||||
|
||||
uint8_t fifos[0x100 * (USB_NUM_FIFOS+1)];
|
||||
|
||||
} synopsys_usb_state;
|
||||
|
||||
DeviceState *ipod_touch_init_usb_otg(qemu_irq _irq, uint32_t _hwcfg[4]);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,64 @@
|
||||
#ifndef IPOD_TOUCH_I2C_H
|
||||
#define IPOD_TOUCH_I2C_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/i2c/i2c.h"
|
||||
#include "hw/irq.h"
|
||||
|
||||
#define TYPE_IPOD_TOUCH_I2C "ipodtouch.i2c"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(IPodTouchI2CState, IPOD_TOUCH_I2C)
|
||||
|
||||
#define I2CCON 0x00 /* I2C Control register */
|
||||
#define I2CSTAT 0x04 /* I2C Status register */
|
||||
#define I2CADD 0x08 /* I2C Slave Address register */
|
||||
#define I2CDS 0x0c /* I2C Data Shift register */
|
||||
#define I2CLC 0x10 /* I2C Line Control register */
|
||||
|
||||
#define IICREG20 0x20
|
||||
|
||||
#define SR_MODE 0x0 /* Slave Receive Mode */
|
||||
#define ST_MODE 0x1 /* Slave Transmit Mode */
|
||||
#define MR_MODE 0x2 /* Master Receive Mode */
|
||||
#define MT_MODE 0x3 /* Master Transmit Mode */
|
||||
|
||||
|
||||
#define S5L8900_IICCON_ACKEN (1<<7)
|
||||
#define S5L8900_IICCON_TXDIV_16 (0<<6)
|
||||
#define S5L8900_IICCON_TXDIV_512 (1<<6)
|
||||
#define S5L8900_IICCON_IRQEN (1<<5)
|
||||
#define S5L8900_IICCON_IRQPEND (1<<4)
|
||||
|
||||
#define S5L8900_IICSTAT_START (1<<5)
|
||||
#define S5L8900_IICSTAT_BUSBUSY (1<<5)
|
||||
#define S5L8900_IICSTAT_TXRXEN (1<<4)
|
||||
#define S5L8900_IICSTAT_ARBITR (1<<3)
|
||||
#define S5L8900_IICSTAT_ASSLAVE (1<<2)
|
||||
#define S5L8900_IICSTAT_ADDR0 (1<<1)
|
||||
#define S5L8900_IICSTAT_LASTBIT (1<<0)
|
||||
|
||||
#define S5L8900_I2C_REG_MEM_SIZE 0x1000
|
||||
|
||||
typedef struct IPodTouchI2CState {
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
MemoryRegion iomem;
|
||||
I2CBus *bus;
|
||||
qemu_irq irq;
|
||||
|
||||
uint8_t control;
|
||||
uint8_t status;
|
||||
uint8_t address;
|
||||
uint8_t datashift;
|
||||
uint8_t line_ctrl;
|
||||
uint32_t iicreg20;
|
||||
|
||||
uint8_t active;
|
||||
|
||||
uint8_t ibmr;
|
||||
uint8_t data;
|
||||
} IPodTouchI2CState;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,80 @@
|
||||
#ifndef PL192_H
|
||||
#define PL192_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
|
||||
#define TYPE_PL192 "pl192"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(PL192State, PL192)
|
||||
|
||||
|
||||
#define PL192_INT_SOURCES 32
|
||||
#define PL192_DAISY_IRQ PL192_INT_SOURCES
|
||||
#define PL192_NO_IRQ PL192_INT_SOURCES+1
|
||||
#define PL192_PRIO_LEVELS 16
|
||||
|
||||
#define PL192_IRQSTATUS 0x00
|
||||
#define PL192_FIQSTATUS 0x04
|
||||
#define PL192_RAWINTR 0x08
|
||||
#define PL192_INTSELECT 0x0C
|
||||
#define PL192_INTENABLE 0x10
|
||||
#define PL192_INTENCLEAR 0x14
|
||||
#define PL192_SOFTINT 0x18
|
||||
#define PL192_SOFTINTCLEAR 0x1C
|
||||
#define PL192_PROTECTION 0x20
|
||||
#define PL192_SWPRIORITYMASK 0x24
|
||||
#define PL192_PRIORITYDAISY 0x28
|
||||
#define PL192_VECTADDR 0xF00
|
||||
#define PL192_IOMEM_SIZE 0x1000
|
||||
|
||||
#define PL190_ITCR 0x300
|
||||
#define PL190_VECTADDR 0x30
|
||||
#define PL190_DEFVECTADDR 0x34
|
||||
|
||||
#define PL192_IOMEM_SIZE 0x1000
|
||||
|
||||
|
||||
struct PL192State {
|
||||
SysBusDevice busdev;
|
||||
MemoryRegion iomem;
|
||||
|
||||
/* Control registers */
|
||||
uint32_t irq_status;
|
||||
uint32_t fiq_status;
|
||||
uint32_t rawintr;
|
||||
uint32_t intselect;
|
||||
uint32_t intenable;
|
||||
uint32_t softint;
|
||||
uint32_t protection;
|
||||
uint32_t sw_priority_mask;
|
||||
uint32_t vect_addr[PL192_INT_SOURCES];
|
||||
uint32_t vect_priority[PL192_INT_SOURCES];
|
||||
uint32_t address;
|
||||
|
||||
/* Currently processed interrupt and
|
||||
highest priority interrupt */
|
||||
uint32_t current;
|
||||
uint32_t current_highest;
|
||||
|
||||
/* Priority masking logic */
|
||||
int32_t stack_i;
|
||||
uint32_t priority_stack[PL192_PRIO_LEVELS+1];
|
||||
uint8_t irq_stack[PL192_PRIO_LEVELS+1];
|
||||
uint32_t priority;
|
||||
|
||||
/* Daisy-chain interface */
|
||||
uint32_t daisy_vectaddr;
|
||||
uint32_t daisy_priority;
|
||||
PL192State *daisy_callback;
|
||||
uint8_t daisy_input;
|
||||
|
||||
/* Parent interrupts */
|
||||
qemu_irq irq;
|
||||
qemu_irq fiq;
|
||||
|
||||
/* Next controller in chain */
|
||||
PL192State *daisy;
|
||||
};
|
||||
|
||||
DeviceState *pl192_manual_init(char *mem_name, ...);
|
||||
|
||||
#endif
|
||||
@@ -249,7 +249,7 @@ static void absolute_mouse_grab(struct sdl2_console *scon)
|
||||
SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
|
||||
if (mouse_x > 0 && mouse_x < scr_w - 1 &&
|
||||
mouse_y > 0 && mouse_y < scr_h - 1) {
|
||||
sdl_grab_start(scon);
|
||||
//sdl_grab_start(scon);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -486,7 +486,7 @@ static void handle_mousemotion(SDL_Event *ev)
|
||||
if (!gui_grab &&
|
||||
(ev->motion.x > 0 && ev->motion.x < max_x &&
|
||||
ev->motion.y > 0 && ev->motion.y < max_y)) {
|
||||
sdl_grab_start(scon);
|
||||
//sdl_grab_start(scon);
|
||||
}
|
||||
}
|
||||
if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
|
||||
@@ -509,7 +509,7 @@ static void handle_mousebutton(SDL_Event *ev)
|
||||
if (!gui_grab && !qemu_input_is_absolute()) {
|
||||
if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) {
|
||||
/* start grabbing all events */
|
||||
sdl_grab_start(scon);
|
||||
//sdl_grab_start(scon);
|
||||
}
|
||||
} else {
|
||||
if (ev->type == SDL_MOUSEBUTTONDOWN) {
|
||||
@@ -911,7 +911,7 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o)
|
||||
sdl_cursor_normal = SDL_GetCursor();
|
||||
|
||||
if (gui_fullscreen) {
|
||||
sdl_grab_start(&sdl2_console[0]);
|
||||
//sdl_grab_start(&sdl2_console[0]);
|
||||
}
|
||||
|
||||
atexit(sdl_cleanup);
|
||||
|
||||
Reference in new issue
Block a user