]> git.proxmox.com Git - qemu.git/blobdiff - hw/escc.c
user: Restore debug usage message for '-d ?' in user mode emulation
[qemu.git] / hw / escc.c
index 71d6368a536dd4e6d9359966ed587dd0cd57221d..f6fd9192eaf28f3ca53a92f04f7848e7f81788c5 100644 (file)
--- a/hw/escc.c
+++ b/hw/escc.c
@@ -1,5 +1,5 @@
 /*
- * QEMU Sparc SLAVIO serial port emulation
+ * QEMU ESCC (Z8030/Z8530/Z85C30/SCC/ESCC) serial port emulation
  *
  * Copyright (c) 2003-2005 Fabrice Bellard
  *
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  */
+
 #include "hw.h"
-#include "sun4m.h"
+#include "sysbus.h"
+#include "escc.h"
 #include "qemu-char.h"
 #include "console.h"
 
 //#define DEBUG_MOUSE
 
 /*
- * This is the serial port, mouse and keyboard part of chip STP2001
+ * Chipset docs:
+ * "Z80C30/Z85C30/Z80230/Z85230/Z85233 SCC/ESCC User Manual",
+ * http://www.zilog.com/docs/serial/scc_escc_um.pdf
+ *
+ * On Sparc32 this is the serial port, mouse and keyboard 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
  *
  * mouse and keyboard ports don't implement all functions and they are
  * only asynchronous. There is no DMA.
  *
+ * Z85C30 is also used on PowerMacs. There are some small differences
+ * between Sparc version (sunzilog) and PowerMac (pmac):
+ *  Offset between control and data registers
+ *  There is some kind of lockup bug, but we can ignore it
+ *  CTS is inverted
+ *  DMA on pmac using DBDMA chip
+ *  pmac can do IRDA and faster rates, sunzilog can only do 38400
+ *  pmac baud rate generator clock is 3.6864 MHz, sunzilog 4.9152 MHz
  */
 
 /*
  *  2006-Aug-10  Igor Kovalenko :   Renamed KBDQueue to SERIOQueue, implemented
  *                                  serial mouse queue.
  *                                  Implemented serial mouse protocol.
+ *
+ *  2010-May-23  Artyom Tarasenko:  Reworked IUS logic
  */
 
 #ifdef DEBUG_SERIAL
-#define SER_DPRINTF(fmt, args...) \
-do { printf("SER: " fmt , ##args); } while (0)
+#define SER_DPRINTF(fmt, ...)                                   \
+    do { printf("SER: " fmt , ## __VA_ARGS__); } while (0)
 #else
-#define SER_DPRINTF(fmt, args...)
+#define SER_DPRINTF(fmt, ...)
 #endif
 #ifdef DEBUG_KBD
-#define KBD_DPRINTF(fmt, args...) \
-do { printf("KBD: " fmt , ##args); } while (0)
+#define KBD_DPRINTF(fmt, ...)                                   \
+    do { printf("KBD: " fmt , ## __VA_ARGS__); } while (0)
 #else
-#define KBD_DPRINTF(fmt, args...)
+#define KBD_DPRINTF(fmt, ...)
 #endif
 #ifdef DEBUG_MOUSE
-#define MS_DPRINTF(fmt, args...) \
-do { printf("MSC: " fmt , ##args); } while (0)
+#define MS_DPRINTF(fmt, ...)                                    \
+    do { printf("MSC: " fmt , ## __VA_ARGS__); } while (0)
 #else
-#define MS_DPRINTF(fmt, args...)
+#define MS_DPRINTF(fmt, ...)
 #endif
 
 typedef enum {
     chn_a, chn_b,
-} chn_id_t;
+} ChnID;
 
 #define CHN_C(s) ((s)->chn == chn_b? 'b' : 'a')
 
 typedef enum {
     ser, kbd, mouse,
-} chn_type_t;
+} ChnType;
 
 #define SERIO_QUEUE_SIZE 256
 
@@ -94,21 +110,27 @@ typedef struct ChannelState {
     qemu_irq irq;
     uint32_t reg;
     uint32_t rxint, txint, rxint_under_svc, txint_under_svc;
-    chn_id_t chn; // this channel, A (base+4) or B (base+0)
-    chn_type_t type;
+    ChnID chn; // this channel, A (base+4) or B (base+0)
+    ChnType type;
     struct ChannelState *otherchn;
     uint8_t rx, tx, wregs[SERIAL_REGS], rregs[SERIAL_REGS];
     SERIOQueue queue;
     CharDriverState *chr;
     int e0_mode, led_mode, caps_lock_mode, num_lock_mode;
     int disabled;
+    int clock;
+    uint32_t vmstate_dummy;
 } ChannelState;
 
 struct SerialState {
+    SysBusDevice busdev;
     struct ChannelState chn[2];
+    uint32_t it_shift;
+    int mmio_index;
+    uint32_t disabled;
+    uint32_t frequency;
 };
 
-#define SERIAL_SIZE 8
 #define SERIAL_CTRL 0
 #define SERIAL_DATA 1
 
@@ -257,9 +279,9 @@ static uint32_t get_queue(void *opaque)
     return val;
 }
 
-static int slavio_serial_update_irq_chn(ChannelState *s)
+static int escc_update_irq_chn(ChannelState *s)
 {
-    if ((((s->wregs[W_INTR] & INTR_TXINT) && s->txint == 1) ||
+    if ((((s->wregs[W_INTR] & INTR_TXINT) && (s->txint == 1)) ||
          // tx ints enabled, pending
          ((((s->wregs[W_INTR] & INTR_RXMODEMSK) == INTR_RXINT1ST) ||
            ((s->wregs[W_INTR] & INTR_RXMODEMSK) == INTR_RXINTALL)) &&
@@ -271,18 +293,18 @@ static int slavio_serial_update_irq_chn(ChannelState *s)
     return 0;
 }
 
-static void slavio_serial_update_irq(ChannelState *s)
+static void escc_update_irq(ChannelState *s)
 {
     int irq;
 
-    irq = slavio_serial_update_irq_chn(s);
-    irq |= slavio_serial_update_irq_chn(s->otherchn);
+    irq = escc_update_irq_chn(s);
+    irq |= escc_update_irq_chn(s->otherchn);
 
     SER_DPRINTF("IRQ = %d\n", irq);
     qemu_set_irq(s->irq, irq);
 }
 
-static void slavio_serial_reset_chn(ChannelState *s)
+static void escc_reset_chn(ChannelState *s)
 {
     int i;
 
@@ -311,35 +333,34 @@ static void slavio_serial_reset_chn(ChannelState *s)
     clear_queue(s);
 }
 
-static void slavio_serial_reset(void *opaque)
+static void escc_reset(DeviceState *d)
 {
-    SerialState *s = opaque;
-    slavio_serial_reset_chn(&s->chn[0]);
-    slavio_serial_reset_chn(&s->chn[1]);
+    SerialState *s = container_of(d, SerialState, busdev.qdev);
+
+    escc_reset_chn(&s->chn[0]);
+    escc_reset_chn(&s->chn[1]);
 }
 
 static inline void set_rxint(ChannelState *s)
 {
     s->rxint = 1;
-    if (!s->txint_under_svc) {
-        s->rxint_under_svc = 1;
-        if (s->chn == chn_a) {
-            if (s->wregs[W_MINTR] & MINTR_STATUSHI)
-                s->otherchn->rregs[R_IVEC] = IVEC_HIRXINTA;
-            else
-                s->otherchn->rregs[R_IVEC] = IVEC_LORXINTA;
-        } else {
-            if (s->wregs[W_MINTR] & MINTR_STATUSHI)
-                s->rregs[R_IVEC] = IVEC_HIRXINTB;
-            else
-                s->rregs[R_IVEC] = IVEC_LORXINTB;
-        }
-    }
-    if (s->chn == chn_a)
+    /* XXX: missing daisy chainnig: chn_b rx should have a lower priority
+       than chn_a rx/tx/special_condition service*/
+    s->rxint_under_svc = 1;
+    if (s->chn == chn_a) {
         s->rregs[R_INTR] |= INTR_RXINTA;
-    else
+        if (s->wregs[W_MINTR] & MINTR_STATUSHI)
+            s->otherchn->rregs[R_IVEC] = IVEC_HIRXINTA;
+        else
+            s->otherchn->rregs[R_IVEC] = IVEC_LORXINTA;
+    } else {
         s->otherchn->rregs[R_INTR] |= INTR_RXINTB;
-    slavio_serial_update_irq(s);
+        if (s->wregs[W_MINTR] & MINTR_STATUSHI)
+            s->rregs[R_IVEC] = IVEC_HIRXINTB;
+        else
+            s->rregs[R_IVEC] = IVEC_LORXINTB;
+    }
+    escc_update_irq(s);
 }
 
 static inline void set_txint(ChannelState *s)
@@ -348,19 +369,21 @@ static inline void set_txint(ChannelState *s)
     if (!s->rxint_under_svc) {
         s->txint_under_svc = 1;
         if (s->chn == chn_a) {
+            if (s->wregs[W_INTR] & INTR_TXINT) {
+                s->rregs[R_INTR] |= INTR_TXINTA;
+            }
             if (s->wregs[W_MINTR] & MINTR_STATUSHI)
                 s->otherchn->rregs[R_IVEC] = IVEC_HITXINTA;
             else
                 s->otherchn->rregs[R_IVEC] = IVEC_LOTXINTA;
         } else {
             s->rregs[R_IVEC] = IVEC_TXINTB;
+            if (s->wregs[W_INTR] & INTR_TXINT) {
+                s->otherchn->rregs[R_INTR] |= INTR_TXINTB;
+            }
         }
+    escc_update_irq(s);
     }
-    if (s->chn == chn_a)
-        s->rregs[R_INTR] |= INTR_TXINTA;
-    else
-        s->otherchn->rregs[R_INTR] |= INTR_TXINTB;
-    slavio_serial_update_irq(s);
 }
 
 static inline void clr_rxint(ChannelState *s)
@@ -382,7 +405,7 @@ static inline void clr_rxint(ChannelState *s)
     }
     if (s->txint)
         set_txint(s);
-    slavio_serial_update_irq(s);
+    escc_update_irq(s);
 }
 
 static inline void clr_txint(ChannelState *s)
@@ -396,6 +419,7 @@ static inline void clr_txint(ChannelState *s)
             s->otherchn->rregs[R_IVEC] = IVEC_LONOINT;
         s->rregs[R_INTR] &= ~INTR_TXINTA;
     } else {
+        s->otherchn->rregs[R_INTR] &= ~INTR_TXINTB;
         if (s->wregs[W_MINTR] & MINTR_STATUSHI)
             s->rregs[R_IVEC] = IVEC_HINOINT;
         else
@@ -404,10 +428,10 @@ static inline void clr_txint(ChannelState *s)
     }
     if (s->rxint)
         set_rxint(s);
-    slavio_serial_update_irq(s);
+    escc_update_irq(s);
 }
 
-static void slavio_serial_update_parameters(ChannelState *s)
+static void escc_update_parameters(ChannelState *s)
 {
     int speed, parity, data_bits, stop_bits;
     QEMUSerialSetParams ssp;
@@ -442,7 +466,7 @@ static void slavio_serial_update_parameters(ChannelState *s)
         data_bits = 8;
         break;
     }
-    speed = 2457600 / ((s->wregs[W_BRGLO] | (s->wregs[W_BRGHI] << 8)) + 2);
+    speed = s->clock / ((s->wregs[W_BRGLO] | (s->wregs[W_BRGHI] << 8)) + 2);
     switch (s->wregs[W_TXCTRL1] & TXCTRL1_CLKMSK) {
     case TXCTRL1_CLK1X:
         break;
@@ -466,8 +490,7 @@ static void slavio_serial_update_parameters(ChannelState *s)
     qemu_chr_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
 }
 
-static void slavio_serial_mem_writeb(void *opaque, target_phys_addr_t addr,
-                                     uint32_t val)
+static void escc_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
 {
     SerialState *serial = opaque;
     ChannelState *s;
@@ -475,8 +498,8 @@ static void slavio_serial_mem_writeb(void *opaque, target_phys_addr_t addr,
     int newreg, channel;
 
     val &= 0xff;
-    saddr = (addr & 3) >> 1;
-    channel = addr >> 2;
+    saddr = (addr >> serial->it_shift) & 1;
+    channel = (addr >> (serial->it_shift + 1)) & 1;
     s = &serial->chn[channel];
     switch (saddr) {
     case SERIAL_CTRL:
@@ -495,10 +518,15 @@ static void slavio_serial_mem_writeb(void *opaque, target_phys_addr_t addr,
                 clr_txint(s);
                 break;
             case CMD_CLR_IUS:
-                if (s->rxint_under_svc)
-                    clr_rxint(s);
-                else if (s->txint_under_svc)
-                    clr_txint(s);
+                if (s->rxint_under_svc) {
+                    s->rxint_under_svc = 0;
+                    if (s->txint) {
+                        set_txint(s);
+                    }
+                } else if (s->txint_under_svc) {
+                    s->txint_under_svc = 0;
+                }
+                escc_update_irq(s);
                 break;
             default:
                 break;
@@ -513,13 +541,13 @@ static void slavio_serial_mem_writeb(void *opaque, target_phys_addr_t addr,
         case W_TXCTRL1:
         case W_TXCTRL2:
             s->wregs[s->reg] = val;
-            slavio_serial_update_parameters(s);
+            escc_update_parameters(s);
             break;
         case W_BRGLO:
         case W_BRGHI:
             s->wregs[s->reg] = val;
             s->rregs[s->reg] = val;
-            slavio_serial_update_parameters(s);
+            escc_update_parameters(s);
             break;
         case W_MINTR:
             switch (val & MINTR_RST_MASK) {
@@ -527,13 +555,13 @@ static void slavio_serial_mem_writeb(void *opaque, target_phys_addr_t addr,
             default:
                 break;
             case MINTR_RST_B:
-                slavio_serial_reset_chn(&serial->chn[0]);
+                escc_reset_chn(&serial->chn[0]);
                 return;
             case MINTR_RST_A:
-                slavio_serial_reset_chn(&serial->chn[1]);
+                escc_reset_chn(&serial->chn[1]);
                 return;
             case MINTR_RST_ALL:
-                slavio_serial_reset(serial);
+                escc_reset(&serial->busdev.qdev);
                 return;
             }
             break;
@@ -564,7 +592,7 @@ static void slavio_serial_mem_writeb(void *opaque, target_phys_addr_t addr,
     }
 }
 
-static uint32_t slavio_serial_mem_readb(void *opaque, target_phys_addr_t addr)
+static uint32_t escc_mem_readb(void *opaque, target_phys_addr_t addr)
 {
     SerialState *serial = opaque;
     ChannelState *s;
@@ -572,8 +600,8 @@ static uint32_t slavio_serial_mem_readb(void *opaque, target_phys_addr_t addr)
     uint32_t ret;
     int channel;
 
-    saddr = (addr & 3) >> 1;
-    channel = addr >> 2;
+    saddr = (addr >> serial->it_shift) & 1;
+    channel = (addr >> (serial->it_shift + 1)) & 1;
     s = &serial->chn[channel];
     switch (saddr) {
     case SERIAL_CTRL:
@@ -624,7 +652,7 @@ static void serial_receive_byte(ChannelState *s, int ch)
 static void serial_receive_break(ChannelState *s)
 {
     s->rregs[R_STATUS] |= STATUS_BRK;
-    slavio_serial_update_irq(s);
+    escc_update_irq(s);
 }
 
 static void serial_receive1(void *opaque, const uint8_t *buf, int size)
@@ -640,113 +668,76 @@ static void serial_event(void *opaque, int event)
         serial_receive_break(s);
 }
 
-static CPUReadMemoryFunc *slavio_serial_mem_read[3] = {
-    slavio_serial_mem_readb,
+static CPUReadMemoryFunc * const escc_mem_read[3] = {
+    escc_mem_readb,
     NULL,
     NULL,
 };
 
-static CPUWriteMemoryFunc *slavio_serial_mem_write[3] = {
-    slavio_serial_mem_writeb,
+static CPUWriteMemoryFunc * const escc_mem_write[3] = {
+    escc_mem_writeb,
     NULL,
     NULL,
 };
 
-static void slavio_serial_save_chn(QEMUFile *f, ChannelState *s)
-{
-    uint32_t tmp = 0;
-
-    qemu_put_be32s(f, &tmp); /* unused, was IRQ.  */
-    qemu_put_be32s(f, &s->reg);
-    qemu_put_be32s(f, &s->rxint);
-    qemu_put_be32s(f, &s->txint);
-    qemu_put_be32s(f, &s->rxint_under_svc);
-    qemu_put_be32s(f, &s->txint_under_svc);
-    qemu_put_8s(f, &s->rx);
-    qemu_put_8s(f, &s->tx);
-    qemu_put_buffer(f, s->wregs, SERIAL_REGS);
-    qemu_put_buffer(f, s->rregs, SERIAL_REGS);
-}
-
-static void slavio_serial_save(QEMUFile *f, void *opaque)
-{
-    SerialState *s = opaque;
-
-    slavio_serial_save_chn(f, &s->chn[0]);
-    slavio_serial_save_chn(f, &s->chn[1]);
-}
-
-static int slavio_serial_load_chn(QEMUFile *f, ChannelState *s, int version_id)
-{
-    uint32_t tmp;
-
-    if (version_id > 2)
-        return -EINVAL;
-
-    qemu_get_be32s(f, &tmp); /* unused */
-    qemu_get_be32s(f, &s->reg);
-    qemu_get_be32s(f, &s->rxint);
-    qemu_get_be32s(f, &s->txint);
-    if (version_id >= 2) {
-        qemu_get_be32s(f, &s->rxint_under_svc);
-        qemu_get_be32s(f, &s->txint_under_svc);
+static const VMStateDescription vmstate_escc_chn = {
+    .name ="escc_chn",
+    .version_id = 2,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT32(vmstate_dummy, ChannelState),
+        VMSTATE_UINT32(reg, ChannelState),
+        VMSTATE_UINT32(rxint, ChannelState),
+        VMSTATE_UINT32(txint, ChannelState),
+        VMSTATE_UINT32(rxint_under_svc, ChannelState),
+        VMSTATE_UINT32(txint_under_svc, ChannelState),
+        VMSTATE_UINT8(rx, ChannelState),
+        VMSTATE_UINT8(tx, ChannelState),
+        VMSTATE_BUFFER(wregs, ChannelState),
+        VMSTATE_BUFFER(rregs, ChannelState),
+        VMSTATE_END_OF_LIST()
     }
-    qemu_get_8s(f, &s->rx);
-    qemu_get_8s(f, &s->tx);
-    qemu_get_buffer(f, s->wregs, SERIAL_REGS);
-    qemu_get_buffer(f, s->rregs, SERIAL_REGS);
-    return 0;
-}
-
-static int slavio_serial_load(QEMUFile *f, void *opaque, int version_id)
-{
-    SerialState *s = opaque;
-    int ret;
-
-    ret = slavio_serial_load_chn(f, &s->chn[0], version_id);
-    if (ret != 0)
-        return ret;
-    ret = slavio_serial_load_chn(f, &s->chn[1], version_id);
-    return ret;
+};
 
-}
+static const VMStateDescription vmstate_escc = {
+    .name ="escc",
+    .version_id = 2,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField []) {
+        VMSTATE_STRUCT_ARRAY(chn, SerialState, 2, 2, vmstate_escc_chn,
+                             ChannelState),
+        VMSTATE_END_OF_LIST()
+    }
+};
 
-SerialState *slavio_serial_init(target_phys_addr_t base, qemu_irq irq,
-                                CharDriverState *chr1, CharDriverState *chr2)
+int escc_init(target_phys_addr_t base, qemu_irq irqA, qemu_irq irqB,
+              CharDriverState *chrA, CharDriverState *chrB,
+              int clock, int it_shift)
 {
-    int slavio_serial_io_memory, i;
-    SerialState *s;
-
-    s = qemu_mallocz(sizeof(SerialState));
-    if (!s)
-        return NULL;
-
-    slavio_serial_io_memory = cpu_register_io_memory(0, slavio_serial_mem_read,
-                                                     slavio_serial_mem_write,
-                                                     s);
-    cpu_register_physical_memory(base, SERIAL_SIZE, slavio_serial_io_memory);
-
-    s->chn[0].chr = chr1;
-    s->chn[1].chr = chr2;
-    s->chn[0].disabled = 0;
-    s->chn[1].disabled = 0;
-
-    for (i = 0; i < 2; i++) {
-        s->chn[i].irq = irq;
-        s->chn[i].chn = 1 - i;
-        s->chn[i].type = ser;
-        if (s->chn[i].chr) {
-            qemu_chr_add_handlers(s->chn[i].chr, serial_can_receive,
-                                  serial_receive1, serial_event, &s->chn[i]);
-        }
+    DeviceState *dev;
+    SysBusDevice *s;
+    SerialState *d;
+
+    dev = qdev_create(NULL, "escc");
+    qdev_prop_set_uint32(dev, "disabled", 0);
+    qdev_prop_set_uint32(dev, "frequency", clock);
+    qdev_prop_set_uint32(dev, "it_shift", it_shift);
+    qdev_prop_set_chr(dev, "chrB", chrB);
+    qdev_prop_set_chr(dev, "chrA", chrA);
+    qdev_prop_set_uint32(dev, "chnBtype", ser);
+    qdev_prop_set_uint32(dev, "chnAtype", ser);
+    qdev_init_nofail(dev);
+    s = sysbus_from_qdev(dev);
+    sysbus_connect_irq(s, 0, irqB);
+    sysbus_connect_irq(s, 1, irqA);
+    if (base) {
+        sysbus_mmio_map(s, 0, base);
     }
-    s->chn[0].otherchn = &s->chn[1];
-    s->chn[1].otherchn = &s->chn[0];
-    register_savevm("slavio_serial", base, 2, slavio_serial_save,
-                    slavio_serial_load, s);
-    qemu_register_reset(slavio_serial_reset, s);
-    slavio_serial_reset(s);
-    return s;
+
+    d = FROM_SYSBUS(SerialState, s);
+    return d->mmio_index;
 }
 
 static const uint8_t keycodes[128] = {
@@ -865,18 +856,18 @@ static void sunmouse_event(void *opaque,
     ch = dx;
 
     if (ch > 127)
-        ch=127;
+        ch = 127;
     else if (ch < -127)
-        ch=-127;
+        ch = -127;
 
     put_queue(s, ch & 0xff);
 
     ch = -dy;
 
     if (ch > 127)
-        ch=127;
+        ch = 127;
     else if (ch < -127)
-        ch=-127;
+        ch = -127;
 
     put_queue(s, ch & 0xff);
 
@@ -887,36 +878,84 @@ static void sunmouse_event(void *opaque,
 }
 
 void slavio_serial_ms_kbd_init(target_phys_addr_t base, qemu_irq irq,
-                               int disabled)
+                               int disabled, int clock, int it_shift)
 {
-    int slavio_serial_io_memory, i;
-    SerialState *s;
+    DeviceState *dev;
+    SysBusDevice *s;
+
+    dev = qdev_create(NULL, "escc");
+    qdev_prop_set_uint32(dev, "disabled", disabled);
+    qdev_prop_set_uint32(dev, "frequency", clock);
+    qdev_prop_set_uint32(dev, "it_shift", it_shift);
+    qdev_prop_set_chr(dev, "chrB", NULL);
+    qdev_prop_set_chr(dev, "chrA", NULL);
+    qdev_prop_set_uint32(dev, "chnBtype", mouse);
+    qdev_prop_set_uint32(dev, "chnAtype", kbd);
+    qdev_init_nofail(dev);
+    s = sysbus_from_qdev(dev);
+    sysbus_connect_irq(s, 0, irq);
+    sysbus_connect_irq(s, 1, irq);
+    sysbus_mmio_map(s, 0, base);
+}
 
-    s = qemu_mallocz(sizeof(SerialState));
-    if (!s)
-        return;
+static int escc_init1(SysBusDevice *dev)
+{
+    SerialState *s = FROM_SYSBUS(SerialState, dev);
+    int io;
+    unsigned int i;
+
+    s->chn[0].disabled = s->disabled;
+    s->chn[1].disabled = s->disabled;
     for (i = 0; i < 2; i++) {
-        s->chn[i].irq = irq;
+        sysbus_init_irq(dev, &s->chn[i].irq);
         s->chn[i].chn = 1 - i;
-        s->chn[i].chr = NULL;
+        s->chn[i].clock = s->frequency / 2;
+        if (s->chn[i].chr) {
+            qemu_chr_add_handlers(s->chn[i].chr, serial_can_receive,
+                                  serial_receive1, serial_event, &s->chn[i]);
+        }
     }
     s->chn[0].otherchn = &s->chn[1];
     s->chn[1].otherchn = &s->chn[0];
-    s->chn[0].type = mouse;
-    s->chn[1].type = kbd;
-    s->chn[0].disabled = disabled;
-    s->chn[1].disabled = disabled;
-
-    slavio_serial_io_memory = cpu_register_io_memory(0, slavio_serial_mem_read,
-                                                     slavio_serial_mem_write,
-                                                     s);
-    cpu_register_physical_memory(base, SERIAL_SIZE, slavio_serial_io_memory);
-
-    qemu_add_mouse_event_handler(sunmouse_event, &s->chn[0], 0,
-                                 "QEMU Sun Mouse");
-    qemu_add_kbd_event_handler(sunkbd_event, &s->chn[1]);
-    register_savevm("slavio_serial_mouse", base, 2, slavio_serial_save,
-                    slavio_serial_load, s);
-    qemu_register_reset(slavio_serial_reset, s);
-    slavio_serial_reset(s);
+
+    io = cpu_register_io_memory(escc_mem_read, escc_mem_write, s,
+                                DEVICE_NATIVE_ENDIAN);
+    sysbus_init_mmio(dev, ESCC_SIZE << s->it_shift, io);
+    s->mmio_index = io;
+
+    if (s->chn[0].type == mouse) {
+        qemu_add_mouse_event_handler(sunmouse_event, &s->chn[0], 0,
+                                     "QEMU Sun Mouse");
+    }
+    if (s->chn[1].type == kbd) {
+        qemu_add_kbd_event_handler(sunkbd_event, &s->chn[1]);
+    }
+
+    return 0;
 }
+
+static SysBusDeviceInfo escc_info = {
+    .init = escc_init1,
+    .qdev.name  = "escc",
+    .qdev.size  = sizeof(SerialState),
+    .qdev.vmsd  = &vmstate_escc,
+    .qdev.reset = escc_reset,
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_UINT32("frequency", SerialState, frequency,   0),
+        DEFINE_PROP_UINT32("it_shift",  SerialState, it_shift,    0),
+        DEFINE_PROP_UINT32("disabled",  SerialState, disabled,    0),
+        DEFINE_PROP_UINT32("disabled",  SerialState, disabled,    0),
+        DEFINE_PROP_UINT32("chnBtype",  SerialState, chn[0].type, 0),
+        DEFINE_PROP_UINT32("chnAtype",  SerialState, chn[1].type, 0),
+        DEFINE_PROP_CHR("chrB", SerialState, chn[0].chr),
+        DEFINE_PROP_CHR("chrA", SerialState, chn[1].chr),
+        DEFINE_PROP_END_OF_LIST(),
+    }
+};
+
+static void escc_register_devices(void)
+{
+    sysbus_register_withprop(&escc_info);
+}
+
+device_init(escc_register_devices)