feat(rk3588): support rk3588

rk3588 is an Octa-core soc with Cortex-a55/a76 inside.
This patch supports the following functions:
1. basic platform setup
2. power up/off cpus
3. suspend/resume cpus
4. suspend/resume system
5. reset system
6. power off system

Signed-off-by: XiaoDong Huang <derrick.huang@rock-chips.com>
Change-Id: I598109f15a2efd5b33aedd176cf708c08cb1dcf4
diff --git a/plat/rockchip/common/include/plat_pm_helpers.h b/plat/rockchip/common/include/plat_pm_helpers.h
new file mode 100644
index 0000000..2204a65
--- /dev/null
+++ b/plat/rockchip/common/include/plat_pm_helpers.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2024, Rockchip, Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef PLAT_PM_HELPERS_H
+#define PLAT_PM_HELPERS_H
+
+#include <stdint.h>
+
+/**
+ * Use this macro to define a register region.
+ * start: start offset from the base address.
+ * end: end offset from the base address.
+ * stride: stride of registers in region.
+ * base: base address of registers in region.
+ * wmsk: write mask of registers in region.
+ */
+#define REG_REGION(_start, _end, _stride, _base, _wmsk)	\
+{							\
+	.start = (_base) + (_start),			\
+	.end   = (_base) + (_end),			\
+	.stride   = _stride,				\
+	.wmsk  = _wmsk					\
+}
+
+struct reg_region {
+	/* Start address of region */
+	uint32_t start;
+	/* End address of region */
+	uint32_t end;
+	/* Stride of registers in region */
+	uint32_t stride;
+	/* Write mask of registers in region */
+	uint32_t wmsk;
+	/* Buffer to save/restore registers in region */
+	uint32_t *buf;
+};
+
+void rockchip_alloc_region_mem(struct reg_region *rgns, uint32_t rgn_num);
+void rockchip_reg_rgn_save(struct reg_region *rgns, uint32_t rgn_num);
+void rockchip_reg_rgn_restore(struct reg_region *rgns, uint32_t rgn_num);
+void rockchip_reg_rgn_restore_reverse(struct reg_region *rgns, uint32_t rgn_num);
+void rockchip_regs_dump(uint32_t base,
+			uint32_t start_offset,
+			uint32_t end_offset,
+			uint32_t stride);
+void rockchip_dump_reg_rgns(struct reg_region *rgns, uint32_t rgn_num);
+
+#endif /* PLAT_PM_HELPERS_H */
diff --git a/plat/rockchip/common/plat_pm_helpers.c b/plat/rockchip/common/plat_pm_helpers.c
new file mode 100644
index 0000000..191b0ca
--- /dev/null
+++ b/plat/rockchip/common/plat_pm_helpers.c
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2024, Rockchip, Inc. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <errno.h>
+
+#include <arch_helpers.h>
+#include <bl31/bl31.h>
+#include <common/debug.h>
+#include <drivers/console.h>
+#include <drivers/delay_timer.h>
+#include <lib/mmio.h>
+#include <plat/common/platform.h>
+#include <platform_def.h>
+
+#include <plat_pm_helpers.h>
+
+#define ROCKCHIP_PM_REG_REGION_MEM_LEN	(ROCKCHIP_PM_REG_REGION_MEM_SIZE / sizeof(uint32_t))
+
+/* REG region */
+#define RGN_LEN(_rgn)		(((_rgn)->end - (_rgn)->start) / (_rgn)->stride + 1)
+
+#ifndef ROCKCHIP_PM_REG_REGION_MEM_SIZE
+#define ROCKCHIP_PM_REG_REGION_MEM_SIZE		0
+#endif
+
+#ifdef ROCKCHIP_REG_RGN_MEM_BASE
+static uint32_t *region_mem = (uint32_t *)ROCKCHIP_REG_RGN_MEM_BASE;
+#else
+static uint32_t region_mem[ROCKCHIP_PM_REG_REGION_MEM_LEN];
+#endif
+
+static int region_mem_idx;
+
+static int alloc_region_mem(uint32_t *buf, int max_len,
+			    struct reg_region *rgns, uint32_t rgn_num)
+{
+	int i;
+	int total_len = 0, len = 0;
+	struct reg_region *r = rgns;
+
+	assert(buf && rgns && rgn_num);
+
+	for (i = 0; i < rgn_num; i++, r++) {
+		if (total_len < max_len)
+			r->buf = &buf[total_len];
+
+		len = RGN_LEN(r);
+		total_len += len;
+	}
+
+	if (total_len > max_len) {
+		ERROR("%s The buffer remain length:%d is too small for region:0x%x, at least %d\n",
+		      __func__, max_len, rgns[0].start, total_len);
+		panic();
+	}
+
+	return total_len;
+}
+
+/**
+ * Alloc memory to reg_region->buf from region_mem.
+ * @rgns - struct reg_region array.
+ * @rgn_num - struct reg_region array length.
+ */
+void rockchip_alloc_region_mem(struct reg_region *rgns, uint32_t rgn_num)
+{
+	int max_len = 0, len;
+
+	assert(rgns && rgn_num);
+
+	max_len = ROCKCHIP_PM_REG_REGION_MEM_LEN - region_mem_idx;
+
+	len = alloc_region_mem(region_mem + region_mem_idx, max_len,
+			       rgns, rgn_num);
+
+	region_mem_idx += len;
+}
+
+/**
+ * Save (reg_region->start ~ reg_region->end) to reg_region->buf.
+ * @rgns - struct reg_region array.
+ * @rgn_num - struct reg_region array length.
+ */
+void rockchip_reg_rgn_save(struct reg_region *rgns, uint32_t rgn_num)
+{
+	struct reg_region *r;
+	uint32_t addr;
+	int i, j;
+
+	assert(rgns && rgn_num);
+
+	for (i = 0; i < rgn_num; i++) {
+		r = &rgns[i];
+		for (j = 0, addr = r->start; addr <= r->end; addr += r->stride, j++)
+			r->buf[j] = mmio_read_32(addr);
+	}
+}
+
+/**
+ * Restore reg_region->buf to (reg_region->start ~ reg_region->end).
+ * @rgns - struct reg_region array.
+ * @rgn_num - struct reg_region array length.
+ */
+void rockchip_reg_rgn_restore(struct reg_region *rgns, uint32_t rgn_num)
+{
+	struct reg_region *r;
+	uint32_t addr;
+	int i, j;
+
+	assert(rgns && rgn_num);
+
+	for (i = 0; i < rgn_num; i++) {
+		r = &rgns[i];
+		for (j = 0, addr = r->start; addr <= r->end; addr += r->stride, j++)
+			mmio_write_32(addr, r->buf[j] | r->wmsk);
+
+		dsb();
+	}
+}
+
+/**
+ * Restore reg_region->buf to (reg_region->start ~ reg_region->end) reversely.
+ * @rgns - struct reg_region array.
+ * @rgn_num - struct reg_region array length.
+ */
+void rockchip_reg_rgn_restore_reverse(struct reg_region *rgns, uint32_t rgn_num)
+{
+	struct reg_region *r;
+	uint32_t addr;
+	int i, j;
+
+	assert(rgns && rgn_num);
+
+	for (i = rgn_num - 1; i >= 0; i--) {
+		r = &rgns[i];
+		j = RGN_LEN(r) - 1;
+		for (addr = r->end; addr >= r->start; addr -= r->stride, j--)
+			mmio_write_32(addr, r->buf[j] | r->wmsk);
+
+		dsb();
+	}
+}
+
+static void rockchip_print_hex(uint32_t val)
+{
+	int i;
+	unsigned char tmp;
+
+	putchar('0');
+	putchar('x');
+	for (i = 0; i < 8; val <<= 4, ++i) {
+		tmp = (val & 0xf0000000) >> 28;
+		if (tmp < 10)
+			putchar('0' + tmp);
+		else
+			putchar('a' + tmp - 10);
+	}
+}
+
+/**
+ * Dump registers (base + start_offset ~ base + end_offset)
+ * @base - the base addr of the register.
+ * @start_offset - the start offset to dump.
+ * @end_offset - the end offset to dump.
+ * @stride - the stride of the registers.
+ */
+void rockchip_regs_dump(uint32_t base,
+			uint32_t start_offset,
+			uint32_t end_offset,
+			uint32_t stride)
+{
+	uint32_t i;
+
+	for (i = start_offset; i <= end_offset; i += stride) {
+		if ((i - start_offset) % 16 == 0) {
+			putchar('\n');
+			rockchip_print_hex(base + i);
+			putchar(':');
+			putchar(' ');
+			putchar(' ');
+			putchar(' ');
+			putchar(' ');
+		}
+		rockchip_print_hex(mmio_read_32(base + i));
+		putchar(' ');
+		putchar(' ');
+		putchar(' ');
+		putchar(' ');
+	}
+	putchar('\n');
+}
+
+/**
+ * Dump reg regions
+ * @rgns - struct reg_region array.
+ * @rgn_num - struct reg_region array length.
+ */
+void rockchip_dump_reg_rgns(struct reg_region *rgns, uint32_t rgn_num)
+{
+	struct reg_region *r;
+	int i;
+
+	assert(rgns && rgn_num);
+
+	for (i = 0; i < rgn_num; i++) {
+		r = &rgns[i];
+		rockchip_regs_dump(0x0, r->start, r->end, r->stride);
+	}
+}