The RK3582 SoC is a variant of the RK3588S with some IP blocks disabled.
What blocks are disabled/non-working is indicated by ip-state in OTP.

This add initial support for RK3582 by using ft_system_setup() to mark
any cpu and/or gpu node with status=fail as indicated by ip-state.

This apply same policy as vendor U-Boot for RK3582, i.e. two big cpu
cores and the gpu is always failed/disabled.

Enable Kconfig option OF_SYSTEM_SETUP in board defconfig to make use of
the required DT fixups for RK3582 board variants.

Signed-off-by: Jonas Karlman <jo...@kwiboo.se>
---
Changes in v2:
- Refactor code to first apply policy to ip-state and then fail cores
  based on the updated ip-state
- Add support for failing rkvdec and rkvenc cores
- Append soc compatible to board model
- Not picking up t-b tag due to significant changes to patch
---
 arch/arm/mach-rockchip/rk3588/rk3588.c | 221 +++++++++++++++++++++++++
 1 file changed, 221 insertions(+)

diff --git a/arch/arm/mach-rockchip/rk3588/rk3588.c 
b/arch/arm/mach-rockchip/rk3588/rk3588.c
index c01a40020896..3ab0afe0979e 100644
--- a/arch/arm/mach-rockchip/rk3588/rk3588.c
+++ b/arch/arm/mach-rockchip/rk3588/rk3588.c
@@ -7,6 +7,7 @@
 #define LOG_CATEGORY LOGC_ARCH
 
 #include <dm.h>
+#include <fdt_support.h>
 #include <misc.h>
 #include <spl.h>
 #include <asm/armv8/mmu.h>
@@ -200,6 +201,16 @@ int arch_cpu_init(void)
 
 #define RK3588_OTP_CPU_CODE_OFFSET             0x02
 #define RK3588_OTP_SPECIFICATION_OFFSET                0x06
+#define RK3588_OTP_IP_STATE_OFFSET             0x1d
+
+#define FAIL_CPU_CLUSTER0              GENMASK(3, 0)
+#define FAIL_CPU_CLUSTER1              GENMASK(5, 4)
+#define FAIL_CPU_CLUSTER2              GENMASK(7, 6)
+#define FAIL_GPU                               GENMASK(4, 1)
+#define FAIL_RKVDEC0                   BIT(6)
+#define FAIL_RKVDEC1                   BIT(7)
+#define FAIL_RKVENC0                   BIT(0)
+#define FAIL_RKVENC1                   BIT(2)
 
 int checkboard(void)
 {
@@ -245,3 +256,213 @@ int checkboard(void)
 
        return 0;
 }
+
+static int fdt_path_del_node(void *fdt, const char *path)
+{
+       int nodeoffset;
+
+       nodeoffset = fdt_path_offset(fdt, path);
+       if (nodeoffset < 0)
+               return nodeoffset;
+
+       return fdt_del_node(fdt, nodeoffset);
+}
+
+static int fdt_path_set_name(void *fdt, const char *path, const char *name)
+{
+       int nodeoffset;
+
+       nodeoffset = fdt_path_offset(fdt, path);
+       if (nodeoffset < 0)
+               return nodeoffset;
+
+       return fdt_set_name(fdt, nodeoffset, name);
+}
+
+/*
+ * RK3582 is a variant of the RK3588S with some IP blocks disabled. What blocks
+ * are disabled/non-working is indicated by ip-state in OTP. ft_system_setup()
+ * is used to mark any cpu and/or gpu node with status=fail as indicated by
+ * ip-state. Apply same policy as vendor U-Boot for RK3582, i.e. two big cpu
+ * cores and the gpu is always failed/disabled. Enable OF_SYSTEM_SETUP to make
+ * use of the required DT fixups for RK3582 board variants.
+ */
+int ft_system_setup(void *blob, struct bd_info *bd)
+{
+       static const char * const cpu_node_names[] = {
+               "cpu@0", "cpu@100", "cpu@200", "cpu@300",
+               "cpu@400", "cpu@500", "cpu@600", "cpu@700",
+       };
+       int parent, node, i, comp_len, len, ret;
+       bool cluster1_removed = false;
+       u8 cpu_code[2], ip_state[3];
+       struct udevice *dev;
+       char soc_comp[16];
+       const char *comp;
+       void *data;
+
+       if (!IS_ENABLED(CONFIG_OF_SYSTEM_SETUP))
+               return 0;
+
+       if (!IS_ENABLED(CONFIG_ROCKCHIP_OTP) || !CONFIG_IS_ENABLED(MISC))
+               return -ENOSYS;
+
+       ret = uclass_get_device_by_driver(UCLASS_MISC,
+                                         DM_DRIVER_GET(rockchip_otp), &dev);
+       if (ret) {
+               log_debug("Could not find otp device, ret=%d\n", ret);
+               return ret;
+       }
+
+       /* cpu-code: SoC model, e.g. 0x35 0x82 or 0x35 0x88 */
+       ret = misc_read(dev, RK3588_OTP_CPU_CODE_OFFSET, cpu_code, 2);
+       if (ret < 0) {
+               log_debug("Could not read cpu-code, ret=%d\n", ret);
+               return ret;
+       }
+
+       log_debug("cpu-code: %02x %02x\n", cpu_code[0], cpu_code[1]);
+
+       /* only fail devices on rk3582/rk3583 */
+       if (!(cpu_code[0] == 0x35 && cpu_code[1] == 0x82) &&
+           !(cpu_code[0] == 0x35 && cpu_code[1] == 0x83))
+               return 0;
+
+       ret = misc_read(dev, RK3588_OTP_IP_STATE_OFFSET, &ip_state, 3);
+       if (ret < 0) {
+               log_err("Could not read ip-state, ret=%d\n", ret);
+               return ret;
+       }
+
+       log_debug("ip-state: %02x %02x %02x (otp)\n",
+                 ip_state[0], ip_state[1], ip_state[2]);
+
+       /* policy: fail entire big core cluster when one or more core is bad */
+       if (ip_state[0] & FAIL_CPU_CLUSTER1)
+               ip_state[0] |= FAIL_CPU_CLUSTER1;
+       if (ip_state[0] & FAIL_CPU_CLUSTER2)
+               ip_state[0] |= FAIL_CPU_CLUSTER2;
+
+       /* policy: always fail one big core cluster on rk3582/rk3583 */
+       if (!(ip_state[0] & (FAIL_CPU_CLUSTER1 | FAIL_CPU_CLUSTER2)))
+               ip_state[0] |= FAIL_CPU_CLUSTER2;
+
+       if (cpu_code[0] == 0x35 && cpu_code[1] == 0x82) {
+               /* policy: always fail gpu on rk3582 */
+               ip_state[1] |= FAIL_GPU;
+
+               /* policy: always fail rkvdec on rk3582 */
+               ip_state[1] |= FAIL_RKVDEC0 | FAIL_RKVDEC1;
+       } else if (cpu_code[0] == 0x35 && cpu_code[1] == 0x83) {
+               /* policy: always fail one rkvdec core on rk3583 */
+               if (!(ip_state[1] & (FAIL_RKVDEC0 | FAIL_RKVDEC1)))
+                       ip_state[1] |= FAIL_RKVDEC1;
+       }
+
+       /* policy: always fail one rkvenc core on rk3582/rk3583 */
+       if (!(ip_state[2] & (FAIL_RKVENC0 | FAIL_RKVENC1)))
+               ip_state[2] |= FAIL_RKVENC1;
+
+       log_debug("ip-state: %02x %02x %02x (policy)\n",
+                 ip_state[0], ip_state[1], ip_state[2]);
+
+       /* cpu cluster1: ip_state[0]: bit4~5 */
+       if ((ip_state[0] & FAIL_CPU_CLUSTER1) == FAIL_CPU_CLUSTER1) {
+               log_debug("remove cpu-map cluster1\n");
+               fdt_path_del_node(blob, "/cpus/cpu-map/cluster1");
+               cluster1_removed = true;
+       }
+
+       /* cpu cluster2: ip_state[0]: bit6~7 */
+       if ((ip_state[0] & FAIL_CPU_CLUSTER2) == FAIL_CPU_CLUSTER2) {
+               log_debug("remove cpu-map cluster2\n");
+               fdt_path_del_node(blob, "/cpus/cpu-map/cluster2");
+       } else if (cluster1_removed) {
+               /* cluster nodes must be named in a continuous series */
+               log_debug("rename cpu-map cluster2\n");
+               fdt_path_set_name(blob, "/cpus/cpu-map/cluster2", "cluster1");
+       }
+
+       /* gpu: ip_state[1]: bit1~4 */
+       if (ip_state[1] & FAIL_GPU) {
+               log_debug("fail gpu\n");
+               fdt_status_fail_by_pathf(blob, "/gpu@fb000000");
+       }
+
+       /* rkvdec: ip_state[1]: bit6,7 */
+       if (ip_state[1] & FAIL_RKVDEC0) {
+               log_debug("fail rkvdec0\n");
+               fdt_status_fail_by_pathf(blob, "/video-codec@fdc38000");
+               fdt_status_fail_by_pathf(blob, "/iommu@fdc38700");
+       }
+       if (ip_state[1] & FAIL_RKVDEC1) {
+               log_debug("fail rkvdec1\n");
+               fdt_status_fail_by_pathf(blob, "/video-codec@fdc40000");
+               fdt_status_fail_by_pathf(blob, "/iommu@fdc40700");
+       }
+
+       /* rkvenc: ip_state[2]: bit0,2 */
+       if (ip_state[2] & FAIL_RKVENC0) {
+               log_debug("fail rkvenc0\n");
+               fdt_status_fail_by_pathf(blob, "/video-codec@fdbd0000");
+               fdt_status_fail_by_pathf(blob, "/iommu@fdbdf000");
+       }
+       if (ip_state[2] & FAIL_RKVENC1) {
+               log_debug("fail rkvenc1\n");
+               fdt_status_fail_by_pathf(blob, "/video-codec@fdbe0000");
+               fdt_status_fail_by_pathf(blob, "/iommu@fdbef000");
+       }
+
+       parent = fdt_path_offset(blob, "/cpus");
+       if (parent < 0) {
+               log_err("Could not find /cpus, parent=%d\n", parent);
+               return parent;
+       }
+
+       /* cpu: ip_state[0]: bit0~7 */
+       for (i = 0; i < 8; i++) {
+               /* fail any bad cpu core */
+               if (!(ip_state[0] & BIT(i)))
+                       continue;
+
+               node = fdt_subnode_offset(blob, parent, cpu_node_names[i]);
+               if (node >= 0) {
+                       log_debug("fail cpu %s\n", cpu_node_names[i]);
+                       fdt_status_fail(blob, node);
+               } else {
+                       log_err("Could not find %s, node=%d\n",
+                               cpu_node_names[i], node);
+                       return node;
+               }
+       }
+
+       node = fdt_path_offset(blob, "/");
+       if (node < 0) {
+               log_err("Could not find /, node=%d\n", node);
+               return node;
+       }
+
+       snprintf(soc_comp, sizeof(soc_comp), "rockchip,rk35%x", cpu_code[1]);
+
+       for (i = 0, comp_len = 0;
+            (comp = fdt_stringlist_get(blob, node, "compatible", i, &len));
+            i++) {
+               /* stop at soc compatible */
+               if (!strcmp(comp, soc_comp) ||
+                   !strcmp(comp, "rockchip,rk3588s") ||
+                   !strcmp(comp, "rockchip,rk3588"))
+                       break;
+
+               log_debug("compatible[%d]: %s\n", i, comp);
+               comp_len += len + 1;
+       }
+
+       /* truncate to only include board compatible */
+       fdt_setprop_placeholder(blob, node, "compatible", comp_len, &data);
+
+       /* append soc compatible */
+       fdt_appendprop_string(blob, node, "compatible", soc_comp);
+       fdt_appendprop_string(blob, node, "compatible", "rockchip,rk3588s");
+
+       return 0;
+}
-- 
2.50.1

Reply via email to