1 /* $NetBSD: nouveau_nvkm_subdev_pci_pcie.c,v 1.5 2023/09/30 10:46:45 mrg Exp $ */ 2 3 /* 4 * Copyright 2015 Karol Herbst <nouveau (at) karolherbst.de> 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 * OTHER DEALINGS IN THE SOFTWARE. 23 * 24 * Authors: Karol Herbst <git (at) karolherbst.de> 25 */ 26 #include <sys/cdefs.h> 27 __KERNEL_RCSID(0, "$NetBSD: nouveau_nvkm_subdev_pci_pcie.c,v 1.5 2023/09/30 10:46:45 mrg Exp $"); 28 29 #include "priv.h" 30 31 static const char *nvkm_pcie_speeds[] = { 32 "2.5GT/s", 33 "5.0GT/s", 34 "8.0GT/s", 35 }; 36 37 static enum nvkm_pcie_speed 38 nvkm_pcie_speed(enum pci_bus_speed speed) 39 { 40 switch (speed) { 41 case PCIE_SPEED_2_5GT: 42 return NVKM_PCIE_SPEED_2_5; 43 case PCIE_SPEED_5_0GT: 44 return NVKM_PCIE_SPEED_5_0; 45 case PCIE_SPEED_8_0GT: 46 return NVKM_PCIE_SPEED_8_0; 47 default: 48 /* XXX 0x16 is 8_0, assume 0x17 will be 16_0 for now */ 49 if (speed == 0x17) 50 return NVKM_PCIE_SPEED_8_0; 51 return NVKM_PCIE_SPEED_2_5; 52 } 53 } 54 55 static int 56 nvkm_pcie_get_version(struct nvkm_pci *pci) 57 { 58 if (!pci->func->pcie.version) 59 return -ENOSYS; 60 61 return pci->func->pcie.version(pci); 62 } 63 64 static int 65 nvkm_pcie_get_max_version(struct nvkm_pci *pci) 66 { 67 if (!pci->func->pcie.version_supported) 68 return -ENOSYS; 69 70 return pci->func->pcie.version_supported(pci); 71 } 72 73 static int 74 nvkm_pcie_set_version(struct nvkm_pci *pci, int version) 75 { 76 if (!pci->func->pcie.set_version) 77 return -ENOSYS; 78 79 nvkm_trace(&pci->subdev, "set to version %i\n", version); 80 pci->func->pcie.set_version(pci, version); 81 return nvkm_pcie_get_version(pci); 82 } 83 84 int 85 nvkm_pcie_oneinit(struct nvkm_pci *pci) 86 { 87 if (pci->func->pcie.max_speed) 88 nvkm_debug(&pci->subdev, "pcie max speed: %s\n", 89 nvkm_pcie_speeds[pci->func->pcie.max_speed(pci)]); 90 return 0; 91 } 92 93 int 94 nvkm_pcie_init(struct nvkm_pci *pci) 95 { 96 struct nvkm_subdev *subdev = &pci->subdev; 97 int ret; 98 99 /* raise pcie version first */ 100 ret = nvkm_pcie_get_version(pci); 101 if (ret > 0) { 102 int max_version = nvkm_pcie_get_max_version(pci); 103 if (max_version > 0 && max_version > ret) 104 ret = nvkm_pcie_set_version(pci, max_version); 105 106 if (ret < max_version) 107 nvkm_error(subdev, "couldn't raise version: %i\n", ret); 108 } 109 110 if (pci->func->pcie.init) 111 pci->func->pcie.init(pci); 112 113 if (pci->pcie.speed != -1) 114 nvkm_pcie_set_link(pci, pci->pcie.speed, pci->pcie.width); 115 116 return 0; 117 } 118 119 int 120 nvkm_pcie_set_link(struct nvkm_pci *pci, enum nvkm_pcie_speed speed, u8 width) 121 { 122 struct nvkm_subdev *subdev = &pci->subdev; 123 enum nvkm_pcie_speed cur_speed, max_speed; 124 struct pci_bus *pbus; 125 int ret; 126 127 if (!pci || !pci_is_pcie(pci->pdev)) 128 return 0; 129 pbus = pci->pdev->bus; 130 131 if (!pci->func->pcie.set_link) 132 return -ENOSYS; 133 134 nvkm_trace(subdev, "requested %s\n", nvkm_pcie_speeds[speed]); 135 136 if (pci->func->pcie.version(pci) < 2) { 137 nvkm_error(subdev, "setting link failed due to low version\n"); 138 return -ENODEV; 139 } 140 141 cur_speed = pci->func->pcie.cur_speed(pci); 142 max_speed = min(nvkm_pcie_speed(pbus->max_bus_speed), 143 pci->func->pcie.max_speed(pci)); 144 145 nvkm_trace(subdev, "current speed: %s\n", nvkm_pcie_speeds[cur_speed]); 146 147 if (speed > max_speed) { 148 nvkm_debug(subdev, "%s not supported by bus or card, dropping" 149 "requested speed to %s", nvkm_pcie_speeds[speed], 150 nvkm_pcie_speeds[max_speed]); 151 speed = max_speed; 152 } 153 154 pci->pcie.speed = speed; 155 pci->pcie.width = width; 156 157 if (speed == cur_speed) { 158 nvkm_debug(subdev, "requested matches current speed\n"); 159 return speed; 160 } 161 162 nvkm_debug(subdev, "set link to %s x%i\n", 163 nvkm_pcie_speeds[speed], width); 164 165 ret = pci->func->pcie.set_link(pci, speed, width); 166 if (ret < 0) 167 nvkm_error(subdev, "setting link failed: %i\n", ret); 168 169 return ret; 170 } 171