symbian-qemu-0.9.1-12/qemu-symbian-svp/hw/sh_serial.c
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 /*
       
     2  * QEMU SCI/SCIF serial port emulation
       
     3  *
       
     4  * Copyright (c) 2007 Magnus Damm
       
     5  *
       
     6  * Based on serial.c - QEMU 16450 UART emulation
       
     7  * Copyright (c) 2003-2004 Fabrice Bellard
       
     8  *
       
     9  * Permission is hereby granted, free of charge, to any person obtaining a copy
       
    10  * of this software and associated documentation files (the "Software"), to deal
       
    11  * in the Software without restriction, including without limitation the rights
       
    12  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       
    13  * copies of the Software, and to permit persons to whom the Software is
       
    14  * furnished to do so, subject to the following conditions:
       
    15  *
       
    16  * The above copyright notice and this permission notice shall be included in
       
    17  * all copies or substantial portions of the Software.
       
    18  *
       
    19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       
    20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       
    21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
       
    22  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       
    23  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       
    24  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
       
    25  * THE SOFTWARE.
       
    26  */
       
    27 #include "hw.h"
       
    28 #include "sh.h"
       
    29 #include "qemu-char.h"
       
    30 #include <assert.h>
       
    31 
       
    32 //#define DEBUG_SERIAL
       
    33 
       
    34 #define SH_SERIAL_FLAG_TEND (1 << 0)
       
    35 #define SH_SERIAL_FLAG_TDE  (1 << 1)
       
    36 #define SH_SERIAL_FLAG_RDF  (1 << 2)
       
    37 #define SH_SERIAL_FLAG_BRK  (1 << 3)
       
    38 #define SH_SERIAL_FLAG_DR   (1 << 4)
       
    39 
       
    40 #define SH_RX_FIFO_LENGTH (16)
       
    41 
       
    42 typedef struct {
       
    43     uint8_t smr;
       
    44     uint8_t brr;
       
    45     uint8_t scr;
       
    46     uint8_t dr; /* ftdr / tdr */
       
    47     uint8_t sr; /* fsr / ssr */
       
    48     uint16_t fcr;
       
    49     uint8_t sptr;
       
    50 
       
    51     uint8_t rx_fifo[SH_RX_FIFO_LENGTH]; /* frdr / rdr */
       
    52     uint8_t rx_cnt;
       
    53     uint8_t rx_tail;
       
    54     uint8_t rx_head;
       
    55 
       
    56     int freq;
       
    57     int feat;
       
    58     int flags;
       
    59     int rtrg;
       
    60 
       
    61     CharDriverState *chr;
       
    62 
       
    63     qemu_irq eri;
       
    64     qemu_irq rxi;
       
    65     qemu_irq txi;
       
    66     qemu_irq tei;
       
    67     qemu_irq bri;
       
    68 } sh_serial_state;
       
    69 
       
    70 static void sh_serial_clear_fifo(sh_serial_state * s)
       
    71 {
       
    72     memset(s->rx_fifo, 0, SH_RX_FIFO_LENGTH);
       
    73     s->rx_cnt = 0;
       
    74     s->rx_head = 0;
       
    75     s->rx_tail = 0;
       
    76 }
       
    77 
       
    78 static void sh_serial_ioport_write(void *opaque, uint32_t offs, uint32_t val)
       
    79 {
       
    80     sh_serial_state *s = opaque;
       
    81     unsigned char ch;
       
    82 
       
    83 #ifdef DEBUG_SERIAL
       
    84     printf("sh_serial: write offs=0x%02x val=0x%02x\n",
       
    85 	   offs, val);
       
    86 #endif
       
    87     switch(offs) {
       
    88     case 0x00: /* SMR */
       
    89         s->smr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0x7b : 0xff);
       
    90         return;
       
    91     case 0x04: /* BRR */
       
    92         s->brr = val;
       
    93 	return;
       
    94     case 0x08: /* SCR */
       
    95         /* TODO : For SH7751, SCIF mask should be 0xfb. */
       
    96         s->scr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0xfa : 0xff);
       
    97         if (!(val & (1 << 5)))
       
    98             s->flags |= SH_SERIAL_FLAG_TEND;
       
    99         if ((s->feat & SH_SERIAL_FEAT_SCIF) && s->txi) {
       
   100 	    qemu_set_irq(s->txi, val & (1 << 7));
       
   101         }
       
   102         if (!(val & (1 << 6))) {
       
   103 	    qemu_set_irq(s->rxi, 0);
       
   104         }
       
   105         return;
       
   106     case 0x0c: /* FTDR / TDR */
       
   107         if (s->chr) {
       
   108             ch = val;
       
   109             qemu_chr_write(s->chr, &ch, 1);
       
   110 	}
       
   111 	s->dr = val;
       
   112 	s->flags &= ~SH_SERIAL_FLAG_TDE;
       
   113         return;
       
   114 #if 0
       
   115     case 0x14: /* FRDR / RDR */
       
   116         ret = 0;
       
   117         break;
       
   118 #endif
       
   119     }
       
   120     if (s->feat & SH_SERIAL_FEAT_SCIF) {
       
   121         switch(offs) {
       
   122         case 0x10: /* FSR */
       
   123             if (!(val & (1 << 6)))
       
   124                 s->flags &= ~SH_SERIAL_FLAG_TEND;
       
   125             if (!(val & (1 << 5)))
       
   126                 s->flags &= ~SH_SERIAL_FLAG_TDE;
       
   127             if (!(val & (1 << 4)))
       
   128                 s->flags &= ~SH_SERIAL_FLAG_BRK;
       
   129             if (!(val & (1 << 1)))
       
   130                 s->flags &= ~SH_SERIAL_FLAG_RDF;
       
   131             if (!(val & (1 << 0)))
       
   132                 s->flags &= ~SH_SERIAL_FLAG_DR;
       
   133 
       
   134             if (!(val & (1 << 1)) || !(val & (1 << 0))) {
       
   135                 if (s->rxi) {
       
   136                     qemu_set_irq(s->rxi, 0);
       
   137                 }
       
   138             }
       
   139             return;
       
   140         case 0x18: /* FCR */
       
   141             s->fcr = val;
       
   142             switch ((val >> 6) & 3) {
       
   143             case 0:
       
   144                 s->rtrg = 1;
       
   145                 break;
       
   146             case 1:
       
   147                 s->rtrg = 4;
       
   148                 break;
       
   149             case 2:
       
   150                 s->rtrg = 8;
       
   151                 break;
       
   152             case 3:
       
   153                 s->rtrg = 14;
       
   154                 break;
       
   155             }
       
   156             if (val & (1 << 1)) {
       
   157                 sh_serial_clear_fifo(s);
       
   158                 s->sr &= ~(1 << 1);
       
   159             }
       
   160 
       
   161             return;
       
   162         case 0x20: /* SPTR */
       
   163             s->sptr = val & 0xf3;
       
   164             return;
       
   165         case 0x24: /* LSR */
       
   166             return;
       
   167         }
       
   168     }
       
   169     else {
       
   170         switch(offs) {
       
   171 #if 0
       
   172         case 0x0c:
       
   173             ret = s->dr;
       
   174             break;
       
   175         case 0x10:
       
   176             ret = 0;
       
   177             break;
       
   178 #endif
       
   179         case 0x1c:
       
   180             s->sptr = val & 0x8f;
       
   181             return;
       
   182         }
       
   183     }
       
   184 
       
   185     fprintf(stderr, "sh_serial: unsupported write to 0x%02x\n", offs);
       
   186     assert(0);
       
   187 }
       
   188 
       
   189 static uint32_t sh_serial_ioport_read(void *opaque, uint32_t offs)
       
   190 {
       
   191     sh_serial_state *s = opaque;
       
   192     uint32_t ret = ~0;
       
   193 
       
   194 #if 0
       
   195     switch(offs) {
       
   196     case 0x00:
       
   197         ret = s->smr;
       
   198         break;
       
   199     case 0x04:
       
   200         ret = s->brr;
       
   201 	break;
       
   202     case 0x08:
       
   203         ret = s->scr;
       
   204         break;
       
   205     case 0x14:
       
   206         ret = 0;
       
   207         break;
       
   208     }
       
   209 #endif
       
   210     if (s->feat & SH_SERIAL_FEAT_SCIF) {
       
   211         switch(offs) {
       
   212         case 0x00: /* SMR */
       
   213             ret = s->smr;
       
   214             break;
       
   215         case 0x08: /* SCR */
       
   216             ret = s->scr;
       
   217             break;
       
   218         case 0x10: /* FSR */
       
   219             ret = 0;
       
   220             if (s->flags & SH_SERIAL_FLAG_TEND)
       
   221                 ret |= (1 << 6);
       
   222             if (s->flags & SH_SERIAL_FLAG_TDE)
       
   223                 ret |= (1 << 5);
       
   224             if (s->flags & SH_SERIAL_FLAG_BRK)
       
   225                 ret |= (1 << 4);
       
   226             if (s->flags & SH_SERIAL_FLAG_RDF)
       
   227                 ret |= (1 << 1);
       
   228             if (s->flags & SH_SERIAL_FLAG_DR)
       
   229                 ret |= (1 << 0);
       
   230 
       
   231             if (s->scr & (1 << 5))
       
   232                 s->flags |= SH_SERIAL_FLAG_TDE | SH_SERIAL_FLAG_TEND;
       
   233 
       
   234             break;
       
   235         case 0x14:
       
   236             if (s->rx_cnt > 0) {
       
   237                 ret = s->rx_fifo[s->rx_tail++];
       
   238                 s->rx_cnt--;
       
   239                 if (s->rx_tail == SH_RX_FIFO_LENGTH)
       
   240                     s->rx_tail = 0;
       
   241                 if (s->rx_cnt < s->rtrg)
       
   242                     s->flags &= ~SH_SERIAL_FLAG_RDF;
       
   243             }
       
   244             break;
       
   245 #if 0
       
   246         case 0x18:
       
   247             ret = s->fcr;
       
   248             break;
       
   249 #endif
       
   250         case 0x1c:
       
   251             ret = s->rx_cnt;
       
   252             break;
       
   253         case 0x20:
       
   254             ret = s->sptr;
       
   255             break;
       
   256         case 0x24:
       
   257             ret = 0;
       
   258             break;
       
   259         }
       
   260     }
       
   261     else {
       
   262         switch(offs) {
       
   263 #if 0
       
   264         case 0x0c:
       
   265             ret = s->dr;
       
   266             break;
       
   267         case 0x10:
       
   268             ret = 0;
       
   269             break;
       
   270         case 0x14:
       
   271             ret = s->rx_fifo[0];
       
   272             break;
       
   273 #endif
       
   274         case 0x1c:
       
   275             ret = s->sptr;
       
   276             break;
       
   277         }
       
   278     }
       
   279 #ifdef DEBUG_SERIAL
       
   280     printf("sh_serial: read offs=0x%02x val=0x%x\n",
       
   281 	   offs, ret);
       
   282 #endif
       
   283 
       
   284     if (ret & ~((1 << 16) - 1)) {
       
   285         fprintf(stderr, "sh_serial: unsupported read from 0x%02x\n", offs);
       
   286 	assert(0);
       
   287     }
       
   288 
       
   289     return ret;
       
   290 }
       
   291 
       
   292 static int sh_serial_can_receive(sh_serial_state *s)
       
   293 {
       
   294     return s->scr & (1 << 4);
       
   295 }
       
   296 
       
   297 static void sh_serial_receive_byte(sh_serial_state *s, int ch)
       
   298 {
       
   299     if (s->feat & SH_SERIAL_FEAT_SCIF) {
       
   300         if (s->rx_cnt < SH_RX_FIFO_LENGTH) {
       
   301             s->rx_fifo[s->rx_head++] = ch;
       
   302             if (s->rx_head == SH_RX_FIFO_LENGTH)
       
   303                 s->rx_head = 0;
       
   304             s->rx_cnt++;
       
   305             if (s->rx_cnt >= s->rtrg) {
       
   306                 s->flags |= SH_SERIAL_FLAG_RDF;
       
   307                 if (s->scr & (1 << 6) && s->rxi) {
       
   308                     qemu_set_irq(s->rxi, 1);
       
   309                 }
       
   310             }
       
   311         }
       
   312     } else {
       
   313         s->rx_fifo[0] = ch;
       
   314     }
       
   315 }
       
   316 
       
   317 static void sh_serial_receive_break(sh_serial_state *s)
       
   318 {
       
   319     if (s->feat & SH_SERIAL_FEAT_SCIF)
       
   320         s->sr |= (1 << 4);
       
   321 }
       
   322 
       
   323 static int sh_serial_can_receive1(void *opaque)
       
   324 {
       
   325     sh_serial_state *s = opaque;
       
   326     return sh_serial_can_receive(s);
       
   327 }
       
   328 
       
   329 static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size)
       
   330 {
       
   331     sh_serial_state *s = opaque;
       
   332     sh_serial_receive_byte(s, buf[0]);
       
   333 }
       
   334 
       
   335 static void sh_serial_event(void *opaque, int event)
       
   336 {
       
   337     sh_serial_state *s = opaque;
       
   338     if (event == CHR_EVENT_BREAK)
       
   339         sh_serial_receive_break(s);
       
   340 }
       
   341 
       
   342 static uint32_t sh_serial_read (void *opaque, target_phys_addr_t addr)
       
   343 {
       
   344     sh_serial_state *s = opaque;
       
   345     return sh_serial_ioport_read(s, addr);
       
   346 }
       
   347 
       
   348 static void sh_serial_write (void *opaque,
       
   349                              target_phys_addr_t addr, uint32_t value)
       
   350 {
       
   351     sh_serial_state *s = opaque;
       
   352     sh_serial_ioport_write(s, addr, value);
       
   353 }
       
   354 
       
   355 static CPUReadMemoryFunc *sh_serial_readfn[] = {
       
   356     &sh_serial_read,
       
   357     &sh_serial_read,
       
   358     &sh_serial_read,
       
   359 };
       
   360 
       
   361 static CPUWriteMemoryFunc *sh_serial_writefn[] = {
       
   362     &sh_serial_write,
       
   363     &sh_serial_write,
       
   364     &sh_serial_write,
       
   365 };
       
   366 
       
   367 void sh_serial_init (target_phys_addr_t base, int feat,
       
   368 		     uint32_t freq, CharDriverState *chr,
       
   369 		     qemu_irq eri_source,
       
   370 		     qemu_irq rxi_source,
       
   371 		     qemu_irq txi_source,
       
   372 		     qemu_irq tei_source,
       
   373 		     qemu_irq bri_source)
       
   374 {
       
   375     sh_serial_state *s;
       
   376     int s_io_memory;
       
   377 
       
   378     s = qemu_mallocz(sizeof(sh_serial_state));
       
   379     if (!s)
       
   380         return;
       
   381 
       
   382     s->feat = feat;
       
   383     s->flags = SH_SERIAL_FLAG_TEND | SH_SERIAL_FLAG_TDE;
       
   384     s->rtrg = 1;
       
   385 
       
   386     s->smr = 0;
       
   387     s->brr = 0xff;
       
   388     s->scr = 1 << 5; /* pretend that TX is enabled so early printk works */
       
   389     s->sptr = 0;
       
   390 
       
   391     if (feat & SH_SERIAL_FEAT_SCIF) {
       
   392         s->fcr = 0;
       
   393     }
       
   394     else {
       
   395         s->dr = 0xff;
       
   396     }
       
   397 
       
   398     sh_serial_clear_fifo(s);
       
   399 
       
   400     s_io_memory = cpu_register_io_memory(0, sh_serial_readfn,
       
   401 					 sh_serial_writefn, s);
       
   402     cpu_register_physical_memory(P4ADDR(base), 0x28, s_io_memory);
       
   403     cpu_register_physical_memory(A7ADDR(base), 0x28, s_io_memory);
       
   404 
       
   405     s->chr = chr;
       
   406 
       
   407     if (chr)
       
   408         qemu_chr_add_handlers(chr, sh_serial_can_receive1, sh_serial_receive1,
       
   409 			      sh_serial_event, s);
       
   410 
       
   411     s->eri = eri_source;
       
   412     s->rxi = rxi_source;
       
   413     s->txi = txi_source;
       
   414     s->tei = tei_source;
       
   415     s->bri = bri_source;
       
   416 }