From 6305020526cee4adcedec1fc64f901a6f0eb6406 Mon Sep 17 00:00:00 2001 From: Andriy Gapon Date: Mon, 7 Sep 2020 06:32:03 +0000 Subject: [PATCH] twsi: some variants clear interrupt flag by writing 0, others by writing 1 Make that distinction more explicit and regular in the code. The difference in behavior is documented in the respective datasheets. Previously, the code handled the distinction by writing the control register multiple times where at least one write was zero and another was one. This can be considered a follow-up to r363021. Reviewed by: manu MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D26308 --- sys/dev/iicbus/twsi/a10_twsi.c | 4 ++++ sys/dev/iicbus/twsi/twsi.c | 16 +++++++++------- sys/dev/iicbus/twsi/twsi.h | 1 + 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/sys/dev/iicbus/twsi/a10_twsi.c b/sys/dev/iicbus/twsi/a10_twsi.c index e70e33fbe229..17f551f27234 100644 --- a/sys/dev/iicbus/twsi/a10_twsi.c +++ b/sys/dev/iicbus/twsi/a10_twsi.c @@ -122,6 +122,10 @@ a10_twsi_attach(device_t dev) sc->reg_soft_reset = TWI_SRST; sc->need_ack = true; + + if (ofw_bus_is_compatible(dev, "allwinner,sun6i-a31-i2c") || + ofw_bus_is_compatible(dev, "allwinner,sun6i-a83t-i2c")) + sc->iflag_w1c = true; return (twsi_attach(dev)); } diff --git a/sys/dev/iicbus/twsi/twsi.c b/sys/dev/iicbus/twsi/twsi.c index 9d1a17f43103..a606c2aefd36 100644 --- a/sys/dev/iicbus/twsi/twsi.c +++ b/sys/dev/iicbus/twsi/twsi.c @@ -147,7 +147,11 @@ twsi_clear_iflg(struct twsi_softc *sc) { DELAY(1000); - twsi_control_clear(sc, TWSI_CONTROL_IFLG); + /* There are two ways of clearing IFLAG. */ + if (sc->iflag_w1c) + twsi_control_set(sc, TWSI_CONTROL_IFLG); + else + twsi_control_clear(sc, TWSI_CONTROL_IFLG); DELAY(1000); } @@ -667,13 +671,11 @@ twsi_intr(void *arg) } debugf(sc->dev, "Refresh reg_control\n"); - /* - * Fix silicon bug on > Allwinner A20 by doing a read and writing - * again to the control register + /* + * Newer Allwinner chips clear IFLG after writing 1 to it. */ - status = TWSI_READ(sc, sc->reg_status); - TWSI_WRITE(sc, sc->reg_control, - sc->control_val | TWSI_CONTROL_IFLG); + TWSI_WRITE(sc, sc->reg_control, sc->control_val | + (sc->iflag_w1c ? TWSI_CONTROL_IFLG : 0)); debugf(sc->dev, "Done with interrupts\n\n"); if (transfer_done == 1) { diff --git a/sys/dev/iicbus/twsi/twsi.h b/sys/dev/iicbus/twsi/twsi.h index a0c4958b47de..631486fb3f0c 100644 --- a/sys/dev/iicbus/twsi/twsi.h +++ b/sys/dev/iicbus/twsi/twsi.h @@ -66,6 +66,7 @@ struct twsi_softc { int error; uint32_t control_val; bool need_ack; + bool iflag_w1c; bus_size_t reg_data; bus_size_t reg_slave_addr;