-
Notifications
You must be signed in to change notification settings - Fork 183
Fix u-boot to support NVMe devices on Raspberry Pi 5 #6118
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
197 changes: 197 additions & 0 deletions
197
.../patches/patches-v2026.04/0022-nvme-translate-DMA-addresses-through-the-host-bridge.patch
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,197 @@ | ||
| From 5454954298640f70d956a286b81d9d22350876ad Mon Sep 17 00:00:00 2001 | ||
| From: =?UTF-8?q?Ren=C3=AA=20de=20Souza=20Pinto?= <rene@renesp.com.br> | ||
| Date: Thu, 2 Jul 2026 15:58:21 +0200 | ||
| Subject: [PATCH 22/23] nvme: translate DMA addresses through the host bridge | ||
| dma-ranges | ||
| MIME-Version: 1.0 | ||
| Content-Type: text/plain; charset=UTF-8 | ||
| Content-Transfer-Encoding: 8bit | ||
|
|
||
| The NVMe driver programmed the controller with raw CPU physical | ||
| addresses for the admin/IO queues, PRP lists and data buffers. That | ||
| only works when the PCIe host bridge maps system memory into PCI space | ||
| as an identity map. | ||
|
|
||
| On BCM2712 (Raspberry Pi 5) the external PCIe controller's inbound | ||
| window maps system memory at PCI bus offset 0x10_0000_0000 (see the | ||
| controller's dma-ranges). Handing the device a raw CPU address made | ||
| every submission-queue fetch target an unclaimed low PCI address, which | ||
| the root complex answers with an Unsupported Request; the controller | ||
| then sets CSTS.CFS and the admin command times out (-ETIMEDOUT). | ||
|
|
||
| Derive the phys->bus offset from the host bridge's first dma-range and | ||
| add it (dma_shift) to every address the controller consumes. The offset | ||
| is zero on identity-mapped platforms, so behaviour there is unchanged. | ||
|
|
||
| Signed-off-by: Renê de Souza Pinto <rene@renesp.com.br> | ||
| Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> | ||
| --- | ||
| drivers/nvme/nvme.c | 37 +++++++++++++++++++++++-------------- | ||
| drivers/nvme/nvme.h | 1 + | ||
| drivers/nvme/nvme_pci.c | 15 +++++++++++++++ | ||
| 3 files changed, 39 insertions(+), 14 deletions(-) | ||
|
|
||
| diff --git a/drivers/nvme/nvme.c b/drivers/nvme/nvme.c | ||
| index 2b14437f69c..efa83ef077c 100644 | ||
| --- a/drivers/nvme/nvme.c | ||
| +++ b/drivers/nvme/nvme.c | ||
| @@ -44,6 +44,15 @@ static int nvme_wait_csts(struct nvme_dev *dev, u32 mask, u32 val) | ||
| return -ETIME; | ||
| } | ||
|
|
||
| +/* | ||
| + * Translate a CPU physical address to the PCI bus address the controller must | ||
| + * use for DMA. dev->dma_shift is 0 on identity-mapped platforms. | ||
| + */ | ||
| +static inline u64 nvme_dma(struct nvme_dev *dev, u64 phys) | ||
| +{ | ||
| + return phys + dev->dma_shift; | ||
| +} | ||
| + | ||
| static int nvme_setup_prps(struct nvme_dev *dev, u64 *prp2, | ||
| int total_len, u64 dma_addr) | ||
| { | ||
| @@ -66,7 +75,7 @@ static int nvme_setup_prps(struct nvme_dev *dev, u64 *prp2, | ||
| dma_addr += (page_size - offset); | ||
|
|
||
| if (length <= page_size) { | ||
| - *prp2 = dma_addr; | ||
| + *prp2 = nvme_dma(dev, dma_addr); | ||
| return 0; | ||
| } | ||
|
|
||
| @@ -91,16 +100,16 @@ static int nvme_setup_prps(struct nvme_dev *dev, u64 *prp2, | ||
| i = 0; | ||
| while (nprps) { | ||
| if ((i == (prps_per_page - 1)) && nprps > 1) { | ||
| - *(prp_pool + i) = cpu_to_le64((ulong)prp_pool + | ||
| - page_size); | ||
| + *(prp_pool + i) = cpu_to_le64(nvme_dma(dev, | ||
| + (ulong)prp_pool + page_size)); | ||
| i = 0; | ||
| prp_pool += page_size; | ||
| } | ||
| - *(prp_pool + i++) = cpu_to_le64(dma_addr); | ||
| + *(prp_pool + i++) = cpu_to_le64(nvme_dma(dev, dma_addr)); | ||
| dma_addr += page_size; | ||
| nprps--; | ||
| } | ||
| - *prp2 = (ulong)dev->prp_pool; | ||
| + *prp2 = nvme_dma(dev, (ulong)dev->prp_pool); | ||
|
|
||
| flush_dcache_range((ulong)dev->prp_pool, (ulong)dev->prp_pool + | ||
| num_pages * page_size); | ||
| @@ -393,8 +402,8 @@ static int nvme_configure_admin_queue(struct nvme_dev *dev) | ||
| dev->ctrl_config |= NVME_CC_IOSQES | NVME_CC_IOCQES; | ||
|
|
||
| writel(aqa, &dev->bar->aqa); | ||
| - nvme_writeq((ulong)nvmeq->sq_cmds, &dev->bar->asq); | ||
| - nvme_writeq((ulong)nvmeq->cqes, &dev->bar->acq); | ||
| + nvme_writeq(nvme_dma(dev, (ulong)nvmeq->sq_cmds), &dev->bar->asq); | ||
| + nvme_writeq(nvme_dma(dev, (ulong)nvmeq->cqes), &dev->bar->acq); | ||
|
|
||
| result = nvme_enable_ctrl(dev); | ||
| if (result) | ||
| @@ -420,7 +429,7 @@ static int nvme_alloc_cq(struct nvme_dev *dev, u16 qid, | ||
|
|
||
| memset(&c, 0, sizeof(c)); | ||
| c.create_cq.opcode = nvme_admin_create_cq; | ||
| - c.create_cq.prp1 = cpu_to_le64((ulong)nvmeq->cqes); | ||
| + c.create_cq.prp1 = cpu_to_le64(nvme_dma(dev, (ulong)nvmeq->cqes)); | ||
| c.create_cq.cqid = cpu_to_le16(qid); | ||
| c.create_cq.qsize = cpu_to_le16(nvmeq->q_depth - 1); | ||
| c.create_cq.cq_flags = cpu_to_le16(flags); | ||
| @@ -437,7 +446,7 @@ static int nvme_alloc_sq(struct nvme_dev *dev, u16 qid, | ||
|
|
||
| memset(&c, 0, sizeof(c)); | ||
| c.create_sq.opcode = nvme_admin_create_sq; | ||
| - c.create_sq.prp1 = cpu_to_le64((ulong)nvmeq->sq_cmds); | ||
| + c.create_sq.prp1 = cpu_to_le64(nvme_dma(dev, (ulong)nvmeq->sq_cmds)); | ||
| c.create_sq.sqid = cpu_to_le16(qid); | ||
| c.create_sq.qsize = cpu_to_le16(nvmeq->q_depth - 1); | ||
| c.create_sq.sq_flags = cpu_to_le16(flags); | ||
| @@ -458,14 +467,14 @@ int nvme_identify(struct nvme_dev *dev, unsigned nsid, | ||
| memset(&c, 0, sizeof(c)); | ||
| c.identify.opcode = nvme_admin_identify; | ||
| c.identify.nsid = cpu_to_le32(nsid); | ||
| - c.identify.prp1 = cpu_to_le64(dma_addr); | ||
| + c.identify.prp1 = cpu_to_le64(nvme_dma(dev, dma_addr)); | ||
|
|
||
| length -= (page_size - offset); | ||
| if (length <= 0) { | ||
| c.identify.prp2 = 0; | ||
| } else { | ||
| dma_addr += (page_size - offset); | ||
| - c.identify.prp2 = cpu_to_le64(dma_addr); | ||
| + c.identify.prp2 = cpu_to_le64(nvme_dma(dev, dma_addr)); | ||
| } | ||
|
|
||
| c.identify.cns = cpu_to_le32(cns); | ||
| @@ -490,7 +499,7 @@ int nvme_get_features(struct nvme_dev *dev, unsigned fid, unsigned nsid, | ||
| memset(&c, 0, sizeof(c)); | ||
| c.features.opcode = nvme_admin_get_features; | ||
| c.features.nsid = cpu_to_le32(nsid); | ||
| - c.features.prp1 = cpu_to_le64(dma_addr); | ||
| + c.features.prp1 = cpu_to_le64(nvme_dma(dev, dma_addr)); | ||
| c.features.fid = cpu_to_le32(fid); | ||
|
|
||
| ret = nvme_submit_admin_cmd(dev, &c, result); | ||
| @@ -516,7 +525,7 @@ int nvme_set_features(struct nvme_dev *dev, unsigned fid, unsigned dword11, | ||
|
|
||
| memset(&c, 0, sizeof(c)); | ||
| c.features.opcode = nvme_admin_set_features; | ||
| - c.features.prp1 = cpu_to_le64(dma_addr); | ||
| + c.features.prp1 = cpu_to_le64(nvme_dma(dev, dma_addr)); | ||
| c.features.fid = cpu_to_le32(fid); | ||
| c.features.dword11 = cpu_to_le32(dword11); | ||
|
|
||
| @@ -785,7 +794,7 @@ static ulong nvme_blk_rw(struct udevice *udev, lbaint_t blknr, | ||
| c.rw.slba = cpu_to_le64(slba); | ||
| slba += lbas; | ||
| c.rw.length = cpu_to_le16(lbas - 1); | ||
| - c.rw.prp1 = cpu_to_le64(temp_buffer); | ||
| + c.rw.prp1 = cpu_to_le64(nvme_dma(dev, temp_buffer)); | ||
| c.rw.prp2 = cpu_to_le64(prp2); | ||
| status = nvme_submit_sync_cmd(dev->queues[NVME_IO_Q], | ||
| &c, NULL, IO_TIMEOUT); | ||
| diff --git a/drivers/nvme/nvme.h b/drivers/nvme/nvme.h | ||
| index bc1d612dde4..5c2b34b71cc 100644 | ||
| --- a/drivers/nvme/nvme.h | ||
| +++ b/drivers/nvme/nvme.h | ||
| @@ -621,6 +621,7 @@ struct nvme_dev { | ||
| u64 *prp_pool; | ||
| u32 prp_entry_num; | ||
| u32 nn; | ||
| + u64 dma_shift; | ||
| }; | ||
|
|
||
| /* Admin queue and a single I/O queue. */ | ||
| diff --git a/drivers/nvme/nvme_pci.c b/drivers/nvme/nvme_pci.c | ||
| index c24f8cf1eb1..eb29bd266b6 100644 | ||
| --- a/drivers/nvme/nvme_pci.c | ||
| +++ b/drivers/nvme/nvme_pci.c | ||
| @@ -34,6 +34,21 @@ static int nvme_probe(struct udevice *udev) | ||
| /* Turn on bus-mastering */ | ||
| dm_pci_clrset_config16(udev, PCI_COMMAND, 0, PCI_COMMAND_MASTER); | ||
|
|
||
| + /* | ||
| + * The controller's DMA sees host memory through the host bridge's | ||
| + * inbound window (dma-ranges), which on some SoCs (e.g. BCM2712 / | ||
| + * Raspberry Pi 5) is not an identity map. Derive the phys->bus offset | ||
| + * from the controller's first dma-range so queue/PRP/data addresses | ||
| + * handed to the device are translated. Identity maps give 0. | ||
| + */ | ||
| + { | ||
| + struct udevice *ctlr = pci_get_controller(udev); | ||
| + struct pci_region region; | ||
| + | ||
| + if (ctlr && !pci_get_dma_regions(ctlr, ®ion, 0)) | ||
| + ndev->dma_shift = region.bus_start - region.phys_start; | ||
| + } | ||
| + | ||
| return nvme_init(udev); | ||
| } | ||
|
|
||
| -- | ||
| 2.53.0 | ||
|
|
69 changes: 69 additions & 0 deletions
69
...u-boot/patches/patches-v2026.04/0023-pci-brcmstb-align-BCM2712-MISC_CTRL-with-Linux.patch
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| From 98eba5a86a4fbcc743cd0e20e22beeea0ccbdcb1 Mon Sep 17 00:00:00 2001 | ||
| From: =?UTF-8?q?Ren=C3=AA=20de=20Souza=20Pinto?= <rene@renesp.com.br> | ||
| Date: Thu, 2 Jul 2026 15:58:21 +0200 | ||
| Subject: [PATCH 23/23] pci: brcmstb: align BCM2712 MISC_CTRL with Linux | ||
| MIME-Version: 1.0 | ||
| Content-Type: text/plain; charset=UTF-8 | ||
| Content-Transfer-Encoding: 8bit | ||
|
|
||
| Match the mainline Linux pcie-brcmstb driver for the 7712/2712 variant: | ||
| enable PCIE_RCB_64B mode, use max-burst 0x2, and stop programming the | ||
| SCB0 memory-controller window size. 7712/2712 route inbound DMA through | ||
| the per-BAR UBUS remap registers rather than the SCB memory-controller | ||
| windows, so leaving an SCB size enabled sets up a conflicting inbound | ||
| window. | ||
|
|
||
| Signed-off-by: Renê de Souza Pinto <rene@renesp.com.br> | ||
| Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> | ||
| --- | ||
| drivers/pci/pcie_brcmstb.c | 19 ++++++++++++++----- | ||
| 1 file changed, 14 insertions(+), 5 deletions(-) | ||
|
|
||
| diff --git a/drivers/pci/pcie_brcmstb.c b/drivers/pci/pcie_brcmstb.c | ||
| index 1b03b0a7b05..ac774a11dd1 100644 | ||
| --- a/drivers/pci/pcie_brcmstb.c | ||
| +++ b/drivers/pci/pcie_brcmstb.c | ||
| @@ -72,6 +72,7 @@ | ||
| #define PCIE_MISC_UBUS_BAR2_CONFIG_REMAP 0x40b4 | ||
| #define PCIE_MISC_UBUS_BAR2_CONFIG_REMAP_ACCESS_ENABLE_MASK BIT(0) | ||
| #define MISC_CTRL_PCIE_RCB_MPS_MODE_MASK 0x400 | ||
| +#define MISC_CTRL_PCIE_RCB_64B_MODE_MASK 0x80 | ||
|
|
||
| enum { | ||
| RGR1_SW_INIT_1, | ||
| @@ -635,10 +636,12 @@ static int brcm_pcie_probe(struct udevice *dev) | ||
| writel(tmp, base + PCIE_RC_PL_PHY_CTL_15); | ||
| } | ||
|
|
||
| - tmp = (pcie->pcie_cfg->type == BCM2712) ? | ||
| - MISC_CTRL_MAX_BURST_SIZE_128_2712 : | ||
| - MISC_CTRL_MAX_BURST_SIZE_128; | ||
| /* Set SCB_MAX_BURST_SIZE, CFG_READ_UR_MODE, SCB_ACCESS_EN */ | ||
| + if (pcie->pcie_cfg->type == BCM2712) | ||
| + /* Match Linux 7712: burst 0x2 and RCB 64-byte mode */ | ||
| + tmp = (0x2 << 20) | MISC_CTRL_PCIE_RCB_64B_MODE_MASK; | ||
| + else | ||
| + tmp = MISC_CTRL_MAX_BURST_SIZE_128; | ||
| clrsetbits_le32(base + PCIE_MISC_MISC_CTRL, | ||
| MISC_CTRL_MAX_BURST_SIZE_MASK, | ||
| MISC_CTRL_SCB_ACCESS_EN_MASK | | ||
| @@ -648,8 +651,14 @@ static int brcm_pcie_probe(struct udevice *dev) | ||
|
|
||
| tmp = readl(base + PCIE_MISC_MISC_CTRL); | ||
| if (pcie->pcie_cfg->type == BCM2712) { | ||
| - /* BCM2712: fixed 32GB SCB0 window */ | ||
| - u32p_replace_bits(&tmp, 20, MISC_CTRL_SCB0_SIZE_MASK); | ||
| + /* | ||
| + * 7712/2712 route inbound DMA through the per-BAR UBUS remap | ||
| + * registers, not the SCB memory-controller windows. An enabled | ||
| + * SCB size creates a conflicting inbound window that makes the | ||
| + * device's upstream reads fail, so disable it. Linux never | ||
| + * programs SCBx_SIZE on 7712. | ||
| + */ | ||
| + tmp &= ~MISC_CTRL_SCB0_SIZE_MASK; | ||
| } else { | ||
| /* Pre-BCM2712: size SCB0 to match the actual DMA region. | ||
| * rc_bar2_size must be a power of two; ilog2(size) - 15 | ||
| -- | ||
| 2.53.0 | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.