svn commit: r354315 - head/sys/dev/ichiic
Vladimir Kondratyev
wulf at FreeBSD.org
Sun Nov 3 21:08:27 UTC 2019
Author: wulf
Date: Sun Nov 3 21:08:26 2019
New Revision: 354315
URL: https://svnweb.freebsd.org/changeset/base/354315
Log:
[ig4] Set STOP condition and flush TX/RX FIFOs on error
if controller has not it done for us yet.
Reset controller when transfer abort is failed.
Modified:
head/sys/dev/ichiic/ig4_iic.c
head/sys/dev/ichiic/ig4_reg.h
Modified: head/sys/dev/ichiic/ig4_iic.c
==============================================================================
--- head/sys/dev/ichiic/ig4_iic.c Sun Nov 3 21:07:12 2019 (r354314)
+++ head/sys/dev/ichiic/ig4_iic.c Sun Nov 3 21:08:26 2019 (r354315)
@@ -117,6 +117,7 @@ static const struct ig4_hw ig4iic_hw[] = {
},
};
+static int ig4iic_set_config(ig4iic_softc_t *sc, bool reset);
static void ig4iic_intr(void *cookie);
static void ig4iic_dump(ig4iic_softc_t *sc);
@@ -272,7 +273,7 @@ wait_intr(ig4iic_softc_t *sc, uint32_t intr)
* reset the timeout if we see a change in the transmit
* FIFO level as progress is being made.
*/
- if (intr & IG4_INTR_TX_EMPTY) {
+ if (intr & (IG4_INTR_TX_EMPTY | IG4_INTR_STOP_DET)) {
v = reg_read(sc, IG4_REG_TXFLR) & IG4_FIFOLVL_MASK;
if (txlvl != v) {
txlvl = v;
@@ -369,6 +370,34 @@ ig4iic_xfer_start(ig4iic_softc_t *sc, uint16_t slave,
return (0);
}
+static bool
+ig4iic_xfer_is_started(ig4iic_softc_t *sc)
+{
+ /*
+ * It requires that no IG4_REG_CLR_INTR or IG4_REG_CLR_START/STOP_DET
+ * register reads is issued after START condition.
+ */
+ return ((reg_read(sc, IG4_REG_RAW_INTR_STAT) &
+ (IG4_INTR_START_DET | IG4_INTR_STOP_DET)) == IG4_INTR_START_DET);
+}
+
+static int
+ig4iic_xfer_abort(ig4iic_softc_t *sc)
+{
+ int error;
+
+ /* Request send of STOP condition and flush of TX FIFO */
+ set_controller(sc, IG4_I2C_ABORT | IG4_I2C_ENABLE);
+ /*
+ * Wait for the TX_ABRT interrupt with ABRTSRC_TRANSFER
+ * bit set in TX_ABRT_SOURCE register.
+ */
+ error = wait_intr(sc, IG4_INTR_STOP_DET);
+ set_controller(sc, IG4_I2C_ENABLE);
+
+ return (error == IIC_ESTATUS ? 0 : error);
+}
+
/*
* Amount of unread data before next burst to get better I2C bus utilization.
* 2 bytes is enough in FAST mode. 8 bytes is better in FAST+ and HIGH modes.
@@ -584,8 +613,27 @@ ig4iic_transfer(device_t dev, struct iic_msg *msgs, ui
else
error = ig4iic_write(sc, msgs[i].buf, msgs[i].len,
rpstart, stop);
- if (error != 0)
+
+ if (error != 0) {
+ /*
+ * Send STOP condition if it's not done yet and flush
+ * both FIFOs. Do a controller soft reset if transfer
+ * abort is failed.
+ */
+ if (ig4iic_xfer_is_started(sc) &&
+ ig4iic_xfer_abort(sc) != 0) {
+ device_printf(sc->dev, "Failed to abort "
+ "transfer. Do the controller reset.\n");
+ ig4iic_set_config(sc, true);
+ } else {
+ while (reg_read(sc, IG4_REG_I2C_STA) &
+ IG4_STATUS_RX_NOTEMPTY)
+ reg_read(sc, IG4_REG_DATA_CMD);
+ reg_read(sc, IG4_REG_TX_ABRT_SOURCE);
+ reg_read(sc, IG4_REG_CLR_INTR);
+ }
break;
+ }
rpstart = !stop;
}
@@ -843,7 +891,7 @@ ig4iic_get_config(ig4iic_softc_t *sc)
}
static int
-ig4iic_set_config(ig4iic_softc_t *sc)
+ig4iic_set_config(ig4iic_softc_t *sc, bool reset)
{
uint32_t v;
@@ -851,10 +899,16 @@ ig4iic_set_config(ig4iic_softc_t *sc)
if (sc->version == IG4_SKYLAKE && (v & IG4_RESTORE_REQUIRED) ) {
reg_write(sc, IG4_REG_DEVIDLE_CTRL, IG4_DEVICE_IDLE | IG4_RESTORE_REQUIRED);
reg_write(sc, IG4_REG_DEVIDLE_CTRL, 0);
+ pause("i2crst", 1);
+ reset = true;
+ }
+ if ((sc->version == IG4_HASWELL || sc->version == IG4_ATOM) && reset) {
+ reg_write(sc, IG4_REG_RESETS_HSW, IG4_RESETS_ASSERT_HSW);
+ reg_write(sc, IG4_REG_RESETS_HSW, IG4_RESETS_DEASSERT_HSW);
+ } else if (sc->version == IG4_SKYLAKE && reset) {
reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_ASSERT_SKL);
reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_DEASSERT_SKL);
- DELAY(1000);
}
if (sc->version == IG4_ATOM)
@@ -922,6 +976,9 @@ ig4iic_set_config(ig4iic_softc_t *sc)
IG4_CTL_RESTARTEN |
(sc->cfg.bus_speed & IG4_CTL_SPEED_MASK));
+ /* Force setting of the target address on the next transfer */
+ sc->slave_valid = 0;
+
return (0);
}
@@ -938,7 +995,7 @@ ig4iic_attach(ig4iic_softc_t *sc)
ig4iic_get_config(sc);
- error = ig4iic_set_config(sc);
+ error = ig4iic_set_config(sc, false);
if (error)
goto done;
@@ -949,19 +1006,6 @@ ig4iic_attach(ig4iic_softc_t *sc)
goto done;
}
-#if 0
- /*
- * Don't do this, it blows up the PCI config
- */
- if (sc->version == IG4_HASWELL || sc->version == IG4_ATOM) {
- reg_write(sc, IG4_REG_RESETS_HSW, IG4_RESETS_ASSERT_HSW);
- reg_write(sc, IG4_REG_RESETS_HSW, IG4_RESETS_DEASSERT_HSW);
- } else if (sc->version = IG4_SKYLAKE) {
- reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_ASSERT_SKL);
- reg_write(sc, IG4_REG_RESETS_SKL, IG4_RESETS_DEASSERT_SKL);
- }
-#endif
-
if (set_controller(sc, IG4_I2C_ENABLE)) {
device_printf(sc->dev, "controller error during attach-2\n");
error = ENXIO;
@@ -1051,10 +1095,8 @@ int ig4iic_resume(ig4iic_softc_t *sc)
int error;
sx_xlock(&sc->call_lock);
- if (ig4iic_set_config(sc))
+ if (ig4iic_set_config(sc, sc->version == IG4_SKYLAKE))
device_printf(sc->dev, "controller error during resume\n");
- /* Force setting of the target address on the next transfer */
- sc->slave_valid = 0;
sx_xunlock(&sc->call_lock);
error = bus_generic_resume(sc->dev);
Modified: head/sys/dev/ichiic/ig4_reg.h
==============================================================================
--- head/sys/dev/ichiic/ig4_reg.h Sun Nov 3 21:07:12 2019 (r354314)
+++ head/sys/dev/ichiic/ig4_reg.h Sun Nov 3 21:08:26 2019 (r354315)
@@ -383,7 +383,9 @@
* I2C_EN - (RW) I2C Enable Register 22.2.22
*
* ABORT Software can abort an I2C transfer by setting this
- * bit. Hardware will clear the bit once the STOP has
+ * bit. In response, the controller issues the STOP
+ * condition over the I2C bus, followed by TX FIFO flush.
+ * Hardware will clear the bit once the STOP has
* been detected. This bit can only be set while the
* I2C interface is enabled.
*
More information about the svn-src-head
mailing list