3 * Samsung S3C24XX Serial block
5 * Copyright 2006, 2007 Daniel Silverstone and Vincent Sanders
7 * Copyright 2010, 2013 Stefan Weil
9 * This file is under the terms of the GNU General Public License Version 2.
12 #include "qemu/osdep.h"
15 #include "exec/address-spaces.h" /* get_system_memory */
16 #include "sysemu/char.h"
17 #include "sysemu/sysemu.h"
22 # define DEBUG_S3C24XX
25 #if defined(DEBUG_S3C24XX)
26 # define logout(fmt, ...) \
27 fprintf(stderr, "S3C24xx\t%-24s" fmt, __func__, ##__VA_ARGS__)
29 # define logout(fmt, ...) (void)0
32 /* S3C24XX serial port registers */
34 /* Line control RW WORD */
35 #define S3C_SERIAL_ULCON 0x00
36 /* General control RW WORD */
37 #define S3C_SERIAL_UCON 0x04
38 /* Fifo control RW WORD */
39 #define S3C_SERIAL_UFCON 0x08
40 /* Modem control RW WORD */
41 #define S3C_SERIAL_UMCON 0x0C
42 /* TX/RX Status RO WORD */
43 #define S3C_SERIAL_UTRSTAT 0x10
44 /* Receive Error Status RO WORD */
45 #define S3C_SERIAL_UERSTAT 0x14
46 /* FiFo Status RO WORD */
47 #define S3C_SERIAL_UFSTAT 0x18
48 /* Modem Status RO WORD */
49 #define S3C_SERIAL_UMSTAT 0x1C
50 /* TX buffer WR BYTE */
51 #define S3C_SERIAL_UTXH 0x20
52 /* RX buffer RO BYTE */
53 #define S3C_SERIAL_URXH 0x24
54 /* BAUD Divisor RW WORD */
55 #define S3C_SERIAL_UBRDIV 0x28
57 /* S3C24XX serial port state */
58 typedef struct s3c24xx_serial_dev_s
{
60 uint32_t ulcon
, ucon
, ufcon
, umcon
, ubrdiv
;
61 unsigned char rx_byte
;
62 /* Byte is available to be read */
63 unsigned int rx_available
: 1;
71 static void s3c24xx_serial_write(void *opaque
, hwaddr addr
,
72 uint64_t value
, unsigned size
)
74 s3c24xx_serial_dev
*s
= opaque
;
75 int reg
= addr
& 0x3f;
77 logout("0x" TARGET_FMT_plx
" 0x%08x\n", addr
, value
);
80 case S3C_SERIAL_ULCON
:
86 if( s
->ucon
& 1<<9 ) {
87 qemu_set_irq(s
->tx_level
, 1);
89 qemu_set_irq(s
->tx_level
, 0);
91 if( !(s
->ucon
& 1<<8) ) {
92 qemu_set_irq(s
->rx_level
, 0);
96 case S3C_SERIAL_UFCON
:
97 s
->ufcon
= (value
& ~6);
100 case S3C_SERIAL_UMCON
:
104 case S3C_SERIAL_UTRSTAT
:
107 case S3C_SERIAL_UERSTAT
:
110 case S3C_SERIAL_UFSTAT
:
113 case S3C_SERIAL_UMSTAT
:
116 case S3C_SERIAL_UTXH
: {
117 unsigned char ch
= value
& 0xff;
118 if ((s
->ucon
& 1 << 5) == 0) {
119 qemu_chr_fe_write(&s
->chr
, &ch
, 1);
123 if( s
->ucon
& 1<<8 ) {
124 qemu_set_irq(s
->rx_level
, 1);
126 qemu_set_irq(s
->rx_irq
, 1);
129 if (s
->ucon
& 1<<9) {
130 qemu_set_irq(s
->tx_level
, 1);
132 qemu_set_irq(s
->tx_irq
, 1);
137 case S3C_SERIAL_URXH
:
140 case S3C_SERIAL_UBRDIV
:
149 static uint64_t s3c24xx_serial_read(void *opaque
, hwaddr addr
,
152 s3c24xx_serial_dev
*s
= opaque
;
153 int reg
= addr
& 0x3f;
155 logout("0x" TARGET_FMT_plx
"\n", addr
);
158 case S3C_SERIAL_ULCON
:
161 case S3C_SERIAL_UCON
:
164 case S3C_SERIAL_UFCON
:
165 return s
->ufcon
& ~0x8; /* bit 3 is reserved, must be zero */
167 case S3C_SERIAL_UMCON
:
168 return s
->umcon
& 0x11; /* Rest are reserved, must be zero */
170 case S3C_SERIAL_UTRSTAT
:
171 return 6 | s
->rx_available
; /* TX always clear, RX when available */
173 case S3C_SERIAL_UERSTAT
:
174 return 0; /* Later, break detect comes in here */
176 case S3C_SERIAL_UFSTAT
:
177 return s
->rx_available
; /* TXFIFO, always empty, RXFIFO 0 or 1 bytes */
179 case S3C_SERIAL_UMSTAT
:
182 case S3C_SERIAL_UTXH
:
185 case S3C_SERIAL_URXH
:
187 if (s
->ucon
& 1<<8) {
188 qemu_set_irq(s
->rx_level
, 0);
192 case S3C_SERIAL_UBRDIV
:
200 static const MemoryRegionOps s3c24xx_serial_ops
= {
201 .read
= s3c24xx_serial_read
,
202 .write
= s3c24xx_serial_write
,
203 .endianness
= DEVICE_NATIVE_ENDIAN
,
205 .min_access_size
= 1,
210 static void s3c24xx_serial_event(void *opaque
, int event
)
215 s3c24xx_serial_can_receive(void *opaque
)
217 s3c24xx_serial_dev
*s
= opaque
;
219 /* If there's no byte to be read, we can receive a new one */
220 return !s
->rx_available
;
224 s3c24xx_serial_receive(void *opaque
, const uint8_t *buf
, int size
)
226 s3c24xx_serial_dev
*s
= opaque
;
229 if ( s
->ucon
& 1 << 8 ) {
230 qemu_set_irq(s
->rx_level
, 1);
232 /* Is there something we can do here to ensure it's just a pulse ? */
233 qemu_set_irq(s
->rx_irq
, 1);
237 /* Create a S3C serial port, the port implementation is common to all
238 * current s3c devices only differing in the I/O base address and number of
241 struct s3c24xx_serial_dev_s
*
242 s3c24xx_serial_init(S3CState
*soc
,
247 /* Initialise a serial port at the given port address */
248 s3c24xx_serial_dev
*s
= g_new0(s3c24xx_serial_dev
, 1);
250 /* initialise serial port context */
251 s
->rx_irq
= s3c24xx_get_irq(soc
->irq
, irqn
);
252 s
->rx_level
= s3c24xx_get_irq(soc
->irq
, irqn
+ 64);
254 s
->tx_irq
= s3c24xx_get_irq(soc
->irq
, irqn
+ 1);
255 s
->tx_level
= s3c24xx_get_irq(soc
->irq
, irqn
+ 1 + 64);
257 /* Register the MMIO region. */
258 memory_region_init_io(&s
->mmio
, OBJECT(s
), &s3c24xx_serial_ops
, s
,
259 "s3c24xx.serial", 44);
260 memory_region_add_subregion(get_system_memory(), base_addr
, &s
->mmio
);
262 qemu_chr_fe_set_handlers(&s
->chr
,
263 s3c24xx_serial_can_receive
,
264 s3c24xx_serial_receive
,
265 s3c24xx_serial_event
,