• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <console/console.h>
4 #include <delay.h>
5 #include <device/device.h>
6 #include <device/pci.h>
7 #include <device/pci_ops.h>
8 #include <string.h>
9 #include <timer.h>
10 
11 #include "chip.h"
12 #include "pch.h"
13 
14 /* Set bit in function disable register to hide this device */
pch_hide_devfn(unsigned int devfn)15 static void pch_hide_devfn(unsigned int devfn)
16 {
17 	struct device *dev = pcidev_path_on_root(devfn);
18 	if (dev)
19 		dev->hidden = true;
20 
21 	switch (devfn) {
22 	case PCI_DEVFN(20, 0): /* xHCI */
23 		if (pch_silicon_type() == PCH_TYPE_PPT) {
24 			/* on CPT this bit is reserved */
25 			RCBA32_OR(FD, PCH_DISABLE_XHCI);
26 		}
27 		break;
28 	case PCI_DEVFN(22, 0): /* MEI #1 */
29 		RCBA32_OR(FD2, PCH_DISABLE_MEI1);
30 		break;
31 	case PCI_DEVFN(22, 1): /* MEI #2 */
32 		RCBA32_OR(FD2, PCH_DISABLE_MEI2);
33 		break;
34 	case PCI_DEVFN(22, 2): /* IDE-R */
35 		RCBA32_OR(FD2, PCH_DISABLE_IDER);
36 		break;
37 	case PCI_DEVFN(22, 3): /* KT */
38 		RCBA32_OR(FD2, PCH_DISABLE_KT);
39 		break;
40 	case PCI_DEVFN(25, 0): /* Gigabit Ethernet */
41 		/* BUC is already handled in `early_pch.c`. */
42 		break;
43 	case PCI_DEVFN(26, 0): /* EHCI #2 */
44 		RCBA32_OR(FD, PCH_DISABLE_EHCI2);
45 		break;
46 	case PCI_DEVFN(27, 0): /* HD Audio Controller */
47 		RCBA32_OR(FD, PCH_DISABLE_HD_AUDIO);
48 		break;
49 	case PCI_DEVFN(28, 0): /* PCI Express Root Port 1 */
50 	case PCI_DEVFN(28, 1): /* PCI Express Root Port 2 */
51 	case PCI_DEVFN(28, 2): /* PCI Express Root Port 3 */
52 	case PCI_DEVFN(28, 3): /* PCI Express Root Port 4 */
53 	case PCI_DEVFN(28, 4): /* PCI Express Root Port 5 */
54 	case PCI_DEVFN(28, 5): /* PCI Express Root Port 6 */
55 	case PCI_DEVFN(28, 6): /* PCI Express Root Port 7 */
56 	case PCI_DEVFN(28, 7): /* PCI Express Root Port 8 */
57 		RCBA32_OR(FD, PCH_DISABLE_PCIE(PCI_FUNC(devfn)));
58 		break;
59 	case PCI_DEVFN(29, 0): /* EHCI #1 */
60 		RCBA32_OR(FD, PCH_DISABLE_EHCI1);
61 		break;
62 	case PCI_DEVFN(30, 0): /* PCI-to-PCI Bridge */
63 		RCBA32_OR(FD, PCH_DISABLE_P2P);
64 		break;
65 	case PCI_DEVFN(31, 0): /* LPC */
66 		RCBA32_OR(FD, PCH_DISABLE_LPC);
67 		break;
68 	case PCI_DEVFN(31, 2): /* SATA #1 */
69 		RCBA32_OR(FD, PCH_DISABLE_SATA1);
70 		break;
71 	case PCI_DEVFN(31, 3): /* SMBUS */
72 		RCBA32_OR(FD, PCH_DISABLE_SMBUS);
73 		break;
74 	case PCI_DEVFN(31, 5): /* SATA #22 */
75 		RCBA32_OR(FD, PCH_DISABLE_SATA2);
76 		break;
77 	case PCI_DEVFN(31, 6): /* Thermal Subsystem */
78 		RCBA32_OR(FD, PCH_DISABLE_THERMAL);
79 		break;
80 	}
81 }
82 
83 /* Check if any port in set X to X+3 is enabled */
pch_pcie_check_set_enabled(struct device * dev)84 static int pch_pcie_check_set_enabled(struct device *dev)
85 {
86 	struct device *port;
87 	int port_func;
88 	int dev_func = PCI_FUNC(dev->path.pci.devfn);
89 
90 	printk(BIOS_DEBUG, "%s: check set enabled\n", dev_path(dev));
91 
92 	/* Go through static device tree list of devices
93 	 * because enumeration is still in progress */
94 	for (port = all_devices; port; port = port->next) {
95 		/* Only care about PCIe root ports */
96 		if (PCI_SLOT(port->path.pci.devfn) !=
97 		    PCI_SLOT(dev->path.pci.devfn))
98 			continue;
99 
100 		/* Check if port is in range and enabled */
101 		port_func = PCI_FUNC(port->path.pci.devfn);
102 		if (port_func >= dev_func &&
103 		    port_func < (dev_func + 4) &&
104 		    port->enabled)
105 			return 1;
106 	}
107 
108 	/* None of the ports in this set are enabled */
109 	return 0;
110 }
111 
112 /* RPFN is a write-once register so keep a copy until it is written */
113 static u32 new_rpfn;
114 
115 /* Swap function numbers assigned to two PCIe Root Ports */
pch_pcie_function_swap(u8 old_fn,u8 new_fn)116 static void pch_pcie_function_swap(u8 old_fn, u8 new_fn)
117 {
118 	u32 old_rpfn = new_rpfn;
119 
120 	printk(BIOS_DEBUG, "PCH: Remap PCIe function %d to %d\n",
121 	       old_fn, new_fn);
122 
123 	new_rpfn &= ~(RPFN_FNMASK(old_fn) | RPFN_FNMASK(new_fn));
124 
125 	/* Old function set to new function and disabled */
126 	new_rpfn |= RPFN_FNSET(old_fn, RPFN_FNGET(old_rpfn, new_fn));
127 	new_rpfn |= RPFN_FNSET(new_fn, RPFN_FNGET(old_rpfn, old_fn));
128 }
129 
130 /* Update devicetree with new Root Port function number assignment */
pch_pcie_devicetree_update(struct southbridge_intel_bd82x6x_config * config)131 static void pch_pcie_devicetree_update(
132 		struct southbridge_intel_bd82x6x_config *config)
133 {
134 	struct device *dev;
135 
136 	/*
137 	 * hotplug map should also be updated along with their
138 	 * corresponding port
139 	 */
140 	u8 new_hotplug_map[sizeof(config->pcie_hotplug_map)];
141 
142 	/*
143 	 * Slots that didn't move need the hotplug setting copied too,
144 	 * so "new_hotplug_map" is initialized with the values of the old map.
145 	 */
146 	memcpy(new_hotplug_map, config->pcie_hotplug_map,
147 	       sizeof(new_hotplug_map));
148 
149 	/* Update the function numbers in the static devicetree */
150 	for (dev = all_devices; dev; dev = dev->next) {
151 		u8 new_devfn;
152 
153 		/* Only care about PCH PCIe root ports */
154 		if (PCI_SLOT(dev->path.pci.devfn) !=
155 		    PCH_PCIE_DEV_SLOT)
156 			continue;
157 
158 		/* Determine the new devfn for this port */
159 		new_devfn = PCI_DEVFN(PCH_PCIE_DEV_SLOT,
160 			      RPFN_FNGET(new_rpfn,
161 				 PCI_FUNC(dev->path.pci.devfn)));
162 
163 		if (dev->path.pci.devfn != new_devfn) {
164 			printk(BIOS_DEBUG,
165 			       "PCH: PCIe map %02x.%1x -> %02x.%1x\n",
166 			       PCI_SLOT(dev->path.pci.devfn),
167 			       PCI_FUNC(dev->path.pci.devfn),
168 			       PCI_SLOT(new_devfn), PCI_FUNC(new_devfn));
169 
170 			/*
171 			 * Copy the flag to its new position along with
172 			 * the corresponding port
173 			 */
174 			new_hotplug_map[PCI_FUNC(new_devfn)] =
175 				config->pcie_hotplug_map
176 				[PCI_FUNC(dev->path.pci.devfn)];
177 
178 			dev->path.pci.devfn = new_devfn;
179 		}
180 	}
181 
182 	/* Copy the updated map back to its place */
183 	memcpy(config->pcie_hotplug_map, new_hotplug_map,
184 	       sizeof(new_hotplug_map));
185 }
186 
check_device_present(struct device * dev)187 static void check_device_present(struct device *dev)
188 {
189 	struct southbridge_intel_bd82x6x_config *config = dev->chip_info;
190 	struct stopwatch timeout;
191 	bool present, hot_plugable;
192 	uint32_t cap;
193 
194 	/* Set slot implemented. */
195 	cap = pci_find_capability(dev, PCI_CAP_ID_PCIE);
196 	pci_or_config16(dev, cap + PCI_EXP_FLAGS, PCI_EXP_FLAGS_SLOT);
197 
198 	/*
199 	 * By setting the PCI_EXP_FLAGS_SLOT bit in register PCI_EXP_FLAGS the
200 	 * PCI_EXP_SLTSTA_PDS bit will be updated with in band device
201 	 * detection from the PCIe PHY. While this is primarly used for PCIe
202 	 * hot-plug detection, it is more reliable than probing for downstream
203 	 * devices by reading DID/VID PCI registers of such.
204 	 *
205 	 * Usually the PCI_EXP_FLAGS_SLOT isn't set for integrated devices,
206 	 * but to simplify device detection it's set for all ports.
207 	 *
208 	 * It also allows to detect device before PCI enumeration has run.
209 	 */
210 	hot_plugable = config && config->pcie_hotplug_map[PCI_FUNC(dev->path.pci.devfn)];
211 	present = !!(pci_read_config16(dev, cap + PCI_EXP_SLTSTA) & PCI_EXP_SLTSTA_PDS);
212 
213 	printk(BIOS_DEBUG, "%s: %s downstream device\n",
214 	       dev_path(dev), present ? "Found a" : "No");
215 
216 	if (!present && !hot_plugable) {
217 		/* No device present. */
218 		stopwatch_init_usecs_expire(&timeout, 50 * 1000);
219 		pci_or_config32(dev, 0x338, 1 << 26);
220 
221 		while (!stopwatch_expired(&timeout)) {
222 			if ((pci_read_config32(dev, 0x328) & (0x1f << 23)) == 0)
223 				break;
224 			udelay(100);
225 		}
226 		dev->enabled = 0;
227 	} else if (present && !hot_plugable && !dev->enabled) {
228 		/* Port will be disabled, but device present. Disable link. */
229 		pci_or_config32(dev, cap + PCI_EXP_LNKCTL, PCI_EXP_LNKCTL_LD);
230 	}
231 }
232 
233 /* Special handling for PCIe Root Port devices */
pch_pcie_enable(struct device * dev)234 static void pch_pcie_enable(struct device *dev)
235 {
236 	struct southbridge_intel_bd82x6x_config *config = dev->chip_info;
237 
238 	if (!config)
239 		return;
240 
241 	check_device_present(dev);
242 
243 	/*
244 	 * Save a copy of the Root Port Function Number map when
245 	 * starting to walk the list of PCIe Root Ports so it can
246 	 * be updated locally and written out when the last port
247 	 * has been processed.
248 	 */
249 	if (PCI_FUNC(dev->path.pci.devfn) == 0) {
250 		new_rpfn = RCBA32(RPFN);
251 
252 		/*
253 		 * Enable Root Port coalescing if the first port is disabled
254 		 * or the other devices will not be enumerated by the OS.
255 		 */
256 		if (!dev->enabled)
257 			config->pcie_port_coalesce = true;
258 
259 		if (config->pcie_port_coalesce)
260 			printk(BIOS_INFO,
261 			       "PCH: PCIe Root Port coalescing is enabled\n");
262 	}
263 
264 	if (!dev->enabled) {
265 		printk(BIOS_DEBUG, "%s: Disabling device\n", dev_path(dev));
266 
267 		/*
268 		 * PCIE Power Savings for PantherPoint and CougarPoint/B1+
269 		 *
270 		 * If PCIe 0-3 disabled set Function 0 0xE2[0] = 1
271 		 * If PCIe 4-7 disabled set Function 4 0xE2[0] = 1
272 		 *
273 		 * This check is done here instead of PCIe driver
274 		 * because the PCIe driver enable() handler is not
275 		 * called unless the device is enabled.
276 		 */
277 		if ((PCI_FUNC(dev->path.pci.devfn) == 0 ||
278 		     PCI_FUNC(dev->path.pci.devfn) == 4)) {
279 			/* Handle workaround for PPT and CPT/B1+ */
280 			if (pch_silicon_supported(PCH_TYPE_CPT, PCH_STEP_B1) &&
281 			    !pch_pcie_check_set_enabled(dev)) {
282 				pci_or_config8(dev, 0xe2, 1);
283 			}
284 
285 			/*
286 			 * Enable Clock Gating for shared PCIe resources
287 			 * before disabling this particular port.
288 			 */
289 			pci_write_config8(dev, 0xe1, 0x3c);
290 		}
291 
292 		/* Ensure memory, io, and bus master are all disabled */
293 		pci_and_config16(dev, PCI_COMMAND,
294 				 ~(PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO));
295 
296 		/* Do not claim downstream transactions for PCIe ports */
297 		new_rpfn |= RPFN_HIDE(PCI_FUNC(dev->path.pci.devfn));
298 
299 		/* Hide this device if possible */
300 		pch_hide_devfn(dev->path.pci.devfn);
301 	} else {
302 		int fn;
303 
304 		/*
305 		 * Check if there is a lower disabled port to swap with this
306 		 * port in order to maintain linear order starting at zero.
307 		 */
308 		if (config->pcie_port_coalesce) {
309 			for (fn=0; fn < PCI_FUNC(dev->path.pci.devfn); fn++) {
310 				if (!(new_rpfn & RPFN_HIDE(fn)))
311 					continue;
312 
313 				/* Swap places with this function */
314 				pch_pcie_function_swap(
315 					PCI_FUNC(dev->path.pci.devfn), fn);
316 				break;
317 			}
318 		}
319 
320 		/* Enable SERR */
321 		pci_or_config16(dev, PCI_COMMAND, PCI_COMMAND_SERR);
322 	}
323 
324 	/*
325 	 * When processing the last PCIe root port we can now
326 	 * update the Root Port Function Number and Hide register.
327 	 */
328 	if (PCI_FUNC(dev->path.pci.devfn) == 7) {
329 		printk(BIOS_SPEW, "PCH: RPFN 0x%08x -> 0x%08x\n",
330 		       RCBA32(RPFN), new_rpfn);
331 		RCBA32(RPFN) = new_rpfn;
332 
333 		/* Update static devictree with new function numbers */
334 		if (config->pcie_port_coalesce)
335 			pch_pcie_devicetree_update(config);
336 	}
337 }
338 
pch_enable(struct device * dev)339 void pch_enable(struct device *dev)
340 {
341 	/* PCH PCIe Root Ports get special handling */
342 	if (PCI_SLOT(dev->path.pci.devfn) == PCH_PCIE_DEV_SLOT)
343 		return pch_pcie_enable(dev);
344 
345 	if (!dev->enabled) {
346 		printk(BIOS_DEBUG, "%s: Disabling device\n", dev_path(dev));
347 
348 		/* Ensure memory, io, and bus master are all disabled */
349 		pci_and_config16(dev, PCI_COMMAND,
350 				 ~(PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO));
351 
352 		/* Hide this device if possible */
353 		pch_hide_devfn(dev->path.pci.devfn);
354 	} else {
355 		/* Enable SERR */
356 		pci_or_config16(dev, PCI_COMMAND, PCI_COMMAND_SERR);
357 	}
358 }
359 
360 struct chip_operations southbridge_intel_bd82x6x_ops = {
361 	.name = "Intel Series 6/7 (Cougar Point/Panther Point) Southbridge",
362 	.enable_dev = pch_enable,
363 };
364