Merge remote-tracking branch 'qemu/master'
[qemu/ar7.git] / hw / arm / s3c24xx_serial.c
blob69f33c486b60a0cb6dcd1d6a0a0f4ce0c6f532d0
1 /* hw/s3c24xx_serial.c
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"
13 #include "hw/hw.h"
14 #include "exec/address-spaces.h" /* get_system_memory */
15 #include "sysemu/char.h"
16 #include "sysemu/sysemu.h"
18 #include "s3c24xx.h"
20 #if 0
21 # define DEBUG_S3C24XX
22 #endif
24 #if defined(DEBUG_S3C24XX)
25 # define logout(fmt, ...) \
26 fprintf(stderr, "S3C24xx\t%-24s" fmt, __func__, ##__VA_ARGS__)
27 #else
28 # define logout(fmt, ...) (void)0
29 #endif
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 {
58 MemoryRegion mmio;
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;
63 CharDriverState *chr;
64 qemu_irq tx_irq;
65 qemu_irq rx_irq;
66 qemu_irq tx_level;
67 qemu_irq rx_level;
68 } s3c24xx_serial_dev;
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);
78 switch(reg) {
79 case S3C_SERIAL_ULCON:
80 s->ulcon = value;
81 break;
83 case S3C_SERIAL_UCON:
84 s->ucon = value;
85 if( s->ucon & 1<<9 ) {
86 qemu_set_irq(s->tx_level, 1);
87 } else {
88 qemu_set_irq(s->tx_level, 0);
90 if( !(s->ucon & 1<<8) ) {
91 qemu_set_irq(s->rx_level, 0);
93 break;
95 case S3C_SERIAL_UFCON:
96 s->ufcon = (value & ~6);
97 break;
99 case S3C_SERIAL_UMCON:
100 s->umcon = value;
101 break;
103 case S3C_SERIAL_UTRSTAT:
104 break;
106 case S3C_SERIAL_UERSTAT:
107 break;
109 case S3C_SERIAL_UFSTAT:
110 break;
112 case S3C_SERIAL_UMSTAT:
113 break;
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);
119 } else {
120 s->rx_byte = ch;
121 s->rx_available = 1;
122 if( s->ucon & 1<<8 ) {
123 qemu_set_irq(s->rx_level, 1);
124 } else {
125 qemu_set_irq(s->rx_irq, 1);
128 if (s->ucon & 1<<9) {
129 qemu_set_irq(s->tx_level, 1);
130 } else {
131 qemu_set_irq(s->tx_irq, 1);
133 break;
136 case S3C_SERIAL_URXH:
137 break;
139 case S3C_SERIAL_UBRDIV:
140 s->ubrdiv = value;
141 break;
143 default:
144 break;
148 static uint64_t s3c24xx_serial_read(void *opaque, hwaddr addr,
149 unsigned size)
151 s3c24xx_serial_dev *s = opaque;
152 int reg = addr & 0x3f;
154 logout("0x" TARGET_FMT_plx "\n", addr);
156 switch (reg) {
157 case S3C_SERIAL_ULCON:
158 return s->ulcon;
160 case S3C_SERIAL_UCON:
161 return s->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:
179 return 0;
181 case S3C_SERIAL_UTXH:
182 return 0;
184 case S3C_SERIAL_URXH:
185 s->rx_available = 0;
186 if (s->ucon & 1<<8) {
187 qemu_set_irq(s->rx_level, 0);
189 return s->rx_byte;
191 case S3C_SERIAL_UBRDIV:
192 return s->ubrdiv;
194 default:
195 return 0;
199 static const MemoryRegionOps s3c24xx_serial_ops = {
200 .read = s3c24xx_serial_read,
201 .write = s3c24xx_serial_write,
202 .endianness = DEVICE_NATIVE_ENDIAN,
203 .valid = {
204 .min_access_size = 1,
205 .max_access_size = 4
209 static void s3c24xx_serial_event(void *opaque, int event)
213 static int
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;
222 static void
223 s3c24xx_serial_receive(void *opaque, const uint8_t *buf, int size)
225 s3c24xx_serial_dev *s = opaque;
226 s->rx_byte = buf[0];
227 s->rx_available = 1;
228 if ( s->ucon & 1 << 8 ) {
229 qemu_set_irq(s->rx_level, 1);
230 } else {
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
238 * ports.
240 struct s3c24xx_serial_dev_s *
241 s3c24xx_serial_init(S3CState *soc,
242 CharDriverState *chr,
243 hwaddr base_addr,
244 int irqn)
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);
261 if (chr) {
262 /* If the port is present add to the character device's IO handlers. */
263 s->chr = chr;
265 qemu_chr_add_handlers(s->chr,
266 s3c24xx_serial_can_receive,
267 s3c24xx_serial_receive,
268 s3c24xx_serial_event,
271 return s;