symbian-qemu-0.9.1-12/qemu-symbian-svp/hw/pl031.c
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 /*
       
     2  * ARM AMBA PrimeCell PL031 RTC
       
     3  *
       
     4  * Copyright (c) 2007 CodeSourcery
       
     5  *
       
     6  * This file is free software; you can redistribute it and/or modify
       
     7  * it under the terms of the GNU General Public License version 2 as
       
     8  * published by the Free Software Foundation.
       
     9  *
       
    10  */
       
    11 
       
    12 #include "hw.h"
       
    13 #include "primecell.h"
       
    14 #include "qemu-timer.h"
       
    15 #include "sysemu.h"
       
    16 
       
    17 //#define DEBUG_PL031
       
    18 
       
    19 #ifdef DEBUG_PL031
       
    20 #define DPRINTF(fmt, args...) \
       
    21 do { printf("pl031: " fmt , ##args); } while (0)
       
    22 #else
       
    23 #define DPRINTF(fmt, args...) do {} while(0)
       
    24 #endif
       
    25 
       
    26 #define RTC_DR      0x00    /* Data read register */
       
    27 #define RTC_MR      0x04    /* Match register */
       
    28 #define RTC_LR      0x08    /* Data load register */
       
    29 #define RTC_CR      0x0c    /* Control register */
       
    30 #define RTC_IMSC    0x10    /* Interrupt mask and set register */
       
    31 #define RTC_RIS     0x14    /* Raw interrupt status register */
       
    32 #define RTC_MIS     0x18    /* Masked interrupt status register */
       
    33 #define RTC_ICR     0x1c    /* Interrupt clear register */
       
    34 
       
    35 typedef struct {
       
    36     QEMUTimer *timer;
       
    37     qemu_irq irq;
       
    38 
       
    39     uint64_t start_time;
       
    40     uint32_t tick_offset;
       
    41 
       
    42     uint32_t mr;
       
    43     uint32_t lr;
       
    44     uint32_t cr;
       
    45     uint32_t im;
       
    46     uint32_t is;
       
    47 } pl031_state;
       
    48 
       
    49 static const unsigned char pl031_id[] = {
       
    50     0x31, 0x10, 0x14, 0x00,         /* Device ID        */
       
    51     0x0d, 0xf0, 0x05, 0xb1          /* Cell ID      */
       
    52 };
       
    53 
       
    54 static void pl031_update(pl031_state *s)
       
    55 {
       
    56     qemu_set_irq(s->irq, s->is & s->im);
       
    57 }
       
    58 
       
    59 static void pl031_interrupt(void * opaque)
       
    60 {
       
    61     pl031_state *s = (pl031_state *)opaque;
       
    62 
       
    63     s->im = 1;
       
    64     DPRINTF("Alarm raised\n");
       
    65     pl031_update(s);
       
    66 }
       
    67 
       
    68 static uint32_t pl031_get_count(pl031_state *s)
       
    69 {
       
    70     /* This assumes qemu_get_clock returns the time since the machine was
       
    71        created.  */
       
    72     return s->tick_offset + qemu_get_clock(vm_clock) / ticks_per_sec;
       
    73 }
       
    74 
       
    75 static void pl031_set_alarm(pl031_state *s)
       
    76 {
       
    77     int64_t now;
       
    78     uint32_t ticks;
       
    79 
       
    80     now = qemu_get_clock(vm_clock);
       
    81     ticks = s->tick_offset + now / ticks_per_sec;
       
    82 
       
    83     /* The timer wraps around.  This subtraction also wraps in the same way,
       
    84        and gives correct results when alarm < now_ticks.  */
       
    85     ticks = s->mr - ticks;
       
    86     DPRINTF("Alarm set in %ud ticks\n", ticks);
       
    87     if (ticks == 0) {
       
    88         qemu_del_timer(s->timer);
       
    89         pl031_interrupt(s);
       
    90     } else {
       
    91         qemu_mod_timer(s->timer, now + (int64_t)ticks * ticks_per_sec);
       
    92     }
       
    93 }
       
    94 
       
    95 static uint32_t pl031_read(void *opaque, target_phys_addr_t offset)
       
    96 {
       
    97     pl031_state *s = (pl031_state *)opaque;
       
    98 
       
    99     if (offset >= 0xfe0  &&  offset < 0x1000)
       
   100         return pl031_id[(offset - 0xfe0) >> 2];
       
   101 
       
   102     switch (offset) {
       
   103     case RTC_DR:
       
   104         return pl031_get_count(s);
       
   105     case RTC_MR:
       
   106         return s->mr;
       
   107     case RTC_IMSC:
       
   108         return s->im;
       
   109     case RTC_RIS:
       
   110         return s->is;
       
   111     case RTC_LR:
       
   112         return s->lr;
       
   113     case RTC_CR:
       
   114         /* RTC is permanently enabled.  */
       
   115         return 1;
       
   116     case RTC_MIS:
       
   117         return s->is & s->im;
       
   118     case RTC_ICR:
       
   119         fprintf(stderr, "qemu: pl031_read: Unexpected offset 0x%x\n",
       
   120                 (int)offset);
       
   121         break;
       
   122     default:
       
   123         cpu_abort(cpu_single_env, "pl031_read: Bad offset 0x%x\n",
       
   124                   (int)offset);
       
   125         break;
       
   126     }
       
   127 
       
   128     return 0;
       
   129 }
       
   130 
       
   131 static void pl031_write(void * opaque, target_phys_addr_t offset,
       
   132                         uint32_t value)
       
   133 {
       
   134     pl031_state *s = (pl031_state *)opaque;
       
   135 
       
   136 
       
   137     switch (offset) {
       
   138     case RTC_LR:
       
   139         s->tick_offset += value - pl031_get_count(s);
       
   140         pl031_set_alarm(s);
       
   141         break;
       
   142     case RTC_MR:
       
   143         s->mr = value;
       
   144         pl031_set_alarm(s);
       
   145         break;
       
   146     case RTC_IMSC:
       
   147         s->im = value & 1;
       
   148         DPRINTF("Interrupt mask %d\n", s->im);
       
   149         pl031_update(s);
       
   150         break;
       
   151     case RTC_ICR:
       
   152         /* The PL031 documentation (DDI0224B) states that the interupt is
       
   153            cleared when bit 0 of the written value is set.  However the
       
   154            arm926e documentation (DDI0287B) states that the interrupt is
       
   155            cleared when any value is written.  */
       
   156         DPRINTF("Interrupt cleared");
       
   157         s->is = 0;
       
   158         pl031_update(s);
       
   159         break;
       
   160     case RTC_CR:
       
   161         /* Written value is ignored.  */
       
   162         break;
       
   163 
       
   164     case RTC_DR:
       
   165     case RTC_MIS:
       
   166     case RTC_RIS:
       
   167         fprintf(stderr, "qemu: pl031_write: Unexpected offset 0x%x\n",
       
   168                 (int)offset);
       
   169         break;
       
   170 
       
   171     default:
       
   172         cpu_abort(cpu_single_env, "pl031_write: Bad offset 0x%x\n",
       
   173                   (int)offset);
       
   174         break;
       
   175     }
       
   176 }
       
   177 
       
   178 static CPUWriteMemoryFunc * pl031_writefn[] = {
       
   179     pl031_write,
       
   180     pl031_write,
       
   181     pl031_write
       
   182 };
       
   183 
       
   184 static CPUReadMemoryFunc * pl031_readfn[] = {
       
   185     pl031_read,
       
   186     pl031_read,
       
   187     pl031_read
       
   188 };
       
   189 
       
   190 void pl031_init(uint32_t base, qemu_irq irq)
       
   191 {
       
   192     int iomemtype;
       
   193     pl031_state *s;
       
   194     struct tm tm;
       
   195 
       
   196     s = qemu_mallocz(sizeof(pl031_state));
       
   197     if (!s)
       
   198         cpu_abort(cpu_single_env, "pl031_init: Out of memory\n");
       
   199 
       
   200     iomemtype = cpu_register_io_memory(0, pl031_readfn, pl031_writefn, s);
       
   201     if (iomemtype == -1)
       
   202         cpu_abort(cpu_single_env, "pl031_init: Can't register I/O memory\n");
       
   203 
       
   204     cpu_register_physical_memory(base, 0x00001000, iomemtype);
       
   205 
       
   206     s->irq  = irq;
       
   207     /* ??? We assume vm_clock is zero at this point.  */
       
   208     qemu_get_timedate(&tm, 0);
       
   209     s->tick_offset = mktimegm(&tm);
       
   210 
       
   211     s->timer = qemu_new_timer(vm_clock, pl031_interrupt, s);
       
   212 }