symbian-qemu-0.9.1-12/qemu-symbian-svp/hw/pl011.c
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 /*
       
     2  * Arm PrimeCell PL011 UART
       
     3  *
       
     4  * Copyright (c) 2006 CodeSourcery.
       
     5  * Written by Paul Brook
       
     6  *
       
     7  * This code is licenced under the GPL.
       
     8  */
       
     9 
       
    10 #include "hw.h"
       
    11 #include "qemu-char.h"
       
    12 #include "primecell.h"
       
    13 
       
    14 typedef struct {
       
    15     uint32_t readbuff;
       
    16     uint32_t flags;
       
    17     uint32_t lcr;
       
    18     uint32_t cr;
       
    19     uint32_t dmacr;
       
    20     uint32_t int_enabled;
       
    21     uint32_t int_level;
       
    22     uint32_t read_fifo[16];
       
    23     uint32_t ilpr;
       
    24     uint32_t ibrd;
       
    25     uint32_t fbrd;
       
    26     uint32_t ifl;
       
    27     int read_pos;
       
    28     int read_count;
       
    29     int read_trigger;
       
    30     CharDriverState *chr;
       
    31     qemu_irq irq;
       
    32     enum pl011_type type;
       
    33 } pl011_state;
       
    34 
       
    35 #define PL011_INT_TX 0x20
       
    36 #define PL011_INT_RX 0x10
       
    37 
       
    38 #define PL011_FLAG_TXFE 0x80
       
    39 #define PL011_FLAG_RXFF 0x40
       
    40 #define PL011_FLAG_TXFF 0x20
       
    41 #define PL011_FLAG_RXFE 0x10
       
    42 
       
    43 static const unsigned char pl011_id[2][8] = {
       
    44   { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }, /* PL011_ARM */
       
    45   { 0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 }, /* PL011_LUMINARY */
       
    46 };
       
    47 
       
    48 static void pl011_update(pl011_state *s)
       
    49 {
       
    50     uint32_t flags;
       
    51 
       
    52     flags = s->int_level & s->int_enabled;
       
    53     qemu_set_irq(s->irq, flags != 0);
       
    54 }
       
    55 
       
    56 static uint32_t pl011_read(void *opaque, target_phys_addr_t offset)
       
    57 {
       
    58     pl011_state *s = (pl011_state *)opaque;
       
    59     uint32_t c;
       
    60 
       
    61     if (offset >= 0xfe0 && offset < 0x1000) {
       
    62         return pl011_id[s->type][(offset - 0xfe0) >> 2];
       
    63     }
       
    64     switch (offset >> 2) {
       
    65     case 0: /* UARTDR */
       
    66         s->flags &= ~PL011_FLAG_RXFF;
       
    67         c = s->read_fifo[s->read_pos];
       
    68         if (s->read_count > 0) {
       
    69             s->read_count--;
       
    70             if (++s->read_pos == 16)
       
    71                 s->read_pos = 0;
       
    72         }
       
    73         if (s->read_count == 0) {
       
    74             s->flags |= PL011_FLAG_RXFE;
       
    75         }
       
    76         if (s->read_count == s->read_trigger - 1)
       
    77             s->int_level &= ~ PL011_INT_RX;
       
    78         pl011_update(s);
       
    79         qemu_chr_accept_input(s->chr);
       
    80         return c;
       
    81     case 1: /* UARTCR */
       
    82         return 0;
       
    83     case 6: /* UARTFR */
       
    84         return s->flags;
       
    85     case 8: /* UARTILPR */
       
    86         return s->ilpr;
       
    87     case 9: /* UARTIBRD */
       
    88         return s->ibrd;
       
    89     case 10: /* UARTFBRD */
       
    90         return s->fbrd;
       
    91     case 11: /* UARTLCR_H */
       
    92         return s->lcr;
       
    93     case 12: /* UARTCR */
       
    94         return s->cr;
       
    95     case 13: /* UARTIFLS */
       
    96         return s->ifl;
       
    97     case 14: /* UARTIMSC */
       
    98         return s->int_enabled;
       
    99     case 15: /* UARTRIS */
       
   100         return s->int_level;
       
   101     case 16: /* UARTMIS */
       
   102         return s->int_level & s->int_enabled;
       
   103     case 18: /* UARTDMACR */
       
   104         return s->dmacr;
       
   105     default:
       
   106         cpu_abort (cpu_single_env, "pl011_read: Bad offset %x\n", (int)offset);
       
   107         return 0;
       
   108     }
       
   109 }
       
   110 
       
   111 static void pl011_set_read_trigger(pl011_state *s)
       
   112 {
       
   113 #if 0
       
   114     /* The docs say the RX interrupt is triggered when the FIFO exceeds
       
   115        the threshold.  However linux only reads the FIFO in response to an
       
   116        interrupt.  Triggering the interrupt when the FIFO is non-empty seems
       
   117        to make things work.  */
       
   118     if (s->lcr & 0x10)
       
   119         s->read_trigger = (s->ifl >> 1) & 0x1c;
       
   120     else
       
   121 #endif
       
   122         s->read_trigger = 1;
       
   123 }
       
   124 
       
   125 static void pl011_write(void *opaque, target_phys_addr_t offset,
       
   126                           uint32_t value)
       
   127 {
       
   128     pl011_state *s = (pl011_state *)opaque;
       
   129     unsigned char ch;
       
   130 
       
   131     switch (offset >> 2) {
       
   132     case 0: /* UARTDR */
       
   133         /* ??? Check if transmitter is enabled.  */
       
   134         ch = value;
       
   135         if (s->chr)
       
   136             qemu_chr_write(s->chr, &ch, 1);
       
   137         s->int_level |= PL011_INT_TX;
       
   138         pl011_update(s);
       
   139         break;
       
   140     case 1: /* UARTCR */
       
   141         s->cr = value;
       
   142         break;
       
   143     case 6: /* UARTFR */
       
   144         /* Writes to Flag register are ignored.  */
       
   145         break;
       
   146     case 8: /* UARTUARTILPR */
       
   147         s->ilpr = value;
       
   148         break;
       
   149     case 9: /* UARTIBRD */
       
   150         s->ibrd = value;
       
   151         break;
       
   152     case 10: /* UARTFBRD */
       
   153         s->fbrd = value;
       
   154         break;
       
   155     case 11: /* UARTLCR_H */
       
   156         s->lcr = value;
       
   157         pl011_set_read_trigger(s);
       
   158         break;
       
   159     case 12: /* UARTCR */
       
   160         /* ??? Need to implement the enable and loopback bits.  */
       
   161         s->cr = value;
       
   162         break;
       
   163     case 13: /* UARTIFS */
       
   164         s->ifl = value;
       
   165         pl011_set_read_trigger(s);
       
   166         break;
       
   167     case 14: /* UARTIMSC */
       
   168         s->int_enabled = value;
       
   169         pl011_update(s);
       
   170         break;
       
   171     case 17: /* UARTICR */
       
   172         s->int_level &= ~value;
       
   173         pl011_update(s);
       
   174         break;
       
   175     case 18: /* UARTDMACR */
       
   176         s->dmacr = value;
       
   177         if (value & 3)
       
   178             cpu_abort(cpu_single_env, "PL011: DMA not implemented\n");
       
   179         break;
       
   180     default:
       
   181         cpu_abort (cpu_single_env, "pl011_write: Bad offset %x\n", (int)offset);
       
   182     }
       
   183 }
       
   184 
       
   185 static int pl011_can_receive(void *opaque)
       
   186 {
       
   187     pl011_state *s = (pl011_state *)opaque;
       
   188 
       
   189     if (s->lcr & 0x10)
       
   190         return s->read_count < 16;
       
   191     else
       
   192         return s->read_count < 1;
       
   193 }
       
   194 
       
   195 static void pl011_put_fifo(void *opaque, uint32_t value)
       
   196 {
       
   197     pl011_state *s = (pl011_state *)opaque;
       
   198     int slot;
       
   199 
       
   200     slot = s->read_pos + s->read_count;
       
   201     if (slot >= 16)
       
   202         slot -= 16;
       
   203     s->read_fifo[slot] = value;
       
   204     s->read_count++;
       
   205     s->flags &= ~PL011_FLAG_RXFE;
       
   206     if (s->cr & 0x10 || s->read_count == 16) {
       
   207         s->flags |= PL011_FLAG_RXFF;
       
   208     }
       
   209     if (s->read_count == s->read_trigger) {
       
   210         s->int_level |= PL011_INT_RX;
       
   211         pl011_update(s);
       
   212     }
       
   213 }
       
   214 
       
   215 static void pl011_receive(void *opaque, const uint8_t *buf, int size)
       
   216 {
       
   217     pl011_put_fifo(opaque, *buf);
       
   218 }
       
   219 
       
   220 static void pl011_event(void *opaque, int event)
       
   221 {
       
   222     if (event == CHR_EVENT_BREAK)
       
   223         pl011_put_fifo(opaque, 0x400);
       
   224 }
       
   225 
       
   226 static CPUReadMemoryFunc *pl011_readfn[] = {
       
   227    pl011_read,
       
   228    pl011_read,
       
   229    pl011_read
       
   230 };
       
   231 
       
   232 static CPUWriteMemoryFunc *pl011_writefn[] = {
       
   233    pl011_write,
       
   234    pl011_write,
       
   235    pl011_write
       
   236 };
       
   237 
       
   238 static void pl011_save(QEMUFile *f, void *opaque)
       
   239 {
       
   240     pl011_state *s = (pl011_state *)opaque;
       
   241     int i;
       
   242 
       
   243     qemu_put_be32(f, s->readbuff);
       
   244     qemu_put_be32(f, s->flags);
       
   245     qemu_put_be32(f, s->lcr);
       
   246     qemu_put_be32(f, s->cr);
       
   247     qemu_put_be32(f, s->dmacr);
       
   248     qemu_put_be32(f, s->int_enabled);
       
   249     qemu_put_be32(f, s->int_level);
       
   250     for (i = 0; i < 16; i++)
       
   251         qemu_put_be32(f, s->read_fifo[i]);
       
   252     qemu_put_be32(f, s->ilpr);
       
   253     qemu_put_be32(f, s->ibrd);
       
   254     qemu_put_be32(f, s->fbrd);
       
   255     qemu_put_be32(f, s->ifl);
       
   256     qemu_put_be32(f, s->read_pos);
       
   257     qemu_put_be32(f, s->read_count);
       
   258     qemu_put_be32(f, s->read_trigger);
       
   259 }
       
   260 
       
   261 static int pl011_load(QEMUFile *f, void *opaque, int version_id)
       
   262 {
       
   263     pl011_state *s = (pl011_state *)opaque;
       
   264     int i;
       
   265 
       
   266     if (version_id != 1)
       
   267         return -EINVAL;
       
   268 
       
   269     s->readbuff = qemu_get_be32(f);
       
   270     s->flags = qemu_get_be32(f);
       
   271     s->lcr = qemu_get_be32(f);
       
   272     s->cr = qemu_get_be32(f);
       
   273     s->dmacr = qemu_get_be32(f);
       
   274     s->int_enabled = qemu_get_be32(f);
       
   275     s->int_level = qemu_get_be32(f);
       
   276     for (i = 0; i < 16; i++)
       
   277         s->read_fifo[i] = qemu_get_be32(f);
       
   278     s->ilpr = qemu_get_be32(f);
       
   279     s->ibrd = qemu_get_be32(f);
       
   280     s->fbrd = qemu_get_be32(f);
       
   281     s->ifl = qemu_get_be32(f);
       
   282     s->read_pos = qemu_get_be32(f);
       
   283     s->read_count = qemu_get_be32(f);
       
   284     s->read_trigger = qemu_get_be32(f);
       
   285 
       
   286     return 0;
       
   287 }
       
   288 
       
   289 void pl011_init(uint32_t base, qemu_irq irq,
       
   290                 CharDriverState *chr, enum pl011_type type)
       
   291 {
       
   292     int iomemtype;
       
   293     pl011_state *s;
       
   294 
       
   295     s = (pl011_state *)qemu_mallocz(sizeof(pl011_state));
       
   296     iomemtype = cpu_register_io_memory(0, pl011_readfn,
       
   297                                        pl011_writefn, s);
       
   298     cpu_register_physical_memory(base, 0x00001000, iomemtype);
       
   299     s->irq = irq;
       
   300     s->type = type;
       
   301     s->chr = chr;
       
   302     s->read_trigger = 1;
       
   303     s->ifl = 0x12;
       
   304     s->cr = 0x300;
       
   305     s->flags = 0x90;
       
   306     if (chr){
       
   307         qemu_chr_add_handlers(chr, pl011_can_receive, pl011_receive,
       
   308                               pl011_event, s);
       
   309     }
       
   310     register_savevm("pl011_uart", -1, 1, pl011_save, pl011_load, s);
       
   311 }