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