Angel Pons | 182dbde | 2020-04-02 23:49:05 +0200 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 2 | |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 3 | #include <arch/io.h> |
Kyösti Mälkki | f1b58b7 | 2019-03-01 13:43:02 +0200 | [diff] [blame] | 4 | #include <device/pci_ops.h> |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 5 | #include <device/device.h> |
| 6 | #include <device/pci.h> |
| 7 | #include <console/console.h> |
Kyösti Mälkki | 12b121c | 2019-08-18 16:33:39 +0300 | [diff] [blame] | 8 | #include "chip.h" |
Arthur Heymans | 349e085 | 2017-04-09 20:48:37 +0200 | [diff] [blame] | 9 | #include "i82801jx.h" |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 10 | |
Arthur Heymans | 349e085 | 2017-04-09 20:48:37 +0200 | [diff] [blame] | 11 | typedef struct southbridge_intel_i82801jx_config config_t; |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 12 | |
Elyes HAOUAS | 1a8c1df | 2018-05-13 13:36:44 +0200 | [diff] [blame] | 13 | static void i82801jx_enable_device(struct device *dev) |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 14 | { |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 15 | /* Enable SERR */ |
Elyes HAOUAS | ca4ff25 | 2020-04-28 10:29:11 +0200 | [diff] [blame] | 16 | pci_or_config16(dev, PCI_COMMAND, PCI_COMMAND_SERR); |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 17 | } |
| 18 | |
Arthur Heymans | 349e085 | 2017-04-09 20:48:37 +0200 | [diff] [blame] | 19 | static void i82801jx_early_settings(const config_t *const info) |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 20 | { |
| 21 | /* Program FERR# as processor break event indicator. */ |
Stefan Tauner | cea31ea | 2018-08-11 18:45:28 +0200 | [diff] [blame] | 22 | RCBA32(GCS) |= (1 << 6); |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 23 | /* BIOS must program... */ |
Stefan Tauner | cea31ea | 2018-08-11 18:45:28 +0200 | [diff] [blame] | 24 | 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 Tauner | 97c8089 | 2018-08-15 08:06:13 +0200 | [diff] [blame] | 29 | /* RCBA32(RCBA_CIR5) |= (1 << 0); cf. Specification Update */ |
Stefan Tauner | cea31ea | 2018-08-11 18:45:28 +0200 | [diff] [blame] | 30 | RCBA32(RCBA_CIR10) |= (3 << 16); |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 31 | } |
| 32 | |
Arthur Heymans | 349e085 | 2017-04-09 20:48:37 +0200 | [diff] [blame] | 33 | static void i82801jx_pcie_init(const config_t *const info) |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 34 | { |
Elyes HAOUAS | 1a8c1df | 2018-05-13 13:36:44 +0200 | [diff] [blame] | 35 | struct device *pciePort[6]; |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 36 | int i, slot_number = 1; /* Reserve slot number 0 for nb's PEG. */ |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 37 | |
| 38 | /* PCIe - BIOS must program... */ |
| 39 | for (i = 0; i < 6; ++i) { |
Kyösti Mälkki | c70eed1 | 2018-05-22 02:18:00 +0300 | [diff] [blame] | 40 | pciePort[i] = pcidev_on_root(0x1c, i); |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 41 | if (!pciePort[i]) { |
| 42 | printk(BIOS_EMERG, "PCIe port 00:1c.%x", i); |
| 43 | die(" is not listed in devicetree.\n"); |
| 44 | } |
Angel Pons | 2048cb4 | 2020-06-08 02:09:33 +0200 | [diff] [blame] | 45 | pci_or_config32(pciePort[i], 0x300, 1 << 21); |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 46 | pci_write_config8(pciePort[i], 0x324, 0x40); |
| 47 | } |
| 48 | |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 49 | for (i = 5; (i >= 0) && !pciePort[i]->enabled; --i) { |
| 50 | /* Only for the top disabled ports. */ |
Angel Pons | 2048cb4 | 2020-06-08 02:09:33 +0200 | [diff] [blame] | 51 | pci_or_config32(pciePort[i], 0x300, 0x3 << 16); |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 52 | } |
| 53 | |
| 54 | /* Set slot implemented, slot number and slot power limits. */ |
| 55 | for (i = 0; i < 6; ++i) { |
Elyes HAOUAS | 1a8c1df | 2018-05-13 13:36:44 +0200 | [diff] [blame] | 56 | struct device *const dev = pciePort[i]; |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 57 | u32 xcap = pci_read_config32(dev, D28Fx_XCAP); |
| 58 | if (info->pcie_slot_implemented & (1 << i)) |
| 59 | xcap |= PCI_EXP_FLAGS_SLOT; |
| 60 | else |
| 61 | xcap &= ~PCI_EXP_FLAGS_SLOT; |
| 62 | pci_write_config32(dev, D28Fx_XCAP, xcap); |
| 63 | |
| 64 | if (info->pcie_slot_implemented & (1 << i)) { |
| 65 | u32 slcap = pci_read_config32(dev, D28Fx_SLCAP); |
| 66 | slcap &= ~(0x1fff << 19); |
| 67 | slcap |= (slot_number++ << 19); |
| 68 | slcap &= ~(0x0003 << 16); |
| 69 | slcap |= (info->pcie_power_limits[i].scale << 16); |
| 70 | slcap &= ~(0x00ff << 7); |
| 71 | slcap |= (info->pcie_power_limits[i].value << 7); |
| 72 | pci_write_config32(dev, D28Fx_SLCAP, slcap); |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | /* Lock R/WO ASPM support bits. */ |
Angel Pons | 2048cb4 | 2020-06-08 02:09:33 +0200 | [diff] [blame] | 77 | for (i = 0; i < 6; ++i) |
| 78 | pci_update_config32(pciePort[i], 0x4c, ~0, 0); |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 79 | } |
| 80 | |
Arthur Heymans | 349e085 | 2017-04-09 20:48:37 +0200 | [diff] [blame] | 81 | static void i82801jx_ehci_init(void) |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 82 | { |
Kyösti Mälkki | c70eed1 | 2018-05-22 02:18:00 +0300 | [diff] [blame] | 83 | struct device *const pciEHCI1 = pcidev_on_root(0x1d, 7); |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 84 | if (!pciEHCI1) |
| 85 | die("EHCI controller (00:1d.7) not listed in devicetree.\n"); |
Kyösti Mälkki | c70eed1 | 2018-05-22 02:18:00 +0300 | [diff] [blame] | 86 | struct device *const pciEHCI2 = pcidev_on_root(0x1a, 7); |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 87 | if (!pciEHCI2) |
| 88 | die("EHCI controller (00:1a.7) not listed in devicetree.\n"); |
| 89 | |
| 90 | u32 reg32; |
| 91 | |
| 92 | /* TODO: Maybe we have to save and |
| 93 | restore these settings across S3. */ |
| 94 | reg32 = pci_read_config32(pciEHCI1, 0xfc); |
| 95 | pci_write_config32(pciEHCI1, 0xfc, (reg32 & ~(3 << 2)) | |
| 96 | (1 << 29) | (1 << 17) | (2 << 2)); |
| 97 | reg32 = pci_read_config32(pciEHCI2, 0xfc); |
| 98 | pci_write_config32(pciEHCI2, 0xfc, (reg32 & ~(3 << 2)) | |
| 99 | (1 << 29) | (1 << 17) | (2 << 2)); |
| 100 | } |
| 101 | |
Arthur Heymans | 349e085 | 2017-04-09 20:48:37 +0200 | [diff] [blame] | 102 | static int i82801jx_function_disabled(const unsigned int devfn) |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 103 | { |
Kyösti Mälkki | e737755 | 2018-06-21 16:20:55 +0300 | [diff] [blame] | 104 | struct device *const dev = pcidev_path_on_root(devfn); |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 105 | if (!dev) { |
| 106 | printk(BIOS_EMERG, |
| 107 | "PCI device 00:%x.%x", |
| 108 | PCI_SLOT(devfn), PCI_FUNC(devfn)); |
| 109 | die(" is not listed in devicetree.\n"); |
| 110 | } |
| 111 | return !dev->enabled; |
| 112 | } |
| 113 | |
Arthur Heymans | 349e085 | 2017-04-09 20:48:37 +0200 | [diff] [blame] | 114 | static void i82801jx_hide_functions(void) |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 115 | { |
| 116 | int i; |
| 117 | u32 reg32; |
| 118 | |
| 119 | /* FIXME: This works pretty good if the devicetree is consistent. But |
| 120 | some functions have to be disabled in right order and/or have |
| 121 | other constraints. */ |
| 122 | |
Arthur Heymans | 349e085 | 2017-04-09 20:48:37 +0200 | [diff] [blame] | 123 | if (i82801jx_function_disabled(PCI_DEVFN(0x19, 0))) |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 124 | RCBA32(RCBA_BUC) |= BUC_LAND; |
| 125 | |
| 126 | reg32 = RCBA32(RCBA_FD); |
| 127 | struct { |
| 128 | int devfn; |
| 129 | u32 mask; |
| 130 | } functions[] = { |
| 131 | { PCI_DEVFN(0x1a, 0), FD_U4D }, /* UHCI #4 */ |
| 132 | { PCI_DEVFN(0x1a, 1), FD_U5D }, /* UHCI #5 */ |
| 133 | { PCI_DEVFN(0x1a, 2), FD_U6D }, /* UHCI #6 */ |
| 134 | { PCI_DEVFN(0x1a, 7), FD_EHCI2D }, /* EHCI #2 */ |
| 135 | { PCI_DEVFN(0x1b, 0), FD_HDAD }, /* HD Audio */ |
| 136 | { PCI_DEVFN(0x1c, 0), FD_PE1D }, /* PCIe #1 */ |
| 137 | { PCI_DEVFN(0x1c, 1), FD_PE2D }, /* PCIe #2 */ |
| 138 | { PCI_DEVFN(0x1c, 2), FD_PE3D }, /* PCIe #3 */ |
| 139 | { PCI_DEVFN(0x1c, 3), FD_PE4D }, /* PCIe #4 */ |
| 140 | { PCI_DEVFN(0x1c, 4), FD_PE5D }, /* PCIe #5 */ |
| 141 | { PCI_DEVFN(0x1c, 5), FD_PE6D }, /* PCIe #6 */ |
| 142 | { PCI_DEVFN(0x1d, 0), FD_U1D }, /* UHCI #1 */ |
| 143 | { PCI_DEVFN(0x1d, 1), FD_U2D }, /* UHCI #2 */ |
| 144 | { PCI_DEVFN(0x1d, 2), FD_U3D }, /* UHCI #3 */ |
| 145 | { PCI_DEVFN(0x1d, 7), FD_EHCI1D }, /* EHCI #1 */ |
| 146 | { PCI_DEVFN(0x1f, 0), FD_LBD }, /* LPC */ |
| 147 | { PCI_DEVFN(0x1f, 2), FD_SAD1 }, /* SATA #1 */ |
| 148 | { PCI_DEVFN(0x1f, 3), FD_SD }, /* SMBus */ |
| 149 | { PCI_DEVFN(0x1f, 5), FD_SAD2 }, /* SATA #2 */ |
| 150 | { PCI_DEVFN(0x1f, 6), FD_TTD }, /* Thermal Throttle */ |
| 151 | }; |
| 152 | for (i = 0; i < ARRAY_SIZE(functions); ++i) { |
Arthur Heymans | 349e085 | 2017-04-09 20:48:37 +0200 | [diff] [blame] | 153 | if (i82801jx_function_disabled(functions[i].devfn)) |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 154 | reg32 |= functions[i].mask; |
| 155 | } |
| 156 | RCBA32(RCBA_FD) = reg32; |
| 157 | RCBA32(RCBA_FD) |= (1 << 0); /* BIOS must write this... */ |
| 158 | RCBA32(RCBA_FDSW) |= (1 << 7); /* Lock function-disable? */ |
| 159 | |
| 160 | /* Hide PCIe root port PCI functions. RPFN is partially R/WO. */ |
| 161 | reg32 = RCBA32(RCBA_RPFN); |
| 162 | for (i = 0; i < 6; ++i) { |
Arthur Heymans | 349e085 | 2017-04-09 20:48:37 +0200 | [diff] [blame] | 163 | if (i82801jx_function_disabled(PCI_DEVFN(0x1c, i))) |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 164 | reg32 |= (1 << ((i * 4) + 3)); |
| 165 | } |
| 166 | RCBA32(RCBA_RPFN) = reg32; |
| 167 | |
| 168 | /* Lock R/WO UHCI controller #6 remapping. */ |
| 169 | RCBA32(RCBA_MAP) = RCBA32(RCBA_MAP); |
| 170 | } |
| 171 | |
Arthur Heymans | 349e085 | 2017-04-09 20:48:37 +0200 | [diff] [blame] | 172 | static void i82801jx_init(void *chip_info) |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 173 | { |
| 174 | const config_t *const info = (config_t *)chip_info; |
| 175 | |
Arthur Heymans | 349e085 | 2017-04-09 20:48:37 +0200 | [diff] [blame] | 176 | printk(BIOS_DEBUG, "Initializing i82801jx southbridge...\n"); |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 177 | |
Arthur Heymans | 349e085 | 2017-04-09 20:48:37 +0200 | [diff] [blame] | 178 | i82801jx_early_settings(info); |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 179 | |
| 180 | /* PCI Express setup. */ |
Arthur Heymans | 349e085 | 2017-04-09 20:48:37 +0200 | [diff] [blame] | 181 | i82801jx_pcie_init(info); |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 182 | |
| 183 | /* EHCI configuration. */ |
Arthur Heymans | 349e085 | 2017-04-09 20:48:37 +0200 | [diff] [blame] | 184 | i82801jx_ehci_init(); |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 185 | |
| 186 | /* Now hide internal functions. We can't access them after this. */ |
Arthur Heymans | 349e085 | 2017-04-09 20:48:37 +0200 | [diff] [blame] | 187 | i82801jx_hide_functions(); |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 188 | |
| 189 | /* Reset watchdog timer. */ |
Julius Werner | cd49cce | 2019-03-05 16:53:33 -0800 | [diff] [blame] | 190 | #if !CONFIG(HAVE_SMI_HANDLER) |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 191 | outw(0x0008, DEFAULT_TCOBASE + 0x12); /* Set higher timer value. */ |
| 192 | #endif |
| 193 | outw(0x0000, DEFAULT_TCOBASE + 0x00); /* Update timer. */ |
| 194 | } |
| 195 | |
Arthur Heymans | 349e085 | 2017-04-09 20:48:37 +0200 | [diff] [blame] | 196 | struct chip_operations southbridge_intel_i82801jx_ops = { |
Nicholas Sudsgaard | bfb11be | 2024-01-30 09:53:46 +0900 | [diff] [blame] | 197 | .name = "Intel ICH10 (82801Jx) Series Southbridge", |
Arthur Heymans | 349e085 | 2017-04-09 20:48:37 +0200 | [diff] [blame] | 198 | .enable_dev = i82801jx_enable_device, |
| 199 | .init = i82801jx_init, |
Arthur Heymans | 7b9c139 | 2017-04-09 20:40:39 +0200 | [diff] [blame] | 200 | }; |