diff options
Diffstat (limited to 'src/soc/amd/common/block/psp/psp_gen2.c')
-rw-r--r-- | src/soc/amd/common/block/psp/psp_gen2.c | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/src/soc/amd/common/block/psp/psp_gen2.c b/src/soc/amd/common/block/psp/psp_gen2.c new file mode 100644 index 0000000000..b70babc14a --- /dev/null +++ b/src/soc/amd/common/block/psp/psp_gen2.c @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* This file is part of the coreboot project. */ + +#include <device/mmio.h> +#include <timer.h> +#include <bootstate.h> +#include <amdblocks/psp.h> +#include <soc/iomap.h> +#include <soc/northbridge.h> +#include "psp_def.h" + +static u16 rd_mbox_sts(struct pspv2_mbox *mbox) +{ + union { + u32 val; + struct pspv2_mbox_cmd_fields fields; + } tmp = { 0 }; + + tmp.val = read32(&mbox->val); + return tmp.fields.mbox_status; +} + +static void wr_mbox_cmd(struct pspv2_mbox *mbox, u8 cmd) +{ + union { + u32 val; + struct pspv2_mbox_cmd_fields fields; + } tmp = { 0 }; + + /* Write entire 32-bit area to begin command execution */ + tmp.fields.mbox_command = cmd; + write32(&mbox->val, tmp.val); +} + +static u8 rd_mbox_recovery(struct pspv2_mbox *mbox) +{ + union { + u32 val; + struct pspv2_mbox_cmd_fields fields; + } tmp = { 0 }; + + tmp.val = read32(&mbox->val); + return !!tmp.fields.recovery; +} + +static void wr_mbox_cmd_resp(struct pspv2_mbox *mbox, void *buffer) +{ + write64(&mbox->cmd_response, (uintptr_t)buffer); +} + +static int wait_command(struct pspv2_mbox *mbox, bool wait_for_ready) +{ + struct pspv2_mbox and_mask = { .val = ~0 }; + struct pspv2_mbox expected = { .val = 0 }; + struct stopwatch sw; + u32 tmp; + + /* Zero fields from and_mask that should be kept */ + and_mask.fields.mbox_command = 0; + and_mask.fields.ready = wait_for_ready ? 0 : 1; + + /* Expect mbox_cmd == 0 but ready depends */ + if (wait_for_ready) + expected.fields.ready = 1; + + stopwatch_init_msecs_expire(&sw, PSP_CMD_TIMEOUT); + + do { + tmp = read32(&mbox->val); + tmp &= ~and_mask.val; + if (tmp == expected.val) + return 0; + } while (!stopwatch_expired(&sw)); + + return -PSPSTS_CMD_TIMEOUT; +} + +int send_psp_command(u32 command, void *buffer) +{ + struct pspv2_mbox *mbox = soc_get_mbox_address(); + if (!mbox) + return -PSPSTS_NOBASE; + + if (rd_mbox_recovery(mbox)) + return -PSPSTS_RECOVERY; + + if (wait_command(mbox, true)) + return -PSPSTS_CMD_TIMEOUT; + + /* set address of command-response buffer and write command register */ + wr_mbox_cmd_resp(mbox, buffer); + wr_mbox_cmd(mbox, command); + + /* PSP clears command register when complete. All commands except + * SxInfo set the Ready bit. */ + if (wait_command(mbox, command != MBOX_BIOS_CMD_SX_INFO)) + return -PSPSTS_CMD_TIMEOUT; + + /* check delivery status */ + if (rd_mbox_sts(mbox)) + return -PSPSTS_SEND_ERROR; + + return 0; +} |