From: Florian Fainelli <florian.fainelli@broadcom.com>
To: Daniel Drake <dan@reactivated.net>,
"Joerg Roedel (AMD)" <joro@8bytes.org>,
Will Deacon <will@kernel.org>,
Robin Murphy <robin.murphy@arm.com>,
Rob Herring <robh@kernel.org>,
Krzysztof Kozlowski <krzk+dt@kernel.org>,
Conor Dooley <conor+dt@kernel.org>,
Broadcom internal kernel review list
<bcm-kernel-feedback-list@broadcom.com>
Cc: iommu@lists.linux.dev, linux-kernel@vger.kernel.org,
devicetree@vger.kernel.org, linux-rpi-kernel@lists.infradead.org,
linux-arm-kernel@lists.infradead.org,
nick.hollinghurst@raspberrypi.com, james.quinlan@broadcom.com,
Jason Gunthorpe <jgg@ziepe.ca>
Subject: Re: [PATCH v3 4/5] iommu: Add Broadcom BCM2712 IOMMU driver
Date: Wed, 26 Aug 2026 11:33:49 -0700 [thread overview]
Message-ID: <635282ef-5c24-4820-abfa-7eaaea6e7f0e@broadcom.com> (raw)
In-Reply-To: <20260825-bcm2712-iommu-submit-v3-4-7f143e7289b6@reactivated.net>
On 8/25/26 13:56, Daniel Drake wrote:
> This IOMMU translates memory access requests for the VC6 display
> pipeline and various multimedia devices in the Broadcom BCM2712 SoC used
> on Raspberry Pi 5.
>
> The driver uses the generic_pt framework to manage the 2-level page
> tables. It also provides support for the centralized L2 TLB (IOMMUC),
> which is shared across the SoC's IOMMU instances and must be flushed
> when modifying or unmapping page table entries.
>
> Adapted from Raspberry Pi's downstream bcm2712-iommu driver
> (original author Nick Hollinghurst).
>
> Signed-off-by: Daniel Drake <dan@reactivated.net>
> ---
> drivers/iommu/Kconfig | 15 +
> drivers/iommu/Makefile | 1 +
> drivers/iommu/bcm2712-iommu-cache.c | 83 ++++++
> drivers/iommu/bcm2712-iommu-cache.h | 9 +
> drivers/iommu/bcm2712-iommu.c | 556 ++++++++++++++++++++++++++++++++++++
> 5 files changed, 664 insertions(+)
>
> diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
> index 6e07bd69467a..f2c8788158b0 100644
> --- a/drivers/iommu/Kconfig
> +++ b/drivers/iommu/Kconfig
> @@ -386,6 +386,21 @@ config VSI_IOMMU
> Say Y here if you want to use this IOMMU in front of these
> hardware blocks.
>
> +config BCM2712_IOMMU
> + bool "BCM2712 IOMMU driver"
> + depends on (ARCH_BCM && ARM64) || COMPILE_TEST
> + select IOMMU_API
> + select GENERIC_PT
> + select IOMMU_PT
> + select IOMMU_PT_BCM2712
> + help
> + Support for IOMMU on BCM2712 SoC. This IOMMU can be used by the
> + display controller and various multimedia devices to perform
> + efficient memory management.
> +
> + Say Y here if you want to use this IOMMU in front of these
> + hardware blocks.
> +
> config IOMMU_DEBUG_PAGEALLOC
> bool "Debug IOMMU mappings against page allocations"
> depends on DEBUG_PAGEALLOC && IOMMU_API && PAGE_EXTENSION
> diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
> index 2f05725eaab1..29a26d2a3af1 100644
> --- a/drivers/iommu/Makefile
> +++ b/drivers/iommu/Makefile
> @@ -37,4 +37,5 @@ obj-$(CONFIG_IOMMU_IOPF) += io-pgfault.o
> obj-$(CONFIG_SPRD_IOMMU) += sprd-iommu.o
> obj-$(CONFIG_APPLE_DART) += apple-dart.o
> obj-$(CONFIG_VSI_IOMMU) += vsi-iommu.o
> +obj-$(CONFIG_BCM2712_IOMMU) += bcm2712-iommu.o bcm2712-iommu-cache.o
> obj-$(CONFIG_IOMMU_DEBUG_PAGEALLOC) += iommu-debug-pagealloc.o
> diff --git a/drivers/iommu/bcm2712-iommu-cache.c b/drivers/iommu/bcm2712-iommu-cache.c
> new file mode 100644
> index 000000000000..040cfc4ccf9f
> --- /dev/null
> +++ b/drivers/iommu/bcm2712-iommu-cache.c
> @@ -0,0 +1,83 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * IOMMU driver for BCM2712 TLB cache
> + *
> + * Copyright (c) 2023 Raspberry Pi Ltd.
> + * Copyright (c) 2026 Daniel Drake
> + *
> + * The BCM2712 IOMMUC is a centralized TLB which accelerates address translation
> + * across the SoC's IOMMU devices. If an address mapping is not found in the
> + * IOMMU's local TLB cache, then this IOMMUC is consulted. The IOMMUC must be
> + * explicitly invalidated when modifying or unmapping IOMMU page tables.
> + */
> +
> +#include <linux/cleanup.h>
> +#include <linux/err.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/spinlock.h>
> +#include <linux/iopoll.h>
> +
> +#include "bcm2712-iommu-cache.h"
> +
> +struct bcm2712_iommu_cache {
> + struct device *dev;
> + spinlock_t hw_lock;
> + void __iomem *reg_base;
> +};
> +
> +#define MMUC_CONTROL_ENABLE 1
> +#define MMUC_CONTROL_FLUSH 2
> +#define MMUC_CONTROL_FLUSHING 4
Those should be BIT(0), BIT(1) and BIT(2) respectively.
> +
> +void bcm2712_iommu_cache_flush(struct bcm2712_iommu_cache *cache)
> +{
> + u32 val;
> + int ret;
> +
> + scoped_guard(spinlock_irqsave, &cache->hw_lock) {
> + writel(MMUC_CONTROL_ENABLE | MMUC_CONTROL_FLUSH,
> + cache->reg_base);
> +
> + ret = readl_poll_timeout_atomic(cache->reg_base, val,
> + !(val & MMUC_CONTROL_FLUSHING),
> + 0, 50);
> + }
> +
> + if (ret)
> + dev_err_ratelimited(cache->dev, "cache flush timed out\n");
> +}
> +
> +static int bcm2712_iommu_cache_probe(struct platform_device *pdev)
> +{
> + struct bcm2712_iommu_cache *cache;
> +
> + cache = devm_kzalloc(&pdev->dev, sizeof(*cache), GFP_KERNEL);
> + if (!cache)
> + return -ENOMEM;
> +
> + cache->dev = &pdev->dev;
> + spin_lock_init(&cache->hw_lock);
> +
> + cache->reg_base = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(cache->reg_base))
> + return PTR_ERR(cache->reg_base);
> +
> + platform_set_drvdata(pdev, cache);
> + return 0;
> +}
> +
> +static const struct of_device_id bcm2712_iommu_cache_of_match[] = {
> + { .compatible = "brcm,bcm2712-iommuc" },
> + { /* sentinel */ },
> +};
> +
> +static struct platform_driver bcm2712_iommu_cache_driver = {
> + .probe = bcm2712_iommu_cache_probe,
> + .driver = {
> + .name = "bcm2712-iommu-cache",
> + .of_match_table = bcm2712_iommu_cache_of_match,
> + .suppress_bind_attrs = true,
> + },
> +};
> +builtin_platform_driver(bcm2712_iommu_cache_driver);
> diff --git a/drivers/iommu/bcm2712-iommu-cache.h b/drivers/iommu/bcm2712-iommu-cache.h
> new file mode 100644
> index 000000000000..d2f7851831cc
> --- /dev/null
> +++ b/drivers/iommu/bcm2712-iommu-cache.h
> @@ -0,0 +1,9 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +#ifndef _BCM2712_IOMMU_CACHE_H
> +#define _BCM2712_IOMMU_CACHE_H
> +
> +struct bcm2712_iommu_cache;
> +
> +void bcm2712_iommu_cache_flush(struct bcm2712_iommu_cache *cache);
> +
> +#endif
> diff --git a/drivers/iommu/bcm2712-iommu.c b/drivers/iommu/bcm2712-iommu.c
> new file mode 100644
> index 000000000000..1bb456a83df5
> --- /dev/null
> +++ b/drivers/iommu/bcm2712-iommu.c
> @@ -0,0 +1,556 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * IOMMU driver for Broadcom BCM2712
> + *
> + * Copyright (c) 2023-2025 Raspberry Pi Ltd.
> + * Copyright (c) 2026 Daniel Drake
> + *
> + * Each BCM2712 IOMMU has multiple devices hardwired into it, whose
> + * DMA transactions all route through the IOMMU. There is no stream ID tagging
> + * or any other kind of segmentation to differentiate between requests from
> + * different devices. It is also not possible to toggle a specific device
> + * between iommu-mapped and bypass modes.
> + *
> + * The hardware supports the simultaneous configuration of a bypass window
> + * (where memory accesses operate in identity mode) and a translation aperture
> + * (supporting page mapping). While combining these two would allow for
> + * GART-like operation, this driver's paging domain implementation configures
> + * only the translation aperture (starting at IOVA 0) offering a degree of
> + * memory protection.
> + *
> + * The page table format is a two-level format handled by generic_pt/bcm2712.
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/cleanup.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/err.h>
> +#include <linux/iommu.h>
> +#include <linux/iopoll.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/spinlock.h>
> +#include <linux/sizes.h>
> +#include <linux/generic_pt/iommu.h>
> +
> +#include "bcm2712-iommu-cache.h"
> +#include "dma-iommu.h"
> +#include "iommu-pages.h"
> +
> +/* BCM2712 IOMMU is organized around 4Kbyte pages */
> +#define IOMMU_PAGE_SHIFT 12
> +#define IOMMU_PAGE_SIZE (1ul << IOMMU_PAGE_SHIFT)
> +/* A PTE is 4 bytes */
> +#define PTE_SIZE_SHIFT 2
> +/* L1/L2 table sizing (IOMMU hardware pages): 1024 entries per page */
> +#define PTES_PER_IOPG_SHIFT (IOMMU_PAGE_SHIFT - PTE_SIZE_SHIFT)
> +
> +#define MMMU_CTRL_OFFSET 0x00
> +#define MMMU_CTRL_CAP_EXCEEDED BIT(27)
> +#define MMMU_CTRL_CAP_EXCEEDED_ABORT_EN BIT(26)
> +#define MMMU_CTRL_CAP_EXCEEDED_INT_EN BIT(25)
> +#define MMMU_CTRL_CAP_EXCEEDED_EXCEPTION_EN BIT(24)
> +#define MMMU_CTRL_PT_INVALID BIT(20)
> +#define MMMU_CTRL_PT_INVALID_ABORT_EN BIT(19)
> +#define MMMU_CTRL_PT_INVALID_INT_EN BIT(18)
> +#define MMMU_CTRL_PT_INVALID_EXCEPTION_EN BIT(17)
> +#define MMMU_CTRL_PT_INVALID_EN BIT(16)
> +#define MMMU_CTRL_WRITE_VIOLATION BIT(12)
> +#define MMMU_CTRL_WRITE_VIOLATION_ABORT_EN BIT(11)
> +#define MMMU_CTRL_WRITE_VIOLATION_INT_EN BIT(10)
> +#define MMMU_CTRL_WRITE_VIOLATION_EXCEPTION_EN BIT(9)
> +#define MMMU_CTRL_BYPASS BIT(8)
> +#define MMMU_CTRL_TLB_CLEARING BIT(7)
> +#define MMMU_CTRL_STATS_CLEAR BIT(3)
> +#define MMMU_CTRL_TLB_CLEAR BIT(2)
> +#define MMMU_CTRL_STATS_ENABLE BIT(1)
> +#define MMMU_CTRL_ENABLE BIT(0)
> +
> +#define MMMU_CTRL_OPERATING_FLAGS (\
> + MMMU_CTRL_CAP_EXCEEDED_ABORT_EN | \
> + MMMU_CTRL_PT_INVALID_ABORT_EN | \
> + MMMU_CTRL_PT_INVALID_EN | \
> + MMMU_CTRL_WRITE_VIOLATION_ABORT_EN | \
> + MMMU_CTRL_STATS_ENABLE | \
> + MMMU_CTRL_ENABLE)
> +
> +#define MMMU_PT_PA_BASE_OFFSET 0x04
> +
> +#define MMMU_ADDR_CAP_OFFSET 0x14
> +#define MMMU_ADDR_CAP_ENABLE BIT(31)
> +#define ADDR_CAP_SHIFT ilog2(SZ_256M)
> +
> +#define MMMU_SHOOT_DOWN_OFFSET 0x18
> +#define MMMU_SHOOT_DOWN_SHOOTING BIT(31)
> +#define MMMU_SHOOT_DOWN_SHOOT BIT(30)
> +
> +#define MMMU_BYPASS_START_OFFSET 0x1c
> +#define MMMU_BYPASS_START_ENABLE BIT(31)
> +
> +#define MMMU_BYPASS_END_OFFSET 0x20
> +#define MMMU_BYPASS_END_ENABLE BIT(31)
> +
> +#define MMMU_MISC_OFFSET 0x24
> +#define MMMU_MISC_SINGLE_TABLE BIT(31)
> +
> +#define MMMU_ILLEGAL_ADR_OFFSET 0x30
> +#define MMMU_ILLEGAL_ADR_ENABLE BIT(31)
> +
> +#define MMMU_DEBUG_INFO_OFFSET 0x38
> +#define MMMU_DEBUG_INFO_VERSION_MASK 0x0000000Fu
> +#define MMMU_DEBUG_INFO_VA_WIDTH_MASK 0x000000F0u
> +#define MMMU_DEBUG_INFO_PA_WIDTH_MASK 0x00000F00u
> +#define MMMU_DEBUG_INFO_BIGPAGE_WIDTH_MASK 0x000FF000u
> +#define MMMU_DEBUG_INFO_SUPERPAGE_WIDTH_MASK 0x0FF00000u
> +#define MMMU_DEBUG_INFO_BYPASS_4M BIT(28)
> +#define MMMU_DEBUG_INFO_BYPASS BIT(29)
> +
> +struct bcm2712_iommu {
> + struct device *dev;
> + struct iommu_device iommu;
> + struct bcm2712_iommu_domain *domain;
> + struct bcm2712_iommu_cache *cache;
> + void __iomem *reg_base;
> + spinlock_t hw_lock;
> + size_t bigpage_size;
> + size_t superpage_size;
> +};
> +
> +struct bcm2712_iommu_domain {
> + union {
> + struct iommu_domain base;
> + struct pt_iommu_bcm2712 pt;
> + };
> + struct bcm2712_iommu *mmu;
> + void *default_page;
> +};
> +
> +#define MMU_WR(off, val) writel(val, mmu->reg_base + (off))
> +#define MMU_RD(off) readl(mmu->reg_base + (off))
I would prefer using static inline wrappers here because it gets super
easy to just instrument those with debug prints by adding a couple of
lines, it also gives you type validation at the same time.
[snip]
> +static struct iommu_domain bcm2712_blocking_domain = {
> + .type = IOMMU_DOMAIN_BLOCKED,
> + .ops = &(const struct iommu_domain_ops) {
> + .attach_dev = bcm2712_iommu_blocking_attach,
> + },
> +};
> +
> +static int bcm2712_iommu_enable_and_clear_tlb(struct bcm2712_iommu *mmu)
Maybe add __must_hold() here to indicate when this is called?
The rest looks good to me, but I defer to Jim for the functional review.
--
Florian
next prev parent reply other threads:[~2026-08-26 18:34 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-25 20:56 [PATCH v3 0/5] Add support for Broadcom BCM2712 IOMMU driver (Raspberry Pi 5) Daniel Drake
2026-08-25 20:56 ` [PATCH v3 1/5] iommu_pt: Fix test_pgsize_boundary() failure on narrow-OA formats Daniel Drake
2026-08-26 18:18 ` Jason Gunthorpe
2026-08-25 20:56 ` [PATCH v3 2/5] dt-bindings: iommu: Add Broadcom BCM2712 IOMMU Daniel Drake
2026-08-26 20:19 ` Florian Fainelli
2026-08-28 10:16 ` Krzysztof Kozlowski
2026-08-25 20:56 ` [PATCH v3 3/5] iommu/generic_pt: Add Broadcom BCM2712 page table format Daniel Drake
2026-08-26 18:18 ` Jason Gunthorpe
2026-08-25 20:56 ` [PATCH v3 4/5] iommu: Add Broadcom BCM2712 IOMMU driver Daniel Drake
2026-08-26 18:18 ` Jason Gunthorpe
2026-08-26 18:33 ` Florian Fainelli [this message]
2026-08-25 20:56 ` [PATCH v3 5/5] arm64: dts: broadcom: bcm2712: Add GPU IOMMU and IOMMU cache nodes Daniel Drake
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=635282ef-5c24-4820-abfa-7eaaea6e7f0e@broadcom.com \
--to=florian.fainelli@broadcom.com \
--cc=bcm-kernel-feedback-list@broadcom.com \
--cc=conor+dt@kernel.org \
--cc=dan@reactivated.net \
--cc=devicetree@vger.kernel.org \
--cc=iommu@lists.linux.dev \
--cc=james.quinlan@broadcom.com \
--cc=jgg@ziepe.ca \
--cc=joro@8bytes.org \
--cc=krzk+dt@kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-rpi-kernel@lists.infradead.org \
--cc=nick.hollinghurst@raspberrypi.com \
--cc=robh@kernel.org \
--cc=robin.murphy@arm.com \
--cc=will@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox