From 9944d320016914912133b348b6fbbb18c7417035 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 5 Feb 2013 13:35:34 +0100 Subject: hw: move char devices to hw/char/, configure via default-configs/ Signed-off-by: Paolo Bonzini --- default-configs/arm-softmmu.mak | 1 + default-configs/i386-softmmu.mak | 1 + default-configs/lm32-softmmu.mak | 1 + default-configs/s390x-softmmu.mak | 1 + default-configs/sparc-softmmu.mak | 1 + default-configs/x86_64-softmmu.mak | 1 + hw/arm/Makefile.objs | 6 +- hw/char/Makefile.objs | 15 + hw/char/debugcon.c | 134 ++++++++ hw/char/etraxfs_ser.c | 246 ++++++++++++++ hw/char/exynos4210_uart.c | 676 +++++++++++++++++++++++++++++++++++++ hw/char/grlib_apbuart.c | 293 ++++++++++++++++ hw/char/imx_serial.c | 467 +++++++++++++++++++++++++ hw/char/lm32_juart.c | 159 +++++++++ hw/char/lm32_uart.c | 297 ++++++++++++++++ hw/char/mcf_uart.c | 307 +++++++++++++++++ hw/char/milkymist-uart.c | 244 +++++++++++++ hw/char/omap_uart.c | 187 ++++++++++ hw/char/sclpconsole.c | 305 +++++++++++++++++ hw/char/sh_serial.c | 410 ++++++++++++++++++++++ hw/char/spapr_vty.c | 221 ++++++++++++ hw/cris/Makefile.objs | 1 - hw/debugcon.c | 134 -------- hw/etraxfs_ser.c | 248 -------------- hw/exynos4210_uart.c | 676 ------------------------------------- hw/grlib_apbuart.c | 293 ---------------- hw/i386/Makefile.objs | 2 +- hw/imx_serial.c | 467 ------------------------- hw/lm32/Makefile.objs | 3 - hw/lm32_juart.c | 159 --------- hw/lm32_uart.c | 297 ---------------- hw/m68k/Makefile.objs | 4 - hw/mcf_uart.c | 307 ----------------- hw/milkymist-uart.c | 244 ------------- hw/omap_uart.c | 187 ---------- hw/ppc/Makefile.objs | 1 - hw/s390x/Makefile.objs | 2 +- hw/s390x/sclpconsole.c | 305 ----------------- hw/sh4/Makefile.objs | 2 +- hw/sh_serial.c | 410 ---------------------- hw/spapr_vty.c | 221 ------------ hw/sparc/Makefile.objs | 2 +- 42 files changed, 3974 insertions(+), 3964 deletions(-) create mode 100644 hw/char/debugcon.c create mode 100644 hw/char/etraxfs_ser.c create mode 100644 hw/char/exynos4210_uart.c create mode 100644 hw/char/grlib_apbuart.c create mode 100644 hw/char/imx_serial.c create mode 100644 hw/char/lm32_juart.c create mode 100644 hw/char/lm32_uart.c create mode 100644 hw/char/mcf_uart.c create mode 100644 hw/char/milkymist-uart.c create mode 100644 hw/char/omap_uart.c create mode 100644 hw/char/sclpconsole.c create mode 100644 hw/char/sh_serial.c create mode 100644 hw/char/spapr_vty.c delete mode 100644 hw/debugcon.c delete mode 100644 hw/etraxfs_ser.c delete mode 100644 hw/exynos4210_uart.c delete mode 100644 hw/grlib_apbuart.c delete mode 100644 hw/imx_serial.c delete mode 100644 hw/lm32_juart.c delete mode 100644 hw/lm32_uart.c delete mode 100644 hw/mcf_uart.c delete mode 100644 hw/milkymist-uart.c delete mode 100644 hw/omap_uart.c delete mode 100644 hw/s390x/sclpconsole.c delete mode 100644 hw/sh_serial.c delete mode 100644 hw/spapr_vty.c diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index 8eb04e2..8da5ec8 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -57,6 +57,7 @@ CONFIG_MARVELL_88W8618=y CONFIG_OMAP=y CONFIG_BLIZZARD=y CONFIG_ONENAND=y +CONFIG_IMX=y CONFIG_ZAURUS=y CONFIG_VERSATILE_PCI=y diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak index 89aaff5..9d852ff 100644 --- a/default-configs/i386-softmmu.mak +++ b/default-configs/i386-softmmu.mak @@ -34,3 +34,4 @@ CONFIG_MC146818RTC=y CONFIG_WDT_IB700=y CONFIG_PC_SYSFW=y CONFIG_XEN_I386=$(CONFIG_XEN) +CONFIG_ISA_DEBUG=y diff --git a/default-configs/lm32-softmmu.mak b/default-configs/lm32-softmmu.mak index 6b2ee43..ef0f4ba 100644 --- a/default-configs/lm32-softmmu.mak +++ b/default-configs/lm32-softmmu.mak @@ -1,5 +1,6 @@ # Default configuration for lm32-softmmu +CONFIG_LM32=y CONFIG_MILKYMIST=y CONFIG_FRAMEBUFFER=y CONFIG_PTIMER=y diff --git a/default-configs/s390x-softmmu.mak b/default-configs/s390x-softmmu.mak index 3005729..81fbc68 100644 --- a/default-configs/s390x-softmmu.mak +++ b/default-configs/s390x-softmmu.mak @@ -1 +1,2 @@ CONFIG_VIRTIO=y +CONFIG_SCLPCONSOLE=y diff --git a/default-configs/sparc-softmmu.mak b/default-configs/sparc-softmmu.mak index 25bcbe3..da5b02d 100644 --- a/default-configs/sparc-softmmu.mak +++ b/default-configs/sparc-softmmu.mak @@ -11,3 +11,4 @@ CONFIG_PCNET_COMMON=y CONFIG_LANCE=y CONFIG_TCX=y CONFIG_CS4231=y +CONFIG_GRLIB=y diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak index c34f8f8..760d51e 100644 --- a/default-configs/x86_64-softmmu.mak +++ b/default-configs/x86_64-softmmu.mak @@ -34,3 +34,4 @@ CONFIG_MC146818RTC=y CONFIG_WDT_IB700=y CONFIG_PC_SYSFW=y CONFIG_XEN_I386=$(CONFIG_XEN) +CONFIG_ISA_DEBUG=y diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 6f764e6..26e107f 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -3,7 +3,7 @@ obj-y += arm_gic.o arm_gic_common.o obj-y += a9scu.o obj-y += realview_gic.o arm_sysctl.o arm11mpcore.o a9mpcore.o obj-y += exynos4210_gic.o exynos4210_combiner.o -obj-y += exynos4210_uart.o exynos4210_pwm.o +obj-y += exynos4210_pwm.o obj-y += exynos4210_pmu.o exynos4210_mct.o obj-y += exynos4210_rtc.o obj-y += arm_mptimer.o a15mpcore.o @@ -12,14 +12,14 @@ obj-y += pxa2xx_timer.o pxa2xx_dma.o obj-y += pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o obj-y += zaurus.o obj-y += omap_dma.o omap_clk.o omap_mmc.o \ - omap_gpio.o omap_intc.o omap_uart.o + omap_gpio.o omap_intc.o obj-y += soc_dma.o omap_gptimer.o omap_synctimer.o \ omap_gpmc.o omap_sdrc.o omap_tap.o omap_l4.o obj-y += tsc210x.o obj-y += cbus.o tusb6010.o obj-y += mst_fpga.o obj-y += strongarm.o -obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o +obj-y += imx_ccm.o imx_timer.o imx_avic.o obj-$(CONFIG_KVM) += kvm/arm_gic.o obj-y := $(addprefix ../,$(obj-y)) diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs index ddfd3ec..f8f3dbc 100644 --- a/hw/char/Makefile.objs +++ b/hw/char/Makefile.objs @@ -9,4 +9,19 @@ common-obj-$(CONFIG_XILINX) += xilinx_uartlite.o common-obj-$(CONFIG_XEN_BACKEND) += xen_console.o common-obj-$(CONFIG_CADENCE) += cadence_uart.o +obj-$(CONFIG_EXYNOS4) += exynos4210_uart.o +obj-$(CONFIG_COLDFIRE) += mcf_uart.o +obj-$(CONFIG_OMAP) += omap_uart.o +obj-$(CONFIG_SH4) += sh_serial.o +obj-$(CONFIG_PSERIES) += spapr_vty.o + +common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o +common-obj-$(CONFIG_ISA_DEBUG) += debugcon.o +common-obj-$(CONFIG_GRLIB) += grlib_apbuart.o +common-obj-$(CONFIG_IMX) += imx_serial.o +common-obj-$(CONFIG_LM32) += lm32_juart.o +common-obj-$(CONFIG_LM32) += lm32_uart.o +common-obj-$(CONFIG_MILKYMIST) += milkymist-uart.o +common-obj-$(CONFIG_SCLPCONSOLE) += sclpconsole.o + obj-$(CONFIG_VIRTIO) += virtio-serial-bus.o diff --git a/hw/char/debugcon.c b/hw/char/debugcon.c new file mode 100644 index 0000000..0588eeb --- /dev/null +++ b/hw/char/debugcon.c @@ -0,0 +1,134 @@ +/* + * QEMU Bochs-style debug console ("port E9") emulation + * + * Copyright (c) 2003-2004 Fabrice Bellard + * Copyright (c) 2008 Citrix Systems, Inc. + * Copyright (c) Intel Corporation; author: H. Peter Anvin + * + * 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/hw.h" +#include "char/char.h" +#include "hw/isa/isa.h" +#include "hw/i386/pc.h" + +#define TYPE_ISA_DEBUGCON_DEVICE "isa-debugcon" +#define ISA_DEBUGCON_DEVICE(obj) \ + OBJECT_CHECK(ISADebugconState, (obj), TYPE_ISA_DEBUGCON_DEVICE) + +//#define DEBUG_DEBUGCON + +typedef struct DebugconState { + MemoryRegion io; + CharDriverState *chr; + uint32_t readback; +} DebugconState; + +typedef struct ISADebugconState { + ISADevice parent_obj; + + uint32_t iobase; + DebugconState state; +} ISADebugconState; + +static void debugcon_ioport_write(void *opaque, hwaddr addr, uint64_t val, + unsigned width) +{ + DebugconState *s = opaque; + unsigned char ch = val; + +#ifdef DEBUG_DEBUGCON + printf("debugcon: write addr=0x%04x val=0x%02x\n", addr, val); +#endif + + qemu_chr_fe_write(s->chr, &ch, 1); +} + + +static uint64_t debugcon_ioport_read(void *opaque, hwaddr addr, unsigned width) +{ + DebugconState *s = opaque; + +#ifdef DEBUG_DEBUGCON + printf("debugcon: read addr=0x%04x\n", addr); +#endif + + return s->readback; +} + +static const MemoryRegionOps debugcon_ops = { + .read = debugcon_ioport_read, + .write = debugcon_ioport_write, + .valid.min_access_size = 1, + .valid.max_access_size = 1, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void debugcon_init_core(DebugconState *s) +{ + if (!s->chr) { + fprintf(stderr, "Can't create debugcon device, empty char device\n"); + exit(1); + } + + qemu_chr_add_handlers(s->chr, NULL, NULL, NULL, s); +} + +static int debugcon_isa_initfn(ISADevice *dev) +{ + ISADebugconState *isa = ISA_DEBUGCON_DEVICE(dev); + DebugconState *s = &isa->state; + + debugcon_init_core(s); + memory_region_init_io(&s->io, &debugcon_ops, s, + TYPE_ISA_DEBUGCON_DEVICE, 1); + memory_region_add_subregion(isa_address_space_io(dev), + isa->iobase, &s->io); + return 0; +} + +static Property debugcon_isa_properties[] = { + DEFINE_PROP_HEX32("iobase", ISADebugconState, iobase, 0xe9), + DEFINE_PROP_CHR("chardev", ISADebugconState, state.chr), + DEFINE_PROP_HEX32("readback", ISADebugconState, state.readback, 0xe9), + DEFINE_PROP_END_OF_LIST(), +}; + +static void debugcon_isa_class_initfn(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ISADeviceClass *ic = ISA_DEVICE_CLASS(klass); + ic->init = debugcon_isa_initfn; + dc->props = debugcon_isa_properties; +} + +static const TypeInfo debugcon_isa_info = { + .name = TYPE_ISA_DEBUGCON_DEVICE, + .parent = TYPE_ISA_DEVICE, + .instance_size = sizeof(ISADebugconState), + .class_init = debugcon_isa_class_initfn, +}; + +static void debugcon_register_types(void) +{ + type_register_static(&debugcon_isa_info); +} + +type_init(debugcon_register_types) diff --git a/hw/char/etraxfs_ser.c b/hw/char/etraxfs_ser.c new file mode 100644 index 0000000..b7499d7 --- /dev/null +++ b/hw/char/etraxfs_ser.c @@ -0,0 +1,246 @@ +/* + * QEMU ETRAX System Emulator + * + * Copyright (c) 2007 Edgar E. Iglesias, Axis Communications AB. + * + * 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/sysbus.h" +#include "char/char.h" +#include "qemu/log.h" + +#define D(x) + +#define RW_TR_CTRL (0x00 / 4) +#define RW_TR_DMA_EN (0x04 / 4) +#define RW_REC_CTRL (0x08 / 4) +#define RW_DOUT (0x1c / 4) +#define RS_STAT_DIN (0x20 / 4) +#define R_STAT_DIN (0x24 / 4) +#define RW_INTR_MASK (0x2c / 4) +#define RW_ACK_INTR (0x30 / 4) +#define R_INTR (0x34 / 4) +#define R_MASKED_INTR (0x38 / 4) +#define R_MAX (0x3c / 4) + +#define STAT_DAV 16 +#define STAT_TR_IDLE 22 +#define STAT_TR_RDY 24 + +struct etrax_serial +{ + SysBusDevice busdev; + MemoryRegion mmio; + CharDriverState *chr; + qemu_irq irq; + + int pending_tx; + + uint8_t rx_fifo[16]; + unsigned int rx_fifo_pos; + unsigned int rx_fifo_len; + + /* Control registers. */ + uint32_t regs[R_MAX]; +}; + +static void ser_update_irq(struct etrax_serial *s) +{ + + if (s->rx_fifo_len) { + s->regs[R_INTR] |= 8; + } else { + s->regs[R_INTR] &= ~8; + } + + s->regs[R_MASKED_INTR] = s->regs[R_INTR] & s->regs[RW_INTR_MASK]; + qemu_set_irq(s->irq, !!s->regs[R_MASKED_INTR]); +} + +static uint64_t +ser_read(void *opaque, hwaddr addr, unsigned int size) +{ + struct etrax_serial *s = opaque; + uint32_t r = 0; + + addr >>= 2; + switch (addr) + { + case R_STAT_DIN: + r = s->rx_fifo[(s->rx_fifo_pos - s->rx_fifo_len) & 15]; + if (s->rx_fifo_len) { + r |= 1 << STAT_DAV; + } + r |= 1 << STAT_TR_RDY; + r |= 1 << STAT_TR_IDLE; + break; + case RS_STAT_DIN: + r = s->rx_fifo[(s->rx_fifo_pos - s->rx_fifo_len) & 15]; + if (s->rx_fifo_len) { + r |= 1 << STAT_DAV; + s->rx_fifo_len--; + } + r |= 1 << STAT_TR_RDY; + r |= 1 << STAT_TR_IDLE; + break; + default: + r = s->regs[addr]; + D(qemu_log("%s " TARGET_FMT_plx "=%x\n", __func__, addr, r)); + break; + } + return r; +} + +static void +ser_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + struct etrax_serial *s = opaque; + uint32_t value = val64; + unsigned char ch = val64; + + D(qemu_log("%s " TARGET_FMT_plx "=%x\n", __func__, addr, value)); + addr >>= 2; + switch (addr) + { + case RW_DOUT: + qemu_chr_fe_write(s->chr, &ch, 1); + s->regs[R_INTR] |= 3; + s->pending_tx = 1; + s->regs[addr] = value; + break; + case RW_ACK_INTR: + if (s->pending_tx) { + value &= ~1; + s->pending_tx = 0; + D(qemu_log("fixedup value=%x r_intr=%x\n", + value, s->regs[R_INTR])); + } + s->regs[addr] = value; + s->regs[R_INTR] &= ~value; + D(printf("r_intr=%x\n", s->regs[R_INTR])); + break; + default: + s->regs[addr] = value; + break; + } + ser_update_irq(s); +} + +static const MemoryRegionOps ser_ops = { + .read = ser_read, + .write = ser_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4 + } +}; + +static void serial_receive(void *opaque, const uint8_t *buf, int size) +{ + struct etrax_serial *s = opaque; + int i; + + /* Got a byte. */ + if (s->rx_fifo_len >= 16) { + qemu_log("WARNING: UART dropped char.\n"); + return; + } + + for (i = 0; i < size; i++) { + s->rx_fifo[s->rx_fifo_pos] = buf[i]; + s->rx_fifo_pos++; + s->rx_fifo_pos &= 15; + s->rx_fifo_len++; + } + + ser_update_irq(s); +} + +static int serial_can_receive(void *opaque) +{ + struct etrax_serial *s = opaque; + int r; + + /* Is the receiver enabled? */ + if (!(s->regs[RW_REC_CTRL] & (1 << 3))) { + return 0; + } + + r = sizeof(s->rx_fifo) - s->rx_fifo_len; + return r; +} + +static void serial_event(void *opaque, int event) +{ + +} + +static void etraxfs_ser_reset(DeviceState *d) +{ + struct etrax_serial *s = container_of(d, typeof(*s), busdev.qdev); + + /* transmitter begins ready and idle. */ + s->regs[RS_STAT_DIN] |= (1 << STAT_TR_RDY); + s->regs[RS_STAT_DIN] |= (1 << STAT_TR_IDLE); + + s->regs[RW_REC_CTRL] = 0x10000; + +} + +static int etraxfs_ser_init(SysBusDevice *dev) +{ + struct etrax_serial *s = FROM_SYSBUS(typeof (*s), dev); + + sysbus_init_irq(dev, &s->irq); + memory_region_init_io(&s->mmio, &ser_ops, s, "etraxfs-serial", R_MAX * 4); + sysbus_init_mmio(dev, &s->mmio); + + s->chr = qemu_char_get_next_serial(); + if (s->chr) + qemu_chr_add_handlers(s->chr, + serial_can_receive, serial_receive, + serial_event, s); + return 0; +} + +static void etraxfs_ser_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = etraxfs_ser_init; + dc->reset = etraxfs_ser_reset; +} + +static const TypeInfo etraxfs_ser_info = { + .name = "etraxfs,serial", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(struct etrax_serial), + .class_init = etraxfs_ser_class_init, +}; + +static void etraxfs_serial_register_types(void) +{ + type_register_static(&etraxfs_ser_info); +} + +type_init(etraxfs_serial_register_types) diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c new file mode 100644 index 0000000..8b4e72c --- /dev/null +++ b/hw/char/exynos4210_uart.c @@ -0,0 +1,676 @@ +/* + * Exynos4210 UART Emulation + * + * Copyright (C) 2011 Samsung Electronics Co Ltd. + * Maksim Kozlov, + * + * 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 . + * + */ + +#include "hw/sysbus.h" +#include "sysemu/sysemu.h" +#include "char/char.h" + +#include "hw/arm/exynos4210.h" + +#undef DEBUG_UART +#undef DEBUG_UART_EXTEND +#undef DEBUG_IRQ +#undef DEBUG_Rx_DATA +#undef DEBUG_Tx_DATA + +#define DEBUG_UART 0 +#define DEBUG_UART_EXTEND 0 +#define DEBUG_IRQ 0 +#define DEBUG_Rx_DATA 0 +#define DEBUG_Tx_DATA 0 + +#if DEBUG_UART +#define PRINT_DEBUG(fmt, args...) \ + do { \ + fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \ + } while (0) + +#if DEBUG_UART_EXTEND +#define PRINT_DEBUG_EXTEND(fmt, args...) \ + do { \ + fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \ + } while (0) +#else +#define PRINT_DEBUG_EXTEND(fmt, args...) \ + do {} while (0) +#endif /* EXTEND */ + +#else +#define PRINT_DEBUG(fmt, args...) \ + do {} while (0) +#define PRINT_DEBUG_EXTEND(fmt, args...) \ + do {} while (0) +#endif + +#define PRINT_ERROR(fmt, args...) \ + do { \ + fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \ + } while (0) + +/* + * Offsets for UART registers relative to SFR base address + * for UARTn + * + */ +#define ULCON 0x0000 /* Line Control */ +#define UCON 0x0004 /* Control */ +#define UFCON 0x0008 /* FIFO Control */ +#define UMCON 0x000C /* Modem Control */ +#define UTRSTAT 0x0010 /* Tx/Rx Status */ +#define UERSTAT 0x0014 /* UART Error Status */ +#define UFSTAT 0x0018 /* FIFO Status */ +#define UMSTAT 0x001C /* Modem Status */ +#define UTXH 0x0020 /* Transmit Buffer */ +#define URXH 0x0024 /* Receive Buffer */ +#define UBRDIV 0x0028 /* Baud Rate Divisor */ +#define UFRACVAL 0x002C /* Divisor Fractional Value */ +#define UINTP 0x0030 /* Interrupt Pending */ +#define UINTSP 0x0034 /* Interrupt Source Pending */ +#define UINTM 0x0038 /* Interrupt Mask */ + +/* + * for indexing register in the uint32_t array + * + * 'reg' - register offset (see offsets definitions above) + * + */ +#define I_(reg) (reg / sizeof(uint32_t)) + +typedef struct Exynos4210UartReg { + const char *name; /* the only reason is the debug output */ + hwaddr offset; + uint32_t reset_value; +} Exynos4210UartReg; + +static Exynos4210UartReg exynos4210_uart_regs[] = { + {"ULCON", ULCON, 0x00000000}, + {"UCON", UCON, 0x00003000}, + {"UFCON", UFCON, 0x00000000}, + {"UMCON", UMCON, 0x00000000}, + {"UTRSTAT", UTRSTAT, 0x00000006}, /* RO */ + {"UERSTAT", UERSTAT, 0x00000000}, /* RO */ + {"UFSTAT", UFSTAT, 0x00000000}, /* RO */ + {"UMSTAT", UMSTAT, 0x00000000}, /* RO */ + {"UTXH", UTXH, 0x5c5c5c5c}, /* WO, undefined reset value*/ + {"URXH", URXH, 0x00000000}, /* RO */ + {"UBRDIV", UBRDIV, 0x00000000}, + {"UFRACVAL", UFRACVAL, 0x00000000}, + {"UINTP", UINTP, 0x00000000}, + {"UINTSP", UINTSP, 0x00000000}, + {"UINTM", UINTM, 0x00000000}, +}; + +#define EXYNOS4210_UART_REGS_MEM_SIZE 0x3C + +/* UART FIFO Control */ +#define UFCON_FIFO_ENABLE 0x1 +#define UFCON_Rx_FIFO_RESET 0x2 +#define UFCON_Tx_FIFO_RESET 0x4 +#define UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT 8 +#define UFCON_Tx_FIFO_TRIGGER_LEVEL (7 << UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT) +#define UFCON_Rx_FIFO_TRIGGER_LEVEL_SHIFT 4 +#define UFCON_Rx_FIFO_TRIGGER_LEVEL (7 << UFCON_Rx_FIFO_TRIGGER_LEVEL_SHIFT) + +/* Uart FIFO Status */ +#define UFSTAT_Rx_FIFO_COUNT 0xff +#define UFSTAT_Rx_FIFO_FULL 0x100 +#define UFSTAT_Rx_FIFO_ERROR 0x200 +#define UFSTAT_Tx_FIFO_COUNT_SHIFT 16 +#define UFSTAT_Tx_FIFO_COUNT (0xff << UFSTAT_Tx_FIFO_COUNT_SHIFT) +#define UFSTAT_Tx_FIFO_FULL_SHIFT 24 +#define UFSTAT_Tx_FIFO_FULL (1 << UFSTAT_Tx_FIFO_FULL_SHIFT) + +/* UART Interrupt Source Pending */ +#define UINTSP_RXD 0x1 /* Receive interrupt */ +#define UINTSP_ERROR 0x2 /* Error interrupt */ +#define UINTSP_TXD 0x4 /* Transmit interrupt */ +#define UINTSP_MODEM 0x8 /* Modem interrupt */ + +/* UART Line Control */ +#define ULCON_IR_MODE_SHIFT 6 +#define ULCON_PARITY_SHIFT 3 +#define ULCON_STOP_BIT_SHIFT 1 + +/* UART Tx/Rx Status */ +#define UTRSTAT_TRANSMITTER_EMPTY 0x4 +#define UTRSTAT_Tx_BUFFER_EMPTY 0x2 +#define UTRSTAT_Rx_BUFFER_DATA_READY 0x1 + +/* UART Error Status */ +#define UERSTAT_OVERRUN 0x1 +#define UERSTAT_PARITY 0x2 +#define UERSTAT_FRAME 0x4 +#define UERSTAT_BREAK 0x8 + +typedef struct { + uint8_t *data; + uint32_t sp, rp; /* store and retrieve pointers */ + uint32_t size; +} Exynos4210UartFIFO; + +typedef struct { + SysBusDevice busdev; + MemoryRegion iomem; + + uint32_t reg[EXYNOS4210_UART_REGS_MEM_SIZE / sizeof(uint32_t)]; + Exynos4210UartFIFO rx; + Exynos4210UartFIFO tx; + + CharDriverState *chr; + qemu_irq irq; + + uint32_t channel; + +} Exynos4210UartState; + + +#if DEBUG_UART +/* Used only for debugging inside PRINT_DEBUG_... macros */ +static const char *exynos4210_uart_regname(hwaddr offset) +{ + + int regs_number = sizeof(exynos4210_uart_regs) / sizeof(Exynos4210UartReg); + int i; + + for (i = 0; i < regs_number; i++) { + if (offset == exynos4210_uart_regs[i].offset) { + return exynos4210_uart_regs[i].name; + } + } + + return NULL; +} +#endif + + +static void fifo_store(Exynos4210UartFIFO *q, uint8_t ch) +{ + q->data[q->sp] = ch; + q->sp = (q->sp + 1) % q->size; +} + +static uint8_t fifo_retrieve(Exynos4210UartFIFO *q) +{ + uint8_t ret = q->data[q->rp]; + q->rp = (q->rp + 1) % q->size; + return ret; +} + +static int fifo_elements_number(Exynos4210UartFIFO *q) +{ + if (q->sp < q->rp) { + return q->size - q->rp + q->sp; + } + + return q->sp - q->rp; +} + +static int fifo_empty_elements_number(Exynos4210UartFIFO *q) +{ + return q->size - fifo_elements_number(q); +} + +static void fifo_reset(Exynos4210UartFIFO *q) +{ + if (q->data != NULL) { + g_free(q->data); + q->data = NULL; + } + + q->data = (uint8_t *)g_malloc0(q->size); + + q->sp = 0; + q->rp = 0; +} + +static uint32_t exynos4210_uart_Tx_FIFO_trigger_level(Exynos4210UartState *s) +{ + uint32_t level = 0; + uint32_t reg; + + reg = (s->reg[I_(UFCON)] & UFCON_Tx_FIFO_TRIGGER_LEVEL) >> + UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT; + + switch (s->channel) { + case 0: + level = reg * 32; + break; + case 1: + case 4: + level = reg * 8; + break; + case 2: + case 3: + level = reg * 2; + break; + default: + level = 0; + PRINT_ERROR("Wrong UART channel number: %d\n", s->channel); + } + + return level; +} + +static void exynos4210_uart_update_irq(Exynos4210UartState *s) +{ + /* + * The Tx interrupt is always requested if the number of data in the + * transmit FIFO is smaller than the trigger level. + */ + if (s->reg[I_(UFCON)] & UFCON_FIFO_ENABLE) { + + uint32_t count = (s->reg[I_(UFSTAT)] & UFSTAT_Tx_FIFO_COUNT) >> + UFSTAT_Tx_FIFO_COUNT_SHIFT; + + if (count <= exynos4210_uart_Tx_FIFO_trigger_level(s)) { + s->reg[I_(UINTSP)] |= UINTSP_TXD; + } + } + + s->reg[I_(UINTP)] = s->reg[I_(UINTSP)] & ~s->reg[I_(UINTM)]; + + if (s->reg[I_(UINTP)]) { + qemu_irq_raise(s->irq); + +#if DEBUG_IRQ + fprintf(stderr, "UART%d: IRQ has been raised: %08x\n", + s->channel, s->reg[I_(UINTP)]); +#endif + + } else { + qemu_irq_lower(s->irq); + } +} + +static void exynos4210_uart_update_parameters(Exynos4210UartState *s) +{ + int speed, parity, data_bits, stop_bits, frame_size; + QEMUSerialSetParams ssp; + uint64_t uclk_rate; + + if (s->reg[I_(UBRDIV)] == 0) { + return; + } + + frame_size = 1; /* start bit */ + if (s->reg[I_(ULCON)] & 0x20) { + frame_size++; /* parity bit */ + if (s->reg[I_(ULCON)] & 0x28) { + parity = 'E'; + } else { + parity = 'O'; + } + } else { + parity = 'N'; + } + + if (s->reg[I_(ULCON)] & 0x4) { + stop_bits = 2; + } else { + stop_bits = 1; + } + + data_bits = (s->reg[I_(ULCON)] & 0x3) + 5; + + frame_size += data_bits + stop_bits; + + uclk_rate = 24000000; + + speed = uclk_rate / ((16 * (s->reg[I_(UBRDIV)]) & 0xffff) + + (s->reg[I_(UFRACVAL)] & 0x7) + 16); + + ssp.speed = speed; + ssp.parity = parity; + ssp.data_bits = data_bits; + ssp.stop_bits = stop_bits; + + qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); + + PRINT_DEBUG("UART%d: speed: %d, parity: %c, data: %d, stop: %d\n", + s->channel, speed, parity, data_bits, stop_bits); +} + +static void exynos4210_uart_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + Exynos4210UartState *s = (Exynos4210UartState *)opaque; + uint8_t ch; + + PRINT_DEBUG_EXTEND("UART%d: <0x%04x> %s <- 0x%08llx\n", s->channel, + offset, exynos4210_uart_regname(offset), (long long unsigned int)val); + + switch (offset) { + case ULCON: + case UBRDIV: + case UFRACVAL: + s->reg[I_(offset)] = val; + exynos4210_uart_update_parameters(s); + break; + case UFCON: + s->reg[I_(UFCON)] = val; + if (val & UFCON_Rx_FIFO_RESET) { + fifo_reset(&s->rx); + s->reg[I_(UFCON)] &= ~UFCON_Rx_FIFO_RESET; + PRINT_DEBUG("UART%d: Rx FIFO Reset\n", s->channel); + } + if (val & UFCON_Tx_FIFO_RESET) { + fifo_reset(&s->tx); + s->reg[I_(UFCON)] &= ~UFCON_Tx_FIFO_RESET; + PRINT_DEBUG("UART%d: Tx FIFO Reset\n", s->channel); + } + break; + + case UTXH: + if (s->chr) { + s->reg[I_(UTRSTAT)] &= ~(UTRSTAT_TRANSMITTER_EMPTY | + UTRSTAT_Tx_BUFFER_EMPTY); + ch = (uint8_t)val; + qemu_chr_fe_write(s->chr, &ch, 1); +#if DEBUG_Tx_DATA + fprintf(stderr, "%c", ch); +#endif + s->reg[I_(UTRSTAT)] |= UTRSTAT_TRANSMITTER_EMPTY | + UTRSTAT_Tx_BUFFER_EMPTY; + s->reg[I_(UINTSP)] |= UINTSP_TXD; + exynos4210_uart_update_irq(s); + } + break; + + case UINTP: + s->reg[I_(UINTP)] &= ~val; + s->reg[I_(UINTSP)] &= ~val; + PRINT_DEBUG("UART%d: UINTP [%04x] have been cleared: %08x\n", + s->channel, offset, s->reg[I_(UINTP)]); + exynos4210_uart_update_irq(s); + break; + case UTRSTAT: + case UERSTAT: + case UFSTAT: + case UMSTAT: + case URXH: + PRINT_DEBUG("UART%d: Trying to write into RO register: %s [%04x]\n", + s->channel, exynos4210_uart_regname(offset), offset); + break; + case UINTSP: + s->reg[I_(UINTSP)] &= ~val; + break; + case UINTM: + s->reg[I_(UINTM)] = val; + exynos4210_uart_update_irq(s); + break; + case UCON: + case UMCON: + default: + s->reg[I_(offset)] = val; + break; + } +} +static uint64_t exynos4210_uart_read(void *opaque, hwaddr offset, + unsigned size) +{ + Exynos4210UartState *s = (Exynos4210UartState *)opaque; + uint32_t res; + + switch (offset) { + case UERSTAT: /* Read Only */ + res = s->reg[I_(UERSTAT)]; + s->reg[I_(UERSTAT)] = 0; + return res; + case UFSTAT: /* Read Only */ + s->reg[I_(UFSTAT)] = fifo_elements_number(&s->rx) & 0xff; + if (fifo_empty_elements_number(&s->rx) == 0) { + s->reg[I_(UFSTAT)] |= UFSTAT_Rx_FIFO_FULL; + s->reg[I_(UFSTAT)] &= ~0xff; + } + return s->reg[I_(UFSTAT)]; + case URXH: + if (s->reg[I_(UFCON)] & UFCON_FIFO_ENABLE) { + if (fifo_elements_number(&s->rx)) { + res = fifo_retrieve(&s->rx); +#if DEBUG_Rx_DATA + fprintf(stderr, "%c", res); +#endif + if (!fifo_elements_number(&s->rx)) { + s->reg[I_(UTRSTAT)] &= ~UTRSTAT_Rx_BUFFER_DATA_READY; + } else { + s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY; + } + } else { + s->reg[I_(UINTSP)] |= UINTSP_ERROR; + exynos4210_uart_update_irq(s); + res = 0; + } + } else { + s->reg[I_(UTRSTAT)] &= ~UTRSTAT_Rx_BUFFER_DATA_READY; + res = s->reg[I_(URXH)]; + } + return res; + case UTXH: + PRINT_DEBUG("UART%d: Trying to read from WO register: %s [%04x]\n", + s->channel, exynos4210_uart_regname(offset), offset); + break; + default: + return s->reg[I_(offset)]; + } + + return 0; +} + +static const MemoryRegionOps exynos4210_uart_ops = { + .read = exynos4210_uart_read, + .write = exynos4210_uart_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .max_access_size = 4, + .unaligned = false + }, +}; + +static int exynos4210_uart_can_receive(void *opaque) +{ + Exynos4210UartState *s = (Exynos4210UartState *)opaque; + + return fifo_empty_elements_number(&s->rx); +} + + +static void exynos4210_uart_receive(void *opaque, const uint8_t *buf, int size) +{ + Exynos4210UartState *s = (Exynos4210UartState *)opaque; + int i; + + if (s->reg[I_(UFCON)] & UFCON_FIFO_ENABLE) { + if (fifo_empty_elements_number(&s->rx) < size) { + for (i = 0; i < fifo_empty_elements_number(&s->rx); i++) { + fifo_store(&s->rx, buf[i]); + } + s->reg[I_(UINTSP)] |= UINTSP_ERROR; + s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY; + } else { + for (i = 0; i < size; i++) { + fifo_store(&s->rx, buf[i]); + } + s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY; + } + /* XXX: Around here we maybe should check Rx trigger level */ + s->reg[I_(UINTSP)] |= UINTSP_RXD; + } else { + s->reg[I_(URXH)] = buf[0]; + s->reg[I_(UINTSP)] |= UINTSP_RXD; + s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY; + } + + exynos4210_uart_update_irq(s); +} + + +static void exynos4210_uart_event(void *opaque, int event) +{ + Exynos4210UartState *s = (Exynos4210UartState *)opaque; + + if (event == CHR_EVENT_BREAK) { + /* When the RxDn is held in logic 0, then a null byte is pushed into the + * fifo */ + fifo_store(&s->rx, '\0'); + s->reg[I_(UERSTAT)] |= UERSTAT_BREAK; + exynos4210_uart_update_irq(s); + } +} + + +static void exynos4210_uart_reset(DeviceState *dev) +{ + Exynos4210UartState *s = + container_of(dev, Exynos4210UartState, busdev.qdev); + int regs_number = sizeof(exynos4210_uart_regs)/sizeof(Exynos4210UartReg); + int i; + + for (i = 0; i < regs_number; i++) { + s->reg[I_(exynos4210_uart_regs[i].offset)] = + exynos4210_uart_regs[i].reset_value; + } + + fifo_reset(&s->rx); + fifo_reset(&s->tx); + + PRINT_DEBUG("UART%d: Rx FIFO size: %d\n", s->channel, s->rx.size); +} + +static const VMStateDescription vmstate_exynos4210_uart_fifo = { + .name = "exynos4210.uart.fifo", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(sp, Exynos4210UartFIFO), + VMSTATE_UINT32(rp, Exynos4210UartFIFO), + VMSTATE_VBUFFER_UINT32(data, Exynos4210UartFIFO, 1, NULL, 0, size), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_exynos4210_uart = { + .name = "exynos4210.uart", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(rx, Exynos4210UartState, 1, + vmstate_exynos4210_uart_fifo, Exynos4210UartFIFO), + VMSTATE_UINT32_ARRAY(reg, Exynos4210UartState, + EXYNOS4210_UART_REGS_MEM_SIZE / sizeof(uint32_t)), + VMSTATE_END_OF_LIST() + } +}; + +DeviceState *exynos4210_uart_create(hwaddr addr, + int fifo_size, + int channel, + CharDriverState *chr, + qemu_irq irq) +{ + DeviceState *dev; + SysBusDevice *bus; + + const char chr_name[] = "serial"; + char label[ARRAY_SIZE(chr_name) + 1]; + + dev = qdev_create(NULL, "exynos4210.uart"); + + if (!chr) { + if (channel >= MAX_SERIAL_PORTS) { + hw_error("Only %d serial ports are supported by QEMU.\n", + MAX_SERIAL_PORTS); + } + chr = serial_hds[channel]; + if (!chr) { + snprintf(label, ARRAY_SIZE(label), "%s%d", chr_name, channel); + chr = qemu_chr_new(label, "null", NULL); + if (!(chr)) { + hw_error("Can't assign serial port to UART%d.\n", channel); + } + } + } + + qdev_prop_set_chr(dev, "chardev", chr); + qdev_prop_set_uint32(dev, "channel", channel); + qdev_prop_set_uint32(dev, "rx-size", fifo_size); + qdev_prop_set_uint32(dev, "tx-size", fifo_size); + + bus = SYS_BUS_DEVICE(dev); + qdev_init_nofail(dev); + if (addr != (hwaddr)-1) { + sysbus_mmio_map(bus, 0, addr); + } + sysbus_connect_irq(bus, 0, irq); + + return dev; +} + +static int exynos4210_uart_init(SysBusDevice *dev) +{ + Exynos4210UartState *s = FROM_SYSBUS(Exynos4210UartState, dev); + + /* memory mapping */ + memory_region_init_io(&s->iomem, &exynos4210_uart_ops, s, "exynos4210.uart", + EXYNOS4210_UART_REGS_MEM_SIZE); + sysbus_init_mmio(dev, &s->iomem); + + sysbus_init_irq(dev, &s->irq); + + qemu_chr_add_handlers(s->chr, exynos4210_uart_can_receive, + exynos4210_uart_receive, exynos4210_uart_event, s); + + return 0; +} + +static Property exynos4210_uart_properties[] = { + DEFINE_PROP_CHR("chardev", Exynos4210UartState, chr), + DEFINE_PROP_UINT32("channel", Exynos4210UartState, channel, 0), + DEFINE_PROP_UINT32("rx-size", Exynos4210UartState, rx.size, 16), + DEFINE_PROP_UINT32("tx-size", Exynos4210UartState, tx.size, 16), + DEFINE_PROP_END_OF_LIST(), +}; + +static void exynos4210_uart_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = exynos4210_uart_init; + dc->reset = exynos4210_uart_reset; + dc->props = exynos4210_uart_properties; + dc->vmsd = &vmstate_exynos4210_uart; +} + +static const TypeInfo exynos4210_uart_info = { + .name = "exynos4210.uart", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Exynos4210UartState), + .class_init = exynos4210_uart_class_init, +}; + +static void exynos4210_uart_register(void) +{ + type_register_static(&exynos4210_uart_info); +} + +type_init(exynos4210_uart_register) diff --git a/hw/char/grlib_apbuart.c b/hw/char/grlib_apbuart.c new file mode 100644 index 0000000..62f7990 --- /dev/null +++ b/hw/char/grlib_apbuart.c @@ -0,0 +1,293 @@ +/* + * QEMU GRLIB APB UART Emulator + * + * Copyright (c) 2010-2011 AdaCore + * + * 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/sysbus.h" +#include "char/char.h" + +#include "trace.h" + +#define UART_REG_SIZE 20 /* Size of memory mapped registers */ + +/* UART status register fields */ +#define UART_DATA_READY (1 << 0) +#define UART_TRANSMIT_SHIFT_EMPTY (1 << 1) +#define UART_TRANSMIT_FIFO_EMPTY (1 << 2) +#define UART_BREAK_RECEIVED (1 << 3) +#define UART_OVERRUN (1 << 4) +#define UART_PARITY_ERROR (1 << 5) +#define UART_FRAMING_ERROR (1 << 6) +#define UART_TRANSMIT_FIFO_HALF (1 << 7) +#define UART_RECEIVE_FIFO_HALF (1 << 8) +#define UART_TRANSMIT_FIFO_FULL (1 << 9) +#define UART_RECEIVE_FIFO_FULL (1 << 10) + +/* UART control register fields */ +#define UART_RECEIVE_ENABLE (1 << 0) +#define UART_TRANSMIT_ENABLE (1 << 1) +#define UART_RECEIVE_INTERRUPT (1 << 2) +#define UART_TRANSMIT_INTERRUPT (1 << 3) +#define UART_PARITY_SELECT (1 << 4) +#define UART_PARITY_ENABLE (1 << 5) +#define UART_FLOW_CONTROL (1 << 6) +#define UART_LOOPBACK (1 << 7) +#define UART_EXTERNAL_CLOCK (1 << 8) +#define UART_RECEIVE_FIFO_INTERRUPT (1 << 9) +#define UART_TRANSMIT_FIFO_INTERRUPT (1 << 10) +#define UART_FIFO_DEBUG_MODE (1 << 11) +#define UART_OUTPUT_ENABLE (1 << 12) +#define UART_FIFO_AVAILABLE (1 << 31) + +/* Memory mapped register offsets */ +#define DATA_OFFSET 0x00 +#define STATUS_OFFSET 0x04 +#define CONTROL_OFFSET 0x08 +#define SCALER_OFFSET 0x0C /* not supported */ +#define FIFO_DEBUG_OFFSET 0x10 /* not supported */ + +#define FIFO_LENGTH 1024 + +typedef struct UART { + SysBusDevice busdev; + MemoryRegion iomem; + qemu_irq irq; + + CharDriverState *chr; + + /* registers */ + uint32_t status; + uint32_t control; + + /* FIFO */ + char buffer[FIFO_LENGTH]; + int len; + int current; +} UART; + +static int uart_data_to_read(UART *uart) +{ + return uart->current < uart->len; +} + +static char uart_pop(UART *uart) +{ + char ret; + + if (uart->len == 0) { + uart->status &= ~UART_DATA_READY; + return 0; + } + + ret = uart->buffer[uart->current++]; + + if (uart->current >= uart->len) { + /* Flush */ + uart->len = 0; + uart->current = 0; + } + + if (!uart_data_to_read(uart)) { + uart->status &= ~UART_DATA_READY; + } + + return ret; +} + +static void uart_add_to_fifo(UART *uart, + const uint8_t *buffer, + int length) +{ + if (uart->len + length > FIFO_LENGTH) { + abort(); + } + memcpy(uart->buffer + uart->len, buffer, length); + uart->len += length; +} + +static int grlib_apbuart_can_receive(void *opaque) +{ + UART *uart = opaque; + + return FIFO_LENGTH - uart->len; +} + +static void grlib_apbuart_receive(void *opaque, const uint8_t *buf, int size) +{ + UART *uart = opaque; + + if (uart->control & UART_RECEIVE_ENABLE) { + uart_add_to_fifo(uart, buf, size); + + uart->status |= UART_DATA_READY; + + if (uart->control & UART_RECEIVE_INTERRUPT) { + qemu_irq_pulse(uart->irq); + } + } +} + +static void grlib_apbuart_event(void *opaque, int event) +{ + trace_grlib_apbuart_event(event); +} + + +static uint64_t grlib_apbuart_read(void *opaque, hwaddr addr, + unsigned size) +{ + UART *uart = opaque; + + addr &= 0xff; + + /* Unit registers */ + switch (addr) { + case DATA_OFFSET: + case DATA_OFFSET + 3: /* when only one byte read */ + return uart_pop(uart); + + case STATUS_OFFSET: + /* Read Only */ + return uart->status; + + case CONTROL_OFFSET: + return uart->control; + + case SCALER_OFFSET: + /* Not supported */ + return 0; + + default: + trace_grlib_apbuart_readl_unknown(addr); + return 0; + } +} + +static void grlib_apbuart_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + UART *uart = opaque; + unsigned char c = 0; + + addr &= 0xff; + + /* Unit registers */ + switch (addr) { + case DATA_OFFSET: + case DATA_OFFSET + 3: /* When only one byte write */ + /* Transmit when character device available and transmitter enabled */ + if ((uart->chr) && (uart->control & UART_TRANSMIT_ENABLE)) { + c = value & 0xFF; + qemu_chr_fe_write(uart->chr, &c, 1); + /* Generate interrupt */ + if (uart->control & UART_TRANSMIT_INTERRUPT) { + qemu_irq_pulse(uart->irq); + } + } + return; + + case STATUS_OFFSET: + /* Read Only */ + return; + + case CONTROL_OFFSET: + uart->control = value; + return; + + case SCALER_OFFSET: + /* Not supported */ + return; + + default: + break; + } + + trace_grlib_apbuart_writel_unknown(addr, value); +} + +static const MemoryRegionOps grlib_apbuart_ops = { + .write = grlib_apbuart_write, + .read = grlib_apbuart_read, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static int grlib_apbuart_init(SysBusDevice *dev) +{ + UART *uart = FROM_SYSBUS(typeof(*uart), dev); + + qemu_chr_add_handlers(uart->chr, + grlib_apbuart_can_receive, + grlib_apbuart_receive, + grlib_apbuart_event, + uart); + + sysbus_init_irq(dev, &uart->irq); + + memory_region_init_io(&uart->iomem, &grlib_apbuart_ops, uart, + "uart", UART_REG_SIZE); + + sysbus_init_mmio(dev, &uart->iomem); + + return 0; +} + +static void grlib_apbuart_reset(DeviceState *d) +{ + UART *uart = container_of(d, UART, busdev.qdev); + + /* Transmitter FIFO and shift registers are always empty in QEMU */ + uart->status = UART_TRANSMIT_FIFO_EMPTY | UART_TRANSMIT_SHIFT_EMPTY; + /* Everything is off */ + uart->control = 0; + /* Flush receive FIFO */ + uart->len = 0; + uart->current = 0; +} + +static Property grlib_apbuart_properties[] = { + DEFINE_PROP_CHR("chrdev", UART, chr), + DEFINE_PROP_END_OF_LIST(), +}; + +static void grlib_apbuart_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = grlib_apbuart_init; + dc->reset = grlib_apbuart_reset; + dc->props = grlib_apbuart_properties; +} + +static const TypeInfo grlib_apbuart_info = { + .name = "grlib,apbuart", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(UART), + .class_init = grlib_apbuart_class_init, +}; + +static void grlib_apbuart_register_types(void) +{ + type_register_static(&grlib_apbuart_info); +} + +type_init(grlib_apbuart_register_types) diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c new file mode 100644 index 0000000..d7ec209 --- /dev/null +++ b/hw/char/imx_serial.c @@ -0,0 +1,467 @@ +/* + * IMX31 UARTS + * + * Copyright (c) 2008 OKL + * Originally Written by Hans Jiang + * Copyright (c) 2011 NICTA Pty Ltd. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * This is a `bare-bones' implementation of the IMX series serial ports. + * TODO: + * -- implement FIFOs. The real hardware has 32 word transmit + * and receive FIFOs; we currently use a 1-char buffer + * -- implement DMA + * -- implement BAUD-rate and modem lines, for when the backend + * is a real serial device. + */ + +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "sysemu/sysemu.h" +#include "char/char.h" +#include "hw/arm/imx.h" + +//#define DEBUG_SERIAL 1 +#ifdef DEBUG_SERIAL +#define DPRINTF(fmt, args...) \ +do { printf("imx_serial: " fmt , ##args); } while (0) +#else +#define DPRINTF(fmt, args...) do {} while (0) +#endif + +/* + * Define to 1 for messages about attempts to + * access unimplemented registers or similar. + */ +//#define DEBUG_IMPLEMENTATION 1 +#ifdef DEBUG_IMPLEMENTATION +# define IPRINTF(fmt, args...) \ + do { fprintf(stderr, "imx_serial: " fmt, ##args); } while (0) +#else +# define IPRINTF(fmt, args...) do {} while (0) +#endif + +typedef struct { + SysBusDevice busdev; + MemoryRegion iomem; + int32_t readbuff; + + uint32_t usr1; + uint32_t usr2; + uint32_t ucr1; + uint32_t ucr2; + uint32_t uts1; + + /* + * The registers below are implemented just so that the + * guest OS sees what it has written + */ + uint32_t onems; + uint32_t ufcr; + uint32_t ubmr; + uint32_t ubrc; + uint32_t ucr3; + + qemu_irq irq; + CharDriverState *chr; +} IMXSerialState; + +static const VMStateDescription vmstate_imx_serial = { + .name = "imx-serial", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32(readbuff, IMXSerialState), + VMSTATE_UINT32(usr1, IMXSerialState), + VMSTATE_UINT32(usr2, IMXSerialState), + VMSTATE_UINT32(ucr1, IMXSerialState), + VMSTATE_UINT32(uts1, IMXSerialState), + VMSTATE_UINT32(onems, IMXSerialState), + VMSTATE_UINT32(ufcr, IMXSerialState), + VMSTATE_UINT32(ubmr, IMXSerialState), + VMSTATE_UINT32(ubrc, IMXSerialState), + VMSTATE_UINT32(ucr3, IMXSerialState), + VMSTATE_END_OF_LIST() + }, +}; + + +#define URXD_CHARRDY (1<<15) /* character read is valid */ +#define URXD_ERR (1<<14) /* Character has error */ +#define URXD_BRK (1<<11) /* Break received */ + +#define USR1_PARTYER (1<<15) /* Parity Error */ +#define USR1_RTSS (1<<14) /* RTS pin status */ +#define USR1_TRDY (1<<13) /* Tx ready */ +#define USR1_RTSD (1<<12) /* RTS delta: pin changed state */ +#define USR1_ESCF (1<<11) /* Escape sequence interrupt */ +#define USR1_FRAMERR (1<<10) /* Framing error */ +#define USR1_RRDY (1<<9) /* receiver ready */ +#define USR1_AGTIM (1<<8) /* Aging timer interrupt */ +#define USR1_DTRD (1<<7) /* DTR changed */ +#define USR1_RXDS (1<<6) /* Receiver is idle */ +#define USR1_AIRINT (1<<5) /* Aysnch IR interrupt */ +#define USR1_AWAKE (1<<4) /* Falling edge detected on RXd pin */ + +#define USR2_ADET (1<<15) /* Autobaud complete */ +#define USR2_TXFE (1<<14) /* Transmit FIFO empty */ +#define USR2_DTRF (1<<13) /* DTR/DSR transition */ +#define USR2_IDLE (1<<12) /* UART has been idle for too long */ +#define USR2_ACST (1<<11) /* Autobaud counter stopped */ +#define USR2_RIDELT (1<<10) /* Ring Indicator delta */ +#define USR2_RIIN (1<<9) /* Ring Indicator Input */ +#define USR2_IRINT (1<<8) /* Serial Infrared Interrupt */ +#define USR2_WAKE (1<<7) /* Start bit detected */ +#define USR2_DCDDELT (1<<6) /* Data Carrier Detect delta */ +#define USR2_DCDIN (1<<5) /* Data Carrier Detect Input */ +#define USR2_RTSF (1<<4) /* RTS transition */ +#define USR2_TXDC (1<<3) /* Transmission complete */ +#define USR2_BRCD (1<<2) /* Break condition detected */ +#define USR2_ORE (1<<1) /* Overrun error */ +#define USR2_RDR (1<<0) /* Receive data ready */ + +#define UCR1_TRDYEN (1<<13) /* Tx Ready Interrupt Enable */ +#define UCR1_RRDYEN (1<<9) /* Rx Ready Interrupt Enable */ +#define UCR1_TXMPTYEN (1<<6) /* Tx Empty Interrupt Enable */ +#define UCR1_UARTEN (1<<0) /* UART Enable */ + +#define UCR2_TXEN (1<<2) /* Transmitter enable */ +#define UCR2_RXEN (1<<1) /* Receiver enable */ +#define UCR2_SRST (1<<0) /* Reset complete */ + +#define UTS1_TXEMPTY (1<<6) +#define UTS1_RXEMPTY (1<<5) +#define UTS1_TXFULL (1<<4) +#define UTS1_RXFULL (1<<3) + +static void imx_update(IMXSerialState *s) +{ + uint32_t flags; + + flags = (s->usr1 & s->ucr1) & (USR1_TRDY|USR1_RRDY); + if (!(s->ucr1 & UCR1_TXMPTYEN)) { + flags &= ~USR1_TRDY; + } + + qemu_set_irq(s->irq, !!flags); +} + +static void imx_serial_reset(IMXSerialState *s) +{ + + s->usr1 = USR1_TRDY | USR1_RXDS; + /* + * Fake attachment of a terminal: assert RTS. + */ + s->usr1 |= USR1_RTSS; + s->usr2 = USR2_TXFE | USR2_TXDC | USR2_DCDIN; + s->uts1 = UTS1_RXEMPTY | UTS1_TXEMPTY; + s->ucr1 = 0; + s->ucr2 = UCR2_SRST; + s->ucr3 = 0x700; + s->ubmr = 0; + s->ubrc = 4; + s->readbuff = URXD_ERR; +} + +static void imx_serial_reset_at_boot(DeviceState *dev) +{ + IMXSerialState *s = container_of(dev, IMXSerialState, busdev.qdev); + + imx_serial_reset(s); + + /* + * enable the uart on boot, so messages from the linux decompresser + * are visible. On real hardware this is done by the boot rom + * before anything else is loaded. + */ + s->ucr1 = UCR1_UARTEN; + s->ucr2 = UCR2_TXEN; + +} + +static uint64_t imx_serial_read(void *opaque, hwaddr offset, + unsigned size) +{ + IMXSerialState *s = (IMXSerialState *)opaque; + uint32_t c; + + DPRINTF("read(offset=%x)\n", offset >> 2); + switch (offset >> 2) { + case 0x0: /* URXD */ + c = s->readbuff; + if (!(s->uts1 & UTS1_RXEMPTY)) { + /* Character is valid */ + c |= URXD_CHARRDY; + s->usr1 &= ~USR1_RRDY; + s->usr2 &= ~USR2_RDR; + s->uts1 |= UTS1_RXEMPTY; + imx_update(s); + qemu_chr_accept_input(s->chr); + } + return c; + + case 0x20: /* UCR1 */ + return s->ucr1; + + case 0x21: /* UCR2 */ + return s->ucr2; + + case 0x25: /* USR1 */ + return s->usr1; + + case 0x26: /* USR2 */ + return s->usr2; + + case 0x2A: /* BRM Modulator */ + return s->ubmr; + + case 0x2B: /* Baud Rate Count */ + return s->ubrc; + + case 0x2d: /* Test register */ + return s->uts1; + + case 0x24: /* UFCR */ + return s->ufcr; + + case 0x2c: + return s->onems; + + case 0x22: /* UCR3 */ + return s->ucr3; + + case 0x23: /* UCR4 */ + case 0x29: /* BRM Incremental */ + return 0x0; /* TODO */ + + default: + IPRINTF("imx_serial_read: bad offset: 0x%x\n", (int)offset); + return 0; + } +} + +static void imx_serial_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + IMXSerialState *s = (IMXSerialState *)opaque; + unsigned char ch; + + DPRINTF("write(offset=%x, value = %x) to %s\n", + offset >> 2, + (unsigned int)value, s->chr ? s->chr->label : "NODEV"); + + switch (offset >> 2) { + case 0x10: /* UTXD */ + ch = value; + if (s->ucr2 & UCR2_TXEN) { + if (s->chr) { + qemu_chr_fe_write(s->chr, &ch, 1); + } + s->usr1 &= ~USR1_TRDY; + imx_update(s); + s->usr1 |= USR1_TRDY; + imx_update(s); + } + break; + + case 0x20: /* UCR1 */ + s->ucr1 = value & 0xffff; + DPRINTF("write(ucr1=%x)\n", (unsigned int)value); + imx_update(s); + break; + + case 0x21: /* UCR2 */ + /* + * Only a few bits in control register 2 are implemented as yet. + * If it's intended to use a real serial device as a back-end, this + * register will have to be implemented more fully. + */ + if (!(value & UCR2_SRST)) { + imx_serial_reset(s); + imx_update(s); + value |= UCR2_SRST; + } + if (value & UCR2_RXEN) { + if (!(s->ucr2 & UCR2_RXEN)) { + qemu_chr_accept_input(s->chr); + } + } + s->ucr2 = value & 0xffff; + break; + + case 0x25: /* USR1 */ + value &= USR1_AWAKE | USR1_AIRINT | USR1_DTRD | USR1_AGTIM | + USR1_FRAMERR | USR1_ESCF | USR1_RTSD | USR1_PARTYER; + s->usr1 &= ~value; + break; + + case 0x26: /* USR2 */ + /* + * Writing 1 to some bits clears them; all other + * values are ignored + */ + value &= USR2_ADET | USR2_DTRF | USR2_IDLE | USR2_ACST | + USR2_RIDELT | USR2_IRINT | USR2_WAKE | + USR2_DCDDELT | USR2_RTSF | USR2_BRCD | USR2_ORE; + s->usr2 &= ~value; + break; + + /* + * Linux expects to see what it writes to these registers + * We don't currently alter the baud rate + */ + case 0x29: /* UBIR */ + s->ubrc = value & 0xffff; + break; + + case 0x2a: /* UBMR */ + s->ubmr = value & 0xffff; + break; + + case 0x2c: /* One ms reg */ + s->onems = value & 0xffff; + break; + + case 0x24: /* FIFO control register */ + s->ufcr = value & 0xffff; + break; + + case 0x22: /* UCR3 */ + s->ucr3 = value & 0xffff; + break; + + case 0x2d: /* UTS1 */ + case 0x23: /* UCR4 */ + IPRINTF("Unimplemented Register %x written to\n", offset >> 2); + /* TODO */ + break; + + default: + IPRINTF("imx_serial_write: Bad offset 0x%x\n", (int)offset); + } +} + +static int imx_can_receive(void *opaque) +{ + IMXSerialState *s = (IMXSerialState *)opaque; + return !(s->usr1 & USR1_RRDY); +} + +static void imx_put_data(void *opaque, uint32_t value) +{ + IMXSerialState *s = (IMXSerialState *)opaque; + DPRINTF("received char\n"); + s->usr1 |= USR1_RRDY; + s->usr2 |= USR2_RDR; + s->uts1 &= ~UTS1_RXEMPTY; + s->readbuff = value; + imx_update(s); +} + +static void imx_receive(void *opaque, const uint8_t *buf, int size) +{ + imx_put_data(opaque, *buf); +} + +static void imx_event(void *opaque, int event) +{ + if (event == CHR_EVENT_BREAK) { + imx_put_data(opaque, URXD_BRK); + } +} + + +static const struct MemoryRegionOps imx_serial_ops = { + .read = imx_serial_read, + .write = imx_serial_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static int imx_serial_init(SysBusDevice *dev) +{ + IMXSerialState *s = FROM_SYSBUS(IMXSerialState, dev); + + + memory_region_init_io(&s->iomem, &imx_serial_ops, s, "imx-serial", 0x1000); + sysbus_init_mmio(dev, &s->iomem); + sysbus_init_irq(dev, &s->irq); + + if (s->chr) { + qemu_chr_add_handlers(s->chr, imx_can_receive, imx_receive, + imx_event, s); + } else { + DPRINTF("No char dev for uart at 0x%lx\n", + (unsigned long)s->iomem.ram_addr); + } + + return 0; +} + +void imx_serial_create(int uart, const hwaddr addr, qemu_irq irq) +{ + DeviceState *dev; + SysBusDevice *bus; + CharDriverState *chr; + const char chr_name[] = "serial"; + char label[ARRAY_SIZE(chr_name) + 1]; + + dev = qdev_create(NULL, "imx-serial"); + + if (uart >= MAX_SERIAL_PORTS) { + hw_error("Cannot assign uart %d: QEMU supports only %d ports\n", + uart, MAX_SERIAL_PORTS); + } + chr = serial_hds[uart]; + if (!chr) { + snprintf(label, ARRAY_SIZE(label), "%s%d", chr_name, uart); + chr = qemu_chr_new(label, "null", NULL); + if (!(chr)) { + hw_error("Can't assign serial port to imx-uart%d.\n", uart); + } + } + + qdev_prop_set_chr(dev, "chardev", chr); + bus = SYS_BUS_DEVICE(dev); + qdev_init_nofail(dev); + if (addr != (hwaddr)-1) { + sysbus_mmio_map(bus, 0, addr); + } + sysbus_connect_irq(bus, 0, irq); + +} + + +static Property imx32_serial_properties[] = { + DEFINE_PROP_CHR("chardev", IMXSerialState, chr), + DEFINE_PROP_END_OF_LIST(), +}; + +static void imx_serial_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = imx_serial_init; + dc->vmsd = &vmstate_imx_serial; + dc->reset = imx_serial_reset_at_boot; + dc->desc = "i.MX series UART"; + dc->props = imx32_serial_properties; +} + +static const TypeInfo imx_serial_info = { + .name = "imx-serial", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IMXSerialState), + .class_init = imx_serial_class_init, +}; + +static void imx_serial_register_types(void) +{ + type_register_static(&imx_serial_info); +} + +type_init(imx_serial_register_types) diff --git a/hw/char/lm32_juart.c b/hw/char/lm32_juart.c new file mode 100644 index 0000000..93f0d15 --- /dev/null +++ b/hw/char/lm32_juart.c @@ -0,0 +1,159 @@ +/* + * LatticeMico32 JTAG UART model. + * + * Copyright (c) 2010 Michael Walle + * + * 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 . + */ + +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "trace.h" +#include "char/char.h" + +#include "hw/lm32/lm32_juart.h" + +enum { + LM32_JUART_MIN_SAVE_VERSION = 0, + LM32_JUART_CURRENT_SAVE_VERSION = 0, + LM32_JUART_MAX_SAVE_VERSION = 0, +}; + +enum { + JTX_FULL = (1<<8), +}; + +enum { + JRX_FULL = (1<<8), +}; + +struct LM32JuartState { + SysBusDevice busdev; + CharDriverState *chr; + + uint32_t jtx; + uint32_t jrx; +}; +typedef struct LM32JuartState LM32JuartState; + +uint32_t lm32_juart_get_jtx(DeviceState *d) +{ + LM32JuartState *s = container_of(d, LM32JuartState, busdev.qdev); + + trace_lm32_juart_get_jtx(s->jtx); + return s->jtx; +} + +uint32_t lm32_juart_get_jrx(DeviceState *d) +{ + LM32JuartState *s = container_of(d, LM32JuartState, busdev.qdev); + + trace_lm32_juart_get_jrx(s->jrx); + return s->jrx; +} + +void lm32_juart_set_jtx(DeviceState *d, uint32_t jtx) +{ + LM32JuartState *s = container_of(d, LM32JuartState, busdev.qdev); + unsigned char ch = jtx & 0xff; + + trace_lm32_juart_set_jtx(s->jtx); + + s->jtx = jtx; + if (s->chr) { + qemu_chr_fe_write(s->chr, &ch, 1); + } +} + +void lm32_juart_set_jrx(DeviceState *d, uint32_t jtx) +{ + LM32JuartState *s = container_of(d, LM32JuartState, busdev.qdev); + + trace_lm32_juart_set_jrx(s->jrx); + s->jrx &= ~JRX_FULL; +} + +static void juart_rx(void *opaque, const uint8_t *buf, int size) +{ + LM32JuartState *s = opaque; + + s->jrx = *buf | JRX_FULL; +} + +static int juart_can_rx(void *opaque) +{ + LM32JuartState *s = opaque; + + return !(s->jrx & JRX_FULL); +} + +static void juart_event(void *opaque, int event) +{ +} + +static void juart_reset(DeviceState *d) +{ + LM32JuartState *s = container_of(d, LM32JuartState, busdev.qdev); + + s->jtx = 0; + s->jrx = 0; +} + +static int lm32_juart_init(SysBusDevice *dev) +{ + LM32JuartState *s = FROM_SYSBUS(typeof(*s), dev); + + s->chr = qemu_char_get_next_serial(); + if (s->chr) { + qemu_chr_add_handlers(s->chr, juart_can_rx, juart_rx, juart_event, s); + } + + return 0; +} + +static const VMStateDescription vmstate_lm32_juart = { + .name = "lm32-juart", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(jtx, LM32JuartState), + VMSTATE_UINT32(jrx, LM32JuartState), + VMSTATE_END_OF_LIST() + } +}; + +static void lm32_juart_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = lm32_juart_init; + dc->reset = juart_reset; + dc->vmsd = &vmstate_lm32_juart; +} + +static const TypeInfo lm32_juart_info = { + .name = "lm32-juart", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(LM32JuartState), + .class_init = lm32_juart_class_init, +}; + +static void lm32_juart_register_types(void) +{ + type_register_static(&lm32_juart_info); +} + +type_init(lm32_juart_register_types) diff --git a/hw/char/lm32_uart.c b/hw/char/lm32_uart.c new file mode 100644 index 0000000..32bc37a --- /dev/null +++ b/hw/char/lm32_uart.c @@ -0,0 +1,297 @@ +/* + * QEMU model of the LatticeMico32 UART block. + * + * Copyright (c) 2010 Michael Walle + * + * 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 . + * + * + * Specification available at: + * http://www.latticesemi.com/documents/mico32uart.pdf + */ + + +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "trace.h" +#include "char/char.h" +#include "qemu/error-report.h" + +enum { + R_RXTX = 0, + R_IER, + R_IIR, + R_LCR, + R_MCR, + R_LSR, + R_MSR, + R_DIV, + R_MAX +}; + +enum { + IER_RBRI = (1<<0), + IER_THRI = (1<<1), + IER_RLSI = (1<<2), + IER_MSI = (1<<3), +}; + +enum { + IIR_STAT = (1<<0), + IIR_ID0 = (1<<1), + IIR_ID1 = (1<<2), +}; + +enum { + LCR_WLS0 = (1<<0), + LCR_WLS1 = (1<<1), + LCR_STB = (1<<2), + LCR_PEN = (1<<3), + LCR_EPS = (1<<4), + LCR_SP = (1<<5), + LCR_SB = (1<<6), +}; + +enum { + MCR_DTR = (1<<0), + MCR_RTS = (1<<1), +}; + +enum { + LSR_DR = (1<<0), + LSR_OE = (1<<1), + LSR_PE = (1<<2), + LSR_FE = (1<<3), + LSR_BI = (1<<4), + LSR_THRE = (1<<5), + LSR_TEMT = (1<<6), +}; + +enum { + MSR_DCTS = (1<<0), + MSR_DDSR = (1<<1), + MSR_TERI = (1<<2), + MSR_DDCD = (1<<3), + MSR_CTS = (1<<4), + MSR_DSR = (1<<5), + MSR_RI = (1<<6), + MSR_DCD = (1<<7), +}; + +struct LM32UartState { + SysBusDevice busdev; + MemoryRegion iomem; + CharDriverState *chr; + qemu_irq irq; + + uint32_t regs[R_MAX]; +}; +typedef struct LM32UartState LM32UartState; + +static void uart_update_irq(LM32UartState *s) +{ + unsigned int irq; + + if ((s->regs[R_LSR] & (LSR_OE | LSR_PE | LSR_FE | LSR_BI)) + && (s->regs[R_IER] & IER_RLSI)) { + irq = 1; + s->regs[R_IIR] = IIR_ID1 | IIR_ID0; + } else if ((s->regs[R_LSR] & LSR_DR) && (s->regs[R_IER] & IER_RBRI)) { + irq = 1; + s->regs[R_IIR] = IIR_ID1; + } else if ((s->regs[R_LSR] & LSR_THRE) && (s->regs[R_IER] & IER_THRI)) { + irq = 1; + s->regs[R_IIR] = IIR_ID0; + } else if ((s->regs[R_MSR] & 0x0f) && (s->regs[R_IER] & IER_MSI)) { + irq = 1; + s->regs[R_IIR] = 0; + } else { + irq = 0; + s->regs[R_IIR] = IIR_STAT; + } + + trace_lm32_uart_irq_state(irq); + qemu_set_irq(s->irq, irq); +} + +static uint64_t uart_read(void *opaque, hwaddr addr, + unsigned size) +{ + LM32UartState *s = opaque; + uint32_t r = 0; + + addr >>= 2; + switch (addr) { + case R_RXTX: + r = s->regs[R_RXTX]; + s->regs[R_LSR] &= ~LSR_DR; + uart_update_irq(s); + qemu_chr_accept_input(s->chr); + break; + case R_IIR: + case R_LSR: + case R_MSR: + r = s->regs[addr]; + break; + case R_IER: + case R_LCR: + case R_MCR: + case R_DIV: + error_report("lm32_uart: read access to write only register 0x" + TARGET_FMT_plx, addr << 2); + break; + default: + error_report("lm32_uart: read access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } + + trace_lm32_uart_memory_read(addr << 2, r); + return r; +} + +static void uart_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + LM32UartState *s = opaque; + unsigned char ch = value; + + trace_lm32_uart_memory_write(addr, value); + + addr >>= 2; + switch (addr) { + case R_RXTX: + if (s->chr) { + qemu_chr_fe_write(s->chr, &ch, 1); + } + break; + case R_IER: + case R_LCR: + case R_MCR: + case R_DIV: + s->regs[addr] = value; + break; + case R_IIR: + case R_LSR: + case R_MSR: + error_report("lm32_uart: write access to read only register 0x" + TARGET_FMT_plx, addr << 2); + break; + default: + error_report("lm32_uart: write access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } + uart_update_irq(s); +} + +static const MemoryRegionOps uart_ops = { + .read = uart_read, + .write = uart_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void uart_rx(void *opaque, const uint8_t *buf, int size) +{ + LM32UartState *s = opaque; + + if (s->regs[R_LSR] & LSR_DR) { + s->regs[R_LSR] |= LSR_OE; + } + + s->regs[R_LSR] |= LSR_DR; + s->regs[R_RXTX] = *buf; + + uart_update_irq(s); +} + +static int uart_can_rx(void *opaque) +{ + LM32UartState *s = opaque; + + return !(s->regs[R_LSR] & LSR_DR); +} + +static void uart_event(void *opaque, int event) +{ +} + +static void uart_reset(DeviceState *d) +{ + LM32UartState *s = container_of(d, LM32UartState, busdev.qdev); + int i; + + for (i = 0; i < R_MAX; i++) { + s->regs[i] = 0; + } + + /* defaults */ + s->regs[R_LSR] = LSR_THRE | LSR_TEMT; +} + +static int lm32_uart_init(SysBusDevice *dev) +{ + LM32UartState *s = FROM_SYSBUS(typeof(*s), dev); + + sysbus_init_irq(dev, &s->irq); + + memory_region_init_io(&s->iomem, &uart_ops, s, "uart", R_MAX * 4); + sysbus_init_mmio(dev, &s->iomem); + + s->chr = qemu_char_get_next_serial(); + if (s->chr) { + qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s); + } + + return 0; +} + +static const VMStateDescription vmstate_lm32_uart = { + .name = "lm32-uart", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, LM32UartState, R_MAX), + VMSTATE_END_OF_LIST() + } +}; + +static void lm32_uart_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = lm32_uart_init; + dc->reset = uart_reset; + dc->vmsd = &vmstate_lm32_uart; +} + +static const TypeInfo lm32_uart_info = { + .name = "lm32-uart", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(LM32UartState), + .class_init = lm32_uart_class_init, +}; + +static void lm32_uart_register_types(void) +{ + type_register_static(&lm32_uart_info); +} + +type_init(lm32_uart_register_types) diff --git a/hw/char/mcf_uart.c b/hw/char/mcf_uart.c new file mode 100644 index 0000000..6724b1b --- /dev/null +++ b/hw/char/mcf_uart.c @@ -0,0 +1,307 @@ +/* + * ColdFire UART emulation. + * + * Copyright (c) 2007 CodeSourcery. + * + * This code is licensed under the GPL + */ +#include "hw/hw.h" +#include "hw/m68k/mcf.h" +#include "char/char.h" +#include "exec/address-spaces.h" + +typedef struct { + MemoryRegion iomem; + uint8_t mr[2]; + uint8_t sr; + uint8_t isr; + uint8_t imr; + uint8_t bg1; + uint8_t bg2; + uint8_t fifo[4]; + uint8_t tb; + int current_mr; + int fifo_len; + int tx_enabled; + int rx_enabled; + qemu_irq irq; + CharDriverState *chr; +} mcf_uart_state; + +/* UART Status Register bits. */ +#define MCF_UART_RxRDY 0x01 +#define MCF_UART_FFULL 0x02 +#define MCF_UART_TxRDY 0x04 +#define MCF_UART_TxEMP 0x08 +#define MCF_UART_OE 0x10 +#define MCF_UART_PE 0x20 +#define MCF_UART_FE 0x40 +#define MCF_UART_RB 0x80 + +/* Interrupt flags. */ +#define MCF_UART_TxINT 0x01 +#define MCF_UART_RxINT 0x02 +#define MCF_UART_DBINT 0x04 +#define MCF_UART_COSINT 0x80 + +/* UMR1 flags. */ +#define MCF_UART_BC0 0x01 +#define MCF_UART_BC1 0x02 +#define MCF_UART_PT 0x04 +#define MCF_UART_PM0 0x08 +#define MCF_UART_PM1 0x10 +#define MCF_UART_ERR 0x20 +#define MCF_UART_RxIRQ 0x40 +#define MCF_UART_RxRTS 0x80 + +static void mcf_uart_update(mcf_uart_state *s) +{ + s->isr &= ~(MCF_UART_TxINT | MCF_UART_RxINT); + if (s->sr & MCF_UART_TxRDY) + s->isr |= MCF_UART_TxINT; + if ((s->sr & ((s->mr[0] & MCF_UART_RxIRQ) + ? MCF_UART_FFULL : MCF_UART_RxRDY)) != 0) + s->isr |= MCF_UART_RxINT; + + qemu_set_irq(s->irq, (s->isr & s->imr) != 0); +} + +uint64_t mcf_uart_read(void *opaque, hwaddr addr, + unsigned size) +{ + mcf_uart_state *s = (mcf_uart_state *)opaque; + switch (addr & 0x3f) { + case 0x00: + return s->mr[s->current_mr]; + case 0x04: + return s->sr; + case 0x0c: + { + uint8_t val; + int i; + + if (s->fifo_len == 0) + return 0; + + val = s->fifo[0]; + s->fifo_len--; + for (i = 0; i < s->fifo_len; i++) + s->fifo[i] = s->fifo[i + 1]; + s->sr &= ~MCF_UART_FFULL; + if (s->fifo_len == 0) + s->sr &= ~MCF_UART_RxRDY; + mcf_uart_update(s); + qemu_chr_accept_input(s->chr); + return val; + } + case 0x10: + /* TODO: Implement IPCR. */ + return 0; + case 0x14: + return s->isr; + case 0x18: + return s->bg1; + case 0x1c: + return s->bg2; + default: + return 0; + } +} + +/* Update TxRDY flag and set data if present and enabled. */ +static void mcf_uart_do_tx(mcf_uart_state *s) +{ + if (s->tx_enabled && (s->sr & MCF_UART_TxEMP) == 0) { + if (s->chr) + qemu_chr_fe_write(s->chr, (unsigned char *)&s->tb, 1); + s->sr |= MCF_UART_TxEMP; + } + if (s->tx_enabled) { + s->sr |= MCF_UART_TxRDY; + } else { + s->sr &= ~MCF_UART_TxRDY; + } +} + +static void mcf_do_command(mcf_uart_state *s, uint8_t cmd) +{ + /* Misc command. */ + switch ((cmd >> 4) & 3) { + case 0: /* No-op. */ + break; + case 1: /* Reset mode register pointer. */ + s->current_mr = 0; + break; + case 2: /* Reset receiver. */ + s->rx_enabled = 0; + s->fifo_len = 0; + s->sr &= ~(MCF_UART_RxRDY | MCF_UART_FFULL); + break; + case 3: /* Reset transmitter. */ + s->tx_enabled = 0; + s->sr |= MCF_UART_TxEMP; + s->sr &= ~MCF_UART_TxRDY; + break; + case 4: /* Reset error status. */ + break; + case 5: /* Reset break-change interrupt. */ + s->isr &= ~MCF_UART_DBINT; + break; + case 6: /* Start break. */ + case 7: /* Stop break. */ + break; + } + + /* Transmitter command. */ + switch ((cmd >> 2) & 3) { + case 0: /* No-op. */ + break; + case 1: /* Enable. */ + s->tx_enabled = 1; + mcf_uart_do_tx(s); + break; + case 2: /* Disable. */ + s->tx_enabled = 0; + mcf_uart_do_tx(s); + break; + case 3: /* Reserved. */ + fprintf(stderr, "mcf_uart: Bad TX command\n"); + break; + } + + /* Receiver command. */ + switch (cmd & 3) { + case 0: /* No-op. */ + break; + case 1: /* Enable. */ + s->rx_enabled = 1; + break; + case 2: + s->rx_enabled = 0; + break; + case 3: /* Reserved. */ + fprintf(stderr, "mcf_uart: Bad RX command\n"); + break; + } +} + +void mcf_uart_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + mcf_uart_state *s = (mcf_uart_state *)opaque; + switch (addr & 0x3f) { + case 0x00: + s->mr[s->current_mr] = val; + s->current_mr = 1; + break; + case 0x04: + /* CSR is ignored. */ + break; + case 0x08: /* Command Register. */ + mcf_do_command(s, val); + break; + case 0x0c: /* Transmit Buffer. */ + s->sr &= ~MCF_UART_TxEMP; + s->tb = val; + mcf_uart_do_tx(s); + break; + case 0x10: + /* ACR is ignored. */ + break; + case 0x14: + s->imr = val; + break; + default: + break; + } + mcf_uart_update(s); +} + +static void mcf_uart_reset(mcf_uart_state *s) +{ + s->fifo_len = 0; + s->mr[0] = 0; + s->mr[1] = 0; + s->sr = MCF_UART_TxEMP; + s->tx_enabled = 0; + s->rx_enabled = 0; + s->isr = 0; + s->imr = 0; +} + +static void mcf_uart_push_byte(mcf_uart_state *s, uint8_t data) +{ + /* Break events overwrite the last byte if the fifo is full. */ + if (s->fifo_len == 4) + s->fifo_len--; + + s->fifo[s->fifo_len] = data; + s->fifo_len++; + s->sr |= MCF_UART_RxRDY; + if (s->fifo_len == 4) + s->sr |= MCF_UART_FFULL; + + mcf_uart_update(s); +} + +static void mcf_uart_event(void *opaque, int event) +{ + mcf_uart_state *s = (mcf_uart_state *)opaque; + + switch (event) { + case CHR_EVENT_BREAK: + s->isr |= MCF_UART_DBINT; + mcf_uart_push_byte(s, 0); + break; + default: + break; + } +} + +static int mcf_uart_can_receive(void *opaque) +{ + mcf_uart_state *s = (mcf_uart_state *)opaque; + + return s->rx_enabled && (s->sr & MCF_UART_FFULL) == 0; +} + +static void mcf_uart_receive(void *opaque, const uint8_t *buf, int size) +{ + mcf_uart_state *s = (mcf_uart_state *)opaque; + + mcf_uart_push_byte(s, buf[0]); +} + +void *mcf_uart_init(qemu_irq irq, CharDriverState *chr) +{ + mcf_uart_state *s; + + s = g_malloc0(sizeof(mcf_uart_state)); + s->chr = chr; + s->irq = irq; + if (chr) { + qemu_chr_fe_claim_no_fail(chr); + qemu_chr_add_handlers(chr, mcf_uart_can_receive, mcf_uart_receive, + mcf_uart_event, s); + } + mcf_uart_reset(s); + return s; +} + +static const MemoryRegionOps mcf_uart_ops = { + .read = mcf_uart_read, + .write = mcf_uart_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +void mcf_uart_mm_init(MemoryRegion *sysmem, + hwaddr base, + qemu_irq irq, + CharDriverState *chr) +{ + mcf_uart_state *s; + + s = mcf_uart_init(irq, chr); + memory_region_init_io(&s->iomem, &mcf_uart_ops, s, "uart", 0x40); + memory_region_add_subregion(sysmem, base, &s->iomem); +} diff --git a/hw/char/milkymist-uart.c b/hw/char/milkymist-uart.c new file mode 100644 index 0000000..f3bdf69 --- /dev/null +++ b/hw/char/milkymist-uart.c @@ -0,0 +1,244 @@ +/* + * QEMU model of the Milkymist UART block. + * + * Copyright (c) 2010 Michael Walle + * + * 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 . + * + * + * Specification available at: + * http://www.milkymist.org/socdoc/uart.pdf + */ + +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "trace.h" +#include "char/char.h" +#include "qemu/error-report.h" + +enum { + R_RXTX = 0, + R_DIV, + R_STAT, + R_CTRL, + R_DBG, + R_MAX +}; + +enum { + STAT_THRE = (1<<0), + STAT_RX_EVT = (1<<1), + STAT_TX_EVT = (1<<2), +}; + +enum { + CTRL_RX_IRQ_EN = (1<<0), + CTRL_TX_IRQ_EN = (1<<1), + CTRL_THRU_EN = (1<<2), +}; + +enum { + DBG_BREAK_EN = (1<<0), +}; + +struct MilkymistUartState { + SysBusDevice busdev; + MemoryRegion regs_region; + CharDriverState *chr; + qemu_irq irq; + + uint32_t regs[R_MAX]; +}; +typedef struct MilkymistUartState MilkymistUartState; + +static void uart_update_irq(MilkymistUartState *s) +{ + int rx_event = s->regs[R_STAT] & STAT_RX_EVT; + int tx_event = s->regs[R_STAT] & STAT_TX_EVT; + int rx_irq_en = s->regs[R_CTRL] & CTRL_RX_IRQ_EN; + int tx_irq_en = s->regs[R_CTRL] & CTRL_TX_IRQ_EN; + + if ((rx_irq_en && rx_event) || (tx_irq_en && tx_event)) { + trace_milkymist_uart_raise_irq(); + qemu_irq_raise(s->irq); + } else { + trace_milkymist_uart_lower_irq(); + qemu_irq_lower(s->irq); + } +} + +static uint64_t uart_read(void *opaque, hwaddr addr, + unsigned size) +{ + MilkymistUartState *s = opaque; + uint32_t r = 0; + + addr >>= 2; + switch (addr) { + case R_RXTX: + r = s->regs[addr]; + break; + case R_DIV: + case R_STAT: + case R_CTRL: + case R_DBG: + r = s->regs[addr]; + break; + + default: + error_report("milkymist_uart: read access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } + + trace_milkymist_uart_memory_read(addr << 2, r); + + return r; +} + +static void uart_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + MilkymistUartState *s = opaque; + unsigned char ch = value; + + trace_milkymist_uart_memory_write(addr, value); + + addr >>= 2; + switch (addr) { + case R_RXTX: + if (s->chr) { + qemu_chr_fe_write(s->chr, &ch, 1); + } + s->regs[R_STAT] |= STAT_TX_EVT; + break; + case R_DIV: + case R_CTRL: + case R_DBG: + s->regs[addr] = value; + break; + + case R_STAT: + /* write one to clear bits */ + s->regs[addr] &= ~(value & (STAT_RX_EVT | STAT_TX_EVT)); + qemu_chr_accept_input(s->chr); + break; + + default: + error_report("milkymist_uart: write access to unknown register 0x" + TARGET_FMT_plx, addr << 2); + break; + } + + uart_update_irq(s); +} + +static const MemoryRegionOps uart_mmio_ops = { + .read = uart_read, + .write = uart_write, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void uart_rx(void *opaque, const uint8_t *buf, int size) +{ + MilkymistUartState *s = opaque; + + assert(!(s->regs[R_STAT] & STAT_RX_EVT)); + + s->regs[R_STAT] |= STAT_RX_EVT; + s->regs[R_RXTX] = *buf; + + uart_update_irq(s); +} + +static int uart_can_rx(void *opaque) +{ + MilkymistUartState *s = opaque; + + return !(s->regs[R_STAT] & STAT_RX_EVT); +} + +static void uart_event(void *opaque, int event) +{ +} + +static void milkymist_uart_reset(DeviceState *d) +{ + MilkymistUartState *s = container_of(d, MilkymistUartState, busdev.qdev); + int i; + + for (i = 0; i < R_MAX; i++) { + s->regs[i] = 0; + } + + /* THRE is always set */ + s->regs[R_STAT] = STAT_THRE; +} + +static int milkymist_uart_init(SysBusDevice *dev) +{ + MilkymistUartState *s = FROM_SYSBUS(typeof(*s), dev); + + sysbus_init_irq(dev, &s->irq); + + memory_region_init_io(&s->regs_region, &uart_mmio_ops, s, + "milkymist-uart", R_MAX * 4); + sysbus_init_mmio(dev, &s->regs_region); + + s->chr = qemu_char_get_next_serial(); + if (s->chr) { + qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s); + } + + return 0; +} + +static const VMStateDescription vmstate_milkymist_uart = { + .name = "milkymist-uart", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, MilkymistUartState, R_MAX), + VMSTATE_END_OF_LIST() + } +}; + +static void milkymist_uart_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = milkymist_uart_init; + dc->reset = milkymist_uart_reset; + dc->vmsd = &vmstate_milkymist_uart; +} + +static const TypeInfo milkymist_uart_info = { + .name = "milkymist-uart", + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MilkymistUartState), + .class_init = milkymist_uart_class_init, +}; + +static void milkymist_uart_register_types(void) +{ + type_register_static(&milkymist_uart_info); +} + +type_init(milkymist_uart_register_types) diff --git a/hw/char/omap_uart.c b/hw/char/omap_uart.c new file mode 100644 index 0000000..26c1426 --- /dev/null +++ b/hw/char/omap_uart.c @@ -0,0 +1,187 @@ +/* + * TI OMAP processors UART emulation. + * + * Copyright (C) 2006-2008 Andrzej Zaborowski + * Copyright (C) 2007-2009 Nokia Corporation + * + * 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 or + * (at your option) version 3 of the License. + * + * 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 . + */ +#include "char/char.h" +#include "hw/hw.h" +#include "hw/arm/omap.h" +#include "hw/char/serial.h" +#include "exec/address-spaces.h" + +/* UARTs */ +struct omap_uart_s { + MemoryRegion iomem; + hwaddr base; + SerialState *serial; /* TODO */ + struct omap_target_agent_s *ta; + omap_clk fclk; + qemu_irq irq; + + uint8_t eblr; + uint8_t syscontrol; + uint8_t wkup; + uint8_t cfps; + uint8_t mdr[2]; + uint8_t scr; + uint8_t clksel; +}; + +void omap_uart_reset(struct omap_uart_s *s) +{ + s->eblr = 0x00; + s->syscontrol = 0; + s->wkup = 0x3f; + s->cfps = 0x69; + s->clksel = 0; +} + +struct omap_uart_s *omap_uart_init(hwaddr base, + qemu_irq irq, omap_clk fclk, omap_clk iclk, + qemu_irq txdma, qemu_irq rxdma, + const char *label, CharDriverState *chr) +{ + struct omap_uart_s *s = (struct omap_uart_s *) + g_malloc0(sizeof(struct omap_uart_s)); + + s->base = base; + s->fclk = fclk; + s->irq = irq; + s->serial = serial_mm_init(get_system_memory(), base, 2, irq, + omap_clk_getrate(fclk)/16, + chr ?: qemu_chr_new(label, "null", NULL), + DEVICE_NATIVE_ENDIAN); + return s; +} + +static uint64_t omap_uart_read(void *opaque, hwaddr addr, + unsigned size) +{ + struct omap_uart_s *s = (struct omap_uart_s *) opaque; + + if (size == 4) { + return omap_badwidth_read8(opaque, addr); + } + + switch (addr) { + case 0x20: /* MDR1 */ + return s->mdr[0]; + case 0x24: /* MDR2 */ + return s->mdr[1]; + case 0x40: /* SCR */ + return s->scr; + case 0x44: /* SSR */ + return 0x0; + case 0x48: /* EBLR (OMAP2) */ + return s->eblr; + case 0x4C: /* OSC_12M_SEL (OMAP1) */ + return s->clksel; + case 0x50: /* MVR */ + return 0x30; + case 0x54: /* SYSC (OMAP2) */ + return s->syscontrol; + case 0x58: /* SYSS (OMAP2) */ + return 1; + case 0x5c: /* WER (OMAP2) */ + return s->wkup; + case 0x60: /* CFPS (OMAP2) */ + return s->cfps; + } + + OMAP_BAD_REG(addr); + return 0; +} + +static void omap_uart_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + struct omap_uart_s *s = (struct omap_uart_s *) opaque; + + if (size == 4) { + return omap_badwidth_write8(opaque, addr, value); + } + + switch (addr) { + case 0x20: /* MDR1 */ + s->mdr[0] = value & 0x7f; + break; + case 0x24: /* MDR2 */ + s->mdr[1] = value & 0xff; + break; + case 0x40: /* SCR */ + s->scr = value & 0xff; + break; + case 0x48: /* EBLR (OMAP2) */ + s->eblr = value & 0xff; + break; + case 0x4C: /* OSC_12M_SEL (OMAP1) */ + s->clksel = value & 1; + break; + case 0x44: /* SSR */ + case 0x50: /* MVR */ + case 0x58: /* SYSS (OMAP2) */ + OMAP_RO_REG(addr); + break; + case 0x54: /* SYSC (OMAP2) */ + s->syscontrol = value & 0x1d; + if (value & 2) + omap_uart_reset(s); + break; + case 0x5c: /* WER (OMAP2) */ + s->wkup = value & 0x7f; + break; + case 0x60: /* CFPS (OMAP2) */ + s->cfps = value & 0xff; + break; + default: + OMAP_BAD_REG(addr); + } +} + +static const MemoryRegionOps omap_uart_ops = { + .read = omap_uart_read, + .write = omap_uart_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +struct omap_uart_s *omap2_uart_init(MemoryRegion *sysmem, + struct omap_target_agent_s *ta, + qemu_irq irq, omap_clk fclk, omap_clk iclk, + qemu_irq txdma, qemu_irq rxdma, + const char *label, CharDriverState *chr) +{ + hwaddr base = omap_l4_attach(ta, 0, NULL); + struct omap_uart_s *s = omap_uart_init(base, irq, + fclk, iclk, txdma, rxdma, label, chr); + + memory_region_init_io(&s->iomem, &omap_uart_ops, s, "omap.uart", 0x100); + + s->ta = ta; + + memory_region_add_subregion(sysmem, base + 0x20, &s->iomem); + + return s; +} + +void omap_uart_attach(struct omap_uart_s *s, CharDriverState *chr) +{ + /* TODO: Should reuse or destroy current s->serial */ + s->serial = serial_mm_init(get_system_memory(), s->base, 2, s->irq, + omap_clk_getrate(s->fclk) / 16, + chr ?: qemu_chr_new("null", "null", NULL), + DEVICE_NATIVE_ENDIAN); +} diff --git a/hw/char/sclpconsole.c b/hw/char/sclpconsole.c new file mode 100644 index 0000000..42ed54c --- /dev/null +++ b/hw/char/sclpconsole.c @@ -0,0 +1,305 @@ +/* + * SCLP event type + * Ascii Console Data (VT220 Console) + * + * Copyright IBM, Corp. 2012 + * + * Authors: + * Heinz Graalfs + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at your + * option) any later version. See the COPYING file in the top-level directory. + * + */ + +#include +#include "qemu/thread.h" +#include "qemu/error-report.h" + +#include "hw/s390x/sclp.h" +#include "hw/s390x/event-facility.h" +#include "char/char.h" + +typedef struct ASCIIConsoleData { + EventBufferHeader ebh; + char data[0]; +} QEMU_PACKED ASCIIConsoleData; + +/* max size for ASCII data in 4K SCCB page */ +#define SIZE_BUFFER_VT220 4080 + +typedef struct SCLPConsole { + SCLPEvent event; + CharDriverState *chr; + /* io vector */ + uint8_t *iov; /* iov buffer pointer */ + uint8_t *iov_sclp; /* pointer to SCLP read offset */ + uint8_t *iov_bs; /* pointer byte stream read offset */ + uint32_t iov_data_len; /* length of byte stream in buffer */ + uint32_t iov_sclp_rest; /* length of byte stream not read via SCLP */ + qemu_irq irq_read_vt220; +} SCLPConsole; + +/* character layer call-back functions */ + +/* Return number of bytes that fit into iov buffer */ +static int chr_can_read(void *opaque) +{ + SCLPConsole *scon = opaque; + + return scon->iov ? SIZE_BUFFER_VT220 - scon->iov_data_len : 0; +} + +/* Receive n bytes from character layer, save in iov buffer, + * and set event pending */ +static void receive_from_chr_layer(SCLPConsole *scon, const uint8_t *buf, + int size) +{ + assert(scon->iov); + + /* read data must fit into current buffer */ + assert(size <= SIZE_BUFFER_VT220 - scon->iov_data_len); + + /* put byte-stream from character layer into buffer */ + memcpy(scon->iov_bs, buf, size); + scon->iov_data_len += size; + scon->iov_sclp_rest += size; + scon->iov_bs += size; + scon->event.event_pending = true; +} + +/* Send data from a char device over to the guest */ +static void chr_read(void *opaque, const uint8_t *buf, int size) +{ + SCLPConsole *scon = opaque; + + assert(scon); + + receive_from_chr_layer(scon, buf, size); + /* trigger SCLP read operation */ + qemu_irq_raise(scon->irq_read_vt220); +} + +static void chr_event(void *opaque, int event) +{ + SCLPConsole *scon = opaque; + + switch (event) { + case CHR_EVENT_OPENED: + if (!scon->iov) { + scon->iov = g_malloc0(SIZE_BUFFER_VT220); + scon->iov_sclp = scon->iov; + scon->iov_bs = scon->iov; + scon->iov_data_len = 0; + scon->iov_sclp_rest = 0; + } + break; + case CHR_EVENT_CLOSED: + if (scon->iov) { + g_free(scon->iov); + scon->iov = NULL; + } + break; + } +} + +/* functions to be called by event facility */ + +static int event_type(void) +{ + return SCLP_EVENT_ASCII_CONSOLE_DATA; +} + +static unsigned int send_mask(void) +{ + return SCLP_EVENT_MASK_MSG_ASCII; +} + +static unsigned int receive_mask(void) +{ + return SCLP_EVENT_MASK_MSG_ASCII; +} + +/* triggered by SCLP's read_event_data - + * copy console data byte-stream into provided (SCLP) buffer + */ +static void get_console_data(SCLPEvent *event, uint8_t *buf, size_t *size, + int avail) +{ + SCLPConsole *cons = DO_UPCAST(SCLPConsole, event, event); + + /* first byte is hex 0 saying an ascii string follows */ + *buf++ = '\0'; + avail--; + /* if all data fit into provided SCLP buffer */ + if (avail >= cons->iov_sclp_rest) { + /* copy character byte-stream to SCLP buffer */ + memcpy(buf, cons->iov_sclp, cons->iov_sclp_rest); + *size = cons->iov_sclp_rest + 1; + cons->iov_sclp = cons->iov; + cons->iov_bs = cons->iov; + cons->iov_data_len = 0; + cons->iov_sclp_rest = 0; + event->event_pending = false; + /* data provided and no more data pending */ + } else { + /* if provided buffer is too small, just copy part */ + memcpy(buf, cons->iov_sclp, avail); + *size = avail + 1; + cons->iov_sclp_rest -= avail; + cons->iov_sclp += avail; + /* more data pending */ + } +} + +static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr, + int *slen) +{ + int avail; + size_t src_len; + uint8_t *to; + ASCIIConsoleData *acd = (ASCIIConsoleData *) evt_buf_hdr; + + if (!event->event_pending) { + /* no data pending */ + return 0; + } + + to = (uint8_t *)&acd->data; + avail = *slen - sizeof(ASCIIConsoleData); + get_console_data(event, to, &src_len, avail); + + acd->ebh.length = cpu_to_be16(sizeof(ASCIIConsoleData) + src_len); + acd->ebh.type = SCLP_EVENT_ASCII_CONSOLE_DATA; + acd->ebh.flags |= SCLP_EVENT_BUFFER_ACCEPTED; + *slen = avail - src_len; + + return 1; +} + +/* triggered by SCLP's write_event_data + * - write console data to character layer + * returns < 0 if an error occurred + */ +static ssize_t write_console_data(SCLPEvent *event, const uint8_t *buf, + size_t len) +{ + ssize_t ret = 0; + const uint8_t *iov_offset; + SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event); + + if (!scon->chr) { + /* If there's no backend, we can just say we consumed all data. */ + return len; + } + + iov_offset = buf; + while (len > 0) { + ret = qemu_chr_fe_write(scon->chr, buf, len); + if (ret == 0) { + /* a pty doesn't seem to be connected - no error */ + len = 0; + } else if (ret == -EAGAIN || (ret > 0 && ret < len)) { + len -= ret; + iov_offset += ret; + } else { + len = 0; + } + } + + return ret; +} + +static int write_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr) +{ + int rc; + int length; + ssize_t written; + ASCIIConsoleData *acd = (ASCIIConsoleData *) evt_buf_hdr; + + length = be16_to_cpu(evt_buf_hdr->length) - sizeof(EventBufferHeader); + written = write_console_data(event, (uint8_t *)acd->data, length); + + rc = SCLP_RC_NORMAL_COMPLETION; + /* set event buffer accepted flag */ + evt_buf_hdr->flags |= SCLP_EVENT_BUFFER_ACCEPTED; + + /* written will be zero if a pty is not connected - don't treat as error */ + if (written < 0) { + /* event buffer not accepted due to error in character layer */ + evt_buf_hdr->flags &= ~(SCLP_EVENT_BUFFER_ACCEPTED); + rc = SCLP_RC_CONTAINED_EQUIPMENT_CHECK; + } + + return rc; +} + +static void trigger_ascii_console_data(void *opaque, int n, int level) +{ + sclp_service_interrupt(0); +} + +/* qemu object creation and initialization functions */ + +/* tell character layer our call-back functions */ +static int console_init(SCLPEvent *event) +{ + static bool console_available; + + SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event); + + if (console_available) { + error_report("Multiple VT220 operator consoles are not supported"); + return -1; + } + console_available = true; + event->event_type = SCLP_EVENT_ASCII_CONSOLE_DATA; + if (scon->chr) { + qemu_chr_add_handlers(scon->chr, chr_can_read, + chr_read, chr_event, scon); + } + scon->irq_read_vt220 = *qemu_allocate_irqs(trigger_ascii_console_data, + NULL, 1); + + return 0; +} + +static int console_exit(SCLPEvent *event) +{ + return 0; +} + +static Property console_properties[] = { + DEFINE_PROP_CHR("chardev", SCLPConsole, chr), + DEFINE_PROP_END_OF_LIST(), +}; + +static void console_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SCLPEventClass *ec = SCLP_EVENT_CLASS(klass); + + dc->props = console_properties; + ec->init = console_init; + ec->exit = console_exit; + ec->get_send_mask = send_mask; + ec->get_receive_mask = receive_mask; + ec->event_type = event_type; + ec->read_event_data = read_event_data; + ec->write_event_data = write_event_data; +} + +static const TypeInfo sclp_console_info = { + .name = "sclpconsole", + .parent = TYPE_SCLP_EVENT, + .instance_size = sizeof(SCLPConsole), + .class_init = console_class_init, + .class_size = sizeof(SCLPEventClass), +}; + +static void register_types(void) +{ + type_register_static(&sclp_console_info); +} + +type_init(register_types) diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c new file mode 100644 index 0000000..450c7d8 --- /dev/null +++ b/hw/char/sh_serial.c @@ -0,0 +1,410 @@ +/* + * QEMU SCI/SCIF serial port emulation + * + * Copyright (c) 2007 Magnus Damm + * + * Based on serial.c - QEMU 16450 UART emulation + * Copyright (c) 2003-2004 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/hw.h" +#include "hw/sh4/sh.h" +#include "char/char.h" +#include "exec/address-spaces.h" + +//#define DEBUG_SERIAL + +#define SH_SERIAL_FLAG_TEND (1 << 0) +#define SH_SERIAL_FLAG_TDE (1 << 1) +#define SH_SERIAL_FLAG_RDF (1 << 2) +#define SH_SERIAL_FLAG_BRK (1 << 3) +#define SH_SERIAL_FLAG_DR (1 << 4) + +#define SH_RX_FIFO_LENGTH (16) + +typedef struct { + MemoryRegion iomem; + MemoryRegion iomem_p4; + MemoryRegion iomem_a7; + uint8_t smr; + uint8_t brr; + uint8_t scr; + uint8_t dr; /* ftdr / tdr */ + uint8_t sr; /* fsr / ssr */ + uint16_t fcr; + uint8_t sptr; + + uint8_t rx_fifo[SH_RX_FIFO_LENGTH]; /* frdr / rdr */ + uint8_t rx_cnt; + uint8_t rx_tail; + uint8_t rx_head; + + int freq; + int feat; + int flags; + int rtrg; + + CharDriverState *chr; + + qemu_irq eri; + qemu_irq rxi; + qemu_irq txi; + qemu_irq tei; + qemu_irq bri; +} sh_serial_state; + +static void sh_serial_clear_fifo(sh_serial_state * s) +{ + memset(s->rx_fifo, 0, SH_RX_FIFO_LENGTH); + s->rx_cnt = 0; + s->rx_head = 0; + s->rx_tail = 0; +} + +static void sh_serial_write(void *opaque, hwaddr offs, + uint64_t val, unsigned size) +{ + sh_serial_state *s = opaque; + unsigned char ch; + +#ifdef DEBUG_SERIAL + printf("sh_serial: write offs=0x%02x val=0x%02x\n", + offs, val); +#endif + switch(offs) { + case 0x00: /* SMR */ + s->smr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0x7b : 0xff); + return; + case 0x04: /* BRR */ + s->brr = val; + return; + case 0x08: /* SCR */ + /* TODO : For SH7751, SCIF mask should be 0xfb. */ + s->scr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0xfa : 0xff); + if (!(val & (1 << 5))) + s->flags |= SH_SERIAL_FLAG_TEND; + if ((s->feat & SH_SERIAL_FEAT_SCIF) && s->txi) { + qemu_set_irq(s->txi, val & (1 << 7)); + } + if (!(val & (1 << 6))) { + qemu_set_irq(s->rxi, 0); + } + return; + case 0x0c: /* FTDR / TDR */ + if (s->chr) { + ch = val; + qemu_chr_fe_write(s->chr, &ch, 1); + } + s->dr = val; + s->flags &= ~SH_SERIAL_FLAG_TDE; + return; +#if 0 + case 0x14: /* FRDR / RDR */ + ret = 0; + break; +#endif + } + if (s->feat & SH_SERIAL_FEAT_SCIF) { + switch(offs) { + case 0x10: /* FSR */ + if (!(val & (1 << 6))) + s->flags &= ~SH_SERIAL_FLAG_TEND; + if (!(val & (1 << 5))) + s->flags &= ~SH_SERIAL_FLAG_TDE; + if (!(val & (1 << 4))) + s->flags &= ~SH_SERIAL_FLAG_BRK; + if (!(val & (1 << 1))) + s->flags &= ~SH_SERIAL_FLAG_RDF; + if (!(val & (1 << 0))) + s->flags &= ~SH_SERIAL_FLAG_DR; + + if (!(val & (1 << 1)) || !(val & (1 << 0))) { + if (s->rxi) { + qemu_set_irq(s->rxi, 0); + } + } + return; + case 0x18: /* FCR */ + s->fcr = val; + switch ((val >> 6) & 3) { + case 0: + s->rtrg = 1; + break; + case 1: + s->rtrg = 4; + break; + case 2: + s->rtrg = 8; + break; + case 3: + s->rtrg = 14; + break; + } + if (val & (1 << 1)) { + sh_serial_clear_fifo(s); + s->sr &= ~(1 << 1); + } + + return; + case 0x20: /* SPTR */ + s->sptr = val & 0xf3; + return; + case 0x24: /* LSR */ + return; + } + } + else { + switch(offs) { +#if 0 + case 0x0c: + ret = s->dr; + break; + case 0x10: + ret = 0; + break; +#endif + case 0x1c: + s->sptr = val & 0x8f; + return; + } + } + + fprintf(stderr, "sh_serial: unsupported write to 0x%02" + HWADDR_PRIx "\n", offs); + abort(); +} + +static uint64_t sh_serial_read(void *opaque, hwaddr offs, + unsigned size) +{ + sh_serial_state *s = opaque; + uint32_t ret = ~0; + +#if 0 + switch(offs) { + case 0x00: + ret = s->smr; + break; + case 0x04: + ret = s->brr; + break; + case 0x08: + ret = s->scr; + break; + case 0x14: + ret = 0; + break; + } +#endif + if (s->feat & SH_SERIAL_FEAT_SCIF) { + switch(offs) { + case 0x00: /* SMR */ + ret = s->smr; + break; + case 0x08: /* SCR */ + ret = s->scr; + break; + case 0x10: /* FSR */ + ret = 0; + if (s->flags & SH_SERIAL_FLAG_TEND) + ret |= (1 << 6); + if (s->flags & SH_SERIAL_FLAG_TDE) + ret |= (1 << 5); + if (s->flags & SH_SERIAL_FLAG_BRK) + ret |= (1 << 4); + if (s->flags & SH_SERIAL_FLAG_RDF) + ret |= (1 << 1); + if (s->flags & SH_SERIAL_FLAG_DR) + ret |= (1 << 0); + + if (s->scr & (1 << 5)) + s->flags |= SH_SERIAL_FLAG_TDE | SH_SERIAL_FLAG_TEND; + + break; + case 0x14: + if (s->rx_cnt > 0) { + ret = s->rx_fifo[s->rx_tail++]; + s->rx_cnt--; + if (s->rx_tail == SH_RX_FIFO_LENGTH) + s->rx_tail = 0; + if (s->rx_cnt < s->rtrg) + s->flags &= ~SH_SERIAL_FLAG_RDF; + } + break; +#if 0 + case 0x18: + ret = s->fcr; + break; +#endif + case 0x1c: + ret = s->rx_cnt; + break; + case 0x20: + ret = s->sptr; + break; + case 0x24: + ret = 0; + break; + } + } + else { + switch(offs) { +#if 0 + case 0x0c: + ret = s->dr; + break; + case 0x10: + ret = 0; + break; + case 0x14: + ret = s->rx_fifo[0]; + break; +#endif + case 0x1c: + ret = s->sptr; + break; + } + } +#ifdef DEBUG_SERIAL + printf("sh_serial: read offs=0x%02x val=0x%x\n", + offs, ret); +#endif + + if (ret & ~((1 << 16) - 1)) { + fprintf(stderr, "sh_serial: unsupported read from 0x%02" + HWADDR_PRIx "\n", offs); + abort(); + } + + return ret; +} + +static int sh_serial_can_receive(sh_serial_state *s) +{ + return s->scr & (1 << 4); +} + +static void sh_serial_receive_break(sh_serial_state *s) +{ + if (s->feat & SH_SERIAL_FEAT_SCIF) + s->sr |= (1 << 4); +} + +static int sh_serial_can_receive1(void *opaque) +{ + sh_serial_state *s = opaque; + return sh_serial_can_receive(s); +} + +static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size) +{ + sh_serial_state *s = opaque; + + if (s->feat & SH_SERIAL_FEAT_SCIF) { + int i; + for (i = 0; i < size; i++) { + if (s->rx_cnt < SH_RX_FIFO_LENGTH) { + s->rx_fifo[s->rx_head++] = buf[i]; + if (s->rx_head == SH_RX_FIFO_LENGTH) { + s->rx_head = 0; + } + s->rx_cnt++; + if (s->rx_cnt >= s->rtrg) { + s->flags |= SH_SERIAL_FLAG_RDF; + if (s->scr & (1 << 6) && s->rxi) { + qemu_set_irq(s->rxi, 1); + } + } + } + } + } else { + s->rx_fifo[0] = buf[0]; + } +} + +static void sh_serial_event(void *opaque, int event) +{ + sh_serial_state *s = opaque; + if (event == CHR_EVENT_BREAK) + sh_serial_receive_break(s); +} + +static const MemoryRegionOps sh_serial_ops = { + .read = sh_serial_read, + .write = sh_serial_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +void sh_serial_init(MemoryRegion *sysmem, + hwaddr base, int feat, + uint32_t freq, CharDriverState *chr, + qemu_irq eri_source, + qemu_irq rxi_source, + qemu_irq txi_source, + qemu_irq tei_source, + qemu_irq bri_source) +{ + sh_serial_state *s; + + s = g_malloc0(sizeof(sh_serial_state)); + + s->feat = feat; + s->flags = SH_SERIAL_FLAG_TEND | SH_SERIAL_FLAG_TDE; + s->rtrg = 1; + + s->smr = 0; + s->brr = 0xff; + s->scr = 1 << 5; /* pretend that TX is enabled so early printk works */ + s->sptr = 0; + + if (feat & SH_SERIAL_FEAT_SCIF) { + s->fcr = 0; + } + else { + s->dr = 0xff; + } + + sh_serial_clear_fifo(s); + + memory_region_init_io(&s->iomem, &sh_serial_ops, s, + "serial", 0x100000000ULL); + + memory_region_init_alias(&s->iomem_p4, "serial-p4", &s->iomem, + 0, 0x28); + memory_region_add_subregion(sysmem, P4ADDR(base), &s->iomem_p4); + + memory_region_init_alias(&s->iomem_a7, "serial-a7", &s->iomem, + 0, 0x28); + memory_region_add_subregion(sysmem, A7ADDR(base), &s->iomem_a7); + + s->chr = chr; + + if (chr) { + qemu_chr_fe_claim_no_fail(chr); + qemu_chr_add_handlers(chr, sh_serial_can_receive1, sh_serial_receive1, + sh_serial_event, s); + } + + s->eri = eri_source; + s->rxi = rxi_source; + s->txi = txi_source; + s->tei = tei_source; + s->bri = bri_source; +} diff --git a/hw/char/spapr_vty.c b/hw/char/spapr_vty.c new file mode 100644 index 0000000..9df018a --- /dev/null +++ b/hw/char/spapr_vty.c @@ -0,0 +1,221 @@ +#include "hw/qdev.h" +#include "char/char.h" +#include "hw/ppc/spapr.h" +#include "hw/ppc/spapr_vio.h" + +#define VTERM_BUFSIZE 16 + +typedef struct VIOsPAPRVTYDevice { + VIOsPAPRDevice sdev; + CharDriverState *chardev; + uint32_t in, out; + uint8_t buf[VTERM_BUFSIZE]; +} VIOsPAPRVTYDevice; + +static int vty_can_receive(void *opaque) +{ + VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)opaque; + + return (dev->in - dev->out) < VTERM_BUFSIZE; +} + +static void vty_receive(void *opaque, const uint8_t *buf, int size) +{ + VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)opaque; + int i; + + if ((dev->in == dev->out) && size) { + /* toggle line to simulate edge interrupt */ + qemu_irq_pulse(spapr_vio_qirq(&dev->sdev)); + } + for (i = 0; i < size; i++) { + assert((dev->in - dev->out) < VTERM_BUFSIZE); + dev->buf[dev->in++ % VTERM_BUFSIZE] = buf[i]; + } +} + +static int vty_getchars(VIOsPAPRDevice *sdev, uint8_t *buf, int max) +{ + VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)sdev; + int n = 0; + + while ((n < max) && (dev->out != dev->in)) { + buf[n++] = dev->buf[dev->out++ % VTERM_BUFSIZE]; + } + + return n; +} + +void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int len) +{ + VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)sdev; + + /* FIXME: should check the qemu_chr_fe_write() return value */ + qemu_chr_fe_write(dev->chardev, buf, len); +} + +static int spapr_vty_init(VIOsPAPRDevice *sdev) +{ + VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)sdev; + + if (!dev->chardev) { + fprintf(stderr, "spapr-vty: Can't create vty without a chardev!\n"); + exit(1); + } + + qemu_chr_add_handlers(dev->chardev, vty_can_receive, + vty_receive, NULL, dev); + + return 0; +} + +/* Forward declaration */ +static target_ulong h_put_term_char(PowerPCCPU *cpu, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong reg = args[0]; + target_ulong len = args[1]; + target_ulong char0_7 = args[2]; + target_ulong char8_15 = args[3]; + VIOsPAPRDevice *sdev; + uint8_t buf[16]; + + sdev = vty_lookup(spapr, reg); + if (!sdev) { + return H_PARAMETER; + } + + if (len > 16) { + return H_PARAMETER; + } + + *((uint64_t *)buf) = cpu_to_be64(char0_7); + *((uint64_t *)buf + 1) = cpu_to_be64(char8_15); + + vty_putchars(sdev, buf, len); + + return H_SUCCESS; +} + +static target_ulong h_get_term_char(PowerPCCPU *cpu, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong reg = args[0]; + target_ulong *len = args + 0; + target_ulong *char0_7 = args + 1; + target_ulong *char8_15 = args + 2; + VIOsPAPRDevice *sdev; + uint8_t buf[16]; + + sdev = vty_lookup(spapr, reg); + if (!sdev) { + return H_PARAMETER; + } + + *len = vty_getchars(sdev, buf, sizeof(buf)); + if (*len < 16) { + memset(buf + *len, 0, 16 - *len); + } + + *char0_7 = be64_to_cpu(*((uint64_t *)buf)); + *char8_15 = be64_to_cpu(*((uint64_t *)buf + 1)); + + return H_SUCCESS; +} + +void spapr_vty_create(VIOsPAPRBus *bus, CharDriverState *chardev) +{ + DeviceState *dev; + + dev = qdev_create(&bus->bus, "spapr-vty"); + qdev_prop_set_chr(dev, "chardev", chardev); + qdev_init_nofail(dev); +} + +static Property spapr_vty_properties[] = { + DEFINE_SPAPR_PROPERTIES(VIOsPAPRVTYDevice, sdev), + DEFINE_PROP_CHR("chardev", VIOsPAPRVTYDevice, chardev), + DEFINE_PROP_END_OF_LIST(), +}; + +static void spapr_vty_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VIOsPAPRDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); + + k->init = spapr_vty_init; + k->dt_name = "vty"; + k->dt_type = "serial"; + k->dt_compatible = "hvterm1"; + dc->props = spapr_vty_properties; +} + +static const TypeInfo spapr_vty_info = { + .name = "spapr-vty", + .parent = TYPE_VIO_SPAPR_DEVICE, + .instance_size = sizeof(VIOsPAPRVTYDevice), + .class_init = spapr_vty_class_init, +}; + +VIOsPAPRDevice *spapr_vty_get_default(VIOsPAPRBus *bus) +{ + VIOsPAPRDevice *sdev, *selected; + BusChild *kid; + + /* + * To avoid the console bouncing around we want one VTY to be + * the "default". We haven't really got anything to go on, so + * arbitrarily choose the one with the lowest reg value. + */ + + selected = NULL; + QTAILQ_FOREACH(kid, &bus->bus.children, sibling) { + DeviceState *iter = kid->child; + + /* Only look at VTY devices */ + if (!object_dynamic_cast(OBJECT(iter), "spapr-vty")) { + continue; + } + + sdev = DO_UPCAST(VIOsPAPRDevice, qdev, iter); + + /* First VTY we've found, so it is selected for now */ + if (!selected) { + selected = sdev; + continue; + } + + /* Choose VTY with lowest reg value */ + if (sdev->reg < selected->reg) { + selected = sdev; + } + } + + return selected; +} + +VIOsPAPRDevice *vty_lookup(sPAPREnvironment *spapr, target_ulong reg) +{ + VIOsPAPRDevice *sdev; + + sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); + if (!sdev && reg == 0) { + /* Hack for kernel early debug, which always specifies reg==0. + * We search all VIO devices, and grab the vty with the lowest + * reg. This attempts to mimic existing PowerVM behaviour + * (early debug does work there, despite having no vty with + * reg==0. */ + return spapr_vty_get_default(spapr->vio_bus); + } + + return sdev; +} + +static void spapr_vty_register_types(void) +{ + spapr_register_hypercall(H_PUT_TERM_CHAR, h_put_term_char); + spapr_register_hypercall(H_GET_TERM_CHAR, h_get_term_char); + type_register_static(&spapr_vty_info); +} + +type_init(spapr_vty_register_types) diff --git a/hw/cris/Makefile.objs b/hw/cris/Makefile.objs index e02365d..c4d5189 100644 --- a/hw/cris/Makefile.objs +++ b/hw/cris/Makefile.objs @@ -2,7 +2,6 @@ obj-y += etraxfs_dma.o obj-y += etraxfs_pic.o obj-y += etraxfs_timer.o -obj-y += etraxfs_ser.o obj-y := $(addprefix ../,$(obj-y)) diff --git a/hw/debugcon.c b/hw/debugcon.c deleted file mode 100644 index 0588eeb..0000000 --- a/hw/debugcon.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - * QEMU Bochs-style debug console ("port E9") emulation - * - * Copyright (c) 2003-2004 Fabrice Bellard - * Copyright (c) 2008 Citrix Systems, Inc. - * Copyright (c) Intel Corporation; author: H. Peter Anvin - * - * 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/hw.h" -#include "char/char.h" -#include "hw/isa/isa.h" -#include "hw/i386/pc.h" - -#define TYPE_ISA_DEBUGCON_DEVICE "isa-debugcon" -#define ISA_DEBUGCON_DEVICE(obj) \ - OBJECT_CHECK(ISADebugconState, (obj), TYPE_ISA_DEBUGCON_DEVICE) - -//#define DEBUG_DEBUGCON - -typedef struct DebugconState { - MemoryRegion io; - CharDriverState *chr; - uint32_t readback; -} DebugconState; - -typedef struct ISADebugconState { - ISADevice parent_obj; - - uint32_t iobase; - DebugconState state; -} ISADebugconState; - -static void debugcon_ioport_write(void *opaque, hwaddr addr, uint64_t val, - unsigned width) -{ - DebugconState *s = opaque; - unsigned char ch = val; - -#ifdef DEBUG_DEBUGCON - printf("debugcon: write addr=0x%04x val=0x%02x\n", addr, val); -#endif - - qemu_chr_fe_write(s->chr, &ch, 1); -} - - -static uint64_t debugcon_ioport_read(void *opaque, hwaddr addr, unsigned width) -{ - DebugconState *s = opaque; - -#ifdef DEBUG_DEBUGCON - printf("debugcon: read addr=0x%04x\n", addr); -#endif - - return s->readback; -} - -static const MemoryRegionOps debugcon_ops = { - .read = debugcon_ioport_read, - .write = debugcon_ioport_write, - .valid.min_access_size = 1, - .valid.max_access_size = 1, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void debugcon_init_core(DebugconState *s) -{ - if (!s->chr) { - fprintf(stderr, "Can't create debugcon device, empty char device\n"); - exit(1); - } - - qemu_chr_add_handlers(s->chr, NULL, NULL, NULL, s); -} - -static int debugcon_isa_initfn(ISADevice *dev) -{ - ISADebugconState *isa = ISA_DEBUGCON_DEVICE(dev); - DebugconState *s = &isa->state; - - debugcon_init_core(s); - memory_region_init_io(&s->io, &debugcon_ops, s, - TYPE_ISA_DEBUGCON_DEVICE, 1); - memory_region_add_subregion(isa_address_space_io(dev), - isa->iobase, &s->io); - return 0; -} - -static Property debugcon_isa_properties[] = { - DEFINE_PROP_HEX32("iobase", ISADebugconState, iobase, 0xe9), - DEFINE_PROP_CHR("chardev", ISADebugconState, state.chr), - DEFINE_PROP_HEX32("readback", ISADebugconState, state.readback, 0xe9), - DEFINE_PROP_END_OF_LIST(), -}; - -static void debugcon_isa_class_initfn(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - ISADeviceClass *ic = ISA_DEVICE_CLASS(klass); - ic->init = debugcon_isa_initfn; - dc->props = debugcon_isa_properties; -} - -static const TypeInfo debugcon_isa_info = { - .name = TYPE_ISA_DEBUGCON_DEVICE, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(ISADebugconState), - .class_init = debugcon_isa_class_initfn, -}; - -static void debugcon_register_types(void) -{ - type_register_static(&debugcon_isa_info); -} - -type_init(debugcon_register_types) diff --git a/hw/etraxfs_ser.c b/hw/etraxfs_ser.c deleted file mode 100644 index 7e24d34..0000000 --- a/hw/etraxfs_ser.c +++ /dev/null @@ -1,248 +0,0 @@ -/* - * QEMU ETRAX System Emulator - * - * Copyright (c) 2007 Edgar E. Iglesias, Axis Communications AB. - * - * 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/sysbus.h" -#include "char/char.h" -#include "qemu/log.h" - -#define D(x) - -#define RW_TR_CTRL (0x00 / 4) -#define RW_TR_DMA_EN (0x04 / 4) -#define RW_REC_CTRL (0x08 / 4) -#define RW_DOUT (0x1c / 4) -#define RS_STAT_DIN (0x20 / 4) -#define R_STAT_DIN (0x24 / 4) -#define RW_INTR_MASK (0x2c / 4) -#define RW_ACK_INTR (0x30 / 4) -#define R_INTR (0x34 / 4) -#define R_MASKED_INTR (0x38 / 4) -#define R_MAX (0x3c / 4) - -#define STAT_DAV 16 -#define STAT_TR_IDLE 22 -#define STAT_TR_RDY 24 - -struct etrax_serial -{ - SysBusDevice busdev; - MemoryRegion mmio; - CharDriverState *chr; - qemu_irq irq; - - int pending_tx; - - uint8_t rx_fifo[16]; - unsigned int rx_fifo_pos; - unsigned int rx_fifo_len; - - /* Control registers. */ - uint32_t regs[R_MAX]; -}; - -static void ser_update_irq(struct etrax_serial *s) -{ - - if (s->rx_fifo_len) { - s->regs[R_INTR] |= 8; - } else { - s->regs[R_INTR] &= ~8; - } - - s->regs[R_MASKED_INTR] = s->regs[R_INTR] & s->regs[RW_INTR_MASK]; - qemu_set_irq(s->irq, !!s->regs[R_MASKED_INTR]); -} - -static uint64_t -ser_read(void *opaque, hwaddr addr, unsigned int size) -{ - struct etrax_serial *s = opaque; - D(CPUCRISState *env = s->env); - uint32_t r = 0; - - addr >>= 2; - switch (addr) - { - case R_STAT_DIN: - r = s->rx_fifo[(s->rx_fifo_pos - s->rx_fifo_len) & 15]; - if (s->rx_fifo_len) { - r |= 1 << STAT_DAV; - } - r |= 1 << STAT_TR_RDY; - r |= 1 << STAT_TR_IDLE; - break; - case RS_STAT_DIN: - r = s->rx_fifo[(s->rx_fifo_pos - s->rx_fifo_len) & 15]; - if (s->rx_fifo_len) { - r |= 1 << STAT_DAV; - s->rx_fifo_len--; - } - r |= 1 << STAT_TR_RDY; - r |= 1 << STAT_TR_IDLE; - break; - default: - r = s->regs[addr]; - D(qemu_log("%s " TARGET_FMT_plx "=%x\n", __func__, addr, r)); - break; - } - return r; -} - -static void -ser_write(void *opaque, hwaddr addr, - uint64_t val64, unsigned int size) -{ - struct etrax_serial *s = opaque; - uint32_t value = val64; - unsigned char ch = val64; - D(CPUCRISState *env = s->env); - - D(qemu_log("%s " TARGET_FMT_plx "=%x\n", __func__, addr, value)); - addr >>= 2; - switch (addr) - { - case RW_DOUT: - qemu_chr_fe_write(s->chr, &ch, 1); - s->regs[R_INTR] |= 3; - s->pending_tx = 1; - s->regs[addr] = value; - break; - case RW_ACK_INTR: - if (s->pending_tx) { - value &= ~1; - s->pending_tx = 0; - D(qemu_log("fixedup value=%x r_intr=%x\n", - value, s->regs[R_INTR])); - } - s->regs[addr] = value; - s->regs[R_INTR] &= ~value; - D(printf("r_intr=%x\n", s->regs[R_INTR])); - break; - default: - s->regs[addr] = value; - break; - } - ser_update_irq(s); -} - -static const MemoryRegionOps ser_ops = { - .read = ser_read, - .write = ser_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4 - } -}; - -static void serial_receive(void *opaque, const uint8_t *buf, int size) -{ - struct etrax_serial *s = opaque; - int i; - - /* Got a byte. */ - if (s->rx_fifo_len >= 16) { - qemu_log("WARNING: UART dropped char.\n"); - return; - } - - for (i = 0; i < size; i++) { - s->rx_fifo[s->rx_fifo_pos] = buf[i]; - s->rx_fifo_pos++; - s->rx_fifo_pos &= 15; - s->rx_fifo_len++; - } - - ser_update_irq(s); -} - -static int serial_can_receive(void *opaque) -{ - struct etrax_serial *s = opaque; - int r; - - /* Is the receiver enabled? */ - if (!(s->regs[RW_REC_CTRL] & (1 << 3))) { - return 0; - } - - r = sizeof(s->rx_fifo) - s->rx_fifo_len; - return r; -} - -static void serial_event(void *opaque, int event) -{ - -} - -static void etraxfs_ser_reset(DeviceState *d) -{ - struct etrax_serial *s = container_of(d, typeof(*s), busdev.qdev); - - /* transmitter begins ready and idle. */ - s->regs[RS_STAT_DIN] |= (1 << STAT_TR_RDY); - s->regs[RS_STAT_DIN] |= (1 << STAT_TR_IDLE); - - s->regs[RW_REC_CTRL] = 0x10000; - -} - -static int etraxfs_ser_init(SysBusDevice *dev) -{ - struct etrax_serial *s = FROM_SYSBUS(typeof (*s), dev); - - sysbus_init_irq(dev, &s->irq); - memory_region_init_io(&s->mmio, &ser_ops, s, "etraxfs-serial", R_MAX * 4); - sysbus_init_mmio(dev, &s->mmio); - - s->chr = qemu_char_get_next_serial(); - if (s->chr) - qemu_chr_add_handlers(s->chr, - serial_can_receive, serial_receive, - serial_event, s); - return 0; -} - -static void etraxfs_ser_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = etraxfs_ser_init; - dc->reset = etraxfs_ser_reset; -} - -static const TypeInfo etraxfs_ser_info = { - .name = "etraxfs,serial", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(struct etrax_serial), - .class_init = etraxfs_ser_class_init, -}; - -static void etraxfs_serial_register_types(void) -{ - type_register_static(&etraxfs_ser_info); -} - -type_init(etraxfs_serial_register_types) diff --git a/hw/exynos4210_uart.c b/hw/exynos4210_uart.c deleted file mode 100644 index 8b4e72c..0000000 --- a/hw/exynos4210_uart.c +++ /dev/null @@ -1,676 +0,0 @@ -/* - * Exynos4210 UART Emulation - * - * Copyright (C) 2011 Samsung Electronics Co Ltd. - * Maksim Kozlov, - * - * 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 . - * - */ - -#include "hw/sysbus.h" -#include "sysemu/sysemu.h" -#include "char/char.h" - -#include "hw/arm/exynos4210.h" - -#undef DEBUG_UART -#undef DEBUG_UART_EXTEND -#undef DEBUG_IRQ -#undef DEBUG_Rx_DATA -#undef DEBUG_Tx_DATA - -#define DEBUG_UART 0 -#define DEBUG_UART_EXTEND 0 -#define DEBUG_IRQ 0 -#define DEBUG_Rx_DATA 0 -#define DEBUG_Tx_DATA 0 - -#if DEBUG_UART -#define PRINT_DEBUG(fmt, args...) \ - do { \ - fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \ - } while (0) - -#if DEBUG_UART_EXTEND -#define PRINT_DEBUG_EXTEND(fmt, args...) \ - do { \ - fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \ - } while (0) -#else -#define PRINT_DEBUG_EXTEND(fmt, args...) \ - do {} while (0) -#endif /* EXTEND */ - -#else -#define PRINT_DEBUG(fmt, args...) \ - do {} while (0) -#define PRINT_DEBUG_EXTEND(fmt, args...) \ - do {} while (0) -#endif - -#define PRINT_ERROR(fmt, args...) \ - do { \ - fprintf(stderr, " [%s:%d] "fmt, __func__, __LINE__, ##args); \ - } while (0) - -/* - * Offsets for UART registers relative to SFR base address - * for UARTn - * - */ -#define ULCON 0x0000 /* Line Control */ -#define UCON 0x0004 /* Control */ -#define UFCON 0x0008 /* FIFO Control */ -#define UMCON 0x000C /* Modem Control */ -#define UTRSTAT 0x0010 /* Tx/Rx Status */ -#define UERSTAT 0x0014 /* UART Error Status */ -#define UFSTAT 0x0018 /* FIFO Status */ -#define UMSTAT 0x001C /* Modem Status */ -#define UTXH 0x0020 /* Transmit Buffer */ -#define URXH 0x0024 /* Receive Buffer */ -#define UBRDIV 0x0028 /* Baud Rate Divisor */ -#define UFRACVAL 0x002C /* Divisor Fractional Value */ -#define UINTP 0x0030 /* Interrupt Pending */ -#define UINTSP 0x0034 /* Interrupt Source Pending */ -#define UINTM 0x0038 /* Interrupt Mask */ - -/* - * for indexing register in the uint32_t array - * - * 'reg' - register offset (see offsets definitions above) - * - */ -#define I_(reg) (reg / sizeof(uint32_t)) - -typedef struct Exynos4210UartReg { - const char *name; /* the only reason is the debug output */ - hwaddr offset; - uint32_t reset_value; -} Exynos4210UartReg; - -static Exynos4210UartReg exynos4210_uart_regs[] = { - {"ULCON", ULCON, 0x00000000}, - {"UCON", UCON, 0x00003000}, - {"UFCON", UFCON, 0x00000000}, - {"UMCON", UMCON, 0x00000000}, - {"UTRSTAT", UTRSTAT, 0x00000006}, /* RO */ - {"UERSTAT", UERSTAT, 0x00000000}, /* RO */ - {"UFSTAT", UFSTAT, 0x00000000}, /* RO */ - {"UMSTAT", UMSTAT, 0x00000000}, /* RO */ - {"UTXH", UTXH, 0x5c5c5c5c}, /* WO, undefined reset value*/ - {"URXH", URXH, 0x00000000}, /* RO */ - {"UBRDIV", UBRDIV, 0x00000000}, - {"UFRACVAL", UFRACVAL, 0x00000000}, - {"UINTP", UINTP, 0x00000000}, - {"UINTSP", UINTSP, 0x00000000}, - {"UINTM", UINTM, 0x00000000}, -}; - -#define EXYNOS4210_UART_REGS_MEM_SIZE 0x3C - -/* UART FIFO Control */ -#define UFCON_FIFO_ENABLE 0x1 -#define UFCON_Rx_FIFO_RESET 0x2 -#define UFCON_Tx_FIFO_RESET 0x4 -#define UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT 8 -#define UFCON_Tx_FIFO_TRIGGER_LEVEL (7 << UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT) -#define UFCON_Rx_FIFO_TRIGGER_LEVEL_SHIFT 4 -#define UFCON_Rx_FIFO_TRIGGER_LEVEL (7 << UFCON_Rx_FIFO_TRIGGER_LEVEL_SHIFT) - -/* Uart FIFO Status */ -#define UFSTAT_Rx_FIFO_COUNT 0xff -#define UFSTAT_Rx_FIFO_FULL 0x100 -#define UFSTAT_Rx_FIFO_ERROR 0x200 -#define UFSTAT_Tx_FIFO_COUNT_SHIFT 16 -#define UFSTAT_Tx_FIFO_COUNT (0xff << UFSTAT_Tx_FIFO_COUNT_SHIFT) -#define UFSTAT_Tx_FIFO_FULL_SHIFT 24 -#define UFSTAT_Tx_FIFO_FULL (1 << UFSTAT_Tx_FIFO_FULL_SHIFT) - -/* UART Interrupt Source Pending */ -#define UINTSP_RXD 0x1 /* Receive interrupt */ -#define UINTSP_ERROR 0x2 /* Error interrupt */ -#define UINTSP_TXD 0x4 /* Transmit interrupt */ -#define UINTSP_MODEM 0x8 /* Modem interrupt */ - -/* UART Line Control */ -#define ULCON_IR_MODE_SHIFT 6 -#define ULCON_PARITY_SHIFT 3 -#define ULCON_STOP_BIT_SHIFT 1 - -/* UART Tx/Rx Status */ -#define UTRSTAT_TRANSMITTER_EMPTY 0x4 -#define UTRSTAT_Tx_BUFFER_EMPTY 0x2 -#define UTRSTAT_Rx_BUFFER_DATA_READY 0x1 - -/* UART Error Status */ -#define UERSTAT_OVERRUN 0x1 -#define UERSTAT_PARITY 0x2 -#define UERSTAT_FRAME 0x4 -#define UERSTAT_BREAK 0x8 - -typedef struct { - uint8_t *data; - uint32_t sp, rp; /* store and retrieve pointers */ - uint32_t size; -} Exynos4210UartFIFO; - -typedef struct { - SysBusDevice busdev; - MemoryRegion iomem; - - uint32_t reg[EXYNOS4210_UART_REGS_MEM_SIZE / sizeof(uint32_t)]; - Exynos4210UartFIFO rx; - Exynos4210UartFIFO tx; - - CharDriverState *chr; - qemu_irq irq; - - uint32_t channel; - -} Exynos4210UartState; - - -#if DEBUG_UART -/* Used only for debugging inside PRINT_DEBUG_... macros */ -static const char *exynos4210_uart_regname(hwaddr offset) -{ - - int regs_number = sizeof(exynos4210_uart_regs) / sizeof(Exynos4210UartReg); - int i; - - for (i = 0; i < regs_number; i++) { - if (offset == exynos4210_uart_regs[i].offset) { - return exynos4210_uart_regs[i].name; - } - } - - return NULL; -} -#endif - - -static void fifo_store(Exynos4210UartFIFO *q, uint8_t ch) -{ - q->data[q->sp] = ch; - q->sp = (q->sp + 1) % q->size; -} - -static uint8_t fifo_retrieve(Exynos4210UartFIFO *q) -{ - uint8_t ret = q->data[q->rp]; - q->rp = (q->rp + 1) % q->size; - return ret; -} - -static int fifo_elements_number(Exynos4210UartFIFO *q) -{ - if (q->sp < q->rp) { - return q->size - q->rp + q->sp; - } - - return q->sp - q->rp; -} - -static int fifo_empty_elements_number(Exynos4210UartFIFO *q) -{ - return q->size - fifo_elements_number(q); -} - -static void fifo_reset(Exynos4210UartFIFO *q) -{ - if (q->data != NULL) { - g_free(q->data); - q->data = NULL; - } - - q->data = (uint8_t *)g_malloc0(q->size); - - q->sp = 0; - q->rp = 0; -} - -static uint32_t exynos4210_uart_Tx_FIFO_trigger_level(Exynos4210UartState *s) -{ - uint32_t level = 0; - uint32_t reg; - - reg = (s->reg[I_(UFCON)] & UFCON_Tx_FIFO_TRIGGER_LEVEL) >> - UFCON_Tx_FIFO_TRIGGER_LEVEL_SHIFT; - - switch (s->channel) { - case 0: - level = reg * 32; - break; - case 1: - case 4: - level = reg * 8; - break; - case 2: - case 3: - level = reg * 2; - break; - default: - level = 0; - PRINT_ERROR("Wrong UART channel number: %d\n", s->channel); - } - - return level; -} - -static void exynos4210_uart_update_irq(Exynos4210UartState *s) -{ - /* - * The Tx interrupt is always requested if the number of data in the - * transmit FIFO is smaller than the trigger level. - */ - if (s->reg[I_(UFCON)] & UFCON_FIFO_ENABLE) { - - uint32_t count = (s->reg[I_(UFSTAT)] & UFSTAT_Tx_FIFO_COUNT) >> - UFSTAT_Tx_FIFO_COUNT_SHIFT; - - if (count <= exynos4210_uart_Tx_FIFO_trigger_level(s)) { - s->reg[I_(UINTSP)] |= UINTSP_TXD; - } - } - - s->reg[I_(UINTP)] = s->reg[I_(UINTSP)] & ~s->reg[I_(UINTM)]; - - if (s->reg[I_(UINTP)]) { - qemu_irq_raise(s->irq); - -#if DEBUG_IRQ - fprintf(stderr, "UART%d: IRQ has been raised: %08x\n", - s->channel, s->reg[I_(UINTP)]); -#endif - - } else { - qemu_irq_lower(s->irq); - } -} - -static void exynos4210_uart_update_parameters(Exynos4210UartState *s) -{ - int speed, parity, data_bits, stop_bits, frame_size; - QEMUSerialSetParams ssp; - uint64_t uclk_rate; - - if (s->reg[I_(UBRDIV)] == 0) { - return; - } - - frame_size = 1; /* start bit */ - if (s->reg[I_(ULCON)] & 0x20) { - frame_size++; /* parity bit */ - if (s->reg[I_(ULCON)] & 0x28) { - parity = 'E'; - } else { - parity = 'O'; - } - } else { - parity = 'N'; - } - - if (s->reg[I_(ULCON)] & 0x4) { - stop_bits = 2; - } else { - stop_bits = 1; - } - - data_bits = (s->reg[I_(ULCON)] & 0x3) + 5; - - frame_size += data_bits + stop_bits; - - uclk_rate = 24000000; - - speed = uclk_rate / ((16 * (s->reg[I_(UBRDIV)]) & 0xffff) + - (s->reg[I_(UFRACVAL)] & 0x7) + 16); - - ssp.speed = speed; - ssp.parity = parity; - ssp.data_bits = data_bits; - ssp.stop_bits = stop_bits; - - qemu_chr_fe_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); - - PRINT_DEBUG("UART%d: speed: %d, parity: %c, data: %d, stop: %d\n", - s->channel, speed, parity, data_bits, stop_bits); -} - -static void exynos4210_uart_write(void *opaque, hwaddr offset, - uint64_t val, unsigned size) -{ - Exynos4210UartState *s = (Exynos4210UartState *)opaque; - uint8_t ch; - - PRINT_DEBUG_EXTEND("UART%d: <0x%04x> %s <- 0x%08llx\n", s->channel, - offset, exynos4210_uart_regname(offset), (long long unsigned int)val); - - switch (offset) { - case ULCON: - case UBRDIV: - case UFRACVAL: - s->reg[I_(offset)] = val; - exynos4210_uart_update_parameters(s); - break; - case UFCON: - s->reg[I_(UFCON)] = val; - if (val & UFCON_Rx_FIFO_RESET) { - fifo_reset(&s->rx); - s->reg[I_(UFCON)] &= ~UFCON_Rx_FIFO_RESET; - PRINT_DEBUG("UART%d: Rx FIFO Reset\n", s->channel); - } - if (val & UFCON_Tx_FIFO_RESET) { - fifo_reset(&s->tx); - s->reg[I_(UFCON)] &= ~UFCON_Tx_FIFO_RESET; - PRINT_DEBUG("UART%d: Tx FIFO Reset\n", s->channel); - } - break; - - case UTXH: - if (s->chr) { - s->reg[I_(UTRSTAT)] &= ~(UTRSTAT_TRANSMITTER_EMPTY | - UTRSTAT_Tx_BUFFER_EMPTY); - ch = (uint8_t)val; - qemu_chr_fe_write(s->chr, &ch, 1); -#if DEBUG_Tx_DATA - fprintf(stderr, "%c", ch); -#endif - s->reg[I_(UTRSTAT)] |= UTRSTAT_TRANSMITTER_EMPTY | - UTRSTAT_Tx_BUFFER_EMPTY; - s->reg[I_(UINTSP)] |= UINTSP_TXD; - exynos4210_uart_update_irq(s); - } - break; - - case UINTP: - s->reg[I_(UINTP)] &= ~val; - s->reg[I_(UINTSP)] &= ~val; - PRINT_DEBUG("UART%d: UINTP [%04x] have been cleared: %08x\n", - s->channel, offset, s->reg[I_(UINTP)]); - exynos4210_uart_update_irq(s); - break; - case UTRSTAT: - case UERSTAT: - case UFSTAT: - case UMSTAT: - case URXH: - PRINT_DEBUG("UART%d: Trying to write into RO register: %s [%04x]\n", - s->channel, exynos4210_uart_regname(offset), offset); - break; - case UINTSP: - s->reg[I_(UINTSP)] &= ~val; - break; - case UINTM: - s->reg[I_(UINTM)] = val; - exynos4210_uart_update_irq(s); - break; - case UCON: - case UMCON: - default: - s->reg[I_(offset)] = val; - break; - } -} -static uint64_t exynos4210_uart_read(void *opaque, hwaddr offset, - unsigned size) -{ - Exynos4210UartState *s = (Exynos4210UartState *)opaque; - uint32_t res; - - switch (offset) { - case UERSTAT: /* Read Only */ - res = s->reg[I_(UERSTAT)]; - s->reg[I_(UERSTAT)] = 0; - return res; - case UFSTAT: /* Read Only */ - s->reg[I_(UFSTAT)] = fifo_elements_number(&s->rx) & 0xff; - if (fifo_empty_elements_number(&s->rx) == 0) { - s->reg[I_(UFSTAT)] |= UFSTAT_Rx_FIFO_FULL; - s->reg[I_(UFSTAT)] &= ~0xff; - } - return s->reg[I_(UFSTAT)]; - case URXH: - if (s->reg[I_(UFCON)] & UFCON_FIFO_ENABLE) { - if (fifo_elements_number(&s->rx)) { - res = fifo_retrieve(&s->rx); -#if DEBUG_Rx_DATA - fprintf(stderr, "%c", res); -#endif - if (!fifo_elements_number(&s->rx)) { - s->reg[I_(UTRSTAT)] &= ~UTRSTAT_Rx_BUFFER_DATA_READY; - } else { - s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY; - } - } else { - s->reg[I_(UINTSP)] |= UINTSP_ERROR; - exynos4210_uart_update_irq(s); - res = 0; - } - } else { - s->reg[I_(UTRSTAT)] &= ~UTRSTAT_Rx_BUFFER_DATA_READY; - res = s->reg[I_(URXH)]; - } - return res; - case UTXH: - PRINT_DEBUG("UART%d: Trying to read from WO register: %s [%04x]\n", - s->channel, exynos4210_uart_regname(offset), offset); - break; - default: - return s->reg[I_(offset)]; - } - - return 0; -} - -static const MemoryRegionOps exynos4210_uart_ops = { - .read = exynos4210_uart_read, - .write = exynos4210_uart_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .max_access_size = 4, - .unaligned = false - }, -}; - -static int exynos4210_uart_can_receive(void *opaque) -{ - Exynos4210UartState *s = (Exynos4210UartState *)opaque; - - return fifo_empty_elements_number(&s->rx); -} - - -static void exynos4210_uart_receive(void *opaque, const uint8_t *buf, int size) -{ - Exynos4210UartState *s = (Exynos4210UartState *)opaque; - int i; - - if (s->reg[I_(UFCON)] & UFCON_FIFO_ENABLE) { - if (fifo_empty_elements_number(&s->rx) < size) { - for (i = 0; i < fifo_empty_elements_number(&s->rx); i++) { - fifo_store(&s->rx, buf[i]); - } - s->reg[I_(UINTSP)] |= UINTSP_ERROR; - s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY; - } else { - for (i = 0; i < size; i++) { - fifo_store(&s->rx, buf[i]); - } - s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY; - } - /* XXX: Around here we maybe should check Rx trigger level */ - s->reg[I_(UINTSP)] |= UINTSP_RXD; - } else { - s->reg[I_(URXH)] = buf[0]; - s->reg[I_(UINTSP)] |= UINTSP_RXD; - s->reg[I_(UTRSTAT)] |= UTRSTAT_Rx_BUFFER_DATA_READY; - } - - exynos4210_uart_update_irq(s); -} - - -static void exynos4210_uart_event(void *opaque, int event) -{ - Exynos4210UartState *s = (Exynos4210UartState *)opaque; - - if (event == CHR_EVENT_BREAK) { - /* When the RxDn is held in logic 0, then a null byte is pushed into the - * fifo */ - fifo_store(&s->rx, '\0'); - s->reg[I_(UERSTAT)] |= UERSTAT_BREAK; - exynos4210_uart_update_irq(s); - } -} - - -static void exynos4210_uart_reset(DeviceState *dev) -{ - Exynos4210UartState *s = - container_of(dev, Exynos4210UartState, busdev.qdev); - int regs_number = sizeof(exynos4210_uart_regs)/sizeof(Exynos4210UartReg); - int i; - - for (i = 0; i < regs_number; i++) { - s->reg[I_(exynos4210_uart_regs[i].offset)] = - exynos4210_uart_regs[i].reset_value; - } - - fifo_reset(&s->rx); - fifo_reset(&s->tx); - - PRINT_DEBUG("UART%d: Rx FIFO size: %d\n", s->channel, s->rx.size); -} - -static const VMStateDescription vmstate_exynos4210_uart_fifo = { - .name = "exynos4210.uart.fifo", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(sp, Exynos4210UartFIFO), - VMSTATE_UINT32(rp, Exynos4210UartFIFO), - VMSTATE_VBUFFER_UINT32(data, Exynos4210UartFIFO, 1, NULL, 0, size), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_exynos4210_uart = { - .name = "exynos4210.uart", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_STRUCT(rx, Exynos4210UartState, 1, - vmstate_exynos4210_uart_fifo, Exynos4210UartFIFO), - VMSTATE_UINT32_ARRAY(reg, Exynos4210UartState, - EXYNOS4210_UART_REGS_MEM_SIZE / sizeof(uint32_t)), - VMSTATE_END_OF_LIST() - } -}; - -DeviceState *exynos4210_uart_create(hwaddr addr, - int fifo_size, - int channel, - CharDriverState *chr, - qemu_irq irq) -{ - DeviceState *dev; - SysBusDevice *bus; - - const char chr_name[] = "serial"; - char label[ARRAY_SIZE(chr_name) + 1]; - - dev = qdev_create(NULL, "exynos4210.uart"); - - if (!chr) { - if (channel >= MAX_SERIAL_PORTS) { - hw_error("Only %d serial ports are supported by QEMU.\n", - MAX_SERIAL_PORTS); - } - chr = serial_hds[channel]; - if (!chr) { - snprintf(label, ARRAY_SIZE(label), "%s%d", chr_name, channel); - chr = qemu_chr_new(label, "null", NULL); - if (!(chr)) { - hw_error("Can't assign serial port to UART%d.\n", channel); - } - } - } - - qdev_prop_set_chr(dev, "chardev", chr); - qdev_prop_set_uint32(dev, "channel", channel); - qdev_prop_set_uint32(dev, "rx-size", fifo_size); - qdev_prop_set_uint32(dev, "tx-size", fifo_size); - - bus = SYS_BUS_DEVICE(dev); - qdev_init_nofail(dev); - if (addr != (hwaddr)-1) { - sysbus_mmio_map(bus, 0, addr); - } - sysbus_connect_irq(bus, 0, irq); - - return dev; -} - -static int exynos4210_uart_init(SysBusDevice *dev) -{ - Exynos4210UartState *s = FROM_SYSBUS(Exynos4210UartState, dev); - - /* memory mapping */ - memory_region_init_io(&s->iomem, &exynos4210_uart_ops, s, "exynos4210.uart", - EXYNOS4210_UART_REGS_MEM_SIZE); - sysbus_init_mmio(dev, &s->iomem); - - sysbus_init_irq(dev, &s->irq); - - qemu_chr_add_handlers(s->chr, exynos4210_uart_can_receive, - exynos4210_uart_receive, exynos4210_uart_event, s); - - return 0; -} - -static Property exynos4210_uart_properties[] = { - DEFINE_PROP_CHR("chardev", Exynos4210UartState, chr), - DEFINE_PROP_UINT32("channel", Exynos4210UartState, channel, 0), - DEFINE_PROP_UINT32("rx-size", Exynos4210UartState, rx.size, 16), - DEFINE_PROP_UINT32("tx-size", Exynos4210UartState, tx.size, 16), - DEFINE_PROP_END_OF_LIST(), -}; - -static void exynos4210_uart_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = exynos4210_uart_init; - dc->reset = exynos4210_uart_reset; - dc->props = exynos4210_uart_properties; - dc->vmsd = &vmstate_exynos4210_uart; -} - -static const TypeInfo exynos4210_uart_info = { - .name = "exynos4210.uart", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(Exynos4210UartState), - .class_init = exynos4210_uart_class_init, -}; - -static void exynos4210_uart_register(void) -{ - type_register_static(&exynos4210_uart_info); -} - -type_init(exynos4210_uart_register) diff --git a/hw/grlib_apbuart.c b/hw/grlib_apbuart.c deleted file mode 100644 index 62f7990..0000000 --- a/hw/grlib_apbuart.c +++ /dev/null @@ -1,293 +0,0 @@ -/* - * QEMU GRLIB APB UART Emulator - * - * Copyright (c) 2010-2011 AdaCore - * - * 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/sysbus.h" -#include "char/char.h" - -#include "trace.h" - -#define UART_REG_SIZE 20 /* Size of memory mapped registers */ - -/* UART status register fields */ -#define UART_DATA_READY (1 << 0) -#define UART_TRANSMIT_SHIFT_EMPTY (1 << 1) -#define UART_TRANSMIT_FIFO_EMPTY (1 << 2) -#define UART_BREAK_RECEIVED (1 << 3) -#define UART_OVERRUN (1 << 4) -#define UART_PARITY_ERROR (1 << 5) -#define UART_FRAMING_ERROR (1 << 6) -#define UART_TRANSMIT_FIFO_HALF (1 << 7) -#define UART_RECEIVE_FIFO_HALF (1 << 8) -#define UART_TRANSMIT_FIFO_FULL (1 << 9) -#define UART_RECEIVE_FIFO_FULL (1 << 10) - -/* UART control register fields */ -#define UART_RECEIVE_ENABLE (1 << 0) -#define UART_TRANSMIT_ENABLE (1 << 1) -#define UART_RECEIVE_INTERRUPT (1 << 2) -#define UART_TRANSMIT_INTERRUPT (1 << 3) -#define UART_PARITY_SELECT (1 << 4) -#define UART_PARITY_ENABLE (1 << 5) -#define UART_FLOW_CONTROL (1 << 6) -#define UART_LOOPBACK (1 << 7) -#define UART_EXTERNAL_CLOCK (1 << 8) -#define UART_RECEIVE_FIFO_INTERRUPT (1 << 9) -#define UART_TRANSMIT_FIFO_INTERRUPT (1 << 10) -#define UART_FIFO_DEBUG_MODE (1 << 11) -#define UART_OUTPUT_ENABLE (1 << 12) -#define UART_FIFO_AVAILABLE (1 << 31) - -/* Memory mapped register offsets */ -#define DATA_OFFSET 0x00 -#define STATUS_OFFSET 0x04 -#define CONTROL_OFFSET 0x08 -#define SCALER_OFFSET 0x0C /* not supported */ -#define FIFO_DEBUG_OFFSET 0x10 /* not supported */ - -#define FIFO_LENGTH 1024 - -typedef struct UART { - SysBusDevice busdev; - MemoryRegion iomem; - qemu_irq irq; - - CharDriverState *chr; - - /* registers */ - uint32_t status; - uint32_t control; - - /* FIFO */ - char buffer[FIFO_LENGTH]; - int len; - int current; -} UART; - -static int uart_data_to_read(UART *uart) -{ - return uart->current < uart->len; -} - -static char uart_pop(UART *uart) -{ - char ret; - - if (uart->len == 0) { - uart->status &= ~UART_DATA_READY; - return 0; - } - - ret = uart->buffer[uart->current++]; - - if (uart->current >= uart->len) { - /* Flush */ - uart->len = 0; - uart->current = 0; - } - - if (!uart_data_to_read(uart)) { - uart->status &= ~UART_DATA_READY; - } - - return ret; -} - -static void uart_add_to_fifo(UART *uart, - const uint8_t *buffer, - int length) -{ - if (uart->len + length > FIFO_LENGTH) { - abort(); - } - memcpy(uart->buffer + uart->len, buffer, length); - uart->len += length; -} - -static int grlib_apbuart_can_receive(void *opaque) -{ - UART *uart = opaque; - - return FIFO_LENGTH - uart->len; -} - -static void grlib_apbuart_receive(void *opaque, const uint8_t *buf, int size) -{ - UART *uart = opaque; - - if (uart->control & UART_RECEIVE_ENABLE) { - uart_add_to_fifo(uart, buf, size); - - uart->status |= UART_DATA_READY; - - if (uart->control & UART_RECEIVE_INTERRUPT) { - qemu_irq_pulse(uart->irq); - } - } -} - -static void grlib_apbuart_event(void *opaque, int event) -{ - trace_grlib_apbuart_event(event); -} - - -static uint64_t grlib_apbuart_read(void *opaque, hwaddr addr, - unsigned size) -{ - UART *uart = opaque; - - addr &= 0xff; - - /* Unit registers */ - switch (addr) { - case DATA_OFFSET: - case DATA_OFFSET + 3: /* when only one byte read */ - return uart_pop(uart); - - case STATUS_OFFSET: - /* Read Only */ - return uart->status; - - case CONTROL_OFFSET: - return uart->control; - - case SCALER_OFFSET: - /* Not supported */ - return 0; - - default: - trace_grlib_apbuart_readl_unknown(addr); - return 0; - } -} - -static void grlib_apbuart_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - UART *uart = opaque; - unsigned char c = 0; - - addr &= 0xff; - - /* Unit registers */ - switch (addr) { - case DATA_OFFSET: - case DATA_OFFSET + 3: /* When only one byte write */ - /* Transmit when character device available and transmitter enabled */ - if ((uart->chr) && (uart->control & UART_TRANSMIT_ENABLE)) { - c = value & 0xFF; - qemu_chr_fe_write(uart->chr, &c, 1); - /* Generate interrupt */ - if (uart->control & UART_TRANSMIT_INTERRUPT) { - qemu_irq_pulse(uart->irq); - } - } - return; - - case STATUS_OFFSET: - /* Read Only */ - return; - - case CONTROL_OFFSET: - uart->control = value; - return; - - case SCALER_OFFSET: - /* Not supported */ - return; - - default: - break; - } - - trace_grlib_apbuart_writel_unknown(addr, value); -} - -static const MemoryRegionOps grlib_apbuart_ops = { - .write = grlib_apbuart_write, - .read = grlib_apbuart_read, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int grlib_apbuart_init(SysBusDevice *dev) -{ - UART *uart = FROM_SYSBUS(typeof(*uart), dev); - - qemu_chr_add_handlers(uart->chr, - grlib_apbuart_can_receive, - grlib_apbuart_receive, - grlib_apbuart_event, - uart); - - sysbus_init_irq(dev, &uart->irq); - - memory_region_init_io(&uart->iomem, &grlib_apbuart_ops, uart, - "uart", UART_REG_SIZE); - - sysbus_init_mmio(dev, &uart->iomem); - - return 0; -} - -static void grlib_apbuart_reset(DeviceState *d) -{ - UART *uart = container_of(d, UART, busdev.qdev); - - /* Transmitter FIFO and shift registers are always empty in QEMU */ - uart->status = UART_TRANSMIT_FIFO_EMPTY | UART_TRANSMIT_SHIFT_EMPTY; - /* Everything is off */ - uart->control = 0; - /* Flush receive FIFO */ - uart->len = 0; - uart->current = 0; -} - -static Property grlib_apbuart_properties[] = { - DEFINE_PROP_CHR("chrdev", UART, chr), - DEFINE_PROP_END_OF_LIST(), -}; - -static void grlib_apbuart_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = grlib_apbuart_init; - dc->reset = grlib_apbuart_reset; - dc->props = grlib_apbuart_properties; -} - -static const TypeInfo grlib_apbuart_info = { - .name = "grlib,apbuart", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(UART), - .class_init = grlib_apbuart_class_init, -}; - -static void grlib_apbuart_register_types(void) -{ - type_register_static(&grlib_apbuart_info); -} - -type_init(grlib_apbuart_register_types) diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs index c85bb3d..5e91d1e 100644 --- a/hw/i386/Makefile.objs +++ b/hw/i386/Makefile.objs @@ -1,7 +1,7 @@ obj-y += apic_common.o apic.o obj-y += sga.o ioapic_common.o ioapic.o piix_pci.o obj-y += vmport.o -obj-y += debugcon.o debugexit.o +obj-y += debugexit.o obj-y += lpc_ich9.o q35.o obj-y += kvm/ obj-y += pc-testdev.o diff --git a/hw/imx_serial.c b/hw/imx_serial.c deleted file mode 100644 index d7ec209..0000000 --- a/hw/imx_serial.c +++ /dev/null @@ -1,467 +0,0 @@ -/* - * IMX31 UARTS - * - * Copyright (c) 2008 OKL - * Originally Written by Hans Jiang - * Copyright (c) 2011 NICTA Pty Ltd. - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - * This is a `bare-bones' implementation of the IMX series serial ports. - * TODO: - * -- implement FIFOs. The real hardware has 32 word transmit - * and receive FIFOs; we currently use a 1-char buffer - * -- implement DMA - * -- implement BAUD-rate and modem lines, for when the backend - * is a real serial device. - */ - -#include "hw/hw.h" -#include "hw/sysbus.h" -#include "sysemu/sysemu.h" -#include "char/char.h" -#include "hw/arm/imx.h" - -//#define DEBUG_SERIAL 1 -#ifdef DEBUG_SERIAL -#define DPRINTF(fmt, args...) \ -do { printf("imx_serial: " fmt , ##args); } while (0) -#else -#define DPRINTF(fmt, args...) do {} while (0) -#endif - -/* - * Define to 1 for messages about attempts to - * access unimplemented registers or similar. - */ -//#define DEBUG_IMPLEMENTATION 1 -#ifdef DEBUG_IMPLEMENTATION -# define IPRINTF(fmt, args...) \ - do { fprintf(stderr, "imx_serial: " fmt, ##args); } while (0) -#else -# define IPRINTF(fmt, args...) do {} while (0) -#endif - -typedef struct { - SysBusDevice busdev; - MemoryRegion iomem; - int32_t readbuff; - - uint32_t usr1; - uint32_t usr2; - uint32_t ucr1; - uint32_t ucr2; - uint32_t uts1; - - /* - * The registers below are implemented just so that the - * guest OS sees what it has written - */ - uint32_t onems; - uint32_t ufcr; - uint32_t ubmr; - uint32_t ubrc; - uint32_t ucr3; - - qemu_irq irq; - CharDriverState *chr; -} IMXSerialState; - -static const VMStateDescription vmstate_imx_serial = { - .name = "imx-serial", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_INT32(readbuff, IMXSerialState), - VMSTATE_UINT32(usr1, IMXSerialState), - VMSTATE_UINT32(usr2, IMXSerialState), - VMSTATE_UINT32(ucr1, IMXSerialState), - VMSTATE_UINT32(uts1, IMXSerialState), - VMSTATE_UINT32(onems, IMXSerialState), - VMSTATE_UINT32(ufcr, IMXSerialState), - VMSTATE_UINT32(ubmr, IMXSerialState), - VMSTATE_UINT32(ubrc, IMXSerialState), - VMSTATE_UINT32(ucr3, IMXSerialState), - VMSTATE_END_OF_LIST() - }, -}; - - -#define URXD_CHARRDY (1<<15) /* character read is valid */ -#define URXD_ERR (1<<14) /* Character has error */ -#define URXD_BRK (1<<11) /* Break received */ - -#define USR1_PARTYER (1<<15) /* Parity Error */ -#define USR1_RTSS (1<<14) /* RTS pin status */ -#define USR1_TRDY (1<<13) /* Tx ready */ -#define USR1_RTSD (1<<12) /* RTS delta: pin changed state */ -#define USR1_ESCF (1<<11) /* Escape sequence interrupt */ -#define USR1_FRAMERR (1<<10) /* Framing error */ -#define USR1_RRDY (1<<9) /* receiver ready */ -#define USR1_AGTIM (1<<8) /* Aging timer interrupt */ -#define USR1_DTRD (1<<7) /* DTR changed */ -#define USR1_RXDS (1<<6) /* Receiver is idle */ -#define USR1_AIRINT (1<<5) /* Aysnch IR interrupt */ -#define USR1_AWAKE (1<<4) /* Falling edge detected on RXd pin */ - -#define USR2_ADET (1<<15) /* Autobaud complete */ -#define USR2_TXFE (1<<14) /* Transmit FIFO empty */ -#define USR2_DTRF (1<<13) /* DTR/DSR transition */ -#define USR2_IDLE (1<<12) /* UART has been idle for too long */ -#define USR2_ACST (1<<11) /* Autobaud counter stopped */ -#define USR2_RIDELT (1<<10) /* Ring Indicator delta */ -#define USR2_RIIN (1<<9) /* Ring Indicator Input */ -#define USR2_IRINT (1<<8) /* Serial Infrared Interrupt */ -#define USR2_WAKE (1<<7) /* Start bit detected */ -#define USR2_DCDDELT (1<<6) /* Data Carrier Detect delta */ -#define USR2_DCDIN (1<<5) /* Data Carrier Detect Input */ -#define USR2_RTSF (1<<4) /* RTS transition */ -#define USR2_TXDC (1<<3) /* Transmission complete */ -#define USR2_BRCD (1<<2) /* Break condition detected */ -#define USR2_ORE (1<<1) /* Overrun error */ -#define USR2_RDR (1<<0) /* Receive data ready */ - -#define UCR1_TRDYEN (1<<13) /* Tx Ready Interrupt Enable */ -#define UCR1_RRDYEN (1<<9) /* Rx Ready Interrupt Enable */ -#define UCR1_TXMPTYEN (1<<6) /* Tx Empty Interrupt Enable */ -#define UCR1_UARTEN (1<<0) /* UART Enable */ - -#define UCR2_TXEN (1<<2) /* Transmitter enable */ -#define UCR2_RXEN (1<<1) /* Receiver enable */ -#define UCR2_SRST (1<<0) /* Reset complete */ - -#define UTS1_TXEMPTY (1<<6) -#define UTS1_RXEMPTY (1<<5) -#define UTS1_TXFULL (1<<4) -#define UTS1_RXFULL (1<<3) - -static void imx_update(IMXSerialState *s) -{ - uint32_t flags; - - flags = (s->usr1 & s->ucr1) & (USR1_TRDY|USR1_RRDY); - if (!(s->ucr1 & UCR1_TXMPTYEN)) { - flags &= ~USR1_TRDY; - } - - qemu_set_irq(s->irq, !!flags); -} - -static void imx_serial_reset(IMXSerialState *s) -{ - - s->usr1 = USR1_TRDY | USR1_RXDS; - /* - * Fake attachment of a terminal: assert RTS. - */ - s->usr1 |= USR1_RTSS; - s->usr2 = USR2_TXFE | USR2_TXDC | USR2_DCDIN; - s->uts1 = UTS1_RXEMPTY | UTS1_TXEMPTY; - s->ucr1 = 0; - s->ucr2 = UCR2_SRST; - s->ucr3 = 0x700; - s->ubmr = 0; - s->ubrc = 4; - s->readbuff = URXD_ERR; -} - -static void imx_serial_reset_at_boot(DeviceState *dev) -{ - IMXSerialState *s = container_of(dev, IMXSerialState, busdev.qdev); - - imx_serial_reset(s); - - /* - * enable the uart on boot, so messages from the linux decompresser - * are visible. On real hardware this is done by the boot rom - * before anything else is loaded. - */ - s->ucr1 = UCR1_UARTEN; - s->ucr2 = UCR2_TXEN; - -} - -static uint64_t imx_serial_read(void *opaque, hwaddr offset, - unsigned size) -{ - IMXSerialState *s = (IMXSerialState *)opaque; - uint32_t c; - - DPRINTF("read(offset=%x)\n", offset >> 2); - switch (offset >> 2) { - case 0x0: /* URXD */ - c = s->readbuff; - if (!(s->uts1 & UTS1_RXEMPTY)) { - /* Character is valid */ - c |= URXD_CHARRDY; - s->usr1 &= ~USR1_RRDY; - s->usr2 &= ~USR2_RDR; - s->uts1 |= UTS1_RXEMPTY; - imx_update(s); - qemu_chr_accept_input(s->chr); - } - return c; - - case 0x20: /* UCR1 */ - return s->ucr1; - - case 0x21: /* UCR2 */ - return s->ucr2; - - case 0x25: /* USR1 */ - return s->usr1; - - case 0x26: /* USR2 */ - return s->usr2; - - case 0x2A: /* BRM Modulator */ - return s->ubmr; - - case 0x2B: /* Baud Rate Count */ - return s->ubrc; - - case 0x2d: /* Test register */ - return s->uts1; - - case 0x24: /* UFCR */ - return s->ufcr; - - case 0x2c: - return s->onems; - - case 0x22: /* UCR3 */ - return s->ucr3; - - case 0x23: /* UCR4 */ - case 0x29: /* BRM Incremental */ - return 0x0; /* TODO */ - - default: - IPRINTF("imx_serial_read: bad offset: 0x%x\n", (int)offset); - return 0; - } -} - -static void imx_serial_write(void *opaque, hwaddr offset, - uint64_t value, unsigned size) -{ - IMXSerialState *s = (IMXSerialState *)opaque; - unsigned char ch; - - DPRINTF("write(offset=%x, value = %x) to %s\n", - offset >> 2, - (unsigned int)value, s->chr ? s->chr->label : "NODEV"); - - switch (offset >> 2) { - case 0x10: /* UTXD */ - ch = value; - if (s->ucr2 & UCR2_TXEN) { - if (s->chr) { - qemu_chr_fe_write(s->chr, &ch, 1); - } - s->usr1 &= ~USR1_TRDY; - imx_update(s); - s->usr1 |= USR1_TRDY; - imx_update(s); - } - break; - - case 0x20: /* UCR1 */ - s->ucr1 = value & 0xffff; - DPRINTF("write(ucr1=%x)\n", (unsigned int)value); - imx_update(s); - break; - - case 0x21: /* UCR2 */ - /* - * Only a few bits in control register 2 are implemented as yet. - * If it's intended to use a real serial device as a back-end, this - * register will have to be implemented more fully. - */ - if (!(value & UCR2_SRST)) { - imx_serial_reset(s); - imx_update(s); - value |= UCR2_SRST; - } - if (value & UCR2_RXEN) { - if (!(s->ucr2 & UCR2_RXEN)) { - qemu_chr_accept_input(s->chr); - } - } - s->ucr2 = value & 0xffff; - break; - - case 0x25: /* USR1 */ - value &= USR1_AWAKE | USR1_AIRINT | USR1_DTRD | USR1_AGTIM | - USR1_FRAMERR | USR1_ESCF | USR1_RTSD | USR1_PARTYER; - s->usr1 &= ~value; - break; - - case 0x26: /* USR2 */ - /* - * Writing 1 to some bits clears them; all other - * values are ignored - */ - value &= USR2_ADET | USR2_DTRF | USR2_IDLE | USR2_ACST | - USR2_RIDELT | USR2_IRINT | USR2_WAKE | - USR2_DCDDELT | USR2_RTSF | USR2_BRCD | USR2_ORE; - s->usr2 &= ~value; - break; - - /* - * Linux expects to see what it writes to these registers - * We don't currently alter the baud rate - */ - case 0x29: /* UBIR */ - s->ubrc = value & 0xffff; - break; - - case 0x2a: /* UBMR */ - s->ubmr = value & 0xffff; - break; - - case 0x2c: /* One ms reg */ - s->onems = value & 0xffff; - break; - - case 0x24: /* FIFO control register */ - s->ufcr = value & 0xffff; - break; - - case 0x22: /* UCR3 */ - s->ucr3 = value & 0xffff; - break; - - case 0x2d: /* UTS1 */ - case 0x23: /* UCR4 */ - IPRINTF("Unimplemented Register %x written to\n", offset >> 2); - /* TODO */ - break; - - default: - IPRINTF("imx_serial_write: Bad offset 0x%x\n", (int)offset); - } -} - -static int imx_can_receive(void *opaque) -{ - IMXSerialState *s = (IMXSerialState *)opaque; - return !(s->usr1 & USR1_RRDY); -} - -static void imx_put_data(void *opaque, uint32_t value) -{ - IMXSerialState *s = (IMXSerialState *)opaque; - DPRINTF("received char\n"); - s->usr1 |= USR1_RRDY; - s->usr2 |= USR2_RDR; - s->uts1 &= ~UTS1_RXEMPTY; - s->readbuff = value; - imx_update(s); -} - -static void imx_receive(void *opaque, const uint8_t *buf, int size) -{ - imx_put_data(opaque, *buf); -} - -static void imx_event(void *opaque, int event) -{ - if (event == CHR_EVENT_BREAK) { - imx_put_data(opaque, URXD_BRK); - } -} - - -static const struct MemoryRegionOps imx_serial_ops = { - .read = imx_serial_read, - .write = imx_serial_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static int imx_serial_init(SysBusDevice *dev) -{ - IMXSerialState *s = FROM_SYSBUS(IMXSerialState, dev); - - - memory_region_init_io(&s->iomem, &imx_serial_ops, s, "imx-serial", 0x1000); - sysbus_init_mmio(dev, &s->iomem); - sysbus_init_irq(dev, &s->irq); - - if (s->chr) { - qemu_chr_add_handlers(s->chr, imx_can_receive, imx_receive, - imx_event, s); - } else { - DPRINTF("No char dev for uart at 0x%lx\n", - (unsigned long)s->iomem.ram_addr); - } - - return 0; -} - -void imx_serial_create(int uart, const hwaddr addr, qemu_irq irq) -{ - DeviceState *dev; - SysBusDevice *bus; - CharDriverState *chr; - const char chr_name[] = "serial"; - char label[ARRAY_SIZE(chr_name) + 1]; - - dev = qdev_create(NULL, "imx-serial"); - - if (uart >= MAX_SERIAL_PORTS) { - hw_error("Cannot assign uart %d: QEMU supports only %d ports\n", - uart, MAX_SERIAL_PORTS); - } - chr = serial_hds[uart]; - if (!chr) { - snprintf(label, ARRAY_SIZE(label), "%s%d", chr_name, uart); - chr = qemu_chr_new(label, "null", NULL); - if (!(chr)) { - hw_error("Can't assign serial port to imx-uart%d.\n", uart); - } - } - - qdev_prop_set_chr(dev, "chardev", chr); - bus = SYS_BUS_DEVICE(dev); - qdev_init_nofail(dev); - if (addr != (hwaddr)-1) { - sysbus_mmio_map(bus, 0, addr); - } - sysbus_connect_irq(bus, 0, irq); - -} - - -static Property imx32_serial_properties[] = { - DEFINE_PROP_CHR("chardev", IMXSerialState, chr), - DEFINE_PROP_END_OF_LIST(), -}; - -static void imx_serial_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = imx_serial_init; - dc->vmsd = &vmstate_imx_serial; - dc->reset = imx_serial_reset_at_boot; - dc->desc = "i.MX series UART"; - dc->props = imx32_serial_properties; -} - -static const TypeInfo imx_serial_info = { - .name = "imx-serial", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(IMXSerialState), - .class_init = imx_serial_class_init, -}; - -static void imx_serial_register_types(void) -{ - type_register_static(&imx_serial_info); -} - -type_init(imx_serial_register_types) diff --git a/hw/lm32/Makefile.objs b/hw/lm32/Makefile.objs index a894c46..e116156 100644 --- a/hw/lm32/Makefile.objs +++ b/hw/lm32/Makefile.objs @@ -1,15 +1,12 @@ # LM32 peripherals obj-y += lm32_pic.o -obj-y += lm32_juart.o obj-y += lm32_timer.o -obj-y += lm32_uart.o obj-y += lm32_sys.o obj-y += milkymist-hpdmc.o obj-y += milkymist-memcard.o obj-y += milkymist-pfpu.o obj-y += milkymist-softusb.o obj-y += milkymist-sysctl.o -obj-y += milkymist-uart.o obj-y := $(addprefix ../,$(obj-y)) diff --git a/hw/lm32_juart.c b/hw/lm32_juart.c deleted file mode 100644 index 93f0d15..0000000 --- a/hw/lm32_juart.c +++ /dev/null @@ -1,159 +0,0 @@ -/* - * LatticeMico32 JTAG UART model. - * - * Copyright (c) 2010 Michael Walle - * - * 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 . - */ - -#include "hw/hw.h" -#include "hw/sysbus.h" -#include "trace.h" -#include "char/char.h" - -#include "hw/lm32/lm32_juart.h" - -enum { - LM32_JUART_MIN_SAVE_VERSION = 0, - LM32_JUART_CURRENT_SAVE_VERSION = 0, - LM32_JUART_MAX_SAVE_VERSION = 0, -}; - -enum { - JTX_FULL = (1<<8), -}; - -enum { - JRX_FULL = (1<<8), -}; - -struct LM32JuartState { - SysBusDevice busdev; - CharDriverState *chr; - - uint32_t jtx; - uint32_t jrx; -}; -typedef struct LM32JuartState LM32JuartState; - -uint32_t lm32_juart_get_jtx(DeviceState *d) -{ - LM32JuartState *s = container_of(d, LM32JuartState, busdev.qdev); - - trace_lm32_juart_get_jtx(s->jtx); - return s->jtx; -} - -uint32_t lm32_juart_get_jrx(DeviceState *d) -{ - LM32JuartState *s = container_of(d, LM32JuartState, busdev.qdev); - - trace_lm32_juart_get_jrx(s->jrx); - return s->jrx; -} - -void lm32_juart_set_jtx(DeviceState *d, uint32_t jtx) -{ - LM32JuartState *s = container_of(d, LM32JuartState, busdev.qdev); - unsigned char ch = jtx & 0xff; - - trace_lm32_juart_set_jtx(s->jtx); - - s->jtx = jtx; - if (s->chr) { - qemu_chr_fe_write(s->chr, &ch, 1); - } -} - -void lm32_juart_set_jrx(DeviceState *d, uint32_t jtx) -{ - LM32JuartState *s = container_of(d, LM32JuartState, busdev.qdev); - - trace_lm32_juart_set_jrx(s->jrx); - s->jrx &= ~JRX_FULL; -} - -static void juart_rx(void *opaque, const uint8_t *buf, int size) -{ - LM32JuartState *s = opaque; - - s->jrx = *buf | JRX_FULL; -} - -static int juart_can_rx(void *opaque) -{ - LM32JuartState *s = opaque; - - return !(s->jrx & JRX_FULL); -} - -static void juart_event(void *opaque, int event) -{ -} - -static void juart_reset(DeviceState *d) -{ - LM32JuartState *s = container_of(d, LM32JuartState, busdev.qdev); - - s->jtx = 0; - s->jrx = 0; -} - -static int lm32_juart_init(SysBusDevice *dev) -{ - LM32JuartState *s = FROM_SYSBUS(typeof(*s), dev); - - s->chr = qemu_char_get_next_serial(); - if (s->chr) { - qemu_chr_add_handlers(s->chr, juart_can_rx, juart_rx, juart_event, s); - } - - return 0; -} - -static const VMStateDescription vmstate_lm32_juart = { - .name = "lm32-juart", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(jtx, LM32JuartState), - VMSTATE_UINT32(jrx, LM32JuartState), - VMSTATE_END_OF_LIST() - } -}; - -static void lm32_juart_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = lm32_juart_init; - dc->reset = juart_reset; - dc->vmsd = &vmstate_lm32_juart; -} - -static const TypeInfo lm32_juart_info = { - .name = "lm32-juart", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(LM32JuartState), - .class_init = lm32_juart_class_init, -}; - -static void lm32_juart_register_types(void) -{ - type_register_static(&lm32_juart_info); -} - -type_init(lm32_juart_register_types) diff --git a/hw/lm32_uart.c b/hw/lm32_uart.c deleted file mode 100644 index 32bc37a..0000000 --- a/hw/lm32_uart.c +++ /dev/null @@ -1,297 +0,0 @@ -/* - * QEMU model of the LatticeMico32 UART block. - * - * Copyright (c) 2010 Michael Walle - * - * 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 . - * - * - * Specification available at: - * http://www.latticesemi.com/documents/mico32uart.pdf - */ - - -#include "hw/hw.h" -#include "hw/sysbus.h" -#include "trace.h" -#include "char/char.h" -#include "qemu/error-report.h" - -enum { - R_RXTX = 0, - R_IER, - R_IIR, - R_LCR, - R_MCR, - R_LSR, - R_MSR, - R_DIV, - R_MAX -}; - -enum { - IER_RBRI = (1<<0), - IER_THRI = (1<<1), - IER_RLSI = (1<<2), - IER_MSI = (1<<3), -}; - -enum { - IIR_STAT = (1<<0), - IIR_ID0 = (1<<1), - IIR_ID1 = (1<<2), -}; - -enum { - LCR_WLS0 = (1<<0), - LCR_WLS1 = (1<<1), - LCR_STB = (1<<2), - LCR_PEN = (1<<3), - LCR_EPS = (1<<4), - LCR_SP = (1<<5), - LCR_SB = (1<<6), -}; - -enum { - MCR_DTR = (1<<0), - MCR_RTS = (1<<1), -}; - -enum { - LSR_DR = (1<<0), - LSR_OE = (1<<1), - LSR_PE = (1<<2), - LSR_FE = (1<<3), - LSR_BI = (1<<4), - LSR_THRE = (1<<5), - LSR_TEMT = (1<<6), -}; - -enum { - MSR_DCTS = (1<<0), - MSR_DDSR = (1<<1), - MSR_TERI = (1<<2), - MSR_DDCD = (1<<3), - MSR_CTS = (1<<4), - MSR_DSR = (1<<5), - MSR_RI = (1<<6), - MSR_DCD = (1<<7), -}; - -struct LM32UartState { - SysBusDevice busdev; - MemoryRegion iomem; - CharDriverState *chr; - qemu_irq irq; - - uint32_t regs[R_MAX]; -}; -typedef struct LM32UartState LM32UartState; - -static void uart_update_irq(LM32UartState *s) -{ - unsigned int irq; - - if ((s->regs[R_LSR] & (LSR_OE | LSR_PE | LSR_FE | LSR_BI)) - && (s->regs[R_IER] & IER_RLSI)) { - irq = 1; - s->regs[R_IIR] = IIR_ID1 | IIR_ID0; - } else if ((s->regs[R_LSR] & LSR_DR) && (s->regs[R_IER] & IER_RBRI)) { - irq = 1; - s->regs[R_IIR] = IIR_ID1; - } else if ((s->regs[R_LSR] & LSR_THRE) && (s->regs[R_IER] & IER_THRI)) { - irq = 1; - s->regs[R_IIR] = IIR_ID0; - } else if ((s->regs[R_MSR] & 0x0f) && (s->regs[R_IER] & IER_MSI)) { - irq = 1; - s->regs[R_IIR] = 0; - } else { - irq = 0; - s->regs[R_IIR] = IIR_STAT; - } - - trace_lm32_uart_irq_state(irq); - qemu_set_irq(s->irq, irq); -} - -static uint64_t uart_read(void *opaque, hwaddr addr, - unsigned size) -{ - LM32UartState *s = opaque; - uint32_t r = 0; - - addr >>= 2; - switch (addr) { - case R_RXTX: - r = s->regs[R_RXTX]; - s->regs[R_LSR] &= ~LSR_DR; - uart_update_irq(s); - qemu_chr_accept_input(s->chr); - break; - case R_IIR: - case R_LSR: - case R_MSR: - r = s->regs[addr]; - break; - case R_IER: - case R_LCR: - case R_MCR: - case R_DIV: - error_report("lm32_uart: read access to write only register 0x" - TARGET_FMT_plx, addr << 2); - break; - default: - error_report("lm32_uart: read access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } - - trace_lm32_uart_memory_read(addr << 2, r); - return r; -} - -static void uart_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - LM32UartState *s = opaque; - unsigned char ch = value; - - trace_lm32_uart_memory_write(addr, value); - - addr >>= 2; - switch (addr) { - case R_RXTX: - if (s->chr) { - qemu_chr_fe_write(s->chr, &ch, 1); - } - break; - case R_IER: - case R_LCR: - case R_MCR: - case R_DIV: - s->regs[addr] = value; - break; - case R_IIR: - case R_LSR: - case R_MSR: - error_report("lm32_uart: write access to read only register 0x" - TARGET_FMT_plx, addr << 2); - break; - default: - error_report("lm32_uart: write access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } - uart_update_irq(s); -} - -static const MemoryRegionOps uart_ops = { - .read = uart_read, - .write = uart_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - -static void uart_rx(void *opaque, const uint8_t *buf, int size) -{ - LM32UartState *s = opaque; - - if (s->regs[R_LSR] & LSR_DR) { - s->regs[R_LSR] |= LSR_OE; - } - - s->regs[R_LSR] |= LSR_DR; - s->regs[R_RXTX] = *buf; - - uart_update_irq(s); -} - -static int uart_can_rx(void *opaque) -{ - LM32UartState *s = opaque; - - return !(s->regs[R_LSR] & LSR_DR); -} - -static void uart_event(void *opaque, int event) -{ -} - -static void uart_reset(DeviceState *d) -{ - LM32UartState *s = container_of(d, LM32UartState, busdev.qdev); - int i; - - for (i = 0; i < R_MAX; i++) { - s->regs[i] = 0; - } - - /* defaults */ - s->regs[R_LSR] = LSR_THRE | LSR_TEMT; -} - -static int lm32_uart_init(SysBusDevice *dev) -{ - LM32UartState *s = FROM_SYSBUS(typeof(*s), dev); - - sysbus_init_irq(dev, &s->irq); - - memory_region_init_io(&s->iomem, &uart_ops, s, "uart", R_MAX * 4); - sysbus_init_mmio(dev, &s->iomem); - - s->chr = qemu_char_get_next_serial(); - if (s->chr) { - qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s); - } - - return 0; -} - -static const VMStateDescription vmstate_lm32_uart = { - .name = "lm32-uart", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, LM32UartState, R_MAX), - VMSTATE_END_OF_LIST() - } -}; - -static void lm32_uart_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = lm32_uart_init; - dc->reset = uart_reset; - dc->vmsd = &vmstate_lm32_uart; -} - -static const TypeInfo lm32_uart_info = { - .name = "lm32-uart", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(LM32UartState), - .class_init = lm32_uart_class_init, -}; - -static void lm32_uart_register_types(void) -{ - type_register_static(&lm32_uart_info); -} - -type_init(lm32_uart_register_types) diff --git a/hw/m68k/Makefile.objs b/hw/m68k/Makefile.objs index ebbe003..c4352e7 100644 --- a/hw/m68k/Makefile.objs +++ b/hw/m68k/Makefile.objs @@ -1,7 +1,3 @@ -obj-y = mcf_uart.o - -obj-y := $(addprefix ../,$(obj-y)) - obj-y += an5206.o mcf5208.o obj-y += dummy_m68k.o diff --git a/hw/mcf_uart.c b/hw/mcf_uart.c deleted file mode 100644 index 6724b1b..0000000 --- a/hw/mcf_uart.c +++ /dev/null @@ -1,307 +0,0 @@ -/* - * ColdFire UART emulation. - * - * Copyright (c) 2007 CodeSourcery. - * - * This code is licensed under the GPL - */ -#include "hw/hw.h" -#include "hw/m68k/mcf.h" -#include "char/char.h" -#include "exec/address-spaces.h" - -typedef struct { - MemoryRegion iomem; - uint8_t mr[2]; - uint8_t sr; - uint8_t isr; - uint8_t imr; - uint8_t bg1; - uint8_t bg2; - uint8_t fifo[4]; - uint8_t tb; - int current_mr; - int fifo_len; - int tx_enabled; - int rx_enabled; - qemu_irq irq; - CharDriverState *chr; -} mcf_uart_state; - -/* UART Status Register bits. */ -#define MCF_UART_RxRDY 0x01 -#define MCF_UART_FFULL 0x02 -#define MCF_UART_TxRDY 0x04 -#define MCF_UART_TxEMP 0x08 -#define MCF_UART_OE 0x10 -#define MCF_UART_PE 0x20 -#define MCF_UART_FE 0x40 -#define MCF_UART_RB 0x80 - -/* Interrupt flags. */ -#define MCF_UART_TxINT 0x01 -#define MCF_UART_RxINT 0x02 -#define MCF_UART_DBINT 0x04 -#define MCF_UART_COSINT 0x80 - -/* UMR1 flags. */ -#define MCF_UART_BC0 0x01 -#define MCF_UART_BC1 0x02 -#define MCF_UART_PT 0x04 -#define MCF_UART_PM0 0x08 -#define MCF_UART_PM1 0x10 -#define MCF_UART_ERR 0x20 -#define MCF_UART_RxIRQ 0x40 -#define MCF_UART_RxRTS 0x80 - -static void mcf_uart_update(mcf_uart_state *s) -{ - s->isr &= ~(MCF_UART_TxINT | MCF_UART_RxINT); - if (s->sr & MCF_UART_TxRDY) - s->isr |= MCF_UART_TxINT; - if ((s->sr & ((s->mr[0] & MCF_UART_RxIRQ) - ? MCF_UART_FFULL : MCF_UART_RxRDY)) != 0) - s->isr |= MCF_UART_RxINT; - - qemu_set_irq(s->irq, (s->isr & s->imr) != 0); -} - -uint64_t mcf_uart_read(void *opaque, hwaddr addr, - unsigned size) -{ - mcf_uart_state *s = (mcf_uart_state *)opaque; - switch (addr & 0x3f) { - case 0x00: - return s->mr[s->current_mr]; - case 0x04: - return s->sr; - case 0x0c: - { - uint8_t val; - int i; - - if (s->fifo_len == 0) - return 0; - - val = s->fifo[0]; - s->fifo_len--; - for (i = 0; i < s->fifo_len; i++) - s->fifo[i] = s->fifo[i + 1]; - s->sr &= ~MCF_UART_FFULL; - if (s->fifo_len == 0) - s->sr &= ~MCF_UART_RxRDY; - mcf_uart_update(s); - qemu_chr_accept_input(s->chr); - return val; - } - case 0x10: - /* TODO: Implement IPCR. */ - return 0; - case 0x14: - return s->isr; - case 0x18: - return s->bg1; - case 0x1c: - return s->bg2; - default: - return 0; - } -} - -/* Update TxRDY flag and set data if present and enabled. */ -static void mcf_uart_do_tx(mcf_uart_state *s) -{ - if (s->tx_enabled && (s->sr & MCF_UART_TxEMP) == 0) { - if (s->chr) - qemu_chr_fe_write(s->chr, (unsigned char *)&s->tb, 1); - s->sr |= MCF_UART_TxEMP; - } - if (s->tx_enabled) { - s->sr |= MCF_UART_TxRDY; - } else { - s->sr &= ~MCF_UART_TxRDY; - } -} - -static void mcf_do_command(mcf_uart_state *s, uint8_t cmd) -{ - /* Misc command. */ - switch ((cmd >> 4) & 3) { - case 0: /* No-op. */ - break; - case 1: /* Reset mode register pointer. */ - s->current_mr = 0; - break; - case 2: /* Reset receiver. */ - s->rx_enabled = 0; - s->fifo_len = 0; - s->sr &= ~(MCF_UART_RxRDY | MCF_UART_FFULL); - break; - case 3: /* Reset transmitter. */ - s->tx_enabled = 0; - s->sr |= MCF_UART_TxEMP; - s->sr &= ~MCF_UART_TxRDY; - break; - case 4: /* Reset error status. */ - break; - case 5: /* Reset break-change interrupt. */ - s->isr &= ~MCF_UART_DBINT; - break; - case 6: /* Start break. */ - case 7: /* Stop break. */ - break; - } - - /* Transmitter command. */ - switch ((cmd >> 2) & 3) { - case 0: /* No-op. */ - break; - case 1: /* Enable. */ - s->tx_enabled = 1; - mcf_uart_do_tx(s); - break; - case 2: /* Disable. */ - s->tx_enabled = 0; - mcf_uart_do_tx(s); - break; - case 3: /* Reserved. */ - fprintf(stderr, "mcf_uart: Bad TX command\n"); - break; - } - - /* Receiver command. */ - switch (cmd & 3) { - case 0: /* No-op. */ - break; - case 1: /* Enable. */ - s->rx_enabled = 1; - break; - case 2: - s->rx_enabled = 0; - break; - case 3: /* Reserved. */ - fprintf(stderr, "mcf_uart: Bad RX command\n"); - break; - } -} - -void mcf_uart_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) -{ - mcf_uart_state *s = (mcf_uart_state *)opaque; - switch (addr & 0x3f) { - case 0x00: - s->mr[s->current_mr] = val; - s->current_mr = 1; - break; - case 0x04: - /* CSR is ignored. */ - break; - case 0x08: /* Command Register. */ - mcf_do_command(s, val); - break; - case 0x0c: /* Transmit Buffer. */ - s->sr &= ~MCF_UART_TxEMP; - s->tb = val; - mcf_uart_do_tx(s); - break; - case 0x10: - /* ACR is ignored. */ - break; - case 0x14: - s->imr = val; - break; - default: - break; - } - mcf_uart_update(s); -} - -static void mcf_uart_reset(mcf_uart_state *s) -{ - s->fifo_len = 0; - s->mr[0] = 0; - s->mr[1] = 0; - s->sr = MCF_UART_TxEMP; - s->tx_enabled = 0; - s->rx_enabled = 0; - s->isr = 0; - s->imr = 0; -} - -static void mcf_uart_push_byte(mcf_uart_state *s, uint8_t data) -{ - /* Break events overwrite the last byte if the fifo is full. */ - if (s->fifo_len == 4) - s->fifo_len--; - - s->fifo[s->fifo_len] = data; - s->fifo_len++; - s->sr |= MCF_UART_RxRDY; - if (s->fifo_len == 4) - s->sr |= MCF_UART_FFULL; - - mcf_uart_update(s); -} - -static void mcf_uart_event(void *opaque, int event) -{ - mcf_uart_state *s = (mcf_uart_state *)opaque; - - switch (event) { - case CHR_EVENT_BREAK: - s->isr |= MCF_UART_DBINT; - mcf_uart_push_byte(s, 0); - break; - default: - break; - } -} - -static int mcf_uart_can_receive(void *opaque) -{ - mcf_uart_state *s = (mcf_uart_state *)opaque; - - return s->rx_enabled && (s->sr & MCF_UART_FFULL) == 0; -} - -static void mcf_uart_receive(void *opaque, const uint8_t *buf, int size) -{ - mcf_uart_state *s = (mcf_uart_state *)opaque; - - mcf_uart_push_byte(s, buf[0]); -} - -void *mcf_uart_init(qemu_irq irq, CharDriverState *chr) -{ - mcf_uart_state *s; - - s = g_malloc0(sizeof(mcf_uart_state)); - s->chr = chr; - s->irq = irq; - if (chr) { - qemu_chr_fe_claim_no_fail(chr); - qemu_chr_add_handlers(chr, mcf_uart_can_receive, mcf_uart_receive, - mcf_uart_event, s); - } - mcf_uart_reset(s); - return s; -} - -static const MemoryRegionOps mcf_uart_ops = { - .read = mcf_uart_read, - .write = mcf_uart_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -void mcf_uart_mm_init(MemoryRegion *sysmem, - hwaddr base, - qemu_irq irq, - CharDriverState *chr) -{ - mcf_uart_state *s; - - s = mcf_uart_init(irq, chr); - memory_region_init_io(&s->iomem, &mcf_uart_ops, s, "uart", 0x40); - memory_region_add_subregion(sysmem, base, &s->iomem); -} diff --git a/hw/milkymist-uart.c b/hw/milkymist-uart.c deleted file mode 100644 index f3bdf69..0000000 --- a/hw/milkymist-uart.c +++ /dev/null @@ -1,244 +0,0 @@ -/* - * QEMU model of the Milkymist UART block. - * - * Copyright (c) 2010 Michael Walle - * - * 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 . - * - * - * Specification available at: - * http://www.milkymist.org/socdoc/uart.pdf - */ - -#include "hw/hw.h" -#include "hw/sysbus.h" -#include "trace.h" -#include "char/char.h" -#include "qemu/error-report.h" - -enum { - R_RXTX = 0, - R_DIV, - R_STAT, - R_CTRL, - R_DBG, - R_MAX -}; - -enum { - STAT_THRE = (1<<0), - STAT_RX_EVT = (1<<1), - STAT_TX_EVT = (1<<2), -}; - -enum { - CTRL_RX_IRQ_EN = (1<<0), - CTRL_TX_IRQ_EN = (1<<1), - CTRL_THRU_EN = (1<<2), -}; - -enum { - DBG_BREAK_EN = (1<<0), -}; - -struct MilkymistUartState { - SysBusDevice busdev; - MemoryRegion regs_region; - CharDriverState *chr; - qemu_irq irq; - - uint32_t regs[R_MAX]; -}; -typedef struct MilkymistUartState MilkymistUartState; - -static void uart_update_irq(MilkymistUartState *s) -{ - int rx_event = s->regs[R_STAT] & STAT_RX_EVT; - int tx_event = s->regs[R_STAT] & STAT_TX_EVT; - int rx_irq_en = s->regs[R_CTRL] & CTRL_RX_IRQ_EN; - int tx_irq_en = s->regs[R_CTRL] & CTRL_TX_IRQ_EN; - - if ((rx_irq_en && rx_event) || (tx_irq_en && tx_event)) { - trace_milkymist_uart_raise_irq(); - qemu_irq_raise(s->irq); - } else { - trace_milkymist_uart_lower_irq(); - qemu_irq_lower(s->irq); - } -} - -static uint64_t uart_read(void *opaque, hwaddr addr, - unsigned size) -{ - MilkymistUartState *s = opaque; - uint32_t r = 0; - - addr >>= 2; - switch (addr) { - case R_RXTX: - r = s->regs[addr]; - break; - case R_DIV: - case R_STAT: - case R_CTRL: - case R_DBG: - r = s->regs[addr]; - break; - - default: - error_report("milkymist_uart: read access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } - - trace_milkymist_uart_memory_read(addr << 2, r); - - return r; -} - -static void uart_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - MilkymistUartState *s = opaque; - unsigned char ch = value; - - trace_milkymist_uart_memory_write(addr, value); - - addr >>= 2; - switch (addr) { - case R_RXTX: - if (s->chr) { - qemu_chr_fe_write(s->chr, &ch, 1); - } - s->regs[R_STAT] |= STAT_TX_EVT; - break; - case R_DIV: - case R_CTRL: - case R_DBG: - s->regs[addr] = value; - break; - - case R_STAT: - /* write one to clear bits */ - s->regs[addr] &= ~(value & (STAT_RX_EVT | STAT_TX_EVT)); - qemu_chr_accept_input(s->chr); - break; - - default: - error_report("milkymist_uart: write access to unknown register 0x" - TARGET_FMT_plx, addr << 2); - break; - } - - uart_update_irq(s); -} - -static const MemoryRegionOps uart_mmio_ops = { - .read = uart_read, - .write = uart_write, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -static void uart_rx(void *opaque, const uint8_t *buf, int size) -{ - MilkymistUartState *s = opaque; - - assert(!(s->regs[R_STAT] & STAT_RX_EVT)); - - s->regs[R_STAT] |= STAT_RX_EVT; - s->regs[R_RXTX] = *buf; - - uart_update_irq(s); -} - -static int uart_can_rx(void *opaque) -{ - MilkymistUartState *s = opaque; - - return !(s->regs[R_STAT] & STAT_RX_EVT); -} - -static void uart_event(void *opaque, int event) -{ -} - -static void milkymist_uart_reset(DeviceState *d) -{ - MilkymistUartState *s = container_of(d, MilkymistUartState, busdev.qdev); - int i; - - for (i = 0; i < R_MAX; i++) { - s->regs[i] = 0; - } - - /* THRE is always set */ - s->regs[R_STAT] = STAT_THRE; -} - -static int milkymist_uart_init(SysBusDevice *dev) -{ - MilkymistUartState *s = FROM_SYSBUS(typeof(*s), dev); - - sysbus_init_irq(dev, &s->irq); - - memory_region_init_io(&s->regs_region, &uart_mmio_ops, s, - "milkymist-uart", R_MAX * 4); - sysbus_init_mmio(dev, &s->regs_region); - - s->chr = qemu_char_get_next_serial(); - if (s->chr) { - qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s); - } - - return 0; -} - -static const VMStateDescription vmstate_milkymist_uart = { - .name = "milkymist-uart", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, MilkymistUartState, R_MAX), - VMSTATE_END_OF_LIST() - } -}; - -static void milkymist_uart_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = milkymist_uart_init; - dc->reset = milkymist_uart_reset; - dc->vmsd = &vmstate_milkymist_uart; -} - -static const TypeInfo milkymist_uart_info = { - .name = "milkymist-uart", - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(MilkymistUartState), - .class_init = milkymist_uart_class_init, -}; - -static void milkymist_uart_register_types(void) -{ - type_register_static(&milkymist_uart_info); -} - -type_init(milkymist_uart_register_types) diff --git a/hw/omap_uart.c b/hw/omap_uart.c deleted file mode 100644 index 26c1426..0000000 --- a/hw/omap_uart.c +++ /dev/null @@ -1,187 +0,0 @@ -/* - * TI OMAP processors UART emulation. - * - * Copyright (C) 2006-2008 Andrzej Zaborowski - * Copyright (C) 2007-2009 Nokia Corporation - * - * 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 or - * (at your option) version 3 of the License. - * - * 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 . - */ -#include "char/char.h" -#include "hw/hw.h" -#include "hw/arm/omap.h" -#include "hw/char/serial.h" -#include "exec/address-spaces.h" - -/* UARTs */ -struct omap_uart_s { - MemoryRegion iomem; - hwaddr base; - SerialState *serial; /* TODO */ - struct omap_target_agent_s *ta; - omap_clk fclk; - qemu_irq irq; - - uint8_t eblr; - uint8_t syscontrol; - uint8_t wkup; - uint8_t cfps; - uint8_t mdr[2]; - uint8_t scr; - uint8_t clksel; -}; - -void omap_uart_reset(struct omap_uart_s *s) -{ - s->eblr = 0x00; - s->syscontrol = 0; - s->wkup = 0x3f; - s->cfps = 0x69; - s->clksel = 0; -} - -struct omap_uart_s *omap_uart_init(hwaddr base, - qemu_irq irq, omap_clk fclk, omap_clk iclk, - qemu_irq txdma, qemu_irq rxdma, - const char *label, CharDriverState *chr) -{ - struct omap_uart_s *s = (struct omap_uart_s *) - g_malloc0(sizeof(struct omap_uart_s)); - - s->base = base; - s->fclk = fclk; - s->irq = irq; - s->serial = serial_mm_init(get_system_memory(), base, 2, irq, - omap_clk_getrate(fclk)/16, - chr ?: qemu_chr_new(label, "null", NULL), - DEVICE_NATIVE_ENDIAN); - return s; -} - -static uint64_t omap_uart_read(void *opaque, hwaddr addr, - unsigned size) -{ - struct omap_uart_s *s = (struct omap_uart_s *) opaque; - - if (size == 4) { - return omap_badwidth_read8(opaque, addr); - } - - switch (addr) { - case 0x20: /* MDR1 */ - return s->mdr[0]; - case 0x24: /* MDR2 */ - return s->mdr[1]; - case 0x40: /* SCR */ - return s->scr; - case 0x44: /* SSR */ - return 0x0; - case 0x48: /* EBLR (OMAP2) */ - return s->eblr; - case 0x4C: /* OSC_12M_SEL (OMAP1) */ - return s->clksel; - case 0x50: /* MVR */ - return 0x30; - case 0x54: /* SYSC (OMAP2) */ - return s->syscontrol; - case 0x58: /* SYSS (OMAP2) */ - return 1; - case 0x5c: /* WER (OMAP2) */ - return s->wkup; - case 0x60: /* CFPS (OMAP2) */ - return s->cfps; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_uart_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - struct omap_uart_s *s = (struct omap_uart_s *) opaque; - - if (size == 4) { - return omap_badwidth_write8(opaque, addr, value); - } - - switch (addr) { - case 0x20: /* MDR1 */ - s->mdr[0] = value & 0x7f; - break; - case 0x24: /* MDR2 */ - s->mdr[1] = value & 0xff; - break; - case 0x40: /* SCR */ - s->scr = value & 0xff; - break; - case 0x48: /* EBLR (OMAP2) */ - s->eblr = value & 0xff; - break; - case 0x4C: /* OSC_12M_SEL (OMAP1) */ - s->clksel = value & 1; - break; - case 0x44: /* SSR */ - case 0x50: /* MVR */ - case 0x58: /* SYSS (OMAP2) */ - OMAP_RO_REG(addr); - break; - case 0x54: /* SYSC (OMAP2) */ - s->syscontrol = value & 0x1d; - if (value & 2) - omap_uart_reset(s); - break; - case 0x5c: /* WER (OMAP2) */ - s->wkup = value & 0x7f; - break; - case 0x60: /* CFPS (OMAP2) */ - s->cfps = value & 0xff; - break; - default: - OMAP_BAD_REG(addr); - } -} - -static const MemoryRegionOps omap_uart_ops = { - .read = omap_uart_read, - .write = omap_uart_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -struct omap_uart_s *omap2_uart_init(MemoryRegion *sysmem, - struct omap_target_agent_s *ta, - qemu_irq irq, omap_clk fclk, omap_clk iclk, - qemu_irq txdma, qemu_irq rxdma, - const char *label, CharDriverState *chr) -{ - hwaddr base = omap_l4_attach(ta, 0, NULL); - struct omap_uart_s *s = omap_uart_init(base, irq, - fclk, iclk, txdma, rxdma, label, chr); - - memory_region_init_io(&s->iomem, &omap_uart_ops, s, "omap.uart", 0x100); - - s->ta = ta; - - memory_region_add_subregion(sysmem, base + 0x20, &s->iomem); - - return s; -} - -void omap_uart_attach(struct omap_uart_s *s, CharDriverState *chr) -{ - /* TODO: Should reuse or destroy current s->serial */ - s->serial = serial_mm_init(get_system_memory(), s->base, 2, s->irq, - omap_clk_getrate(s->fclk) / 16, - chr ?: qemu_chr_new("null", "null", NULL), - DEVICE_NATIVE_ENDIAN); -} diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index 2d51ae9..1e24b58 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -1,5 +1,4 @@ # IBM pSeries (sPAPR) -obj-$(CONFIG_PSERIES) += spapr_vty.o obj-$(CONFIG_PSERIES) += spapr_pci.o obj-$(CONFIG_PSERIES) += spapr_nvram.o # PowerPC 4xx boards diff --git a/hw/s390x/Makefile.objs b/hw/s390x/Makefile.objs index 9f2f419..77e1218 100644 --- a/hw/s390x/Makefile.objs +++ b/hw/s390x/Makefile.objs @@ -2,7 +2,7 @@ obj-y = s390-virtio-bus.o s390-virtio.o obj-y += s390-virtio-hcall.o obj-y += sclp.o obj-y += event-facility.o -obj-y += sclpquiesce.o sclpconsole.o +obj-y += sclpquiesce.o obj-y += ipl.o obj-y += css.o obj-y += s390-virtio-ccw.o diff --git a/hw/s390x/sclpconsole.c b/hw/s390x/sclpconsole.c deleted file mode 100644 index 5c881e5..0000000 --- a/hw/s390x/sclpconsole.c +++ /dev/null @@ -1,305 +0,0 @@ -/* - * SCLP event type - * Ascii Console Data (VT220 Console) - * - * Copyright IBM, Corp. 2012 - * - * Authors: - * Heinz Graalfs - * - * This work is licensed under the terms of the GNU GPL, version 2 or (at your - * option) any later version. See the COPYING file in the top-level directory. - * - */ - -#include -#include "qemu/thread.h" -#include "qemu/error-report.h" - -#include "hw/s390x/sclp.h" -#include "hw/s390x/event-facility.h" -#include "char/char.h" - -typedef struct ASCIIConsoleData { - EventBufferHeader ebh; - char data[0]; -} QEMU_PACKED ASCIIConsoleData; - -/* max size for ASCII data in 4K SCCB page */ -#define SIZE_BUFFER_VT220 4080 - -typedef struct SCLPConsole { - SCLPEvent event; - CharDriverState *chr; - /* io vector */ - uint8_t *iov; /* iov buffer pointer */ - uint8_t *iov_sclp; /* pointer to SCLP read offset */ - uint8_t *iov_bs; /* pointer byte stream read offset */ - uint32_t iov_data_len; /* length of byte stream in buffer */ - uint32_t iov_sclp_rest; /* length of byte stream not read via SCLP */ - qemu_irq irq_read_vt220; -} SCLPConsole; - -/* character layer call-back functions */ - -/* Return number of bytes that fit into iov buffer */ -static int chr_can_read(void *opaque) -{ - SCLPConsole *scon = opaque; - - return scon->iov ? SIZE_BUFFER_VT220 - scon->iov_data_len : 0; -} - -/* Receive n bytes from character layer, save in iov buffer, - * and set event pending */ -static void receive_from_chr_layer(SCLPConsole *scon, const uint8_t *buf, - int size) -{ - assert(scon->iov); - - /* read data must fit into current buffer */ - assert(size <= SIZE_BUFFER_VT220 - scon->iov_data_len); - - /* put byte-stream from character layer into buffer */ - memcpy(scon->iov_bs, buf, size); - scon->iov_data_len += size; - scon->iov_sclp_rest += size; - scon->iov_bs += size; - scon->event.event_pending = true; -} - -/* Send data from a char device over to the guest */ -static void chr_read(void *opaque, const uint8_t *buf, int size) -{ - SCLPConsole *scon = opaque; - - assert(scon); - - receive_from_chr_layer(scon, buf, size); - /* trigger SCLP read operation */ - qemu_irq_raise(scon->irq_read_vt220); -} - -static void chr_event(void *opaque, int event) -{ - SCLPConsole *scon = opaque; - - switch (event) { - case CHR_EVENT_OPENED: - if (!scon->iov) { - scon->iov = g_malloc0(SIZE_BUFFER_VT220); - scon->iov_sclp = scon->iov; - scon->iov_bs = scon->iov; - scon->iov_data_len = 0; - scon->iov_sclp_rest = 0; - } - break; - case CHR_EVENT_CLOSED: - if (scon->iov) { - g_free(scon->iov); - scon->iov = NULL; - } - break; - } -} - -/* functions to be called by event facility */ - -static int event_type(void) -{ - return SCLP_EVENT_ASCII_CONSOLE_DATA; -} - -static unsigned int send_mask(void) -{ - return SCLP_EVENT_MASK_MSG_ASCII; -} - -static unsigned int receive_mask(void) -{ - return SCLP_EVENT_MASK_MSG_ASCII; -} - -/* triggered by SCLP's read_event_data - - * copy console data byte-stream into provided (SCLP) buffer - */ -static void get_console_data(SCLPEvent *event, uint8_t *buf, size_t *size, - int avail) -{ - SCLPConsole *cons = DO_UPCAST(SCLPConsole, event, event); - - /* first byte is hex 0 saying an ascii string follows */ - *buf++ = '\0'; - avail--; - /* if all data fit into provided SCLP buffer */ - if (avail >= cons->iov_sclp_rest) { - /* copy character byte-stream to SCLP buffer */ - memcpy(buf, cons->iov_sclp, cons->iov_sclp_rest); - *size = cons->iov_sclp_rest + 1; - cons->iov_sclp = cons->iov; - cons->iov_bs = cons->iov; - cons->iov_data_len = 0; - cons->iov_sclp_rest = 0; - event->event_pending = false; - /* data provided and no more data pending */ - } else { - /* if provided buffer is too small, just copy part */ - memcpy(buf, cons->iov_sclp, avail); - *size = avail + 1; - cons->iov_sclp_rest -= avail; - cons->iov_sclp += avail; - /* more data pending */ - } -} - -static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr, - int *slen) -{ - int avail; - size_t src_len; - uint8_t *to; - ASCIIConsoleData *acd = (ASCIIConsoleData *) evt_buf_hdr; - - if (!event->event_pending) { - /* no data pending */ - return 0; - } - - to = (uint8_t *)&acd->data; - avail = *slen - sizeof(ASCIIConsoleData); - get_console_data(event, to, &src_len, avail); - - acd->ebh.length = cpu_to_be16(sizeof(ASCIIConsoleData) + src_len); - acd->ebh.type = SCLP_EVENT_ASCII_CONSOLE_DATA; - acd->ebh.flags |= SCLP_EVENT_BUFFER_ACCEPTED; - *slen = avail - src_len; - - return 1; -} - -/* triggered by SCLP's write_event_data - * - write console data to character layer - * returns < 0 if an error occurred - */ -static ssize_t write_console_data(SCLPEvent *event, const uint8_t *buf, - size_t len) -{ - ssize_t ret = 0; - const uint8_t *iov_offset; - SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event); - - if (!scon->chr) { - /* If there's no backend, we can just say we consumed all data. */ - return len; - } - - iov_offset = buf; - while (len > 0) { - ret = qemu_chr_fe_write(scon->chr, buf, len); - if (ret == 0) { - /* a pty doesn't seem to be connected - no error */ - len = 0; - } else if (ret == -EAGAIN || (ret > 0 && ret < len)) { - len -= ret; - iov_offset += ret; - } else { - len = 0; - } - } - - return ret; -} - -static int write_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr) -{ - int rc; - int length; - ssize_t written; - ASCIIConsoleData *acd = (ASCIIConsoleData *) evt_buf_hdr; - - length = be16_to_cpu(evt_buf_hdr->length) - sizeof(EventBufferHeader); - written = write_console_data(event, (uint8_t *)acd->data, length); - - rc = SCLP_RC_NORMAL_COMPLETION; - /* set event buffer accepted flag */ - evt_buf_hdr->flags |= SCLP_EVENT_BUFFER_ACCEPTED; - - /* written will be zero if a pty is not connected - don't treat as error */ - if (written < 0) { - /* event buffer not accepted due to error in character layer */ - evt_buf_hdr->flags &= ~(SCLP_EVENT_BUFFER_ACCEPTED); - rc = SCLP_RC_CONTAINED_EQUIPMENT_CHECK; - } - - return rc; -} - -static void trigger_ascii_console_data(void *env, int n, int level) -{ - sclp_service_interrupt(0); -} - -/* qemu object creation and initialization functions */ - -/* tell character layer our call-back functions */ -static int console_init(SCLPEvent *event) -{ - static bool console_available; - - SCLPConsole *scon = DO_UPCAST(SCLPConsole, event, event); - - if (console_available) { - error_report("Multiple VT220 operator consoles are not supported"); - return -1; - } - console_available = true; - event->event_type = SCLP_EVENT_ASCII_CONSOLE_DATA; - if (scon->chr) { - qemu_chr_add_handlers(scon->chr, chr_can_read, - chr_read, chr_event, scon); - } - scon->irq_read_vt220 = *qemu_allocate_irqs(trigger_ascii_console_data, - NULL, 1); - - return 0; -} - -static int console_exit(SCLPEvent *event) -{ - return 0; -} - -static Property console_properties[] = { - DEFINE_PROP_CHR("chardev", SCLPConsole, chr), - DEFINE_PROP_END_OF_LIST(), -}; - -static void console_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SCLPEventClass *ec = SCLP_EVENT_CLASS(klass); - - dc->props = console_properties; - ec->init = console_init; - ec->exit = console_exit; - ec->get_send_mask = send_mask; - ec->get_receive_mask = receive_mask; - ec->event_type = event_type; - ec->read_event_data = read_event_data; - ec->write_event_data = write_event_data; -} - -static const TypeInfo sclp_console_info = { - .name = "sclpconsole", - .parent = TYPE_SCLP_EVENT, - .instance_size = sizeof(SCLPConsole), - .class_init = console_class_init, - .class_size = sizeof(SCLPEventClass), -}; - -static void register_types(void) -{ - type_register_static(&sclp_console_info); -} - -type_init(register_types) diff --git a/hw/sh4/Makefile.objs b/hw/sh4/Makefile.objs index c9b0416..10c971a 100644 --- a/hw/sh4/Makefile.objs +++ b/hw/sh4/Makefile.objs @@ -1,4 +1,4 @@ -obj-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o +obj-y += sh_timer.o sh_intc.o sh_pci.o obj-y := $(addprefix ../,$(obj-y)) diff --git a/hw/sh_serial.c b/hw/sh_serial.c deleted file mode 100644 index 450c7d8..0000000 --- a/hw/sh_serial.c +++ /dev/null @@ -1,410 +0,0 @@ -/* - * QEMU SCI/SCIF serial port emulation - * - * Copyright (c) 2007 Magnus Damm - * - * Based on serial.c - QEMU 16450 UART emulation - * Copyright (c) 2003-2004 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/hw.h" -#include "hw/sh4/sh.h" -#include "char/char.h" -#include "exec/address-spaces.h" - -//#define DEBUG_SERIAL - -#define SH_SERIAL_FLAG_TEND (1 << 0) -#define SH_SERIAL_FLAG_TDE (1 << 1) -#define SH_SERIAL_FLAG_RDF (1 << 2) -#define SH_SERIAL_FLAG_BRK (1 << 3) -#define SH_SERIAL_FLAG_DR (1 << 4) - -#define SH_RX_FIFO_LENGTH (16) - -typedef struct { - MemoryRegion iomem; - MemoryRegion iomem_p4; - MemoryRegion iomem_a7; - uint8_t smr; - uint8_t brr; - uint8_t scr; - uint8_t dr; /* ftdr / tdr */ - uint8_t sr; /* fsr / ssr */ - uint16_t fcr; - uint8_t sptr; - - uint8_t rx_fifo[SH_RX_FIFO_LENGTH]; /* frdr / rdr */ - uint8_t rx_cnt; - uint8_t rx_tail; - uint8_t rx_head; - - int freq; - int feat; - int flags; - int rtrg; - - CharDriverState *chr; - - qemu_irq eri; - qemu_irq rxi; - qemu_irq txi; - qemu_irq tei; - qemu_irq bri; -} sh_serial_state; - -static void sh_serial_clear_fifo(sh_serial_state * s) -{ - memset(s->rx_fifo, 0, SH_RX_FIFO_LENGTH); - s->rx_cnt = 0; - s->rx_head = 0; - s->rx_tail = 0; -} - -static void sh_serial_write(void *opaque, hwaddr offs, - uint64_t val, unsigned size) -{ - sh_serial_state *s = opaque; - unsigned char ch; - -#ifdef DEBUG_SERIAL - printf("sh_serial: write offs=0x%02x val=0x%02x\n", - offs, val); -#endif - switch(offs) { - case 0x00: /* SMR */ - s->smr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0x7b : 0xff); - return; - case 0x04: /* BRR */ - s->brr = val; - return; - case 0x08: /* SCR */ - /* TODO : For SH7751, SCIF mask should be 0xfb. */ - s->scr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0xfa : 0xff); - if (!(val & (1 << 5))) - s->flags |= SH_SERIAL_FLAG_TEND; - if ((s->feat & SH_SERIAL_FEAT_SCIF) && s->txi) { - qemu_set_irq(s->txi, val & (1 << 7)); - } - if (!(val & (1 << 6))) { - qemu_set_irq(s->rxi, 0); - } - return; - case 0x0c: /* FTDR / TDR */ - if (s->chr) { - ch = val; - qemu_chr_fe_write(s->chr, &ch, 1); - } - s->dr = val; - s->flags &= ~SH_SERIAL_FLAG_TDE; - return; -#if 0 - case 0x14: /* FRDR / RDR */ - ret = 0; - break; -#endif - } - if (s->feat & SH_SERIAL_FEAT_SCIF) { - switch(offs) { - case 0x10: /* FSR */ - if (!(val & (1 << 6))) - s->flags &= ~SH_SERIAL_FLAG_TEND; - if (!(val & (1 << 5))) - s->flags &= ~SH_SERIAL_FLAG_TDE; - if (!(val & (1 << 4))) - s->flags &= ~SH_SERIAL_FLAG_BRK; - if (!(val & (1 << 1))) - s->flags &= ~SH_SERIAL_FLAG_RDF; - if (!(val & (1 << 0))) - s->flags &= ~SH_SERIAL_FLAG_DR; - - if (!(val & (1 << 1)) || !(val & (1 << 0))) { - if (s->rxi) { - qemu_set_irq(s->rxi, 0); - } - } - return; - case 0x18: /* FCR */ - s->fcr = val; - switch ((val >> 6) & 3) { - case 0: - s->rtrg = 1; - break; - case 1: - s->rtrg = 4; - break; - case 2: - s->rtrg = 8; - break; - case 3: - s->rtrg = 14; - break; - } - if (val & (1 << 1)) { - sh_serial_clear_fifo(s); - s->sr &= ~(1 << 1); - } - - return; - case 0x20: /* SPTR */ - s->sptr = val & 0xf3; - return; - case 0x24: /* LSR */ - return; - } - } - else { - switch(offs) { -#if 0 - case 0x0c: - ret = s->dr; - break; - case 0x10: - ret = 0; - break; -#endif - case 0x1c: - s->sptr = val & 0x8f; - return; - } - } - - fprintf(stderr, "sh_serial: unsupported write to 0x%02" - HWADDR_PRIx "\n", offs); - abort(); -} - -static uint64_t sh_serial_read(void *opaque, hwaddr offs, - unsigned size) -{ - sh_serial_state *s = opaque; - uint32_t ret = ~0; - -#if 0 - switch(offs) { - case 0x00: - ret = s->smr; - break; - case 0x04: - ret = s->brr; - break; - case 0x08: - ret = s->scr; - break; - case 0x14: - ret = 0; - break; - } -#endif - if (s->feat & SH_SERIAL_FEAT_SCIF) { - switch(offs) { - case 0x00: /* SMR */ - ret = s->smr; - break; - case 0x08: /* SCR */ - ret = s->scr; - break; - case 0x10: /* FSR */ - ret = 0; - if (s->flags & SH_SERIAL_FLAG_TEND) - ret |= (1 << 6); - if (s->flags & SH_SERIAL_FLAG_TDE) - ret |= (1 << 5); - if (s->flags & SH_SERIAL_FLAG_BRK) - ret |= (1 << 4); - if (s->flags & SH_SERIAL_FLAG_RDF) - ret |= (1 << 1); - if (s->flags & SH_SERIAL_FLAG_DR) - ret |= (1 << 0); - - if (s->scr & (1 << 5)) - s->flags |= SH_SERIAL_FLAG_TDE | SH_SERIAL_FLAG_TEND; - - break; - case 0x14: - if (s->rx_cnt > 0) { - ret = s->rx_fifo[s->rx_tail++]; - s->rx_cnt--; - if (s->rx_tail == SH_RX_FIFO_LENGTH) - s->rx_tail = 0; - if (s->rx_cnt < s->rtrg) - s->flags &= ~SH_SERIAL_FLAG_RDF; - } - break; -#if 0 - case 0x18: - ret = s->fcr; - break; -#endif - case 0x1c: - ret = s->rx_cnt; - break; - case 0x20: - ret = s->sptr; - break; - case 0x24: - ret = 0; - break; - } - } - else { - switch(offs) { -#if 0 - case 0x0c: - ret = s->dr; - break; - case 0x10: - ret = 0; - break; - case 0x14: - ret = s->rx_fifo[0]; - break; -#endif - case 0x1c: - ret = s->sptr; - break; - } - } -#ifdef DEBUG_SERIAL - printf("sh_serial: read offs=0x%02x val=0x%x\n", - offs, ret); -#endif - - if (ret & ~((1 << 16) - 1)) { - fprintf(stderr, "sh_serial: unsupported read from 0x%02" - HWADDR_PRIx "\n", offs); - abort(); - } - - return ret; -} - -static int sh_serial_can_receive(sh_serial_state *s) -{ - return s->scr & (1 << 4); -} - -static void sh_serial_receive_break(sh_serial_state *s) -{ - if (s->feat & SH_SERIAL_FEAT_SCIF) - s->sr |= (1 << 4); -} - -static int sh_serial_can_receive1(void *opaque) -{ - sh_serial_state *s = opaque; - return sh_serial_can_receive(s); -} - -static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size) -{ - sh_serial_state *s = opaque; - - if (s->feat & SH_SERIAL_FEAT_SCIF) { - int i; - for (i = 0; i < size; i++) { - if (s->rx_cnt < SH_RX_FIFO_LENGTH) { - s->rx_fifo[s->rx_head++] = buf[i]; - if (s->rx_head == SH_RX_FIFO_LENGTH) { - s->rx_head = 0; - } - s->rx_cnt++; - if (s->rx_cnt >= s->rtrg) { - s->flags |= SH_SERIAL_FLAG_RDF; - if (s->scr & (1 << 6) && s->rxi) { - qemu_set_irq(s->rxi, 1); - } - } - } - } - } else { - s->rx_fifo[0] = buf[0]; - } -} - -static void sh_serial_event(void *opaque, int event) -{ - sh_serial_state *s = opaque; - if (event == CHR_EVENT_BREAK) - sh_serial_receive_break(s); -} - -static const MemoryRegionOps sh_serial_ops = { - .read = sh_serial_read, - .write = sh_serial_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; - -void sh_serial_init(MemoryRegion *sysmem, - hwaddr base, int feat, - uint32_t freq, CharDriverState *chr, - qemu_irq eri_source, - qemu_irq rxi_source, - qemu_irq txi_source, - qemu_irq tei_source, - qemu_irq bri_source) -{ - sh_serial_state *s; - - s = g_malloc0(sizeof(sh_serial_state)); - - s->feat = feat; - s->flags = SH_SERIAL_FLAG_TEND | SH_SERIAL_FLAG_TDE; - s->rtrg = 1; - - s->smr = 0; - s->brr = 0xff; - s->scr = 1 << 5; /* pretend that TX is enabled so early printk works */ - s->sptr = 0; - - if (feat & SH_SERIAL_FEAT_SCIF) { - s->fcr = 0; - } - else { - s->dr = 0xff; - } - - sh_serial_clear_fifo(s); - - memory_region_init_io(&s->iomem, &sh_serial_ops, s, - "serial", 0x100000000ULL); - - memory_region_init_alias(&s->iomem_p4, "serial-p4", &s->iomem, - 0, 0x28); - memory_region_add_subregion(sysmem, P4ADDR(base), &s->iomem_p4); - - memory_region_init_alias(&s->iomem_a7, "serial-a7", &s->iomem, - 0, 0x28); - memory_region_add_subregion(sysmem, A7ADDR(base), &s->iomem_a7); - - s->chr = chr; - - if (chr) { - qemu_chr_fe_claim_no_fail(chr); - qemu_chr_add_handlers(chr, sh_serial_can_receive1, sh_serial_receive1, - sh_serial_event, s); - } - - s->eri = eri_source; - s->rxi = rxi_source; - s->txi = txi_source; - s->tei = tei_source; - s->bri = bri_source; -} diff --git a/hw/spapr_vty.c b/hw/spapr_vty.c deleted file mode 100644 index 9df018a..0000000 --- a/hw/spapr_vty.c +++ /dev/null @@ -1,221 +0,0 @@ -#include "hw/qdev.h" -#include "char/char.h" -#include "hw/ppc/spapr.h" -#include "hw/ppc/spapr_vio.h" - -#define VTERM_BUFSIZE 16 - -typedef struct VIOsPAPRVTYDevice { - VIOsPAPRDevice sdev; - CharDriverState *chardev; - uint32_t in, out; - uint8_t buf[VTERM_BUFSIZE]; -} VIOsPAPRVTYDevice; - -static int vty_can_receive(void *opaque) -{ - VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)opaque; - - return (dev->in - dev->out) < VTERM_BUFSIZE; -} - -static void vty_receive(void *opaque, const uint8_t *buf, int size) -{ - VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)opaque; - int i; - - if ((dev->in == dev->out) && size) { - /* toggle line to simulate edge interrupt */ - qemu_irq_pulse(spapr_vio_qirq(&dev->sdev)); - } - for (i = 0; i < size; i++) { - assert((dev->in - dev->out) < VTERM_BUFSIZE); - dev->buf[dev->in++ % VTERM_BUFSIZE] = buf[i]; - } -} - -static int vty_getchars(VIOsPAPRDevice *sdev, uint8_t *buf, int max) -{ - VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)sdev; - int n = 0; - - while ((n < max) && (dev->out != dev->in)) { - buf[n++] = dev->buf[dev->out++ % VTERM_BUFSIZE]; - } - - return n; -} - -void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int len) -{ - VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)sdev; - - /* FIXME: should check the qemu_chr_fe_write() return value */ - qemu_chr_fe_write(dev->chardev, buf, len); -} - -static int spapr_vty_init(VIOsPAPRDevice *sdev) -{ - VIOsPAPRVTYDevice *dev = (VIOsPAPRVTYDevice *)sdev; - - if (!dev->chardev) { - fprintf(stderr, "spapr-vty: Can't create vty without a chardev!\n"); - exit(1); - } - - qemu_chr_add_handlers(dev->chardev, vty_can_receive, - vty_receive, NULL, dev); - - return 0; -} - -/* Forward declaration */ -static target_ulong h_put_term_char(PowerPCCPU *cpu, sPAPREnvironment *spapr, - target_ulong opcode, target_ulong *args) -{ - target_ulong reg = args[0]; - target_ulong len = args[1]; - target_ulong char0_7 = args[2]; - target_ulong char8_15 = args[3]; - VIOsPAPRDevice *sdev; - uint8_t buf[16]; - - sdev = vty_lookup(spapr, reg); - if (!sdev) { - return H_PARAMETER; - } - - if (len > 16) { - return H_PARAMETER; - } - - *((uint64_t *)buf) = cpu_to_be64(char0_7); - *((uint64_t *)buf + 1) = cpu_to_be64(char8_15); - - vty_putchars(sdev, buf, len); - - return H_SUCCESS; -} - -static target_ulong h_get_term_char(PowerPCCPU *cpu, sPAPREnvironment *spapr, - target_ulong opcode, target_ulong *args) -{ - target_ulong reg = args[0]; - target_ulong *len = args + 0; - target_ulong *char0_7 = args + 1; - target_ulong *char8_15 = args + 2; - VIOsPAPRDevice *sdev; - uint8_t buf[16]; - - sdev = vty_lookup(spapr, reg); - if (!sdev) { - return H_PARAMETER; - } - - *len = vty_getchars(sdev, buf, sizeof(buf)); - if (*len < 16) { - memset(buf + *len, 0, 16 - *len); - } - - *char0_7 = be64_to_cpu(*((uint64_t *)buf)); - *char8_15 = be64_to_cpu(*((uint64_t *)buf + 1)); - - return H_SUCCESS; -} - -void spapr_vty_create(VIOsPAPRBus *bus, CharDriverState *chardev) -{ - DeviceState *dev; - - dev = qdev_create(&bus->bus, "spapr-vty"); - qdev_prop_set_chr(dev, "chardev", chardev); - qdev_init_nofail(dev); -} - -static Property spapr_vty_properties[] = { - DEFINE_SPAPR_PROPERTIES(VIOsPAPRVTYDevice, sdev), - DEFINE_PROP_CHR("chardev", VIOsPAPRVTYDevice, chardev), - DEFINE_PROP_END_OF_LIST(), -}; - -static void spapr_vty_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VIOsPAPRDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); - - k->init = spapr_vty_init; - k->dt_name = "vty"; - k->dt_type = "serial"; - k->dt_compatible = "hvterm1"; - dc->props = spapr_vty_properties; -} - -static const TypeInfo spapr_vty_info = { - .name = "spapr-vty", - .parent = TYPE_VIO_SPAPR_DEVICE, - .instance_size = sizeof(VIOsPAPRVTYDevice), - .class_init = spapr_vty_class_init, -}; - -VIOsPAPRDevice *spapr_vty_get_default(VIOsPAPRBus *bus) -{ - VIOsPAPRDevice *sdev, *selected; - BusChild *kid; - - /* - * To avoid the console bouncing around we want one VTY to be - * the "default". We haven't really got anything to go on, so - * arbitrarily choose the one with the lowest reg value. - */ - - selected = NULL; - QTAILQ_FOREACH(kid, &bus->bus.children, sibling) { - DeviceState *iter = kid->child; - - /* Only look at VTY devices */ - if (!object_dynamic_cast(OBJECT(iter), "spapr-vty")) { - continue; - } - - sdev = DO_UPCAST(VIOsPAPRDevice, qdev, iter); - - /* First VTY we've found, so it is selected for now */ - if (!selected) { - selected = sdev; - continue; - } - - /* Choose VTY with lowest reg value */ - if (sdev->reg < selected->reg) { - selected = sdev; - } - } - - return selected; -} - -VIOsPAPRDevice *vty_lookup(sPAPREnvironment *spapr, target_ulong reg) -{ - VIOsPAPRDevice *sdev; - - sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); - if (!sdev && reg == 0) { - /* Hack for kernel early debug, which always specifies reg==0. - * We search all VIO devices, and grab the vty with the lowest - * reg. This attempts to mimic existing PowerVM behaviour - * (early debug does work there, despite having no vty with - * reg==0. */ - return spapr_vty_get_default(spapr->vio_bus); - } - - return sdev; -} - -static void spapr_vty_register_types(void) -{ - spapr_register_hypercall(H_PUT_TERM_CHAR, h_put_term_char); - spapr_register_hypercall(H_GET_TERM_CHAR, h_get_term_char); - type_register_static(&spapr_vty_info); -} - -type_init(spapr_vty_register_types) diff --git a/hw/sparc/Makefile.objs b/hw/sparc/Makefile.objs index 2354616..e18bc67 100644 --- a/hw/sparc/Makefile.objs +++ b/hw/sparc/Makefile.objs @@ -3,7 +3,7 @@ obj-y += slavio_timer.o slavio_misc.o sparc32_dma.o obj-y += eccmemctl.o sbi.o sun4c_intctl.o # GRLIB -obj-y += grlib_gptimer.o grlib_irqmp.o grlib_apbuart.o +obj-y += grlib_gptimer.o grlib_irqmp.o obj-y := $(addprefix ../,$(obj-y)) -- cgit v1.1