summaryrefslogtreecommitdiff
path: root/hw/gpio/pl061.c
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2021-07-02 11:40:13 +0100
committerPeter Maydell <peter.maydell@linaro.org>2021-07-09 16:09:12 +0100
commitad06d56fc7155c7893b18efecb9fe0f2e9124eaf (patch)
tree3ba5a94c14ceca3675e0913582e393903146714f /hw/gpio/pl061.c
parent455736df2cfd3a980782986d597132776d630823 (diff)
downloadqemu-ad06d56fc7155c7893b18efecb9fe0f2e9124eaf.zip
hw/gpio/pl061: Honour Luminary PL061 PUR and PDR registers
The Luminary variant of the PL061 has registers GPIOPUR and GPIOPDR which lets the guest configure whether the GPIO lines are pull-up, pull-down, or truly floating. Instead of assuming all lines are pulled high, honour the PUR and PDR registers. For the plain PL061, continue to assume that lines have an external pull-up resistor, as we did before. The stellaris board actually relies on this behaviour -- the CD line of the ssd0323 display device is connected to GPIO output C7, and it is only because of a different bug which we're about to fix that we weren't incorrectly driving this line high on reset and putting the ssd0323 into data mode. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Diffstat (limited to 'hw/gpio/pl061.c')
-rw-r--r--hw/gpio/pl061.c62
1 files changed, 56 insertions, 6 deletions
diff --git a/hw/gpio/pl061.c b/hw/gpio/pl061.c
index a3c1386221..9360c143ee 100644
--- a/hw/gpio/pl061.c
+++ b/hw/gpio/pl061.c
@@ -94,18 +94,68 @@ static const VMStateDescription vmstate_pl061 = {
}
};
+static uint8_t pl061_floating(PL061State *s)
+{
+ /*
+ * Return mask of bits which correspond to pins configured as inputs
+ * and which are floating (neither pulled up to 1 nor down to 0).
+ */
+ uint8_t floating;
+
+ if (s->id == pl061_id_luminary) {
+ /*
+ * If both PUR and PDR bits are clear, there is neither a pullup
+ * nor a pulldown in place, and the output truly floats.
+ */
+ floating = ~(s->pur | s->pdr);
+ } else {
+ /* Assume outputs are pulled high. FIXME: this is board dependent. */
+ floating = 0;
+ }
+ return floating & ~s->dir;
+}
+
+static uint8_t pl061_pullups(PL061State *s)
+{
+ /*
+ * Return mask of bits which correspond to pins configured as inputs
+ * and which are pulled up to 1.
+ */
+ uint8_t pullups;
+
+ if (s->id == pl061_id_luminary) {
+ /*
+ * The Luminary variant of the PL061 has an extra registers which
+ * the guest can use to configure whether lines should be pullup
+ * or pulldown.
+ */
+ pullups = s->pur;
+ } else {
+ /* Assume outputs are pulled high. FIXME: this is board dependent. */
+ pullups = 0xff;
+ }
+ return pullups & ~s->dir;
+}
+
static void pl061_update(PL061State *s)
{
uint8_t changed;
uint8_t mask;
uint8_t out;
int i;
-
- trace_pl061_update(DEVICE(s)->canonical_path, s->dir, s->data);
-
- /* Outputs float high. */
- /* FIXME: This is board dependent. */
- out = (s->data & s->dir) | ~s->dir;
+ uint8_t pullups = pl061_pullups(s);
+ uint8_t floating = pl061_floating(s);
+
+ trace_pl061_update(DEVICE(s)->canonical_path, s->dir, s->data,
+ pullups, floating);
+
+ /*
+ * Pins configured as output are driven from the data register;
+ * otherwise if they're pulled up they're 1, and if they're floating
+ * then we give them the same value they had previously, so we don't
+ * report any change to the other end.
+ */
+ out = (s->data & s->dir) | pullups | (s->old_out_data & floating);
changed = s->old_out_data ^ out;
if (changed) {
s->old_out_data = out;