1 /* 2 * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com> 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 7 #include <common.h> 8 #include <dm.h> 9 #include <dm/device-internal.h> 10 #include <pci.h> 11 #include <asm/io.h> 12 #include <asm/irq.h> 13 #include <asm/post.h> 14 #include <asm/arch/device.h> 15 #include <asm/arch/tnc.h> 16 #include <asm/fsp/fsp_support.h> 17 #include <asm/processor.h> 18 19 static int __maybe_unused disable_igd(void) 20 { 21 struct udevice *igd, *sdvo; 22 int ret; 23 24 ret = dm_pci_bus_find_bdf(TNC_IGD, &igd); 25 if (ret) 26 return ret; 27 if (!igd) 28 return 0; 29 30 ret = dm_pci_bus_find_bdf(TNC_SDVO, &sdvo); 31 if (ret) 32 return ret; 33 if (!sdvo) 34 return 0; 35 36 /* 37 * According to Atom E6xx datasheet, setting VGA Disable (bit17) 38 * of Graphics Controller register (offset 0x50) prevents IGD 39 * (D2:F0) from reporting itself as a VGA display controller 40 * class in the PCI configuration space, and should also prevent 41 * it from responding to VGA legacy memory range and I/O addresses. 42 * 43 * However test result shows that with just VGA Disable bit set and 44 * a PCIe graphics card connected to one of the PCIe controllers on 45 * the E6xx, accessing the VGA legacy space still causes system hang. 46 * After a number of attempts, it turns out besides VGA Disable bit, 47 * the SDVO (D3:F0) device should be disabled to make it work. 48 * 49 * To simplify, use the Function Disable register (offset 0xc4) 50 * to disable both IGD (D2:F0) and SDVO (D3:F0) devices. Now these 51 * two devices will be completely disabled (invisible in the PCI 52 * configuration space) unless a system reset is performed. 53 */ 54 dm_pci_write_config32(igd, IGD_FD, FUNC_DISABLE); 55 dm_pci_write_config32(sdvo, IGD_FD, FUNC_DISABLE); 56 57 /* 58 * After setting the function disable bit, IGD and SDVO devices will 59 * disappear in the PCI configuration space. This however creates an 60 * inconsistent state from a driver model PCI controller point of view, 61 * as these two PCI devices are still attached to its parent's child 62 * device list as maintained by the driver model. Some driver model PCI 63 * APIs like dm_pci_find_class(), are referring to the list to speed up 64 * the finding process instead of re-enumerating the whole PCI bus, so 65 * it gets the stale cached data which is wrong. 66 * 67 * Note x86 PCI enueration normally happens twice, in pre-relocation 68 * phase and post-relocation. One option might be to call disable_igd() 69 * in one of the pre-relocation initialization hooks so that it gets 70 * disabled in the first round, and when it comes to the second round 71 * driver model PCI will construct a correct list. Unfortunately this 72 * does not work as Intel FSP is used on this platform to perform low 73 * level initialization, and fsp_init_phase_pci() is called only once 74 * in the post-relocation phase. If we disable IGD and SDVO devices, 75 * fsp_init_phase_pci() simply hangs and never returns. 76 * 77 * So the only option we have is to manually remove these two devices. 78 */ 79 ret = device_remove(igd, DM_REMOVE_NORMAL); 80 if (ret) 81 return ret; 82 ret = device_unbind(igd); 83 if (ret) 84 return ret; 85 ret = device_remove(sdvo, DM_REMOVE_NORMAL); 86 if (ret) 87 return ret; 88 ret = device_unbind(sdvo); 89 if (ret) 90 return ret; 91 92 return 0; 93 } 94 95 int arch_cpu_init(void) 96 { 97 post_code(POST_CPU_INIT); 98 99 return x86_cpu_init_f(); 100 } 101 102 int arch_early_init_r(void) 103 { 104 int ret = 0; 105 106 #ifdef CONFIG_DISABLE_IGD 107 ret = disable_igd(); 108 #endif 109 110 return ret; 111 } 112