|
21 | 21 |
|
22 | 22 | #include "wolfboot/wolfboot.h" |
23 | 23 | #include <stdint.h> |
| 24 | +#include <string.h> |
24 | 25 |
|
25 | 26 | #ifdef __WOLFBOOT |
26 | 27 |
|
@@ -190,6 +191,156 @@ int hal_flash_protect(haladdr_t address, int len) |
190 | 191 | * hook's address/len are the same range FREG1 describes. */ |
191 | 192 | return tgl_lock_bios_region(); |
192 | 193 | } |
| 194 | + |
| 195 | +#ifdef WOLFBOOT_FSP_NVS_CACHE |
| 196 | +/* Hardware-sequencing flash cycles, used to save the FSP memory training |
| 197 | + * data before the BIOS region is locked. Runs from RAM (stage 2) only. */ |
| 198 | +#define SPI_BIOS_CNTL (0xdc) |
| 199 | +#define SPI_BIOS_CNTL_WPD (1U << 0) |
| 200 | +#define SPI_FADDR (0x8) |
| 201 | +#define SPI_FDATA0 (0x10) |
| 202 | +#define SPI_HSFSTS_FDONE (1U << 0) |
| 203 | +#define SPI_HSFSTS_FCERR (1U << 1) |
| 204 | +#define SPI_HSFSTS_AEL (1U << 2) |
| 205 | +#define SPI_HSFSTS_SCIP (1U << 5) |
| 206 | +#define SPI_HSFSTS_FGO (1U << 16) |
| 207 | +#define SPI_HSFSTS_FCYCLE_SHIFT (17) |
| 208 | +#define SPI_HSFSTS_FDBC_SHIFT (24) |
| 209 | +#define SPI_FCYCLE_WRITE (2) |
| 210 | +#define SPI_FCYCLE_ERASE_4K (3) |
| 211 | +#define SPI_HWSEQ_MAX_DATA (64) |
| 212 | +#define SPI_ERASE_SIZE (0x1000) |
| 213 | +#define SPI_HWSEQ_TIMEOUT_MS (1000) |
| 214 | + |
| 215 | +static uint32_t tgl_spi_begin(uint32_t *cmd) |
| 216 | +{ |
| 217 | + uint32_t spi_bar, cntl; |
| 218 | + |
| 219 | + spi_bar = pci_config_read32(0, SPI_PCI_DEV, SPI_PCI_FUN, PCI_BAR0_OFFSET); |
| 220 | + spi_bar &= PCI_BAR_MASK; |
| 221 | + *cmd = pci_config_read32(0, SPI_PCI_DEV, SPI_PCI_FUN, PCI_COMMAND_OFFSET); |
| 222 | + pci_config_write32(0, SPI_PCI_DEV, SPI_PCI_FUN, PCI_COMMAND_OFFSET, |
| 223 | + *cmd | PCI_COMMAND_MEM_SPACE); |
| 224 | + cntl = pci_config_read8(0, SPI_PCI_DEV, SPI_PCI_FUN, SPI_BIOS_CNTL); |
| 225 | + pci_config_write8(0, SPI_PCI_DEV, SPI_PCI_FUN, SPI_BIOS_CNTL, |
| 226 | + cntl | SPI_BIOS_CNTL_WPD); |
| 227 | + if ((pci_config_read8(0, SPI_PCI_DEV, SPI_PCI_FUN, SPI_BIOS_CNTL) & |
| 228 | + SPI_BIOS_CNTL_WPD) == 0) |
| 229 | + return 0; |
| 230 | + return spi_bar; |
| 231 | +} |
| 232 | + |
| 233 | +static void tgl_spi_end(uint32_t cmd) |
| 234 | +{ |
| 235 | + uint8_t cntl; |
| 236 | + |
| 237 | + cntl = pci_config_read8(0, SPI_PCI_DEV, SPI_PCI_FUN, SPI_BIOS_CNTL); |
| 238 | + pci_config_write8(0, SPI_PCI_DEV, SPI_PCI_FUN, SPI_BIOS_CNTL, |
| 239 | + cntl & ~SPI_BIOS_CNTL_WPD); |
| 240 | + pci_config_write32(0, SPI_PCI_DEV, SPI_PCI_FUN, PCI_COMMAND_OFFSET, cmd); |
| 241 | +} |
| 242 | + |
| 243 | +/* The BIOS region is mapped so that its last byte sits at 4 GB - 1; |
| 244 | + * returns the flash linear address of the memory-mapped address, or -1 |
| 245 | + * when the range is outside the BIOS region. */ |
| 246 | +static int64_t tgl_spi_linear(uint32_t spi_bar, uint32_t address, |
| 247 | + uint32_t len) |
| 248 | +{ |
| 249 | + uint32_t reg, base, top; |
| 250 | + uint64_t lin; |
| 251 | + |
| 252 | + reg = mmio_read32(spi_bar + SPI_FREG1); |
| 253 | + base = (reg & SPI_FREG_BASE_MASK) << SPI_FREG_ADDR_SHIFT; |
| 254 | + top = (((reg & SPI_FREG_LIMIT_MASK) >> SPI_FREG_LIMIT_SHIFT) |
| 255 | + << SPI_FREG_ADDR_SHIFT) | 0xfffU; |
| 256 | + if (top < base || len == 0) |
| 257 | + return -1; |
| 258 | + lin = (uint64_t)top - (0xffffffffULL - address); |
| 259 | + if (lin < base || lin + len - 1 > top) |
| 260 | + return -1; |
| 261 | + return (int64_t)lin; |
| 262 | +} |
| 263 | + |
| 264 | +static int tgl_spi_cycle(uint32_t spi_bar, uint32_t cycle, uint32_t faddr, |
| 265 | + uint32_t len) |
| 266 | +{ |
| 267 | + uint32_t sts = 0; |
| 268 | + int ms; |
| 269 | + |
| 270 | + for (ms = 0; ms < SPI_HWSEQ_TIMEOUT_MS; ms++) { |
| 271 | + if ((mmio_read32(spi_bar + SPI_BIOS_HSFSTS_CTL) & |
| 272 | + SPI_HSFSTS_SCIP) == 0) |
| 273 | + break; |
| 274 | + delay(1); |
| 275 | + } |
| 276 | + if (ms == SPI_HWSEQ_TIMEOUT_MS) |
| 277 | + return -1; |
| 278 | + mmio_write32(spi_bar + SPI_BIOS_HSFSTS_CTL, |
| 279 | + SPI_HSFSTS_FDONE | SPI_HSFSTS_FCERR | SPI_HSFSTS_AEL); |
| 280 | + mmio_write32(spi_bar + SPI_FADDR, faddr); |
| 281 | + mmio_write32(spi_bar + SPI_BIOS_HSFSTS_CTL, |
| 282 | + ((len - 1) << SPI_HSFSTS_FDBC_SHIFT) | |
| 283 | + (cycle << SPI_HSFSTS_FCYCLE_SHIFT) | SPI_HSFSTS_FGO); |
| 284 | + for (ms = 0; ms < SPI_HWSEQ_TIMEOUT_MS; ms++) { |
| 285 | + sts = mmio_read32(spi_bar + SPI_BIOS_HSFSTS_CTL); |
| 286 | + if (sts & (SPI_HSFSTS_FDONE | SPI_HSFSTS_FCERR)) |
| 287 | + break; |
| 288 | + delay(1); |
| 289 | + } |
| 290 | + mmio_write32(spi_bar + SPI_BIOS_HSFSTS_CTL, |
| 291 | + SPI_HSFSTS_FDONE | SPI_HSFSTS_FCERR | SPI_HSFSTS_AEL); |
| 292 | + if (ms == SPI_HWSEQ_TIMEOUT_MS || (sts & SPI_HSFSTS_FCERR)) |
| 293 | + return -1; |
| 294 | + return 0; |
| 295 | +} |
| 296 | + |
| 297 | +int tgl_spi_flash_erase(uint32_t address, uint32_t len) |
| 298 | +{ |
| 299 | + uint32_t spi_bar, cmd, off; |
| 300 | + int64_t lin; |
| 301 | + int ret = 0; |
| 302 | + |
| 303 | + if ((address | len) & (SPI_ERASE_SIZE - 1)) |
| 304 | + return -1; |
| 305 | + spi_bar = tgl_spi_begin(&cmd); |
| 306 | + lin = (spi_bar != 0) ? tgl_spi_linear(spi_bar, address, len) : -1; |
| 307 | + if (lin < 0) |
| 308 | + ret = -1; |
| 309 | + for (off = 0; ret == 0 && off < len; off += SPI_ERASE_SIZE) |
| 310 | + ret = tgl_spi_cycle(spi_bar, SPI_FCYCLE_ERASE_4K, |
| 311 | + (uint32_t)lin + off, 1); |
| 312 | + tgl_spi_end(cmd); |
| 313 | + return ret; |
| 314 | +} |
| 315 | + |
| 316 | +int tgl_spi_flash_write(uint32_t address, const uint8_t *buf, uint32_t len) |
| 317 | +{ |
| 318 | + uint32_t spi_bar, cmd, off, chunk, word, i; |
| 319 | + int64_t lin; |
| 320 | + int ret = 0; |
| 321 | + |
| 322 | + spi_bar = tgl_spi_begin(&cmd); |
| 323 | + lin = (spi_bar != 0) ? tgl_spi_linear(spi_bar, address, len) : -1; |
| 324 | + if (lin < 0) |
| 325 | + ret = -1; |
| 326 | + for (off = 0; ret == 0 && off < len; off += chunk) { |
| 327 | + /* 64-byte aligned cycles never cross a flash page */ |
| 328 | + chunk = SPI_HWSEQ_MAX_DATA - (((uint32_t)lin + off) & |
| 329 | + (SPI_HWSEQ_MAX_DATA - 1)); |
| 330 | + if (chunk > len - off) |
| 331 | + chunk = len - off; |
| 332 | + for (i = 0; i < chunk; i += 4) { |
| 333 | + word = 0xffffffffU; |
| 334 | + memcpy(&word, buf + off + i, (chunk - i < 4) ? chunk - i : 4); |
| 335 | + mmio_write32(spi_bar + SPI_FDATA0 + i, word); |
| 336 | + } |
| 337 | + ret = tgl_spi_cycle(spi_bar, SPI_FCYCLE_WRITE, (uint32_t)lin + off, |
| 338 | + chunk); |
| 339 | + } |
| 340 | + tgl_spi_end(cmd); |
| 341 | + return ret; |
| 342 | +} |
| 343 | +#endif /* WOLFBOOT_FSP_NVS_CACHE */ |
193 | 344 | #endif /* WOLFBOOT_TGL */ |
194 | 345 | #endif |
195 | 346 |
|
0 commit comments