{"id":2235244,"url":"http://patchwork.ozlabs.org/api/1.2/patches/2235244/?format=json","web_url":"http://patchwork.ozlabs.org/project/uboot/patch/6ab2025f19dfc764f2d56b866f839da7475a3773.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":"<6ab2025f19dfc764f2d56b866f839da7475a3773.1778277334.git.aidan@wolfssl.com>","list_archive_url":null,"date":"2026-05-09T00:04:10","name":"[v3,03/12] spi: add BCM2835/BCM2711 hardware SPI controller driver","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"f22036b0485d7b7a8447cf636c1b26945e70e88c","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/6ab2025f19dfc764f2d56b866f839da7475a3773.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/2235244/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/2235244/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=hKdeNAt8;\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=\"hKdeNAt8\";\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 4gC6bf6NYgz1yCg\n\tfor <incoming@patchwork.ozlabs.org>; Sat, 09 May 2026 10:40:46 +1000 (AEST)","from h2850616.stratoserver.net (localhost [IPv6:::1])\n\tby phobos.denx.de (Postfix) with ESMTP id BF61684E16;\n\tSat,  9 May 2026 02:40:14 +0200 (CEST)","by phobos.denx.de (Postfix, from userid 109)\n id 5AABB84E0B; Sat,  9 May 2026 02:04:43 +0200 (CEST)","from mail-dl1-x122b.google.com (mail-dl1-x122b.google.com\n [IPv6:2607:f8b0:4864:20::122b])\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 C2FDF84E0A\n for <u-boot@lists.denx.de>; Sat,  9 May 2026 02:04:40 +0200 (CEST)","by mail-dl1-x122b.google.com with SMTP id\n a92af1059eb24-12c1a170a50so3638636c88.0\n for <u-boot@lists.denx.de>; Fri, 08 May 2026 17:04:40 -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.38\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Fri, 08 May 2026 17:04:38 -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=1778285079; x=1778889879;\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=zDJkHdwNQ4gZHJU8+cDBCIAaeAuvZnTWqbphhAXvWJ0=;\n b=hKdeNAt8Tup9WJwgK2ElQ1nKQWTewzW7R6JrQmEg7a4//HvoCGfuUnhHFCDgsjEB1f\n 42ebyFONb5JpcheGpMzd6XRdO/LjvNVrkM5m6UkShbXc3b+1mQEEomB727dTIAuJID+m\n IJ7W965/WEA57hHDkAr2kK4eKlcR8+rxxo80xz/g1/K0HPx09S4H2pu9otNcyQrREGSP\n D2gdRKu3kZa66aPwJx8PArRgK/ZcRFsVmH6N2CSEsyWo2jAyk3L51oyqApR4i3k5kVrP\n yv8lY+62glhW1+8kbXOoG2zK1by3gZrtlXL+MjolCcok8wG+zpouHe5+ReOcut/bAj9e\n ACiQ==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1778285079; x=1778889879;\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=zDJkHdwNQ4gZHJU8+cDBCIAaeAuvZnTWqbphhAXvWJ0=;\n b=AuC7PlrUmjwvxNWC+pL9FB9pQrmmMMD3RSTrPUuf/rdbCLa5gcOO21HsL4aJJXQxLZ\n M3uWUpp1F9Ptfz6HcTuTBdmaf43n3XrSsri/haNooKW4oSbdCdIxRrQH/aFSn0WBFHCm\n qFThqVbePx7DnWK/FHx9+p2p0l/QMAVLq1J/mrd3r3VZ8LFQwaBjzQKOG4iG+Q2BTn+Q\n FHgoJwd+gEjjQl+qY59hmnbU7v/laUgcY9rwEqulyXOd6kC/YOi/Ecqt0Rr1WtwTK2+v\n zlS62S1oDDg2RNroGUAB2DjLLjMCgC0ClkrznONh1seim/AliM7U3xf2K5/YJ+Z9h1vL\n fmSQ==","X-Gm-Message-State":"AOJu0Yxv899WLNjrvZ5yeNxCSQYqHWbSGHmafkPNHWqKQd8YaRiJEK2m\n cc90jeJNMtQF4fhYlUgnchWgMu83Y2yV7FVqlrsPgrLMqOyfKO8b4ZHZXM0pd+bLCt6OWKK71S+\n lcgqt","X-Gm-Gg":"AeBDieuibm+9VqLEVCbkav4OqQ2cRKsM4j5r7XvP0yM7uycHUAB8r+IVCiBftzfqCJU\n VPOuTUSxq/QfdhYDmUzT/Ny2VZfRLDe76tWOoH8RdK+smCfDJJyKuLvQeArh3tsVL8LkamI8zl1\n KyQ3rKjqK4v4cvpBwY3Umd8BmY6bSxNFzLOX+mpLczz9IMl6350fMiJYs/d/rGsFCQH0TcwBkVO\n RcpXnUO3NtfR7YUXf55qlI/7812fuQzLPPYIUOFcJNEMu2digtj29q7m8Pf+582B7BtdVvkD9FJ\n PYI2vgjFpKLgy6zDBXC8ssjrKTlFGMFXRkr64OXEYIqB3j7Yc3B5BwLEiDP0sv9sccdFMqhizx0\n ZjN1QHNB/KqmlbY/y1ktfYAbL350NYlzMnUmL6tCkxl3D9WOpavVfCiWjSI8cGl2wlQD4kjkFHy\n Ri1E/Otdt8sI8tzdZd6I1FAEH7vIxyrga7lNHF5E81G1d8uc3c5wK3TesJv50jAJifhfW6dOFn+\n o7LMxs7uqfMZrrLzS4g08v2BLwAC1Ze","X-Received":"by 2002:a05:701b:240e:b0:132:5d96:3c0f with SMTP id\n a92af1059eb24-1325d963c6cmr2398605c88.31.1778285078767;\n Fri, 08 May 2026 17:04:38 -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 03/12] spi: add BCM2835/BCM2711 hardware SPI controller\n driver","Date":"Fri,  8 May 2026 17:04:10 -0700","Message-ID":"\n <6ab2025f19dfc764f2d56b866f839da7475a3773.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 hardware SPI controller driver for the BCM2835/BCM2711 SoC\nfound on Raspberry Pi 3 and Pi 4 boards.\n\nThe driver implements:\n- Register-based SPI transfers using the BCM2835 SPI0 peripheral\n- Software GPIO chip-select control (matching the Linux driver\n  approach) rather than hardware CS, which avoids issues with\n  automatic CS deassertion during multi-byte TPM TIS transactions\n- Clock divider calculation for configurable SPI speed\n- SPI mode 0/1/2/3 support via CPOL/CPHA configuration\n- GPIO pin setup for SPI0 (MISO/MOSI/SCLK as ALT0, CE0/CE1 as\n  output for software CS)\n- TPM reset via GPIO4/GPIO24 during probe\n\nThis driver is needed for wolfTPM to communicate with SPI-attached\nTPM chips (e.g., Infineon SLB9670/9672) on Raspberry Pi hardware.\n\nSigned-off-by: Aidan Garske <aidan@wolfssl.com>\n---\n drivers/spi/Kconfig       |   9 +\n drivers/spi/Makefile      |   1 +\n drivers/spi/bcm2835_spi.c | 431 ++++++++++++++++++++++++++++++++++++++\n 3 files changed, 441 insertions(+)\n create mode 100644 drivers/spi/bcm2835_spi.c","diff":"diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig\nindex 63d61ccf8ed..02ee2b2e6a0 100644\n--- a/drivers/spi/Kconfig\n+++ b/drivers/spi/Kconfig\n@@ -116,6 +116,15 @@ config ATMEL_SPI\n \t  many AT91 (ARM) chips. This driver can be used to access\n \t  the SPI Flash, such as AT25DF321.\n \n+config BCM2835_SPI\n+\tbool \"BCM2835/BCM2711 SPI driver\"\n+\tdepends on ARCH_BCM283X\n+\thelp\n+\t  Enable the BCM2835/BCM2711 SPI controller driver. This driver\n+\t  can be used to access SPI devices on Raspberry Pi boards\n+\t  including Pi 3 and Pi 4. It uses the hardware SPI controller\n+\t  rather than GPIO bit-banging.\n+\n config BCM63XX_HSSPI\n \tbool \"BCM63XX HSSPI driver\"\n \tdepends on (ARCH_BMIPS || ARCH_BCMBCA)\ndiff --git a/drivers/spi/Makefile b/drivers/spi/Makefile\nindex 0dc2d23e172..47a1c6194b1 100644\n--- a/drivers/spi/Makefile\n+++ b/drivers/spi/Makefile\n@@ -27,6 +27,7 @@ obj-$(CONFIG_APPLE_SPI) += apple_spi.o\n obj-$(CONFIG_ATH79_SPI) += ath79_spi.o\n obj-$(CONFIG_ATMEL_QSPI) += atmel-quadspi.o\n obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o\n+obj-$(CONFIG_BCM2835_SPI) += bcm2835_spi.o\n obj-$(CONFIG_BCM63XX_HSSPI) += bcm63xx_hsspi.o\n obj-$(CONFIG_BCMBCA_HSSPI) += bcmbca_hsspi.o\n obj-$(CONFIG_BCM63XX_SPI) += bcm63xx_spi.o\ndiff --git a/drivers/spi/bcm2835_spi.c b/drivers/spi/bcm2835_spi.c\nnew file mode 100644\nindex 00000000000..8133a41a0f8\n--- /dev/null\n+++ b/drivers/spi/bcm2835_spi.c\n@@ -0,0 +1,431 @@\n+// SPDX-License-Identifier: GPL-2.0+\n+/*\n+ * BCM2835/BCM2711 SPI controller driver for U-Boot\n+ *\n+ * Copyright (C) 2025 wolfSSL Inc.\n+ * Author: Aidan Garske <aidan@wolfssl.com>\n+ *\n+ * Based on Linux driver by Chris Boot, Martin Sperl, et al.\n+ */\n+\n+#include <dm.h>\n+#include <errno.h>\n+#include <log.h>\n+#include <malloc.h>\n+#include <spi.h>\n+#include <asm/io.h>\n+#include <asm/gpio.h>\n+#include <dm/device_compat.h>\n+#include <linux/delay.h>\n+\n+/* SPI register offsets */\n+#define BCM2835_SPI_CS      0x00    /* Control and Status */\n+#define BCM2835_SPI_FIFO    0x04    /* TX and RX FIFOs */\n+#define BCM2835_SPI_CLK     0x08    /* Clock Divider */\n+#define BCM2835_SPI_DLEN    0x0c    /* Data Length */\n+#define BCM2835_SPI_LTOH    0x10    /* LoSSI mode TOH */\n+#define BCM2835_SPI_DC      0x14    /* DMA DREQ Controls */\n+\n+/* CS register bits */\n+#define BCM2835_SPI_CS_LEN_LONG     BIT(25)\n+#define BCM2835_SPI_CS_DMA_LEN      BIT(24)\n+#define BCM2835_SPI_CS_CSPOL2       BIT(23)\n+#define BCM2835_SPI_CS_CSPOL1       BIT(22)\n+#define BCM2835_SPI_CS_CSPOL0       BIT(21)\n+#define BCM2835_SPI_CS_RXF          BIT(20)\n+#define BCM2835_SPI_CS_RXR          BIT(19)\n+#define BCM2835_SPI_CS_TXD          BIT(18)\n+#define BCM2835_SPI_CS_RXD          BIT(17)\n+#define BCM2835_SPI_CS_DONE         BIT(16)\n+#define BCM2835_SPI_CS_LEN          BIT(13)\n+#define BCM2835_SPI_CS_REN          BIT(12)\n+#define BCM2835_SPI_CS_ADCS         BIT(11)\n+#define BCM2835_SPI_CS_INTR         BIT(10)\n+#define BCM2835_SPI_CS_INTD         BIT(9)\n+#define BCM2835_SPI_CS_DMAEN        BIT(8)\n+#define BCM2835_SPI_CS_TA           BIT(7)\n+#define BCM2835_SPI_CS_CSPOL        BIT(6)\n+#define BCM2835_SPI_CS_CLEAR_RX     BIT(5)\n+#define BCM2835_SPI_CS_CLEAR_TX     BIT(4)\n+#define BCM2835_SPI_CS_CPOL         BIT(3)\n+#define BCM2835_SPI_CS_CPHA         BIT(2)\n+#define BCM2835_SPI_CS_CS_10        BIT(1)\n+#define BCM2835_SPI_CS_CS_01        BIT(0)\n+\n+/* Default clock rate - 250 MHz for Pi 4 */\n+#define BCM2835_SPI_DEFAULT_CLK     250000000\n+\n+struct bcm2835_spi_priv {\n+\tvoid __iomem *regs;\n+\tu32 clk_hz;\n+\tu32 cs_reg;         /* Cached CS register value */\n+\tu32 speed_hz;\n+\tu8 mode;\n+\tstruct gpio_desc cs_gpio;\n+\tint cs_gpio_valid;\n+\tint cs_asserted;    /* Track if CS should stay asserted between transfers */\n+};\n+\n+struct bcm2835_spi_plat {\n+\tfdt_addr_t base;\n+\tu32 clk_hz;\n+};\n+\n+static inline u32 bcm2835_spi_readl(struct bcm2835_spi_priv *priv, u32 reg)\n+{\n+\treturn readl(priv->regs + reg);\n+}\n+\n+static inline void bcm2835_spi_writel(struct bcm2835_spi_priv *priv,\n+\t\t\t\t\t\t\t\t\t   u32 reg, u32 val)\n+{\n+\twritel(val, priv->regs + reg);\n+}\n+\n+static void bcm2835_spi_reset(struct bcm2835_spi_priv *priv)\n+{\n+\t/* Clear FIFOs and disable SPI */\n+\tbcm2835_spi_writel(priv, BCM2835_SPI_CS,\n+\t\t\t\t\t   BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX);\n+}\n+\n+/* GPIO base for software CS control */\n+static void __iomem *g_gpio_base = (void __iomem *)0xFE200000;\n+\n+/* Software CS control - assert (LOW = active) */\n+static void bcm2835_spi_cs_assert(int cs_pin)\n+{\n+\t/* GPCLR0 - clear pin (drive LOW) */\n+\twritel(1 << cs_pin, g_gpio_base + 0x28);\n+}\n+\n+/* Software CS control - deassert (HIGH = inactive) */\n+static void bcm2835_spi_cs_deassert(int cs_pin)\n+{\n+\t/* GPSET0 - set pin (drive HIGH) */\n+\twritel(1 << cs_pin, g_gpio_base + 0x1C);\n+}\n+\n+static int bcm2835_spi_xfer(struct udevice *dev, unsigned int bitlen,\n+\t\t\t\t\t\t\tconst void *dout, void *din, unsigned long flags)\n+{\n+\tstruct udevice *bus = dev_get_parent(dev);\n+\tstruct bcm2835_spi_priv *priv = dev_get_priv(bus);\n+\tconst u8 *tx = dout;\n+\tu8 *rx = din;\n+\tu32 len = bitlen / 8;\n+\tu32 cs_reg;\n+\tu32 tx_count = 0, rx_count = 0;\n+\tint timeout;\n+\tint cs = spi_chip_select(dev);  /* Get chip select from slave device */\n+\tint cs_pin = (cs == 0) ? 8 : 7; /* CS0=GPIO8, CS1=GPIO7 */\n+\tu32 stat;\n+\n+\tif (bitlen == 0) {\n+\t\t/* Handle CS-only operations (deassert) */\n+\t\tif (flags & SPI_XFER_END) {\n+\t\t\tbcm2835_spi_cs_deassert(cs_pin);\n+\t\t\tpriv->cs_asserted = 0;\n+\t\t}\n+\t\treturn 0;\n+\t}\n+\n+\tif (bitlen % 8) {\n+\t\tdev_err(dev, \"Non-byte-aligned transfer not supported\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\t/*\n+\t * SOFTWARE GPIO CHIP SELECT - like Linux driver\n+\t * Don't use hardware CS bits - set to 0 (unused)\n+\t */\n+\tcs_reg = priv->cs_reg & ~(BCM2835_SPI_CS_CS_10 | BCM2835_SPI_CS_CS_01);\n+\n+\t/* Assert CS at start of transaction (SPI_XFER_BEGIN) */\n+\tif (flags & SPI_XFER_BEGIN) {\n+\t\tbcm2835_spi_cs_assert(cs_pin);\n+\t\tpriv->cs_asserted = 1;\n+\t\tudelay(1);  /* CS setup time */\n+\t}\n+\n+\t/* Clear FIFOs for new transaction */\n+\tif (flags & SPI_XFER_BEGIN) {\n+\t\tbcm2835_spi_writel(priv, BCM2835_SPI_CS,\n+\t\t\t\t\t\t   cs_reg | BCM2835_SPI_CS_CLEAR_RX |\n+\t\t\t\t\t\t   BCM2835_SPI_CS_CLEAR_TX);\n+\t\tudelay(1);\n+\t}\n+\n+\t/* Start transfer with TA=1 (but CS is controlled by GPIO, not hardware) */\n+\tbcm2835_spi_writel(priv, BCM2835_SPI_CS, cs_reg | BCM2835_SPI_CS_TA);\n+\n+\t/* Poll for completion - transfer byte by byte */\n+\ttimeout = 100000;\n+\twhile ((tx_count < len || rx_count < len) && timeout > 0) {\n+\t\tstat = bcm2835_spi_readl(priv, BCM2835_SPI_CS);\n+\n+\t\t/* TX FIFO not full - send next byte */\n+\t\twhile ((stat & BCM2835_SPI_CS_TXD) && tx_count < len) {\n+\t\t\tu8 byte = tx ? tx[tx_count] : 0;\n+\n+\t\t\tbcm2835_spi_writel(priv, BCM2835_SPI_FIFO, byte);\n+\t\t\ttx_count++;\n+\t\t\tstat = bcm2835_spi_readl(priv, BCM2835_SPI_CS);\n+\t\t}\n+\n+\t\t/* RX FIFO has data - read it */\n+\t\twhile ((stat & BCM2835_SPI_CS_RXD) && rx_count < len) {\n+\t\t\tu8 byte = bcm2835_spi_readl(priv, BCM2835_SPI_FIFO) & 0xff;\n+\n+\t\t\tif (rx)\n+\t\t\t\trx[rx_count] = byte;\n+\t\t\trx_count++;\n+\t\t\tstat = bcm2835_spi_readl(priv, BCM2835_SPI_CS);\n+\t\t}\n+\n+\t\ttimeout--;\n+\t}\n+\n+\t/* Wait for DONE */\n+\ttimeout = 10000;\n+\twhile (!(bcm2835_spi_readl(priv, BCM2835_SPI_CS) & BCM2835_SPI_CS_DONE) &&\n+\t\t   timeout > 0) {\n+\t\tudelay(1);\n+\t\ttimeout--;\n+\t}\n+\n+\t/* Read any remaining RX data from FIFO */\n+\twhile (bcm2835_spi_readl(priv, BCM2835_SPI_CS) & BCM2835_SPI_CS_RXD) {\n+\t\tu8 byte = bcm2835_spi_readl(priv, BCM2835_SPI_FIFO) & 0xff;\n+\n+\t\tif (rx && rx_count < len)\n+\t\t\trx[rx_count++] = byte;\n+\t}\n+\n+\t/* Clear TA to complete this transfer (doesn't affect GPIO CS) */\n+\tbcm2835_spi_writel(priv, BCM2835_SPI_CS, cs_reg);\n+\n+\t/*\n+\t * SOFTWARE GPIO CHIP SELECT control:\n+\t * - SPI_XFER_END: deassert CS (GPIO HIGH)\n+\t * - No END flag: keep CS asserted for next transfer\n+\t */\n+\tif (flags & SPI_XFER_END) {\n+\t\tbcm2835_spi_cs_deassert(cs_pin);\n+\t\tpriv->cs_asserted = 0;\n+\t} else {\n+\t\t/* Keep CS asserted for next transfer (e.g., wait state polling) */\n+\t\tpriv->cs_asserted = 1;\n+\t}\n+\n+\tif (timeout == 0) {\n+\t\tbcm2835_spi_cs_deassert(cs_pin);  /* Make sure CS is released */\n+\t\treturn -ETIMEDOUT;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int bcm2835_spi_set_speed(struct udevice *bus, uint speed)\n+{\n+\tstruct bcm2835_spi_priv *priv = dev_get_priv(bus);\n+\tu32 cdiv;\n+\n+\tif (speed == 0)\n+\t\tspeed = 1000000;  /* Default 1 MHz */\n+\n+\tpriv->speed_hz = speed;\n+\n+\t/* Calculate clock divider */\n+\tif (speed >= priv->clk_hz / 2) {\n+\t\tcdiv = 2;  /* Fastest possible */\n+\t} else {\n+\t\tcdiv = (priv->clk_hz + speed - 1) / speed;\n+\t\tcdiv += (cdiv & 1);  /* Must be even */\n+\t\tif (cdiv >= 65536)\n+\t\t\tcdiv = 0;  /* Slowest: clk/65536 */\n+\t}\n+\n+\tbcm2835_spi_writel(priv, BCM2835_SPI_CLK, cdiv);\n+\n+\treturn 0;\n+}\n+\n+static int bcm2835_spi_set_mode(struct udevice *bus, uint mode)\n+{\n+\tstruct bcm2835_spi_priv *priv = dev_get_priv(bus);\n+\tu32 cs_reg = 0;\n+\n+\tpriv->mode = mode;\n+\n+\t/* Set clock polarity and phase */\n+\tif (mode & SPI_CPOL)\n+\t\tcs_reg |= BCM2835_SPI_CS_CPOL;\n+\tif (mode & SPI_CPHA)\n+\t\tcs_reg |= BCM2835_SPI_CS_CPHA;\n+\n+\t/* CS bits will be set in xfer based on slave's chip select */\n+\tpriv->cs_reg = cs_reg;\n+\n+\treturn 0;\n+}\n+\n+static int bcm2835_spi_claim_bus(struct udevice *dev)\n+{\n+\treturn 0;\n+}\n+\n+static int bcm2835_spi_release_bus(struct udevice *dev)\n+{\n+\treturn 0;\n+}\n+\n+/* Setup GPIO pins for SPI0 with SOFTWARE chip select */\n+static void bcm2835_spi_setup_gpio(void)\n+{\n+\tu32 val;\n+\n+\t/*\n+\t * SPI0 pin configuration:\n+\t * GPIO7  (CE1)  - OUTPUT (software CS) - GPFSEL0 bits 23:21 = 001\n+\t * GPIO8  (CE0)  - OUTPUT (software CS) - GPFSEL0 bits 26:24 = 001\n+\t * GPIO9  (MISO) - ALT0 (SPI)           - GPFSEL0 bits 29:27 = 100\n+\t * GPIO10 (MOSI) - ALT0 (SPI)           - GPFSEL1 bits 2:0   = 100\n+\t * GPIO11 (SCLK) - ALT0 (SPI)           - GPFSEL1 bits 5:3   = 100\n+\t */\n+\n+\t/* Set GPIO7, GPIO8 to OUTPUT, GPIO9 to ALT0 in GPFSEL0 */\n+\tval = readl(g_gpio_base + 0x00);\n+\tval &= ~((7 << 21) | (7 << 24) | (7 << 27));  /* Clear GPIO7,8,9 */\n+\tval |= (1 << 21);   /* GPIO7 = OUTPUT (001) */\n+\tval |= (1 << 24);   /* GPIO8 = OUTPUT (001) */\n+\tval |= (4 << 27);   /* GPIO9 = ALT0 (100) for MISO */\n+\twritel(val, g_gpio_base + 0x00);\n+\n+\t/* Set GPIO10, GPIO11 to ALT0 in GPFSEL1 */\n+\tval = readl(g_gpio_base + 0x04);\n+\tval &= ~((7 << 0) | (7 << 3));  /* Clear GPIO10,11 */\n+\tval |= (4 << 0);    /* GPIO10 = ALT0 (100) for MOSI */\n+\tval |= (4 << 3);    /* GPIO11 = ALT0 (100) for SCLK */\n+\twritel(val, g_gpio_base + 0x04);\n+\n+\t/* Deassert both CS lines (HIGH = inactive) */\n+\tbcm2835_spi_cs_deassert(7);  /* CE1 */\n+\tbcm2835_spi_cs_deassert(8);  /* CE0 */\n+}\n+\n+/* TPM Reset via GPIO4 and GPIO24 */\n+static void bcm2835_spi_tpm_reset(void)\n+{\n+\tvoid __iomem *gpio_base = (void __iomem *)0xFE200000;\n+\tu32 val;\n+\n+\t/* Set GPIO4 as output (GPFSEL0, bits 14:12) */\n+\tval = readl(gpio_base + 0x00);  /* GPFSEL0 */\n+\tval &= ~(7 << 12);  /* Clear bits 14:12 for GPIO4 */\n+\tval |= (1 << 12);   /* Set to output */\n+\twritel(val, gpio_base + 0x00);\n+\n+\t/* Set GPIO24 as output (GPFSEL2, bits 14:12) */\n+\tval = readl(gpio_base + 0x08);  /* GPFSEL2 */\n+\tval &= ~(7 << 12);  /* Clear bits 14:12 for GPIO24 */\n+\tval |= (1 << 12);   /* Set to output */\n+\twritel(val, gpio_base + 0x08);\n+\n+\t/* Assert reset on BOTH pins (LOW) */\n+\twritel((1 << 4) | (1 << 24), gpio_base + 0x28);  /* GPCLR0 */\n+\tmdelay(100);\n+\n+\t/* Release reset on BOTH pins (HIGH) */\n+\twritel((1 << 4) | (1 << 24), gpio_base + 0x1C);  /* GPSET0 */\n+\tmdelay(150);  /* Wait for TPM to initialize */\n+}\n+\n+static int bcm2835_spi_probe(struct udevice *bus)\n+{\n+\tstruct bcm2835_spi_plat *plat = dev_get_plat(bus);\n+\tstruct bcm2835_spi_priv *priv = dev_get_priv(bus);\n+\tint ret;\n+\n+\tpriv->regs = (void __iomem *)plat->base;\n+\tpriv->clk_hz = plat->clk_hz ? plat->clk_hz : BCM2835_SPI_DEFAULT_CLK;\n+\n+\t/* Setup GPIO pins for SPI0 (ALT0 function) */\n+\tbcm2835_spi_setup_gpio();\n+\n+\t/* Reset TPM before using SPI */\n+\tbcm2835_spi_tpm_reset();\n+\n+\t/* Try to get CS GPIO from device tree */\n+\tret = gpio_request_by_name(bus, \"cs-gpios\", 0, &priv->cs_gpio,\n+\t\t\t\t   GPIOD_IS_OUT | GPIOD_ACTIVE_LOW);\n+\tif (!ret) {\n+\t\tpriv->cs_gpio_valid = 1;\n+\t\t/* Deassert CS initially */\n+\t\tdm_gpio_set_value(&priv->cs_gpio, 1);\n+\t} else {\n+\t\tpriv->cs_gpio_valid = 0;\n+\t}\n+\n+\t/* Reset the SPI controller */\n+\tbcm2835_spi_reset(priv);\n+\n+\t/* Set default speed and mode */\n+\tbcm2835_spi_set_speed(bus, 1000000);  /* 1 MHz default */\n+\tbcm2835_spi_set_mode(bus, SPI_MODE_0);\n+\n+\treturn 0;\n+}\n+\n+static int bcm2835_spi_of_to_plat(struct udevice *bus)\n+{\n+\tstruct bcm2835_spi_plat *plat = dev_get_plat(bus);\n+\tfdt_addr_t addr;\n+\n+\taddr = dev_read_addr(bus);\n+\tif (addr == FDT_ADDR_T_NONE) {\n+\t\tdev_err(bus, \"Failed to get SPI base address\\n\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\t/*\n+\t * On BCM2711 (Pi 4), the device tree often uses VideoCore bus addresses\n+\t * which start with 0x7E. The ARM needs to access these via the ARM\n+\t * peripheral base at 0xFE000000.\n+\t */\n+\tif ((addr & 0xFF000000) == 0x7E000000)\n+\t\taddr = (addr & 0x00FFFFFF) | 0xFE000000;\n+\n+\tplat->base = addr;\n+\n+\t/* Try to get clock rate from device tree */\n+\tplat->clk_hz = dev_read_u32_default(bus, \"clock-frequency\",\n+\t\t\t\t\t     BCM2835_SPI_DEFAULT_CLK);\n+\n+\treturn 0;\n+}\n+\n+static const struct dm_spi_ops bcm2835_spi_ops = {\n+\t.claim_bus      = bcm2835_spi_claim_bus,\n+\t.release_bus    = bcm2835_spi_release_bus,\n+\t.xfer           = bcm2835_spi_xfer,\n+\t.set_speed      = bcm2835_spi_set_speed,\n+\t.set_mode       = bcm2835_spi_set_mode,\n+};\n+\n+static const struct udevice_id bcm2835_spi_ids[] = {\n+\t{ .compatible = \"brcm,bcm2835-spi\" },\n+\t{ .compatible = \"brcm,bcm2711-spi\" },\n+\t{ }\n+};\n+\n+U_BOOT_DRIVER(bcm2835_spi) = {\n+\t.name           = \"bcm2835_spi\",\n+\t.id             = UCLASS_SPI,\n+\t.of_match       = bcm2835_spi_ids,\n+\t.ops            = &bcm2835_spi_ops,\n+\t.of_to_plat     = bcm2835_spi_of_to_plat,\n+\t.plat_auto      = sizeof(struct bcm2835_spi_plat),\n+\t.priv_auto      = sizeof(struct bcm2835_spi_priv),\n+\t.probe          = bcm2835_spi_probe,\n+};\n","prefixes":["v3","03/12"]}