blob: d15f0e3556ae943181e04f65b86ed9827f9787e3 [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>
Kyösti Mälkkif1b58b72019-03-01 13:43:02 +020021#include <device/pci_ops.h>
Arthur Heymans7b9c1392017-04-09 20:40:39 +020022#include <device/device.h>
23#include <device/pci.h>
24#include <console/console.h>
Arthur Heymans349e0852017-04-09 20:48:37 +020025#include "i82801jx.h"
Arthur Heymans7b9c1392017-04-09 20:40:39 +020026
Arthur Heymans349e0852017-04-09 20:48:37 +020027typedef struct southbridge_intel_i82801jx_config config_t;
Arthur Heymans7b9c1392017-04-09 20:40:39 +020028
Elyes HAOUAS1a8c1df2018-05-13 13:36:44 +020029static void i82801jx_enable_device(struct device *dev)
Arthur Heymans7b9c1392017-04-09 20:40:39 +020030{
31 u32 reg32;
32
33 /* Enable SERR */
34 reg32 = pci_read_config32(dev, PCI_COMMAND);
35 reg32 |= PCI_COMMAND_SERR;
36 pci_write_config32(dev, PCI_COMMAND, reg32);
37}
38
Arthur Heymans349e0852017-04-09 20:48:37 +020039static void i82801jx_early_settings(const config_t *const info)
Arthur Heymans7b9c1392017-04-09 20:40:39 +020040{
41 /* Program FERR# as processor break event indicator. */
Stefan Taunercea31ea2018-08-11 18:45:28 +020042 RCBA32(GCS) |= (1 << 6);
Arthur Heymans7b9c1392017-04-09 20:40:39 +020043 /* BIOS must program... */
Stefan Taunercea31ea2018-08-11 18:45:28 +020044 RCBA32(RCBA_CIR8) = (RCBA32(RCBA_CIR8) & ~(0x3 << 0)) | (0x2 << 0);
45 RCBA32(RCBA_FD) |= (1 << 0);
46 RCBA32(RCBA_CIR9) = (RCBA32(RCBA_CIR9) & ~(0x3 << 26)) | (0x2 << 26);
47 RCBA32(RCBA_CIR7) = (RCBA32(RCBA_CIR7) & ~(0xf << 16)) | (0x5 << 16);
48 RCBA32(RCBA_CIR13) = (RCBA32(RCBA_CIR13) & ~(0xf << 16)) | (0x5 << 16);
Stefan Tauner97c80892018-08-15 08:06:13 +020049 /* RCBA32(RCBA_CIR5) |= (1 << 0); cf. Specification Update */
Stefan Taunercea31ea2018-08-11 18:45:28 +020050 RCBA32(RCBA_CIR10) |= (3 << 16);
Arthur Heymans7b9c1392017-04-09 20:40:39 +020051}
52
Arthur Heymans349e0852017-04-09 20:48:37 +020053static void i82801jx_pcie_init(const config_t *const info)
Arthur Heymans7b9c1392017-04-09 20:40:39 +020054{
Elyes HAOUAS1a8c1df2018-05-13 13:36:44 +020055 struct device *pciePort[6];
Arthur Heymans7b9c1392017-04-09 20:40:39 +020056 int i, slot_number = 1; /* Reserve slot number 0 for nb's PEG. */
57 u32 reg32;
58
59 /* PCIe - BIOS must program... */
60 for (i = 0; i < 6; ++i) {
Kyösti Mälkkic70eed12018-05-22 02:18:00 +030061 pciePort[i] = pcidev_on_root(0x1c, i);
Arthur Heymans7b9c1392017-04-09 20:40:39 +020062 if (!pciePort[i]) {
63 printk(BIOS_EMERG, "PCIe port 00:1c.%x", i);
64 die(" is not listed in devicetree.\n");
65 }
66 reg32 = pci_read_config32(pciePort[i], 0x300);
67 pci_write_config32(pciePort[i], 0x300, reg32 | (1 << 21));
68 pci_write_config8(pciePort[i], 0x324, 0x40);
69 }
70
Kyösti Mälkkic70eed12018-05-22 02:18:00 +030071 if (LPC_IS_MOBILE(pcidev_on_root(0x1f, 0))) {
Arthur Heymans7b9c1392017-04-09 20:40:39 +020072 for (i = 0; i < 6; ++i) {
73 if (pciePort[i]->enabled) {
74 reg32 = pci_read_config32(pciePort[i], 0xe8);
75 reg32 |= 1;
76 pci_write_config32(pciePort[i], 0xe8, reg32);
77 }
78 }
79 }
80
81 for (i = 5; (i >= 0) && !pciePort[i]->enabled; --i) {
82 /* Only for the top disabled ports. */
83 reg32 = pci_read_config32(pciePort[i], 0x300);
84 reg32 |= 0x3 << 16;
85 pci_write_config32(pciePort[i], 0x300, reg32);
86 }
87
88 /* Set slot implemented, slot number and slot power limits. */
89 for (i = 0; i < 6; ++i) {
Elyes HAOUAS1a8c1df2018-05-13 13:36:44 +020090 struct device *const dev = pciePort[i];
Arthur Heymans7b9c1392017-04-09 20:40:39 +020091 u32 xcap = pci_read_config32(dev, D28Fx_XCAP);
92 if (info->pcie_slot_implemented & (1 << i))
93 xcap |= PCI_EXP_FLAGS_SLOT;
94 else
95 xcap &= ~PCI_EXP_FLAGS_SLOT;
96 pci_write_config32(dev, D28Fx_XCAP, xcap);
97
98 if (info->pcie_slot_implemented & (1 << i)) {
99 u32 slcap = pci_read_config32(dev, D28Fx_SLCAP);
100 slcap &= ~(0x1fff << 19);
101 slcap |= (slot_number++ << 19);
102 slcap &= ~(0x0003 << 16);
103 slcap |= (info->pcie_power_limits[i].scale << 16);
104 slcap &= ~(0x00ff << 7);
105 slcap |= (info->pcie_power_limits[i].value << 7);
106 pci_write_config32(dev, D28Fx_SLCAP, slcap);
107 }
108 }
109
110 /* Lock R/WO ASPM support bits. */
111 for (i = 0; i < 6; ++i) {
112 reg32 = pci_read_config32(pciePort[i], 0x4c);
113 pci_write_config32(pciePort[i], 0x4c, reg32);
114 }
115}
116
Arthur Heymans349e0852017-04-09 20:48:37 +0200117static void i82801jx_ehci_init(void)
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200118{
Kyösti Mälkkic70eed12018-05-22 02:18:00 +0300119 struct device *const pciEHCI1 = pcidev_on_root(0x1d, 7);
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200120 if (!pciEHCI1)
121 die("EHCI controller (00:1d.7) not listed in devicetree.\n");
Kyösti Mälkkic70eed12018-05-22 02:18:00 +0300122 struct device *const pciEHCI2 = pcidev_on_root(0x1a, 7);
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200123 if (!pciEHCI2)
124 die("EHCI controller (00:1a.7) not listed in devicetree.\n");
125
126 u32 reg32;
127
128 /* TODO: Maybe we have to save and
129 restore these settings across S3. */
130 reg32 = pci_read_config32(pciEHCI1, 0xfc);
131 pci_write_config32(pciEHCI1, 0xfc, (reg32 & ~(3 << 2)) |
132 (1 << 29) | (1 << 17) | (2 << 2));
133 reg32 = pci_read_config32(pciEHCI2, 0xfc);
134 pci_write_config32(pciEHCI2, 0xfc, (reg32 & ~(3 << 2)) |
135 (1 << 29) | (1 << 17) | (2 << 2));
136}
137
Arthur Heymans349e0852017-04-09 20:48:37 +0200138static int i82801jx_function_disabled(const unsigned int devfn)
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200139{
Kyösti Mälkkie7377552018-06-21 16:20:55 +0300140 struct device *const dev = pcidev_path_on_root(devfn);
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200141 if (!dev) {
142 printk(BIOS_EMERG,
143 "PCI device 00:%x.%x",
144 PCI_SLOT(devfn), PCI_FUNC(devfn));
145 die(" is not listed in devicetree.\n");
146 }
147 return !dev->enabled;
148}
149
Arthur Heymans349e0852017-04-09 20:48:37 +0200150static void i82801jx_hide_functions(void)
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200151{
152 int i;
153 u32 reg32;
154
155 /* FIXME: This works pretty good if the devicetree is consistent. But
156 some functions have to be disabled in right order and/or have
157 other constraints. */
158
Arthur Heymans349e0852017-04-09 20:48:37 +0200159 if (i82801jx_function_disabled(PCI_DEVFN(0x19, 0)))
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200160 RCBA32(RCBA_BUC) |= BUC_LAND;
161
162 reg32 = RCBA32(RCBA_FD);
163 struct {
164 int devfn;
165 u32 mask;
166 } functions[] = {
167 { PCI_DEVFN(0x1a, 0), FD_U4D }, /* UHCI #4 */
168 { PCI_DEVFN(0x1a, 1), FD_U5D }, /* UHCI #5 */
169 { PCI_DEVFN(0x1a, 2), FD_U6D }, /* UHCI #6 */
170 { PCI_DEVFN(0x1a, 7), FD_EHCI2D }, /* EHCI #2 */
171 { PCI_DEVFN(0x1b, 0), FD_HDAD }, /* HD Audio */
172 { PCI_DEVFN(0x1c, 0), FD_PE1D }, /* PCIe #1 */
173 { PCI_DEVFN(0x1c, 1), FD_PE2D }, /* PCIe #2 */
174 { PCI_DEVFN(0x1c, 2), FD_PE3D }, /* PCIe #3 */
175 { PCI_DEVFN(0x1c, 3), FD_PE4D }, /* PCIe #4 */
176 { PCI_DEVFN(0x1c, 4), FD_PE5D }, /* PCIe #5 */
177 { PCI_DEVFN(0x1c, 5), FD_PE6D }, /* PCIe #6 */
178 { PCI_DEVFN(0x1d, 0), FD_U1D }, /* UHCI #1 */
179 { PCI_DEVFN(0x1d, 1), FD_U2D }, /* UHCI #2 */
180 { PCI_DEVFN(0x1d, 2), FD_U3D }, /* UHCI #3 */
181 { PCI_DEVFN(0x1d, 7), FD_EHCI1D }, /* EHCI #1 */
182 { PCI_DEVFN(0x1f, 0), FD_LBD }, /* LPC */
183 { PCI_DEVFN(0x1f, 2), FD_SAD1 }, /* SATA #1 */
184 { PCI_DEVFN(0x1f, 3), FD_SD }, /* SMBus */
185 { PCI_DEVFN(0x1f, 5), FD_SAD2 }, /* SATA #2 */
186 { PCI_DEVFN(0x1f, 6), FD_TTD }, /* Thermal Throttle */
187 };
188 for (i = 0; i < ARRAY_SIZE(functions); ++i) {
Arthur Heymans349e0852017-04-09 20:48:37 +0200189 if (i82801jx_function_disabled(functions[i].devfn))
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200190 reg32 |= functions[i].mask;
191 }
192 RCBA32(RCBA_FD) = reg32;
193 RCBA32(RCBA_FD) |= (1 << 0); /* BIOS must write this... */
194 RCBA32(RCBA_FDSW) |= (1 << 7); /* Lock function-disable? */
195
196 /* Hide PCIe root port PCI functions. RPFN is partially R/WO. */
197 reg32 = RCBA32(RCBA_RPFN);
198 for (i = 0; i < 6; ++i) {
Arthur Heymans349e0852017-04-09 20:48:37 +0200199 if (i82801jx_function_disabled(PCI_DEVFN(0x1c, i)))
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200200 reg32 |= (1 << ((i * 4) + 3));
201 }
202 RCBA32(RCBA_RPFN) = reg32;
203
204 /* Lock R/WO UHCI controller #6 remapping. */
205 RCBA32(RCBA_MAP) = RCBA32(RCBA_MAP);
206}
207
Arthur Heymans349e0852017-04-09 20:48:37 +0200208static void i82801jx_init(void *chip_info)
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200209{
210 const config_t *const info = (config_t *)chip_info;
211
Arthur Heymans349e0852017-04-09 20:48:37 +0200212 printk(BIOS_DEBUG, "Initializing i82801jx southbridge...\n");
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200213
Arthur Heymans349e0852017-04-09 20:48:37 +0200214 i82801jx_early_settings(info);
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200215
216 /* PCI Express setup. */
Arthur Heymans349e0852017-04-09 20:48:37 +0200217 i82801jx_pcie_init(info);
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200218
219 /* EHCI configuration. */
Arthur Heymans349e0852017-04-09 20:48:37 +0200220 i82801jx_ehci_init();
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200221
222 /* Now hide internal functions. We can't access them after this. */
Arthur Heymans349e0852017-04-09 20:48:37 +0200223 i82801jx_hide_functions();
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200224
225 /* Reset watchdog timer. */
Martin Rothb137c132017-07-21 10:23:57 -0600226#if !IS_ENABLED(CONFIG_HAVE_SMI_HANDLER)
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200227 outw(0x0008, DEFAULT_TCOBASE + 0x12); /* Set higher timer value. */
228#endif
229 outw(0x0000, DEFAULT_TCOBASE + 0x00); /* Update timer. */
230}
231
Arthur Heymans349e0852017-04-09 20:48:37 +0200232struct chip_operations southbridge_intel_i82801jx_ops = {
233 CHIP_NAME("Intel ICH10 (82801Jx) Series Southbridge")
234 .enable_dev = i82801jx_enable_device,
235 .init = i82801jx_init,
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200236};