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 954f5b76bfcf7ddd6f6dae27117829d080d8070b..b80236a4eeacdb61c9393d860e1be4236d95c441 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 61d729b82c69b116e374a43ab438a3c1b8a9fb79..ed458c7f056bde079b06f8cde9126da011bb003c 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) {