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