diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2015-08-27 10:33:19 +0300 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2015-08-28 05:40:05 +0300 |
commit | 205877f9156daebb975fb46205488da6fdf5b3f5 (patch) | |
tree | 6c8762b1606e09e22a93e392d4c53200a5fdfa4f | |
parent | 2a89359415da2fc1250b4c205de3c384bd781f54 (diff) | |
download | linux-205877f9156daebb975fb46205488da6fdf5b3f5.tar.xz |
drm/nouveau/pmu/gk104: implement a hackish workaround for a hw bug
Only a handful of machines have this enabled by default, where it's been
proven to work. The workaround can be explicitly enabled with a module
option also.
Still waiting on feedback from NVIDIA for a proper idea of exactly what
this fix is doing, and how to implement it properly.
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk104.c | 51 |
1 files changed, 51 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk104.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk104.c index 28fdb8ea9ed8..b744136f5526 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk104.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gk104.c @@ -26,9 +26,36 @@ #include "priv.h" #include "fuc/gf110.fuc4.h" +#include <core/device.h> +#include <core/option.h> +#include <subdev/timer.h> + +static void +magic_(struct nvkm_pmu *pmu, u32 ctrl, int size) +{ + nv_wr32(pmu, 0x00c800, 0x00000000); + nv_wr32(pmu, 0x00c808, 0x00000000); + nv_wr32(pmu, 0x00c800, ctrl); + if (nv_wait(pmu, 0x00c800, 0x40000000, 0x40000000)) { + while (size--) + nv_wr32(pmu, 0x00c804, 0x00000000); + } + nv_wr32(pmu, 0x00c800, 0x00000000); +} + +static void +magic(struct nvkm_pmu *pmu, u32 ctrl) +{ + magic_(pmu, 0x8000a41f | ctrl, 6); + magic_(pmu, 0x80000421 | ctrl, 1); +} + static void gk104_pmu_pgob(struct nvkm_pmu *pmu, bool enable) { + struct nvkm_device *device = nv_device(pmu); + struct nvkm_object *dev = nv_object(device); + nv_mask(pmu, 0x000200, 0x00001000, 0x00000000); nv_rd32(pmu, 0x000200); nv_mask(pmu, 0x000200, 0x08000000, 0x08000000); @@ -48,6 +75,30 @@ gk104_pmu_pgob(struct nvkm_pmu *pmu, bool enable) nv_mask(pmu, 0x000200, 0x08000000, 0x00000000); nv_mask(pmu, 0x000200, 0x00001000, 0x00001000); nv_rd32(pmu, 0x000200); + + if (nv_device_match(dev, 0x11fc, 0x17aa, 0x2211) /* Lenovo W541 */ + || nv_device_match(dev, 0x11fc, 0x17aa, 0x221e) /* Lenovo W541 */ + || nvkm_boolopt(device->cfgopt, "War00C800_0", false)) { + nv_info(pmu, "hw bug workaround enabled\n"); + switch (device->chipset) { + case 0xe4: + magic(pmu, 0x04000000); + magic(pmu, 0x06000000); + magic(pmu, 0x0c000000); + magic(pmu, 0x0e000000); + break; + case 0xe6: + magic(pmu, 0x02000000); + magic(pmu, 0x04000000); + magic(pmu, 0x0a000000); + break; + case 0xe7: + magic(pmu, 0x02000000); + break; + default: + break; + } + } } struct nvkm_oclass * |