symbian-qemu-0.9.1-12/qemu-symbian-svp/hw/i8254.c
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 /*
       
     2  * QEMU 8253/8254 interval timer emulation
       
     3  *
       
     4  * Copyright (c) 2003-2004 Fabrice Bellard
       
     5  *
       
     6  * Permission is hereby granted, free of charge, to any person obtaining a copy
       
     7  * of this software and associated documentation files (the "Software"), to deal
       
     8  * in the Software without restriction, including without limitation the rights
       
     9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       
    10  * copies of the Software, and to permit persons to whom the Software is
       
    11  * furnished to do so, subject to the following conditions:
       
    12  *
       
    13  * The above copyright notice and this permission notice shall be included in
       
    14  * all copies or substantial portions of the Software.
       
    15  *
       
    16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       
    17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       
    18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
       
    19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       
    20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       
    21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
       
    22  * THE SOFTWARE.
       
    23  */
       
    24 #include "hw.h"
       
    25 #include "pc.h"
       
    26 #include "isa.h"
       
    27 #include "qemu-timer.h"
       
    28 
       
    29 //#define DEBUG_PIT
       
    30 
       
    31 #define RW_STATE_LSB 1
       
    32 #define RW_STATE_MSB 2
       
    33 #define RW_STATE_WORD0 3
       
    34 #define RW_STATE_WORD1 4
       
    35 
       
    36 typedef struct PITChannelState {
       
    37     int count; /* can be 65536 */
       
    38     uint16_t latched_count;
       
    39     uint8_t count_latched;
       
    40     uint8_t status_latched;
       
    41     uint8_t status;
       
    42     uint8_t read_state;
       
    43     uint8_t write_state;
       
    44     uint8_t write_latch;
       
    45     uint8_t rw_mode;
       
    46     uint8_t mode;
       
    47     uint8_t bcd; /* not supported */
       
    48     uint8_t gate; /* timer start */
       
    49     int64_t count_load_time;
       
    50     /* irq handling */
       
    51     int64_t next_transition_time;
       
    52     QEMUTimer *irq_timer;
       
    53     qemu_irq irq;
       
    54 } PITChannelState;
       
    55 
       
    56 struct PITState {
       
    57     PITChannelState channels[3];
       
    58 };
       
    59 
       
    60 static PITState pit_state;
       
    61 
       
    62 static void pit_irq_timer_update(PITChannelState *s, int64_t current_time);
       
    63 
       
    64 static int pit_get_count(PITChannelState *s)
       
    65 {
       
    66     uint64_t d;
       
    67     int counter;
       
    68 
       
    69     d = muldiv64(qemu_get_clock(vm_clock) - s->count_load_time, PIT_FREQ, ticks_per_sec);
       
    70     switch(s->mode) {
       
    71     case 0:
       
    72     case 1:
       
    73     case 4:
       
    74     case 5:
       
    75         counter = (s->count - d) & 0xffff;
       
    76         break;
       
    77     case 3:
       
    78         /* XXX: may be incorrect for odd counts */
       
    79         counter = s->count - ((2 * d) % s->count);
       
    80         break;
       
    81     default:
       
    82         counter = s->count - (d % s->count);
       
    83         break;
       
    84     }
       
    85     return counter;
       
    86 }
       
    87 
       
    88 /* get pit output bit */
       
    89 static int pit_get_out1(PITChannelState *s, int64_t current_time)
       
    90 {
       
    91     uint64_t d;
       
    92     int out;
       
    93 
       
    94     d = muldiv64(current_time - s->count_load_time, PIT_FREQ, ticks_per_sec);
       
    95     switch(s->mode) {
       
    96     default:
       
    97     case 0:
       
    98         out = (d >= s->count);
       
    99         break;
       
   100     case 1:
       
   101         out = (d < s->count);
       
   102         break;
       
   103     case 2:
       
   104         if ((d % s->count) == 0 && d != 0)
       
   105             out = 1;
       
   106         else
       
   107             out = 0;
       
   108         break;
       
   109     case 3:
       
   110         out = (d % s->count) < ((s->count + 1) >> 1);
       
   111         break;
       
   112     case 4:
       
   113     case 5:
       
   114         out = (d == s->count);
       
   115         break;
       
   116     }
       
   117     return out;
       
   118 }
       
   119 
       
   120 int pit_get_out(PITState *pit, int channel, int64_t current_time)
       
   121 {
       
   122     PITChannelState *s = &pit->channels[channel];
       
   123     return pit_get_out1(s, current_time);
       
   124 }
       
   125 
       
   126 /* return -1 if no transition will occur.  */
       
   127 static int64_t pit_get_next_transition_time(PITChannelState *s,
       
   128                                             int64_t current_time)
       
   129 {
       
   130     uint64_t d, next_time, base;
       
   131     int period2;
       
   132 
       
   133     d = muldiv64(current_time - s->count_load_time, PIT_FREQ, ticks_per_sec);
       
   134     switch(s->mode) {
       
   135     default:
       
   136     case 0:
       
   137     case 1:
       
   138         if (d < s->count)
       
   139             next_time = s->count;
       
   140         else
       
   141             return -1;
       
   142         break;
       
   143     case 2:
       
   144         base = (d / s->count) * s->count;
       
   145         if ((d - base) == 0 && d != 0)
       
   146             next_time = base + s->count;
       
   147         else
       
   148             next_time = base + s->count + 1;
       
   149         break;
       
   150     case 3:
       
   151         base = (d / s->count) * s->count;
       
   152         period2 = ((s->count + 1) >> 1);
       
   153         if ((d - base) < period2)
       
   154             next_time = base + period2;
       
   155         else
       
   156             next_time = base + s->count;
       
   157         break;
       
   158     case 4:
       
   159     case 5:
       
   160         if (d < s->count)
       
   161             next_time = s->count;
       
   162         else if (d == s->count)
       
   163             next_time = s->count + 1;
       
   164         else
       
   165             return -1;
       
   166         break;
       
   167     }
       
   168     /* convert to timer units */
       
   169     next_time = s->count_load_time + muldiv64(next_time, ticks_per_sec, PIT_FREQ);
       
   170     /* fix potential rounding problems */
       
   171     /* XXX: better solution: use a clock at PIT_FREQ Hz */
       
   172     if (next_time <= current_time)
       
   173         next_time = current_time + 1;
       
   174     return next_time;
       
   175 }
       
   176 
       
   177 /* val must be 0 or 1 */
       
   178 void pit_set_gate(PITState *pit, int channel, int val)
       
   179 {
       
   180     PITChannelState *s = &pit->channels[channel];
       
   181 
       
   182     switch(s->mode) {
       
   183     default:
       
   184     case 0:
       
   185     case 4:
       
   186         /* XXX: just disable/enable counting */
       
   187         break;
       
   188     case 1:
       
   189     case 5:
       
   190         if (s->gate < val) {
       
   191             /* restart counting on rising edge */
       
   192             s->count_load_time = qemu_get_clock(vm_clock);
       
   193             pit_irq_timer_update(s, s->count_load_time);
       
   194         }
       
   195         break;
       
   196     case 2:
       
   197     case 3:
       
   198         if (s->gate < val) {
       
   199             /* restart counting on rising edge */
       
   200             s->count_load_time = qemu_get_clock(vm_clock);
       
   201             pit_irq_timer_update(s, s->count_load_time);
       
   202         }
       
   203         /* XXX: disable/enable counting */
       
   204         break;
       
   205     }
       
   206     s->gate = val;
       
   207 }
       
   208 
       
   209 int pit_get_gate(PITState *pit, int channel)
       
   210 {
       
   211     PITChannelState *s = &pit->channels[channel];
       
   212     return s->gate;
       
   213 }
       
   214 
       
   215 int pit_get_initial_count(PITState *pit, int channel)
       
   216 {
       
   217     PITChannelState *s = &pit->channels[channel];
       
   218     return s->count;
       
   219 }
       
   220 
       
   221 int pit_get_mode(PITState *pit, int channel)
       
   222 {
       
   223     PITChannelState *s = &pit->channels[channel];
       
   224     return s->mode;
       
   225 }
       
   226 
       
   227 static inline void pit_load_count(PITChannelState *s, int val)
       
   228 {
       
   229     if (val == 0)
       
   230         val = 0x10000;
       
   231     s->count_load_time = qemu_get_clock(vm_clock);
       
   232     s->count = val;
       
   233     pit_irq_timer_update(s, s->count_load_time);
       
   234 }
       
   235 
       
   236 /* if already latched, do not latch again */
       
   237 static void pit_latch_count(PITChannelState *s)
       
   238 {
       
   239     if (!s->count_latched) {
       
   240         s->latched_count = pit_get_count(s);
       
   241         s->count_latched = s->rw_mode;
       
   242     }
       
   243 }
       
   244 
       
   245 static void pit_ioport_write(void *opaque, uint32_t addr, uint32_t val)
       
   246 {
       
   247     PITState *pit = opaque;
       
   248     int channel, access;
       
   249     PITChannelState *s;
       
   250 
       
   251     addr &= 3;
       
   252     if (addr == 3) {
       
   253         channel = val >> 6;
       
   254         if (channel == 3) {
       
   255             /* read back command */
       
   256             for(channel = 0; channel < 3; channel++) {
       
   257                 s = &pit->channels[channel];
       
   258                 if (val & (2 << channel)) {
       
   259                     if (!(val & 0x20)) {
       
   260                         pit_latch_count(s);
       
   261                     }
       
   262                     if (!(val & 0x10) && !s->status_latched) {
       
   263                         /* status latch */
       
   264                         /* XXX: add BCD and null count */
       
   265                         s->status =  (pit_get_out1(s, qemu_get_clock(vm_clock)) << 7) |
       
   266                             (s->rw_mode << 4) |
       
   267                             (s->mode << 1) |
       
   268                             s->bcd;
       
   269                         s->status_latched = 1;
       
   270                     }
       
   271                 }
       
   272             }
       
   273         } else {
       
   274             s = &pit->channels[channel];
       
   275             access = (val >> 4) & 3;
       
   276             if (access == 0) {
       
   277                 pit_latch_count(s);
       
   278             } else {
       
   279                 s->rw_mode = access;
       
   280                 s->read_state = access;
       
   281                 s->write_state = access;
       
   282 
       
   283                 s->mode = (val >> 1) & 7;
       
   284                 s->bcd = val & 1;
       
   285                 /* XXX: update irq timer ? */
       
   286             }
       
   287         }
       
   288     } else {
       
   289         s = &pit->channels[addr];
       
   290         switch(s->write_state) {
       
   291         default:
       
   292         case RW_STATE_LSB:
       
   293             pit_load_count(s, val);
       
   294             break;
       
   295         case RW_STATE_MSB:
       
   296             pit_load_count(s, val << 8);
       
   297             break;
       
   298         case RW_STATE_WORD0:
       
   299             s->write_latch = val;
       
   300             s->write_state = RW_STATE_WORD1;
       
   301             break;
       
   302         case RW_STATE_WORD1:
       
   303             pit_load_count(s, s->write_latch | (val << 8));
       
   304             s->write_state = RW_STATE_WORD0;
       
   305             break;
       
   306         }
       
   307     }
       
   308 }
       
   309 
       
   310 static uint32_t pit_ioport_read(void *opaque, uint32_t addr)
       
   311 {
       
   312     PITState *pit = opaque;
       
   313     int ret, count;
       
   314     PITChannelState *s;
       
   315 
       
   316     addr &= 3;
       
   317     s = &pit->channels[addr];
       
   318     if (s->status_latched) {
       
   319         s->status_latched = 0;
       
   320         ret = s->status;
       
   321     } else if (s->count_latched) {
       
   322         switch(s->count_latched) {
       
   323         default:
       
   324         case RW_STATE_LSB:
       
   325             ret = s->latched_count & 0xff;
       
   326             s->count_latched = 0;
       
   327             break;
       
   328         case RW_STATE_MSB:
       
   329             ret = s->latched_count >> 8;
       
   330             s->count_latched = 0;
       
   331             break;
       
   332         case RW_STATE_WORD0:
       
   333             ret = s->latched_count & 0xff;
       
   334             s->count_latched = RW_STATE_MSB;
       
   335             break;
       
   336         }
       
   337     } else {
       
   338         switch(s->read_state) {
       
   339         default:
       
   340         case RW_STATE_LSB:
       
   341             count = pit_get_count(s);
       
   342             ret = count & 0xff;
       
   343             break;
       
   344         case RW_STATE_MSB:
       
   345             count = pit_get_count(s);
       
   346             ret = (count >> 8) & 0xff;
       
   347             break;
       
   348         case RW_STATE_WORD0:
       
   349             count = pit_get_count(s);
       
   350             ret = count & 0xff;
       
   351             s->read_state = RW_STATE_WORD1;
       
   352             break;
       
   353         case RW_STATE_WORD1:
       
   354             count = pit_get_count(s);
       
   355             ret = (count >> 8) & 0xff;
       
   356             s->read_state = RW_STATE_WORD0;
       
   357             break;
       
   358         }
       
   359     }
       
   360     return ret;
       
   361 }
       
   362 
       
   363 static void pit_irq_timer_update(PITChannelState *s, int64_t current_time)
       
   364 {
       
   365     int64_t expire_time;
       
   366     int irq_level;
       
   367 
       
   368     if (!s->irq_timer)
       
   369         return;
       
   370     expire_time = pit_get_next_transition_time(s, current_time);
       
   371     irq_level = pit_get_out1(s, current_time);
       
   372     qemu_set_irq(s->irq, irq_level);
       
   373 #ifdef DEBUG_PIT
       
   374     printf("irq_level=%d next_delay=%f\n",
       
   375            irq_level,
       
   376            (double)(expire_time - current_time) / ticks_per_sec);
       
   377 #endif
       
   378     s->next_transition_time = expire_time;
       
   379     if (expire_time != -1)
       
   380         qemu_mod_timer(s->irq_timer, expire_time);
       
   381     else
       
   382         qemu_del_timer(s->irq_timer);
       
   383 }
       
   384 
       
   385 static void pit_irq_timer(void *opaque)
       
   386 {
       
   387     PITChannelState *s = opaque;
       
   388 
       
   389     pit_irq_timer_update(s, s->next_transition_time);
       
   390 }
       
   391 
       
   392 static void pit_save(QEMUFile *f, void *opaque)
       
   393 {
       
   394     PITState *pit = opaque;
       
   395     PITChannelState *s;
       
   396     int i;
       
   397 
       
   398     for(i = 0; i < 3; i++) {
       
   399         s = &pit->channels[i];
       
   400         qemu_put_be32(f, s->count);
       
   401         qemu_put_be16s(f, &s->latched_count);
       
   402         qemu_put_8s(f, &s->count_latched);
       
   403         qemu_put_8s(f, &s->status_latched);
       
   404         qemu_put_8s(f, &s->status);
       
   405         qemu_put_8s(f, &s->read_state);
       
   406         qemu_put_8s(f, &s->write_state);
       
   407         qemu_put_8s(f, &s->write_latch);
       
   408         qemu_put_8s(f, &s->rw_mode);
       
   409         qemu_put_8s(f, &s->mode);
       
   410         qemu_put_8s(f, &s->bcd);
       
   411         qemu_put_8s(f, &s->gate);
       
   412         qemu_put_be64(f, s->count_load_time);
       
   413         if (s->irq_timer) {
       
   414             qemu_put_be64(f, s->next_transition_time);
       
   415             qemu_put_timer(f, s->irq_timer);
       
   416         }
       
   417     }
       
   418 }
       
   419 
       
   420 static int pit_load(QEMUFile *f, void *opaque, int version_id)
       
   421 {
       
   422     PITState *pit = opaque;
       
   423     PITChannelState *s;
       
   424     int i;
       
   425 
       
   426     if (version_id != 1)
       
   427         return -EINVAL;
       
   428 
       
   429     for(i = 0; i < 3; i++) {
       
   430         s = &pit->channels[i];
       
   431         s->count=qemu_get_be32(f);
       
   432         qemu_get_be16s(f, &s->latched_count);
       
   433         qemu_get_8s(f, &s->count_latched);
       
   434         qemu_get_8s(f, &s->status_latched);
       
   435         qemu_get_8s(f, &s->status);
       
   436         qemu_get_8s(f, &s->read_state);
       
   437         qemu_get_8s(f, &s->write_state);
       
   438         qemu_get_8s(f, &s->write_latch);
       
   439         qemu_get_8s(f, &s->rw_mode);
       
   440         qemu_get_8s(f, &s->mode);
       
   441         qemu_get_8s(f, &s->bcd);
       
   442         qemu_get_8s(f, &s->gate);
       
   443         s->count_load_time=qemu_get_be64(f);
       
   444         if (s->irq_timer) {
       
   445             s->next_transition_time=qemu_get_be64(f);
       
   446             qemu_get_timer(f, s->irq_timer);
       
   447         }
       
   448     }
       
   449     return 0;
       
   450 }
       
   451 
       
   452 static void pit_reset(void *opaque)
       
   453 {
       
   454     PITState *pit = opaque;
       
   455     PITChannelState *s;
       
   456     int i;
       
   457 
       
   458     for(i = 0;i < 3; i++) {
       
   459         s = &pit->channels[i];
       
   460         s->mode = 3;
       
   461         s->gate = (i != 2);
       
   462         pit_load_count(s, 0);
       
   463     }
       
   464 }
       
   465 
       
   466 /* When HPET is operating in legacy mode, i8254 timer0 is disabled */
       
   467 void hpet_pit_disable(void) {
       
   468     PITChannelState *s;
       
   469     s = &pit_state.channels[0];
       
   470     qemu_del_timer(s->irq_timer);
       
   471 }
       
   472 
       
   473 /* When HPET is reset or leaving legacy mode, it must reenable i8254
       
   474  * timer 0
       
   475  */
       
   476 
       
   477 void hpet_pit_enable(void)
       
   478 {
       
   479     PITState *pit = &pit_state;
       
   480     PITChannelState *s;
       
   481     s = &pit->channels[0];
       
   482     s->mode = 3;
       
   483     s->gate = 1;
       
   484     pit_load_count(s, 0);
       
   485 }
       
   486 
       
   487 PITState *pit_init(int base, qemu_irq irq)
       
   488 {
       
   489     PITState *pit = &pit_state;
       
   490     PITChannelState *s;
       
   491 
       
   492     s = &pit->channels[0];
       
   493     /* the timer 0 is connected to an IRQ */
       
   494     s->irq_timer = qemu_new_timer(vm_clock, pit_irq_timer, s);
       
   495     s->irq = irq;
       
   496 
       
   497     register_savevm("i8254", base, 1, pit_save, pit_load, pit);
       
   498 
       
   499     qemu_register_reset(pit_reset, pit);
       
   500     register_ioport_write(base, 4, 1, pit_ioport_write, pit);
       
   501     register_ioport_read(base, 3, 1, pit_ioport_read, pit);
       
   502 
       
   503     pit_reset(pit);
       
   504 
       
   505     return pit;
       
   506 }