drm/nouveau/i2c/aux/g94-: retry transactions after hw reports an error
authorBen Skeggs <bskeggs@redhat.com>
Thu, 3 Nov 2016 06:37:33 +0000 (16:37 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Mon, 7 Nov 2016 04:04:30 +0000 (14:04 +1000)
This fixes (works around?) link training failures seen on (at least)
the Lenovo P50's internal panel.

It's also an important fix on the same system for MST support on the
dock.  Sometimes, right after receiving an IRQ from the sink, there's
an error bit (SINKSTAT_ERR) set in the DPAUX registers before we've
even attempted a transaction.

v2. Fixed regression on passive DP->DVI adapters.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxg94.c
drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgm200.c

index 954f5b7..b80236a 100644 (file)
@@ -79,7 +79,7 @@ g94_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry,
        struct g94_i2c_aux *aux = g94_i2c_aux(obj);
        struct nvkm_device *device = aux->base.pad->i2c->subdev.device;
        const u32 base = aux->ch * 0x50;
-       u32 ctrl, stat, timeout, retries;
+       u32 ctrl, stat, timeout, retries = 0;
        u32 xbuf[4] = {};
        int ret, i;
 
@@ -111,7 +111,7 @@ g94_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry,
        nvkm_wr32(device, 0x00e4e0 + base, addr);
 
        /* (maybe) retry transaction a number of times on failure... */
-       for (retries = 0; !ret && retries < 32; retries++) {
+       do {
                /* reset, and delay a while if this is a retry */
                nvkm_wr32(device, 0x00e4e4 + base, 0x80000000 | ctrl);
                nvkm_wr32(device, 0x00e4e4 + base, 0x00000000 | ctrl);
@@ -131,20 +131,20 @@ g94_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry,
                                goto out;
                        }
                } while (ctrl & 0x00010000);
-               ret = 1;
+               ret = 0;
 
                /* read status, and check if transaction completed ok */
                stat = nvkm_mask(device, 0x00e4e8 + base, 0, 0);
                if ((stat & 0x000f0000) == 0x00080000 ||
                    (stat & 0x000f0000) == 0x00020000)
-                       ret = retry ? 0 : 1;
+                       ret = 1;
                if ((stat & 0x00000100))
                        ret = -ETIMEDOUT;
                if ((stat & 0x00000e00))
                        ret = -EIO;
 
                AUX_TRACE(&aux->base, "%02d %08x %08x", retries, ctrl, stat);
-       }
+       } while (ret && retry && retries++ < 32);
 
        if (type & 1) {
                for (i = 0; i < 16; i += 4) {
index 61d729b..ed458c7 100644 (file)
@@ -79,7 +79,7 @@ gm200_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry,
        struct gm200_i2c_aux *aux = gm200_i2c_aux(obj);
        struct nvkm_device *device = aux->base.pad->i2c->subdev.device;
        const u32 base = aux->ch * 0x50;
-       u32 ctrl, stat, timeout, retries;
+       u32 ctrl, stat, timeout, retries = 0;
        u32 xbuf[4] = {};
        int ret, i;
 
@@ -111,7 +111,7 @@ gm200_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry,
        nvkm_wr32(device, 0x00d950 + base, addr);
 
        /* (maybe) retry transaction a number of times on failure... */
-       for (retries = 0; !ret && retries < 32; retries++) {
+       do {
                /* reset, and delay a while if this is a retry */
                nvkm_wr32(device, 0x00d954 + base, 0x80000000 | ctrl);
                nvkm_wr32(device, 0x00d954 + base, 0x00000000 | ctrl);
@@ -131,20 +131,20 @@ gm200_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry,
                                goto out;
                        }
                } while (ctrl & 0x00010000);
-               ret = 1;
+               ret = 0;
 
                /* read status, and check if transaction completed ok */
                stat = nvkm_mask(device, 0x00d958 + base, 0, 0);
                if ((stat & 0x000f0000) == 0x00080000 ||
                    (stat & 0x000f0000) == 0x00020000)
-                       ret = retry ? 0 : 1;
+                       ret = 1;
                if ((stat & 0x00000100))
                        ret = -ETIMEDOUT;
                if ((stat & 0x00000e00))
                        ret = -EIO;
 
                AUX_TRACE(&aux->base, "%02d %08x %08x", retries, ctrl, stat);
-       }
+       } while (ret && retry && retries++ < 32);
 
        if (type & 1) {
                for (i = 0; i < 16; i += 4) {