From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1Lx3Du-0004xN-83 for qemu-devel@nongnu.org; Thu, 23 Apr 2009 14:00:34 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1Lx3Ds-0004ww-9y for qemu-devel@nongnu.org; Thu, 23 Apr 2009 14:00:32 -0400 Received: from [199.232.76.173] (port=33047 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Lx3Dr-0004ws-Rk for qemu-devel@nongnu.org; Thu, 23 Apr 2009 14:00:31 -0400 Received: from flounder.pepperfish.net ([87.237.62.181]:54155) by monty-python.gnu.org with esmtps (TLS-1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from ) id 1Lx3Dp-0004IO-FW for qemu-devel@nongnu.org; Thu, 23 Apr 2009 14:00:30 -0400 Received: from [10.112.102.2] (helo=jennifer.kylikki.org) by flounder.pepperfish.net with esmtps (Exim 4.69 #1 (Debian)) id 1Lx3Dn-00042s-C1 for ; Thu, 23 Apr 2009 19:00:27 +0100 Received: from derik.kyllikki.org ([192.168.7.20] helo=derik) by jennifer.kylikki.org with esmtp (Exim 4.63) (envelope-from ) id 1Lx3Dn-0006BI-Ns for qemu-devel@nongnu.org; Thu, 23 Apr 2009 19:00:27 +0100 Received: from vince by derik with local (Exim 4.69) (envelope-from ) id 1Lx3Dn-00040f-L4 for qemu-devel@nongnu.org; Thu, 23 Apr 2009 19:00:27 +0100 Date: Thu, 23 Apr 2009 19:00:27 +0100 From: Vincent Sanders Subject: [Qemu-devel] [PATCH 7/16] S3C serial peripheral Message-ID: <20090423180027.GI4629@derik> References: <20090423171503.GC4629@derik> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20090423171503.GC4629@derik> List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org S3C serial peripheral. Signed-off-by: Vincent Sanders --- s3c24xx_serial.c | 266 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 266 insertions(+) diff -urN qemusvnclean/hw/s3c24xx_serial.c qemusvnpatches/hw/s3c24xx_serial.c --- qemusvnclean/hw/s3c24xx_serial.c 1970-01-01 01:00:00.000000000 +0100 +++ qemusvnpatches/hw/s3c24xx_serial.c 2009-04-23 17:06:16.000000000 +0100 @@ -0,0 +1,266 @@ +/* hw/s3c24xx_serial.c + * + * Samsung S3C24XX Serial block + * + * Copyright 2006, 2007 Daniel Silverstone and Vincent Sanders + * + * This file is under the terms of the GNU General Public + * License Version 2 + */ + +#include "hw.h" +#include "qemu-char.h" +#include "sysemu.h" + +#include "s3c24xx.h" + +/* S3C24XX serial port registers */ + +/* port spacing */ +#define S3C_SERIAL_PORT_STRIDE 0x4000 + +/* Line control RW WORD */ +#define S3C_SERIAL_ULCON 0x00 +/* General control RW WORD */ +#define S3C_SERIAL_UCON 0x04 +/* Fifo control RW WORD */ +#define S3C_SERIAL_UFCON 0x08 +/* Modem control RW WORD */ +#define S3C_SERIAL_UMCON 0x0C +/* TX/RX Status RO WORD */ +#define S3C_SERIAL_UTRSTAT 0x10 +/* Receive Error Status RO WORD */ +#define S3C_SERIAL_UERSTAT 0x14 +/* FiFo Status RO WORD */ +#define S3C_SERIAL_UFSTAT 0x18 +/* Modem Status RO WORD */ +#define S3C_SERIAL_UMSTAT 0x1C +/* TX buffer WR BYTE */ +#define S3C_SERIAL_UTXH 0x20 +/* RX buffer RO BYTE */ +#define S3C_SERIAL_URXH 0x24 +/* BAUD Divisor RW WORD */ +#define S3C_SERIAL_UBRDIV 0x28 + +/* S3C24XX serial port state */ +typedef struct { + uint32_t ulcon, ucon, ufcon, umcon, ubrdiv; + unsigned char rx_byte; + /* Byte is available to be read */ + unsigned int rx_available : 1; + CharDriverState *chr; + int port; + qemu_irq tx_irq; + qemu_irq rx_irq; + qemu_irq tx_level; + qemu_irq rx_level; +} s3c24xx_serial_dev; + +static void +s3c24xx_serial_write_f(void *opaque, target_phys_addr_t addr, uint32_t value) +{ + s3c24xx_serial_dev *s = opaque; + int reg = addr & 0x3f; + + switch(reg) { + case S3C_SERIAL_ULCON: + s->ulcon = value; + break; + + case S3C_SERIAL_UCON: + s->ucon = value; + if( s->ucon & 1<<9 ) { + qemu_set_irq(s->tx_level, 1); + } else { + qemu_set_irq(s->tx_level, 0); + } + if( !(s->ucon & 1<<8) ) { + qemu_set_irq(s->rx_level, 0); + } + break; + + case S3C_SERIAL_UFCON: + s->ufcon = (value & ~6); + break; + + case S3C_SERIAL_UMCON: + s->umcon = value; + break; + + case S3C_SERIAL_UTRSTAT: + break; + + case S3C_SERIAL_UERSTAT: + break; + + case S3C_SERIAL_UFSTAT: + break; + + case S3C_SERIAL_UMSTAT: + break; + + case S3C_SERIAL_UTXH: { + unsigned char ch = value & 0xff; + if (s->chr && ((s->ucon & 1<<5)==0)) + qemu_chr_write(s->chr, &ch, 1); + else { + s->rx_byte = ch; + s->rx_available = 1; + if( s->ucon & 1<<8 ) { + qemu_set_irq(s->rx_level, 1); + } else { + qemu_set_irq(s->rx_irq, 1); + } + } + if( s->ucon & 1<<9 ) { + qemu_set_irq(s->tx_level, 1); + } else { + qemu_set_irq(s->tx_irq, 1); + } + break; + } + + case S3C_SERIAL_URXH: + break; + + case S3C_SERIAL_UBRDIV: + s->ubrdiv = value; + break; + + default: + break; + }; +} + +static uint32_t +s3c24xx_serial_read_f(void *opaque, target_phys_addr_t addr) +{ + s3c24xx_serial_dev *s = opaque; + int reg = addr & 0x3f; + + switch(reg) { + case S3C_SERIAL_ULCON: + return s->ulcon; + + case S3C_SERIAL_UCON: + return s->ucon; + + case S3C_SERIAL_UFCON: + return s->ufcon & ~0x8; /* bit 3 is reserved, must be zero */ + + case S3C_SERIAL_UMCON: + return s->umcon & 0x11; /* Rest are reserved, must be zero */ + + case S3C_SERIAL_UTRSTAT: + return 6 | s->rx_available; /* TX always clear, RX when available */ + + case S3C_SERIAL_UERSTAT: + return 0; /* Later, break detect comes in here */ + + case S3C_SERIAL_UFSTAT: + return s->rx_available; /* TXFIFO, always empty, RXFIFO 0 or 1 bytes */ + + case S3C_SERIAL_UMSTAT: + return 0; + + case S3C_SERIAL_UTXH: + return 0; + + case S3C_SERIAL_URXH: + s->rx_available = 0; + if( s->ucon & 1<<8 ) { + qemu_set_irq(s->rx_level, 0); + } + return s->rx_byte; + + case S3C_SERIAL_UBRDIV: + return s->ubrdiv; + + default: + return 0; + }; +} + +static CPUReadMemoryFunc *s3c24xx_serial_read[] = { + &s3c24xx_serial_read_f, + &s3c24xx_serial_read_f, + &s3c24xx_serial_read_f, +}; + +static CPUWriteMemoryFunc *s3c24xx_serial_write[] = { + &s3c24xx_serial_write_f, + &s3c24xx_serial_write_f, + &s3c24xx_serial_write_f, +}; + + +static void s3c24xx_serial_event(void *opaque, int event) +{ +} + +static int +s3c24xx_serial_can_receive(void *opaque) +{ + s3c24xx_serial_dev *s = opaque; + + /* If there's no byte to be read, we can receive a new one */ + return !s->rx_available; +} + +static void +s3c24xx_serial_receive(void *opaque, const uint8_t *buf, int size) +{ + s3c24xx_serial_dev *s = opaque; + s->rx_byte = buf[0]; + s->rx_available = 1; + if ( s->ucon & 1 << 8 ) { + qemu_set_irq(s->rx_level, 1); + } else { + /* Is there something we can do here to ensure it's just a pulse ? */ + qemu_set_irq(s->rx_irq, 1); + } +} + +/* Create a S3C serial port, the port implementation is common to all + * current s3c devices only differing in the I/O base address and number of + * ports. + */ +void +s3c24xx_serial_init(S3CState *soc, int port, target_phys_addr_t base_addr) +{ + /* Initialise a serial port at the given port address */ + s3c24xx_serial_dev *s; + int serial_io; + + s = qemu_mallocz(sizeof(s3c24xx_serial_dev)); + if (!s) + return; + + /* initialise serial port context */ + s->chr = serial_hds[port]; + s->ulcon = 0; + s->ucon = 0; + s->ufcon = 0; + s->umcon = 0; + s->ubrdiv = 0; + s->port = port; + s->rx_available = 0; + s->tx_irq = soc->irqs[32 + (port * 3) + 1]; + s->rx_irq = soc->irqs[32 + (port * 3)]; + s->tx_level = soc->irqs[64 + 32 + (port * 3) + 1]; + s->rx_level = soc->irqs[64 + 32 + (port * 3)]; + + /* Prepare our MMIO tag */ + serial_io = cpu_register_io_memory(0, s3c24xx_serial_read, s3c24xx_serial_write, s); + /* Register the region with the tag */ + cpu_register_physical_memory(base_addr + (port * S3C_SERIAL_PORT_STRIDE), 44, serial_io); + + if (s->chr) { + /* If the port is present add to the character device's IO handlers. */ + qemu_chr_add_handlers(s->chr, + s3c24xx_serial_can_receive, + s3c24xx_serial_receive, + s3c24xx_serial_event, + s); + } +} -- Regards Vincent http://www.kyllikki.org/