symbian-qemu-0.9.1-12/qemu-symbian-svp/hw/pl061.c
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 /*
       
     2  * Arm PrimeCell PL061 General Purpose IO with additional
       
     3  * Luminary Micro Stellaris bits.
       
     4  *
       
     5  * Copyright (c) 2007 CodeSourcery.
       
     6  * Written by Paul Brook
       
     7  *
       
     8  * This code is licenced under the GPL.
       
     9  */
       
    10 
       
    11 #include "hw.h"
       
    12 #include "primecell.h"
       
    13 
       
    14 //#define DEBUG_PL061 1
       
    15 
       
    16 #ifdef DEBUG_PL061
       
    17 #define DPRINTF(fmt, args...) \
       
    18 do { printf("pl061: " fmt , ##args); } while (0)
       
    19 #define BADF(fmt, args...) \
       
    20 do { fprintf(stderr, "pl061: error: " fmt , ##args); exit(1);} while (0)
       
    21 #else
       
    22 #define DPRINTF(fmt, args...) do {} while(0)
       
    23 #define BADF(fmt, args...) \
       
    24 do { fprintf(stderr, "pl061: error: " fmt , ##args);} while (0)
       
    25 #endif
       
    26 
       
    27 static const uint8_t pl061_id[12] =
       
    28   { 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 };
       
    29 
       
    30 typedef struct {
       
    31     int locked;
       
    32     uint8_t data;
       
    33     uint8_t old_data;
       
    34     uint8_t dir;
       
    35     uint8_t isense;
       
    36     uint8_t ibe;
       
    37     uint8_t iev;
       
    38     uint8_t im;
       
    39     uint8_t istate;
       
    40     uint8_t afsel;
       
    41     uint8_t dr2r;
       
    42     uint8_t dr4r;
       
    43     uint8_t dr8r;
       
    44     uint8_t odr;
       
    45     uint8_t pur;
       
    46     uint8_t pdr;
       
    47     uint8_t slr;
       
    48     uint8_t den;
       
    49     uint8_t cr;
       
    50     uint8_t float_high;
       
    51     qemu_irq irq;
       
    52     qemu_irq out[8];
       
    53 } pl061_state;
       
    54 
       
    55 static void pl061_update(pl061_state *s)
       
    56 {
       
    57     uint8_t changed;
       
    58     uint8_t mask;
       
    59     uint8_t out;
       
    60     int i;
       
    61 
       
    62     /* Outputs float high.  */
       
    63     /* FIXME: This is board dependent.  */
       
    64     out = (s->data & s->dir) | ~s->dir;
       
    65     changed = s->old_data ^ out;
       
    66     if (!changed)
       
    67         return;
       
    68 
       
    69     s->old_data = out;
       
    70     for (i = 0; i < 8; i++) {
       
    71         mask = 1 << i;
       
    72         if ((changed & mask) && s->out) {
       
    73             DPRINTF("Set output %d = %d\n", i, (out & mask) != 0);
       
    74             qemu_set_irq(s->out[i], (out & mask) != 0);
       
    75         }
       
    76     }
       
    77 
       
    78     /* FIXME: Implement input interrupts.  */
       
    79 }
       
    80 
       
    81 static uint32_t pl061_read(void *opaque, target_phys_addr_t offset)
       
    82 {
       
    83     pl061_state *s = (pl061_state *)opaque;
       
    84 
       
    85     if (offset >= 0xfd0 && offset < 0x1000) {
       
    86         return pl061_id[(offset - 0xfd0) >> 2];
       
    87     }
       
    88     if (offset < 0x400) {
       
    89         return s->data & (offset >> 2);
       
    90     }
       
    91     switch (offset) {
       
    92     case 0x400: /* Direction */
       
    93         return s->dir;
       
    94     case 0x404: /* Interrupt sense */
       
    95         return s->isense;
       
    96     case 0x408: /* Interrupt both edges */
       
    97         return s->ibe;
       
    98     case 0x40c: /* Interupt event */
       
    99         return s->iev;
       
   100     case 0x410: /* Interrupt mask */
       
   101         return s->im;
       
   102     case 0x414: /* Raw interrupt status */
       
   103         return s->istate;
       
   104     case 0x418: /* Masked interrupt status */
       
   105         return s->istate | s->im;
       
   106     case 0x420: /* Alternate function select */
       
   107         return s->afsel;
       
   108     case 0x500: /* 2mA drive */
       
   109         return s->dr2r;
       
   110     case 0x504: /* 4mA drive */
       
   111         return s->dr4r;
       
   112     case 0x508: /* 8mA drive */
       
   113         return s->dr8r;
       
   114     case 0x50c: /* Open drain */
       
   115         return s->odr;
       
   116     case 0x510: /* Pull-up */
       
   117         return s->pur;
       
   118     case 0x514: /* Pull-down */
       
   119         return s->pdr;
       
   120     case 0x518: /* Slew rate control */
       
   121         return s->slr;
       
   122     case 0x51c: /* Digital enable */
       
   123         return s->den;
       
   124     case 0x520: /* Lock */
       
   125         return s->locked;
       
   126     case 0x524: /* Commit */
       
   127         return s->cr;
       
   128     default:
       
   129         cpu_abort (cpu_single_env, "pl061_read: Bad offset %x\n",
       
   130                    (int)offset);
       
   131         return 0;
       
   132     }
       
   133 }
       
   134 
       
   135 static void pl061_write(void *opaque, target_phys_addr_t offset,
       
   136                         uint32_t value)
       
   137 {
       
   138     pl061_state *s = (pl061_state *)opaque;
       
   139     uint8_t mask;
       
   140 
       
   141     if (offset < 0x400) {
       
   142         mask = (offset >> 2) & s->dir;
       
   143         s->data = (s->data & ~mask) | (value & mask);
       
   144         pl061_update(s);
       
   145         return;
       
   146     }
       
   147     switch (offset) {
       
   148     case 0x400: /* Direction */
       
   149         s->dir = value;
       
   150         break;
       
   151     case 0x404: /* Interrupt sense */
       
   152         s->isense = value;
       
   153         break;
       
   154     case 0x408: /* Interrupt both edges */
       
   155         s->ibe = value;
       
   156         break;
       
   157     case 0x40c: /* Interupt event */
       
   158         s->iev = value;
       
   159         break;
       
   160     case 0x410: /* Interrupt mask */
       
   161         s->im = value;
       
   162         break;
       
   163     case 0x41c: /* Interrupt clear */
       
   164         s->istate &= ~value;
       
   165         break;
       
   166     case 0x420: /* Alternate function select */
       
   167         mask = s->cr;
       
   168         s->afsel = (s->afsel & ~mask) | (value & mask);
       
   169         break;
       
   170     case 0x500: /* 2mA drive */
       
   171         s->dr2r = value;
       
   172         break;
       
   173     case 0x504: /* 4mA drive */
       
   174         s->dr4r = value;
       
   175         break;
       
   176     case 0x508: /* 8mA drive */
       
   177         s->dr8r = value;
       
   178         break;
       
   179     case 0x50c: /* Open drain */
       
   180         s->odr = value;
       
   181         break;
       
   182     case 0x510: /* Pull-up */
       
   183         s->pur = value;
       
   184         break;
       
   185     case 0x514: /* Pull-down */
       
   186         s->pdr = value;
       
   187         break;
       
   188     case 0x518: /* Slew rate control */
       
   189         s->slr = value;
       
   190         break;
       
   191     case 0x51c: /* Digital enable */
       
   192         s->den = value;
       
   193         break;
       
   194     case 0x520: /* Lock */
       
   195         s->locked = (value != 0xacce551);
       
   196         break;
       
   197     case 0x524: /* Commit */
       
   198         if (!s->locked)
       
   199             s->cr = value;
       
   200         break;
       
   201     default:
       
   202         cpu_abort (cpu_single_env, "pl061_write: Bad offset %x\n",
       
   203                    (int)offset);
       
   204     }
       
   205     pl061_update(s);
       
   206 }
       
   207 
       
   208 static void pl061_reset(pl061_state *s)
       
   209 {
       
   210   s->locked = 1;
       
   211   s->cr = 0xff;
       
   212 }
       
   213 
       
   214 static void pl061_set_irq(void * opaque, int irq, int level)
       
   215 {
       
   216     pl061_state *s = (pl061_state *)opaque;
       
   217     uint8_t mask;
       
   218 
       
   219     mask = 1 << irq;
       
   220     if ((s->dir & mask) == 0) {
       
   221         s->data &= ~mask;
       
   222         if (level)
       
   223             s->data |= mask;
       
   224         pl061_update(s);
       
   225     }
       
   226 }
       
   227 
       
   228 static CPUReadMemoryFunc *pl061_readfn[] = {
       
   229    pl061_read,
       
   230    pl061_read,
       
   231    pl061_read
       
   232 };
       
   233 
       
   234 static CPUWriteMemoryFunc *pl061_writefn[] = {
       
   235    pl061_write,
       
   236    pl061_write,
       
   237    pl061_write
       
   238 };
       
   239 
       
   240 static void pl061_save(QEMUFile *f, void *opaque)
       
   241 {
       
   242     pl061_state *s = (pl061_state *)opaque;
       
   243 
       
   244     qemu_put_be32(f, s->locked);
       
   245     qemu_put_be32(f, s->data);
       
   246     qemu_put_be32(f, s->old_data);
       
   247     qemu_put_be32(f, s->dir);
       
   248     qemu_put_be32(f, s->isense);
       
   249     qemu_put_be32(f, s->ibe);
       
   250     qemu_put_be32(f, s->iev);
       
   251     qemu_put_be32(f, s->im);
       
   252     qemu_put_be32(f, s->istate);
       
   253     qemu_put_be32(f, s->afsel);
       
   254     qemu_put_be32(f, s->dr2r);
       
   255     qemu_put_be32(f, s->dr4r);
       
   256     qemu_put_be32(f, s->dr8r);
       
   257     qemu_put_be32(f, s->odr);
       
   258     qemu_put_be32(f, s->pur);
       
   259     qemu_put_be32(f, s->pdr);
       
   260     qemu_put_be32(f, s->slr);
       
   261     qemu_put_be32(f, s->den);
       
   262     qemu_put_be32(f, s->cr);
       
   263     qemu_put_be32(f, s->float_high);
       
   264 }
       
   265 
       
   266 static int pl061_load(QEMUFile *f, void *opaque, int version_id)
       
   267 {
       
   268     pl061_state *s = (pl061_state *)opaque;
       
   269     if (version_id != 1)
       
   270         return -EINVAL;
       
   271 
       
   272     s->locked = qemu_get_be32(f);
       
   273     s->data = qemu_get_be32(f);
       
   274     s->old_data = qemu_get_be32(f);
       
   275     s->dir = qemu_get_be32(f);
       
   276     s->isense = qemu_get_be32(f);
       
   277     s->ibe = qemu_get_be32(f);
       
   278     s->iev = qemu_get_be32(f);
       
   279     s->im = qemu_get_be32(f);
       
   280     s->istate = qemu_get_be32(f);
       
   281     s->afsel = qemu_get_be32(f);
       
   282     s->dr2r = qemu_get_be32(f);
       
   283     s->dr4r = qemu_get_be32(f);
       
   284     s->dr8r = qemu_get_be32(f);
       
   285     s->odr = qemu_get_be32(f);
       
   286     s->pur = qemu_get_be32(f);
       
   287     s->pdr = qemu_get_be32(f);
       
   288     s->slr = qemu_get_be32(f);
       
   289     s->den = qemu_get_be32(f);
       
   290     s->cr = qemu_get_be32(f);
       
   291     s->float_high = qemu_get_be32(f);
       
   292 
       
   293     return 0;
       
   294 }
       
   295 
       
   296 /* Returns an array of inputs.  */
       
   297 qemu_irq *pl061_init(uint32_t base, qemu_irq irq, qemu_irq **out)
       
   298 {
       
   299     int iomemtype;
       
   300     pl061_state *s;
       
   301 
       
   302     s = (pl061_state *)qemu_mallocz(sizeof(pl061_state));
       
   303     iomemtype = cpu_register_io_memory(0, pl061_readfn,
       
   304                                        pl061_writefn, s);
       
   305     cpu_register_physical_memory(base, 0x00001000, iomemtype);
       
   306     s->irq = irq;
       
   307     pl061_reset(s);
       
   308     if (out)
       
   309         *out = s->out;
       
   310 
       
   311     register_savevm("pl061_gpio", -1, 1, pl061_save, pl061_load, s);
       
   312     return qemu_allocate_irqs(pl061_set_irq, s, 8);
       
   313 }