xref: /qemu/hw/char/sh_serial.c (revision 2c9b15cab12c21e32dffb67c5e18f3dc407ca224)
12f062c72Sths /*
22f062c72Sths  * QEMU SCI/SCIF serial port emulation
32f062c72Sths  *
42f062c72Sths  * Copyright (c) 2007 Magnus Damm
52f062c72Sths  *
62f062c72Sths  * Based on serial.c - QEMU 16450 UART emulation
72f062c72Sths  * Copyright (c) 2003-2004 Fabrice Bellard
82f062c72Sths  *
92f062c72Sths  * Permission is hereby granted, free of charge, to any person obtaining a copy
102f062c72Sths  * of this software and associated documentation files (the "Software"), to deal
112f062c72Sths  * in the Software without restriction, including without limitation the rights
122f062c72Sths  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
132f062c72Sths  * copies of the Software, and to permit persons to whom the Software is
142f062c72Sths  * furnished to do so, subject to the following conditions:
152f062c72Sths  *
162f062c72Sths  * The above copyright notice and this permission notice shall be included in
172f062c72Sths  * all copies or substantial portions of the Software.
182f062c72Sths  *
192f062c72Sths  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
202f062c72Sths  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
212f062c72Sths  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
222f062c72Sths  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
232f062c72Sths  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
242f062c72Sths  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
252f062c72Sths  * THE SOFTWARE.
262f062c72Sths  */
2783c9f4caSPaolo Bonzini #include "hw/hw.h"
280d09e41aSPaolo Bonzini #include "hw/sh4/sh.h"
29dccfcd0eSPaolo Bonzini #include "sysemu/char.h"
30022c62cbSPaolo Bonzini #include "exec/address-spaces.h"
312f062c72Sths 
322f062c72Sths //#define DEBUG_SERIAL
332f062c72Sths 
342f062c72Sths #define SH_SERIAL_FLAG_TEND (1 << 0)
352f062c72Sths #define SH_SERIAL_FLAG_TDE  (1 << 1)
362f062c72Sths #define SH_SERIAL_FLAG_RDF  (1 << 2)
372f062c72Sths #define SH_SERIAL_FLAG_BRK  (1 << 3)
382f062c72Sths #define SH_SERIAL_FLAG_DR   (1 << 4)
392f062c72Sths 
4063242a00Saurel32 #define SH_RX_FIFO_LENGTH (16)
4163242a00Saurel32 
422f062c72Sths typedef struct {
439a9d0b81SBenoît Canet     MemoryRegion iomem;
449a9d0b81SBenoît Canet     MemoryRegion iomem_p4;
459a9d0b81SBenoît Canet     MemoryRegion iomem_a7;
462f062c72Sths     uint8_t smr;
472f062c72Sths     uint8_t brr;
482f062c72Sths     uint8_t scr;
492f062c72Sths     uint8_t dr; /* ftdr / tdr */
502f062c72Sths     uint8_t sr; /* fsr / ssr */
512f062c72Sths     uint16_t fcr;
522f062c72Sths     uint8_t sptr;
532f062c72Sths 
5463242a00Saurel32     uint8_t rx_fifo[SH_RX_FIFO_LENGTH]; /* frdr / rdr */
552f062c72Sths     uint8_t rx_cnt;
5663242a00Saurel32     uint8_t rx_tail;
5763242a00Saurel32     uint8_t rx_head;
582f062c72Sths 
592f062c72Sths     int freq;
602f062c72Sths     int feat;
612f062c72Sths     int flags;
6263242a00Saurel32     int rtrg;
632f062c72Sths 
642f062c72Sths     CharDriverState *chr;
65bf5b7423Saurel32 
664e7ed2d1Saurel32     qemu_irq eri;
674e7ed2d1Saurel32     qemu_irq rxi;
684e7ed2d1Saurel32     qemu_irq txi;
694e7ed2d1Saurel32     qemu_irq tei;
704e7ed2d1Saurel32     qemu_irq bri;
712f062c72Sths } sh_serial_state;
722f062c72Sths 
7363242a00Saurel32 static void sh_serial_clear_fifo(sh_serial_state * s)
7463242a00Saurel32 {
7563242a00Saurel32     memset(s->rx_fifo, 0, SH_RX_FIFO_LENGTH);
7663242a00Saurel32     s->rx_cnt = 0;
7763242a00Saurel32     s->rx_head = 0;
7863242a00Saurel32     s->rx_tail = 0;
7963242a00Saurel32 }
8063242a00Saurel32 
81a8170e5eSAvi Kivity static void sh_serial_write(void *opaque, hwaddr offs,
829a9d0b81SBenoît Canet                             uint64_t val, unsigned size)
832f062c72Sths {
842f062c72Sths     sh_serial_state *s = opaque;
852f062c72Sths     unsigned char ch;
862f062c72Sths 
872f062c72Sths #ifdef DEBUG_SERIAL
888da3ff18Spbrook     printf("sh_serial: write offs=0x%02x val=0x%02x\n",
898da3ff18Spbrook 	   offs, val);
902f062c72Sths #endif
912f062c72Sths     switch(offs) {
922f062c72Sths     case 0x00: /* SMR */
932f062c72Sths         s->smr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0x7b : 0xff);
942f062c72Sths         return;
952f062c72Sths     case 0x04: /* BRR */
962f062c72Sths         s->brr = val;
972f062c72Sths 	return;
982f062c72Sths     case 0x08: /* SCR */
9963242a00Saurel32         /* TODO : For SH7751, SCIF mask should be 0xfb. */
100bf5b7423Saurel32         s->scr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0xfa : 0xff);
1012f062c72Sths         if (!(val & (1 << 5)))
1022f062c72Sths             s->flags |= SH_SERIAL_FLAG_TEND;
103bf5b7423Saurel32         if ((s->feat & SH_SERIAL_FEAT_SCIF) && s->txi) {
1044e7ed2d1Saurel32 	    qemu_set_irq(s->txi, val & (1 << 7));
105bf5b7423Saurel32         }
1064e7ed2d1Saurel32         if (!(val & (1 << 6))) {
1074e7ed2d1Saurel32 	    qemu_set_irq(s->rxi, 0);
10863242a00Saurel32         }
1092f062c72Sths         return;
1102f062c72Sths     case 0x0c: /* FTDR / TDR */
1112f062c72Sths         if (s->chr) {
1122f062c72Sths             ch = val;
1132cc6e0a1SAnthony Liguori             qemu_chr_fe_write(s->chr, &ch, 1);
1142f062c72Sths 	}
1152f062c72Sths 	s->dr = val;
1162f062c72Sths 	s->flags &= ~SH_SERIAL_FLAG_TDE;
1172f062c72Sths         return;
1182f062c72Sths #if 0
1192f062c72Sths     case 0x14: /* FRDR / RDR */
1202f062c72Sths         ret = 0;
1212f062c72Sths         break;
1222f062c72Sths #endif
1232f062c72Sths     }
1242f062c72Sths     if (s->feat & SH_SERIAL_FEAT_SCIF) {
1252f062c72Sths         switch(offs) {
1262f062c72Sths         case 0x10: /* FSR */
1272f062c72Sths             if (!(val & (1 << 6)))
1282f062c72Sths                 s->flags &= ~SH_SERIAL_FLAG_TEND;
1292f062c72Sths             if (!(val & (1 << 5)))
1302f062c72Sths                 s->flags &= ~SH_SERIAL_FLAG_TDE;
1312f062c72Sths             if (!(val & (1 << 4)))
1322f062c72Sths                 s->flags &= ~SH_SERIAL_FLAG_BRK;
1332f062c72Sths             if (!(val & (1 << 1)))
1342f062c72Sths                 s->flags &= ~SH_SERIAL_FLAG_RDF;
1352f062c72Sths             if (!(val & (1 << 0)))
1362f062c72Sths                 s->flags &= ~SH_SERIAL_FLAG_DR;
13763242a00Saurel32 
13863242a00Saurel32             if (!(val & (1 << 1)) || !(val & (1 << 0))) {
1394e7ed2d1Saurel32                 if (s->rxi) {
1404e7ed2d1Saurel32                     qemu_set_irq(s->rxi, 0);
14163242a00Saurel32                 }
14263242a00Saurel32             }
1432f062c72Sths             return;
1442f062c72Sths         case 0x18: /* FCR */
1452f062c72Sths             s->fcr = val;
14663242a00Saurel32             switch ((val >> 6) & 3) {
14763242a00Saurel32             case 0:
14863242a00Saurel32                 s->rtrg = 1;
14963242a00Saurel32                 break;
15063242a00Saurel32             case 1:
15163242a00Saurel32                 s->rtrg = 4;
15263242a00Saurel32                 break;
15363242a00Saurel32             case 2:
15463242a00Saurel32                 s->rtrg = 8;
15563242a00Saurel32                 break;
15663242a00Saurel32             case 3:
15763242a00Saurel32                 s->rtrg = 14;
15863242a00Saurel32                 break;
15963242a00Saurel32             }
16063242a00Saurel32             if (val & (1 << 1)) {
16163242a00Saurel32                 sh_serial_clear_fifo(s);
16263242a00Saurel32                 s->sr &= ~(1 << 1);
16363242a00Saurel32             }
16463242a00Saurel32 
1652f062c72Sths             return;
1662f062c72Sths         case 0x20: /* SPTR */
16763242a00Saurel32             s->sptr = val & 0xf3;
1682f062c72Sths             return;
1692f062c72Sths         case 0x24: /* LSR */
1702f062c72Sths             return;
1712f062c72Sths         }
1722f062c72Sths     }
1732f062c72Sths     else {
1742f062c72Sths         switch(offs) {
175d1f193b0Saurel32 #if 0
1762f062c72Sths         case 0x0c:
1772f062c72Sths             ret = s->dr;
1782f062c72Sths             break;
1792f062c72Sths         case 0x10:
1802f062c72Sths             ret = 0;
1812f062c72Sths             break;
1822f062c72Sths #endif
183d1f193b0Saurel32         case 0x1c:
184d1f193b0Saurel32             s->sptr = val & 0x8f;
185d1f193b0Saurel32             return;
186d1f193b0Saurel32         }
1872f062c72Sths     }
1882f062c72Sths 
189c1950a4eSPeter Maydell     fprintf(stderr, "sh_serial: unsupported write to 0x%02"
190a8170e5eSAvi Kivity             HWADDR_PRIx "\n", offs);
19143dc2a64SBlue Swirl     abort();
1922f062c72Sths }
1932f062c72Sths 
194a8170e5eSAvi Kivity static uint64_t sh_serial_read(void *opaque, hwaddr offs,
1959a9d0b81SBenoît Canet                                unsigned size)
1962f062c72Sths {
1972f062c72Sths     sh_serial_state *s = opaque;
1982f062c72Sths     uint32_t ret = ~0;
1992f062c72Sths 
2002f062c72Sths #if 0
2012f062c72Sths     switch(offs) {
2022f062c72Sths     case 0x00:
2032f062c72Sths         ret = s->smr;
2042f062c72Sths         break;
2052f062c72Sths     case 0x04:
2062f062c72Sths         ret = s->brr;
2072f062c72Sths 	break;
2082f062c72Sths     case 0x08:
2092f062c72Sths         ret = s->scr;
2102f062c72Sths         break;
2112f062c72Sths     case 0x14:
2122f062c72Sths         ret = 0;
2132f062c72Sths         break;
2142f062c72Sths     }
2152f062c72Sths #endif
2162f062c72Sths     if (s->feat & SH_SERIAL_FEAT_SCIF) {
2172f062c72Sths         switch(offs) {
218bf5b7423Saurel32         case 0x00: /* SMR */
219bf5b7423Saurel32             ret = s->smr;
220bf5b7423Saurel32             break;
221bf5b7423Saurel32         case 0x08: /* SCR */
222bf5b7423Saurel32             ret = s->scr;
223bf5b7423Saurel32             break;
2242f062c72Sths         case 0x10: /* FSR */
2252f062c72Sths             ret = 0;
2262f062c72Sths             if (s->flags & SH_SERIAL_FLAG_TEND)
2272f062c72Sths                 ret |= (1 << 6);
2282f062c72Sths             if (s->flags & SH_SERIAL_FLAG_TDE)
2292f062c72Sths                 ret |= (1 << 5);
2302f062c72Sths             if (s->flags & SH_SERIAL_FLAG_BRK)
2312f062c72Sths                 ret |= (1 << 4);
2322f062c72Sths             if (s->flags & SH_SERIAL_FLAG_RDF)
2332f062c72Sths                 ret |= (1 << 1);
2342f062c72Sths             if (s->flags & SH_SERIAL_FLAG_DR)
2352f062c72Sths                 ret |= (1 << 0);
2362f062c72Sths 
2372f062c72Sths             if (s->scr & (1 << 5))
2382f062c72Sths                 s->flags |= SH_SERIAL_FLAG_TDE | SH_SERIAL_FLAG_TEND;
2392f062c72Sths 
2402f062c72Sths             break;
24163242a00Saurel32         case 0x14:
24263242a00Saurel32             if (s->rx_cnt > 0) {
24363242a00Saurel32                 ret = s->rx_fifo[s->rx_tail++];
24463242a00Saurel32                 s->rx_cnt--;
24563242a00Saurel32                 if (s->rx_tail == SH_RX_FIFO_LENGTH)
24663242a00Saurel32                     s->rx_tail = 0;
24763242a00Saurel32                 if (s->rx_cnt < s->rtrg)
24863242a00Saurel32                     s->flags &= ~SH_SERIAL_FLAG_RDF;
24963242a00Saurel32             }
25063242a00Saurel32             break;
2512f062c72Sths #if 0
2522f062c72Sths         case 0x18:
2532f062c72Sths             ret = s->fcr;
2542f062c72Sths             break;
2552f062c72Sths #endif
2562f062c72Sths         case 0x1c:
2572f062c72Sths             ret = s->rx_cnt;
2582f062c72Sths             break;
2592f062c72Sths         case 0x20:
2602f062c72Sths             ret = s->sptr;
2612f062c72Sths             break;
2622f062c72Sths         case 0x24:
2632f062c72Sths             ret = 0;
2642f062c72Sths             break;
2652f062c72Sths         }
2662f062c72Sths     }
2672f062c72Sths     else {
2682f062c72Sths         switch(offs) {
269d1f193b0Saurel32 #if 0
2702f062c72Sths         case 0x0c:
2712f062c72Sths             ret = s->dr;
2722f062c72Sths             break;
2732f062c72Sths         case 0x10:
2742f062c72Sths             ret = 0;
2752f062c72Sths             break;
27663242a00Saurel32         case 0x14:
27763242a00Saurel32             ret = s->rx_fifo[0];
27863242a00Saurel32             break;
279d1f193b0Saurel32 #endif
2802f062c72Sths         case 0x1c:
2812f062c72Sths             ret = s->sptr;
2822f062c72Sths             break;
2832f062c72Sths         }
2842f062c72Sths     }
2852f062c72Sths #ifdef DEBUG_SERIAL
2868da3ff18Spbrook     printf("sh_serial: read offs=0x%02x val=0x%x\n",
2878da3ff18Spbrook 	   offs, ret);
2882f062c72Sths #endif
2892f062c72Sths 
2902f062c72Sths     if (ret & ~((1 << 16) - 1)) {
291c1950a4eSPeter Maydell         fprintf(stderr, "sh_serial: unsupported read from 0x%02"
292a8170e5eSAvi Kivity                 HWADDR_PRIx "\n", offs);
29343dc2a64SBlue Swirl         abort();
2942f062c72Sths     }
2952f062c72Sths 
2962f062c72Sths     return ret;
2972f062c72Sths }
2982f062c72Sths 
2992f062c72Sths static int sh_serial_can_receive(sh_serial_state *s)
3002f062c72Sths {
30163242a00Saurel32     return s->scr & (1 << 4);
3022f062c72Sths }
3032f062c72Sths 
3042f062c72Sths static void sh_serial_receive_break(sh_serial_state *s)
3052f062c72Sths {
30663242a00Saurel32     if (s->feat & SH_SERIAL_FEAT_SCIF)
30763242a00Saurel32         s->sr |= (1 << 4);
3082f062c72Sths }
3092f062c72Sths 
3102f062c72Sths static int sh_serial_can_receive1(void *opaque)
3112f062c72Sths {
3122f062c72Sths     sh_serial_state *s = opaque;
3132f062c72Sths     return sh_serial_can_receive(s);
3142f062c72Sths }
3152f062c72Sths 
3162f062c72Sths static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size)
3172f062c72Sths {
3182f062c72Sths     sh_serial_state *s = opaque;
319b7d2b020SAurelien Jarno 
320b7d2b020SAurelien Jarno     if (s->feat & SH_SERIAL_FEAT_SCIF) {
321b7d2b020SAurelien Jarno         int i;
322b7d2b020SAurelien Jarno         for (i = 0; i < size; i++) {
323b7d2b020SAurelien Jarno             if (s->rx_cnt < SH_RX_FIFO_LENGTH) {
324b7d2b020SAurelien Jarno                 s->rx_fifo[s->rx_head++] = buf[i];
325b7d2b020SAurelien Jarno                 if (s->rx_head == SH_RX_FIFO_LENGTH) {
326b7d2b020SAurelien Jarno                     s->rx_head = 0;
327b7d2b020SAurelien Jarno                 }
328b7d2b020SAurelien Jarno                 s->rx_cnt++;
329b7d2b020SAurelien Jarno                 if (s->rx_cnt >= s->rtrg) {
330b7d2b020SAurelien Jarno                     s->flags |= SH_SERIAL_FLAG_RDF;
331b7d2b020SAurelien Jarno                     if (s->scr & (1 << 6) && s->rxi) {
332b7d2b020SAurelien Jarno                         qemu_set_irq(s->rxi, 1);
333b7d2b020SAurelien Jarno                     }
334b7d2b020SAurelien Jarno                 }
335b7d2b020SAurelien Jarno             }
336b7d2b020SAurelien Jarno         }
337b7d2b020SAurelien Jarno     } else {
338b7d2b020SAurelien Jarno         s->rx_fifo[0] = buf[0];
339b7d2b020SAurelien Jarno     }
3402f062c72Sths }
3412f062c72Sths 
3422f062c72Sths static void sh_serial_event(void *opaque, int event)
3432f062c72Sths {
3442f062c72Sths     sh_serial_state *s = opaque;
3452f062c72Sths     if (event == CHR_EVENT_BREAK)
3462f062c72Sths         sh_serial_receive_break(s);
3472f062c72Sths }
3482f062c72Sths 
3499a9d0b81SBenoît Canet static const MemoryRegionOps sh_serial_ops = {
3509a9d0b81SBenoît Canet     .read = sh_serial_read,
3519a9d0b81SBenoît Canet     .write = sh_serial_write,
3529a9d0b81SBenoît Canet     .endianness = DEVICE_NATIVE_ENDIAN,
3532f062c72Sths };
3542f062c72Sths 
3559a9d0b81SBenoît Canet void sh_serial_init(MemoryRegion *sysmem,
356a8170e5eSAvi Kivity                     hwaddr base, int feat,
357bf5b7423Saurel32                     uint32_t freq, CharDriverState *chr,
3584e7ed2d1Saurel32                     qemu_irq eri_source,
3594e7ed2d1Saurel32                     qemu_irq rxi_source,
3604e7ed2d1Saurel32                     qemu_irq txi_source,
3614e7ed2d1Saurel32                     qemu_irq tei_source,
3624e7ed2d1Saurel32                     qemu_irq bri_source)
3632f062c72Sths {
3642f062c72Sths     sh_serial_state *s;
3652f062c72Sths 
3667267c094SAnthony Liguori     s = g_malloc0(sizeof(sh_serial_state));
3672f062c72Sths 
3682f062c72Sths     s->feat = feat;
3692f062c72Sths     s->flags = SH_SERIAL_FLAG_TEND | SH_SERIAL_FLAG_TDE;
37063242a00Saurel32     s->rtrg = 1;
3712f062c72Sths 
3722f062c72Sths     s->smr = 0;
3732f062c72Sths     s->brr = 0xff;
374b7d35e65Sbalrog     s->scr = 1 << 5; /* pretend that TX is enabled so early printk works */
3752f062c72Sths     s->sptr = 0;
3762f062c72Sths 
3772f062c72Sths     if (feat & SH_SERIAL_FEAT_SCIF) {
3782f062c72Sths         s->fcr = 0;
3792f062c72Sths     }
3802f062c72Sths     else {
3812f062c72Sths         s->dr = 0xff;
3822f062c72Sths     }
3832f062c72Sths 
38463242a00Saurel32     sh_serial_clear_fifo(s);
3852f062c72Sths 
386*2c9b15caSPaolo Bonzini     memory_region_init_io(&s->iomem, NULL, &sh_serial_ops, s,
3879a9d0b81SBenoît Canet                           "serial", 0x100000000ULL);
3889a9d0b81SBenoît Canet 
389*2c9b15caSPaolo Bonzini     memory_region_init_alias(&s->iomem_p4, NULL, "serial-p4", &s->iomem,
3909a9d0b81SBenoît Canet                              0, 0x28);
3919a9d0b81SBenoît Canet     memory_region_add_subregion(sysmem, P4ADDR(base), &s->iomem_p4);
3929a9d0b81SBenoît Canet 
393*2c9b15caSPaolo Bonzini     memory_region_init_alias(&s->iomem_a7, NULL, "serial-a7", &s->iomem,
3949a9d0b81SBenoît Canet                              0, 0x28);
3959a9d0b81SBenoît Canet     memory_region_add_subregion(sysmem, A7ADDR(base), &s->iomem_a7);
3962f062c72Sths 
3972f062c72Sths     s->chr = chr;
3982f062c72Sths 
399456d6069SHans de Goede     if (chr) {
400456d6069SHans de Goede         qemu_chr_fe_claim_no_fail(chr);
4012f062c72Sths         qemu_chr_add_handlers(chr, sh_serial_can_receive1, sh_serial_receive1,
4022f062c72Sths 			      sh_serial_event, s);
403456d6069SHans de Goede     }
404bf5b7423Saurel32 
405bf5b7423Saurel32     s->eri = eri_source;
406bf5b7423Saurel32     s->rxi = rxi_source;
407bf5b7423Saurel32     s->txi = txi_source;
408bf5b7423Saurel32     s->tei = tei_source;
409bf5b7423Saurel32     s->bri = bri_source;
4102f062c72Sths }
411