/[gxemul]/trunk/src/devices/dev_mc146818.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Diff of /trunk/src/devices/dev_mc146818.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 6 by dpavlin, Mon Oct 8 16:18:11 2007 UTC revision 24 by dpavlin, Mon Oct 8 16:19:56 2007 UTC
# Line 1  Line 1 
1  /*  /*
2   *  Copyright (C) 2003-2005  Anders Gavare.  All rights reserved.   *  Copyright (C) 2003-2006  Anders Gavare.  All rights reserved.
3   *   *
4   *  Redistribution and use in source and binary forms, with or without   *  Redistribution and use in source and binary forms, with or without
5   *  modification, are permitted provided that the following conditions are met:   *  modification, are permitted provided that the following conditions are met:
# Line 25  Line 25 
25   *  SUCH DAMAGE.   *  SUCH DAMAGE.
26   *     *  
27   *   *
28   *  $Id: dev_mc146818.c,v 1.69 2005/05/20 07:42:12 debug Exp $   *  $Id: dev_mc146818.c,v 1.86 2006/06/22 13:22:41 debug Exp $
29   *     *  
30   *  MC146818 real-time clock, used by many different machines types.   *  MC146818 real-time clock, used by many different machines types.
31   *  (DS1687 as used in some SGI machines is similar to MC146818.)   *  (DS1687 as used in some other machines is also similar to the MC146818.)
32   *   *
33   *  This device contains Date/time, the machine's ethernet address (on   *  This device contains Date/time, the machine's ethernet address (on
34   *  DECstation 3100), and can cause periodic (hardware) interrupts.   *  DECstation 3100), and can cause periodic (hardware) interrupts.
# Line 53  Line 53 
53    
54    
55  #define to_bcd(x)       ( ((x)/10) * 16 + ((x)%10) )  #define to_bcd(x)       ( ((x)/10) * 16 + ((x)%10) )
56    #define from_bcd(x)     ( ((x)>>4) * 10 + ((x)&15) )
57    
58  /*  #define MC146818_DEBUG  */  /*  #define MC146818_DEBUG  */
59    
60  #define TICK_STEPS_SHIFT        14  #define TICK_SHIFT      14
61    
62    
63  /*  256 on DECstation, SGI uses reg at 72*4 as the Century  */  /*  256 on DECstation, SGI uses reg at 72*4 as the Century  */
# Line 76  struct mc_data { Line 77  struct mc_data {
77          int     irq_nr;          int     irq_nr;
78    
79          int     previous_second;          int     previous_second;
80            int     n_seconds_elapsed;
81            int     uip_threshold;
82    
83          int     interrupt_every_x_cycles;          int     interrupt_every_x_cycles;
84          int     cycles_left_until_interrupt;          int     cycles_left_until_interrupt;
85    
86            int     ugly_netbsd_prep_hack_done;
87            int     ugly_netbsd_prep_hack_sec;
88  };  };
89    
90    
91  /*  /*
92     *  Ugly hack to fool NetBSD/prep to accept the clock.  (See mcclock_isa_match
93     *  in NetBSD's arch/prep/isa/mcclock_isa.c for details.)
94     */
95    #define NETBSD_HACK_INIT                0
96    #define NETBSD_HACK_FIRST_1             1
97    #define NETBSD_HACK_FIRST_2             2
98    #define NETBSD_HACK_SECOND_1            3
99    #define NETBSD_HACK_SECOND_2            4
100    #define NETBSD_HACK_DONE                5
101    
102    
103    /*
104   *  recalc_interrupt_cycle():   *  recalc_interrupt_cycle():
105   *   *
106   *  If automatic_clock_adjustment is turned on, then emulated_hz is modified   *  If automatic_clock_adjustment is turned on, then emulated_hz is modified
# Line 119  static void recalc_interrupt_cycle(struc Line 137  static void recalc_interrupt_cycle(struc
137  #endif  #endif
138    
139          if (d->interrupt_hz > 0)          if (d->interrupt_hz > 0)
140                  d->interrupt_every_x_cycles =                  d->interrupt_every_x_cycles = emulated_hz / d->interrupt_hz;
                     emulated_hz / d->interrupt_hz;  
141          else          else
142                  d->interrupt_every_x_cycles = 0;                  d->interrupt_every_x_cycles = 0;
143  }  }
# Line 133  void dev_mc146818_tick(struct cpu *cpu, Line 150  void dev_mc146818_tick(struct cpu *cpu,
150  {  {
151          struct mc_data *d = extra;          struct mc_data *d = extra;
152    
         if (d == NULL)  
                 return;  
   
153          recalc_interrupt_cycle(cpu, d);          recalc_interrupt_cycle(cpu, d);
154    
155          if ((d->reg[MC_REGB * 4] & MC_REGB_PIE) &&          if ((d->reg[MC_REGB * 4] & MC_REGB_PIE) &&
156               d->interrupt_every_x_cycles > 0) {               d->interrupt_every_x_cycles > 0) {
157                  d->cycles_left_until_interrupt -=                  d->cycles_left_until_interrupt -= (1 << TICK_SHIFT);
                     (1 << TICK_STEPS_SHIFT);  
158    
159                  if (d->cycles_left_until_interrupt < 0 ||                  if (d->cycles_left_until_interrupt < 0 ||
160                      d->cycles_left_until_interrupt >=                      d->cycles_left_until_interrupt >=
161                      d->interrupt_every_x_cycles) {                      d->interrupt_every_x_cycles) {
162                          /*  debug("[ rtc interrupt (every %i cycles) ]\n",                          /*  fatal("[ rtc interrupt (every %i cycles) ]\n",
163                              d->interrupt_every_x_cycles);  */                              d->interrupt_every_x_cycles);  */
164                          cpu_interrupt(cpu, d->irq_nr);                          cpu_interrupt(cpu, d->irq_nr);
165    
# Line 223  static void mc146818_update_time(struct Line 236  static void mc146818_update_time(struct
236          d->reg[4 * MC_MONTH] = tmp->tm_mon + 1;          d->reg[4 * MC_MONTH] = tmp->tm_mon + 1;
237          d->reg[4 * MC_YEAR]  = tmp->tm_year;          d->reg[4 * MC_YEAR]  = tmp->tm_year;
238    
239            /*
240             *  Special hacks for emulating the behaviour of various machines:
241             */
242          switch (d->access_style) {          switch (d->access_style) {
243            case MC146818_ALGOR:
244                    d->reg[4 * MC_YEAR] += 80;
245                    break;
246          case MC146818_ARC_NEC:          case MC146818_ARC_NEC:
247                  d->reg[4 * MC_YEAR] += (0x18 - 104);                  d->reg[4 * MC_YEAR] += (0x18 - 104);
248                  break;                  break;
249            case MC146818_CATS:
250                    d->reg[4 * MC_YEAR] %= 100;
251                    break;
252          case MC146818_SGI:          case MC146818_SGI:
253                  /*                  /*
254                   *  NetBSD/sgimips assumes data in BCD format.                   *  NetBSD/sgimips assumes data in BCD format.
# Line 248  static void mc146818_update_time(struct Line 270  static void mc146818_update_time(struct
270                            (d->reg[4 * MC_YEAR] - 30 + 40)                            (d->reg[4 * MC_YEAR] - 30 + 40)
271                          : (d->reg[4 * MC_YEAR] - 40)                          : (d->reg[4 * MC_YEAR] - 40)
272                        );                        );
   
273                  /*  Century:  */                  /*  Century:  */
274                  d->reg[72 * 4] = 19 + (tmp->tm_year / 100);                  d->reg[72 * 4] = 19 + (tmp->tm_year / 100);
   
275                  break;                  break;
276          case MC146818_DEC:          case MC146818_DEC:
277                  /*                  /*
# Line 298  int dev_mc146818_access(struct cpu *cpu, Line 318  int dev_mc146818_access(struct cpu *cpu,
318          struct tm *tmp;          struct tm *tmp;
319          time_t timet;          time_t timet;
320          struct mc_data *d = extra;          struct mc_data *d = extra;
321          int i, relative_addr = r;          int relative_addr = r;
322            size_t i;
323    
324          relative_addr /= d->addrdiv;          relative_addr /= d->addrdiv;
325    
326          /*  Different ways of accessing the registers:  */          /*  Different ways of accessing the registers:  */
327          switch (d->access_style) {          switch (d->access_style) {
328            case MC146818_ALGOR:
329            case MC146818_CATS:
330          case MC146818_PC_CMOS:          case MC146818_PC_CMOS:
331                  if (relative_addr == 0x70 || relative_addr == 0x00) {                  if ((relative_addr & 1) == 0x00) {
332                          if (writeflag == MEM_WRITE) {                          if (writeflag == MEM_WRITE) {
333                                  d->last_addr = data[0];                                  d->last_addr = data[0];
334                                  return 1;                                  return 1;
# Line 313  int dev_mc146818_access(struct cpu *cpu, Line 336  int dev_mc146818_access(struct cpu *cpu,
336                                  data[0] = d->last_addr;                                  data[0] = d->last_addr;
337                                  return 1;                                  return 1;
338                          }                          }
339                  } else if (relative_addr == 0x71 || relative_addr == 0x01)                  } else
340                          relative_addr = d->last_addr * 4;                          relative_addr = d->last_addr * 4;
                 else {  
                         fatal("[ mc146818: not accessed as an "  
                             "MC146818_PC_CMOS device! ]\n");  
                 }  
341                  break;                  break;
342          case MC146818_ARC_NEC:          case MC146818_ARC_NEC:
343                  if (relative_addr == 0x01) {                  if (relative_addr == 0x01) {
# Line 351  int dev_mc146818_access(struct cpu *cpu, Line 370  int dev_mc146818_access(struct cpu *cpu,
370                   *  should be ignored. It works _almost_ as DEC, if offsets are                   *  should be ignored. It works _almost_ as DEC, if offsets are
371                   *  divided by 0x40.                   *  divided by 0x40.
372                   */                   */
373                    break;
374            case MC146818_PMPPC:
375                    relative_addr *= 4;
376                    break;
377          default:          default:
378                  ;                  ;
379          }          }
# Line 360  int dev_mc146818_access(struct cpu *cpu, Line 383  int dev_mc146818_access(struct cpu *cpu,
383                  fatal("[ mc146818: write to addr=0x%04x (len %i): ",                  fatal("[ mc146818: write to addr=0x%04x (len %i): ",
384                      (int)relative_addr, (int)len);                      (int)relative_addr, (int)len);
385                  for (i=0; i<len; i++)                  for (i=0; i<len; i++)
386                          fatal("%02x ", data[i]);                          fatal("0x%02x ", data[i]);
387                  fatal("]\n");                  fatal("]\n");
388          }          }
389  #endif  #endif
390    
391          /*          /*
392           *  For some reason, Linux/sgimips relies on the UIP bit to go           *  Sprite seems to wants UF interrupt status, once every second, or
          *  on and off. Without this code, booting Linux takes forever:  
          */  
         d->reg[MC_REGA * 4] &= ~MC_REGA_UIP;  
 #if 1  
         /*  TODO:  solve this more nicely  */  
         if ((random() & 0xff) == 0)  
                 d->reg[MC_REGA * 4] ^= MC_REGA_UIP;  
 #endif  
   
         /*  
          *  Sprite seens to wants UF interrupt status, once every second, or  
393           *  it hangs forever during bootup.  (These do not cause interrupts,           *  it hangs forever during bootup.  (These do not cause interrupts,
394           *  but it is good enough... Sprite polls this, iirc.)           *  but it is good enough... Sprite polls this, iirc.)
395             *
396             *  Linux on at least sgimips and evbmips (Malta) wants the UIP bit
397             *  in REGA to be updated once a second.
398           */           */
399          timet = time(NULL);          if (relative_addr == MC_REGA*4 || relative_addr == MC_REGC*4) {
400          tmp = gmtime(&timet);                  timet = time(NULL);
401          d->reg[MC_REGC * 4] &= ~MC_REGC_UF;                  tmp = gmtime(&timet);
402          if (tmp->tm_sec != d->previous_second) {                  d->reg[MC_REGC * 4] &= ~MC_REGC_UF;
403                  d->reg[MC_REGC * 4] |= MC_REGC_UF;                  if (tmp->tm_sec != d->previous_second) {
404                  d->reg[MC_REGC * 4] |= MC_REGC_IRQF;                          d->n_seconds_elapsed ++;
405                  d->previous_second = tmp->tm_sec;                          d->previous_second = tmp->tm_sec;
406                    }
407                    if (d->n_seconds_elapsed > d->uip_threshold) {
408                            d->n_seconds_elapsed = 0;
409    
410                            d->reg[MC_REGA * 4] |= MC_REGA_UIP;
411    
412                  /*  For some reason, some Linux/DECstation KN04 kernels want                          d->reg[MC_REGC * 4] |= MC_REGC_UF;
413                      the PF (periodic flag) bit set, even though interrupts                          d->reg[MC_REGC * 4] |= MC_REGC_IRQF;
414                      are not enabled?  */  
415                  d->reg[MC_REGC * 4] |= MC_REGC_PF;                          /*  For some reason, some Linux/DECstation KN04
416                                kernels want the PF (periodic flag) bit set,
417                                even though interrupts are not enabled?  */
418                            d->reg[MC_REGC * 4] |= MC_REGC_PF;
419                    } else
420                            d->reg[MC_REGA * 4] &= ~MC_REGA_UIP;
421          }          }
422    
423          /*  RTC data is in either BCD format or binary:  */          /*  RTC data is in either BCD format or binary:  */
424          if (d->use_bcd) {          if (d->use_bcd)
425                  d->reg[MC_REGB * 4] &= ~(1 << 2);                  d->reg[MC_REGB * 4] &= ~(1 << 2);
426          } else {          else
427                  d->reg[MC_REGB * 4] |= (1 << 2);                  d->reg[MC_REGB * 4] |= (1 << 2);
         }  
428    
429          /*  RTC date/time is always Valid:  */          /*  RTC date/time is always Valid:  */
430          d->reg[MC_REGD * 4] |= MC_REGD_VRT;          d->reg[MC_REGD * 4] |= MC_REGD_VRT;
# Line 444  int dev_mc146818_access(struct cpu *cpu, Line 468  int dev_mc146818_access(struct cpu *cpu,
468                          case MC_RATE_8_Hz:      d->interrupt_hz = 8;    break;                          case MC_RATE_8_Hz:      d->interrupt_hz = 8;    break;
469                          case MC_RATE_4_Hz:      d->interrupt_hz = 4;    break;                          case MC_RATE_4_Hz:      d->interrupt_hz = 4;    break;
470                          case MC_RATE_2_Hz:      d->interrupt_hz = 2;    break;                          case MC_RATE_2_Hz:      d->interrupt_hz = 2;    break;
471                          default:                          default:/*  debug("[ mc146818: unimplemented "
                                 /*  debug("[ mc146818: unimplemented "  
472                                      "MC_REGA RS: %i ]\n",                                      "MC_REGA RS: %i ]\n",
473                                      data[0] & MC_REGA_RSMASK);  */                                      data[0] & MC_REGA_RSMASK);  */
474                                  ;                                  ;
# Line 483  int dev_mc146818_access(struct cpu *cpu, Line 506  int dev_mc146818_access(struct cpu *cpu,
506                  case 0x128:                  case 0x128:
507                          d->reg[relative_addr] = data[0];                          d->reg[relative_addr] = data[0];
508                          if (data[0] & 8) {                          if (data[0] & 8) {
509                                    int j;
510    
511                                  /*  Used on SGI to power off the machine.  */                                  /*  Used on SGI to power off the machine.  */
512                                  fatal("[ md146818: power off ]\n");                                  fatal("[ md146818: power off ]\n");
513                                  for (i=0; i<cpu->machine->ncpus; i++)                                  for (j=0; j<cpu->machine->ncpus; j++)
514                                          cpu->machine->cpus[i]->running = 0;                                          cpu->machine->cpus[j]->running = 0;
515                                  cpu->machine->                                  cpu->machine->
516                                      exit_without_entering_debugger = 1;                                      exit_without_entering_debugger = 1;
517                          }                          }
# Line 511  int dev_mc146818_access(struct cpu *cpu, Line 536  int dev_mc146818_access(struct cpu *cpu,
536                  case 0x15:                  case 0x15:
537                          break;                          break;
538                  case 4 * MC_SEC:                  case 4 * MC_SEC:
539                            if (d->ugly_netbsd_prep_hack_done < NETBSD_HACK_DONE) {
540                                    d->ugly_netbsd_prep_hack_done ++;
541                                    switch (d->ugly_netbsd_prep_hack_done) {
542                                    case NETBSD_HACK_FIRST_1:
543                                            d->ugly_netbsd_prep_hack_sec =
544                                                from_bcd(d->reg[relative_addr]);
545                                            break;
546                                    case NETBSD_HACK_FIRST_2:
547                                            d->reg[relative_addr] = to_bcd(
548                                                d->ugly_netbsd_prep_hack_sec);
549                                            break;
550                                    case NETBSD_HACK_SECOND_1:
551                                    case NETBSD_HACK_SECOND_2:
552                                            d->reg[relative_addr] = to_bcd((1 +
553                                                d->ugly_netbsd_prep_hack_sec) % 60);
554                                            break;
555                                    }
556                            }
557                  case 4 * MC_MIN:                  case 4 * MC_MIN:
558                  case 4 * MC_HOUR:                  case 4 * MC_HOUR:
559                  case 4 * MC_DOW:                  case 4 * MC_DOW:
# Line 527  int dev_mc146818_access(struct cpu *cpu, Line 570  int dev_mc146818_access(struct cpu *cpu,
570                          if (d->reg[MC_REGB * 4] & MC_REGB_SET)                          if (d->reg[MC_REGB * 4] & MC_REGB_SET)
571                                  break;                                  break;
572    
573                          mc146818_update_time(d);                          if (d->ugly_netbsd_prep_hack_done >= NETBSD_HACK_DONE)
574                                    mc146818_update_time(d);
575                            break;
576                    case 4 * MC_REGA:
577                          break;                          break;
578                  case 4 * MC_REGC:       /*  Interrupt ack.  */                  case 4 * MC_REGC:       /*  Interrupt ack.  */
579                          /*  NOTE: Acking is done below, _after_ the                          /*  NOTE: Acking is done below, _after_ the
580                              register has been read.  */                              register has been read.  */
581                          break;                          break;
582                  default:                  default:debug("[ mc146818: read from relative_addr = "
                         debug("[ mc146818: read from relative_addr = "  
583                              "%04x ]\n", (int)relative_addr);                              "%04x ]\n", (int)relative_addr);
                         ;  
584                  }                  }
585    
586                  data[0] = d->reg[relative_addr];                  data[0] = d->reg[relative_addr];
# Line 554  int dev_mc146818_access(struct cpu *cpu, Line 598  int dev_mc146818_access(struct cpu *cpu,
598                  fatal("[ mc146818: read from addr=0x%04x (len %i): ",                  fatal("[ mc146818: read from addr=0x%04x (len %i): ",
599                      (int)relative_addr, (int)len);                      (int)relative_addr, (int)len);
600                  for (i=0; i<len; i++)                  for (i=0; i<len; i++)
601                          fatal("%02x ", data[i]);                          fatal("0x%02x ", data[i]);
602                  fatal("]\n");                  fatal("]\n");
603          }          }
604  #endif  #endif
# Line 587  void dev_mc146818_init(struct machine *m Line 631  void dev_mc146818_init(struct machine *m
631          d->access_style  = access_style;          d->access_style  = access_style;
632          d->addrdiv       = addrdiv;          d->addrdiv       = addrdiv;
633    
         /*  Only SGIs and PCs use BCD format (?)  */  
634          d->use_bcd = 0;          d->use_bcd = 0;
635          if (access_style == MC146818_SGI || access_style == MC146818_PC_CMOS)          switch (access_style) {
636            case MC146818_SGI:
637            case MC146818_PC_CMOS:
638            case MC146818_PMPPC:
639                  d->use_bcd = 1;                  d->use_bcd = 1;
640            }
641    
642            if (machine->machine_type != MACHINE_PREP) {
643                    /*  NetBSD/prep has a really ugly clock detection code;
644                        no other machines/OSes don't need this.  */
645                    d->ugly_netbsd_prep_hack_done = NETBSD_HACK_DONE;
646            }
647    
648          if (access_style == MC146818_DEC) {          if (access_style == MC146818_DEC) {
649          /*  Station Ethernet Address, on DECstation 3100:  */          /*  Station Ethernet Address, on DECstation 3100:  */
# Line 630  void dev_mc146818_init(struct machine *m Line 683  void dev_mc146818_init(struct machine *m
683                  d->reg[0xf8] = 1;                  d->reg[0xf8] = 1;
684          }          }
685    
686            /*
687             *  uip_threshold should ideally be 1, but when Linux polls the UIP bit
688             *  it looses speed. This hack gives Linux the impression that the cpu
689             *  is uip_threshold times faster than the slow clock it would
690             *  otherwise detect.
691             *
692             *  TODO:  Find out if this messes up Sprite emulation; if so, then
693             *         this hack has to be removed.
694             */
695            d->uip_threshold = 8;
696    
697          if (access_style == MC146818_ARC_JAZZ)          if (access_style == MC146818_ARC_JAZZ)
698                  memory_device_register(mem, "mc146818_jazz", 0x90000070ULL,                  memory_device_register(mem, "mc146818_jazz", 0x90000070ULL,
699                      1, dev_mc146818_jazz_access, d, MEM_DEFAULT, NULL);                      1, dev_mc146818_jazz_access, d, DM_DEFAULT, NULL);
700    
701          dev_len = DEV_MC146818_LENGTH;          dev_len = DEV_MC146818_LENGTH;
702          switch (access_style) {          switch (access_style) {
703            case MC146818_CATS:
704          case MC146818_PC_CMOS:          case MC146818_PC_CMOS:
705                  dev_len = 2;                  dev_len = 2;
706                  break;                  break;
# Line 645  void dev_mc146818_init(struct machine *m Line 710  void dev_mc146818_init(struct machine *m
710    
711          memory_device_register(mem, "mc146818", baseaddr,          memory_device_register(mem, "mc146818", baseaddr,
712              dev_len * addrdiv, dev_mc146818_access,              dev_len * addrdiv, dev_mc146818_access,
713              d, MEM_DEFAULT, NULL);              d, DM_DEFAULT, NULL);
714    
715          mc146818_update_time(d);          mc146818_update_time(d);
716    
717          machine_add_tickfunction(machine, dev_mc146818_tick,          machine_add_tickfunction(machine, dev_mc146818_tick, d,
718              d, TICK_STEPS_SHIFT);              TICK_SHIFT, 0.0);
719  }  }
720    

Legend:
Removed from v.6  
changed lines
  Added in v.24

  ViewVC Help
Powered by ViewVC 1.1.26