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"
14 #include "exec/address-spaces.h" /* get_system_memory */
15 #include "sysemu/char.h"
16 #include "sysemu/sysemu.h"
21 # define DEBUG_S3C24XX
24 #if defined(DEBUG_S3C24XX)
25 # define logout(fmt, ...) \
26 fprintf(stderr, "S3C24xx\t%-24s" fmt, __func__, ##__VA_ARGS__)
28 # define logout(fmt, ...) (void)0
31 /* S3C24XX serial port registers */
33 /* Line control RW WORD */
34 #define S3C_SERIAL_ULCON 0x00
35 /* General control RW WORD */
36 #define S3C_SERIAL_UCON 0x04
37 /* Fifo control RW WORD */
38 #define S3C_SERIAL_UFCON 0x08
39 /* Modem control RW WORD */
40 #define S3C_SERIAL_UMCON 0x0C
41 /* TX/RX Status RO WORD */
42 #define S3C_SERIAL_UTRSTAT 0x10
43 /* Receive Error Status RO WORD */
44 #define S3C_SERIAL_UERSTAT 0x14
45 /* FiFo Status RO WORD */
46 #define S3C_SERIAL_UFSTAT 0x18
47 /* Modem Status RO WORD */
48 #define S3C_SERIAL_UMSTAT 0x1C
49 /* TX buffer WR BYTE */
50 #define S3C_SERIAL_UTXH 0x20
51 /* RX buffer RO BYTE */
52 #define S3C_SERIAL_URXH 0x24
53 /* BAUD Divisor RW WORD */
54 #define S3C_SERIAL_UBRDIV 0x28
56 /* S3C24XX serial port state */
57 typedef struct s3c24xx_serial_dev_s
{
59 uint32_t ulcon
, ucon
, ufcon
, umcon
, ubrdiv
;
60 unsigned char rx_byte
;
61 /* Byte is available to be read */
62 unsigned int rx_available
: 1;
70 static void s3c24xx_serial_write(void *opaque
, hwaddr addr
,
71 uint64_t value
, unsigned size
)
73 s3c24xx_serial_dev
*s
= opaque
;
74 int reg
= addr
& 0x3f;
76 logout("0x" TARGET_FMT_plx
" 0x%08x\n", addr
, value
);
79 case S3C_SERIAL_ULCON
:
85 if( s
->ucon
& 1<<9 ) {
86 qemu_set_irq(s
->tx_level
, 1);
88 qemu_set_irq(s
->tx_level
, 0);
90 if( !(s
->ucon
& 1<<8) ) {
91 qemu_set_irq(s
->rx_level
, 0);
95 case S3C_SERIAL_UFCON
:
96 s
->ufcon
= (value
& ~6);
99 case S3C_SERIAL_UMCON
:
103 case S3C_SERIAL_UTRSTAT
:
106 case S3C_SERIAL_UERSTAT
:
109 case S3C_SERIAL_UFSTAT
:
112 case S3C_SERIAL_UMSTAT
:
115 case S3C_SERIAL_UTXH
: {
116 unsigned char ch
= value
& 0xff;
117 if (s
->chr
&& ((s
->ucon
& 1<<5)==0)) {
118 qemu_chr_fe_write(s
->chr
, &ch
, 1);
122 if( s
->ucon
& 1<<8 ) {
123 qemu_set_irq(s
->rx_level
, 1);
125 qemu_set_irq(s
->rx_irq
, 1);
128 if (s
->ucon
& 1<<9) {
129 qemu_set_irq(s
->tx_level
, 1);
131 qemu_set_irq(s
->tx_irq
, 1);
136 case S3C_SERIAL_URXH
:
139 case S3C_SERIAL_UBRDIV
:
148 static uint64_t s3c24xx_serial_read(void *opaque
, hwaddr addr
,
151 s3c24xx_serial_dev
*s
= opaque
;
152 int reg
= addr
& 0x3f;
154 logout("0x" TARGET_FMT_plx
"\n", addr
);
157 case S3C_SERIAL_ULCON
:
160 case S3C_SERIAL_UCON
:
163 case S3C_SERIAL_UFCON
:
164 return s
->ufcon
& ~0x8; /* bit 3 is reserved, must be zero */
166 case S3C_SERIAL_UMCON
:
167 return s
->umcon
& 0x11; /* Rest are reserved, must be zero */
169 case S3C_SERIAL_UTRSTAT
:
170 return 6 | s
->rx_available
; /* TX always clear, RX when available */
172 case S3C_SERIAL_UERSTAT
:
173 return 0; /* Later, break detect comes in here */
175 case S3C_SERIAL_UFSTAT
:
176 return s
->rx_available
; /* TXFIFO, always empty, RXFIFO 0 or 1 bytes */
178 case S3C_SERIAL_UMSTAT
:
181 case S3C_SERIAL_UTXH
:
184 case S3C_SERIAL_URXH
:
186 if (s
->ucon
& 1<<8) {
187 qemu_set_irq(s
->rx_level
, 0);
191 case S3C_SERIAL_UBRDIV
:
199 static const MemoryRegionOps s3c24xx_serial_ops
= {
200 .read
= s3c24xx_serial_read
,
201 .write
= s3c24xx_serial_write
,
202 .endianness
= DEVICE_NATIVE_ENDIAN
,
204 .min_access_size
= 1,
209 static void s3c24xx_serial_event(void *opaque
, int event
)
214 s3c24xx_serial_can_receive(void *opaque
)
216 s3c24xx_serial_dev
*s
= opaque
;
218 /* If there's no byte to be read, we can receive a new one */
219 return !s
->rx_available
;
223 s3c24xx_serial_receive(void *opaque
, const uint8_t *buf
, int size
)
225 s3c24xx_serial_dev
*s
= opaque
;
228 if ( s
->ucon
& 1 << 8 ) {
229 qemu_set_irq(s
->rx_level
, 1);
231 /* Is there something we can do here to ensure it's just a pulse ? */
232 qemu_set_irq(s
->rx_irq
, 1);
236 /* Create a S3C serial port, the port implementation is common to all
237 * current s3c devices only differing in the I/O base address and number of
240 struct s3c24xx_serial_dev_s
*
241 s3c24xx_serial_init(S3CState
*soc
,
242 CharDriverState
*chr
,
246 /* Initialise a serial port at the given port address */
247 s3c24xx_serial_dev
*s
= g_new0(s3c24xx_serial_dev
, 1);
249 /* initialise serial port context */
250 s
->rx_irq
= s3c24xx_get_irq(soc
->irq
, irqn
);
251 s
->rx_level
= s3c24xx_get_irq(soc
->irq
, irqn
+ 64);
253 s
->tx_irq
= s3c24xx_get_irq(soc
->irq
, irqn
+ 1);
254 s
->tx_level
= s3c24xx_get_irq(soc
->irq
, irqn
+ 1 + 64);
256 /* Register the MMIO region. */
257 memory_region_init_io(&s
->mmio
, OBJECT(s
), &s3c24xx_serial_ops
, s
,
258 "s3c24xx.serial", 44);
259 memory_region_add_subregion(get_system_memory(), base_addr
, &s
->mmio
);
262 /* If the port is present add to the character device's IO handlers. */
265 qemu_chr_add_handlers(s
->chr
,
266 s3c24xx_serial_can_receive
,
267 s3c24xx_serial_receive
,
268 s3c24xx_serial_event
,