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