Diff for /qemu/hw/arm_timer.c between versions 1.1.1.2 and 1.1.1.3

version 1.1.1.2, 2018/04/24 16:45:51 version 1.1.1.3, 2018/04/24 16:48:35
Line 1 Line 1
 /*   /*
  * ARM PrimeCell Timer modules.   * ARM PrimeCell Timer modules.
  *   *
  * Copyright (c) 2005-2006 CodeSourcery.   * Copyright (c) 2005-2006 CodeSourcery.
Line 7 Line 7
  * This code is licenced under the GPL.   * This code is licenced under the GPL.
  */   */
   
 #include "vl.h"  #include "hw.h"
 #include "arm_pic.h"  #include "qemu-timer.h"
   #include "primecell.h"
   
 /* Common timer implementation.  */  /* Common timer implementation.  */
   
Line 22 Line 23
 #define TIMER_CTRL_ENABLE       (1 << 7)  #define TIMER_CTRL_ENABLE       (1 << 7)
   
 typedef struct {  typedef struct {
     int64_t next_time;      ptimer_state *timer;
     int64_t expires;  
     int64_t loaded;  
     QEMUTimer *timer;  
     uint32_t control;      uint32_t control;
     uint32_t count;  
     uint32_t limit;      uint32_t limit;
     int raw_freq;  
     int freq;      int freq;
     int int_level;      int int_level;
     void *pic;      qemu_irq irq;
     int irq;  
 } arm_timer_state;  } arm_timer_state;
   
 /* Calculate the new expiry time of the given timer.  */  
   
 static void arm_timer_reload(arm_timer_state *s)  
 {  
     int64_t delay;  
   
     s->loaded = s->expires;  
     delay = muldiv64(s->count, ticks_per_sec, s->freq);  
     if (delay == 0)  
         delay = 1;  
     s->expires += delay;  
 }  
   
 /* Check all active timers, and schedule the next timer interrupt.  */  /* Check all active timers, and schedule the next timer interrupt.  */
   
 static void arm_timer_update(arm_timer_state *s, int64_t now)  static void arm_timer_update(arm_timer_state *s)
 {  {
     int64_t next;  
   
     /* Ignore disabled timers.  */  
     if ((s->control & TIMER_CTRL_ENABLE) == 0)  
         return;  
     /* Ignore expired one-shot timers.  */  
     if (s->count == 0 && (s->control & TIMER_CTRL_ONESHOT))  
         return;  
     if (s->expires - now <= 0) {  
         /* Timer has expired.  */  
         s->int_level = 1;  
         if (s->control & TIMER_CTRL_ONESHOT) {  
             /* One-shot.  */  
             s->count = 0;  
         } else {  
             if ((s->control & TIMER_CTRL_PERIODIC) == 0) {  
                 /* Free running.  */  
                 if (s->control & TIMER_CTRL_32BIT)  
                     s->count = 0xffffffff;  
                 else  
                     s->count = 0xffff;  
             } else {  
                   /* Periodic.  */  
                   s->count = s->limit;  
             }  
         }  
     }  
     while (s->expires - now <= 0) {  
         arm_timer_reload(s);  
     }  
     /* Update interrupts.  */      /* Update interrupts.  */
     if (s->int_level && (s->control & TIMER_CTRL_IE)) {      if (s->int_level && (s->control & TIMER_CTRL_IE)) {
         pic_set_irq_new(s->pic, s->irq, 1);          qemu_irq_raise(s->irq);
     } else {      } else {
         pic_set_irq_new(s->pic, s->irq, 0);          qemu_irq_lower(s->irq);
     }  
   
     next = now;  
     if (next - s->expires < 0)  
         next = s->expires;  
   
     /* Schedule the next timer interrupt.  */  
     if (next == now) {  
         qemu_del_timer(s->timer);  
         s->next_time = 0;  
     } else if (next != s->next_time) {  
         qemu_mod_timer(s->timer, next);  
         s->next_time = next;  
     }  
 }  
   
 /* Return the current value of the timer.  */  
 static uint32_t arm_timer_getcount(arm_timer_state *s, int64_t now)  
 {  
     int64_t left;  
     int64_t period;  
   
     if (s->count == 0)  
         return 0;  
     if ((s->control & TIMER_CTRL_ENABLE) == 0)  
         return s->count;  
     left = s->expires - now;  
     period = s->expires - s->loaded;  
     /* If the timer should have expired then return 0.  This can happen  
        when the host timer signal doesnt occur immediately.  It's better to  
        have a timer appear to sit at zero for a while than have it wrap  
        around before the guest interrupt is raised.  */  
     /* ??? Could we trigger the interrupt here?  */  
     if (left < 0)  
         return 0;  
     /* We need to calculate count * elapsed / period without overfowing.  
        Scale both elapsed and period so they fit in a 32-bit int.  */  
     while (period != (int32_t)period) {  
         period >>= 1;  
         left >>= 1;  
     }      }
     return ((uint64_t)s->count * (uint64_t)(int32_t)left)  
             / (int32_t)period;  
 }  }
   
 uint32_t arm_timer_read(void *opaque, target_phys_addr_t offset)  static uint32_t arm_timer_read(void *opaque, target_phys_addr_t offset)
 {  {
     arm_timer_state *s = (arm_timer_state *)opaque;      arm_timer_state *s = (arm_timer_state *)opaque;
   
Line 142  uint32_t arm_timer_read(void *opaque, ta Line 52  uint32_t arm_timer_read(void *opaque, ta
     case 6: /* TimerBGLoad */      case 6: /* TimerBGLoad */
         return s->limit;          return s->limit;
     case 1: /* TimerValue */      case 1: /* TimerValue */
         return arm_timer_getcount(s, qemu_get_clock(vm_clock));          return ptimer_get_count(s->timer);
     case 2: /* TimerControl */      case 2: /* TimerControl */
         return s->control;          return s->control;
     case 4: /* TimerRIS */      case 4: /* TimerRIS */
Line 152  uint32_t arm_timer_read(void *opaque, ta Line 62  uint32_t arm_timer_read(void *opaque, ta
             return 0;              return 0;
         return s->int_level;          return s->int_level;
     default:      default:
         cpu_abort (cpu_single_env, "arm_timer_read: Bad offset %x\n", offset);          cpu_abort (cpu_single_env, "arm_timer_read: Bad offset %x\n",
                      (int)offset);
         return 0;          return 0;
     }      }
 }  }
   
   /* Reset the timer limit after settings have changed.  */
   static void arm_timer_recalibrate(arm_timer_state *s, int reload)
   {
       uint32_t limit;
   
       if ((s->control & TIMER_CTRL_PERIODIC) == 0) {
           /* Free running.  */
           if (s->control & TIMER_CTRL_32BIT)
               limit = 0xffffffff;
           else
               limit = 0xffff;
       } else {
             /* Periodic.  */
             limit = s->limit;
       }
       ptimer_set_limit(s->timer, limit, reload);
   }
   
 static void arm_timer_write(void *opaque, target_phys_addr_t offset,  static void arm_timer_write(void *opaque, target_phys_addr_t offset,
                             uint32_t value)                              uint32_t value)
 {  {
     arm_timer_state *s = (arm_timer_state *)opaque;      arm_timer_state *s = (arm_timer_state *)opaque;
     int64_t now;      int freq;
   
     now = qemu_get_clock(vm_clock);  
     switch (offset >> 2) {      switch (offset >> 2) {
     case 0: /* TimerLoad */      case 0: /* TimerLoad */
         s->limit = value;          s->limit = value;
         s->count = value;          arm_timer_recalibrate(s, 1);
         s->expires = now;  
         arm_timer_reload(s);  
         break;          break;
     case 1: /* TimerValue */      case 1: /* TimerValue */
         /* ??? Linux seems to want to write to this readonly register.          /* ??? Linux seems to want to write to this readonly register.
Line 180  static void arm_timer_write(void *opaque Line 106  static void arm_timer_write(void *opaque
             /* Pause the timer if it is running.  This may cause some              /* Pause the timer if it is running.  This may cause some
                inaccuracy dure to rounding, but avoids a whole lot of other                 inaccuracy dure to rounding, but avoids a whole lot of other
                messyness.  */                 messyness.  */
             s->count = arm_timer_getcount(s, now);              ptimer_stop(s->timer);
         }          }
         s->control = value;          s->control = value;
         s->freq = s->raw_freq;          freq = s->freq;
         /* ??? Need to recalculate expiry time after changing divisor.  */          /* ??? Need to recalculate expiry time after changing divisor.  */
         switch ((value >> 2) & 3) {          switch ((value >> 2) & 3) {
         case 1: s->freq >>= 4; break;          case 1: freq >>= 4; break;
         case 2: s->freq >>= 8; break;          case 2: freq >>= 8; break;
         }          }
           arm_timer_recalibrate(s, 0);
           ptimer_set_freq(s->timer, freq);
         if (s->control & TIMER_CTRL_ENABLE) {          if (s->control & TIMER_CTRL_ENABLE) {
             /* Restart the timer if still enabled.  */              /* Restart the timer if still enabled.  */
             s->expires = now;              ptimer_run(s->timer, (s->control & TIMER_CTRL_ONESHOT) != 0);
             arm_timer_reload(s);  
         }          }
         break;          break;
     case 3: /* TimerIntClr */      case 3: /* TimerIntClr */
Line 200  static void arm_timer_write(void *opaque Line 127  static void arm_timer_write(void *opaque
         break;          break;
     case 6: /* TimerBGLoad */      case 6: /* TimerBGLoad */
         s->limit = value;          s->limit = value;
           arm_timer_recalibrate(s, 0);
         break;          break;
     default:      default:
         cpu_abort (cpu_single_env, "arm_timer_write: Bad offset %x\n", offset);          cpu_abort (cpu_single_env, "arm_timer_write: Bad offset %x\n",
                      (int)offset);
     }      }
     arm_timer_update(s, now);      arm_timer_update(s);
 }  }
   
 static void arm_timer_tick(void *opaque)  static void arm_timer_tick(void *opaque)
 {  {
     int64_t now;      arm_timer_state *s = (arm_timer_state *)opaque;
       s->int_level = 1;
     now = qemu_get_clock(vm_clock);      arm_timer_update(s);
     arm_timer_update((arm_timer_state *)opaque, now);  
 }  }
   
 static void *arm_timer_init(uint32_t freq, void *pic, int irq)  static void *arm_timer_init(uint32_t freq, qemu_irq irq)
 {  {
     arm_timer_state *s;      arm_timer_state *s;
       QEMUBH *bh;
   
     s = (arm_timer_state *)qemu_mallocz(sizeof(arm_timer_state));      s = (arm_timer_state *)qemu_mallocz(sizeof(arm_timer_state));
     s->pic = pic;  
     s->irq = irq;      s->irq = irq;
     s->raw_freq = s->freq = 1000000;      s->freq = freq;
     s->control = TIMER_CTRL_IE;      s->control = TIMER_CTRL_IE;
     s->count = 0xffffffff;  
   
     s->timer = qemu_new_timer(vm_clock, arm_timer_tick, s);      bh = qemu_bh_new(arm_timer_tick, s);
       s->timer = ptimer_init(bh);
     /* ??? Save/restore.  */      /* ??? Save/restore.  */
     return s;      return s;
 }  }
   
 /* ARM PrimeCell SP804 dual timer module.  /* ARM PrimeCell SP804 dual timer module.
    Docs for this device don't seem to be publicly available.  This     Docs for this device don't seem to be publicly available.  This
    implementation is based on gueswork, the linux kernel sources and the     implementation is based on guesswork, the linux kernel sources and the
    Integrator/CP timer modules.  */     Integrator/CP timer modules.  */
   
 typedef struct {  typedef struct {
     /* Include a pseudo-PIC device to merge the two interrupt sources.  */  
     arm_pic_handler handler;  
     void *timer[2];      void *timer[2];
     int level[2];      int level[2];
     uint32_t base;      uint32_t base;
     /* The output PIC device.  */      qemu_irq irq;
     void *pic;  
     int irq;  
 } sp804_state;  } sp804_state;
   
   /* Merge the IRQs from the two component devices.  */
 static void sp804_set_irq(void *opaque, int irq, int level)  static void sp804_set_irq(void *opaque, int irq, int level)
 {  {
     sp804_state *s = (sp804_state *)opaque;      sp804_state *s = (sp804_state *)opaque;
   
     s->level[irq] = level;      s->level[irq] = level;
     pic_set_irq_new(s->pic, s->irq, s->level[0] || s->level[1]);      qemu_set_irq(s->irq, s->level[0] || s->level[1]);
 }  }
   
 static uint32_t sp804_read(void *opaque, target_phys_addr_t offset)  static uint32_t sp804_read(void *opaque, target_phys_addr_t offset)
Line 293  static CPUWriteMemoryFunc *sp804_writefn Line 218  static CPUWriteMemoryFunc *sp804_writefn
    sp804_write     sp804_write
 };  };
   
 void sp804_init(uint32_t base, void *pic, int irq)  void sp804_init(uint32_t base, qemu_irq irq)
 {  {
     int iomemtype;      int iomemtype;
     sp804_state *s;      sp804_state *s;
       qemu_irq *qi;
   
     s = (sp804_state *)qemu_mallocz(sizeof(sp804_state));      s = (sp804_state *)qemu_mallocz(sizeof(sp804_state));
     s->handler = sp804_set_irq;      qi = qemu_allocate_irqs(sp804_set_irq, s, 2);
     s->base = base;      s->base = base;
     s->pic = pic;  
     s->irq = irq;      s->irq = irq;
     /* ??? The timers are actually configurable between 32kHz and 1MHz, but      /* ??? The timers are actually configurable between 32kHz and 1MHz, but
        we don't implement that.  */         we don't implement that.  */
     s->timer[0] = arm_timer_init(1000000, s, 0);      s->timer[0] = arm_timer_init(1000000, qi[0]);
     s->timer[1] = arm_timer_init(1000000, s, 1);      s->timer[1] = arm_timer_init(1000000, qi[1]);
     iomemtype = cpu_register_io_memory(0, sp804_readfn,      iomemtype = cpu_register_io_memory(0, sp804_readfn,
                                        sp804_writefn, s);                                         sp804_writefn, s);
     cpu_register_physical_memory(base, 0x00000fff, iomemtype);      cpu_register_physical_memory(base, 0x00001000, iomemtype);
     /* ??? Save/restore.  */      /* ??? Save/restore.  */
 }  }
   
Line 362  static CPUWriteMemoryFunc *icp_pit_write Line 287  static CPUWriteMemoryFunc *icp_pit_write
    icp_pit_write     icp_pit_write
 };  };
   
 void icp_pit_init(uint32_t base, void *pic, int irq)  void icp_pit_init(uint32_t base, qemu_irq *pic, int irq)
 {  {
     int iomemtype;      int iomemtype;
     icp_pit_state *s;      icp_pit_state *s;
Line 370  void icp_pit_init(uint32_t base, void *p Line 295  void icp_pit_init(uint32_t base, void *p
     s = (icp_pit_state *)qemu_mallocz(sizeof(icp_pit_state));      s = (icp_pit_state *)qemu_mallocz(sizeof(icp_pit_state));
     s->base = base;      s->base = base;
     /* Timer 0 runs at the system clock speed (40MHz).  */      /* Timer 0 runs at the system clock speed (40MHz).  */
     s->timer[0] = arm_timer_init(40000000, pic, irq);      s->timer[0] = arm_timer_init(40000000, pic[irq]);
     /* The other two timers run at 1MHz.  */      /* The other two timers run at 1MHz.  */
     s->timer[1] = arm_timer_init(1000000, pic, irq + 1);      s->timer[1] = arm_timer_init(1000000, pic[irq + 1]);
     s->timer[2] = arm_timer_init(1000000, pic, irq + 2);      s->timer[2] = arm_timer_init(1000000, pic[irq + 2]);
   
     iomemtype = cpu_register_io_memory(0, icp_pit_readfn,      iomemtype = cpu_register_io_memory(0, icp_pit_readfn,
                                        icp_pit_writefn, s);                                         icp_pit_writefn, s);
     cpu_register_physical_memory(base, 0x00000fff, iomemtype);      cpu_register_physical_memory(base, 0x00001000, iomemtype);
     /* ??? Save/restore.  */      /* ??? Save/restore.  */
 }  }
   

Removed from v.1.1.1.2  
changed lines
  Added in v.1.1.1.3


unix.superglobalmegacorp.com