merge from gcc
[external/binutils.git] / sim / mn10300 / dv-mn103tim.c
index 6347db6..9933ae7 100644 (file)
@@ -1,26 +1,27 @@
 /*  This file is part of the program GDB, the GNU debugger.
     
-    Copyright (C) 1998 Free Software Foundation, Inc.
+    Copyright (C) 1998, 2003, 2007, 2008, 2009, 2010
+    Free Software Foundation, Inc.
     Contributed by Cygnus Solutions.
     
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
+    the Free Software Foundation; either version 3 of the License, or
     (at your option) any later version.
-    
+
     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.
-    
+
     You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
     
     */
 
 #include "sim-main.h"
 #include "hw-main.h"
+#include "sim-assert.h"
 
 /* DEVICE
 
@@ -83,29 +84,35 @@ enum timer_register_types {
   TM6MDB,
   TM6CA,
   TM6CB,
+  LAST_TIMER_REG = TM6BC,
 };
 
 
 /* Don't include timer 6 because it's handled specially. */
 #define NR_8BIT_TIMERS 4
 #define NR_16BIT_TIMERS 2
-#define NR_TIMERS 6
+#define NR_REG_TIMERS 6 /* Exclude timer 6 - it's handled specially. */
+#define NR_TIMERS 7
 
-typedef struct _mn10300_timer {
-  unsigned32 div_ratio, start, base;
+typedef struct _mn10300_timer_regs {
+  unsigned32 base;
   unsigned8  mode;
+} mn10300_timer_regs;
+
+typedef struct _mn10300_timer {
+  unsigned32 div_ratio, start;
   struct hw_event *event;
 } mn10300_timer;
 
 
 struct mn103tim {
   struct mn103tim_block block[NR_TIMER_BLOCKS];
+  mn10300_timer_regs reg[NR_REG_TIMERS];
   mn10300_timer timer[NR_TIMERS];
 
   /* treat timer 6 registers specially. */
-  unsigned16   tm6md, tm6bc, tm6mca, tm6mcb; 
+  unsigned16   tm6md0, tm6md1, tm6bc, tm6ca, tm6cb; 
   unsigned8  tm6mda, tm6mdb;  /* compare/capture mode regs for timer 6 */
-  struct hw_event *event6;
 };
 
 /* output port ID's */
@@ -141,6 +148,7 @@ static const struct hw_port_descriptor mn103tim_ports[] = {
 };
 
 #define bits2to5_mask 0x3c
+#define bits0to2_mask 0x07
 #define load_mask     0x40
 #define count_mask    0x80
 #define count_and_load_mask (load_mask | count_mask)
@@ -203,18 +211,22 @@ mn103tim_finish (struct hw *me)
   attach_mn103tim_regs (me, timers);
 
   /* Initialize the timers */
+  for ( i=0; i < NR_REG_TIMERS; ++i )
+    {
+      timers->reg[i].mode = 0x00;
+      timers->reg[i].base = 0;
+    }
   for ( i=0; i < NR_TIMERS; ++i )
     {
       timers->timer[i].event = NULL;
-      timers->timer[i].mode = 0x00;
-      timers->timer[i].base = 0;
       timers->timer[i].div_ratio = 0;
       timers->timer[i].start = 0;
     }
-  timers->tm6md = 0x0000;
+  timers->tm6md0 = 0x00;
+  timers->tm6md1 = 0x00;
   timers->tm6bc = 0x0000;
-  timers->tm6mca = 0x0000;
-  timers->tm6mcb = 0x0000;
+  timers->tm6ca = 0x0000;
+  timers->tm6cb = 0x0000;
   timers->tm6mda = 0x00;
   timers->tm6mdb = 0x00;
 }
@@ -247,7 +259,8 @@ decode_addr (struct hw *me,
     case 0x23: return TM3BC;
     case 0x80: return TM4MD;
     case 0x82: return TM5MD;
-    case 0x84: return TM6MD;
+    case 0x84: /* fall through */
+    case 0x85: return TM6MD;
     case 0x90: return TM4BR;
     case 0x92: return TM5BR;
     case 0xa0: return TM4BC;
@@ -279,18 +292,25 @@ read_mode_reg (struct hw *me,
     {
     case 1:
       /* Accessing 1 byte is ok for all mode registers. */
-      *(unsigned8*)dest = timers->timer[timer_nr].mode;
+      if ( timer_nr == 6 )
+       {
+         *(unsigned8*)dest = timers->tm6md0;
+       }
+      else
+       {
+         *(unsigned8*)dest = timers->reg[timer_nr].mode;
+       }
       break;
 
     case 2:
       if ( timer_nr == 6 )
        {
-         *(unsigned16 *)dest = timers->tm6md;
+         *(unsigned16 *)dest = (timers->tm6md0 << 8) | timers->tm6md1;
        }
       else if ( timer_nr == 0 || timer_nr == 2 )
        {
-         val16 = (timers->timer[timer_nr].mode << 8)
-           | timers->timer[timer_nr+1].mode;
+         val16 = (timers->reg[timer_nr].mode << 8)
+           | timers->reg[timer_nr+1].mode;
          *(unsigned16*)dest = val16;
        }
       else
@@ -302,10 +322,10 @@ read_mode_reg (struct hw *me,
     case 4:
       if ( timer_nr == 0 )
        {
-         val32 = (timers->timer[0].mode << 24 )
-           | (timers->timer[1].mode << 16)
-           | (timers->timer[2].mode << 8)
-           | timers->timer[3].mode;
+         val32 = (timers->reg[0].mode << 24 )
+           | (timers->reg[1].mode << 16)
+           | (timers->reg[2].mode << 8)
+           | timers->reg[3].mode;
          *(unsigned32*)dest = val32;
        }
       else
@@ -338,7 +358,7 @@ read_base_reg (struct hw *me,
       /* Reading 1 byte is ok for all registers. */
       if ( timer_nr < NR_8BIT_TIMERS )
        {
-         *(unsigned8*)dest = timers->timer[timer_nr].base;
+         *(unsigned8*)dest = timers->reg[timer_nr].base;
        }
       break;
 
@@ -351,12 +371,12 @@ read_base_reg (struct hw *me,
        {
          if ( timer_nr < NR_8BIT_TIMERS )
            {
-             val16 = (timers->timer[timer_nr].base<<8)
-               | timers->timer[timer_nr+1].base;
+             val16 = (timers->reg[timer_nr].base<<8)
+               | timers->reg[timer_nr+1].base;
            }
          else 
            {
-             val16 = timers->timer[timer_nr].base;
+             val16 = timers->reg[timer_nr].base;
            }
          *(unsigned16*)dest = val16;
        }
@@ -365,13 +385,13 @@ read_base_reg (struct hw *me,
     case 4:
       if ( timer_nr == 0 )
        {
-         val32 = (timers->timer[0].base << 24) | (timers->timer[1].base << 16)
-           | (timers->timer[2].base << 8) | timers->timer[3].base;
+         val32 = (timers->reg[0].base << 24) | (timers->reg[1].base << 16)
+           | (timers->reg[2].base << 8) | timers->reg[3].base;
          *(unsigned32*)dest = val32;
        }
       else if ( timer_nr == 4 ) 
        {
-         val32 = (timers->timer[4].base << 16) | timers->timer[5].base;
+         val32 = (timers->reg[4].base << 16) | timers->reg[5].base;
          *(unsigned32*)dest = val32;
        }
       else
@@ -399,16 +419,30 @@ read_counter (struct hw *me,
   if ( NULL == timers->timer[timer_nr].event )
     {
       /* Timer is not counting, use value in base register. */
-      val = timers->timer[timer_nr].base;
+      if ( timer_nr == 6 )
+       {
+         val = 0;  /* timer 6 is an up counter */
+       }
+      else
+       {
+         val = timers->reg[timer_nr].base;
+       }
     }
   else
     {
-      /* ticks left = start time + div ratio - curr time */
-      /* Cannot use base register because it can be written during counting and it
-        doesn't affect counter until underflow occurs. */
-
-      val = timers->timer[timer_nr].start + timers->timer[timer_nr].div_ratio
-       - hw_event_queue_time(me);
+      if ( timer_nr == 6 )  /* timer 6 is an up counter. */
+       {
+         val = hw_event_queue_time(me) - timers->timer[timer_nr].start;
+       }
+      else
+       {
+         /* ticks left = start time + div ratio - curr time */
+         /* Cannot use base register because it can be written during counting and it
+            doesn't affect counter until underflow occurs. */
+         
+         val = timers->timer[timer_nr].start + timers->timer[timer_nr].div_ratio
+           - hw_event_queue_time(me);
+       }
     }
 
   switch (nr_bytes) {
@@ -431,6 +465,63 @@ read_counter (struct hw *me,
 }
 
 
+static void
+read_special_timer6_reg (struct hw *me,
+                        struct mn103tim *timers,
+                        int timer_nr,
+                        void *dest,
+                        unsigned  nr_bytes)
+{
+  unsigned32 val;
+
+  switch (nr_bytes) {
+  case 1:
+    {
+      switch ( timer_nr ) {
+      case TM6MDA:
+       *(unsigned8 *)dest = timers->tm6mda;
+       break;
+    
+      case TM6MDB:
+       *(unsigned8 *)dest = timers->tm6mdb;
+       break;
+    
+      case TM6CA:
+       *(unsigned8 *)dest = timers->tm6ca;
+       break;
+    
+      case TM6CB:
+       *(unsigned8 *)dest = timers->tm6cb;
+       break;
+      
+      default:
+       break;
+      }
+      break;
+    }
+    
+  case 2:
+    if ( timer_nr == TM6CA )
+      {
+       *(unsigned16 *)dest = timers->tm6ca;
+      }
+    else if ( timer_nr == TM6CB )
+      {
+       *(unsigned16 *)dest = timers->tm6cb;
+      }
+    else
+      {
+       hw_abort(me, "bad read size for timer 6 mode A/B register");
+      }
+    break;
+
+  default:
+    hw_abort(me, "bad read size for timer 6 register");
+  }
+      
+}
+
+
 static unsigned
 mn103tim_io_read_buffer (struct hw *me,
                         void *dest,
@@ -445,8 +536,8 @@ mn103tim_io_read_buffer (struct hw *me,
 
   timer_reg = decode_addr (me, timers, base);
 
-  /* It can be either a mode register, a base register or a binary counter. */
-  /* Check in that order. */
+  /* It can be either a mode register, a base register, a binary counter, */
+  /* or a special timer 6 register.  Check in that order. */
   if ( timer_reg >= FIRST_MODE_REG && timer_reg <= LAST_MODE_REG )
     {
       read_mode_reg(me, timers, timer_reg-FIRST_MODE_REG, dest, nr_bytes);
@@ -459,6 +550,10 @@ mn103tim_io_read_buffer (struct hw *me,
     {
       read_counter(me, timers, timer_reg-FIRST_COUNTER, dest, nr_bytes);
     }
+  else if ( timer_reg <= LAST_TIMER_REG )
+    {
+      read_special_timer6_reg(me, timers, timer_reg, dest, nr_bytes);
+    }
   else
     {
       hw_abort(me, "invalid timer register address.");
@@ -473,22 +568,67 @@ do_counter_event (struct hw *me,
                  void *data)
 {
   struct mn103tim *timers = hw_data(me);
-  int timer_nr = (int) data;
+  long timer_nr = (long) data;
+  int next_timer;
 
   /* Check if counting is still enabled. */
-  if ( (timers->timer[timer_nr].mode & count_mask) != 0 )
+  if ( (timers->reg[timer_nr].mode & count_mask) != 0 )
     {
       /* Generate an interrupt for the timer underflow (TIMERn_UFLOW). */
-      hw_port_event (me, timer_nr /*uflow_port[timer_nr]*/, 1 /* level */);
 
-      /* Schedule next timeout.  */
+      /* Port event occurs on port of last cascaded timer. */
+      /* This works across timer range from 0 to NR_REG_TIMERS because */
+      /* the first 16 bit timer (timer 4) is not allowed to be set as  */
+      /* a cascading timer. */
+      for ( next_timer = timer_nr+1; next_timer < NR_REG_TIMERS; ++next_timer )
+       {
+         if ( (timers->reg[next_timer].mode & clock_mask) != clk_cascaded )
+           {
+             break;
+           }
+       }
+      hw_port_event (me, next_timer-1, 1);
 
+      /* Schedule next timeout.  */
       timers->timer[timer_nr].start = hw_event_queue_time(me);
-      /* FIX: Check if div_ ratio has changed and if it's now 0. */
+      /* FIX: Check if div_ratio has changed and if it's now 0. */
       timers->timer[timer_nr].event
        = hw_event_queue_schedule (me, timers->timer[timer_nr].div_ratio,
                                   do_counter_event, (void *)timer_nr);
     }
+  else
+    {
+      timers->timer[timer_nr].event = NULL;
+    }
+
+}
+
+
+static void
+do_counter6_event (struct hw *me,
+                 void *data)
+{
+  struct mn103tim *timers = hw_data(me);
+  long timer_nr = (long) data;
+  int next_timer;
+
+  /* Check if counting is still enabled. */
+  if ( (timers->reg[timer_nr].mode & count_mask) != 0 )
+    {
+      /* Generate an interrupt for the timer underflow (TIMERn_UFLOW). */
+      hw_port_event (me, timer_nr, 1);
+
+      /* Schedule next timeout.  */
+      timers->timer[timer_nr].start = hw_event_queue_time(me);
+      /* FIX: Check if div_ratio has changed and if it's now 0. */
+      timers->timer[timer_nr].event
+       = hw_event_queue_schedule (me, timers->timer[timer_nr].div_ratio,
+                                  do_counter6_event, (void *)timer_nr);
+    }
+  else
+    {
+      timers->timer[timer_nr].event = NULL;
+    }
 
 }
 
@@ -502,7 +642,6 @@ write_base_reg (struct hw *me,
   unsigned i;
   const unsigned8 *buf8 = source;
   const unsigned16 *buf16 = source;
-  unsigned8 mode_val;
 
   /* If TMnCNE == 0 (counting is off),  writing to the base register
      (TMnBR) causes a simultaneous write to the counter reg (TMnBC).
@@ -510,14 +649,12 @@ write_base_reg (struct hw *me,
      underflow occurs.  Since the counter register is not explicitly
      maintained, this functionality is handled in read_counter. */
 
-  mode_val = timers->timer[timer_nr].mode;
-
   /* Check nr_bytes: write of 1, 2 or 4 bytes allowed depending on timer. */
   switch ( nr_bytes )
     {
     case 1:
       /* Storing 1 byte is ok for all registers. */
-      timers->timer[timer_nr].base = buf8[0];
+      timers->reg[timer_nr].base = buf8[0];
       break;
 
     case 2:
@@ -529,12 +666,12 @@ write_base_reg (struct hw *me,
        {
          if ( timer_nr < NR_8BIT_TIMERS )
            {
-             timers->timer[timer_nr].base = buf8[0];
-             timers->timer[timer_nr+1].base = buf8[1];
+             timers->reg[timer_nr].base = buf8[0];
+             timers->reg[timer_nr+1].base = buf8[1];
            }
          else 
            {
-             timers->timer[timer_nr].base = buf16[0];
+             timers->reg[timer_nr].base = buf16[0];
            }
        }
       break;
@@ -542,16 +679,15 @@ write_base_reg (struct hw *me,
     case 4:
       if ( timer_nr == 0 )
        {
-         ASSERT(0);
-         timers->timer[0].base = buf8[0];
-         timers->timer[1].base = buf8[1];
-         timers->timer[2].base = buf8[2];
-         timers->timer[3].base = buf8[3];
+         timers->reg[0].base = buf8[0];
+         timers->reg[1].base = buf8[1];
+         timers->reg[2].base = buf8[2];
+         timers->reg[3].base = buf8[3];
        }
       else if ( timer_nr == 4 )
        {
-         timers->timer[4].base = buf16[0];
-         timers->timer[5].base = buf16[1];
+         timers->reg[4].base = buf16[0];
+         timers->reg[5].base = buf16[1];
        }
       else
        {
@@ -567,12 +703,12 @@ write_base_reg (struct hw *me,
 }
 
 static void
-write_8bit_mode_reg (struct hw *me,
-                    struct mn103tim *timers,
-                    int timer_nr,
-                    const void *source,
-                    unsigned nr_bytes)
-     /* for timers 0 to 3 */
+write_mode_reg (struct hw *me,
+               struct mn103tim *timers,
+               long timer_nr,
+               const void *source,
+               unsigned nr_bytes)
+     /* for timers 0 to 5 */
 {
   unsigned i;
   unsigned8 mode_val, next_mode_val;
@@ -580,11 +716,12 @@ write_8bit_mode_reg (struct hw *me,
 
   if ( nr_bytes != 1 )
     {
-      hw_abort (me, "bad write size of %d bytes to TM%dMD.", nr_bytes, timer_nr);
+      hw_abort (me, "bad write size of %d bytes to TM%ldMD.", nr_bytes,
+               timer_nr);
     }
 
   mode_val = *(unsigned8 *)source;
-  timers->timer[timer_nr].mode = mode_val;
+  timers->reg[timer_nr].mode = mode_val;
       
   if ( ( mode_val & count_and_load_mask ) == count_and_load_mask )
     {
@@ -594,7 +731,7 @@ write_8bit_mode_reg (struct hw *me,
     {
       hw_abort(me, "Cannot write to bits 2 to 5 of mode register");
     }
-      
+
   if ( mode_val & count_mask )
     {
       /* - de-schedule any previous event. */
@@ -604,27 +741,54 @@ write_8bit_mode_reg (struct hw *me,
       /* For cascaded timers, */
       if ( (mode_val & clock_mask) == clk_cascaded )
        {
-         if ( timer_nr == 0 )
+         if ( timer_nr == 0 || timer_nr == 4 )
            {
-             hw_abort(me, "Timer 0 cannot be cascaded.");
+             hw_abort(me, "Timer %ld cannot be cascaded.", timer_nr);
            }
        }
       else
        {
-         div_ratio = timers->timer[timer_nr].base;
+         div_ratio = timers->reg[timer_nr].base;
 
          /* Check for cascading. */
-         next_mode_val = timers->timer[timer_nr+1].mode;
-         if ( ( next_mode_val & clock_mask ) == clk_cascaded )
+         if ( timer_nr < NR_8BIT_TIMERS )
+           {
+             for ( i = timer_nr + 1; i <= 3; ++i ) 
+               {
+                 next_mode_val = timers->reg[i].mode;
+                 if ( ( next_mode_val & clock_mask ) == clk_cascaded )
+                   {
+                     /* Check that CNE is on. */
+                     if ( ( next_mode_val & count_mask ) == 0 ) 
+                       {
+                         hw_abort (me, "cascaded timer not ready for counting");
+                       }
+                     ASSERT(timers->timer[i].event == NULL);
+                     ASSERT(timers->timer[i].div_ratio == 0);
+                     div_ratio = div_ratio
+                       | (timers->reg[i].base << (8*(i-timer_nr)));
+                   }
+                 else
+                   {
+                     break;
+                   }
+               }
+           }
+         else
            {
-             /* Check that CNE is on. */
-             if ( ( next_mode_val & count_mask ) == 0 ) 
+             /* Mode register for a 16 bit timer */
+             next_mode_val = timers->reg[timer_nr+1].mode;
+             if ( ( next_mode_val & clock_mask ) == clk_cascaded )
                {
-                 hw_abort (me, "cascaded timer not ready for counting");
+                 /* Check that CNE is on. */
+                 if ( ( next_mode_val & count_mask ) == 0 ) 
+                   {
+                     hw_abort (me, "cascaded timer not ready for counting");
+                   }
+                 ASSERT(timers->timer[timer_nr+1].event == NULL);
+                 ASSERT(timers->timer[timer_nr+1].div_ratio == 0);
+                 div_ratio = div_ratio | (timers->reg[timer_nr+1].base << 16);
                }
-             ASSERT(timers->timer[timer_nr+1].event == NULL);
-             ASSERT(timers->timer[timer_nr+1].div_ratio == 0);
-             div_ratio = div_ratio | (timers->timer[timer_nr+1].base << 8);
            }
 
          timers->timer[timer_nr].div_ratio = div_ratio;
@@ -639,7 +803,6 @@ write_8bit_mode_reg (struct hw *me,
            {
              /* Set start time. */
              timers->timer[timer_nr].start = hw_event_queue_time(me);
-             
              timers->timer[timer_nr].event
                = hw_event_queue_schedule(me, div_ratio,
                                          do_counter_event,
@@ -652,13 +815,13 @@ write_8bit_mode_reg (struct hw *me,
       /* Turn off counting */
       if ( NULL != timers->timer[timer_nr].event )
        {
-         ASSERT((timers->timer[timer_nr].mode & clock_mask) != clk_cascaded);
+         ASSERT((timers->reg[timer_nr].mode & clock_mask) != clk_cascaded);
          hw_event_queue_deschedule (me, timers->timer[timer_nr].event);
          timers->timer[timer_nr].event = NULL;
        }
       else
        {
-         if ( (timers->timer[timer_nr].mode & clock_mask) == clk_cascaded )
+         if ( (timers->reg[timer_nr].mode & clock_mask) == clk_cascaded )
            {
              ASSERT(timers->timer[timer_nr].event == NULL);
            }
@@ -669,84 +832,81 @@ write_8bit_mode_reg (struct hw *me,
 }
 
 static void
-write_16bit_mode_reg (struct hw *me,
-                     struct mn103tim *timers,
-                     int timer_nr,
-                     const void *source,
-                     unsigned nr_bytes)
-     /* for timers 4 and 5, not 6 */
+write_tm6md (struct hw *me,
+            struct mn103tim *timers,
+            unsigned_word address,
+            const void *source,
+            unsigned nr_bytes)
 {
-  unsigned i;
-  unsigned8 mode_val, next_mode_val;
+  unsigned8 mode_val0 = 0x00, mode_val1 = 0x00;
   unsigned32 div_ratio;
+  long timer_nr = 6;
 
-  if ( nr_bytes != 1 )
+  unsigned_word offset = address - timers->block[0].base;
+  
+  if ((offset != 0x84 && nr_bytes > 1) || nr_bytes > 2 )
     {
-      hw_abort (me, "bad write size of %d bytes to TM%dMD.", nr_bytes, timer_nr);
+      hw_abort (me, "Bad write size of %d bytes to TM6MD", nr_bytes);
     }
 
-  mode_val = *(unsigned8 *)source;
-  timers->timer[timer_nr].mode = mode_val;
-      
-  if ( ( mode_val & count_and_load_mask ) == count_and_load_mask )
+  if ( offset == 0x84 )  /* address of TM6MD */
     {
-      hw_abort(me, "Cannot load base reg and start counting simultaneously.");
+      /*  Fill in first byte of mode */
+      mode_val0 = *(unsigned8 *)source;
+      timers->tm6md0 = mode_val0;
+    
+      if ( ( mode_val0 & 0x26 ) != 0 )
+       {
+         hw_abort(me, "Cannot write to bits 5, 3, and 2 of TM6MD");
+       }
     }
-  if ( ( mode_val & bits2to5_mask ) != 0 )
+  
+  if ( offset == 0x85 || nr_bytes == 2 )
     {
-      hw_abort(me, "Cannot write to bits 2 to 5 of mode register");
+      /*  Fill in second byte of mode */
+      if ( nr_bytes == 2 )
+       {
+         mode_val1 = *(unsigned8 *)source+1;
+       }
+      else
+       {
+         mode_val1 = *(unsigned8 *)source;
+       }
+
+      timers->tm6md1 = mode_val1;
+
+      if ( ( mode_val1 & count_and_load_mask ) == count_and_load_mask )
+       {
+         hw_abort(me, "Cannot load base reg and start counting simultaneously.");
+       }
+      if ( ( mode_val1 & bits0to2_mask ) != 0 )
+       {
+         hw_abort(me, "Cannot write to bits 8 to 10 of TM6MD");
+       }
     }
 
-     
-  if ( mode_val & count_mask )
+  if ( mode_val1 & count_mask )
     {
       /* - de-schedule any previous event. */
       /* - add new event to queue to start counting. */
       /* - assert that counter == base reg? */
 
-      /* For cascaded timers, */
-      if ( (mode_val & clock_mask) == clk_cascaded )
+      div_ratio = timers->tm6ca;  /* binary counter for timer 6 */
+      timers->timer[timer_nr].div_ratio = div_ratio;
+      if ( NULL != timers->timer[timer_nr].event )
        {
-         if ( timer_nr == 4 )
-           {
-             hw_abort(me, "Timer 4 cannot be cascaded.");
-           }
+         hw_event_queue_deschedule (me, timers->timer[timer_nr].event);
+         timers->timer[timer_nr].event = NULL;
        }
-      else
-       {
-         div_ratio = timers->timer[timer_nr].base;
-         
-         /* Check for cascading. */
-         next_mode_val = timers->timer[timer_nr+1].mode;
-         if ( ( next_mode_val & clock_mask ) == clk_cascaded )
-           {
-             /* Check that CNE is on. */
-             if ( ( next_mode_val & count_mask ) == 0 ) 
-               {
-                 hw_abort (me, "cascaded timer not ready for counting");
-               }
-             ASSERT(timers->timer[timer_nr+1].event == NULL);
-             ASSERT(timers->timer[timer_nr+1].div_ratio == 0);
-             div_ratio = div_ratio | (timers->timer[timer_nr+1].base << 16);
-           }
-
-         timers->timer[timer_nr].div_ratio = div_ratio;
-      
-         if ( NULL != timers->timer[timer_nr].event )
-           {
-             hw_event_queue_deschedule (me, timers->timer[timer_nr].event);
-             timers->timer[timer_nr].event = NULL;
-           }
 
-         if ( div_ratio > 0 )
-           {
-             /* Set start time. */
-             timers->timer[timer_nr].start = hw_event_queue_time(me);
-             
-             timers->timer[timer_nr].event
-               = hw_event_queue_schedule(me, div_ratio, do_counter_event,
-                                         (void *)(timer_nr));
-           }
+      if ( div_ratio > 0 )
+       {
+         /* Set start time. */
+         timers->timer[timer_nr].start = hw_event_queue_time(me);
+         timers->timer[timer_nr].event
+           = hw_event_queue_schedule(me, div_ratio,
+                                     do_counter6_event,
+                                     (void *)(timer_nr)); 
        }
     }
   else
@@ -754,22 +914,71 @@ write_16bit_mode_reg (struct hw *me,
       /* Turn off counting */
       if ( NULL != timers->timer[timer_nr].event )
        {
-         ASSERT((timers->timer[timer_nr].mode & clock_mask) != clk_cascaded);
          hw_event_queue_deschedule (me, timers->timer[timer_nr].event);
          timers->timer[timer_nr].event = NULL;
        }
-      else
-       {
-         if ( (timers->timer[timer_nr].mode & clock_mask) == clk_cascaded )
-           {
-             ASSERT(timers->timer[timer_nr].event == NULL);
-           }
-       }
+    }
+}
+
+
+
+static void
+write_special_timer6_reg (struct hw *me,
+                         struct mn103tim *timers,
+                         int timer_nr,
+                         const void *source,
+                         unsigned  nr_bytes)
+{
+  unsigned32 val;
+
+  switch (nr_bytes) {
+  case 1:
+    {
+      switch ( timer_nr ) {
+      case TM6MDA:
+       timers->tm6mda = *(unsigned8 *)source;
+       break;
+    
+      case TM6MDB:
+       timers->tm6mdb = *(unsigned8 *)source;
+       break;
+    
+      case TM6CA:
+       timers->tm6ca = *(unsigned8 *)source;
+       break;
+    
+      case TM6CB:
+       timers->tm6cb = *(unsigned8 *)source;
+       break;
       
+      default:
+       break;
+      }
+      break;
     }
+    
+  case 2:
+    if ( timer_nr == TM6CA )
+      {
+       timers->tm6ca = *(unsigned16 *)source;
+      }
+    else if ( timer_nr == TM6CB )
+      {
+       timers->tm6cb = *(unsigned16 *)source;
+      }
+    else
+      {
+       hw_abort(me, "bad read size for timer 6 mode A/B register");
+      }
+    break;
 
+  default:
+    hw_abort(me, "bad read size for timer 6 register");
+  }
+      
 }
 
+
 static unsigned
 mn103tim_io_write_buffer (struct hw *me,
                          const void *source,
@@ -785,19 +994,18 @@ mn103tim_io_write_buffer (struct hw *me,
 
   timer_reg = decode_addr (me, timers, base);
 
-  /* It can be either a mode register, a base register or a binary counter. */
-  /* Check in that order. */
+  /* It can be either a mode register, a base register, a binary counter, */
+  /* or a special timer 6 register.  Check in that order. */
   if ( timer_reg <= LAST_MODE_REG )
     {
-      if ( timer_reg > 3 )
+      if ( timer_reg == 6 ) 
        {
-         write_16bit_mode_reg(me, timers, timer_reg-FIRST_MODE_REG,
-                              source, nr_bytes);
+         write_tm6md(me, timers, base, source, nr_bytes);
        }
       else
        {
-         write_8bit_mode_reg(me, timers, timer_reg-FIRST_MODE_REG,
-                              source, nr_bytes);
+         write_mode_reg(me, timers, timer_reg-FIRST_MODE_REG,
+                        source, nr_bytes);
        }
     }
   else if ( timer_reg <= LAST_BASE_REG )
@@ -808,6 +1016,10 @@ mn103tim_io_write_buffer (struct hw *me,
     {
       hw_abort(me, "cannot write to counter");
     }
+  else if ( timer_reg <= LAST_TIMER_REG )
+    {
+      write_special_timer6_reg(me, timers, timer_reg, source, nr_bytes);
+    }
   else
     {
       hw_abort(me, "invalid reg type");