blob: 0f3a08c9cfe72f92bd05fc054ebad17294656ed6 [file] [log] [blame]
Arthur Heymans7b9c1392017-04-09 20:40:39 +02001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2008-2009 coresystems GmbH
5 * 2012 secunet Security Networks AG
6 * (Written by Nico Huber <nico.huber@secunet.com> for secunet)
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; version 2 of
11 * the License.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 */
18
19#include <stdlib.h>
20#include <arch/io.h>
21#include <device/device.h>
22#include <device/pci.h>
23#include <console/console.h>
24#include "i82801ix.h"
25
26typedef struct southbridge_intel_i82801ix_config config_t;
27
28static void i82801ix_enable_device(device_t dev)
29{
30 u32 reg32;
31
32 /* Enable SERR */
33 reg32 = pci_read_config32(dev, PCI_COMMAND);
34 reg32 |= PCI_COMMAND_SERR;
35 pci_write_config32(dev, PCI_COMMAND, reg32);
36}
37
38static void i82801ix_early_settings(const config_t *const info)
39{
40 /* Program FERR# as processor break event indicator. */
41 RCBA32(0x3410) |= (1 << 6);
42 /* BIOS must program... */
43 RCBA32(0x3430) = (RCBA32(0x3430) & ~(0x3 << 0)) | (0x2 << 0);
44 RCBA32(0x3418) |= (1 << 0);
45 RCBA32(0x350c) = (RCBA32(0x350c) & ~(0x3 << 26)) | (0x2 << 26);
46 RCBA32(0x2034) = (RCBA32(0x2034) & ~(0xf << 16)) | (0x5 << 16);
47 RCBA32(0x0f20) = (RCBA32(0x0f20) & ~(0xf << 16)) | (0x5 << 16);
48 RCBA32(0x1d40) |= (1 << 0);
49 RCBA32(0x352c) |= (3 << 16);
50}
51
52static void i82801ix_pcie_init(const config_t *const info)
53{
54 device_t pciePort[6];
55 int i, slot_number = 1; /* Reserve slot number 0 for nb's PEG. */
56 u32 reg32;
57
58 /* PCIe - BIOS must program... */
59 for (i = 0; i < 6; ++i) {
60 pciePort[i] = dev_find_slot(0, PCI_DEVFN(0x1c, i));
61 if (!pciePort[i]) {
62 printk(BIOS_EMERG, "PCIe port 00:1c.%x", i);
63 die(" is not listed in devicetree.\n");
64 }
65 reg32 = pci_read_config32(pciePort[i], 0x300);
66 pci_write_config32(pciePort[i], 0x300, reg32 | (1 << 21));
67 pci_write_config8(pciePort[i], 0x324, 0x40);
68 }
69
70 if (LPC_IS_MOBILE(dev_find_slot(0, PCI_DEVFN(0x1f, 0)))) {
71 for (i = 0; i < 6; ++i) {
72 if (pciePort[i]->enabled) {
73 reg32 = pci_read_config32(pciePort[i], 0xe8);
74 reg32 |= 1;
75 pci_write_config32(pciePort[i], 0xe8, reg32);
76 }
77 }
78 }
79
80 for (i = 5; (i >= 0) && !pciePort[i]->enabled; --i) {
81 /* Only for the top disabled ports. */
82 reg32 = pci_read_config32(pciePort[i], 0x300);
83 reg32 |= 0x3 << 16;
84 pci_write_config32(pciePort[i], 0x300, reg32);
85 }
86
87 /* Set slot implemented, slot number and slot power limits. */
88 for (i = 0; i < 6; ++i) {
89 const device_t dev = pciePort[i];
90 u32 xcap = pci_read_config32(dev, D28Fx_XCAP);
91 if (info->pcie_slot_implemented & (1 << i))
92 xcap |= PCI_EXP_FLAGS_SLOT;
93 else
94 xcap &= ~PCI_EXP_FLAGS_SLOT;
95 pci_write_config32(dev, D28Fx_XCAP, xcap);
96
97 if (info->pcie_slot_implemented & (1 << i)) {
98 u32 slcap = pci_read_config32(dev, D28Fx_SLCAP);
99 slcap &= ~(0x1fff << 19);
100 slcap |= (slot_number++ << 19);
101 slcap &= ~(0x0003 << 16);
102 slcap |= (info->pcie_power_limits[i].scale << 16);
103 slcap &= ~(0x00ff << 7);
104 slcap |= (info->pcie_power_limits[i].value << 7);
105 pci_write_config32(dev, D28Fx_SLCAP, slcap);
106 }
107 }
108
109 /* Lock R/WO ASPM support bits. */
110 for (i = 0; i < 6; ++i) {
111 reg32 = pci_read_config32(pciePort[i], 0x4c);
112 pci_write_config32(pciePort[i], 0x4c, reg32);
113 }
114}
115
116static void i82801ix_ehci_init(void)
117{
118 const device_t pciEHCI1 = dev_find_slot(0, PCI_DEVFN(0x1d, 7));
119 if (!pciEHCI1)
120 die("EHCI controller (00:1d.7) not listed in devicetree.\n");
121 const device_t pciEHCI2 = dev_find_slot(0, PCI_DEVFN(0x1a, 7));
122 if (!pciEHCI2)
123 die("EHCI controller (00:1a.7) not listed in devicetree.\n");
124
125 u32 reg32;
126
127 /* TODO: Maybe we have to save and
128 restore these settings across S3. */
129 reg32 = pci_read_config32(pciEHCI1, 0xfc);
130 pci_write_config32(pciEHCI1, 0xfc, (reg32 & ~(3 << 2)) |
131 (1 << 29) | (1 << 17) | (2 << 2));
132 reg32 = pci_read_config32(pciEHCI2, 0xfc);
133 pci_write_config32(pciEHCI2, 0xfc, (reg32 & ~(3 << 2)) |
134 (1 << 29) | (1 << 17) | (2 << 2));
135}
136
137static int i82801ix_function_disabled(const unsigned devfn)
138{
139 const struct device *const dev = dev_find_slot(0, devfn);
140 if (!dev) {
141 printk(BIOS_EMERG,
142 "PCI device 00:%x.%x",
143 PCI_SLOT(devfn), PCI_FUNC(devfn));
144 die(" is not listed in devicetree.\n");
145 }
146 return !dev->enabled;
147}
148
149static void i82801ix_hide_functions(void)
150{
151 int i;
152 u32 reg32;
153
154 /* FIXME: This works pretty good if the devicetree is consistent. But
155 some functions have to be disabled in right order and/or have
156 other constraints. */
157
158 if (i82801ix_function_disabled(PCI_DEVFN(0x19, 0)))
159 RCBA32(RCBA_BUC) |= BUC_LAND;
160
161 reg32 = RCBA32(RCBA_FD);
162 struct {
163 int devfn;
164 u32 mask;
165 } functions[] = {
166 { PCI_DEVFN(0x1a, 0), FD_U4D }, /* UHCI #4 */
167 { PCI_DEVFN(0x1a, 1), FD_U5D }, /* UHCI #5 */
168 { PCI_DEVFN(0x1a, 2), FD_U6D }, /* UHCI #6 */
169 { PCI_DEVFN(0x1a, 7), FD_EHCI2D }, /* EHCI #2 */
170 { PCI_DEVFN(0x1b, 0), FD_HDAD }, /* HD Audio */
171 { PCI_DEVFN(0x1c, 0), FD_PE1D }, /* PCIe #1 */
172 { PCI_DEVFN(0x1c, 1), FD_PE2D }, /* PCIe #2 */
173 { PCI_DEVFN(0x1c, 2), FD_PE3D }, /* PCIe #3 */
174 { PCI_DEVFN(0x1c, 3), FD_PE4D }, /* PCIe #4 */
175 { PCI_DEVFN(0x1c, 4), FD_PE5D }, /* PCIe #5 */
176 { PCI_DEVFN(0x1c, 5), FD_PE6D }, /* PCIe #6 */
177 { PCI_DEVFN(0x1d, 0), FD_U1D }, /* UHCI #1 */
178 { PCI_DEVFN(0x1d, 1), FD_U2D }, /* UHCI #2 */
179 { PCI_DEVFN(0x1d, 2), FD_U3D }, /* UHCI #3 */
180 { PCI_DEVFN(0x1d, 7), FD_EHCI1D }, /* EHCI #1 */
181 { PCI_DEVFN(0x1f, 0), FD_LBD }, /* LPC */
182 { PCI_DEVFN(0x1f, 2), FD_SAD1 }, /* SATA #1 */
183 { PCI_DEVFN(0x1f, 3), FD_SD }, /* SMBus */
184 { PCI_DEVFN(0x1f, 5), FD_SAD2 }, /* SATA #2 */
185 { PCI_DEVFN(0x1f, 6), FD_TTD }, /* Thermal Throttle */
186 };
187 for (i = 0; i < ARRAY_SIZE(functions); ++i) {
188 if (i82801ix_function_disabled(functions[i].devfn))
189 reg32 |= functions[i].mask;
190 }
191 RCBA32(RCBA_FD) = reg32;
192 RCBA32(RCBA_FD) |= (1 << 0); /* BIOS must write this... */
193 RCBA32(RCBA_FDSW) |= (1 << 7); /* Lock function-disable? */
194
195 /* Hide PCIe root port PCI functions. RPFN is partially R/WO. */
196 reg32 = RCBA32(RCBA_RPFN);
197 for (i = 0; i < 6; ++i) {
198 if (i82801ix_function_disabled(PCI_DEVFN(0x1c, i)))
199 reg32 |= (1 << ((i * 4) + 3));
200 }
201 RCBA32(RCBA_RPFN) = reg32;
202
203 /* Lock R/WO UHCI controller #6 remapping. */
204 RCBA32(RCBA_MAP) = RCBA32(RCBA_MAP);
205}
206
207static void i82801ix_init(void *chip_info)
208{
209 const config_t *const info = (config_t *)chip_info;
210
211 printk(BIOS_DEBUG, "Initializing i82801ix southbridge...\n");
212
213 i82801ix_early_settings(info);
214
215 /* PCI Express setup. */
216 i82801ix_pcie_init(info);
217
218 /* EHCI configuration. */
219 i82801ix_ehci_init();
220
221 /* Now hide internal functions. We can't access them after this. */
222 i82801ix_hide_functions();
223
224 /* Reset watchdog timer. */
225#if !CONFIG_HAVE_SMI_HANDLER
226 outw(0x0008, DEFAULT_TCOBASE + 0x12); /* Set higher timer value. */
227#endif
228 outw(0x0000, DEFAULT_TCOBASE + 0x00); /* Update timer. */
229}
230
231struct chip_operations southbridge_intel_i82801ix_ops = {
232 CHIP_NAME("Intel ICH9/ICH9-M (82801Ix) Series Southbridge")
233 .enable_dev = i82801ix_enable_device,
234 .init = i82801ix_init,
235};