blob: b4ffcfaba9e0f6a8d329ff3215bcb144d83b395d [file] [log] [blame]
Angel Pons182dbde2020-04-02 23:49:05 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Arthur Heymans7b9c1392017-04-09 20:40:39 +02002
Arthur Heymans7b9c1392017-04-09 20:40:39 +02003#include <arch/io.h>
Kyösti Mälkkif1b58b72019-03-01 13:43:02 +02004#include <device/pci_ops.h>
Arthur Heymans7b9c1392017-04-09 20:40:39 +02005#include <device/device.h>
6#include <device/pci.h>
7#include <console/console.h>
Kyösti Mälkki12b121c2019-08-18 16:33:39 +03008#include "chip.h"
Arthur Heymans349e0852017-04-09 20:48:37 +02009#include "i82801jx.h"
Arthur Heymans7b9c1392017-04-09 20:40:39 +020010
Arthur Heymans349e0852017-04-09 20:48:37 +020011typedef struct southbridge_intel_i82801jx_config config_t;
Arthur Heymans7b9c1392017-04-09 20:40:39 +020012
Elyes HAOUAS1a8c1df2018-05-13 13:36:44 +020013static void i82801jx_enable_device(struct device *dev)
Arthur Heymans7b9c1392017-04-09 20:40:39 +020014{
Arthur Heymans7b9c1392017-04-09 20:40:39 +020015 /* Enable SERR */
Elyes HAOUASca4ff252020-04-28 10:29:11 +020016 pci_or_config16(dev, PCI_COMMAND, PCI_COMMAND_SERR);
Arthur Heymans7b9c1392017-04-09 20:40:39 +020017}
18
Arthur Heymans349e0852017-04-09 20:48:37 +020019static void i82801jx_early_settings(const config_t *const info)
Arthur Heymans7b9c1392017-04-09 20:40:39 +020020{
21 /* Program FERR# as processor break event indicator. */
Stefan Taunercea31ea2018-08-11 18:45:28 +020022 RCBA32(GCS) |= (1 << 6);
Arthur Heymans7b9c1392017-04-09 20:40:39 +020023 /* BIOS must program... */
Stefan Taunercea31ea2018-08-11 18:45:28 +020024 RCBA32(RCBA_CIR8) = (RCBA32(RCBA_CIR8) & ~(0x3 << 0)) | (0x2 << 0);
25 RCBA32(RCBA_FD) |= (1 << 0);
26 RCBA32(RCBA_CIR9) = (RCBA32(RCBA_CIR9) & ~(0x3 << 26)) | (0x2 << 26);
27 RCBA32(RCBA_CIR7) = (RCBA32(RCBA_CIR7) & ~(0xf << 16)) | (0x5 << 16);
28 RCBA32(RCBA_CIR13) = (RCBA32(RCBA_CIR13) & ~(0xf << 16)) | (0x5 << 16);
Stefan Tauner97c80892018-08-15 08:06:13 +020029 /* RCBA32(RCBA_CIR5) |= (1 << 0); cf. Specification Update */
Stefan Taunercea31ea2018-08-11 18:45:28 +020030 RCBA32(RCBA_CIR10) |= (3 << 16);
Arthur Heymans7b9c1392017-04-09 20:40:39 +020031}
32
Arthur Heymans349e0852017-04-09 20:48:37 +020033static void i82801jx_pcie_init(const config_t *const info)
Arthur Heymans7b9c1392017-04-09 20:40:39 +020034{
Elyes HAOUAS1a8c1df2018-05-13 13:36:44 +020035 struct device *pciePort[6];
Arthur Heymans7b9c1392017-04-09 20:40:39 +020036 int i, slot_number = 1; /* Reserve slot number 0 for nb's PEG. */
37 u32 reg32;
38
39 /* PCIe - BIOS must program... */
40 for (i = 0; i < 6; ++i) {
Kyösti Mälkkic70eed12018-05-22 02:18:00 +030041 pciePort[i] = pcidev_on_root(0x1c, i);
Arthur Heymans7b9c1392017-04-09 20:40:39 +020042 if (!pciePort[i]) {
43 printk(BIOS_EMERG, "PCIe port 00:1c.%x", i);
44 die(" is not listed in devicetree.\n");
45 }
46 reg32 = pci_read_config32(pciePort[i], 0x300);
47 pci_write_config32(pciePort[i], 0x300, reg32 | (1 << 21));
48 pci_write_config8(pciePort[i], 0x324, 0x40);
49 }
50
Kyösti Mälkkic70eed12018-05-22 02:18:00 +030051 if (LPC_IS_MOBILE(pcidev_on_root(0x1f, 0))) {
Arthur Heymans7b9c1392017-04-09 20:40:39 +020052 for (i = 0; i < 6; ++i) {
53 if (pciePort[i]->enabled) {
54 reg32 = pci_read_config32(pciePort[i], 0xe8);
55 reg32 |= 1;
56 pci_write_config32(pciePort[i], 0xe8, reg32);
57 }
58 }
59 }
60
61 for (i = 5; (i >= 0) && !pciePort[i]->enabled; --i) {
62 /* Only for the top disabled ports. */
63 reg32 = pci_read_config32(pciePort[i], 0x300);
64 reg32 |= 0x3 << 16;
65 pci_write_config32(pciePort[i], 0x300, reg32);
66 }
67
68 /* Set slot implemented, slot number and slot power limits. */
69 for (i = 0; i < 6; ++i) {
Elyes HAOUAS1a8c1df2018-05-13 13:36:44 +020070 struct device *const dev = pciePort[i];
Arthur Heymans7b9c1392017-04-09 20:40:39 +020071 u32 xcap = pci_read_config32(dev, D28Fx_XCAP);
72 if (info->pcie_slot_implemented & (1 << i))
73 xcap |= PCI_EXP_FLAGS_SLOT;
74 else
75 xcap &= ~PCI_EXP_FLAGS_SLOT;
76 pci_write_config32(dev, D28Fx_XCAP, xcap);
77
78 if (info->pcie_slot_implemented & (1 << i)) {
79 u32 slcap = pci_read_config32(dev, D28Fx_SLCAP);
80 slcap &= ~(0x1fff << 19);
81 slcap |= (slot_number++ << 19);
82 slcap &= ~(0x0003 << 16);
83 slcap |= (info->pcie_power_limits[i].scale << 16);
84 slcap &= ~(0x00ff << 7);
85 slcap |= (info->pcie_power_limits[i].value << 7);
86 pci_write_config32(dev, D28Fx_SLCAP, slcap);
87 }
88 }
89
90 /* Lock R/WO ASPM support bits. */
91 for (i = 0; i < 6; ++i) {
92 reg32 = pci_read_config32(pciePort[i], 0x4c);
93 pci_write_config32(pciePort[i], 0x4c, reg32);
94 }
95}
96
Arthur Heymans349e0852017-04-09 20:48:37 +020097static void i82801jx_ehci_init(void)
Arthur Heymans7b9c1392017-04-09 20:40:39 +020098{
Kyösti Mälkkic70eed12018-05-22 02:18:00 +030099 struct device *const pciEHCI1 = pcidev_on_root(0x1d, 7);
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200100 if (!pciEHCI1)
101 die("EHCI controller (00:1d.7) not listed in devicetree.\n");
Kyösti Mälkkic70eed12018-05-22 02:18:00 +0300102 struct device *const pciEHCI2 = pcidev_on_root(0x1a, 7);
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200103 if (!pciEHCI2)
104 die("EHCI controller (00:1a.7) not listed in devicetree.\n");
105
106 u32 reg32;
107
108 /* TODO: Maybe we have to save and
109 restore these settings across S3. */
110 reg32 = pci_read_config32(pciEHCI1, 0xfc);
111 pci_write_config32(pciEHCI1, 0xfc, (reg32 & ~(3 << 2)) |
112 (1 << 29) | (1 << 17) | (2 << 2));
113 reg32 = pci_read_config32(pciEHCI2, 0xfc);
114 pci_write_config32(pciEHCI2, 0xfc, (reg32 & ~(3 << 2)) |
115 (1 << 29) | (1 << 17) | (2 << 2));
116}
117
Arthur Heymans349e0852017-04-09 20:48:37 +0200118static int i82801jx_function_disabled(const unsigned int devfn)
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200119{
Kyösti Mälkkie7377552018-06-21 16:20:55 +0300120 struct device *const dev = pcidev_path_on_root(devfn);
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200121 if (!dev) {
122 printk(BIOS_EMERG,
123 "PCI device 00:%x.%x",
124 PCI_SLOT(devfn), PCI_FUNC(devfn));
125 die(" is not listed in devicetree.\n");
126 }
127 return !dev->enabled;
128}
129
Arthur Heymans349e0852017-04-09 20:48:37 +0200130static void i82801jx_hide_functions(void)
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200131{
132 int i;
133 u32 reg32;
134
135 /* FIXME: This works pretty good if the devicetree is consistent. But
136 some functions have to be disabled in right order and/or have
137 other constraints. */
138
Arthur Heymans349e0852017-04-09 20:48:37 +0200139 if (i82801jx_function_disabled(PCI_DEVFN(0x19, 0)))
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200140 RCBA32(RCBA_BUC) |= BUC_LAND;
141
142 reg32 = RCBA32(RCBA_FD);
143 struct {
144 int devfn;
145 u32 mask;
146 } functions[] = {
147 { PCI_DEVFN(0x1a, 0), FD_U4D }, /* UHCI #4 */
148 { PCI_DEVFN(0x1a, 1), FD_U5D }, /* UHCI #5 */
149 { PCI_DEVFN(0x1a, 2), FD_U6D }, /* UHCI #6 */
150 { PCI_DEVFN(0x1a, 7), FD_EHCI2D }, /* EHCI #2 */
151 { PCI_DEVFN(0x1b, 0), FD_HDAD }, /* HD Audio */
152 { PCI_DEVFN(0x1c, 0), FD_PE1D }, /* PCIe #1 */
153 { PCI_DEVFN(0x1c, 1), FD_PE2D }, /* PCIe #2 */
154 { PCI_DEVFN(0x1c, 2), FD_PE3D }, /* PCIe #3 */
155 { PCI_DEVFN(0x1c, 3), FD_PE4D }, /* PCIe #4 */
156 { PCI_DEVFN(0x1c, 4), FD_PE5D }, /* PCIe #5 */
157 { PCI_DEVFN(0x1c, 5), FD_PE6D }, /* PCIe #6 */
158 { PCI_DEVFN(0x1d, 0), FD_U1D }, /* UHCI #1 */
159 { PCI_DEVFN(0x1d, 1), FD_U2D }, /* UHCI #2 */
160 { PCI_DEVFN(0x1d, 2), FD_U3D }, /* UHCI #3 */
161 { PCI_DEVFN(0x1d, 7), FD_EHCI1D }, /* EHCI #1 */
162 { PCI_DEVFN(0x1f, 0), FD_LBD }, /* LPC */
163 { PCI_DEVFN(0x1f, 2), FD_SAD1 }, /* SATA #1 */
164 { PCI_DEVFN(0x1f, 3), FD_SD }, /* SMBus */
165 { PCI_DEVFN(0x1f, 5), FD_SAD2 }, /* SATA #2 */
166 { PCI_DEVFN(0x1f, 6), FD_TTD }, /* Thermal Throttle */
167 };
168 for (i = 0; i < ARRAY_SIZE(functions); ++i) {
Arthur Heymans349e0852017-04-09 20:48:37 +0200169 if (i82801jx_function_disabled(functions[i].devfn))
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200170 reg32 |= functions[i].mask;
171 }
172 RCBA32(RCBA_FD) = reg32;
173 RCBA32(RCBA_FD) |= (1 << 0); /* BIOS must write this... */
174 RCBA32(RCBA_FDSW) |= (1 << 7); /* Lock function-disable? */
175
176 /* Hide PCIe root port PCI functions. RPFN is partially R/WO. */
177 reg32 = RCBA32(RCBA_RPFN);
178 for (i = 0; i < 6; ++i) {
Arthur Heymans349e0852017-04-09 20:48:37 +0200179 if (i82801jx_function_disabled(PCI_DEVFN(0x1c, i)))
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200180 reg32 |= (1 << ((i * 4) + 3));
181 }
182 RCBA32(RCBA_RPFN) = reg32;
183
184 /* Lock R/WO UHCI controller #6 remapping. */
185 RCBA32(RCBA_MAP) = RCBA32(RCBA_MAP);
186}
187
Arthur Heymans349e0852017-04-09 20:48:37 +0200188static void i82801jx_init(void *chip_info)
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200189{
190 const config_t *const info = (config_t *)chip_info;
191
Arthur Heymans349e0852017-04-09 20:48:37 +0200192 printk(BIOS_DEBUG, "Initializing i82801jx southbridge...\n");
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200193
Arthur Heymans349e0852017-04-09 20:48:37 +0200194 i82801jx_early_settings(info);
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200195
196 /* PCI Express setup. */
Arthur Heymans349e0852017-04-09 20:48:37 +0200197 i82801jx_pcie_init(info);
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200198
199 /* EHCI configuration. */
Arthur Heymans349e0852017-04-09 20:48:37 +0200200 i82801jx_ehci_init();
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200201
202 /* Now hide internal functions. We can't access them after this. */
Arthur Heymans349e0852017-04-09 20:48:37 +0200203 i82801jx_hide_functions();
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200204
205 /* Reset watchdog timer. */
Julius Wernercd49cce2019-03-05 16:53:33 -0800206#if !CONFIG(HAVE_SMI_HANDLER)
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200207 outw(0x0008, DEFAULT_TCOBASE + 0x12); /* Set higher timer value. */
208#endif
209 outw(0x0000, DEFAULT_TCOBASE + 0x00); /* Update timer. */
210}
211
Arthur Heymans349e0852017-04-09 20:48:37 +0200212struct chip_operations southbridge_intel_i82801jx_ops = {
213 CHIP_NAME("Intel ICH10 (82801Jx) Series Southbridge")
214 .enable_dev = i82801jx_enable_device,
215 .init = i82801jx_init,
Arthur Heymans7b9c1392017-04-09 20:40:39 +0200216};