PR c++/16597
[deliverable/binutils-gdb.git] / sim / bfin / dv-bfin_gpio.c
index 50baf03e71ceec57fb5e8af015d1230656ca4af2..4fe4aa756881347882a79f7c8c53baf1500982c1 100644 (file)
@@ -1,6 +1,6 @@
 /* Blackfin General Purpose Ports (GPIO) model
 
-   Copyright (C) 2010-2011 Free Software Foundation, Inc.
+   Copyright (C) 2010-2014 Free Software Foundation, Inc.
    Contributed by Analog Devices, Inc.
 
    This file is part of simulators.
@@ -28,6 +28,8 @@ struct bfin_gpio
 {
   bu32 base;
 
+  bu16 int_state;
+
   /* Order after here is important -- matches hardware MMR layout.  */
   bu16 BFIN_MMR_16(data);
   bu16 BFIN_MMR_16(clear);
@@ -60,6 +62,44 @@ static const char * const mmr_names[] =
 };
 #define mmr_name(off) mmr_names[(off) / 4]
 
+static void
+bfin_gpio_forward_int (struct hw *me, struct bfin_gpio *port, bu32 mask,
+                      int dst_port)
+{
+  HW_TRACE ((me, "resending levels on port %c", 'a' + dst_port));
+  hw_port_event (me, dst_port, !!(port->int_state & mask));
+}
+static void
+bfin_gpio_forward_ints (struct hw *me, struct bfin_gpio *port)
+{
+  bfin_gpio_forward_int (me, port, port->maska, 0);
+  bfin_gpio_forward_int (me, port, port->maskb, 1);
+}
+
+static void
+bfin_gpio_forward_ouput (struct hw *me, struct bfin_gpio *port, bu32 odata)
+{
+  int pin, value, ovalue, bit;
+
+  for (pin = 0; pin < 16; ++pin)
+    {
+      bit = 1 << pin;
+
+      /* Make sure this is an output pin.  */
+      if (!(port->dir & bit))
+       continue;
+
+      /* Only signal port if the pin changes value.  */
+      value = !!(port->data & bit);
+      ovalue = !!(odata & bit);
+      if (value == ovalue)
+       continue;
+
+      HW_TRACE ((me, "outputting gpio %i changed to %i", pin, value));
+      hw_port_event (me, pin, value);
+    }
+}
+
 static unsigned
 bfin_gpio_io_write_buffer (struct hw *me, const void *source, int space,
                           address_word addr, unsigned nr_bytes)
@@ -68,6 +108,7 @@ bfin_gpio_io_write_buffer (struct hw *me, const void *source, int space,
   bu32 mmr_off;
   bu16 value;
   bu16 *valuep;
+  bu32 data = port->data;
 
   value = dv_load_2 (source);
   mmr_off = addr - port->base;
@@ -92,16 +133,22 @@ bfin_gpio_io_write_buffer (struct hw *me, const void *source, int space,
     case mmr_offset(clear):
     case mmr_offset(maska_clear):
     case mmr_offset(maskb_clear):
+      /* We want to clear the related data MMR.  */
+      valuep -= 2;
       dv_w1c_2 (valuep, value, -1);
       break;
     case mmr_offset(set):
     case mmr_offset(maska_set):
     case mmr_offset(maskb_set):
+      /* We want to set the related data MMR.  */
+      valuep -= 4;
       *valuep |= value;
       break;
     case mmr_offset(toggle):
     case mmr_offset(maska_toggle):
     case mmr_offset(maskb_toggle):
+      /* We want to toggle the related data MMR.  */
+      valuep -= 6;
       *valuep ^= value;
       break;
     default:
@@ -109,6 +156,21 @@ bfin_gpio_io_write_buffer (struct hw *me, const void *source, int space,
       break;
     }
 
+  /* If updating masks, make sure we send updated port info.  */
+  switch (mmr_off)
+    {
+    case mmr_offset(dir):
+    case mmr_offset(data) ... mmr_offset(toggle):
+      bfin_gpio_forward_ouput (me, port, data);
+      break;
+    case mmr_offset(maska) ... mmr_offset(maska_toggle):
+      bfin_gpio_forward_int (me, port, port->maska, 0);
+      break;
+    case mmr_offset(maskb) ... mmr_offset(maskb_toggle):
+      bfin_gpio_forward_int (me, port, port->maskb, 1);
+      break;
+    }
+
   return nr_bytes;
 }
 
@@ -166,21 +228,22 @@ static const struct hw_port_descriptor bfin_gpio_ports[] =
 {
   { "mask_a", 0, 0, output_port, },
   { "mask_b", 1, 0, output_port, },
-  { "p0",     0, 0, input_port, },
-  { "p1",     1, 0, input_port, },
-  { "p2",     2, 0, input_port, },
-  { "p3",     3, 0, input_port, },
-  { "p4",     4, 0, input_port, },
-  { "p5",     5, 0, input_port, },
-  { "p6",     6, 0, input_port, },
-  { "p7",     7, 0, input_port, },
-  { "p8",     8, 0, input_port, },
-  { "p9",     9, 0, input_port, },
-  { "p10",   10, 0, input_port, },
-  { "p11",   11, 0, input_port, },
-  { "p12",   12, 0, input_port, },
-  { "p13",   13, 0, input_port, },
-  { "p14",   14, 0, input_port, },
+  { "p0",     0, 0, bidirect_port, },
+  { "p1",     1, 0, bidirect_port, },
+  { "p2",     2, 0, bidirect_port, },
+  { "p3",     3, 0, bidirect_port, },
+  { "p4",     4, 0, bidirect_port, },
+  { "p5",     5, 0, bidirect_port, },
+  { "p6",     6, 0, bidirect_port, },
+  { "p7",     7, 0, bidirect_port, },
+  { "p8",     8, 0, bidirect_port, },
+  { "p9",     9, 0, bidirect_port, },
+  { "p10",   10, 0, bidirect_port, },
+  { "p11",   11, 0, bidirect_port, },
+  { "p12",   12, 0, bidirect_port, },
+  { "p13",   13, 0, bidirect_port, },
+  { "p14",   14, 0, bidirect_port, },
+  { "p15",   15, 0, bidirect_port, },
   { NULL, 0, 0, 0, },
 };
 
@@ -192,15 +255,27 @@ bfin_gpio_port_event (struct hw *me, int my_port, struct hw *source,
   bool olvl, nlvl;
   bu32 bit = (1 << my_port);
 
-  /* Only screw with state if this pin is set as an input.  */
-  if (!(port->dir & port->inen & bit))
-    return;
+  /* Normalize the level value.  A simulated device can send any value
+     it likes to us, but in reality we only care about 0 and 1.  This
+     lets us assume only those two values below.  */
+  level = !!level;
+
+  HW_TRACE ((me, "pin %i set to %i", my_port, level));
+
+  /* Only screw with state if this pin is set as an input, and the
+     input is actually enabled.  */
+  if ((port->dir & bit) || !(port->inen & bit))
+    {
+      HW_TRACE ((me, "ignoring level/int due to DIR=%i INEN=%i",
+                !!(port->dir & bit), !!(port->inen & bit)));
+      return;
+    }
 
   /* Get the old pin state for calculating an interrupt.  */
   olvl = !!(port->data & bit);
 
   /* Update the new pin state.  */
-  port->data = (port->data & ~bit) | (level << bit);
+  port->data = (port->data & ~bit) | (level << my_port);
 
   /* See if this state transition will generate an interrupt.  */
   nlvl = !!(port->data & bit);
@@ -208,32 +283,50 @@ bfin_gpio_port_event (struct hw *me, int my_port, struct hw *source,
   if (port->edge & bit)
     {
       /* Pin is edge triggered.  */
-      if (!(port->both & bit))
+      if (port->both & bit)
        {
          /* Both edges.  */
          if (olvl == nlvl)
-           return;
+           {
+             HW_TRACE ((me, "ignoring int due to EDGE=%i BOTH=%i lvl=%i->%i",
+                        !!(port->edge & bit), !!(port->both & bit),
+                        olvl, nlvl));
+             return;
+           }
        }
       else
        {
          /* Just one edge.  */
          if (!(((port->polar & bit) && olvl > nlvl)
                || (!(port->polar & bit) && olvl < nlvl)))
-           return;
+           {
+             HW_TRACE ((me, "ignoring int due to EDGE=%i POLAR=%i lvl=%i->%i",
+                        !!(port->edge & bit), !!(port->polar & bit),
+                        olvl, nlvl));
+             return;
+           }
        }
+
+      /* Send the signal up, and then fall through to clear it.  */
+      port->int_state |= bit;
+      bfin_gpio_forward_ints (me, port);
+      port->int_state &= ~bit;
     }
   else
     {
       /* Pin is level triggered.  */
       if (nlvl == !!(port->polar & bit))
-       return;
+       {
+         HW_TRACE ((me, "ignoring int due to EDGE=%i POLAR=%i lvl=%i",
+                    !!(port->edge & bit), !!(port->polar & bit), nlvl));
+         /* We still need to signal SIC to clear the int, so don't return.  */
+         port->int_state &= ~bit;
+       }
+      else
+       port->int_state |= bit;
     }
 
-  /* If the masks allow it, push the interrupt even higher.  */
-  if (port->maska & bit)
-    hw_port_event (me, 0, 1);
-  if (port->maskb & bit)
-    hw_port_event (me, 1, 1);
+  bfin_gpio_forward_ints (me, port);
 }
 
 static void
This page took 0.025615 seconds and 4 git commands to generate.