{"id":2235249,"url":"http://patchwork.ozlabs.org/api/1.2/patches/2235249/?format=json","web_url":"http://patchwork.ozlabs.org/project/uboot/patch/c964c8cc4075a5f331a3dd108f103302bcbcc365.1778277334.git.aidan@wolfssl.com/","project":{"id":18,"url":"http://patchwork.ozlabs.org/api/1.2/projects/18/?format=json","name":"U-Boot","link_name":"uboot","list_id":"u-boot.lists.denx.de","list_email":"u-boot@lists.denx.de","web_url":null,"scm_url":null,"webscm_url":null,"list_archive_url":"","list_archive_url_format":"","commit_url_format":""},"msgid":"<c964c8cc4075a5f331a3dd108f103302bcbcc365.1778277334.git.aidan@wolfssl.com>","list_archive_url":null,"date":"2026-05-09T00:04:16","name":"[v3,09/12] tpm: add sandbox TPM SPI emulator","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"0b94c6cbba259a0806718c3fd629893ac3d1b3fa","submitter":{"id":92785,"url":"http://patchwork.ozlabs.org/api/1.2/people/92785/?format=json","name":"Aidan Garske","email":"aidan@wolfssl.com"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/uboot/patch/c964c8cc4075a5f331a3dd108f103302bcbcc365.1778277334.git.aidan@wolfssl.com/mbox/","series":[{"id":503464,"url":"http://patchwork.ozlabs.org/api/1.2/series/503464/?format=json","web_url":"http://patchwork.ozlabs.org/project/uboot/list/?series=503464","date":"2026-05-09T00:04:07","name":"tpm: Add wolfTPM library support for TPM 2.0","version":3,"mbox":"http://patchwork.ozlabs.org/series/503464/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/2235249/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/2235249/checks/","tags":{},"related":[],"headers":{"Return-Path":"<u-boot-bounces@lists.denx.de>","X-Original-To":"incoming@patchwork.ozlabs.org","Delivered-To":"patchwork-incoming@legolas.ozlabs.org","Authentication-Results":["legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=wolfssl-com.20251104.gappssmtp.com\n header.i=@wolfssl-com.20251104.gappssmtp.com header.a=rsa-sha256\n header.s=20251104 header.b=tOKYPyES;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de\n (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de;\n envelope-from=u-boot-bounces@lists.denx.de; receiver=patchwork.ozlabs.org)","phobos.denx.de;\n dmarc=fail (p=none dis=none) header.from=wolfssl.com","phobos.denx.de;\n spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de","phobos.denx.de;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=wolfssl-com.20251104.gappssmtp.com\n header.i=@wolfssl-com.20251104.gappssmtp.com header.b=\"tOKYPyES\";\n\tdkim-atps=neutral","phobos.denx.de;\n dmarc=fail (p=none dis=none) header.from=wolfssl.com","phobos.denx.de;\n spf=pass smtp.mailfrom=aidan@wolfssl.com"],"Received":["from phobos.denx.de (phobos.denx.de\n [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4gC6cZ3byXz1yCg\n\tfor <incoming@patchwork.ozlabs.org>; Sat, 09 May 2026 10:41:34 +1000 (AEST)","from h2850616.stratoserver.net (localhost [IPv6:::1])\n\tby phobos.denx.de (Postfix) with ESMTP id 32F6284E34;\n\tSat,  9 May 2026 02:40:16 +0200 (CEST)","by phobos.denx.de (Postfix, from userid 109)\n id 6314F84E10; Sat,  9 May 2026 02:04:54 +0200 (CEST)","from mail-dl1-x122e.google.com (mail-dl1-x122e.google.com\n [IPv6:2607:f8b0:4864:20::122e])\n (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits))\n (No client certificate requested)\n by phobos.denx.de (Postfix) with ESMTPS id CF14484E0A\n for <u-boot@lists.denx.de>; Sat,  9 May 2026 02:04:51 +0200 (CEST)","by mail-dl1-x122e.google.com with SMTP id\n a92af1059eb24-12713e56abdso1740800c88.1\n for <u-boot@lists.denx.de>; Fri, 08 May 2026 17:04:51 -0700 (PDT)","from localhost.localdomain ([207.231.76.218])\n by smtp.gmail.com with ESMTPSA id\n a92af1059eb24-132787673ffsm5505030c88.15.2026.05.08.17.04.49\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Fri, 08 May 2026 17:04:49 -0700 (PDT)"],"X-Spam-Checker-Version":"SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de","X-Spam-Level":"*","X-Spam-Status":"No, score=1.4 required=5.0 tests=BAYES_00,DKIM_SIGNED,\n DKIM_VALID,RCVD_IN_DNSWL_BLOCKED,RCVD_IN_SBL_CSS,SPF_HELO_NONE,\n SPF_PASS autolearn=no autolearn_force=no version=3.4.2","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=wolfssl-com.20251104.gappssmtp.com; s=20251104; t=1778285090; x=1778889890;\n darn=lists.denx.de;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:from:to:cc:subject:date\n :message-id:reply-to;\n bh=A76CLe5OoYZj7fv7gI0UxJtBvXWI13dCHitFpmL3r2Y=;\n b=tOKYPyESeiPpe77j4GHerNlApVD+G8HFtbP/kFoRYN5nN1GNiXi2MQRRMA3gaOklOo\n h2tZw88I+locQrwjZupGwCwnTX6nEFE5HQieXgKjh6GWvaFO8mSCNwIR7tpYCaCbXF04\n E8S3C/c12P0Haypdk0rCWu0gAKeKkojudcxASjHkZoyMqPwhMm2evgzvYCy/Lx2/tnI8\n PuPO/iATO0ejcX/C45dNedIZUyS0cfcpJJJJ86LAWDfA7DB9H2W5i30TlEN2PEHHquTK\n qS+1BFa6jGmm+u8S17DWJcFqx062H7axf0y6r9R44sUBjf4BamxRD/JyhSP1NVeqhvGg\n qW2Q==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1778285090; x=1778889890;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from\n :to:cc:subject:date:message-id:reply-to;\n bh=A76CLe5OoYZj7fv7gI0UxJtBvXWI13dCHitFpmL3r2Y=;\n b=Z0frPBFznST5ffM8+pSuxrEB79codXDcK6k+PwmsmsnzYxTcUzampeccbbdBb6rOQR\n l8h6PGtcpBfzE44He7Po4/fwvC4GcK7DFaUyow4FX4g8iohP1YbGesj7XirKP965UQSl\n rm4z895NaOuu7Skv2ck0E7yy0JMcvdGYY/5lAX9pr/JRSjAIqBQGtdZvjzxc6xxzBIsw\n ymNuZ3j9exMUuzDRNCh8deO7nrrozsq6N/q37yKNaHFZ6URDfoAIJxORcIjGGg2SjHWC\n YpxX3oGKiloo/Hq+zwZYs+cgsQc534ndHl3+II4nj+qKp+WBJXpM8ZviTSsXsald0ThJ\n H9Rw==","X-Gm-Message-State":"AOJu0YynfddUyv352Ubf4fOST9LaKUIyqXCnXOojIPEEmdQeBSpS0YyE\n CRpREV+uFr4fh0Pxv7KH5zl9tCDwmwzmQF56309vDWmVpCjIvwv++o8Ogw+SYtiI4lzOp+dDVzf\n 5SDu+","X-Gm-Gg":"AeBDieug+cXlXPwELyjHv5c3ukQtuKteauqjlU4jvEpqtJwaMJasDYEGnrYkq/nA22n\n E0pqOH8iR3SMDj5CZOL3Qnyhij5KvXmAanIN8NlDu4nmKlNs46m3yJPa2ZFoXGm3tCoMLouqpak\n ZMBFUxcMDkBqXc5eVc2q0fCZZKFvmMuA9NGp0uBrWnklB/EvLLIOGOShXvQnZaS87CzxWZrfUNM\n /blS9MF8zM2akQmtOqfIPcGQN6Y9kSpd7vknenlp7Xv9T5kVbncKztCZkuHe+PRbxE4NBsatLaK\n Q/j+7RUXo+c989ixi6aqaG2SkH3WuM2lWET/oFL9I1d3ZLZT2ZDXeK2Bz6giADPMwPKTQrysTeI\n oqG5CPfocmsGmR8APguh7DCZ/6/yCMpF0xyOKbKXBmWN3DnJg7iTlFjolQR9Co9XAfvQw6LJGlV\n 33Vi+JENyKKl6wlv+MkbRxjU2tvA8jd3SZD3fyklrfrk9sJFUiDh5FVVWUiq3/NvN/Q0wAtUMbS\n slJ0U2pGPCkh4Crcl323QBmjAXI00DG","X-Received":"by 2002:a05:7022:6187:b0:12a:6d05:3938 with SMTP id\n a92af1059eb24-1323aa67954mr4533273c88.7.1778285089802;\n Fri, 08 May 2026 17:04:49 -0700 (PDT)","From":"Aidan Garske <aidan@wolfssl.com>","To":"u-boot@lists.denx.de","Cc":"David Garske <david@wolfssl.com>,\n Ilias Apalodimas <ilias.apalodimas@linaro.org>","Subject":"[PATCH v3 09/12] tpm: add sandbox TPM SPI emulator","Date":"Fri,  8 May 2026 17:04:16 -0700","Message-ID":"\n <c964c8cc4075a5f331a3dd108f103302bcbcc365.1778277334.git.aidan@wolfssl.com>","X-Mailer":"git-send-email 2.47.3","In-Reply-To":"<cover.1778277334.git.aidan@wolfssl.com>","References":"<cover.1778277334.git.aidan@wolfssl.com>","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit","X-Mailman-Approved-At":"Sat, 09 May 2026 02:40:11 +0200","X-BeenThere":"u-boot@lists.denx.de","X-Mailman-Version":"2.1.39","Precedence":"list","List-Id":"U-Boot discussion <u-boot.lists.denx.de>","List-Unsubscribe":"<https://lists.denx.de/options/u-boot>,\n <mailto:u-boot-request@lists.denx.de?subject=unsubscribe>","List-Archive":"<https://lists.denx.de/pipermail/u-boot/>","List-Post":"<mailto:u-boot@lists.denx.de>","List-Help":"<mailto:u-boot-request@lists.denx.de?subject=help>","List-Subscribe":"<https://lists.denx.de/listinfo/u-boot>,\n <mailto:u-boot-request@lists.denx.de?subject=subscribe>","Errors-To":"u-boot-bounces@lists.denx.de","Sender":"\"U-Boot\" <u-boot-bounces@lists.denx.de>","X-Virus-Scanned":"clamav-milter 0.103.8 at phobos.denx.de","X-Virus-Status":"Clean"},"content":"From: Aidan <aidan@wolfssl.com>\n\nAdd a TPM SPI emulator for sandbox testing that implements the TIS\n(TPM Interface Specification) SPI protocol, allowing wolfTPM's SPI\nHAL code to be tested without physical hardware.\n\ndrivers/tpm/tpm_spi_sandbox.c (new):\n  Emulates a TPM connected via SPI by implementing the TIS register\n  set and SPI protocol:\n  - SPI protocol state machine: parses 4-byte TIS SPI headers\n    (R/W bit, transfer length, register address) and handles data\n    phase with immediate ready signaling (no wait states)\n  - TIS register emulation: TPM_ACCESS (locality request/grant),\n    TPM_STS (command ready, data expect, data available, burst\n    count), TPM_INTF_CAPS, TPM_DID_VID (Infineon SLB9670 IDs),\n    TPM_RID, and TPM_DATA_FIFO (command/response buffering)\n  - TIS state machine: IDLE -> READY -> RECEPTION -> EXECUTION ->\n    COMPLETION, with command-ready abort support\n  - Generates simple TPM_RC_SUCCESS responses (a full implementation\n    would integrate the sandbox TPM2 state machine)\n  - Registers as UCLASS_SPI_EMUL with compatible \"sandbox,tpm-spi-emul\"\n  - Also registers a SPI slave driver (UCLASS_SPI_GENERIC) with\n    compatible \"sandbox,tpm-spi\" for the DTS device node\n\ndrivers/mtd/spi/sandbox.c:\n  Modify sandbox_spi_get_emul() to check for a \"sandbox,emul\"\n  phandle property on SPI slave devices before falling back to the\n  default SPI flash emulation binding. This allows non-flash SPI\n  devices (like the TPM emulator) to specify their own emulator\n  via device tree phandle.\n\nSigned-off-by: Aidan Garske <aidan@wolfssl.com>\n---\n drivers/mtd/spi/sandbox.c     |  30 ++-\n drivers/tpm/tpm_spi_sandbox.c | 410 ++++++++++++++++++++++++++++++++++\n 2 files changed, 431 insertions(+), 9 deletions(-)\n create mode 100644 drivers/tpm/tpm_spi_sandbox.c","diff":"diff --git a/drivers/mtd/spi/sandbox.c b/drivers/mtd/spi/sandbox.c\nindex e5ebc3479fb..41bd07817aa 100644\n--- a/drivers/mtd/spi/sandbox.c\n+++ b/drivers/mtd/spi/sandbox.c\n@@ -571,16 +571,28 @@ int sandbox_spi_get_emul(struct sandbox_state *state,\n \n \tinfo = &state->spi[busnum][cs];\n \tif (!info->emul) {\n-\t\t/* Use the same device tree node as the SPI flash device */\n-\t\tdebug(\"%s: busnum=%u, cs=%u: binding SPI flash emulation: \",\n-\t\t      __func__, busnum, cs);\n-\t\tret = sandbox_sf_bind_emul(state, busnum, cs, bus,\n-\t\t\t\t\t   dev_ofnode(slave), slave->name);\n-\t\tif (ret) {\n-\t\t\tdebug(\"failed (err=%d)\\n\", ret);\n-\t\t\treturn ret;\n+\t\tstruct udevice *emul;\n+\t\tofnode node = dev_ofnode(slave);\n+\n+\t\t/* First check for sandbox,emul phandle property */\n+\t\tret = uclass_get_device_by_phandle(UCLASS_SPI_EMUL, slave,\n+\t\t\t\t\t\t   \"sandbox,emul\", &emul);\n+\t\tif (!ret) {\n+\t\t\tdebug(\"%s: busnum=%u, cs=%u: using phandle emulator\\n\",\n+\t\t\t      __func__, busnum, cs);\n+\t\t\tinfo->emul = emul;\n+\t\t} else {\n+\t\t\t/* Fall back to SPI flash emulation binding */\n+\t\t\tdebug(\"%s: busnum=%u, cs=%u: binding SPI flash emulation: \",\n+\t\t\t      __func__, busnum, cs);\n+\t\t\tret = sandbox_sf_bind_emul(state, busnum, cs, bus,\n+\t\t\t\t\t\t   node, slave->name);\n+\t\t\tif (ret) {\n+\t\t\t\tdebug(\"failed (err=%d)\\n\", ret);\n+\t\t\t\treturn ret;\n+\t\t\t}\n+\t\t\tdebug(\"OK\\n\");\n \t\t}\n-\t\tdebug(\"OK\\n\");\n \t}\n \t*emulp = info->emul;\n \ndiff --git a/drivers/tpm/tpm_spi_sandbox.c b/drivers/tpm/tpm_spi_sandbox.c\nnew file mode 100644\nindex 00000000000..694c5d721f0\n--- /dev/null\n+++ b/drivers/tpm/tpm_spi_sandbox.c\n@@ -0,0 +1,410 @@\n+// SPDX-License-Identifier: GPL-2.0+\n+/*\n+ * Sandbox TPM SPI Emulator\n+ *\n+ * Copyright (c) 2025 wolfSSL Inc.\n+ * Author: Aidan Garske <aidan@wolfssl.com>\n+ *\n+ * Emulates TPM TIS SPI protocol for testing wolfTPM SPI HAL\n+ * without hardware. Wraps the existing sandbox TPM2 state machine.\n+ */\n+\n+#include <dm.h>\n+#include <log.h>\n+#include <spi.h>\n+#include <spi_flash.h>\n+#include <asm/spi.h>\n+#include <asm/state.h>\n+#include <linux/bitops.h>\n+\n+/* TIS register addresses (locality 0) */\n+#define TPM_ACCESS_REG\t\t0x0000\n+#define TPM_INT_ENABLE_REG\t0x0008\n+#define TPM_INTF_CAPS_REG\t0x0014\n+#define TPM_STS_REG\t\t0x0018\n+#define TPM_DATA_FIFO_REG\t0x0024\n+#define TPM_DID_VID_REG\t\t0x0F00\n+#define TPM_RID_REG\t\t0x0F04\n+\n+/* TIS access register bits */\n+#define TPM_ACCESS_VALID\t\t0x80\n+#define TPM_ACCESS_ACTIVE_LOCALITY\t0x20\n+#define TPM_ACCESS_REQUEST_PENDING\t0x04\n+#define TPM_ACCESS_REQUEST_USE\t\t0x02\n+\n+/* TIS status register bits */\n+#define TPM_STS_VALID\t\t0x80\n+#define TPM_STS_COMMAND_READY\t0x40\n+#define TPM_STS_GO\t\t0x20\n+#define TPM_STS_DATA_AVAIL\t0x10\n+#define TPM_STS_DATA_EXPECT\t0x08\n+\n+/* Interface capabilities */\n+#define TPM_INTF_CAPS_VALUE\t0x30000697  /* Typical Infineon value */\n+\n+/* Device/Vendor ID - Infineon SLB9670 */\n+#define TPM_DID_VID_VALUE\t0x001D15D1\n+\n+/* Revision ID */\n+#define TPM_RID_VALUE\t\t0x36\n+\n+/* Maximum buffer sizes */\n+#define TPM_CMD_BUF_SIZE\t4096\n+#define TPM_RSP_BUF_SIZE\t4096\n+#define MAX_SPI_FRAMESIZE\t64\n+\n+/* TPM TIS SPI protocol states */\n+enum tpm_spi_state {\n+\tTPM_SPI_IDLE,\n+\tTPM_SPI_HEADER,\t\t/* Receiving 4-byte header */\n+\tTPM_SPI_WAIT_STATE,\t/* Sending wait state bytes */\n+\tTPM_SPI_DATA,\t\t/* Transfer data */\n+};\n+\n+/* TIS state machine */\n+enum tpm_tis_state {\n+\tTIS_IDLE,\n+\tTIS_READY,\t\t/* Ready to receive command */\n+\tTIS_RECEPTION,\t\t/* Receiving command data */\n+\tTIS_EXECUTION,\t\t/* Executing command */\n+\tTIS_COMPLETION,\t\t/* Response available */\n+};\n+\n+struct sandbox_tpm_spi {\n+\t/* SPI protocol state */\n+\tenum tpm_spi_state spi_state;\n+\tu8 header[4];\n+\tint header_pos;\n+\tbool is_read;\n+\tu32 addr;\n+\tint xfer_len;\n+\tint data_pos;\n+\n+\t/* TIS state */\n+\tenum tpm_tis_state tis_state;\n+\tu8 access_reg;\n+\tu32 sts_reg;\n+\tu32 intf_caps;\n+\n+\t/* Command/response buffers */\n+\tu8 cmd_buf[TPM_CMD_BUF_SIZE];\n+\tint cmd_len;\n+\tint cmd_pos;\n+\tu8 rsp_buf[TPM_RSP_BUF_SIZE];\n+\tint rsp_len;\n+\tint rsp_pos;\n+\n+\t/* Burst count for status register */\n+\tu16 burst_count;\n+};\n+\n+/*\n+ * Parse TIS SPI header\n+ * Format: [R/W|len-1][0xD4][addr_hi][addr_lo]\n+ * Bit 7 of byte 0: 1=read, 0=write\n+ * Bits 5:0 of byte 0: transfer length - 1\n+ */\n+static void parse_spi_header(struct sandbox_tpm_spi *priv)\n+{\n+\tpriv->is_read = (priv->header[0] & 0x80) != 0;\n+\tpriv->xfer_len = (priv->header[0] & 0x3F) + 1;\n+\tpriv->addr = (priv->header[2] << 8) | priv->header[3];\n+\tpriv->data_pos = 0;\n+}\n+\n+/*\n+ * Read from TIS register\n+ */\n+static u8 tis_reg_read(struct sandbox_tpm_spi *priv, u32 addr)\n+{\n+\tu32 reg = addr & 0x0FFF;  /* Mask off locality bits */\n+\n+\tswitch (reg) {\n+\tcase TPM_ACCESS_REG:\n+\t\treturn priv->access_reg;\n+\n+\tcase TPM_STS_REG:\n+\tcase TPM_STS_REG + 1:\n+\tcase TPM_STS_REG + 2:\n+\tcase TPM_STS_REG + 3: {\n+\t\tint byte_off = reg - TPM_STS_REG;\n+\t\tu32 sts = priv->sts_reg;\n+\n+\t\t/* Update burst count in status */\n+\t\tsts |= ((u32)priv->burst_count << 8);\n+\t\treturn (sts >> (byte_off * 8)) & 0xFF;\n+\t}\n+\n+\tcase TPM_INTF_CAPS_REG:\n+\tcase TPM_INTF_CAPS_REG + 1:\n+\tcase TPM_INTF_CAPS_REG + 2:\n+\tcase TPM_INTF_CAPS_REG + 3: {\n+\t\tint byte_off = reg - TPM_INTF_CAPS_REG;\n+\n+\t\treturn (priv->intf_caps >> (byte_off * 8)) & 0xFF;\n+\t}\n+\n+\tcase TPM_DID_VID_REG:\n+\tcase TPM_DID_VID_REG + 1:\n+\tcase TPM_DID_VID_REG + 2:\n+\tcase TPM_DID_VID_REG + 3: {\n+\t\tint byte_off = reg - TPM_DID_VID_REG;\n+\n+\t\treturn (TPM_DID_VID_VALUE >> (byte_off * 8)) & 0xFF;\n+\t}\n+\n+\tcase TPM_RID_REG:\n+\t\treturn TPM_RID_VALUE;\n+\n+\tdefault:\n+\t\t/*\n+\t\t * Handle FIFO reads - the FIFO can be accessed at any address\n+\t\t * from 0x0024 up to 0x0F00 for multi-byte transfers.\n+\t\t */\n+\t\tif (reg >= TPM_DATA_FIFO_REG && reg < TPM_DID_VID_REG) {\n+\t\t\tif (priv->tis_state == TIS_COMPLETION &&\n+\t\t\t    priv->rsp_pos < priv->rsp_len) {\n+\t\t\t\tu8 data = priv->rsp_buf[priv->rsp_pos++];\n+\n+\t\t\t\t/* Update status when all data read */\n+\t\t\t\tif (priv->rsp_pos >= priv->rsp_len) {\n+\t\t\t\t\tpriv->sts_reg &= ~TPM_STS_DATA_AVAIL;\n+\t\t\t\t\tpriv->sts_reg |= TPM_STS_COMMAND_READY;\n+\t\t\t\t\tpriv->tis_state = TIS_READY;\n+\t\t\t\t}\n+\t\t\t\treturn data;\n+\t\t\t}\n+\t\t\treturn 0xFF;\n+\t\t}\n+\t\treturn 0xFF;\n+\t}\n+}\n+\n+/*\n+ * Write to TIS register\n+ */\n+static void tis_reg_write(struct sandbox_tpm_spi *priv, u32 addr, u8 value)\n+{\n+\tu32 reg = addr & 0x0FFF;\n+\n+\tswitch (reg) {\n+\tcase TPM_ACCESS_REG:\n+\t\tif (value & TPM_ACCESS_REQUEST_USE) {\n+\t\t\t/* Request locality */\n+\t\t\tpriv->access_reg |= TPM_ACCESS_ACTIVE_LOCALITY;\n+\t\t\tpriv->access_reg |= TPM_ACCESS_VALID;\n+\t\t}\n+\t\tbreak;\n+\n+\tcase TPM_STS_REG:\n+\t\tif (value & TPM_STS_COMMAND_READY) {\n+\t\t\t/* Abort current command and go to ready state */\n+\t\t\tpriv->tis_state = TIS_READY;\n+\t\t\tpriv->cmd_len = 0;\n+\t\t\tpriv->cmd_pos = 0;\n+\t\t\tpriv->rsp_len = 0;\n+\t\t\tpriv->rsp_pos = 0;\n+\t\t\tpriv->sts_reg = TPM_STS_VALID | TPM_STS_COMMAND_READY;\n+\t\t\tpriv->burst_count = MAX_SPI_FRAMESIZE;\n+\t\t}\n+\t\tif (value & TPM_STS_GO) {\n+\t\t\t/* Execute command */\n+\t\t\tif (priv->tis_state == TIS_RECEPTION &&\n+\t\t\t    priv->cmd_len > 0) {\n+\t\t\t\t/*\n+\t\t\t\t * Generate a simple success response.\n+\t\t\t\t * A full implementation would call the\n+\t\t\t\t * sandbox TPM2 state machine here.\n+\t\t\t\t */\n+\t\t\t\tpriv->rsp_buf[0] = 0x80;  /* TPM_ST_NO_SESSIONS */\n+\t\t\t\tpriv->rsp_buf[1] = 0x01;\n+\t\t\t\tpriv->rsp_buf[2] = 0x00;  /* Response size: 10 */\n+\t\t\t\tpriv->rsp_buf[3] = 0x00;\n+\t\t\t\tpriv->rsp_buf[4] = 0x00;\n+\t\t\t\tpriv->rsp_buf[5] = 0x0A;\n+\t\t\t\tpriv->rsp_buf[6] = 0x00;  /* TPM_RC_SUCCESS */\n+\t\t\t\tpriv->rsp_buf[7] = 0x00;\n+\t\t\t\tpriv->rsp_buf[8] = 0x00;\n+\t\t\t\tpriv->rsp_buf[9] = 0x00;\n+\t\t\t\tpriv->rsp_len = 10;\n+\t\t\t\tpriv->rsp_pos = 0;\n+\n+\t\t\t\tpriv->tis_state = TIS_COMPLETION;\n+\t\t\t\tpriv->sts_reg = TPM_STS_VALID |\n+\t\t\t\t\t\tTPM_STS_DATA_AVAIL;\n+\t\t\t}\n+\t\t}\n+\t\tbreak;\n+\n+\tdefault:\n+\t\t/*\n+\t\t * Handle FIFO writes - the FIFO is at 0x0024 but any address\n+\t\t * from 0x0024 up to 0x0F00 can be used for FIFO access when\n+\t\t * doing multi-byte transfers (address auto-increments).\n+\t\t */\n+\t\tif (reg >= TPM_DATA_FIFO_REG && reg < TPM_DID_VID_REG) {\n+\t\t\tif (priv->tis_state == TIS_READY) {\n+\t\t\t\t/* Start receiving command */\n+\t\t\t\tpriv->tis_state = TIS_RECEPTION;\n+\t\t\t\tpriv->cmd_len = 0;\n+\t\t\t\tpriv->cmd_pos = 0;\n+\t\t\t\tpriv->sts_reg = TPM_STS_VALID | TPM_STS_DATA_EXPECT;\n+\t\t\t}\n+\t\t\tif (priv->tis_state == TIS_RECEPTION) {\n+\t\t\t\tif (priv->cmd_len < TPM_CMD_BUF_SIZE) {\n+\t\t\t\t\tpriv->cmd_buf[priv->cmd_len++] = value;\n+\n+\t\t\t\t\t/* Check if we have complete command */\n+\t\t\t\t\tif (priv->cmd_len >= 6) {\n+\t\t\t\t\t\tu32 expected_len;\n+\n+\t\t\t\t\t\texpected_len = (priv->cmd_buf[2] << 24) |\n+\t\t\t\t\t\t\t       (priv->cmd_buf[3] << 16) |\n+\t\t\t\t\t\t\t       (priv->cmd_buf[4] << 8) |\n+\t\t\t\t\t\t\t       priv->cmd_buf[5];\n+\t\t\t\t\t\tif (priv->cmd_len >= expected_len) {\n+\t\t\t\t\t\t\t/* Command complete */\n+\t\t\t\t\t\t\tpriv->sts_reg &=\n+\t\t\t\t\t\t\t\t~TPM_STS_DATA_EXPECT;\n+\t\t\t\t\t\t}\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t\tbreak;\n+\t}\n+}\n+\n+/*\n+ * SPI emulation transfer callback\n+ */\n+static int sandbox_tpm_spi_xfer(struct udevice *dev, unsigned int bitlen,\n+\t\t\t\tconst void *dout, void *din, unsigned long flags)\n+{\n+\tstruct sandbox_tpm_spi *priv = dev_get_priv(dev);\n+\tint bytes = bitlen / 8;\n+\tconst u8 *tx = dout;\n+\tu8 *rx = din;\n+\tint i;\n+\n+\t/* Handle CS assert - reset state machine */\n+\tif (flags & SPI_XFER_BEGIN) {\n+\t\tpriv->spi_state = TPM_SPI_HEADER;\n+\t\tpriv->header_pos = 0;\n+\t}\n+\n+\tfor (i = 0; i < bytes; i++) {\n+\t\tu8 tx_byte = tx ? tx[i] : 0;\n+\t\tu8 rx_byte = 0;\n+\n+\t\tswitch (priv->spi_state) {\n+\t\tcase TPM_SPI_IDLE:\n+\t\t\t/* Should not happen during active transfer */\n+\t\t\trx_byte = 0xFF;\n+\t\t\tbreak;\n+\n+\t\tcase TPM_SPI_HEADER:\n+\t\t\t/* Receive 4-byte header */\n+\t\t\tpriv->header[priv->header_pos++] = tx_byte;\n+\t\t\trx_byte = 0x00;\n+\n+\t\t\tif (priv->header_pos >= 4) {\n+\t\t\t\tparse_spi_header(priv);\n+\t\t\t\tlog_debug(\"TPM SPI: %s len=%d addr=0x%04x\\n\",\n+\t\t\t\t\t  priv->is_read ? \"read\" : \"write\",\n+\t\t\t\t\t  priv->xfer_len, priv->addr);\n+\t\t\t\t/* Return wait state in last header byte */\n+\t\t\t\trx_byte = 0x01;  /* Ready immediately */\n+\t\t\t\tpriv->spi_state = TPM_SPI_DATA;\n+\t\t\t}\n+\t\t\tbreak;\n+\n+\t\tcase TPM_SPI_DATA:\n+\t\t\tif (priv->is_read) {\n+\t\t\t\t/* Read from TPM register */\n+\t\t\t\trx_byte = tis_reg_read(priv,\n+\t\t\t\t\t\t       priv->addr + priv->data_pos);\n+\t\t\t} else {\n+\t\t\t\t/* Write to TPM register */\n+\t\t\t\ttis_reg_write(priv, priv->addr + priv->data_pos,\n+\t\t\t\t\t      tx_byte);\n+\t\t\t\trx_byte = 0x00;\n+\t\t\t}\n+\t\t\tpriv->data_pos++;\n+\t\t\tbreak;\n+\n+\t\tdefault:\n+\t\t\trx_byte = 0xFF;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tif (rx)\n+\t\t\trx[i] = rx_byte;\n+\t}\n+\n+\t/* Handle CS deassert - return to idle */\n+\tif (flags & SPI_XFER_END)\n+\t\tpriv->spi_state = TPM_SPI_IDLE;\n+\n+\treturn 0;\n+}\n+\n+static int sandbox_tpm_spi_probe(struct udevice *dev)\n+{\n+\tstruct sandbox_tpm_spi *priv = dev_get_priv(dev);\n+\n+\t/* Initialize TIS state */\n+\tpriv->spi_state = TPM_SPI_IDLE;\n+\tpriv->tis_state = TIS_IDLE;\n+\tpriv->access_reg = TPM_ACCESS_VALID;\n+\tpriv->sts_reg = TPM_STS_VALID;\n+\tpriv->intf_caps = TPM_INTF_CAPS_VALUE;\n+\tpriv->burst_count = MAX_SPI_FRAMESIZE;\n+\tpriv->cmd_len = 0;\n+\tpriv->rsp_len = 0;\n+\n+\tlog_debug(\"TPM SPI sandbox emulator probed\\n\");\n+\n+\treturn 0;\n+}\n+\n+static const struct dm_spi_emul_ops sandbox_tpm_spi_ops = {\n+\t.xfer = sandbox_tpm_spi_xfer,\n+};\n+\n+static const struct udevice_id sandbox_tpm_spi_ids[] = {\n+\t{ .compatible = \"sandbox,tpm-spi-emul\" },\n+\t{ }\n+};\n+\n+U_BOOT_DRIVER(sandbox_tpm_spi_emul) = {\n+\t.name = \"sandbox_tpm_spi_emul\",\n+\t.id = UCLASS_SPI_EMUL,\n+\t.of_match = sandbox_tpm_spi_ids,\n+\t.ops = &sandbox_tpm_spi_ops,\n+\t.probe = sandbox_tpm_spi_probe,\n+\t.priv_auto = sizeof(struct sandbox_tpm_spi),\n+};\n+\n+/*\n+ * SPI slave driver for TPM device\n+ * This gets probed when a device with \"sandbox,tpm-spi\" is found in DTS.\n+ * The actual SPI transfers are handled by the emulator above.\n+ */\n+static int sandbox_tpm_spi_slave_probe(struct udevice *dev)\n+{\n+\tlog_debug(\"TPM SPI slave device probed\\n\");\n+\treturn 0;\n+}\n+\n+static const struct udevice_id sandbox_tpm_spi_slave_ids[] = {\n+\t{ .compatible = \"sandbox,tpm-spi\" },\n+\t{ }\n+};\n+\n+U_BOOT_DRIVER(sandbox_tpm_spi) = {\n+\t.name = \"sandbox_tpm_spi\",\n+\t.id = UCLASS_SPI_GENERIC,\n+\t.of_match = sandbox_tpm_spi_slave_ids,\n+\t.probe = sandbox_tpm_spi_slave_probe,\n+};\n","prefixes":["v3","09/12"]}