diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nvkm/subdev/pci')
17 files changed, 1572 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/Kbuild new file mode 100644 index 000000000..174bdf995 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/Kbuild @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: MIT +nvkm-y += nvkm/subdev/pci/agp.o +nvkm-y += nvkm/subdev/pci/base.o +nvkm-y += nvkm/subdev/pci/pcie.o +nvkm-y += nvkm/subdev/pci/nv04.o +nvkm-y += nvkm/subdev/pci/nv40.o +nvkm-y += nvkm/subdev/pci/nv46.o +nvkm-y += nvkm/subdev/pci/nv4c.o +nvkm-y += nvkm/subdev/pci/g84.o +nvkm-y += nvkm/subdev/pci/g92.o +nvkm-y += nvkm/subdev/pci/g94.o +nvkm-y += nvkm/subdev/pci/gf100.o +nvkm-y += nvkm/subdev/pci/gf106.o +nvkm-y += nvkm/subdev/pci/gk104.o +nvkm-y += nvkm/subdev/pci/gp100.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/agp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/agp.c new file mode 100644 index 000000000..385a90f91 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/agp.c @@ -0,0 +1,175 @@ +/* + * Copyright 2015 Nouveau Project + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "agp.h" +#ifdef __NVKM_PCI_AGP_H__ +#include <core/option.h> + +struct nvkm_device_agp_quirk { + u16 hostbridge_vendor; + u16 hostbridge_device; + u16 chip_vendor; + u16 chip_device; + int mode; +}; + +static const struct nvkm_device_agp_quirk +nvkm_device_agp_quirks[] = { + /* VIA Apollo PRO133x / GeForce FX 5600 Ultra - fdo#20341 */ + { PCI_VENDOR_ID_VIA, 0x0691, PCI_VENDOR_ID_NVIDIA, 0x0311, 2 }, + /* SiS 761 does not support AGP cards, use PCI mode */ + { PCI_VENDOR_ID_SI, 0x0761, PCI_ANY_ID, PCI_ANY_ID, 0 }, + {}, +}; + +void +nvkm_agp_fini(struct nvkm_pci *pci) +{ + if (pci->agp.acquired) { + agp_backend_release(pci->agp.bridge); + pci->agp.acquired = false; + } +} + +/* Ensure AGP controller is in a consistent state in case we need to + * execute the VBIOS DEVINIT scripts. + */ +void +nvkm_agp_preinit(struct nvkm_pci *pci) +{ + struct nvkm_device *device = pci->subdev.device; + u32 mode = nvkm_pci_rd32(pci, 0x004c); + u32 save[2]; + + /* First of all, disable fast writes, otherwise if it's already + * enabled in the AGP bridge and we disable the card's AGP + * controller we might be locking ourselves out of it. + */ + if ((mode | pci->agp.mode) & PCI_AGP_COMMAND_FW) { + mode = pci->agp.mode & ~PCI_AGP_COMMAND_FW; + agp_enable(pci->agp.bridge, mode); + } + + /* clear busmaster bit, and disable AGP */ + save[0] = nvkm_pci_rd32(pci, 0x0004); + nvkm_pci_wr32(pci, 0x0004, save[0] & ~0x00000004); + nvkm_pci_wr32(pci, 0x004c, 0x00000000); + + /* reset PGRAPH, PFIFO and PTIMER */ + save[1] = nvkm_mask(device, 0x000200, 0x00011100, 0x00000000); + nvkm_mask(device, 0x000200, 0x00011100, save[1]); + + /* and restore busmaster bit (gives effect of resetting AGP) */ + nvkm_pci_wr32(pci, 0x0004, save[0]); +} + +int +nvkm_agp_init(struct nvkm_pci *pci) +{ + if (!agp_backend_acquire(pci->pdev)) { + nvkm_error(&pci->subdev, "failed to acquire agp\n"); + return -ENODEV; + } + + agp_enable(pci->agp.bridge, pci->agp.mode); + pci->agp.acquired = true; + return 0; +} + +void +nvkm_agp_dtor(struct nvkm_pci *pci) +{ + arch_phys_wc_del(pci->agp.mtrr); +} + +void +nvkm_agp_ctor(struct nvkm_pci *pci) +{ + const struct nvkm_device_agp_quirk *quirk = nvkm_device_agp_quirks; + struct nvkm_subdev *subdev = &pci->subdev; + struct nvkm_device *device = subdev->device; + struct agp_kern_info info; + int mode = -1; + +#ifdef __powerpc__ + /* Disable AGP by default on all PowerPC machines for now -- At + * least some UniNorth-2 AGP bridges are known to be broken: + * DMA from the host to the card works just fine, but writeback + * from the card to the host goes straight to memory + * untranslated bypassing that GATT somehow, making them quite + * painful to deal with... + */ + mode = 0; +#endif + mode = nvkm_longopt(device->cfgopt, "NvAGP", mode); + + /* acquire bridge temporarily, so that we can copy its info */ + if (!(pci->agp.bridge = agp_backend_acquire(pci->pdev))) { + nvkm_warn(subdev, "failed to acquire agp\n"); + return; + } + agp_copy_info(pci->agp.bridge, &info); + agp_backend_release(pci->agp.bridge); + + pci->agp.mode = info.mode; + pci->agp.base = info.aper_base; + pci->agp.size = info.aper_size * 1024 * 1024; + pci->agp.cma = info.cant_use_aperture; + pci->agp.mtrr = -1; + + /* determine if bridge + chipset combination needs a workaround */ + while (quirk->hostbridge_vendor) { + if (info.device->vendor == quirk->hostbridge_vendor && + info.device->device == quirk->hostbridge_device && + (quirk->chip_vendor == (u16)PCI_ANY_ID || + pci->pdev->vendor == quirk->chip_vendor) && + (quirk->chip_device == (u16)PCI_ANY_ID || + pci->pdev->device == quirk->chip_device)) { + nvkm_info(subdev, "forcing default agp mode to %dX, " + "use NvAGP=<mode> to override\n", + quirk->mode); + mode = quirk->mode; + break; + } + quirk++; + } + + /* apply quirk / user-specified mode */ + if (mode >= 1) { + if (pci->agp.mode & 0x00000008) + mode /= 4; /* AGPv3 */ + pci->agp.mode &= ~0x00000007; + pci->agp.mode |= (mode & 0x7); + } else + if (mode == 0) { + pci->agp.bridge = NULL; + return; + } + + /* fast writes appear to be broken on nv18, they make the card + * lock up randomly. + */ + if (device->chipset == 0x18) + pci->agp.mode &= ~PCI_AGP_COMMAND_FW; + + pci->agp.mtrr = arch_phys_wc_add(pci->agp.base, pci->agp.size); +} +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/agp.h b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/agp.h new file mode 100644 index 000000000..ad4d3621d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/agp.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: MIT */ +#include "priv.h" +#if defined(CONFIG_AGP) || (defined(CONFIG_AGP_MODULE) && defined(MODULE)) +#ifndef __NVKM_PCI_AGP_H__ +#define __NVKM_PCI_AGP_H__ + +void nvkm_agp_ctor(struct nvkm_pci *); +void nvkm_agp_dtor(struct nvkm_pci *); +void nvkm_agp_preinit(struct nvkm_pci *); +int nvkm_agp_init(struct nvkm_pci *); +void nvkm_agp_fini(struct nvkm_pci *); +#endif +#else +static inline void nvkm_agp_ctor(struct nvkm_pci *pci) {} +static inline void nvkm_agp_dtor(struct nvkm_pci *pci) {} +static inline void nvkm_agp_preinit(struct nvkm_pci *pci) {} +static inline int nvkm_agp_init(struct nvkm_pci *pci) { return -ENOSYS; } +static inline void nvkm_agp_fini(struct nvkm_pci *pci) {} +#endif diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/base.c new file mode 100644 index 000000000..a7d42ea8b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/base.c @@ -0,0 +1,232 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs <bskeggs@redhat.com> + */ +#include "priv.h" +#include "agp.h" + +#include <core/option.h> +#include <core/pci.h> +#include <subdev/mc.h> + +u32 +nvkm_pci_rd32(struct nvkm_pci *pci, u16 addr) +{ + return pci->func->rd32(pci, addr); +} + +void +nvkm_pci_wr08(struct nvkm_pci *pci, u16 addr, u8 data) +{ + pci->func->wr08(pci, addr, data); +} + +void +nvkm_pci_wr32(struct nvkm_pci *pci, u16 addr, u32 data) +{ + pci->func->wr32(pci, addr, data); +} + +u32 +nvkm_pci_mask(struct nvkm_pci *pci, u16 addr, u32 mask, u32 value) +{ + u32 data = pci->func->rd32(pci, addr); + pci->func->wr32(pci, addr, (data & ~mask) | value); + return data; +} + +void +nvkm_pci_rom_shadow(struct nvkm_pci *pci, bool shadow) +{ + u32 data = nvkm_pci_rd32(pci, 0x0050); + if (shadow) + data |= 0x00000001; + else + data &= ~0x00000001; + nvkm_pci_wr32(pci, 0x0050, data); +} + +static irqreturn_t +nvkm_pci_intr(int irq, void *arg) +{ + struct nvkm_pci *pci = arg; + struct nvkm_device *device = pci->subdev.device; + bool handled = false; + + if (pci->irq < 0) + return IRQ_HANDLED; + + nvkm_mc_intr_unarm(device); + if (pci->msi) + pci->func->msi_rearm(pci); + nvkm_mc_intr(device, &handled); + nvkm_mc_intr_rearm(device); + return handled ? IRQ_HANDLED : IRQ_NONE; +} + +static int +nvkm_pci_fini(struct nvkm_subdev *subdev, bool suspend) +{ + struct nvkm_pci *pci = nvkm_pci(subdev); + + if (pci->agp.bridge) + nvkm_agp_fini(pci); + + return 0; +} + +static int +nvkm_pci_preinit(struct nvkm_subdev *subdev) +{ + struct nvkm_pci *pci = nvkm_pci(subdev); + if (pci->agp.bridge) + nvkm_agp_preinit(pci); + return 0; +} + +static int +nvkm_pci_oneinit(struct nvkm_subdev *subdev) +{ + struct nvkm_pci *pci = nvkm_pci(subdev); + struct pci_dev *pdev = pci->pdev; + int ret; + + if (pci_is_pcie(pci->pdev)) { + ret = nvkm_pcie_oneinit(pci); + if (ret) + return ret; + } + + ret = request_irq(pdev->irq, nvkm_pci_intr, IRQF_SHARED, "nvkm", pci); + if (ret) + return ret; + + pci->irq = pdev->irq; + return 0; +} + +static int +nvkm_pci_init(struct nvkm_subdev *subdev) +{ + struct nvkm_pci *pci = nvkm_pci(subdev); + int ret; + + if (pci->agp.bridge) { + ret = nvkm_agp_init(pci); + if (ret) + return ret; + } else if (pci_is_pcie(pci->pdev)) { + nvkm_pcie_init(pci); + } + + if (pci->func->init) + pci->func->init(pci); + + /* Ensure MSI interrupts are armed, for the case where there are + * already interrupts pending (for whatever reason) at load time. + */ + if (pci->msi) + pci->func->msi_rearm(pci); + + return 0; +} + +static void * +nvkm_pci_dtor(struct nvkm_subdev *subdev) +{ + struct nvkm_pci *pci = nvkm_pci(subdev); + + nvkm_agp_dtor(pci); + + if (pci->irq >= 0) { + /* freq_irq() will call the handler, we use pci->irq == -1 + * to signal that it's been torn down and should be a noop. + */ + int irq = pci->irq; + pci->irq = -1; + free_irq(irq, pci); + } + + if (pci->msi) + pci_disable_msi(pci->pdev); + + return nvkm_pci(subdev); +} + +static const struct nvkm_subdev_func +nvkm_pci_func = { + .dtor = nvkm_pci_dtor, + .oneinit = nvkm_pci_oneinit, + .preinit = nvkm_pci_preinit, + .init = nvkm_pci_init, + .fini = nvkm_pci_fini, +}; + +int +nvkm_pci_new_(const struct nvkm_pci_func *func, struct nvkm_device *device, + enum nvkm_subdev_type type, int inst, struct nvkm_pci **ppci) +{ + struct nvkm_pci *pci; + + if (!(pci = *ppci = kzalloc(sizeof(**ppci), GFP_KERNEL))) + return -ENOMEM; + nvkm_subdev_ctor(&nvkm_pci_func, device, type, inst, &pci->subdev); + pci->func = func; + pci->pdev = device->func->pci(device)->pdev; + pci->irq = -1; + pci->pcie.speed = -1; + pci->pcie.width = -1; + + if (device->type == NVKM_DEVICE_AGP) + nvkm_agp_ctor(pci); + + switch (pci->pdev->device & 0x0ff0) { + case 0x00f0: + case 0x02e0: + /* BR02? NFI how these would be handled yet exactly */ + break; + default: + switch (device->chipset) { + case 0xaa: + /* reported broken, nv also disable it */ + break; + default: + pci->msi = true; + break; + } + } + +#ifdef __BIG_ENDIAN + pci->msi = false; +#endif + + pci->msi = nvkm_boolopt(device->cfgopt, "NvMSI", pci->msi); + if (pci->msi && func->msi_rearm) { + pci->msi = pci_enable_msi(pci->pdev) == 0; + if (pci->msi) + nvkm_debug(&pci->subdev, "MSI enabled\n"); + } else { + pci->msi = false; + } + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/g84.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/g84.c new file mode 100644 index 000000000..5b29aaced --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/g84.c @@ -0,0 +1,157 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs <bskeggs@redhat.com> + */ +#include "priv.h" + +#include <core/pci.h> + +static int +g84_pcie_version_supported(struct nvkm_pci *pci) +{ + /* g84 and g86 report wrong information about what they support */ + return 1; +} + +int +g84_pcie_version(struct nvkm_pci *pci) +{ + struct nvkm_device *device = pci->subdev.device; + return (nvkm_rd32(device, 0x00154c) & 0x1) + 1; +} + +void +g84_pcie_set_version(struct nvkm_pci *pci, u8 ver) +{ + struct nvkm_device *device = pci->subdev.device; + nvkm_mask(device, 0x00154c, 0x1, (ver >= 2 ? 0x1 : 0x0)); +} + +static void +g84_pcie_set_cap_speed(struct nvkm_pci *pci, bool full_speed) +{ + struct nvkm_device *device = pci->subdev.device; + nvkm_mask(device, 0x00154c, 0x80, full_speed ? 0x80 : 0x0); +} + +enum nvkm_pcie_speed +g84_pcie_cur_speed(struct nvkm_pci *pci) +{ + u32 reg_v = nvkm_pci_rd32(pci, 0x88) & 0x30000; + switch (reg_v) { + case 0x30000: + return NVKM_PCIE_SPEED_8_0; + case 0x20000: + return NVKM_PCIE_SPEED_5_0; + case 0x10000: + default: + return NVKM_PCIE_SPEED_2_5; + } +} + +enum nvkm_pcie_speed +g84_pcie_max_speed(struct nvkm_pci *pci) +{ + u32 reg_v = nvkm_pci_rd32(pci, 0x460) & 0x3300; + if (reg_v == 0x2200) + return NVKM_PCIE_SPEED_5_0; + return NVKM_PCIE_SPEED_2_5; +} + +void +g84_pcie_set_link_speed(struct nvkm_pci *pci, enum nvkm_pcie_speed speed) +{ + u32 mask_value; + + if (speed == NVKM_PCIE_SPEED_5_0) + mask_value = 0x20; + else + mask_value = 0x10; + + nvkm_pci_mask(pci, 0x460, 0x30, mask_value); + nvkm_pci_mask(pci, 0x460, 0x1, 0x1); +} + +int +g84_pcie_set_link(struct nvkm_pci *pci, enum nvkm_pcie_speed speed, u8 width) +{ + g84_pcie_set_cap_speed(pci, speed == NVKM_PCIE_SPEED_5_0); + g84_pcie_set_link_speed(pci, speed); + return 0; +} + +void +g84_pci_init(struct nvkm_pci *pci) +{ + /* The following only concerns PCIe cards. */ + if (!pci_is_pcie(pci->pdev)) + return; + + /* Tag field is 8-bit long, regardless of EXT_TAG. + * However, if EXT_TAG is disabled, only the lower 5 bits of the tag + * field should be used, limiting the number of request to 32. + * + * Apparently, 0x041c stores some limit on the number of requests + * possible, so if EXT_TAG is disabled, limit that requests number to + * 32 + * + * Fixes fdo#86537 + */ + if (nvkm_pci_rd32(pci, 0x007c) & 0x00000020) + nvkm_pci_mask(pci, 0x0080, 0x00000100, 0x00000100); + else + nvkm_pci_mask(pci, 0x041c, 0x00000060, 0x00000000); +} + +int +g84_pcie_init(struct nvkm_pci *pci) +{ + bool full_speed = g84_pcie_cur_speed(pci) == NVKM_PCIE_SPEED_5_0; + g84_pcie_set_cap_speed(pci, full_speed); + return 0; +} + +static const struct nvkm_pci_func +g84_pci_func = { + .init = g84_pci_init, + .rd32 = nv40_pci_rd32, + .wr08 = nv40_pci_wr08, + .wr32 = nv40_pci_wr32, + .msi_rearm = nv46_pci_msi_rearm, + + .pcie.init = g84_pcie_init, + .pcie.set_link = g84_pcie_set_link, + + .pcie.max_speed = g84_pcie_max_speed, + .pcie.cur_speed = g84_pcie_cur_speed, + + .pcie.set_version = g84_pcie_set_version, + .pcie.version = g84_pcie_version, + .pcie.version_supported = g84_pcie_version_supported, +}; + +int +g84_pci_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pci **ppci) +{ + return nvkm_pci_new_(&g84_pci_func, device, type, inst, ppci); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/g92.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/g92.c new file mode 100644 index 000000000..a9e067400 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/g92.c @@ -0,0 +1,58 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs <bskeggs@redhat.com> + */ +#include "priv.h" + +int +g92_pcie_version_supported(struct nvkm_pci *pci) +{ + if ((nvkm_pci_rd32(pci, 0x460) & 0x200) == 0x200) + return 2; + return 1; +} + +static const struct nvkm_pci_func +g92_pci_func = { + .init = g84_pci_init, + .rd32 = nv40_pci_rd32, + .wr08 = nv40_pci_wr08, + .wr32 = nv40_pci_wr32, + .msi_rearm = nv46_pci_msi_rearm, + + .pcie.init = g84_pcie_init, + .pcie.set_link = g84_pcie_set_link, + + .pcie.max_speed = g84_pcie_max_speed, + .pcie.cur_speed = g84_pcie_cur_speed, + + .pcie.set_version = g84_pcie_set_version, + .pcie.version = g84_pcie_version, + .pcie.version_supported = g92_pcie_version_supported, +}; + +int +g92_pci_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pci **ppci) +{ + return nvkm_pci_new_(&g92_pci_func, device, type, inst, ppci); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/g94.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/g94.c new file mode 100644 index 000000000..7bacd0693 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/g94.c @@ -0,0 +1,50 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs <bskeggs@redhat.com> + */ +#include "priv.h" + +static const struct nvkm_pci_func +g94_pci_func = { + .init = g84_pci_init, + .rd32 = nv40_pci_rd32, + .wr08 = nv40_pci_wr08, + .wr32 = nv40_pci_wr32, + .msi_rearm = nv40_pci_msi_rearm, + + .pcie.init = g84_pcie_init, + .pcie.set_link = g84_pcie_set_link, + + .pcie.max_speed = g84_pcie_max_speed, + .pcie.cur_speed = g84_pcie_cur_speed, + + .pcie.set_version = g84_pcie_set_version, + .pcie.version = g84_pcie_version, + .pcie.version_supported = g92_pcie_version_supported, +}; + +int +g94_pci_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pci **ppci) +{ + return nvkm_pci_new_(&g94_pci_func, device, type, inst, ppci); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/gf100.c new file mode 100644 index 000000000..099906092 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/gf100.c @@ -0,0 +1,103 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs <bskeggs@redhat.com> + */ +#include "priv.h" + +static void +gf100_pci_msi_rearm(struct nvkm_pci *pci) +{ + nvkm_pci_wr08(pci, 0x0704, 0xff); +} + +void +gf100_pcie_set_version(struct nvkm_pci *pci, u8 ver) +{ + struct nvkm_device *device = pci->subdev.device; + nvkm_mask(device, 0x02241c, 0x1, ver > 1 ? 1 : 0); +} + +int +gf100_pcie_version(struct nvkm_pci *pci) +{ + struct nvkm_device *device = pci->subdev.device; + return (nvkm_rd32(device, 0x02241c) & 0x1) + 1; +} + +void +gf100_pcie_set_cap_speed(struct nvkm_pci *pci, bool full_speed) +{ + struct nvkm_device *device = pci->subdev.device; + nvkm_mask(device, 0x02241c, 0x80, full_speed ? 0x80 : 0x0); +} + +int +gf100_pcie_cap_speed(struct nvkm_pci *pci) +{ + struct nvkm_device *device = pci->subdev.device; + u8 punits_pci_cap_speed = nvkm_rd32(device, 0x02241c) & 0x80; + if (punits_pci_cap_speed == 0x80) + return 1; + return 0; +} + +int +gf100_pcie_init(struct nvkm_pci *pci) +{ + bool full_speed = g84_pcie_cur_speed(pci) == NVKM_PCIE_SPEED_5_0; + gf100_pcie_set_cap_speed(pci, full_speed); + return 0; +} + +int +gf100_pcie_set_link(struct nvkm_pci *pci, enum nvkm_pcie_speed speed, u8 width) +{ + gf100_pcie_set_cap_speed(pci, speed == NVKM_PCIE_SPEED_5_0); + g84_pcie_set_link_speed(pci, speed); + return 0; +} + +static const struct nvkm_pci_func +gf100_pci_func = { + .init = g84_pci_init, + .rd32 = nv40_pci_rd32, + .wr08 = nv40_pci_wr08, + .wr32 = nv40_pci_wr32, + .msi_rearm = gf100_pci_msi_rearm, + + .pcie.init = gf100_pcie_init, + .pcie.set_link = gf100_pcie_set_link, + + .pcie.max_speed = g84_pcie_max_speed, + .pcie.cur_speed = g84_pcie_cur_speed, + + .pcie.set_version = gf100_pcie_set_version, + .pcie.version = gf100_pcie_version, + .pcie.version_supported = g92_pcie_version_supported, +}; + +int +gf100_pci_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pci **ppci) +{ + return nvkm_pci_new_(&gf100_pci_func, device, type, inst, ppci); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/gf106.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/gf106.c new file mode 100644 index 000000000..bcde609ba --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/gf106.c @@ -0,0 +1,50 @@ +/* + * Copyright 2015 Karol Herbst <nouveau@karolherbst.de> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Karol Herbst <nouveau@karolherbst.de> + */ +#include "priv.h" + +static const struct nvkm_pci_func +gf106_pci_func = { + .init = g84_pci_init, + .rd32 = nv40_pci_rd32, + .wr08 = nv40_pci_wr08, + .wr32 = nv40_pci_wr32, + .msi_rearm = nv40_pci_msi_rearm, + + .pcie.init = gf100_pcie_init, + .pcie.set_link = gf100_pcie_set_link, + + .pcie.max_speed = g84_pcie_max_speed, + .pcie.cur_speed = g84_pcie_cur_speed, + + .pcie.set_version = gf100_pcie_set_version, + .pcie.version = gf100_pcie_version, + .pcie.version_supported = g92_pcie_version_supported, +}; + +int +gf106_pci_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pci **ppci) +{ + return nvkm_pci_new_(&gf106_pci_func, device, type, inst, ppci); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/gk104.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/gk104.c new file mode 100644 index 000000000..6be87ecff --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/gk104.c @@ -0,0 +1,229 @@ +/* + * Copyright 2015 Karol Herbst <nouveau@karolherbst.de> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Karol Herbst <nouveau@karolherbst.de> + */ +#include "priv.h" + +static int +gk104_pcie_version_supported(struct nvkm_pci *pci) +{ + return (nvkm_rd32(pci->subdev.device, 0x8c1c0) & 0x4) == 0x4 ? 2 : 1; +} + +static void +gk104_pcie_set_cap_speed(struct nvkm_pci *pci, enum nvkm_pcie_speed speed) +{ + struct nvkm_device *device = pci->subdev.device; + + switch (speed) { + case NVKM_PCIE_SPEED_2_5: + gf100_pcie_set_cap_speed(pci, false); + nvkm_mask(device, 0x8c1c0, 0x30000, 0x10000); + break; + case NVKM_PCIE_SPEED_5_0: + gf100_pcie_set_cap_speed(pci, true); + nvkm_mask(device, 0x8c1c0, 0x30000, 0x20000); + break; + case NVKM_PCIE_SPEED_8_0: + gf100_pcie_set_cap_speed(pci, true); + nvkm_mask(device, 0x8c1c0, 0x30000, 0x30000); + break; + } +} + +static enum nvkm_pcie_speed +gk104_pcie_cap_speed(struct nvkm_pci *pci) +{ + int speed = gf100_pcie_cap_speed(pci); + + if (speed == 0) + return NVKM_PCIE_SPEED_2_5; + + if (speed >= 1) { + int speed2 = nvkm_rd32(pci->subdev.device, 0x8c1c0) & 0x30000; + switch (speed2) { + case 0x00000: + case 0x10000: + return NVKM_PCIE_SPEED_2_5; + case 0x20000: + return NVKM_PCIE_SPEED_5_0; + case 0x30000: + return NVKM_PCIE_SPEED_8_0; + } + } + + return -EINVAL; +} + +static void +gk104_pcie_set_lnkctl_speed(struct nvkm_pci *pci, enum nvkm_pcie_speed speed) +{ + u8 reg_v = 0; + switch (speed) { + case NVKM_PCIE_SPEED_2_5: + reg_v = 1; + break; + case NVKM_PCIE_SPEED_5_0: + reg_v = 2; + break; + case NVKM_PCIE_SPEED_8_0: + reg_v = 3; + break; + } + nvkm_pci_mask(pci, 0xa8, 0x3, reg_v); +} + +static enum nvkm_pcie_speed +gk104_pcie_lnkctl_speed(struct nvkm_pci *pci) +{ + u8 reg_v = nvkm_pci_rd32(pci, 0xa8) & 0x3; + switch (reg_v) { + case 0: + case 1: + return NVKM_PCIE_SPEED_2_5; + case 2: + return NVKM_PCIE_SPEED_5_0; + case 3: + return NVKM_PCIE_SPEED_8_0; + } + return -1; +} + +static enum nvkm_pcie_speed +gk104_pcie_max_speed(struct nvkm_pci *pci) +{ + u32 max_speed = nvkm_rd32(pci->subdev.device, 0x8c1c0) & 0x300000; + switch (max_speed) { + case 0x000000: + return NVKM_PCIE_SPEED_8_0; + case 0x100000: + return NVKM_PCIE_SPEED_5_0; + case 0x200000: + return NVKM_PCIE_SPEED_2_5; + } + return NVKM_PCIE_SPEED_2_5; +} + +static void +gk104_pcie_set_link_speed(struct nvkm_pci *pci, enum nvkm_pcie_speed speed) +{ + struct nvkm_device *device = pci->subdev.device; + u32 mask_value; + + switch (speed) { + case NVKM_PCIE_SPEED_8_0: + mask_value = 0x00000; + break; + case NVKM_PCIE_SPEED_5_0: + mask_value = 0x40000; + break; + case NVKM_PCIE_SPEED_2_5: + default: + mask_value = 0x80000; + break; + } + + nvkm_mask(device, 0x8c040, 0xc0000, mask_value); + nvkm_mask(device, 0x8c040, 0x1, 0x1); +} + +static int +gk104_pcie_init(struct nvkm_pci * pci) +{ + enum nvkm_pcie_speed lnkctl_speed, max_speed, cap_speed; + struct nvkm_subdev *subdev = &pci->subdev; + + if (gf100_pcie_version(pci) < 2) + return 0; + + lnkctl_speed = gk104_pcie_lnkctl_speed(pci); + max_speed = gk104_pcie_max_speed(pci); + cap_speed = gk104_pcie_cap_speed(pci); + + if (cap_speed != max_speed) { + nvkm_trace(subdev, "adjusting cap to max speed\n"); + gk104_pcie_set_cap_speed(pci, max_speed); + cap_speed = gk104_pcie_cap_speed(pci); + if (cap_speed != max_speed) + nvkm_warn(subdev, "failed to adjust cap speed\n"); + } + + if (lnkctl_speed != max_speed) { + nvkm_debug(subdev, "adjusting lnkctl to max speed\n"); + gk104_pcie_set_lnkctl_speed(pci, max_speed); + lnkctl_speed = gk104_pcie_lnkctl_speed(pci); + if (lnkctl_speed != max_speed) + nvkm_error(subdev, "failed to adjust lnkctl speed\n"); + } + + return 0; +} + +static int +gk104_pcie_set_link(struct nvkm_pci *pci, enum nvkm_pcie_speed speed, u8 width) +{ + struct nvkm_subdev *subdev = &pci->subdev; + enum nvkm_pcie_speed lnk_ctl_speed = gk104_pcie_lnkctl_speed(pci); + enum nvkm_pcie_speed lnk_cap_speed = gk104_pcie_cap_speed(pci); + + if (speed > lnk_cap_speed) { + speed = lnk_cap_speed; + nvkm_warn(subdev, "dropping requested speed due too low cap" + " speed\n"); + } + + if (speed > lnk_ctl_speed) { + speed = lnk_ctl_speed; + nvkm_warn(subdev, "dropping requested speed due too low" + " lnkctl speed\n"); + } + + gk104_pcie_set_link_speed(pci, speed); + return 0; +} + + +static const struct nvkm_pci_func +gk104_pci_func = { + .init = g84_pci_init, + .rd32 = nv40_pci_rd32, + .wr08 = nv40_pci_wr08, + .wr32 = nv40_pci_wr32, + .msi_rearm = nv40_pci_msi_rearm, + + .pcie.init = gk104_pcie_init, + .pcie.set_link = gk104_pcie_set_link, + + .pcie.max_speed = gk104_pcie_max_speed, + .pcie.cur_speed = g84_pcie_cur_speed, + + .pcie.set_version = gf100_pcie_set_version, + .pcie.version = gf100_pcie_version, + .pcie.version_supported = gk104_pcie_version_supported, +}; + +int +gk104_pci_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pci **ppci) +{ + return nvkm_pci_new_(&gk104_pci_func, device, type, inst, ppci); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/gp100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/gp100.c new file mode 100644 index 000000000..a5fafda00 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/gp100.c @@ -0,0 +1,45 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs <bskeggs@redhat.com> + */ +#include "priv.h" + +static void +gp100_pci_msi_rearm(struct nvkm_pci *pci) +{ + nvkm_pci_wr32(pci, 0x0704, 0x00000000); +} + +static const struct nvkm_pci_func +gp100_pci_func = { + .rd32 = nv40_pci_rd32, + .wr08 = nv40_pci_wr08, + .wr32 = nv40_pci_wr32, + .msi_rearm = gp100_pci_msi_rearm, +}; + +int +gp100_pci_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pci **ppci) +{ + return nvkm_pci_new_(&gp100_pci_func, device, type, inst, ppci); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv04.c new file mode 100644 index 000000000..9ab64194b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv04.c @@ -0,0 +1,59 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs <bskeggs@redhat.com> + */ +#include "priv.h" + +static u32 +nv04_pci_rd32(struct nvkm_pci *pci, u16 addr) +{ + struct nvkm_device *device = pci->subdev.device; + return nvkm_rd32(device, 0x001800 + addr); +} + +static void +nv04_pci_wr08(struct nvkm_pci *pci, u16 addr, u8 data) +{ + struct nvkm_device *device = pci->subdev.device; + nvkm_wr08(device, 0x001800 + addr, data); +} + +static void +nv04_pci_wr32(struct nvkm_pci *pci, u16 addr, u32 data) +{ + struct nvkm_device *device = pci->subdev.device; + nvkm_wr32(device, 0x001800 + addr, data); +} + +static const struct nvkm_pci_func +nv04_pci_func = { + .rd32 = nv04_pci_rd32, + .wr08 = nv04_pci_wr08, + .wr32 = nv04_pci_wr32, +}; + +int +nv04_pci_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pci **ppci) +{ + return nvkm_pci_new_(&nv04_pci_func, device, type, inst, ppci); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv40.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv40.c new file mode 100644 index 000000000..6a3c31cf0 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv40.c @@ -0,0 +1,66 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs <bskeggs@redhat.com> + */ +#include "priv.h" + +u32 +nv40_pci_rd32(struct nvkm_pci *pci, u16 addr) +{ + struct nvkm_device *device = pci->subdev.device; + return nvkm_rd32(device, 0x088000 + addr); +} + +void +nv40_pci_wr08(struct nvkm_pci *pci, u16 addr, u8 data) +{ + struct nvkm_device *device = pci->subdev.device; + nvkm_wr08(device, 0x088000 + addr, data); +} + +void +nv40_pci_wr32(struct nvkm_pci *pci, u16 addr, u32 data) +{ + struct nvkm_device *device = pci->subdev.device; + nvkm_wr32(device, 0x088000 + addr, data); +} + +void +nv40_pci_msi_rearm(struct nvkm_pci *pci) +{ + nvkm_pci_wr08(pci, 0x0068, 0xff); +} + +static const struct nvkm_pci_func +nv40_pci_func = { + .rd32 = nv40_pci_rd32, + .wr08 = nv40_pci_wr08, + .wr32 = nv40_pci_wr32, + .msi_rearm = nv40_pci_msi_rearm, +}; + +int +nv40_pci_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pci **ppci) +{ + return nvkm_pci_new_(&nv40_pci_func, device, type, inst, ppci); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv46.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv46.c new file mode 100644 index 000000000..9cad17f17 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv46.c @@ -0,0 +1,52 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs <bskeggs@redhat.com> + */ +#include "priv.h" + +#include <core/pci.h> + +/* MSI re-arm through the PRI appears to be broken on NV46/NV50/G84/G86/G92, + * so we access it via alternate PCI config space mechanisms. + */ +void +nv46_pci_msi_rearm(struct nvkm_pci *pci) +{ + struct nvkm_device *device = pci->subdev.device; + struct pci_dev *pdev = device->func->pci(device)->pdev; + pci_write_config_byte(pdev, 0x68, 0xff); +} + +static const struct nvkm_pci_func +nv46_pci_func = { + .rd32 = nv40_pci_rd32, + .wr08 = nv40_pci_wr08, + .wr32 = nv40_pci_wr32, + .msi_rearm = nv46_pci_msi_rearm, +}; + +int +nv46_pci_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pci **ppci) +{ + return nvkm_pci_new_(&nv46_pci_func, device, type, inst, ppci); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv4c.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv4c.c new file mode 100644 index 000000000..741e34bf3 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/nv4c.c @@ -0,0 +1,38 @@ +/* + * Copyright 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Ben Skeggs <bskeggs@redhat.com> + */ +#include "priv.h" + +static const struct nvkm_pci_func +nv4c_pci_func = { + .rd32 = nv40_pci_rd32, + .wr08 = nv40_pci_wr08, + .wr32 = nv40_pci_wr32, +}; + +int +nv4c_pci_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, + struct nvkm_pci **ppci) +{ + return nvkm_pci_new_(&nv4c_pci_func, device, type, inst, ppci); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/pcie.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/pcie.c new file mode 100644 index 000000000..d71e5db50 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/pcie.c @@ -0,0 +1,165 @@ +/* + * Copyright 2015 Karol Herbst <nouveau@karolherbst.de> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Karol Herbst <git@karolherbst.de> + */ +#include "priv.h" + +static char *nvkm_pcie_speeds[] = { + "2.5GT/s", + "5.0GT/s", + "8.0GT/s", +}; + +static enum nvkm_pcie_speed +nvkm_pcie_speed(enum pci_bus_speed speed) +{ + switch (speed) { + case PCIE_SPEED_2_5GT: + return NVKM_PCIE_SPEED_2_5; + case PCIE_SPEED_5_0GT: + return NVKM_PCIE_SPEED_5_0; + case PCIE_SPEED_8_0GT: + return NVKM_PCIE_SPEED_8_0; + default: + /* XXX 0x16 is 8_0, assume 0x17 will be 16_0 for now */ + if (speed == 0x17) + return NVKM_PCIE_SPEED_8_0; + return -1; + } +} + +static int +nvkm_pcie_get_version(struct nvkm_pci *pci) +{ + if (!pci->func->pcie.version) + return -ENOSYS; + + return pci->func->pcie.version(pci); +} + +static int +nvkm_pcie_get_max_version(struct nvkm_pci *pci) +{ + if (!pci->func->pcie.version_supported) + return -ENOSYS; + + return pci->func->pcie.version_supported(pci); +} + +static int +nvkm_pcie_set_version(struct nvkm_pci *pci, int version) +{ + if (!pci->func->pcie.set_version) + return -ENOSYS; + + nvkm_trace(&pci->subdev, "set to version %i\n", version); + pci->func->pcie.set_version(pci, version); + return nvkm_pcie_get_version(pci); +} + +int +nvkm_pcie_oneinit(struct nvkm_pci *pci) +{ + if (pci->func->pcie.max_speed) + nvkm_debug(&pci->subdev, "pcie max speed: %s\n", + nvkm_pcie_speeds[pci->func->pcie.max_speed(pci)]); + return 0; +} + +int +nvkm_pcie_init(struct nvkm_pci *pci) +{ + struct nvkm_subdev *subdev = &pci->subdev; + int ret; + + /* raise pcie version first */ + ret = nvkm_pcie_get_version(pci); + if (ret > 0) { + int max_version = nvkm_pcie_get_max_version(pci); + if (max_version > 0 && max_version > ret) + ret = nvkm_pcie_set_version(pci, max_version); + + if (ret < max_version) + nvkm_error(subdev, "couldn't raise version: %i\n", ret); + } + + if (pci->func->pcie.init) + pci->func->pcie.init(pci); + + if (pci->pcie.speed != -1) + nvkm_pcie_set_link(pci, pci->pcie.speed, pci->pcie.width); + + return 0; +} + +int +nvkm_pcie_set_link(struct nvkm_pci *pci, enum nvkm_pcie_speed speed, u8 width) +{ + struct nvkm_subdev *subdev = &pci->subdev; + enum nvkm_pcie_speed cur_speed, max_speed; + struct pci_bus *pbus; + int ret; + + if (!pci || !pci_is_pcie(pci->pdev)) + return 0; + pbus = pci->pdev->bus; + + if (!pci->func->pcie.set_link) + return -ENOSYS; + + nvkm_trace(subdev, "requested %s\n", nvkm_pcie_speeds[speed]); + + if (pci->func->pcie.version(pci) < 2) { + nvkm_error(subdev, "setting link failed due to low version\n"); + return -ENODEV; + } + + cur_speed = pci->func->pcie.cur_speed(pci); + max_speed = min(nvkm_pcie_speed(pbus->max_bus_speed), + pci->func->pcie.max_speed(pci)); + + nvkm_trace(subdev, "current speed: %s\n", nvkm_pcie_speeds[cur_speed]); + + if (speed > max_speed) { + nvkm_debug(subdev, "%s not supported by bus or card, dropping" + "requested speed to %s", nvkm_pcie_speeds[speed], + nvkm_pcie_speeds[max_speed]); + speed = max_speed; + } + + pci->pcie.speed = speed; + pci->pcie.width = width; + + if (speed == cur_speed) { + nvkm_debug(subdev, "requested matches current speed\n"); + return speed; + } + + nvkm_debug(subdev, "set link to %s x%i\n", + nvkm_pcie_speeds[speed], width); + + ret = pci->func->pcie.set_link(pci, speed, width); + if (ret < 0) + nvkm_error(subdev, "setting link failed: %i\n", ret); + + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/priv.h new file mode 100644 index 000000000..9b7583532 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/priv.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NVKM_PCI_PRIV_H__ +#define __NVKM_PCI_PRIV_H__ +#define nvkm_pci(p) container_of((p), struct nvkm_pci, subdev) +#include <subdev/pci.h> + +int nvkm_pci_new_(const struct nvkm_pci_func *, struct nvkm_device *, enum nvkm_subdev_type, int, + struct nvkm_pci **); + +struct nvkm_pci_func { + void (*init)(struct nvkm_pci *); + u32 (*rd32)(struct nvkm_pci *, u16 addr); + void (*wr08)(struct nvkm_pci *, u16 addr, u8 data); + void (*wr32)(struct nvkm_pci *, u16 addr, u32 data); + void (*msi_rearm)(struct nvkm_pci *); + + struct { + int (*init)(struct nvkm_pci *); + int (*set_link)(struct nvkm_pci *, enum nvkm_pcie_speed, u8); + + enum nvkm_pcie_speed (*max_speed)(struct nvkm_pci *); + enum nvkm_pcie_speed (*cur_speed)(struct nvkm_pci *); + + void (*set_version)(struct nvkm_pci *, u8); + int (*version)(struct nvkm_pci *); + int (*version_supported)(struct nvkm_pci *); + } pcie; +}; + +u32 nv40_pci_rd32(struct nvkm_pci *, u16); +void nv40_pci_wr08(struct nvkm_pci *, u16, u8); +void nv40_pci_wr32(struct nvkm_pci *, u16, u32); +void nv40_pci_msi_rearm(struct nvkm_pci *); + +void nv46_pci_msi_rearm(struct nvkm_pci *); + +void g84_pci_init(struct nvkm_pci *pci); + +/* pcie functions */ +void g84_pcie_set_version(struct nvkm_pci *, u8); +int g84_pcie_version(struct nvkm_pci *); +void g84_pcie_set_link_speed(struct nvkm_pci *, enum nvkm_pcie_speed); +enum nvkm_pcie_speed g84_pcie_cur_speed(struct nvkm_pci *); +enum nvkm_pcie_speed g84_pcie_max_speed(struct nvkm_pci *); +int g84_pcie_init(struct nvkm_pci *); +int g84_pcie_set_link(struct nvkm_pci *, enum nvkm_pcie_speed, u8); + +int g92_pcie_version_supported(struct nvkm_pci *); + +void gf100_pcie_set_version(struct nvkm_pci *, u8); +int gf100_pcie_version(struct nvkm_pci *); +void gf100_pcie_set_cap_speed(struct nvkm_pci *, bool); +int gf100_pcie_cap_speed(struct nvkm_pci *); +int gf100_pcie_init(struct nvkm_pci *); +int gf100_pcie_set_link(struct nvkm_pci *, enum nvkm_pcie_speed, u8); + +int nvkm_pcie_oneinit(struct nvkm_pci *); +int nvkm_pcie_init(struct nvkm_pci *); +#endif |