/[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 4 by dpavlin, Mon Oct 8 16:18:00 2007 UTC revision 20 by dpavlin, Mon Oct 8 16:19:23 2007 UTC
# Line 25  Line 25 
25   *  SUCH DAMAGE.   *  SUCH DAMAGE.
26   *     *  
27   *   *
28   *  $Id: dev_mc146818.c,v 1.68 2005/02/07 05:51:54 debug Exp $   *  $Id: dev_mc146818.c,v 1.80 2005/11/20 11:28:45 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 133  void dev_mc146818_tick(struct cpu *cpu, Line 151  void dev_mc146818_tick(struct cpu *cpu,
151  {  {
152          struct mc_data *d = extra;          struct mc_data *d = extra;
153    
         if (d == NULL)  
                 return;  
   
154          recalc_interrupt_cycle(cpu, d);          recalc_interrupt_cycle(cpu, d);
155    
156          if ((d->reg[MC_REGB * 4] & MC_REGB_PIE) &&          if ((d->reg[MC_REGB * 4] & MC_REGB_PIE) &&
157               d->interrupt_every_x_cycles > 0) {               d->interrupt_every_x_cycles > 0) {
158                  d->cycles_left_until_interrupt -=                  d->cycles_left_until_interrupt -= (1 << TICK_SHIFT);
                     (1 << TICK_STEPS_SHIFT);  
159    
160                  if (d->cycles_left_until_interrupt < 0 ||                  if (d->cycles_left_until_interrupt < 0 ||
161                      d->cycles_left_until_interrupt >=                      d->cycles_left_until_interrupt >=
# Line 223  static void mc146818_update_time(struct Line 237  static void mc146818_update_time(struct
237          d->reg[4 * MC_MONTH] = tmp->tm_mon + 1;          d->reg[4 * MC_MONTH] = tmp->tm_mon + 1;
238          d->reg[4 * MC_YEAR]  = tmp->tm_year;          d->reg[4 * MC_YEAR]  = tmp->tm_year;
239    
240            /*
241             *  Special hacks for emulating the behaviour of various machines:
242             */
243          switch (d->access_style) {          switch (d->access_style) {
244            case MC146818_ALGOR:
245                    d->reg[4 * MC_YEAR] += 80;
246                    break;
247          case MC146818_ARC_NEC:          case MC146818_ARC_NEC:
248                  d->reg[4 * MC_YEAR] += (0x18 - 104);                  d->reg[4 * MC_YEAR] += (0x18 - 104);
249                  break;                  break;
250            case MC146818_CATS:
251                    d->reg[4 * MC_YEAR] %= 100;
252                    break;
253          case MC146818_SGI:          case MC146818_SGI:
254                  /*                  /*
255                   *  NetBSD/sgimips assumes data in BCD format.                   *  NetBSD/sgimips assumes data in BCD format.
# Line 248  static void mc146818_update_time(struct Line 271  static void mc146818_update_time(struct
271                            (d->reg[4 * MC_YEAR] - 30 + 40)                            (d->reg[4 * MC_YEAR] - 30 + 40)
272                          : (d->reg[4 * MC_YEAR] - 40)                          : (d->reg[4 * MC_YEAR] - 40)
273                        );                        );
   
274                  /*  Century:  */                  /*  Century:  */
275                  d->reg[72 * 4] = 19 + (tmp->tm_year / 100);                  d->reg[72 * 4] = 19 + (tmp->tm_year / 100);
   
276                  break;                  break;
277          case MC146818_DEC:          case MC146818_DEC:
278                  /*                  /*
# Line 304  int dev_mc146818_access(struct cpu *cpu, Line 325  int dev_mc146818_access(struct cpu *cpu,
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 511  int dev_mc146818_access(struct cpu *cpu, Line 534  int dev_mc146818_access(struct cpu *cpu,
534                  case 0x15:                  case 0x15:
535                          break;                          break;
536                  case 4 * MC_SEC:                  case 4 * MC_SEC:
537                            if (d->ugly_netbsd_prep_hack_done < NETBSD_HACK_DONE) {
538                                    d->ugly_netbsd_prep_hack_done ++;
539                                    switch (d->ugly_netbsd_prep_hack_done) {
540                                    case NETBSD_HACK_FIRST_1:
541                                            d->ugly_netbsd_prep_hack_sec =
542                                                from_bcd(d->reg[relative_addr]);
543                                            break;
544                                    case NETBSD_HACK_FIRST_2:
545                                            d->reg[relative_addr] = to_bcd(
546                                                d->ugly_netbsd_prep_hack_sec);
547                                            break;
548                                    case NETBSD_HACK_SECOND_1:
549                                    case NETBSD_HACK_SECOND_2:
550                                            d->reg[relative_addr] = to_bcd((1 +
551                                                d->ugly_netbsd_prep_hack_sec) % 60);
552                                            break;
553                                    }
554                            }
555                  case 4 * MC_MIN:                  case 4 * MC_MIN:
556                  case 4 * MC_HOUR:                  case 4 * MC_HOUR:
557                  case 4 * MC_DOW:                  case 4 * MC_DOW:
# Line 527  int dev_mc146818_access(struct cpu *cpu, Line 568  int dev_mc146818_access(struct cpu *cpu,
568                          if (d->reg[MC_REGB * 4] & MC_REGB_SET)                          if (d->reg[MC_REGB * 4] & MC_REGB_SET)
569                                  break;                                  break;
570    
571                          mc146818_update_time(d);                          if (d->ugly_netbsd_prep_hack_done >= NETBSD_HACK_DONE)
572                                    mc146818_update_time(d);
573                            break;
574                    case 4 * MC_REGA:
575                          break;                          break;
576                  case 4 * MC_REGC:       /*  Interrupt ack.  */                  case 4 * MC_REGC:       /*  Interrupt ack.  */
577                          /*  NOTE: Acking is done below, _after_ the                          /*  NOTE: Acking is done below, _after_ the
578                              register has been read.  */                              register has been read.  */
579                          break;                          break;
580                  default:                  default:debug("[ mc146818: read from relative_addr = "
                         debug("[ mc146818: read from relative_addr = "  
581                              "%04x ]\n", (int)relative_addr);                              "%04x ]\n", (int)relative_addr);
                         ;  
582                  }                  }
583    
584                  data[0] = d->reg[relative_addr];                  data[0] = d->reg[relative_addr];
# Line 554  int dev_mc146818_access(struct cpu *cpu, Line 596  int dev_mc146818_access(struct cpu *cpu,
596                  fatal("[ mc146818: read from addr=0x%04x (len %i): ",                  fatal("[ mc146818: read from addr=0x%04x (len %i): ",
597                      (int)relative_addr, (int)len);                      (int)relative_addr, (int)len);
598                  for (i=0; i<len; i++)                  for (i=0; i<len; i++)
599                          fatal("%02x ", data[i]);                          fatal("0x%02x ", data[i]);
600                  fatal("]\n");                  fatal("]\n");
601          }          }
602  #endif  #endif
# Line 587  void dev_mc146818_init(struct machine *m Line 629  void dev_mc146818_init(struct machine *m
629          d->access_style  = access_style;          d->access_style  = access_style;
630          d->addrdiv       = addrdiv;          d->addrdiv       = addrdiv;
631    
632            d->use_bcd = 0;
633            switch (access_style) {
634            case MC146818_SGI:
635            case MC146818_PC_CMOS:
636            case MC146818_PMPPC:
637                    d->use_bcd = 1;
638            }
639    
640            if (machine->machine_type != MACHINE_PREP) {
641                    /*  NetBSD/prep has a really ugly clock detection code;
642                        all other machines/OSes don't need this.  */
643                    d->ugly_netbsd_prep_hack_done = NETBSD_HACK_DONE;
644            }
645    
646            if (access_style == MC146818_DEC) {
647          /*  Station Ethernet Address, on DECstation 3100:  */          /*  Station Ethernet Address, on DECstation 3100:  */
648          for (i=0; i<6; i++)          for (i=0; i<6; i++)
649                  ether_address[i] = 0x10 * (i+1);                  ether_address[i] = 0x10 * (i+1);
650    
651          d->reg[0x01] = ether_address[0];                  d->reg[0x01] = ether_address[0];
652          d->reg[0x05] = ether_address[1];                  d->reg[0x05] = ether_address[1];
653          d->reg[0x09] = ether_address[2];                  d->reg[0x09] = ether_address[2];
654          d->reg[0x0d] = ether_address[3];                  d->reg[0x0d] = ether_address[3];
655          d->reg[0x11] = ether_address[4];                  d->reg[0x11] = ether_address[4];
656          d->reg[0x15] = ether_address[5];                  d->reg[0x15] = ether_address[5];
657          /*  TODO:  19, 1d, 21, 25 = checksum bytes 1,2,2,1 resp. */                  /*  TODO:  19, 1d, 21, 25 = checksum bytes 1,2,2,1 resp. */
658          d->reg[0x29] = ether_address[5];                  d->reg[0x29] = ether_address[5];
659          d->reg[0x2d] = ether_address[4];                  d->reg[0x2d] = ether_address[4];
660          d->reg[0x31] = ether_address[3];                  d->reg[0x31] = ether_address[3];
661          d->reg[0x35] = ether_address[2];                  d->reg[0x35] = ether_address[2];
662          d->reg[0x39] = ether_address[1];                  d->reg[0x39] = ether_address[1];
663          d->reg[0x3d] = ether_address[1];                  d->reg[0x3d] = ether_address[1];
664          d->reg[0x41] = ether_address[0];                  d->reg[0x41] = ether_address[0];
665          d->reg[0x45] = ether_address[1];                  d->reg[0x45] = ether_address[1];
666          d->reg[0x49] = ether_address[2];                  d->reg[0x49] = ether_address[2];
667          d->reg[0x4d] = ether_address[3];                  d->reg[0x4d] = ether_address[3];
668          d->reg[0x51] = ether_address[4];                  d->reg[0x51] = ether_address[4];
669          d->reg[0x55] = ether_address[5];                  d->reg[0x55] = ether_address[5];
670          /*  TODO:  59, 5d = checksum bytes 1,2 resp. */                  /*  TODO:  59, 5d = checksum bytes 1,2 resp. */
671          d->reg[0x61] = 0xff;                  d->reg[0x61] = 0xff;
672          d->reg[0x65] = 0x00;                  d->reg[0x65] = 0x00;
673          d->reg[0x69] = 0x55;                  d->reg[0x69] = 0x55;
674          d->reg[0x6d] = 0xaa;                  d->reg[0x6d] = 0xaa;
675          d->reg[0x71] = 0xff;                  d->reg[0x71] = 0xff;
676          d->reg[0x75] = 0x00;                  d->reg[0x75] = 0x00;
677          d->reg[0x79] = 0x55;                  d->reg[0x79] = 0x55;
678          d->reg[0x7d] = 0xaa;                  d->reg[0x7d] = 0xaa;
   
         /*  Only SGI uses BCD format (?)  */  
         d->use_bcd = 0;  
         if (access_style == MC146818_SGI)  
                 d->use_bcd = 1;  
679    
         if (access_style == MC146818_DEC) {  
680                  /*  Battery valid, for DECstations  */                  /*  Battery valid, for DECstations  */
681                  d->reg[0xf8] = 1;                  d->reg[0xf8] = 1;
682          }          }
683    
684            /*
685             *  uip_threshold should ideally be 1, but when Linux polls the UIP bit
686             *  it looses speed. This hack gives Linux the impression that the cpu
687             *  is uip_threshold times faster than the slow clock it would
688             *  otherwise detect.
689             *
690             *  TODO:  Find out if this messes up Sprite emulation; if so, then
691             *         this hack has to be removed.
692             */
693            d->uip_threshold = 5;
694    
695          if (access_style == MC146818_ARC_JAZZ)          if (access_style == MC146818_ARC_JAZZ)
696                  memory_device_register(mem, "mc146818_jazz", 0x90000070ULL,                  memory_device_register(mem, "mc146818_jazz", 0x90000070ULL,
697                      1, dev_mc146818_jazz_access, d, MEM_DEFAULT, NULL);                      1, dev_mc146818_jazz_access, d, DM_DEFAULT, NULL);
698    
699          dev_len = DEV_MC146818_LENGTH;          dev_len = DEV_MC146818_LENGTH;
700          switch (access_style) {          switch (access_style) {
701            case MC146818_CATS:
702          case MC146818_PC_CMOS:          case MC146818_PC_CMOS:
703                  dev_len = 2;                  dev_len = 2;
704                  break;                  break;
# Line 645  void dev_mc146818_init(struct machine *m Line 708  void dev_mc146818_init(struct machine *m
708    
709          memory_device_register(mem, "mc146818", baseaddr,          memory_device_register(mem, "mc146818", baseaddr,
710              dev_len * addrdiv, dev_mc146818_access,              dev_len * addrdiv, dev_mc146818_access,
711              d, MEM_DEFAULT, NULL);              d, DM_DEFAULT, NULL);
712    
713          mc146818_update_time(d);          mc146818_update_time(d);
714    
715          machine_add_tickfunction(machine, dev_mc146818_tick,          machine_add_tickfunction(machine, dev_mc146818_tick, d, TICK_SHIFT);
             d, TICK_STEPS_SHIFT);  
716  }  }
717    

Legend:
Removed from v.4  
changed lines
  Added in v.20

  ViewVC Help
Powered by ViewVC 1.1.26