Merge remote-tracking branch 'qemu/master'
[qemu/ar7.git] / hw / arm / s3c24xx_serial.c
blob959fa9e348e624e9964860736d5560589181957e
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 "cpu.h"
14 #include "hw/hw.h"
15 #include "exec/address-spaces.h" /* get_system_memory */
16 #include "sysemu/char.h"
17 #include "sysemu/sysemu.h"
19 #include "s3c24xx.h"
21 #if 0
22 # define DEBUG_S3C24XX
23 #endif
25 #if defined(DEBUG_S3C24XX)
26 # define logout(fmt, ...) \
27 fprintf(stderr, "S3C24xx\t%-24s" fmt, __func__, ##__VA_ARGS__)
28 #else
29 # define logout(fmt, ...) (void)0
30 #endif
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 {
59 MemoryRegion mmio;
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;
64 CharBackend chr;
65 qemu_irq tx_irq;
66 qemu_irq rx_irq;
67 qemu_irq tx_level;
68 qemu_irq rx_level;
69 } s3c24xx_serial_dev;
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);
79 switch(reg) {
80 case S3C_SERIAL_ULCON:
81 s->ulcon = value;
82 break;
84 case S3C_SERIAL_UCON:
85 s->ucon = value;
86 if( s->ucon & 1<<9 ) {
87 qemu_set_irq(s->tx_level, 1);
88 } else {
89 qemu_set_irq(s->tx_level, 0);
91 if( !(s->ucon & 1<<8) ) {
92 qemu_set_irq(s->rx_level, 0);
94 break;
96 case S3C_SERIAL_UFCON:
97 s->ufcon = (value & ~6);
98 break;
100 case S3C_SERIAL_UMCON:
101 s->umcon = value;
102 break;
104 case S3C_SERIAL_UTRSTAT:
105 break;
107 case S3C_SERIAL_UERSTAT:
108 break;
110 case S3C_SERIAL_UFSTAT:
111 break;
113 case S3C_SERIAL_UMSTAT:
114 break;
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);
120 } else {
121 s->rx_byte = ch;
122 s->rx_available = 1;
123 if( s->ucon & 1<<8 ) {
124 qemu_set_irq(s->rx_level, 1);
125 } else {
126 qemu_set_irq(s->rx_irq, 1);
129 if (s->ucon & 1<<9) {
130 qemu_set_irq(s->tx_level, 1);
131 } else {
132 qemu_set_irq(s->tx_irq, 1);
134 break;
137 case S3C_SERIAL_URXH:
138 break;
140 case S3C_SERIAL_UBRDIV:
141 s->ubrdiv = value;
142 break;
144 default:
145 break;
149 static uint64_t s3c24xx_serial_read(void *opaque, hwaddr addr,
150 unsigned size)
152 s3c24xx_serial_dev *s = opaque;
153 int reg = addr & 0x3f;
155 logout("0x" TARGET_FMT_plx "\n", addr);
157 switch (reg) {
158 case S3C_SERIAL_ULCON:
159 return s->ulcon;
161 case S3C_SERIAL_UCON:
162 return s->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:
180 return 0;
182 case S3C_SERIAL_UTXH:
183 return 0;
185 case S3C_SERIAL_URXH:
186 s->rx_available = 0;
187 if (s->ucon & 1<<8) {
188 qemu_set_irq(s->rx_level, 0);
190 return s->rx_byte;
192 case S3C_SERIAL_UBRDIV:
193 return s->ubrdiv;
195 default:
196 return 0;
200 static const MemoryRegionOps s3c24xx_serial_ops = {
201 .read = s3c24xx_serial_read,
202 .write = s3c24xx_serial_write,
203 .endianness = DEVICE_NATIVE_ENDIAN,
204 .valid = {
205 .min_access_size = 1,
206 .max_access_size = 4
210 static void s3c24xx_serial_event(void *opaque, int event)
214 static int
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;
223 static void
224 s3c24xx_serial_receive(void *opaque, const uint8_t *buf, int size)
226 s3c24xx_serial_dev *s = opaque;
227 s->rx_byte = buf[0];
228 s->rx_available = 1;
229 if ( s->ucon & 1 << 8 ) {
230 qemu_set_irq(s->rx_level, 1);
231 } else {
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
239 * ports.
241 struct s3c24xx_serial_dev_s *
242 s3c24xx_serial_init(S3CState *soc,
243 Chardev *chr,
244 hwaddr base_addr,
245 int irqn)
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,
266 s, NULL, true);
268 return s;