Angel Pons | ae59387 | 2020-04-04 18:50:57 +0200 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
Marshall Dawson | 6ab5ed3 | 2019-05-29 09:24:18 -0600 | [diff] [blame] | 2 | |
Raul E Rangel | 314c716 | 2020-05-01 14:04:08 -0600 | [diff] [blame] | 3 | #include <assert.h> |
Marshall Dawson | 6ab5ed3 | 2019-05-29 09:24:18 -0600 | [diff] [blame] | 4 | #include <stdint.h> |
| 5 | #include <device/device.h> |
| 6 | #include <device/pci_ops.h> |
| 7 | #include <device/pci_def.h> |
Furquan Shaikh | 6d28802 | 2020-05-11 16:35:08 -0700 | [diff] [blame] | 8 | #include <amdblocks/acpimmio.h> |
Marshall Dawson | 6ab5ed3 | 2019-05-29 09:24:18 -0600 | [diff] [blame] | 9 | #include <amdblocks/lpc.h> |
| 10 | #include <soc/iomap.h> |
Raul E Rangel | 466edb5 | 2021-02-09 11:24:13 -0700 | [diff] [blame] | 11 | #include <soc/lpc.h> |
Marshall Dawson | 6ab5ed3 | 2019-05-29 09:24:18 -0600 | [diff] [blame] | 12 | #include <soc/southbridge.h> |
| 13 | |
| 14 | /* The LPC-ISA bridge is always at D14F3 */ |
| 15 | #if !defined(__SIMPLE_DEVICE__) |
Marshall Dawson | 6ab5ed3 | 2019-05-29 09:24:18 -0600 | [diff] [blame] | 16 | #define _LPCB_DEV pcidev_on_root(0x14, 0x3) |
| 17 | #else |
| 18 | #define _LPCB_DEV PCI_DEV(0, 0x14, 0x3) |
| 19 | #endif |
| 20 | |
| 21 | /* |
| 22 | * Structure to simplify code obtaining the total of used wide IO |
| 23 | * registers and the size assigned to each. |
| 24 | */ |
| 25 | static const struct wide_io_ioport_and_bits { |
| 26 | uint32_t enable; |
| 27 | uint16_t port; |
| 28 | uint8_t alt; |
| 29 | } wio_io_en[] = { |
| 30 | { |
| 31 | .enable = LPC_WIDEIO0_ENABLE, |
| 32 | .port = LPC_WIDEIO_GENERIC_PORT, |
| 33 | .alt = LPC_ALT_WIDEIO0_ENABLE |
| 34 | }, |
| 35 | { |
| 36 | .enable = LPC_WIDEIO1_ENABLE, |
| 37 | .port = LPC_WIDEIO1_GENERIC_PORT, |
| 38 | .alt = LPC_ALT_WIDEIO1_ENABLE |
| 39 | }, |
| 40 | { |
| 41 | .enable = LPC_WIDEIO2_ENABLE, |
| 42 | .port = LPC_WIDEIO2_GENERIC_PORT, |
| 43 | .alt = LPC_ALT_WIDEIO2_ENABLE |
| 44 | } |
| 45 | }; |
| 46 | |
| 47 | /** |
| 48 | * @brief Find the size of a particular wide IO |
| 49 | * |
| 50 | * @param index = index of desired wide IO |
| 51 | * |
| 52 | * @return size of desired wide IO |
| 53 | */ |
| 54 | uint16_t lpc_wideio_size(int index) |
| 55 | { |
| 56 | uint32_t enable_register; |
| 57 | uint16_t size = 0; |
| 58 | uint8_t alternate_register; |
| 59 | |
| 60 | if (index >= ARRAY_SIZE(wio_io_en)) |
| 61 | return size; |
| 62 | enable_register = pci_read_config32(_LPCB_DEV, |
| 63 | LPC_IO_OR_MEM_DECODE_ENABLE); |
| 64 | alternate_register = pci_read_config8(_LPCB_DEV, |
| 65 | LPC_ALT_WIDEIO_RANGE_ENABLE); |
| 66 | if (enable_register & wio_io_en[index].enable) |
| 67 | size = (alternate_register & wio_io_en[index].alt) ? |
| 68 | 16 : 512; |
| 69 | return size; |
| 70 | } |
| 71 | |
| 72 | /** |
| 73 | * @brief Identify if any LPC wide IO is covering the IO range |
| 74 | * |
| 75 | * @param start = start of IO range |
| 76 | * @param size = size of IO range |
| 77 | * |
| 78 | * @return Index of wide IO covering the range or error |
| 79 | */ |
| 80 | int lpc_find_wideio_range(uint16_t start, uint16_t size) |
| 81 | { |
| 82 | int i, index = WIDEIO_RANGE_ERROR; |
| 83 | uint16_t end, current_size, start_wideio, end_wideio; |
| 84 | |
| 85 | end = start + size; |
| 86 | for (i = 0; i < ARRAY_SIZE(wio_io_en); i++) { |
| 87 | current_size = lpc_wideio_size(i); |
| 88 | if (current_size == 0) |
| 89 | continue; |
| 90 | start_wideio = pci_read_config16(_LPCB_DEV, |
| 91 | wio_io_en[i].port); |
| 92 | end_wideio = start_wideio + current_size; |
| 93 | if ((start >= start_wideio) && (end <= end_wideio)) { |
| 94 | index = i; |
| 95 | break; |
| 96 | } |
| 97 | } |
| 98 | return index; |
| 99 | } |
| 100 | |
| 101 | /** |
| 102 | * @brief Program a LPC wide IO to support an IO range |
| 103 | * |
| 104 | * @param start = start of range to be routed through wide IO |
| 105 | * @param size = size of range to be routed through wide IO |
| 106 | * |
| 107 | * @return Index of wide IO register used or error |
| 108 | */ |
| 109 | int lpc_set_wideio_range(uint16_t start, uint16_t size) |
| 110 | { |
| 111 | int i, index = WIDEIO_RANGE_ERROR; |
| 112 | uint32_t enable_register; |
| 113 | uint8_t alternate_register; |
| 114 | |
| 115 | enable_register = pci_read_config32(_LPCB_DEV, |
| 116 | LPC_IO_OR_MEM_DECODE_ENABLE); |
| 117 | alternate_register = pci_read_config8(_LPCB_DEV, |
| 118 | LPC_ALT_WIDEIO_RANGE_ENABLE); |
| 119 | for (i = 0; i < ARRAY_SIZE(wio_io_en); i++) { |
| 120 | if (enable_register & wio_io_en[i].enable) |
| 121 | continue; |
| 122 | index = i; |
| 123 | pci_write_config16(_LPCB_DEV, wio_io_en[i].port, start); |
| 124 | enable_register |= wio_io_en[i].enable; |
| 125 | pci_write_config32(_LPCB_DEV, LPC_IO_OR_MEM_DECODE_ENABLE, |
| 126 | enable_register); |
| 127 | if (size <= 16) |
| 128 | alternate_register |= wio_io_en[i].alt; |
| 129 | else |
| 130 | alternate_register &= ~wio_io_en[i].alt; |
| 131 | pci_write_config8(_LPCB_DEV, |
| 132 | LPC_ALT_WIDEIO_RANGE_ENABLE, |
| 133 | alternate_register); |
| 134 | break; |
| 135 | } |
| 136 | return index; |
| 137 | } |
| 138 | |
| 139 | void lpc_enable_port80(void) |
| 140 | { |
Felix Held | f6205d3 | 2021-11-25 19:53:37 +0100 | [diff] [blame] | 141 | uint32_t tmp; |
Marshall Dawson | 6ab5ed3 | 2019-05-29 09:24:18 -0600 | [diff] [blame] | 142 | |
Felix Held | f6205d3 | 2021-11-25 19:53:37 +0100 | [diff] [blame] | 143 | tmp = pci_read_config32(_LPCB_DEV, LPC_IO_OR_MEM_DECODE_ENABLE); |
| 144 | tmp |= DECODE_IO_PORT_ENABLE4; |
| 145 | pci_write_config32(_LPCB_DEV, LPC_IO_OR_MEM_DECODE_ENABLE, tmp); |
Marshall Dawson | 6ab5ed3 | 2019-05-29 09:24:18 -0600 | [diff] [blame] | 146 | } |
| 147 | |
Marshall Dawson | f6dbf4a | 2019-09-04 11:32:25 -0600 | [diff] [blame] | 148 | void lpc_enable_sio_decode(const bool addr) |
| 149 | { |
| 150 | uint32_t decodes; |
| 151 | uint32_t enable; |
| 152 | |
| 153 | decodes = pci_read_config32(_LPCB_DEV, LPC_IO_OR_MEM_DECODE_ENABLE); |
| 154 | enable = addr == LPC_SELECT_SIO_2E2F ? |
| 155 | DECODE_SIO_ENABLE : DECODE_ALTERNATE_SIO_ENABLE; |
| 156 | decodes |= enable; |
| 157 | pci_write_config32(_LPCB_DEV, LPC_IO_OR_MEM_DECODE_ENABLE, decodes); |
| 158 | } |
| 159 | |
Marshall Dawson | 6ab5ed3 | 2019-05-29 09:24:18 -0600 | [diff] [blame] | 160 | void lpc_enable_decode(uint32_t decodes) |
| 161 | { |
| 162 | pci_write_config32(_LPCB_DEV, LPC_IO_PORT_DECODE_ENABLE, decodes); |
| 163 | } |
| 164 | |
Marshall Dawson | ba2533f | 2019-09-04 11:00:06 -0600 | [diff] [blame] | 165 | /* |
| 166 | * Clear all decoding to the LPC bus and erase any range registers associated |
| 167 | * with the enable bits. |
| 168 | */ |
| 169 | void lpc_disable_decodes(void) |
| 170 | { |
| 171 | uint32_t reg; |
| 172 | |
| 173 | lpc_enable_decode(0); |
| 174 | reg = pci_read_config32(_LPCB_DEV, LPC_IO_OR_MEM_DECODE_ENABLE); |
| 175 | reg &= LPC_SYNC_TIMEOUT_COUNT_MASK | LPC_SYNC_TIMEOUT_COUNT_ENABLE; |
| 176 | pci_write_config32(_LPCB_DEV, LPC_IO_OR_MEM_DECODE_ENABLE, reg); |
Furquan Shaikh | ca892fe | 2020-05-09 14:46:09 -0700 | [diff] [blame] | 177 | pci_write_config32(_LPCB_DEV, LPC_IO_PORT_DECODE_ENABLE, 0); |
Marshall Dawson | ba2533f | 2019-09-04 11:00:06 -0600 | [diff] [blame] | 178 | |
| 179 | /* D14F3x48 enables ranges configured in additional registers */ |
| 180 | pci_write_config32(_LPCB_DEV, LPC_MEM_PORT1, 0); |
| 181 | pci_write_config32(_LPCB_DEV, LPC_MEM_PORT0, 0); |
| 182 | pci_write_config32(_LPCB_DEV, LPC_WIDEIO2_GENERIC_PORT, 0); |
| 183 | } |
| 184 | |
Marshall Dawson | 6ab5ed3 | 2019-05-29 09:24:18 -0600 | [diff] [blame] | 185 | uintptr_t lpc_spibase(void) |
| 186 | { |
| 187 | u32 base, enables; |
| 188 | |
| 189 | /* Make sure the base address is predictable */ |
Felix Held | 697fa74 | 2022-03-03 20:54:38 +0100 | [diff] [blame] | 190 | base = pci_read_config32(_LPCB_DEV, SPI_BASE_ADDRESS_REGISTER); |
Marshall Dawson | 6ab5ed3 | 2019-05-29 09:24:18 -0600 | [diff] [blame] | 191 | enables = base & SPI_PRESERVE_BITS; |
| 192 | base &= ~(SPI_PRESERVE_BITS | SPI_BASE_RESERVED); |
| 193 | |
| 194 | if (!base) { |
| 195 | base = SPI_BASE_ADDRESS; |
Felix Held | 697fa74 | 2022-03-03 20:54:38 +0100 | [diff] [blame] | 196 | pci_write_config32(_LPCB_DEV, SPI_BASE_ADDRESS_REGISTER, |
Marshall Dawson | 6ab5ed3 | 2019-05-29 09:24:18 -0600 | [diff] [blame] | 197 | base | enables | SPI_ROM_ENABLE); |
| 198 | /* PCI_COMMAND_MEMORY is read-only and enabled. */ |
| 199 | } |
| 200 | return base; |
| 201 | } |
| 202 | |
| 203 | /* |
| 204 | * Enable FCH to decode TPM associated Memory and IO regions |
| 205 | * |
| 206 | * Enable decoding of TPM cycles defined in TPM 1.2 spec |
| 207 | * Enable decoding of legacy TPM addresses: IO addresses 0x7f- |
| 208 | * 0x7e and 0xef-0xee. |
| 209 | * This function should be called if TPM is connected in any way to the FCH and |
| 210 | * conforms to the regions decoded. |
| 211 | * Absent any other routing configuration the TPM cycles will be claimed by the |
| 212 | * LPC bus |
| 213 | */ |
| 214 | void lpc_tpm_decode(void) |
| 215 | { |
| 216 | u32 value; |
| 217 | |
| 218 | value = pci_read_config32(_LPCB_DEV, LPC_TRUSTED_PLATFORM_MODULE); |
| 219 | value |= TPM_12_EN | TPM_LEGACY_EN; |
| 220 | pci_write_config32(_LPCB_DEV, LPC_TRUSTED_PLATFORM_MODULE, value); |
| 221 | } |
| 222 | |
| 223 | /* |
| 224 | * Enable FCH to decode TPM associated Memory and IO regions to SPI |
| 225 | * |
| 226 | * This should be used if TPM is connected to SPI bus. |
| 227 | * Assumes SPI address space is already configured via a call to lpc_spibase(). |
| 228 | */ |
| 229 | void lpc_tpm_decode_spi(void) |
| 230 | { |
| 231 | /* Enable TPM decoding to FCH */ |
| 232 | lpc_tpm_decode(); |
| 233 | |
| 234 | /* Route TPM accesses to SPI */ |
| 235 | u32 spibase = pci_read_config32(_LPCB_DEV, |
Felix Held | 697fa74 | 2022-03-03 20:54:38 +0100 | [diff] [blame] | 236 | SPI_BASE_ADDRESS_REGISTER); |
| 237 | pci_write_config32(_LPCB_DEV, SPI_BASE_ADDRESS_REGISTER, spibase |
Marshall Dawson | 6ab5ed3 | 2019-05-29 09:24:18 -0600 | [diff] [blame] | 238 | | ROUTE_TPM_2_SPI); |
| 239 | } |
| 240 | |
| 241 | /* |
| 242 | * Enable 4MB (LPC) ROM access at 0xFFC00000 - 0xFFFFFFFF. |
| 243 | * |
| 244 | * Hardware should enable LPC ROM by pin straps. This function does not |
| 245 | * handle the theoretically possible PCI ROM, FWH, or SPI ROM configurations. |
| 246 | * |
| 247 | * The southbridge power-on default is to map 512K ROM space. |
| 248 | * |
| 249 | */ |
| 250 | void lpc_enable_rom(void) |
| 251 | { |
| 252 | u8 reg8; |
| 253 | |
| 254 | /* |
| 255 | * Decode variable LPC ROM address ranges 1 and 2. |
| 256 | * Bits 3-4 are not defined in any publicly available datasheet |
| 257 | */ |
| 258 | reg8 = pci_read_config8(_LPCB_DEV, LPC_IO_OR_MEM_DECODE_ENABLE); |
| 259 | reg8 |= (1 << 3) | (1 << 4); |
| 260 | pci_write_config8(_LPCB_DEV, LPC_IO_OR_MEM_DECODE_ENABLE, reg8); |
| 261 | |
| 262 | /* |
| 263 | * LPC ROM address range 1: |
| 264 | * Enable LPC ROM range mirroring start at 0x000e(0000). |
| 265 | */ |
| 266 | pci_write_config16(_LPCB_DEV, ROM_ADDRESS_RANGE1_START, 0x000e); |
| 267 | |
| 268 | /* Enable LPC ROM range mirroring end at 0x000f(ffff). */ |
| 269 | pci_write_config16(_LPCB_DEV, ROM_ADDRESS_RANGE1_END, 0x000f); |
| 270 | |
| 271 | /* |
| 272 | * LPC ROM address range 2: |
| 273 | * |
| 274 | * Enable LPC ROM range start at: |
| 275 | * 0xfff8(0000): 512KB |
| 276 | * 0xfff0(0000): 1MB |
| 277 | * 0xffe0(0000): 2MB |
| 278 | * 0xffc0(0000): 4MB |
| 279 | */ |
| 280 | pci_write_config16(_LPCB_DEV, ROM_ADDRESS_RANGE2_START, 0x10000 |
| 281 | - (CONFIG_COREBOOT_ROMSIZE_KB >> 6)); |
| 282 | |
| 283 | /* Enable LPC ROM range end at 0xffff(ffff). */ |
| 284 | pci_write_config16(_LPCB_DEV, ROM_ADDRESS_RANGE2_END, 0xffff); |
| 285 | } |
| 286 | |
| 287 | void lpc_enable_spi_prefetch(void) |
| 288 | { |
| 289 | uint32_t dword; |
| 290 | |
| 291 | dword = pci_read_config32(_LPCB_DEV, LPC_ROM_DMA_EC_HOST_CONTROL); |
| 292 | dword |= SPI_FROM_HOST_PREFETCH_EN | SPI_FROM_USB_PREFETCH_EN; |
| 293 | pci_write_config32(_LPCB_DEV, LPC_ROM_DMA_EC_HOST_CONTROL, dword); |
| 294 | } |
| 295 | |
Raul E Rangel | 314c716 | 2020-05-01 14:04:08 -0600 | [diff] [blame] | 296 | void lpc_disable_spi_rom_sharing(void) |
| 297 | { |
| 298 | u8 byte; |
| 299 | |
| 300 | if (!CONFIG(PROVIDES_ROM_SHARING)) |
| 301 | dead_code(); |
| 302 | |
| 303 | byte = pci_read_config8(_LPCB_DEV, LPC_PCI_CONTROL); |
| 304 | byte &= ~VW_ROM_SHARING_EN; |
| 305 | byte &= ~EXT_ROM_SHARING_EN; |
| 306 | pci_write_config8(_LPCB_DEV, LPC_PCI_CONTROL, byte); |
| 307 | } |
| 308 | |
Marshall Dawson | 6ab5ed3 | 2019-05-29 09:24:18 -0600 | [diff] [blame] | 309 | uintptr_t lpc_get_spibase(void) |
| 310 | { |
| 311 | u32 base; |
| 312 | |
Felix Held | 697fa74 | 2022-03-03 20:54:38 +0100 | [diff] [blame] | 313 | base = pci_read_config32(_LPCB_DEV, SPI_BASE_ADDRESS_REGISTER); |
Marshall Dawson | 6ab5ed3 | 2019-05-29 09:24:18 -0600 | [diff] [blame] | 314 | base = ALIGN_DOWN(base, SPI_BASE_ALIGNMENT); |
| 315 | return (uintptr_t)base; |
| 316 | } |
| 317 | |
Furquan Shaikh | d82c7d2 | 2020-05-09 17:18:48 -0700 | [diff] [blame] | 318 | void lpc_set_spibase(uint32_t base) |
Marshall Dawson | 6ab5ed3 | 2019-05-29 09:24:18 -0600 | [diff] [blame] | 319 | { |
Furquan Shaikh | d82c7d2 | 2020-05-09 17:18:48 -0700 | [diff] [blame] | 320 | uint32_t reg32; |
| 321 | |
Felix Held | 697fa74 | 2022-03-03 20:54:38 +0100 | [diff] [blame] | 322 | reg32 = pci_read_config32(_LPCB_DEV, SPI_BASE_ADDRESS_REGISTER); |
Furquan Shaikh | d82c7d2 | 2020-05-09 17:18:48 -0700 | [diff] [blame] | 323 | |
| 324 | reg32 &= SPI_BASE_ALIGNMENT - 1; /* preserve only reserved, enables */ |
| 325 | reg32 |= ALIGN_DOWN(base, SPI_BASE_ALIGNMENT); |
| 326 | |
Felix Held | 697fa74 | 2022-03-03 20:54:38 +0100 | [diff] [blame] | 327 | pci_write_config32(_LPCB_DEV, SPI_BASE_ADDRESS_REGISTER, reg32); |
Furquan Shaikh | d82c7d2 | 2020-05-09 17:18:48 -0700 | [diff] [blame] | 328 | } |
| 329 | |
| 330 | void lpc_enable_spi_rom(uint32_t enable) |
| 331 | { |
| 332 | uint32_t reg32; |
Marshall Dawson | 6ab5ed3 | 2019-05-29 09:24:18 -0600 | [diff] [blame] | 333 | |
| 334 | /* only two types of CS# enables are allowed */ |
| 335 | enable &= SPI_ROM_ENABLE | SPI_ROM_ALT_ENABLE; |
| 336 | |
Felix Held | 697fa74 | 2022-03-03 20:54:38 +0100 | [diff] [blame] | 337 | reg32 = pci_read_config32(_LPCB_DEV, SPI_BASE_ADDRESS_REGISTER); |
Marshall Dawson | 6ab5ed3 | 2019-05-29 09:24:18 -0600 | [diff] [blame] | 338 | |
Marshall Dawson | 6ab5ed3 | 2019-05-29 09:24:18 -0600 | [diff] [blame] | 339 | reg32 &= ~(SPI_ROM_ENABLE | SPI_ROM_ALT_ENABLE); |
| 340 | reg32 |= enable; |
Marshall Dawson | 6ab5ed3 | 2019-05-29 09:24:18 -0600 | [diff] [blame] | 341 | |
Felix Held | 697fa74 | 2022-03-03 20:54:38 +0100 | [diff] [blame] | 342 | pci_write_config32(_LPCB_DEV, SPI_BASE_ADDRESS_REGISTER, reg32); |
Marshall Dawson | 6ab5ed3 | 2019-05-29 09:24:18 -0600 | [diff] [blame] | 343 | } |
Furquan Shaikh | 6d28802 | 2020-05-11 16:35:08 -0700 | [diff] [blame] | 344 | |
| 345 | static void lpc_enable_controller(void) |
| 346 | { |
| 347 | u8 byte; |
| 348 | |
| 349 | /* Enable LPC controller */ |
Felix Held | 854491d | 2023-07-14 19:49:05 +0200 | [diff] [blame^] | 350 | byte = pm_read8(PM_LPC_GATING); |
Furquan Shaikh | 6d28802 | 2020-05-11 16:35:08 -0700 | [diff] [blame] | 351 | byte |= PM_LPC_ENABLE; |
Felix Held | 854491d | 2023-07-14 19:49:05 +0200 | [diff] [blame^] | 352 | pm_write8(PM_LPC_GATING, byte); |
Furquan Shaikh | 6d28802 | 2020-05-11 16:35:08 -0700 | [diff] [blame] | 353 | } |
| 354 | |
| 355 | void lpc_early_init(void) |
| 356 | { |
| 357 | lpc_enable_controller(); |
| 358 | lpc_disable_decodes(); |
| 359 | lpc_set_spibase(SPI_BASE_ADDRESS); |
| 360 | } |