diff -r ffa851df0825 -r 2fb8b9db1c86 symbian-qemu-0.9.1-12/qemu-symbian-svp/hw/slavio_misc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/symbian-qemu-0.9.1-12/qemu-symbian-svp/hw/slavio_misc.c Fri Jul 31 15:01:17 2009 +0100 @@ -0,0 +1,513 @@ +/* + * QEMU Sparc SLAVIO aux io port emulation + * + * Copyright (c) 2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "hw.h" +#include "sun4m.h" +#include "sysemu.h" + +/* debug misc */ +//#define DEBUG_MISC + +/* + * This is the auxio port, chip control and system control part of + * chip STP2001 (Slave I/O), also produced as NCR89C105. See + * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt + * + * This also includes the PMC CPU idle controller. + */ + +#ifdef DEBUG_MISC +#define MISC_DPRINTF(fmt, args...) \ +do { printf("MISC: " fmt , ##args); } while (0) +#else +#define MISC_DPRINTF(fmt, args...) +#endif + +typedef struct MiscState { + qemu_irq irq; + uint8_t config; + uint8_t aux1, aux2; + uint8_t diag, mctrl; + uint32_t sysctrl; + uint16_t leds; + qemu_irq cpu_halt; + qemu_irq fdc_tc; +} MiscState; + +#define MISC_SIZE 1 +#define SYSCTRL_SIZE 4 +#define LED_MAXADDR 1 +#define LED_SIZE (LED_MAXADDR + 1) + +#define MISC_MASK 0x0fff0000 +#define MISC_LEDS 0x01600000 +#define MISC_CFG 0x01800000 +#define MISC_DIAG 0x01a00000 +#define MISC_MDM 0x01b00000 +#define MISC_SYS 0x01f00000 + +#define AUX1_TC 0x02 + +#define AUX2_PWROFF 0x01 +#define AUX2_PWRINTCLR 0x02 +#define AUX2_PWRFAIL 0x20 + +#define CFG_PWRINTEN 0x08 + +#define SYS_RESET 0x01 +#define SYS_RESETSTAT 0x02 + +static void slavio_misc_update_irq(void *opaque) +{ + MiscState *s = opaque; + + if ((s->aux2 & AUX2_PWRFAIL) && (s->config & CFG_PWRINTEN)) { + MISC_DPRINTF("Raise IRQ\n"); + qemu_irq_raise(s->irq); + } else { + MISC_DPRINTF("Lower IRQ\n"); + qemu_irq_lower(s->irq); + } +} + +static void slavio_misc_reset(void *opaque) +{ + MiscState *s = opaque; + + // Diagnostic and system control registers not cleared in reset + s->config = s->aux1 = s->aux2 = s->mctrl = 0; +} + +void slavio_set_power_fail(void *opaque, int power_failing) +{ + MiscState *s = opaque; + + MISC_DPRINTF("Power fail: %d, config: %d\n", power_failing, s->config); + if (power_failing && (s->config & CFG_PWRINTEN)) { + s->aux2 |= AUX2_PWRFAIL; + } else { + s->aux2 &= ~AUX2_PWRFAIL; + } + slavio_misc_update_irq(s); +} + +static void slavio_cfg_mem_writeb(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + MiscState *s = opaque; + + MISC_DPRINTF("Write config %2.2x\n", val & 0xff); + s->config = val & 0xff; + slavio_misc_update_irq(s); +} + +static uint32_t slavio_cfg_mem_readb(void *opaque, target_phys_addr_t addr) +{ + MiscState *s = opaque; + uint32_t ret = 0; + + ret = s->config; + MISC_DPRINTF("Read config %2.2x\n", ret); + return ret; +} + +static CPUReadMemoryFunc *slavio_cfg_mem_read[3] = { + slavio_cfg_mem_readb, + NULL, + NULL, +}; + +static CPUWriteMemoryFunc *slavio_cfg_mem_write[3] = { + slavio_cfg_mem_writeb, + NULL, + NULL, +}; + +static void slavio_diag_mem_writeb(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + MiscState *s = opaque; + + MISC_DPRINTF("Write diag %2.2x\n", val & 0xff); + s->diag = val & 0xff; +} + +static uint32_t slavio_diag_mem_readb(void *opaque, target_phys_addr_t addr) +{ + MiscState *s = opaque; + uint32_t ret = 0; + + ret = s->diag; + MISC_DPRINTF("Read diag %2.2x\n", ret); + return ret; +} + +static CPUReadMemoryFunc *slavio_diag_mem_read[3] = { + slavio_diag_mem_readb, + NULL, + NULL, +}; + +static CPUWriteMemoryFunc *slavio_diag_mem_write[3] = { + slavio_diag_mem_writeb, + NULL, + NULL, +}; + +static void slavio_mdm_mem_writeb(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + MiscState *s = opaque; + + MISC_DPRINTF("Write modem control %2.2x\n", val & 0xff); + s->mctrl = val & 0xff; +} + +static uint32_t slavio_mdm_mem_readb(void *opaque, target_phys_addr_t addr) +{ + MiscState *s = opaque; + uint32_t ret = 0; + + ret = s->mctrl; + MISC_DPRINTF("Read modem control %2.2x\n", ret); + return ret; +} + +static CPUReadMemoryFunc *slavio_mdm_mem_read[3] = { + slavio_mdm_mem_readb, + NULL, + NULL, +}; + +static CPUWriteMemoryFunc *slavio_mdm_mem_write[3] = { + slavio_mdm_mem_writeb, + NULL, + NULL, +}; + +static void slavio_aux1_mem_writeb(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + MiscState *s = opaque; + + MISC_DPRINTF("Write aux1 %2.2x\n", val & 0xff); + if (val & AUX1_TC) { + // Send a pulse to floppy terminal count line + if (s->fdc_tc) { + qemu_irq_raise(s->fdc_tc); + qemu_irq_lower(s->fdc_tc); + } + val &= ~AUX1_TC; + } + s->aux1 = val & 0xff; +} + +static uint32_t slavio_aux1_mem_readb(void *opaque, target_phys_addr_t addr) +{ + MiscState *s = opaque; + uint32_t ret = 0; + + ret = s->aux1; + MISC_DPRINTF("Read aux1 %2.2x\n", ret); + + return ret; +} + +static CPUReadMemoryFunc *slavio_aux1_mem_read[3] = { + slavio_aux1_mem_readb, + NULL, + NULL, +}; + +static CPUWriteMemoryFunc *slavio_aux1_mem_write[3] = { + slavio_aux1_mem_writeb, + NULL, + NULL, +}; + +static void slavio_aux2_mem_writeb(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + MiscState *s = opaque; + + val &= AUX2_PWRINTCLR | AUX2_PWROFF; + MISC_DPRINTF("Write aux2 %2.2x\n", val); + val |= s->aux2 & AUX2_PWRFAIL; + if (val & AUX2_PWRINTCLR) // Clear Power Fail int + val &= AUX2_PWROFF; + s->aux2 = val; + if (val & AUX2_PWROFF) + qemu_system_shutdown_request(); + slavio_misc_update_irq(s); +} + +static uint32_t slavio_aux2_mem_readb(void *opaque, target_phys_addr_t addr) +{ + MiscState *s = opaque; + uint32_t ret = 0; + + ret = s->aux2; + MISC_DPRINTF("Read aux2 %2.2x\n", ret); + + return ret; +} + +static CPUReadMemoryFunc *slavio_aux2_mem_read[3] = { + slavio_aux2_mem_readb, + NULL, + NULL, +}; + +static CPUWriteMemoryFunc *slavio_aux2_mem_write[3] = { + slavio_aux2_mem_writeb, + NULL, + NULL, +}; + +static void apc_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + MiscState *s = opaque; + + MISC_DPRINTF("Write power management %2.2x\n", val & 0xff); + qemu_irq_raise(s->cpu_halt); +} + +static uint32_t apc_mem_readb(void *opaque, target_phys_addr_t addr) +{ + uint32_t ret = 0; + + MISC_DPRINTF("Read power management %2.2x\n", ret); + return ret; +} + +static CPUReadMemoryFunc *apc_mem_read[3] = { + apc_mem_readb, + NULL, + NULL, +}; + +static CPUWriteMemoryFunc *apc_mem_write[3] = { + apc_mem_writeb, + NULL, + NULL, +}; + +static uint32_t slavio_sysctrl_mem_readl(void *opaque, target_phys_addr_t addr) +{ + MiscState *s = opaque; + uint32_t ret = 0; + + switch (addr) { + case 0: + ret = s->sysctrl; + break; + default: + break; + } + MISC_DPRINTF("Read system control %08x\n", ret); + return ret; +} + +static void slavio_sysctrl_mem_writel(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + MiscState *s = opaque; + + MISC_DPRINTF("Write system control %08x\n", val); + switch (addr) { + case 0: + if (val & SYS_RESET) { + s->sysctrl = SYS_RESETSTAT; + qemu_system_reset_request(); + } + break; + default: + break; + } +} + +static CPUReadMemoryFunc *slavio_sysctrl_mem_read[3] = { + NULL, + NULL, + slavio_sysctrl_mem_readl, +}; + +static CPUWriteMemoryFunc *slavio_sysctrl_mem_write[3] = { + NULL, + NULL, + slavio_sysctrl_mem_writel, +}; + +static uint32_t slavio_led_mem_readw(void *opaque, target_phys_addr_t addr) +{ + MiscState *s = opaque; + uint32_t ret = 0; + + switch (addr) { + case 0: + ret = s->leds; + break; + default: + break; + } + MISC_DPRINTF("Read diagnostic LED %04x\n", ret); + return ret; +} + +static void slavio_led_mem_writew(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + MiscState *s = opaque; + + MISC_DPRINTF("Write diagnostic LED %04x\n", val & 0xffff); + switch (addr) { + case 0: + s->leds = val; + break; + default: + break; + } +} + +static CPUReadMemoryFunc *slavio_led_mem_read[3] = { + NULL, + slavio_led_mem_readw, + NULL, +}; + +static CPUWriteMemoryFunc *slavio_led_mem_write[3] = { + NULL, + slavio_led_mem_writew, + NULL, +}; + +static void slavio_misc_save(QEMUFile *f, void *opaque) +{ + MiscState *s = opaque; + uint32_t tmp = 0; + uint8_t tmp8; + + qemu_put_be32s(f, &tmp); /* ignored, was IRQ. */ + qemu_put_8s(f, &s->config); + qemu_put_8s(f, &s->aux1); + qemu_put_8s(f, &s->aux2); + qemu_put_8s(f, &s->diag); + qemu_put_8s(f, &s->mctrl); + tmp8 = s->sysctrl & 0xff; + qemu_put_8s(f, &tmp8); +} + +static int slavio_misc_load(QEMUFile *f, void *opaque, int version_id) +{ + MiscState *s = opaque; + uint32_t tmp; + uint8_t tmp8; + + if (version_id != 1) + return -EINVAL; + + qemu_get_be32s(f, &tmp); + qemu_get_8s(f, &s->config); + qemu_get_8s(f, &s->aux1); + qemu_get_8s(f, &s->aux2); + qemu_get_8s(f, &s->diag); + qemu_get_8s(f, &s->mctrl); + qemu_get_8s(f, &tmp8); + s->sysctrl = (uint32_t)tmp8; + return 0; +} + +void *slavio_misc_init(target_phys_addr_t base, target_phys_addr_t power_base, + target_phys_addr_t aux1_base, + target_phys_addr_t aux2_base, qemu_irq irq, + qemu_irq cpu_halt, qemu_irq **fdc_tc) +{ + int io; + MiscState *s; + + s = qemu_mallocz(sizeof(MiscState)); + if (!s) + return NULL; + + if (base) { + /* 8 bit registers */ + + // Slavio control + io = cpu_register_io_memory(0, slavio_cfg_mem_read, + slavio_cfg_mem_write, s); + cpu_register_physical_memory(base + MISC_CFG, MISC_SIZE, io); + + // Diagnostics + io = cpu_register_io_memory(0, slavio_diag_mem_read, + slavio_diag_mem_write, s); + cpu_register_physical_memory(base + MISC_DIAG, MISC_SIZE, io); + + // Modem control + io = cpu_register_io_memory(0, slavio_mdm_mem_read, + slavio_mdm_mem_write, s); + cpu_register_physical_memory(base + MISC_MDM, MISC_SIZE, io); + + /* 16 bit registers */ + io = cpu_register_io_memory(0, slavio_led_mem_read, + slavio_led_mem_write, s); + /* ss600mp diag LEDs */ + cpu_register_physical_memory(base + MISC_LEDS, MISC_SIZE, io); + + /* 32 bit registers */ + io = cpu_register_io_memory(0, slavio_sysctrl_mem_read, + slavio_sysctrl_mem_write, s); + // System control + cpu_register_physical_memory(base + MISC_SYS, SYSCTRL_SIZE, io); + } + + // AUX 1 (Misc System Functions) + if (aux1_base) { + io = cpu_register_io_memory(0, slavio_aux1_mem_read, + slavio_aux1_mem_write, s); + cpu_register_physical_memory(aux1_base, MISC_SIZE, io); + } + + // AUX 2 (Software Powerdown Control) + if (aux2_base) { + io = cpu_register_io_memory(0, slavio_aux2_mem_read, + slavio_aux2_mem_write, s); + cpu_register_physical_memory(aux2_base, MISC_SIZE, io); + } + + // Power management (APC) XXX: not a Slavio device + if (power_base) { + io = cpu_register_io_memory(0, apc_mem_read, apc_mem_write, s); + cpu_register_physical_memory(power_base, MISC_SIZE, io); + } + + s->irq = irq; + s->cpu_halt = cpu_halt; + *fdc_tc = &s->fdc_tc; + + register_savevm("slavio_misc", base, 1, slavio_misc_save, slavio_misc_load, + s); + qemu_register_reset(slavio_misc_reset, s); + slavio_misc_reset(s); + + return s; +}