drivers: bootcount: Add ZynqMP specific bootcount support

Add native support of the bootcount mechanism in the ZynqMP by
utilising internal PMU registers. The Persistent Global Storage
Registers of the Platform Management Unit can keep their value
during reboot cycles unless there is a POR reset, making them
appropriate for the bootcount mechanism.

Signed-off-by: Vasileios Amoiridis <vasileios.amoiridis@cern.ch>
Reviewed-by: Heiko Schocher <hs@denx.de>
Link: https://lore.kernel.org/r/20241105132744.1572759-2-vassilisamir@gmail.com
Signed-off-by: Michal Simek <michal.simek@amd.com>
diff --git a/MAINTAINERS b/MAINTAINERS
index 0399ed1..bc27a51 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -860,6 +860,7 @@
 S:	Maintained
 T:	git https://source.denx.de/u-boot/custodians/u-boot-microblaze.git
 F:	arch/arm/mach-zynqmp/
+F:	drivers/bootcount/bootcount_zynqmp.c
 F:	drivers/clk/clk_zynqmp.c
 F:	driver/firmware/firmware-zynqmp.c
 F:	drivers/fpga/zynqpl.c
diff --git a/arch/arm/mach-zynqmp/include/mach/hardware.h b/arch/arm/mach-zynqmp/include/mach/hardware.h
index 49e449e..3c372bd 100644
--- a/arch/arm/mach-zynqmp/include/mach/hardware.h
+++ b/arch/arm/mach-zynqmp/include/mach/hardware.h
@@ -188,6 +188,8 @@
 	u32 gen_storage4; /* 0x40 */
 	u32 reserved1[1];
 	u32 gen_storage6; /* 0x48 */
+	u32 reserved2[3];
+	u32 pers_gen_storage2; /* 0x58 */
 };
 
 #define pmu_base ((struct pmu_regs *)ZYNQMP_PMU_BASEADDR)
diff --git a/drivers/bootcount/Kconfig b/drivers/bootcount/Kconfig
index fa6d8e7..0080d2a 100644
--- a/drivers/bootcount/Kconfig
+++ b/drivers/bootcount/Kconfig
@@ -164,6 +164,13 @@
 
 	  Accessing the backend is done using the regmap interface.
 
+config DM_BOOTCOUNT_ZYNQMP
+	bool "Support ZynqMP PMUFW as a backing store for bootcount"
+	depends on ARCH_ZYNQMP
+	help
+	  Enable support for the bootcount API by utilising the Persistent
+	  Global General Storage Register 2 of the PMU.
+
 endmenu
 
 endif
diff --git a/drivers/bootcount/Makefile b/drivers/bootcount/Makefile
index 245f879..0cf79e4 100644
--- a/drivers/bootcount/Makefile
+++ b/drivers/bootcount/Makefile
@@ -16,3 +16,4 @@
 obj-$(CONFIG_DM_BOOTCOUNT_I2C)	+= bootcount_dm_i2c.o
 obj-$(CONFIG_DM_BOOTCOUNT_SPI_FLASH)	+= spi-flash.o
 obj-$(CONFIG_DM_BOOTCOUNT_SYSCON) += bootcount_syscon.o
+obj-$(CONFIG_DM_BOOTCOUNT_ZYNQMP)	+= bootcount_zynqmp.o
diff --git a/drivers/bootcount/bootcount_zynqmp.c b/drivers/bootcount/bootcount_zynqmp.c
new file mode 100644
index 0000000..bc0984e
--- /dev/null
+++ b/drivers/bootcount/bootcount_zynqmp.c
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0+
+// SPDX-FileCopyrightText: 2024 CERN (home.cern)
+
+#include <bootcount.h>
+#include <dm.h>
+#include <stdio.h>
+#include <zynqmp_firmware.h>
+#include <asm/arch/hardware.h>
+#include <dm/platdata.h>
+
+static int bootcount_zynqmp_set(struct udevice *dev, const u32 val)
+{
+	int ret;
+
+	ret = zynqmp_mmio_write((ulong)&pmu_base->pers_gen_storage2, 0xFF, val);
+	if (ret)
+		pr_info("%s write fail\n", __func__);
+
+	return ret;
+}
+
+static int bootcount_zynqmp_get(struct udevice *dev, u32 *val)
+{
+	int ret;
+
+	*val = 0;
+	ret = zynqmp_mmio_read((ulong)&pmu_base->pers_gen_storage2, val);
+	if (ret)
+		pr_info("%s read fail\n", __func__);
+
+	return ret;
+}
+
+U_BOOT_DRVINFO(bootcount_zynqmp) = {
+	.name = "bootcount_zynqmp",
+};
+
+static const struct bootcount_ops bootcount_zynqmp_ops = {
+	.get = bootcount_zynqmp_get,
+	.set = bootcount_zynqmp_set,
+};
+
+U_BOOT_DRIVER(bootcount_zynqmp) = {
+	.name = "bootcount_zynqmp",
+	.id = UCLASS_BOOTCOUNT,
+	.ops = &bootcount_zynqmp_ops,
+};