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