From patchwork Wed Oct 28 16:53:14 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?b?5q2m55SwID0/SVNPLTIwMjItSlA/Qj9JQnNrUWoxVFRHa2JLRUk9Pz0=?= X-Patchwork-Id: 37157 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id DB153B7B90 for ; Thu, 29 Oct 2009 08:55:37 +1100 (EST) Received: from localhost ([127.0.0.1]:53567 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1N3D3Y-0007CI-Rg for incoming@patchwork.ozlabs.org; Wed, 28 Oct 2009 14:15:36 -0400 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1N3Bwm-0008Em-K2 for qemu-devel@nongnu.org; Wed, 28 Oct 2009 13:04:32 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1N3Bwd-00085i-St for qemu-devel@nongnu.org; Wed, 28 Oct 2009 13:04:28 -0400 Received: from [199.232.76.173] (port=52301 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1N3Bwc-00084P-H2 for qemu-devel@nongnu.org; Wed, 28 Oct 2009 13:04:22 -0400 Received: from smtp-vip.mem.interq.net ([210.157.1.50]:38473 helo=smtp01.mem.internal-gmo) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1N3Bwa-0004kU-T0 for qemu-devel@nongnu.org; Wed, 28 Oct 2009 13:04:21 -0400 Received: (from root@localhost) by smtp01.mem.internal-gmo (8.13.8/8.12.6) id n9SH4Jqs019267 for qemu-devel@nongnu.org; Thu, 29 Oct 2009 02:04:19 +0900 (JST) Received: (from root@localhost) by smtp01.mem.internal-gmo (8.13.8/8.12.6) id n9SGsI6J018824 for qemu-devel@nongnu.org; Thu, 29 Oct 2009 01:54:18 +0900 (JST) Received: (from root@localhost) by smtp01.mem.internal-gmo (8.13.8/8.12.6) id n9SGs87T018759 for qemu-devel@nongnu.org; Thu, 29 Oct 2009 01:54:08 +0900 (JST) Received: from YOUR-BD18D6DD63.m1.interq.or.jp (ntymns039132.ymns.nt.ftth.ppp.infoweb.ne.jp [121.92.167.132]) by smtp01.mem.internal-gmo with ESMTP id n9SGs5Ze018647 for ; (me101664 for with PLAIN) Thu, 29 Oct 2009 01:54:08 +0900 (JST) Message-Id: <200910281653.AA00181@YOUR-BD18D6DD63.m1.interq.or.jp> Date: Thu, 29 Oct 2009 01:53:14 +0900 To: qemu-devel From: "TAKEDA, toshiya" MIME-Version: 1.0 X-Mailer: AL-Mail32 Version 1.13 X-detected-operating-system: by monty-python.gnu.org: Solaris 10 (beta) Subject: [Qemu-devel] [PATCH v3 18/25] pc98mouse: add NEC PC-9821 family sysport X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org diff --git a/qemu/hw/pc98sys.c b/qemu/hw/pc98sys.c new file mode 100644 index 0000000..d3f510c --- /dev/null +++ b/qemu/hw/pc98sys.c @@ -0,0 +1,425 @@ +/* + * QEMU NEC PC-9821 systemp port + * + * Copyright (c) 2009 TAKEDA, toshiya + * + * 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 "pc.h" +#include "qemu-timer.h" +#include "isa.h" +#include "qdev-addr.h" + +enum { + RTC_STROBE = 0x08, + RTC_CLOCK = 0x10, + RTC_DIN = 0x20, +}; + +enum { + RTC_CMD_SHIFT = 0x01, + RTC_CMD_READ = 0x03, + RTC_CMD_EXMODE = 0x07, +}; + +enum { + RTC_MODE_UPD4993A = 0x20, +}; + +#define TSTMP_FREQ 307200 + +struct sysport_t { + uint8_t rtc_mode; + uint8_t rtc_reg; + uint8_t rtc_cmd; + uint64_t rtc_shift_out; + uint8_t rtc_shift_cmd; + uint8_t rtc_irq_mode; + uint8_t rtc_irq_count; + + uint8_t sys_portc; + int sys_portc_patch; + uint8_t prn_porta; + uint8_t prn_portc; + + QEMUTimer *rtc_timer; + qemu_irq irq; + int64_t tstmp_initial_clock; +}; + +typedef struct sysport_isabus_t { + ISADevice dev; + uint32_t isairq; + struct sysport_t state; +} sysport_isabus_t; + +typedef struct sysport_t sysport_t; + +/* NEC uPD4993A RTC */ + +// 1000/64 = 15.625 +static const int rtc_period[8] = { 15, 16, 16, 15, 16, 16, 15, 16 }; +static int rtc_period_ptr = 0; + +static void rtc_timer_handler(void *opaque) +{ + sysport_t *s = opaque; + + /* rtc_irq_mode = 0 : 1/64 sec + = 1 : 1/32 sec + = 2 : no irq + = 3 : 1/16 sec + */ + if ((s->rtc_irq_mode & 0x03) != 2) { + if (++s->rtc_irq_count > (s->rtc_irq_mode & 0x03)) { + qemu_set_irq(s->irq, 1); + s->rtc_irq_count = 0; + } + } + + /* set next timer */ + qemu_mod_timer(s->rtc_timer, qemu_get_clock(rt_clock) + + rtc_period[(rtc_period_ptr++) & 7]); +} + +static inline uint64_t to_bcd(int a) +{ + return ((a / 10) << 4) | (a % 10); +} + +static void rtc_read_time(void *opaque) +{ + sysport_t *s = opaque; + struct tm tm; + + qemu_get_timedate(&tm, 0); + s->rtc_shift_out = to_bcd(tm.tm_sec); + s->rtc_shift_out |= to_bcd(tm.tm_min) << 8; + s->rtc_shift_out |= to_bcd(tm.tm_hour) << 16; + s->rtc_shift_out |= to_bcd(tm.tm_mday) << 24; + s->rtc_shift_out |= (uint64_t)tm.tm_wday << 32; + s->rtc_shift_out |= (uint64_t)(tm.tm_mon + 1) << 36; + s->rtc_shift_out |= to_bcd(tm.tm_year % 100) << 40; + if (s->rtc_mode & RTC_MODE_UPD4993A) { + s->rtc_shift_out <<= 4; + } +} + +static void rtc_reg_write(void *opaque, uint32_t addr, uint32_t value) +{ + sysport_t *s = opaque; + + if ((s->rtc_reg & RTC_STROBE) && !(value & RTC_STROBE)) { + s->rtc_cmd = s->rtc_reg & 0x07; + if (s->rtc_cmd == RTC_CMD_READ) { + rtc_read_time(s); + } else if (s->rtc_cmd == RTC_CMD_EXMODE) { + s->rtc_cmd = s->rtc_shift_cmd & 0x0f; + if (s->rtc_cmd == RTC_CMD_READ) { + rtc_read_time(s); + } + } + } + if ((s->rtc_reg & RTC_CLOCK) && !(value & RTC_CLOCK)) { + uint8_t din = ((s->rtc_reg & RTC_DIN) != 0); +// if ((s->rtc_reg & 0x07) == RTC_CMD_EXMODE) { + s->rtc_shift_cmd |= din << 4; + s->rtc_shift_cmd >>= 1; +// } else if (s->rtc_cmd == RTC_CMD_SHIFT) { + s->rtc_shift_out >>= 1; +// } + } + s->rtc_reg = value; +} + +static void rtc_mode_write(void *opaque, uint32_t addr, uint32_t value) +{ + sysport_t *s = opaque; + + s->rtc_mode = value; +} + +static uint32_t rtc_mode_read(void *opaque, uint32_t addr) +{ + sysport_t *s = opaque; + + return s->rtc_mode; +} + +static void rtc_irq_mode_write(void *opaque, uint32_t addr, uint32_t value) +{ + sysport_t *s = opaque; + + s->rtc_irq_mode = value; + s->rtc_irq_count = 0; +} + +static uint32_t rtc_irq_mode_read(void *opaque, uint32_t addr) +{ + sysport_t *s = opaque; + + qemu_set_irq(s->irq, 0); + return s->rtc_irq_mode; +} + +/* system port */ + +static uint32_t sys_porta_read(void *opaque, uint32_t addr) +{ + return 0x73; +} + +static uint32_t sys_portb_read(void *opaque, uint32_t addr) +{ + sysport_t *s = opaque; + + return 0xf8 | (s->rtc_shift_out & 1); +} + +static void sys_portc_write(void *opaque, uint32_t addr, uint32_t value) +{ + sysport_t *s = opaque; + + if ((s->sys_portc & 0xa0) != (value & 0xa0)) { + pc98_system_log("shut0=%d shut1=%d\n", + (value & 0x80) != 0, (value & 0x20) != 0); + } + s->sys_portc = value; + pc98_pcspk_write(value); +} + +static uint32_t sys_portc_read(void *opaque, uint32_t addr) +{ + sysport_t *s = opaque; + + if (s->sys_portc_patch) { + /* itf protect mode patch */ + s->sys_portc_patch--; + if (s->sys_portc_patch == 0 || s->sys_portc_patch == 4) { + s->sys_portc |= 0x20; + } + } + return s->sys_portc; +} + +static void sys_ctrl_write(void *opaque, uint32_t addr, uint32_t value) +{ + sysport_t *s = opaque; + + if (!(value & 0x80)) { + /* set/reset portc bit */ + uint32_t portc = s->sys_portc; + uint32_t bit = 1 << ((value >> 1) & 7); + if (value & 1) { + portc |= bit; + } else { + portc &= ~bit; + } + sys_portc_write(s, 0, portc); + } +} + +uint8_t pc98_sys_read_shut(void *opaque) +{ + sysport_t *s = opaque; + + return ((s->sys_portc & 0xa0) == 0xa0); +} + +/* printer port */ + +static void prn_porta_write(void *opaque, uint32_t addr, uint32_t value) +{ + sysport_t *s = opaque; + + s->prn_porta = value; +} + +static uint32_t prn_porta_read(void *opaque, uint32_t addr) +{ + sysport_t *s = opaque; + + return s->prn_porta; +} + +static uint32_t prn_portb_read(void *opaque, uint32_t addr) +{ +#ifdef PC98_SYSCLOCK_5MHZ + return 0x94; +#else + return 0xb4; +#endif +} + +static void prn_portc_write(void *opaque, uint32_t addr, uint32_t value) +{ + sysport_t *s = opaque; + + s->prn_portc = value; +} + +static uint32_t prn_portc_read(void *opaque, uint32_t addr) +{ + sysport_t *s = opaque; + + return s->prn_portc; +} + +static void prn_ctrl_write(void *opaque, uint32_t addr, uint32_t value) +{ + sysport_t *s = opaque; + + if (!(value & 0x80)) { + /* set/reset portc bit */ + uint8_t portc = s->prn_portc; + uint8_t bit = 1 << ((value >> 1) & 7); + if (value & 1) { + portc |= bit; + } else { + portc &= ~bit; + } + prn_portc_write(s, 0, portc); + } +} + +/* time stamper */ + +static uint32_t tstmp_read(void *opaque, uint32_t addr) +{ + sysport_t *s = opaque; + uint64_t d = muldiv64(qemu_get_clock(vm_clock) - s->tstmp_initial_clock, + TSTMP_FREQ, get_ticks_per_sec()); + + switch(addr) { + case 0x5c: + return d & 0xffff; + case 0x5e: + return (d >> 8) & 0xffff; + } + return 0xffff; +} + +/* interface */ + +static void pc98_sys_reset(void *opaque) +{ + sysport_t *s = opaque; + + s->rtc_mode = 0xff; + s->rtc_reg = 0; + s->rtc_cmd = 0; + s->rtc_irq_mode = 2; + + s->sys_portc = 0xff;//b8; + s->sys_portc_patch = 8; + s->prn_porta = 0xff; + s->prn_portc = 0x81; +} + +static const VMStateDescription vmstate_sysport = { + .name = "pc98-sys", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_UINT8(rtc_reg, sysport_t), + VMSTATE_UINT8(rtc_cmd, sysport_t), + VMSTATE_UINT64(rtc_shift_out, sysport_t), + VMSTATE_UINT8(rtc_shift_cmd, sysport_t), + VMSTATE_UINT8(rtc_irq_mode, sysport_t), + VMSTATE_UINT8(rtc_irq_count, sysport_t), + VMSTATE_UINT8(sys_portc, sysport_t), + VMSTATE_UINT8(prn_porta, sysport_t), + VMSTATE_UINT8(prn_portc, sysport_t), + VMSTATE_END_OF_LIST() + } +}; + +static int pc98_sys_initfn(ISADevice *dev) +{ + sysport_isabus_t *isa = DO_UPCAST(sysport_isabus_t, dev, dev); + sysport_t *s = &isa->state; + + register_ioport_write(0x20, 1, 1, rtc_reg_write, s); + register_ioport_write(0x22, 1, 1, rtc_mode_write, s); + register_ioport_read(0x22, 1, 1, rtc_mode_read, s); + register_ioport_write(0x128, 1, 1, rtc_irq_mode_write, s); + register_ioport_read(0x128, 1, 1, rtc_irq_mode_read, s); + + register_ioport_read(0x31, 1, 1, sys_porta_read, s); + register_ioport_read(0x33, 1, 1, sys_portb_read, s); + register_ioport_write(0x35, 1, 1, sys_portc_write, s); + register_ioport_read(0x35, 1, 1, sys_portc_read, s); + register_ioport_write(0x37, 1, 1, sys_ctrl_write, s); + + register_ioport_write(0x40, 1, 1, prn_porta_write, s); + register_ioport_read(0x40, 1, 1, prn_porta_read, s); + register_ioport_read(0x42, 1, 1, prn_portb_read, s); + register_ioport_write(0x44, 1, 1, prn_portc_write, s); + register_ioport_read(0x44, 1, 1, prn_portc_read, s); + register_ioport_write(0x46, 1, 1, prn_ctrl_write, s); + + register_ioport_read(0x5c, 2, 2, tstmp_read, s); + register_ioport_read(0x5e, 2, 2, tstmp_read, s); + + isa_init_irq(dev, &s->irq, isa->isairq); + + s->rtc_timer = qemu_new_timer(rt_clock, rtc_timer_handler, s); + qemu_mod_timer(s->rtc_timer, qemu_get_clock(rt_clock) + + rtc_period[(rtc_period_ptr++) & 7]); + s->tstmp_initial_clock = qemu_get_clock(vm_clock); + + vmstate_register(-1, &vmstate_sysport, s); + pc98_sys_reset(s); + qemu_register_reset(pc98_sys_reset, s); + + return 0; +} + + + +void *pc98_sys_init(void) +{ + ISADevice *dev; + sysport_isabus_t *isa; + + dev = isa_create("pc98-sys"); + qdev_init_nofail(&dev->qdev); + + isa = DO_UPCAST(sysport_isabus_t, dev, dev); + return &isa->state; +} + +static ISADeviceInfo pc98_sys_info = { + .qdev.name = "pc98-sys", + .qdev.size = sizeof(sysport_isabus_t), + .init = pc98_sys_initfn, + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("irq", sysport_isabus_t, isairq, 15), + DEFINE_PROP_END_OF_LIST(), + }, +}; + +static void pc98_sys_register_devices(void) +{ + isa_qdev_register(&pc98_sys_info); +} + +device_init(pc98_sys_register_devices)