Blob Blame History Raw
From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
Date: Wed, 29 Jul 2020 17:24:33 +0200
Subject: soc: bcm: bcm2835-pm: Add support for bcm2711
Patch-mainline: Not yet, waiting on HW details confirmation
References: jsc#SLE-16259

The new ARGON ASB took over V3D, which is our only consumer of this
driver so far. The old ASB seems to still be present with ISP and
H264 bits but no V3D. This needs to be confirmed. The V3D is in the same
place in the new ASB as the old one, so just poke the new one for now.

Until we get a confirmation from the RPi engineers use the current
binding and assume only ASB register will be provided.

Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
---
 drivers/mfd/bcm2835-pm.c        | 41 +++++++++++++++++++++++++++++------------
 drivers/soc/bcm/bcm2835-power.c |  9 +++++++++
 include/linux/mfd/bcm2835-pm.h  |  1 +
 3 files changed, 39 insertions(+), 12 deletions(-)

diff --git a/drivers/mfd/bcm2835-pm.c b/drivers/mfd/bcm2835-pm.c
index 42fe67f1538e..f6e3fbe05b57 100644
--- a/drivers/mfd/bcm2835-pm.c
+++ b/drivers/mfd/bcm2835-pm.c
@@ -6,6 +6,7 @@
  * the WDT and power drivers.
  */
 
+#include <linux/bits.h>
 #include <linux/delay.h>
 #include <linux/io.h>
 #include <linux/mfd/bcm2835-pm.h>
@@ -17,6 +18,8 @@
 #include <linux/types.h>
 #include <linux/watchdog.h>
 
+#define BCM2711		BIT(1)
+
 static const struct mfd_cell bcm2835_pm_devs[] = {
 	{ .name = "bcm2835-wdt" },
 };
@@ -38,6 +41,7 @@ static int bcm2835_pm_probe(struct platform_device *pdev)
 	platform_set_drvdata(pdev, pm);
 
 	pm->dev = dev;
+	pm->is_bcm2711 = (uintptr_t)device_get_match_data(&pdev->dev) & BCM2711;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	pm->base = devm_ioremap_resource(dev, res);
@@ -50,30 +54,43 @@ static int bcm2835_pm_probe(struct platform_device *pdev)
 	if (ret)
 		return ret;
 
-	/* We'll use the presence of the AXI ASB regs in the
-	 * bcm2835-pm binding as the key for whether we can reference
-	 * the full PM register range and support power domains.
+	/* To support old firmware, check if a third resource was defined and
+	 * use that as a hint that we're on bcm2711.
 	 */
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
 	if (res) {
 		pm->asb = devm_ioremap_resource(dev, res);
-		if (IS_ERR(pm->asb))
+		if (IS_ERR(pm->asb)) {
+			dev_err(dev, "Failed to map RPiVid ASB: %ld\n",
+				PTR_ERR(pm->asb));
 			return PTR_ERR(pm->asb);
+		}
+		pm->is_bcm2711 = true;
+	}
 
-		ret = devm_mfd_add_devices(dev, -1,
-					   bcm2835_power_devs,
-					   ARRAY_SIZE(bcm2835_power_devs),
-					   NULL, 0, NULL);
-		if (ret)
-			return ret;
+	/* We'll use the presence of the AXI ASB regs in the
+	 * bcm2835-pm binding as the key for whether we can reference
+	 * the full PM register range and support power domains. Bypass this if
+	 * a resource was found at index 2.
+	 */
+	if (!pm->asb) {
+		res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+		if (res) {
+			pm->asb = devm_ioremap_resource(dev, res);
+			if (IS_ERR(pm->asb))
+				return PTR_ERR(pm->asb);
+		}
 	}
 
-	return 0;
+	return devm_mfd_add_devices(dev, -1, bcm2835_power_devs,
+				    ARRAY_SIZE(bcm2835_power_devs),
+				    NULL, 0, NULL);
 }
 
 static const struct of_device_id bcm2835_pm_of_match[] = {
 	{ .compatible = "brcm,bcm2835-pm-wdt", },
 	{ .compatible = "brcm,bcm2835-pm", },
+	{ .compatible = "brcm,bcm2711-pm", .data = (void *)BCM2711},
 	{},
 };
 MODULE_DEVICE_TABLE(of, bcm2835_pm_of_match);
diff --git a/drivers/soc/bcm/bcm2835-power.c b/drivers/soc/bcm/bcm2835-power.c
index 1e0041ec8132..59cfaf6b5c53 100644
--- a/drivers/soc/bcm/bcm2835-power.c
+++ b/drivers/soc/bcm/bcm2835-power.c
@@ -143,6 +143,8 @@ struct bcm2835_power {
 	/* AXI Async bridge registers. */
 	void __iomem		*asb;
 
+	bool is_bcm2711;
+
 	struct genpd_onecell_data pd_xlate;
 	struct bcm2835_power_domain domains[BCM2835_POWER_DOMAIN_COUNT];
 	struct reset_controller_dev reset;
@@ -192,6 +194,9 @@ static int bcm2835_power_power_off(struct bcm2835_power_domain *pd, u32 pm_reg)
 {
 	struct bcm2835_power *power = pd->power;
 
+	if (power->is_bcm2711)
+		return 0;
+
 	/* Enable functional isolation */
 	PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_ISFUNC);
 
@@ -213,6 +218,9 @@ static int bcm2835_power_power_on(struct bcm2835_power_domain *pd, u32 pm_reg)
 	int inrush;
 	bool powok;
 
+	if (power->is_bcm2711)
+		return 0;
+
 	/* If it was already powered on by the fw, leave it that way. */
 	if (PM_READ(pm_reg) & PM_POWUP)
 		return 0;
@@ -626,6 +634,7 @@ static int bcm2835_power_probe(struct platform_device *pdev)
 	power->dev = dev;
 	power->base = pm->base;
 	power->asb = pm->asb;
+	power->is_bcm2711 = pm->is_bcm2711;
 
 	id = ASB_READ(ASB_AXI_BRDG_ID);
 	if (id != 0x62726467 /* "BRDG" */) {
diff --git a/include/linux/mfd/bcm2835-pm.h b/include/linux/mfd/bcm2835-pm.h
index ed37dc40e82a..498ea276d5d3 100644
--- a/include/linux/mfd/bcm2835-pm.h
+++ b/include/linux/mfd/bcm2835-pm.h
@@ -9,6 +9,7 @@ struct bcm2835_pm {
 	struct device *dev;
 	void __iomem *base;
 	void __iomem *asb;
+	bool is_bcm2711;
 };
 
 #endif /* BCM2835_MFD_PM_H */