Tizen 2.1 base
[sdk/emulator/qemu.git] / hw / s5pc1xx_uart.c
1 /*
2  * S5PC1XX UART Emulation
3  *
4  * Copyright (c) 2009 Samsung Electronics.
5  * Contributed by Kirill Batuzov <batuzovk@ispras.ru>
6  */
7
8 #include "sysbus.h"
9 #include "qemu-char.h"
10 #include "s5pc1xx.h"
11 #include "s5pc1xx_gpio_regs.h"
12
13
14 #define QUEUE_SIZE   257
15
16 #define INT_RXD     (1 << 0)
17 #define INT_ERROR   (1 << 1)
18 #define INT_TXD     (1 << 2)
19 #define INT_MODEM   (1 << 3)
20
21 #define TRSTATUS_TRANSMITTER_READY  (1 << 2)
22 #define TRSTATUS_BUFFER_EMPTY       (1 << 1)
23 #define TRSTATUS_DATA_READY         (1 << 0)
24
25 #define UFSTAT_RX_FIFO_FULL         (1 << 8)
26
27 #define UFCON_FIFO_ENABLED          (1 << 0)
28 #define UFCON_TX_LEVEL_SHIFT        8
29 #define UFCON_TX_LEVEL              (7 << UFCON_TX_LEVEL_SHIFT)
30
31 #define UFSTAT_TX_COUNT_SHIT        16
32 #define UFSTAT_TX_COUNT             (0xFF << UFSTAT_TX_COUNT_SHIT)
33
34 #define QI(x) ((x + 1) % QUEUE_SIZE)
35 #define QD(x) ((x - 1 + QUEUE_SIZE) % QUEUE_SIZE)
36
37 #define S5PC1XX_UART_REG_MEM_SIZE 0x3C
38
39 typedef struct UartQueue {
40     uint8_t queue[QUEUE_SIZE];
41     uint32_t s, t;
42     uint32_t size;
43 } UartQueue;
44
45 typedef struct S5pc1xxUartState {
46     SysBusDevice busdev;
47
48     UartQueue rx;
49
50     uint32_t ulcon;
51     uint32_t ucon;
52     uint32_t ufcon;
53     uint32_t umcon;
54     uint32_t utrstat;
55     uint32_t uerstat;
56     uint32_t ufstat;
57     uint32_t umstat;
58     uint32_t utxh;
59     uint32_t urxh;
60     uint32_t ubrdiv;
61     uint32_t udivslot;
62     uint32_t uintp;
63     uint32_t uintsp;
64     uint32_t uintm;
65
66     CharDriverState *chr;
67     qemu_irq irq;
68     uint32_t instance;
69 } S5pc1xxUartState;
70
71
72 static inline int queue_elem_count(const UartQueue *s)
73 {
74     if (s->t >= s->s) {
75         return s->t - s->s;
76     } else {
77         return QUEUE_SIZE - s->s + s->t;
78     }
79 }
80
81 static inline int queue_empty_count(const UartQueue *s)
82 {
83     return s->size - queue_elem_count(s) - 1;
84 }
85
86 static inline int queue_empty(const UartQueue *s)
87 {
88     return (queue_elem_count(s) == 0);
89 }
90
91 static inline void queue_push(UartQueue *s, uint8_t x)
92 {
93     s->queue[s->t] = x;
94     s->t = QI(s->t);
95 }
96
97 static inline uint8_t queue_get(UartQueue *s)
98 {
99     uint8_t ret;
100
101     ret = s->queue[s->s];
102     s->s = QI(s->s);
103     return ret;
104 }
105
106 static inline void queue_reset(UartQueue *s)
107 {
108     s->s = 0;
109     s->t = 0;
110 }
111
112 static void s5pc1xx_uart_update(S5pc1xxUartState *s)
113 {
114     if (s->ufcon && UFCON_FIFO_ENABLED) {
115         if (((s->ufstat && UFSTAT_TX_COUNT) >> UFSTAT_TX_COUNT_SHIT) <=
116             ((s->ufcon && UFCON_TX_LEVEL) >> UFCON_TX_LEVEL_SHIFT) * 2 ) {
117             s->uintsp |= INT_TXD;
118         }
119     }
120
121     s->uintp = s->uintsp & ~s->uintm;
122     if (s->uintp) {
123         qemu_irq_raise(s->irq);
124     } else {
125         qemu_irq_lower(s->irq);
126     }
127 }
128
129 /* Read UART by GPIO */
130 static uint32_t s5pc1xx_uart_gpio_read(void *opaque,
131                                        int io_index)
132 {
133     S5pc1xxUartState *s = (S5pc1xxUartState *)opaque;
134
135     /* TODO: check if s->uintp should be used instead of s->uintsp */
136     if (io_index == GPIO_UART_RXD(s->instance)) {
137         return (s->uintsp & INT_RXD);
138     }
139     if (io_index == GPIO_UART_TXD(s->instance)) {
140         return (s->uintsp & INT_TXD);
141     }
142
143     /* TODO: check if this is correct */
144     if (io_index == GPIO_UART_CTS(s->instance)) {
145         return ~(s->umstat & 0x1);
146     }
147     if (io_index == GPIO_UART_RTS(s->instance)) {
148         return ~(s->umcon  & 0x1);
149     }
150
151     /* TODO: return correct values */
152     if (io_index == GPIO_UART_AUDIO_RXD) {
153         return 0;
154     }
155     if (io_index == GPIO_UART_AUDIO_TXD) {
156         return 0;
157     }
158
159     return 0;
160 }
161
162 static GPIOReadMemoryFunc *s5pc1xx_uart_gpio_readfn   = s5pc1xx_uart_gpio_read;
163 static GPIOWriteMemoryFunc *s5pc1xx_uart_gpio_writefn = s5pc1xx_empty_gpio_write;   /* a gag */
164
165 static uint32_t s5pc1xx_uart_mm_read(void *opaque, target_phys_addr_t offset)
166 {
167     uint32_t res;
168     S5pc1xxUartState *s = (S5pc1xxUartState *)opaque;
169
170     switch (offset) {
171     case 0x00:
172         return s->ulcon;
173     case 0x04:
174         return s->ucon;
175     case 0x08:
176         return s->ufcon;
177     case 0x0C:
178         return s->umcon;
179     case 0x10:
180         return s->utrstat;
181     case 0x14:
182         res = s->uerstat;
183         s->uerstat = 0;
184         return res;
185     case 0x18:
186         s->ufstat = queue_elem_count(&s->rx) & 0xff;
187         if (queue_empty_count(&s->rx) == 0) {
188             s->ufstat |= UFSTAT_RX_FIFO_FULL;
189         }
190         return s->ufstat;
191     case 0x1C:
192         return s->umstat;
193     case 0x24:
194         if (s->ufcon & 1) {
195             if (! queue_empty(&s->rx)) {
196                 res = queue_get(&s->rx);
197                 if (queue_empty(&s->rx)) {
198                     s->utrstat &= ~TRSTATUS_DATA_READY;
199                 } else {
200                     s->utrstat |= TRSTATUS_DATA_READY;
201                 }
202             } else {
203                 s->uintsp |= INT_ERROR;
204                 s5pc1xx_uart_update(s);
205                 res = 0;
206             }
207         } else {
208             s->utrstat &= ~TRSTATUS_DATA_READY;
209             res = s->urxh;
210         }
211         return res;
212     case 0x28:
213         return s->ubrdiv;
214     case 0x2C:
215         return s->udivslot;
216     case 0x30:
217         return s->uintp;
218     case 0x34:
219         return s->uintsp;
220     case 0x38:
221         return s->uintm;
222     default:
223         hw_error("s5pc1xx.uart: bad read offset 0x" TARGET_FMT_plx "\n",
224                  offset);
225     }
226 }
227
228 static void s5pc1xx_uart_mm_write(void *opaque, target_phys_addr_t offset,
229                                   uint32_t val)
230 {
231     uint8_t ch;
232     S5pc1xxUartState *s = (S5pc1xxUartState *)opaque;
233
234     switch (offset) {
235     case 0x00:
236         s->ulcon = val;
237         break;
238     case 0x04:
239         s->ucon = val;
240         break;
241     case 0x08:
242         s->ufcon = val;
243         if (val & 2) {
244             queue_reset(&s->rx);
245         }
246         s->ufcon &= ~6;
247         break;
248     case 0x0C:
249         s->umcon = val;
250         break;
251     case 0x20:
252         if (s->chr) {
253             s->utrstat &= ~(TRSTATUS_TRANSMITTER_READY | TRSTATUS_BUFFER_EMPTY);
254             ch = (uint8_t)val;
255             qemu_chr_write(s->chr, &ch, 1);
256             s->utrstat |= TRSTATUS_TRANSMITTER_READY | TRSTATUS_BUFFER_EMPTY;
257             s->uintsp |= INT_TXD;
258         }
259         break;
260     case 0x28:
261         s->ubrdiv = val;
262         break;
263     case 0x2C:
264         s->udivslot = val;
265         break;
266     case 0x30:
267         s->uintp &= ~val;
268         s->uintsp &= ~val; /* TODO: does this really work in this way??? */
269         break;
270     case 0x34:
271         s->uintsp = val;
272         break;
273     case 0x38:
274         s->uintm = val;
275         break;
276     default:
277         hw_error("s5pc1xx.uart: bad write offset 0x" TARGET_FMT_plx "\n",
278                  offset);
279     }
280     s5pc1xx_uart_update(s);
281 }
282
283 CPUReadMemoryFunc * const s5pc1xx_uart_readfn[] = {
284     s5pc1xx_uart_mm_read,
285     s5pc1xx_uart_mm_read,
286     s5pc1xx_uart_mm_read
287 };
288
289 CPUWriteMemoryFunc * const s5pc1xx_uart_writefn[] = {
290     s5pc1xx_uart_mm_write,
291     s5pc1xx_uart_mm_write,
292     s5pc1xx_uart_mm_write
293 };
294
295 static void s5pc1xx_uart_save(QEMUFile *f, void *opaque)
296 {
297     S5pc1xxUartState *s = (S5pc1xxUartState *)opaque;
298
299     qemu_put_buffer(f, s->rx.queue, QUEUE_SIZE);
300     qemu_put_be32s(f, &s->rx.s);
301     qemu_put_be32s(f, &s->rx.t);
302     qemu_put_be32s(f, &s->rx.size);
303
304     qemu_put_be32s(f, &s->ulcon);
305     qemu_put_be32s(f, &s->ucon);
306     qemu_put_be32s(f, &s->ufcon);
307     qemu_put_be32s(f, &s->umcon);
308     qemu_put_be32s(f, &s->utrstat);
309     qemu_put_be32s(f, &s->uerstat);
310     qemu_put_be32s(f, &s->ufstat);
311     qemu_put_be32s(f, &s->umstat);
312     qemu_put_be32s(f, &s->utxh);
313     qemu_put_be32s(f, &s->urxh);
314     qemu_put_be32s(f, &s->ubrdiv);
315     qemu_put_be32s(f, &s->udivslot);
316     qemu_put_be32s(f, &s->uintp);
317     qemu_put_be32s(f, &s->uintsp);
318     qemu_put_be32s(f, &s->uintm);
319 }
320
321 static int s5pc1xx_uart_load(QEMUFile *f, void *opaque, int version_id)
322 {
323     S5pc1xxUartState *s = (S5pc1xxUartState *)opaque;
324
325     if (version_id != 1) {
326         return -EINVAL;
327     }
328
329     qemu_get_buffer(f, s->rx.queue, QUEUE_SIZE);
330     qemu_get_be32s(f, &s->rx.s);
331     qemu_get_be32s(f, &s->rx.t);
332     qemu_get_be32s(f, &s->rx.size);
333
334     qemu_get_be32s(f, &s->ulcon);
335     qemu_get_be32s(f, &s->ucon);
336     qemu_get_be32s(f, &s->ufcon);
337     qemu_get_be32s(f, &s->umcon);
338     qemu_get_be32s(f, &s->utrstat);
339     qemu_get_be32s(f, &s->uerstat);
340     qemu_get_be32s(f, &s->ufstat);
341     qemu_get_be32s(f, &s->umstat);
342     qemu_get_be32s(f, &s->utxh);
343     qemu_get_be32s(f, &s->urxh);
344     qemu_get_be32s(f, &s->ubrdiv);
345     qemu_get_be32s(f, &s->udivslot);
346     qemu_get_be32s(f, &s->uintp);
347     qemu_get_be32s(f, &s->uintsp);
348     qemu_get_be32s(f, &s->uintm);
349
350     return 0;
351 }
352
353 static int s5pc1xx_uart_can_receive(void *opaque)
354 {
355     S5pc1xxUartState *s = (S5pc1xxUartState *)opaque;
356
357     return queue_empty_count(&s->rx);
358 }
359
360 static void s5pc1xx_uart_trigger_level(S5pc1xxUartState *s)
361 {
362     /* TODO: fix this */
363     if (! queue_empty(&s->rx)) {
364         s->uintsp |= INT_RXD;
365     }
366 }
367
368 static void s5pc1xx_uart_receive(void *opaque, const uint8_t *buf, int size)
369 {
370     int i;
371     S5pc1xxUartState *s = (S5pc1xxUartState *)opaque;
372     if (s->ufcon & 1) {
373         if (queue_empty_count(&s->rx) < size) {
374             for (i = 0; i < queue_empty_count(&s->rx); i++) {
375                 queue_push(&s->rx, buf[i]);
376             }
377             s->uintp |= INT_ERROR;
378             s->utrstat |= TRSTATUS_DATA_READY;
379         } else {
380             for (i = 0; i < size; i++) {
381                 queue_push(&s->rx, buf[i]);
382             }
383             s->utrstat |= TRSTATUS_DATA_READY;
384         }
385         s5pc1xx_uart_trigger_level(s);
386     } else {
387         s->urxh = buf[0];
388         s->uintsp |= INT_RXD;
389         s->utrstat |= TRSTATUS_DATA_READY;
390     }
391     s5pc1xx_uart_update(s);
392 }
393
394 static void s5pc1xx_uart_event(void *opaque, int event)
395 {
396     /* TODO: implement this */
397 }
398
399 static void s5pc1xx_uart_reset(DeviceState *d)
400 {
401     S5pc1xxUartState *s =
402         FROM_SYSBUS(S5pc1xxUartState, sysbus_from_qdev(d));
403
404     s->ulcon    = 0;
405     s->ucon     = 0;
406     s->ufcon    = 0;
407     s->umcon    = 0;
408     s->utrstat  = 0x6;
409     s->uerstat  = 0;
410     s->ufstat   = 0;
411     s->umstat   = 0;
412     s->ubrdiv   = 0;
413     s->udivslot = 0;
414     s->uintp    = 0;
415     s->uintsp   = 0;
416     s->uintm    = 0;
417     queue_reset(&s->rx);
418 }
419
420 DeviceState *s5pc1xx_uart_init(target_phys_addr_t base, int instance,
421                                int queue_size, qemu_irq irq,
422                                CharDriverState *chr)
423 {
424     DeviceState *dev = qdev_create(NULL, "s5pc1xx.uart");
425     char str[] = "s5pc1xx.uart.00";
426
427     if (!chr) {
428         snprintf(str, strlen(str) + 1, "s5pc1xx.uart.%02d", instance % 100);
429         chr = qemu_chr_open(str, "null", NULL);
430     }
431     qdev_prop_set_chr(dev, "chr", chr);
432     qdev_prop_set_uint32(dev, "queue-size", queue_size);
433     qdev_prop_set_uint32(dev, "instance", instance);
434     qdev_init_nofail(dev);
435     sysbus_mmio_map(sysbus_from_qdev(dev), 0, base);
436     sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq);
437     return dev;
438 }
439
440 static int s5pc1xx_uart_init1(SysBusDevice *dev)
441 {
442     int iomemtype;
443     S5pc1xxUartState *s = FROM_SYSBUS(S5pc1xxUartState, dev);
444
445     s5pc1xx_uart_reset(&s->busdev.qdev);
446
447     sysbus_init_irq(dev, &s->irq);
448
449     qemu_chr_add_handlers(s->chr, s5pc1xx_uart_can_receive,
450                           s5pc1xx_uart_receive, s5pc1xx_uart_event, s);
451
452     iomemtype =
453         cpu_register_io_memory(s5pc1xx_uart_readfn, s5pc1xx_uart_writefn, s,
454                                 DEVICE_NATIVE_ENDIAN);
455     sysbus_init_mmio(dev, S5PC1XX_UART_REG_MEM_SIZE, iomemtype);
456
457     s5pc1xx_gpio_register_io_memory(GPIO_IDX_UART, s->instance,
458                                     s5pc1xx_uart_gpio_readfn,
459                                     s5pc1xx_uart_gpio_writefn, NULL, s);
460
461     register_savevm(&dev->qdev, "s5pc1xx.uart", s->instance, 1,
462                     s5pc1xx_uart_save, s5pc1xx_uart_load, s);
463
464     return 0;
465 }
466
467 static SysBusDeviceInfo s5pc1xx_uart_info = {
468     .init = s5pc1xx_uart_init1,
469     .qdev.name  = "s5pc1xx.uart",
470     .qdev.size  = sizeof(S5pc1xxUartState),
471     .qdev.reset = s5pc1xx_uart_reset,
472     .qdev.props = (Property[]) {
473         DEFINE_PROP_UINT32("instance",   S5pc1xxUartState, instance, 0),
474         DEFINE_PROP_UINT32("queue-size", S5pc1xxUartState, rx.size, 16),
475         DEFINE_PROP_CHR("chr", S5pc1xxUartState, chr),
476         DEFINE_PROP_END_OF_LIST(),
477     }
478 };
479
480 static void s5pc1xx_uart_register(void)
481 {
482     sysbus_register_withprop(&s5pc1xx_uart_info);
483 }
484
485 device_init(s5pc1xx_uart_register)