/[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 12 by dpavlin, Mon Oct 8 16:18:38 2007 UTC revision 22 by dpavlin, Mon Oct 8 16:19:37 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.72 2005/08/02 07:56:37 debug Exp $   *  $Id: dev_mc146818.c,v 1.84 2006/01/17 05:55:53 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    
# 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) &&
# Line 222  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 247  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 297  int dev_mc146818_access(struct cpu *cpu, Line 319  int dev_mc146818_access(struct cpu *cpu,
319          struct tm *tmp;          struct tm *tmp;
320          time_t timet;          time_t timet;
321          struct mc_data *d = extra;          struct mc_data *d = extra;
322          int i, relative_addr = r;          int relative_addr = r;
323            size_t i;
324    
325          relative_addr /= d->addrdiv;          relative_addr /= d->addrdiv;
326    
327          /*  Different ways of accessing the registers:  */          /*  Different ways of accessing the registers:  */
328          switch (d->access_style) {          switch (d->access_style) {
329            case MC146818_ALGOR:
330            case MC146818_CATS:
331          case MC146818_PC_CMOS:          case MC146818_PC_CMOS:
332                  if (relative_addr == 0x70 || relative_addr == 0x00) {                  if ((relative_addr & 1) == 0x00) {
333                          if (writeflag == MEM_WRITE) {                          if (writeflag == MEM_WRITE) {
334                                  d->last_addr = data[0];                                  d->last_addr = data[0];
335                                  return 1;                                  return 1;
# Line 312  int dev_mc146818_access(struct cpu *cpu, Line 337  int dev_mc146818_access(struct cpu *cpu,
337                                  data[0] = d->last_addr;                                  data[0] = d->last_addr;
338                                  return 1;                                  return 1;
339                          }                          }
340                  } else if (relative_addr == 0x71 || relative_addr == 0x01)                  } else
341                          relative_addr = d->last_addr * 4;                          relative_addr = d->last_addr * 4;
                 else {  
                         fatal("[ mc146818: not accessed as an "  
                             "MC146818_PC_CMOS device! ]\n");  
                 }  
342                  break;                  break;
343          case MC146818_ARC_NEC:          case MC146818_ARC_NEC:
344                  if (relative_addr == 0x01) {                  if (relative_addr == 0x01) {
# Line 350  int dev_mc146818_access(struct cpu *cpu, Line 371  int dev_mc146818_access(struct cpu *cpu,
371                   *  should be ignored. It works _almost_ as DEC, if offsets are                   *  should be ignored. It works _almost_ as DEC, if offsets are
372                   *  divided by 0x40.                   *  divided by 0x40.
373                   */                   */
374                    break;
375            case MC146818_PMPPC:
376                    relative_addr *= 4;
377                    break;
378          default:          default:
379                  ;                  ;
380          }          }
# Line 359  int dev_mc146818_access(struct cpu *cpu, Line 384  int dev_mc146818_access(struct cpu *cpu,
384                  fatal("[ mc146818: write to addr=0x%04x (len %i): ",                  fatal("[ mc146818: write to addr=0x%04x (len %i): ",
385                      (int)relative_addr, (int)len);                      (int)relative_addr, (int)len);
386                  for (i=0; i<len; i++)                  for (i=0; i<len; i++)
387                          fatal("%02x ", data[i]);                          fatal("0x%02x ", data[i]);
388                  fatal("]\n");                  fatal("]\n");
389          }          }
390  #endif  #endif
# Line 372  int dev_mc146818_access(struct cpu *cpu, Line 397  int dev_mc146818_access(struct cpu *cpu,
397           *  Linux on at least sgimips and evbmips (Malta) wants the UIP bit           *  Linux on at least sgimips and evbmips (Malta) wants the UIP bit
398           *  in REGA to be updated once a second.           *  in REGA to be updated once a second.
399           */           */
400          timet = time(NULL);          if (relative_addr == MC_REGA*4 || relative_addr == MC_REGC*4) {
401          tmp = gmtime(&timet);                  timet = time(NULL);
402          d->reg[MC_REGC * 4] &= ~MC_REGC_UF;                  tmp = gmtime(&timet);
403                    d->reg[MC_REGC * 4] &= ~MC_REGC_UF;
404                    if (tmp->tm_sec != d->previous_second) {
405                            d->n_seconds_elapsed ++;
406                            d->previous_second = tmp->tm_sec;
407                    }
408                    if (d->n_seconds_elapsed > d->uip_threshold) {
409                            d->n_seconds_elapsed = 0;
410    
411          if (tmp->tm_sec != d->previous_second) {                          d->reg[MC_REGA * 4] |= MC_REGA_UIP;
                 d->reg[MC_REGA * 4] &= ~MC_REGA_UIP;  
412    
413                  d->reg[MC_REGC * 4] |= MC_REGC_UF;                          d->reg[MC_REGC * 4] |= MC_REGC_UF;
414                  d->reg[MC_REGC * 4] |= MC_REGC_IRQF;                          d->reg[MC_REGC * 4] |= MC_REGC_IRQF;
                 d->previous_second = tmp->tm_sec;  
415    
416                  /*  For some reason, some Linux/DECstation KN04 kernels want                          /*  For some reason, some Linux/DECstation KN04
417                      the PF (periodic flag) bit set, even though interrupts                              kernels want the PF (periodic flag) bit set,
418                      are not enabled?  */                              even though interrupts are not enabled?  */
419                  d->reg[MC_REGC * 4] |= MC_REGC_PF;                          d->reg[MC_REGC * 4] |= MC_REGC_PF;
420          } else                  } else
421                  d->reg[MC_REGA * 4] |= MC_REGA_UIP;                          d->reg[MC_REGA * 4] &= ~MC_REGA_UIP;
422            }
423    
424          /*  RTC data is in either BCD format or binary:  */          /*  RTC data is in either BCD format or binary:  */
425          if (d->use_bcd)          if (d->use_bcd)
# Line 438  int dev_mc146818_access(struct cpu *cpu, Line 469  int dev_mc146818_access(struct cpu *cpu,
469                          case MC_RATE_8_Hz:      d->interrupt_hz = 8;    break;                          case MC_RATE_8_Hz:      d->interrupt_hz = 8;    break;
470                          case MC_RATE_4_Hz:      d->interrupt_hz = 4;    break;                          case MC_RATE_4_Hz:      d->interrupt_hz = 4;    break;
471                          case MC_RATE_2_Hz:      d->interrupt_hz = 2;    break;                          case MC_RATE_2_Hz:      d->interrupt_hz = 2;    break;
472                          default:                          default:/*  debug("[ mc146818: unimplemented "
                                 /*  debug("[ mc146818: unimplemented "  
473                                      "MC_REGA RS: %i ]\n",                                      "MC_REGA RS: %i ]\n",
474                                      data[0] & MC_REGA_RSMASK);  */                                      data[0] & MC_REGA_RSMASK);  */
475                                  ;                                  ;
# Line 477  int dev_mc146818_access(struct cpu *cpu, Line 507  int dev_mc146818_access(struct cpu *cpu,
507                  case 0x128:                  case 0x128:
508                          d->reg[relative_addr] = data[0];                          d->reg[relative_addr] = data[0];
509                          if (data[0] & 8) {                          if (data[0] & 8) {
510                                    int j;
511    
512                                  /*  Used on SGI to power off the machine.  */                                  /*  Used on SGI to power off the machine.  */
513                                  fatal("[ md146818: power off ]\n");                                  fatal("[ md146818: power off ]\n");
514                                  for (i=0; i<cpu->machine->ncpus; i++)                                  for (j=0; j<cpu->machine->ncpus; j++)
515                                          cpu->machine->cpus[i]->running = 0;                                          cpu->machine->cpus[j]->running = 0;
516                                  cpu->machine->                                  cpu->machine->
517                                      exit_without_entering_debugger = 1;                                      exit_without_entering_debugger = 1;
518                          }                          }
# Line 505  int dev_mc146818_access(struct cpu *cpu, Line 537  int dev_mc146818_access(struct cpu *cpu,
537                  case 0x15:                  case 0x15:
538                          break;                          break;
539                  case 4 * MC_SEC:                  case 4 * MC_SEC:
540                            if (d->ugly_netbsd_prep_hack_done < NETBSD_HACK_DONE) {
541                                    d->ugly_netbsd_prep_hack_done ++;
542                                    switch (d->ugly_netbsd_prep_hack_done) {
543                                    case NETBSD_HACK_FIRST_1:
544                                            d->ugly_netbsd_prep_hack_sec =
545                                                from_bcd(d->reg[relative_addr]);
546                                            break;
547                                    case NETBSD_HACK_FIRST_2:
548                                            d->reg[relative_addr] = to_bcd(
549                                                d->ugly_netbsd_prep_hack_sec);
550                                            break;
551                                    case NETBSD_HACK_SECOND_1:
552                                    case NETBSD_HACK_SECOND_2:
553                                            d->reg[relative_addr] = to_bcd((1 +
554                                                d->ugly_netbsd_prep_hack_sec) % 60);
555                                            break;
556                                    }
557                            }
558                  case 4 * MC_MIN:                  case 4 * MC_MIN:
559                  case 4 * MC_HOUR:                  case 4 * MC_HOUR:
560                  case 4 * MC_DOW:                  case 4 * MC_DOW:
# Line 521  int dev_mc146818_access(struct cpu *cpu, Line 571  int dev_mc146818_access(struct cpu *cpu,
571                          if (d->reg[MC_REGB * 4] & MC_REGB_SET)                          if (d->reg[MC_REGB * 4] & MC_REGB_SET)
572                                  break;                                  break;
573    
574                          mc146818_update_time(d);                          if (d->ugly_netbsd_prep_hack_done >= NETBSD_HACK_DONE)
575                                    mc146818_update_time(d);
576                            break;
577                    case 4 * MC_REGA:
578                          break;                          break;
579                  case 4 * MC_REGC:       /*  Interrupt ack.  */                  case 4 * MC_REGC:       /*  Interrupt ack.  */
580                          /*  NOTE: Acking is done below, _after_ the                          /*  NOTE: Acking is done below, _after_ the
581                              register has been read.  */                              register has been read.  */
582                          break;                          break;
583                  default:                  default:debug("[ mc146818: read from relative_addr = "
                         debug("[ mc146818: read from relative_addr = "  
584                              "%04x ]\n", (int)relative_addr);                              "%04x ]\n", (int)relative_addr);
                         ;  
585                  }                  }
586    
587                  data[0] = d->reg[relative_addr];                  data[0] = d->reg[relative_addr];
# Line 548  int dev_mc146818_access(struct cpu *cpu, Line 599  int dev_mc146818_access(struct cpu *cpu,
599                  fatal("[ mc146818: read from addr=0x%04x (len %i): ",                  fatal("[ mc146818: read from addr=0x%04x (len %i): ",
600                      (int)relative_addr, (int)len);                      (int)relative_addr, (int)len);
601                  for (i=0; i<len; i++)                  for (i=0; i<len; i++)
602                          fatal("%02x ", data[i]);                          fatal("0x%02x ", data[i]);
603                  fatal("]\n");                  fatal("]\n");
604          }          }
605  #endif  #endif
# Line 581  void dev_mc146818_init(struct machine *m Line 632  void dev_mc146818_init(struct machine *m
632          d->access_style  = access_style;          d->access_style  = access_style;
633          d->addrdiv       = addrdiv;          d->addrdiv       = addrdiv;
634    
         /*  Only SGIs and PCs use BCD format (?)  */  
635          d->use_bcd = 0;          d->use_bcd = 0;
636          if (access_style == MC146818_SGI || access_style == MC146818_PC_CMOS)          switch (access_style) {
637            case MC146818_SGI:
638            case MC146818_PC_CMOS:
639            case MC146818_PMPPC:
640                  d->use_bcd = 1;                  d->use_bcd = 1;
641            }
642    
643            if (machine->machine_type != MACHINE_PREP) {
644                    /*  NetBSD/prep has a really ugly clock detection code;
645                        no other machines/OSes don't need this.  */
646                    d->ugly_netbsd_prep_hack_done = NETBSD_HACK_DONE;
647            }
648    
649          if (access_style == MC146818_DEC) {          if (access_style == MC146818_DEC) {
650          /*  Station Ethernet Address, on DECstation 3100:  */          /*  Station Ethernet Address, on DECstation 3100:  */
# Line 624  void dev_mc146818_init(struct machine *m Line 684  void dev_mc146818_init(struct machine *m
684                  d->reg[0xf8] = 1;                  d->reg[0xf8] = 1;
685          }          }
686    
687            /*
688             *  uip_threshold should ideally be 1, but when Linux polls the UIP bit
689             *  it looses speed. This hack gives Linux the impression that the cpu
690             *  is uip_threshold times faster than the slow clock it would
691             *  otherwise detect.
692             *
693             *  TODO:  Find out if this messes up Sprite emulation; if so, then
694             *         this hack has to be removed.
695             */
696            d->uip_threshold = 8;
697    
698          if (access_style == MC146818_ARC_JAZZ)          if (access_style == MC146818_ARC_JAZZ)
699                  memory_device_register(mem, "mc146818_jazz", 0x90000070ULL,                  memory_device_register(mem, "mc146818_jazz", 0x90000070ULL,
700                      1, dev_mc146818_jazz_access, d, MEM_DEFAULT, NULL);                      1, dev_mc146818_jazz_access, d, DM_DEFAULT, NULL);
701    
702          dev_len = DEV_MC146818_LENGTH;          dev_len = DEV_MC146818_LENGTH;
703          switch (access_style) {          switch (access_style) {
704            case MC146818_CATS:
705          case MC146818_PC_CMOS:          case MC146818_PC_CMOS:
706                  dev_len = 2;                  dev_len = 2;
707                  break;                  break;
# Line 639  void dev_mc146818_init(struct machine *m Line 711  void dev_mc146818_init(struct machine *m
711    
712          memory_device_register(mem, "mc146818", baseaddr,          memory_device_register(mem, "mc146818", baseaddr,
713              dev_len * addrdiv, dev_mc146818_access,              dev_len * addrdiv, dev_mc146818_access,
714              d, MEM_DEFAULT, NULL);              d, DM_DEFAULT, NULL);
715    
716          mc146818_update_time(d);          mc146818_update_time(d);
717    

Legend:
Removed from v.12  
changed lines
  Added in v.22

  ViewVC Help
Powered by ViewVC 1.1.26