/* Blackfin General Purpose Ports (GPIO) model
- Copyright (C) 2010-2011 Free Software Foundation, Inc.
+ Copyright (C) 2010-2015 Free Software Foundation, Inc.
Contributed by Analog Devices, Inc.
This file is part of simulators.
{
bu32 base;
+ bu16 int_state;
+
/* Order after here is important -- matches hardware MMR layout. */
bu16 BFIN_MMR_16(data);
bu16 BFIN_MMR_16(clear);
};
#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)
bu32 mmr_off;
bu16 value;
bu16 *valuep;
+ bu32 data = port->data;
value = dv_load_2 (source);
mmr_off = addr - port->base;
case mmr_offset(clear):
case mmr_offset(maska_clear):
case mmr_offset(maskb_clear):
- dv_w1c_2 (valuep, value, 0);
+ /* 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:
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;
}
{
{ "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, },
};
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);
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