diff options
Diffstat (limited to 'src/drivers/amd/agesa/oem_s3.c')
-rw-r--r-- | src/drivers/amd/agesa/oem_s3.c | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/src/drivers/amd/agesa/oem_s3.c b/src/drivers/amd/agesa/oem_s3.c new file mode 100644 index 0000000000..1ca6e5b2f6 --- /dev/null +++ b/src/drivers/amd/agesa/oem_s3.c @@ -0,0 +1,165 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2012 Advanced Micro Devices, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <spi-generic.h> +#include <spi_flash.h> +#include <string.h> +#include <cbmem.h> +#include <program_loading.h> +#include <northbridge/amd/agesa/state_machine.h> +#include <AGESA.h> +#include <northbridge/amd/agesa/agesa_helper.h> + +typedef enum { + S3DataTypeNonVolatile = 0, ///< NonVolatile Data Type + S3DataTypeMTRR ///< MTRR storage +} S3_DATA_TYPE; + +/* The size needs to be 4k aligned, which is the sector size of most flashes. */ +#define S3_DATA_MTRR_SIZE 0x1000 +#define S3_DATA_NONVOLATILE_SIZE 0x1000 + +#if IS_ENABLED(CONFIG_HAVE_ACPI_RESUME) && \ + (S3_DATA_MTRR_SIZE + S3_DATA_NONVOLATILE_SIZE) > CONFIG_S3_DATA_SIZE +#error "Please increase the value of S3_DATA_SIZE" +#endif + +static void get_s3nv_data(S3_DATA_TYPE S3DataType, uintptr_t *pos, uintptr_t *len) +{ + /* FIXME: Find file from CBFS. */ + u32 s3_data = CONFIG_S3_DATA_POS; + + switch (S3DataType) { + case S3DataTypeMTRR: + *pos = s3_data; + *len = S3_DATA_MTRR_SIZE; + break; + case S3DataTypeNonVolatile: + *pos = s3_data + S3_DATA_MTRR_SIZE; + *len = S3_DATA_NONVOLATILE_SIZE; + break; + default: + *pos = 0; + *len = 0; + break; + } +} + +AGESA_STATUS OemInitResume(AMD_S3_PARAMS *dataBlock) +{ + uintptr_t pos, size; + get_s3nv_data(S3DataTypeNonVolatile, &pos, &size); + + u32 len = *(u32*)pos; + + /* Test for uninitialized s3nv data in SPI. */ + if (len == 0 || len == (u32)-1ULL) + return AGESA_FATAL; + + dataBlock->NvStorageSize = len; + dataBlock->NvStorage = (void *) (pos + sizeof(u32)); + return AGESA_SUCCESS; +} + +AGESA_STATUS OemS3LateRestore(AMD_S3_PARAMS *dataBlock) +{ + char *heap = cbmem_find(CBMEM_ID_RESUME_SCRATCH); + if (heap == NULL) + return AGESA_FATAL; + + printk(BIOS_DEBUG, "Using resume HEAP at %08x\n", + (unsigned int)(uintptr_t) heap); + + /* Return allocated CBMEM size, we do not keep track of + * how much was actually used. + */ + dataBlock->VolatileStorageSize = HIGH_MEMORY_SCRATCH; + dataBlock->VolatileStorage = heap; + return AGESA_SUCCESS; +} + +#if ENV_RAMSTAGE + +static int spi_SaveS3info(u32 pos, u32 size, u8 *buf, u32 len) +{ +#if IS_ENABLED(CONFIG_SPI_FLASH) + struct spi_flash flash; + + spi_init(); + if (spi_flash_probe(0, 0, &flash)) + return -1; + + spi_flash_volatile_group_begin(&flash); + + spi_flash_erase(&flash, pos, size); + spi_flash_write(&flash, pos, sizeof(len), &len); + spi_flash_write(&flash, pos + sizeof(len), len, buf); + + spi_flash_volatile_group_end(&flash); + return 0; +#else + return -1; +#endif +} + +static u8 MTRRStorage[S3_DATA_MTRR_SIZE]; + +AGESA_STATUS OemS3Save(AMD_S3_PARAMS *dataBlock) +{ + u32 MTRRStorageSize = 0; + uintptr_t pos, size; + + romstage_ram_stack_base(HIGH_ROMSTAGE_STACK_SIZE, ROMSTAGE_STACK_CBMEM); + + /* To be consumed in AmdInitResume. */ + get_s3nv_data(S3DataTypeNonVolatile, &pos, &size); + if (size && dataBlock->NvStorageSize) + spi_SaveS3info(pos, size, dataBlock->NvStorage, + dataBlock->NvStorageSize); + else + printk(BIOS_EMERG, + "Error: Cannot store memory training results in SPI.\n" + "Error: S3 resume will not be possible.\n" + ); + + /* To be consumed in AmdS3LateRestore. */ + char *heap = cbmem_add(CBMEM_ID_RESUME_SCRATCH, HIGH_MEMORY_SCRATCH); + if (heap) { + memset(heap, 0, HIGH_MEMORY_SCRATCH); + memcpy(heap, dataBlock->VolatileStorage, dataBlock->VolatileStorageSize); + } + + /* Collect MTRR setup. */ + backup_mtrr(MTRRStorage, &MTRRStorageSize); + + /* To be consumed in restore_mtrr, CPU enumeration in ramstage. */ + get_s3nv_data(S3DataTypeMTRR, &pos, &size); + if (size && MTRRStorageSize) + spi_SaveS3info(pos, size, MTRRStorage, MTRRStorageSize); + + return AGESA_SUCCESS; +} + +#endif /* ENV_RAMSTAGE */ + +const void *OemS3Saved_MTRR_Storage(void) +{ + uintptr_t pos, size; + get_s3nv_data(S3DataTypeMTRR, &pos, &size); + if (!size) + return NULL; + + return (void*)(pos + sizeof(UINT32)); +} |