Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion arch/arm64/configs/bcm2712_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ 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_SUSPEND=y
CONFIG_PM=y
CONFIG_PM_DEBUG=y
CONFIG_CPU_IDLE=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
2 changes: 2 additions & 0 deletions drivers/gpu/drm/vc4/vc4_hdmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -3441,6 +3441,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
20 changes: 20 additions & 0 deletions drivers/mailbox/bcm2835-mailbox.c
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,25 @@ static int bcm2835_mbox_probe(struct platform_device *pdev)
return ret;
}

static int bcm2835_mbox_resume_noirq(struct device *dev)
{
struct bcm2835_mbox *mbox = dev_get_drvdata(dev);

/*
* MAIL0_CNF is reset while the SoC is powered down in suspend-to-RAM.
* Re-enable the receive interrupt before any driver resumes, otherwise
* the replies are never signalled and every firmware transaction times
* out.
*/
writel(ARM_MC_IHAVEDATAIRQEN, mbox->regs + MAIL0_CNF);

return 0;
}

static const struct dev_pm_ops bcm2835_mbox_pm_ops = {
NOIRQ_SYSTEM_SLEEP_PM_OPS(NULL, bcm2835_mbox_resume_noirq)
};

static const struct of_device_id bcm2835_mbox_of_match[] = {
{ .compatible = "brcm,bcm2835-mbox", },
{},
Expand All @@ -193,6 +212,7 @@ static struct platform_driver bcm2835_mbox_driver = {
.driver = {
.name = "bcm2835-mbox",
.of_match_table = bcm2835_mbox_of_match,
.pm = pm_sleep_ptr(&bcm2835_mbox_pm_ops),
},
.probe = bcm2835_mbox_probe,
};
Expand Down
20 changes: 20 additions & 0 deletions drivers/watchdog/bcm2835_wdt.c
Original file line number Diff line number Diff line change
Expand Up @@ -232,11 +232,31 @@ static void bcm2835_wdt_remove(struct platform_device *pdev)
pm_power_off = NULL;
}

static int bcm2835_wdt_suspend(struct device *dev)
{
if (watchdog_active(&bcm2835_wdt_wdd) || watchdog_hw_running(&bcm2835_wdt_wdd))
bcm2835_wdt_stop(&bcm2835_wdt_wdd);

return 0;
}

static int bcm2835_wdt_resume(struct device *dev)
{
if (watchdog_active(&bcm2835_wdt_wdd) || watchdog_hw_running(&bcm2835_wdt_wdd))
bcm2835_wdt_start(&bcm2835_wdt_wdd);

return 0;
}

static DEFINE_SIMPLE_DEV_PM_OPS(bcm2835_wdt_pm_ops,
bcm2835_wdt_suspend, bcm2835_wdt_resume);

static struct platform_driver bcm2835_wdt_driver = {
.probe = bcm2835_wdt_probe,
.remove = bcm2835_wdt_remove,
.driver = {
.name = "bcm2835-wdt",
.pm = pm_sleep_ptr(&bcm2835_wdt_pm_ops),
},
};
module_platform_driver(bcm2835_wdt_driver);
Expand Down
3 changes: 2 additions & 1 deletion include/linux/acpi.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <linux/resource_ext.h>
#include <linux/device.h>
#include <linux/mod_devicetable.h>
#include <linux/of.h>
#include <linux/property.h>
#include <linux/uuid.h>
#include <linux/node.h>
Expand Down Expand Up @@ -1182,7 +1183,7 @@ static inline int acpi_dev_pm_attach(struct device *dev, bool power_on)
}
static inline bool acpi_storage_d3(struct device *dev)
{
return false;
return of_machine_is_compatible("brcm,bcm2712");
}
static inline bool acpi_dev_state_d0(struct device *dev)
{
Expand Down