Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
e88338e
arm64: configs: Enable CONFIG_SUSPEND
PineappleBeech Jul 21, 2026
f06b1e5
watchdog: bcm2835: Stop watchdog during suspend/resume
PineappleBeech Jul 21, 2026
b07e653
arm64: dts: broadcom: bcm2712: Power off wireless during suspend/resume
PineappleBeech Jul 21, 2026
bd60a51
ACPI: PCI: bcm2712: Set acpi_storage_d3 to true on bcm2712
PineappleBeech Jul 23, 2026
9b38702
iommu: bcm2712-iommu: Add suspend/resume support
PineappleBeech Aug 12, 2026
964750b
drm/vc4: hdmi: Add suspend/resume support
PineappleBeech Aug 12, 2026
3a8d259
drm/vc4: Add suspend/resume support
PineappleBeech Aug 12, 2026
50238c7
drm/vc4: hvs: Add suspend/resume support
PineappleBeech Aug 12, 2026
0981b46
gpio-brcmstb: Enable hibernation support
PineappleBeech Aug 14, 2026
6b593a7
bcm2385: mailbox: Add resumer hander for S3 wakeup on Pi5
timg236 Aug 17, 2026
b7e14c2
arm64: configs: Enable PM Watchdog and Hibernation for testing
PineappleBeech Aug 17, 2026
0e9b665
pinctrl: pinctrl-brcmstb: Save pin mux and pad during suspend
PineappleBeech Aug 17, 2026
a8b7247
serial: amba-pl011: Add start_rx to re-enable interrupts
PineappleBeech Aug 18, 2026
d627f4f
drm/vc4: hdmi: Ignore the hsm clock on resume
PineappleBeech Aug 18, 2026
e2fe239
irqchip: bcm2712-mip: Restore registers after suspend
PineappleBeech Aug 18, 2026
33b71af
mailbox: rp1: check received event bits more carefully
P33M Aug 19, 2026
35fcd28
mmc: sdhci-brcmstb: Reconfigure on resume
PineappleBeech Aug 19, 2026
f4de196
media: pisp_be: Add suspend/resume support
PineappleBeech Aug 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,7 @@ dpi_16bit_gpio2: &rp1_dpi_16bit_gpio2 { };
vmmc-supply = <&wl_on_reg>;
sd-uhs-ddr50;
non-removable;
cap-power-off-card;
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
Expand Down
1 change: 1 addition & 0 deletions arch/arm64/boot/dts/broadcom/bcm2712-rpi-cm5.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@ dpi_16bit_gpio2: &rp1_dpi_16bit_gpio2 { };
vmmc-supply = <&wl_on_reg>;
sd-uhs-ddr50;
non-removable;
cap-power-off-card;
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
Expand Down
7 changes: 4 additions & 3 deletions arch/arm64/configs/bcm2712_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ CONFIG_ARM64_ERRATUM_2441009=y
CONFIG_ARM64_16K_PAGES=y
CONFIG_ARM64_VA_BITS_47=y
CONFIG_NR_CPUS=4
CONFIG_HOTPLUG_CPU=y
CONFIG_NUMA=y
CONFIG_COMPAT=y
CONFIG_ARMV8_DEPRECATED=y
Expand All @@ -59,9 +58,11 @@ CONFIG_CP15_BARRIER_EMULATION=y
CONFIG_SETEND_EMULATION=y
CONFIG_RANDOMIZE_BASE=y
CONFIG_CMDLINE="console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait"
# CONFIG_SUSPEND is not set
CONFIG_PM=y
CONFIG_HIBERNATION=y
CONFIG_PM_DEBUG=y
CONFIG_DPM_WATCHDOG=y
CONFIG_DPM_WATCHDOG_TIMEOUT=30
CONFIG_DPM_WATCHDOG_WARNING_TIMEOUT=5
CONFIG_CPU_IDLE=y
CONFIG_CPU_FREQ=y
CONFIG_CPU_FREQ_STAT=y
Expand Down
12 changes: 10 additions & 2 deletions drivers/gpio/gpio-brcmstb.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <linux/irqchip/chained_irq.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/string_choices.h>

enum gio_reg_index {
Expand Down Expand Up @@ -576,8 +577,15 @@ static int brcmstb_gpio_resume(struct device *dev)
#endif /* CONFIG_PM_SLEEP */

static const struct dev_pm_ops brcmstb_gpio_pm_ops = {
.suspend_noirq = brcmstb_gpio_suspend,
.resume_noirq = brcmstb_gpio_resume,
/*
* A hibernate power cycle wipes this GPIO bank's registers, same
* as a suspend-to-RAM transition. Only wiring .suspend_noirq/
* .resume_noirq left the save/restore logic below unreachable on
* the freeze/thaw and poweroff/restore phases hibernate actually
* uses, so an output pin's level (and anything else save/restore
* covers) was silently lost across hibernate specifically.
*/
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(brcmstb_gpio_suspend, brcmstb_gpio_resume)
};

static int brcmstb_gpio_probe(struct platform_device *pdev)
Expand Down
17 changes: 17 additions & 0 deletions drivers/gpu/drm/vc4/vc4_drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,22 @@ static void vc4_platform_drm_shutdown(struct platform_device *pdev)
drm_atomic_helper_shutdown(platform_get_drvdata(pdev));
}

static int vc4_drm_suspend(struct device *dev)
{
struct drm_device *drm = dev_get_drvdata(dev);

return drm_mode_config_helper_suspend(drm);
}

static int vc4_drm_resume(struct device *dev)
{
struct drm_device *drm = dev_get_drvdata(dev);

return drm_mode_config_helper_resume(drm);
}

static DEFINE_SIMPLE_DEV_PM_OPS(vc4_drm_pm_ops, vc4_drm_suspend, vc4_drm_resume);

static const struct of_device_id vc4_of_match[] = {
{ .compatible = "brcm,bcm2711-vc5", .data = (void *)VC4_GEN_5 },
/* NB GEN_6_C will be corrected on D0 hw to GEN_6_D via vc4_hvs_bind */
Expand All @@ -511,6 +527,7 @@ static struct platform_driver vc4_platform_driver = {
.driver = {
.name = "vc4-drm",
.of_match_table = vc4_of_match,
.pm = pm_sleep_ptr(&vc4_drm_pm_ops),
},
};

Expand Down
18 changes: 7 additions & 11 deletions drivers/gpu/drm/vc4/vc4_hdmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -3091,19 +3091,13 @@ static int vc4_hdmi_runtime_resume(struct device *dev)
return ret;

/*
* Whenever the RaspberryPi boots without an HDMI monitor
* plugged in, the firmware won't have initialized the HSM clock
* rate and it will be reported as 0.
*
* If we try to access a register of the controller in such a
* case, it will lead to a silent CPU stall. Let's make sure we
* prevent such a case.
* The clock rate may be reported wrong after suspend to RAM.
* This may cause a cpu stall.
*/
rate = clk_get_rate(vc4_hdmi->hsm_clock);
if (!rate) {
ret = -EINVAL;
goto err_disable_clk;
}
if (!rate)
dev_warn(&vc4_hdmi->pdev->dev,
"hsm_clock rate is 0, proceeding anyway\n");

ret = clk_prepare_enable(vc4_hdmi->audio_clock);
if (ret)
Expand Down Expand Up @@ -3441,6 +3435,8 @@ static const struct dev_pm_ops vc4_hdmi_pm_ops = {
SET_RUNTIME_PM_OPS(vc4_hdmi_runtime_suspend,
vc4_hdmi_runtime_resume,
NULL)
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
};

struct platform_driver vc4_hdmi_driver = {
Expand Down
64 changes: 52 additions & 12 deletions drivers/gpu/drm/vc4/vc4_hvs.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <linux/clk.h>
#include <linux/component.h>
#include <linux/platform_device.h>
#include <linux/pm.h>

#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
Expand Down Expand Up @@ -437,12 +438,28 @@ static const u32 nearest_neighbour_kernel[] =
VC4_LINEAR_PHASE_KERNEL(0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 255, 255, 255, 255);

static void vc4_hvs_write_linear_kernel(struct vc4_hvs *hvs,
struct drm_mm_node *space,
const u32 *kernel)
{
u32 __iomem *dst_kernel = hvs->dlist + space->start;
unsigned int i;

for (i = 0; i < VC4_KERNEL_DWORDS; i++) {
if (i < VC4_LINEAR_PHASE_KERNEL_DWORDS)
writel(kernel[i], &dst_kernel[i]);
else {
writel(kernel[VC4_KERNEL_DWORDS - i - 1],
&dst_kernel[i]);
}
}
}

static int vc4_hvs_upload_linear_kernel(struct vc4_hvs *hvs,
struct drm_mm_node *space,
const u32 *kernel)
{
int ret, i;
u32 __iomem *dst_kernel;
int ret;

/*
* NOTE: We don't need a call to drm_dev_enter()/drm_dev_exit()
Expand All @@ -456,16 +473,7 @@ static int vc4_hvs_upload_linear_kernel(struct vc4_hvs *hvs,
return ret;
}

dst_kernel = hvs->dlist + space->start;

for (i = 0; i < VC4_KERNEL_DWORDS; i++) {
if (i < VC4_LINEAR_PHASE_KERNEL_DWORDS)
writel(kernel[i], &dst_kernel[i]);
else {
writel(kernel[VC4_KERNEL_DWORDS - i - 1],
&dst_kernel[i]);
}
}
vc4_hvs_write_linear_kernel(hvs, space, kernel);

return 0;
}
Expand Down Expand Up @@ -2109,6 +2117,8 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
if (IS_ERR(hvs))
return PTR_ERR(hvs);

platform_set_drvdata(pdev, hvs);

hvs->regset.base = hvs->regs;

if (vc4->gen == VC4_GEN_6_C) {
Expand Down Expand Up @@ -2291,6 +2301,35 @@ static void vc4_hvs_dev_remove(struct platform_device *pdev)
component_del(&pdev->dev, &vc4_hvs_ops);
}

static int vc4_hvs_resume_early(struct device *dev)
{
struct vc4_hvs *hvs = platform_get_drvdata(to_platform_device(dev));
struct vc4_dev *vc4;
int ret;

if (!hvs)
return 0;

vc4 = hvs->vc4;
if (vc4->gen >= VC4_GEN_6_C)
ret = vc6_hvs_hw_init(hvs);
else
ret = vc4_hvs_hw_init(hvs);
if (ret)
return ret;

vc4_hvs_write_linear_kernel(hvs, &hvs->mitchell_netravali_filter,
mitchell_netravali_1_3_1_3_kernel);
vc4_hvs_write_linear_kernel(hvs, &hvs->nearest_neighbour_filter,
nearest_neighbour_kernel);

return vc4_hvs_cob_init(hvs);
}

static const struct dev_pm_ops vc4_hvs_pm_ops = {
SET_LATE_SYSTEM_SLEEP_PM_OPS(NULL, vc4_hvs_resume_early)
};

static const struct of_device_id vc4_hvs_dt_match[] = {
{ .compatible = "brcm,bcm2711-hvs" },
{ .compatible = "brcm,bcm2712-hvs" },
Expand All @@ -2304,5 +2343,6 @@ struct platform_driver vc4_hvs_driver = {
.driver = {
.name = "vc4_hvs",
.of_match_table = vc4_hvs_dt_match,
.pm = pm_sleep_ptr(&vc4_hvs_pm_ops),
},
};
47 changes: 38 additions & 9 deletions drivers/iommu/bcm2712-iommu.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <linux/minmax.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/spinlock.h>

#define MMU_WR(off, val) writel(val, mmu->reg_base + (off))
Expand Down Expand Up @@ -207,18 +208,25 @@ static int bcm2712_iommu_init(struct bcm2712_iommu *mmu)
* the aperture does not start from zero), and of the default page.
* For simplicity, both these regions are whole Linux pages.
*/
u = bcm2712_iommu_get_page(mmu, &mmu->top_table);
if (!u)
return -ENOMEM;
if (mmu->top_table) {
u = (u32)(virt_to_phys(mmu->top_table) >> IOMMU_PAGE_SHIFT);
} else {
u = bcm2712_iommu_get_page(mmu, &mmu->top_table);
if (!u)
return -ENOMEM;
}
MMU_WR(MMMU_PT_PA_BASE_OFFSET,
u - ((mmu->aperture_base - mmu->dma_iova_offset) >> L1_AP_BASE_SHIFT));
u = bcm2712_iommu_get_page(mmu, &mmu->default_page);
if (!u) {
bcm2712_iommu_free_page(mmu, mmu->top_table);
return -ENOMEM;
if (mmu->default_page) {
u = (u32)(virt_to_phys(mmu->default_page) >> IOMMU_PAGE_SHIFT);
} else {
u = bcm2712_iommu_get_page(mmu, &mmu->default_page);
if (!u) {
bcm2712_iommu_free_page(mmu, mmu->top_table);
return -ENOMEM;
}
}
MMU_WR(MMMU_ILLEGAL_ADR_OFFSET, MMMU_ILLEGAL_ADR_ENABLE + u);
mmu->nmapped_pages = 0;

/* Flush (and enable) the shared TLB cache; enable this MMU. */
if (mmu->cache)
Expand Down Expand Up @@ -744,6 +752,26 @@ static void bcm2712_iommu_remove(struct platform_device *pdev)
MMU_WR(MMMU_CTRL_OFFSET, 0); /* disable the MMU */
}

static int bcm2712_iommu_suspend(struct device *dev)
{
struct bcm2712_iommu *mmu = dev_get_drvdata(dev);

if (mmu->reg_base)
MMU_WR(MMMU_CTRL_OFFSET, 0); /* disable the MMU */

return 0;
}

static int bcm2712_iommu_resume(struct device *dev)
{
struct bcm2712_iommu *mmu = dev_get_drvdata(dev);

return bcm2712_iommu_init(mmu);
}

static DEFINE_SIMPLE_DEV_PM_OPS(bcm2712_iommu_pm_ops, bcm2712_iommu_suspend,
bcm2712_iommu_resume);

static const struct of_device_id bcm2712_iommu_of_match[] = {
{
. compatible = "brcm,bcm2712-iommu"
Expand All @@ -756,7 +784,8 @@ static struct platform_driver bcm2712_iommu_driver = {
.remove = bcm2712_iommu_remove,
.driver = {
.name = "bcm2712-iommu",
.of_match_table = bcm2712_iommu_of_match
.of_match_table = bcm2712_iommu_of_match,
.pm = pm_sleep_ptr(&bcm2712_iommu_pm_ops),
},
};

Expand Down
40 changes: 30 additions & 10 deletions drivers/irqchip/irq-bcm2712-mip.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <linux/msi.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/pm.h>

#include <linux/irqchip/irq-msi-lib.h>

Expand Down Expand Up @@ -170,6 +171,19 @@ static const struct msi_parent_ops mip_msi_parent_ops = {
.init_dev_msi_info = msi_lib_init_dev_msi_info,
};

static void mip_hw_init(struct mip_priv *mip)
{
/*
* All MSI-X unmasked for the host, masked for the VPU, and edge-triggered.
*/
writel(0, mip->base + MIP_INT_MASKL_HOST);
writel(0, mip->base + MIP_INT_MASKH_HOST);
writel(~0, mip->base + MIP_INT_MASKL_VPU);
writel(~0, mip->base + MIP_INT_MASKH_VPU);
writel(~0, mip->base + MIP_INT_CFGL_HOST);
writel(~0, mip->base + MIP_INT_CFGH_HOST);
}

static int mip_init_domains(struct mip_priv *mip, struct device_node *np)
{
struct irq_domain_info info = {
Expand All @@ -184,15 +198,7 @@ static int mip_init_domains(struct mip_priv *mip, struct device_node *np)
if (!msi_create_parent_irq_domain(&info, &mip_msi_parent_ops))
return -ENOMEM;

/*
* All MSI-X unmasked for the host, masked for the VPU, and edge-triggered.
*/
writel(0, mip->base + MIP_INT_MASKL_HOST);
writel(0, mip->base + MIP_INT_MASKH_HOST);
writel(~0, mip->base + MIP_INT_MASKL_VPU);
writel(~0, mip->base + MIP_INT_MASKH_VPU);
writel(~0, mip->base + MIP_INT_CFGL_HOST);
writel(~0, mip->base + MIP_INT_CFGH_HOST);
mip_hw_init(mip);

return 0;
}
Expand Down Expand Up @@ -244,6 +250,7 @@ static int mip_msi_probe(struct platform_device *pdev, struct device_node *paren

spin_lock_init(&mip->lock);
mip->dev = &pdev->dev;
platform_set_drvdata(pdev, mip);

ret = mip_parse_dt(mip, node);
if (ret)
Expand Down Expand Up @@ -279,9 +286,22 @@ static int mip_msi_probe(struct platform_device *pdev, struct device_node *paren
return ret;
}

static int mip_resume(struct device *dev)
{
struct mip_priv *mip = dev_get_drvdata(dev);

mip_hw_init(mip);

return 0;
}

static const struct dev_pm_ops mip_pm_ops = {
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(NULL, mip_resume)
};

IRQCHIP_PLATFORM_DRIVER_BEGIN(mip_msi)
IRQCHIP_MATCH("brcm,bcm2712-mip", mip_msi_probe)
IRQCHIP_PLATFORM_DRIVER_END(mip_msi)
IRQCHIP_PLATFORM_DRIVER_END(mip_msi, .pm = &mip_pm_ops)
MODULE_DESCRIPTION("Broadcom BCM2712 MSI-X interrupt controller");
MODULE_AUTHOR("Phil Elwell <phil@raspberrypi.com>");
MODULE_AUTHOR("Stanimir Varbanov <svarbanov@suse.de>");
Expand Down
Loading