summaryrefslogtreecommitdiff
path: root/src/soc/amd/common/block/psp/psp_gen2.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/soc/amd/common/block/psp/psp_gen2.c')
-rw-r--r--src/soc/amd/common/block/psp/psp_gen2.c104
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;
+}