Skip to content

arm64: cix: fix Orange Pi 6 Plus ACPI CPU topology#44

Open
coffnix wants to merge 5 commits into
cixtech:mainfrom
coffnix:fix-orange-pi-6-plus-acpi
Open

arm64: cix: fix Orange Pi 6 Plus ACPI CPU topology#44
coffnix wants to merge 5 commits into
cixtech:mainfrom
coffnix:fix-orange-pi-6-plus-acpi

Conversation

@coffnix

@coffnix coffnix commented Jul 10, 2026

Copy link
Copy Markdown

This pull request is related to issue #43:

#43

I attached a ready-to-use acpi_override.cpio to make validation easier.

It contains a corrected PPTT describing the proper cache hierarchy:

  • One shared 12 MiB L3 cache for all CPUs.
  • One shared 2 MiB L2 cache for the four Cortex-A520 cores.
  • One private 512 KiB L2 cache for each Cortex-A720 core.

After applying the override, Linux, hwloc and XMRig all report the correct cache topology on the Orange Pi 6 Plus.

The acpi_override.cpio is provided only to simplify testing and validation of the fix. The actual firmware or ACPI source should be updated accordingly.

Signed-off-by: coffnix <raphael@bsd.com.br>
@coffnix

coffnix commented Jul 10, 2026

Copy link
Copy Markdown
Author

The complete instructions for reproducing this acpi_override.cpio, including all commands, the Python script used to generate the PPTT, and the GRUB configuration, are available in Issue #43:

#43 (comment)

@srcshelton

Copy link
Copy Markdown

Unless the SoC on the Orange Pi 6 Plus has advanced from the CD8180 of the Orion O6, I don't think this is right: the A520 cores have no direct L2, and use the shared 12MB (A720) L3 as their L2.

The vendor block-diagram at #43 seems to support this conclusion, as does direct inspection of the per-core CLIDR_EL1/CCSIDR_EL1 cache registers.

@coffnix

coffnix commented Jul 12, 2026

Copy link
Copy Markdown
Author

Unless the SoC on the Orange Pi 6 Plus has advanced from the CD8180 of the Orion O6, I don't think this is right: the A520 cores have no direct L2, and use the shared 12MB (A720) L3 as their L2.

The vendor block-diagram at #43 seems to support this conclusion, as does direct inspection of the per-core CLIDR_EL1/CCSIDR_EL1 cache registers.

I grabbed the settings from the Orange Pi 6 Plus DTB, and I'll review them. Thanks for pointing out that difference. While we're at it, could you also send me the cache topology from your board using the commands below, if they show the information?

for c in /sys/devices/system/cpu/cpu0/cache/index*; do echo "===== $c ====="
    cat "$c/level" "$c/type" "$c/size" "$c/shared_cpu_list" 2>/dev/null
    
    for cpu in /sys/devices/system/cpu/cpu*/cache/index*; do
    printf '%s: ' "$cpu"
    cat "$cpu/level" "$cpu/type" "$cpu/size" "$cpu/shared_cpu_list" 2>/dev/null | paste -sd ' '
done

grep . /sys/devices/system/cpu/cpu0/cache/index*/id 2>/dev/null

for c in /sys/devices/system/cpu/cpu[0-9]*; do
    echo "===== $(basename "$c") ====="
    for i in "$c"/cache/index*; do
        [ -d "$i" ] || continue
        printf '%s level=%s type=%s size=%s id=%s shared=%s\n' \
            "$(basename "$i")" \
            "$(cat "$i/level" 2>/dev/null)" \
            "$(cat "$i/type" 2>/dev/null)" \
            "$(cat "$i/size" 2>/dev/null)" \
            "$(cat "$i/id" 2>/dev/null)" \
            "$(cat "$i/shared_cpu_list" 2>/dev/null)"
    done
done

hwloc-ls
lscpu

thanks!

@coffnix

coffnix commented Jul 13, 2026

Copy link
Copy Markdown
Author

Unless the SoC on the Orange Pi 6 Plus has advanced from the CD8180 of the Orion O6, I don't think this is right: the A520 cores have no direct L2, and use the shared 12MB (A720) L3 as their L2.

The vendor block-diagram at #43 seems to support this conclusion, as does direct inspection of the per-core CLIDR_EL1/CCSIDR_EL1 cache registers.

After reviewing the comments, especially the information provided by @srcshelton, I realized that my previous PPTT implementation was still making an incorrect architectural assumption about the Cortex-A520 cluster.

The original implementation modeled the efficiency cluster as having a shared 2 MiB L2 cache, resulting in the following hierarchy:

Cortex-A520
  L1
   ↓
Shared L2 (2 MiB)
   ↓
Shared L3 (12 MiB)

However, this does not match what is described by the CIX documentation, the Linux Device Tree, nor the cache hierarchy exposed by the processor itself.

According to the available documentation for the CD8160/CD8180 SoC, all twelve CPUs share a single 12 MiB DSU cache. The Device Tree also points every CPU toward the same top-level cache node. In addition, @srcshelton mentioned that reading the CLIDR_EL1/CCSIDR_EL1 registers directly indicates that the Cortex-A520 cores do not expose a private or shared L2 cache in the same way as the Cortex-A720 cores.

Based on this information, I rewrote the PPTT generator.

The main changes are:

  • Removed the artificial shared 2 MiB L2 cache for the Cortex-A520 cluster.
  • Every CPU now receives its own intermediate L2 cache node in the PPTT.
  • For Cortex-A720 cores, these L2 nodes describe the real 512 KiB private L2 caches.
  • For Cortex-A520 cores, these L2 nodes are now placeholder cache objects that intentionally omit the Size Property. They exist only to preserve the hierarchy expected by Linux while indicating that no dedicated L2 cache is described.
  • All A520 placeholder nodes now point directly to the shared 12 MiB DSU cache.
  • The shared 12 MiB cache remains the single top-level cache for the entire CPU complex.

The resulting hierarchy is now:

A720
 L1D
 L1I
   ↓
512 KiB private L2
   ↓
12 MiB shared DSU

A520
 L1D
 L1I
   ↓
L2 placeholder (no Size Property)
   ↓
12 MiB shared DSU

The corresponding changes can be seen in the attached diff.

After rebuilding the ACPI override and booting with the updated PPTT, Linux now reports:

  • the shared 12 MiB L3 cache for all CPUs,
  • individual 512 KiB L2 caches for every Cortex-A720 core,
  • placeholder L2 entries for Cortex-A520 cores without a valid size property.

This is significantly closer to the hardware topology described by the Device Tree and the available CIX documentation than the previous implementation.

One interesting observation is that Linux now reports:

L2: 4 MiB (12 instances)

instead of summing the placeholder nodes into the cache total. hwloc also renders these placeholder nodes as “0 KB” caches, which suggests this behavior comes from the Linux cacheinfo/PPTT parser rather than from the PPTT itself.

At this point I believe the remaining question is how the Linux ACPI parser expects CPUs without a dedicated L2 cache to be represented inside PPTT while still maintaining the correct parent relationship to the shared DSU cache.

I have attached both the updated Python generator and the corresponding diff so they can be reviewed.

Finally, I would like to thank @srcshelton for pointing out the issue and providing the information about the cache hierarchy. That observation led to a much more accurate representation of the hardware topology.

new script on: #43 (comment)

@coffnix

coffnix commented Jul 13, 2026

Copy link
Copy Markdown
Author

I created this @srcshelton :

opi6plus ~/cache-check-soc # cat Makefile
obj-m += cache_regs.o

KDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

all:
	$(MAKE) -C $(KDIR) M=$(PWD) modules

clean:
	$(MAKE) -C $(KDIR) M=$(PWD) clean
opi6plus ~/cache-check-soc # cat cache_regs.c
#include <linux/init.h>
#include <linux/cpu.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/smp.h>
#include <linux/types.h>

#include <asm/barrier.h>
#include <asm/sysreg.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Raphael Bastos");
MODULE_DESCRIPTION("Dump ARM64 cache identification registers on every CPU");

static const char *cache_type_name(u64 type)
{
    switch (type) {
    case 0:
        return "No cache";
    case 1:
        return "Instruction only";
    case 2:
        return "Data only";
    case 3:
        return "Separate instruction and data";
    case 4:
        return "Unified";
    default:
        return "Reserved";
    }
}

static void dump_ccsidr(unsigned int cpu, unsigned int level, bool instruction)
{
    u64 original_csselr;
    u64 csselr;
    u64 ccsidr;

    original_csselr = read_sysreg(csselr_el1);

    csselr = ((u64)level << 1) | (instruction ? 1ULL : 0ULL);

    write_sysreg(csselr, csselr_el1);
    isb();

    ccsidr = read_sysreg(ccsidr_el1);

    pr_info(
        "cache_regs: cpu=%u level=L%u kind=%s CSSELR_EL1=0x%016llx CCSIDR_EL1=0x%016llx\n",
        cpu,
        level + 1,
        instruction ? "Instruction" : "Data/Unified",
        csselr,
        ccsidr
    );

    write_sysreg(original_csselr, csselr_el1);
    isb();
}

static void dump_cpu_cache_registers(void *unused)
{
    unsigned int cpu = smp_processor_id();
    unsigned int level;
    u64 clidr;
    u64 ctr;
    u64 id_aa64mmfr2;

    clidr = read_sysreg(clidr_el1);
    ctr = read_sysreg(ctr_el0);
    id_aa64mmfr2 = read_sysreg(id_aa64mmfr2_el1);

    pr_info(
        "cache_regs: cpu=%u CLIDR_EL1=0x%016llx CTR_EL0=0x%016llx ID_AA64MMFR2_EL1=0x%016llx CCIDX=%llu\n",
        cpu,
        clidr,
        ctr,
        id_aa64mmfr2,
        (id_aa64mmfr2 >> 20) & 0xf
    );

    for (level = 0; level < 7; level++) {
        u64 type = (clidr >> (level * 3)) & 0x7;

        pr_info(
            "cache_regs: cpu=%u level=L%u CLIDR-type=%llu description=\"%s\"\n",
            cpu,
            level + 1,
            type,
            cache_type_name(type)
        );

        switch (type) {
        case 0:
            break;

        case 1:
            dump_ccsidr(cpu, level, true);
            break;

        case 2:
            dump_ccsidr(cpu, level, false);
            break;

        case 3:
            dump_ccsidr(cpu, level, false);
            dump_ccsidr(cpu, level, true);
            break;

        case 4:
            dump_ccsidr(cpu, level, false);
            break;

        default:
            pr_warn(
                "cache_regs: cpu=%u level=L%u has reserved cache type %llu\n",
                cpu,
                level + 1,
                type
            );
            break;
        }
    }
}

static int __init cache_regs_init(void)
{
    unsigned int cpu;
    int ret;

    pr_info("cache_regs: reading cache registers on all online CPUs\n");

    cpus_read_lock();

    for_each_online_cpu(cpu) {
        ret = smp_call_function_single(
            cpu,
            dump_cpu_cache_registers,
            NULL,
            true
        );

        if (ret) {
            pr_err(
                "cache_regs: failed to run on cpu=%u, error=%d\n",
                cpu,
                ret
            );
        }
    }

    cpus_read_unlock();

    pr_info("cache_regs: dump complete\n");

    return 0;
}

static void __exit cache_regs_exit(void)
{
    pr_info("cache_regs: module unloaded\n");
}

module_init(cache_regs_init);
module_exit(cache_regs_exit);

Look:

opi6plus ~/cache-check-soc # make
make -C /lib/modules/7.1.3/build M=/root/cache-check-soc modules
make[1]: Entering directory '/usr/src/linux-7.1.3'
make[2]: Entering directory '/root/cache-check-soc'
  CC [M]  cache_regs.o
  MODPOST Module.symvers
  CC [M]  cache_regs.mod.o
  CC [M]  .module-common.o
  LD [M]  cache_regs.ko
make[2]: Leaving directory '/root/cache-check-soc'
make[1]: Leaving directory '/usr/src/linux-7.1.3'
opi6plus ~/cache-check-soc # dmesg -C
insmod ./cache_regs.ko
dmesg | grep 'cache_regs:'
[ 2022.638901] cache_regs: loading out-of-tree module taints kernel.
[ 2022.646271] cache_regs: reading cache registers on all online CPUs
[ 2022.646295] cache_regs: cpu=0 CLIDR_EL1=0x00000054c3000123 CTR_EL0=0x000000049444c004 ID_AA64MMFR2_EL1=0x1221011110101011 CCIDX=1
[ 2022.646309] cache_regs: cpu=0 level=L1 CLIDR-type=3 description="Separate instruction and data"
[ 2022.646314] cache_regs: cpu=0 level=L1 kind=Data/Unified CSSELR_EL1=0x0000000000000000 CCSIDR_EL1=0x000000ff0000001a
[ 2022.646320] cache_regs: cpu=0 level=L1 kind=Instruction CSSELR_EL1=0x0000000000000001 CCSIDR_EL1=0x000000ff0000001a
[ 2022.646325] cache_regs: cpu=0 level=L2 CLIDR-type=4 description="Unified"
[ 2022.646329] cache_regs: cpu=0 level=L2 kind=Data/Unified CSSELR_EL1=0x0000000000000002 CCSIDR_EL1=0x000003ff0000003a
[ 2022.646334] cache_regs: cpu=0 level=L3 CLIDR-type=4 description="Unified"
[ 2022.646337] cache_regs: cpu=0 level=L3 kind=Data/Unified CSSELR_EL1=0x0000000000000004 CCSIDR_EL1=0x00003fff0000005a
[ 2022.646342] cache_regs: cpu=0 level=L4 CLIDR-type=0 description="No cache"
[ 2022.646345] cache_regs: cpu=0 level=L5 CLIDR-type=0 description="No cache"
[ 2022.646349] cache_regs: cpu=0 level=L6 CLIDR-type=0 description="No cache"
[ 2022.646352] cache_regs: cpu=0 level=L7 CLIDR-type=0 description="No cache"
[ 2022.646359] cache_regs: cpu=1 CLIDR_EL1=0x00000054c3000123 CTR_EL0=0x000000049444c004 ID_AA64MMFR2_EL1=0x1221011110101011 CCIDX=1
[ 2022.646371] cache_regs: cpu=1 level=L1 CLIDR-type=3 description="Separate instruction and data"
[ 2022.646376] cache_regs: cpu=1 level=L1 kind=Data/Unified CSSELR_EL1=0x0000000000000000 CCSIDR_EL1=0x000000ff0000001a
[ 2022.646381] cache_regs: cpu=1 level=L1 kind=Instruction CSSELR_EL1=0x0000000000000001 CCSIDR_EL1=0x000000ff0000001a
[ 2022.646386] cache_regs: cpu=1 level=L2 CLIDR-type=4 description="Unified"
[ 2022.646390] cache_regs: cpu=1 level=L2 kind=Data/Unified CSSELR_EL1=0x0000000000000002 CCSIDR_EL1=0x000003ff0000003a
[ 2022.646394] cache_regs: cpu=1 level=L3 CLIDR-type=4 description="Unified"
[ 2022.646398] cache_regs: cpu=1 level=L3 kind=Data/Unified CSSELR_EL1=0x0000000000000004 CCSIDR_EL1=0x00003fff0000005a
[ 2022.646402] cache_regs: cpu=1 level=L4 CLIDR-type=0 description="No cache"
[ 2022.646406] cache_regs: cpu=1 level=L5 CLIDR-type=0 description="No cache"
[ 2022.646410] cache_regs: cpu=1 level=L6 CLIDR-type=0 description="No cache"
[ 2022.646413] cache_regs: cpu=1 level=L7 CLIDR-type=0 description="No cache"
[ 2022.646422] cache_regs: cpu=2 CLIDR_EL1=0x0000001482000023 CTR_EL0=0x000000049444c004 ID_AA64MMFR2_EL1=0x1221011110101011 CCIDX=1
[ 2022.646461] cache_regs: cpu=2 level=L1 CLIDR-type=3 description="Separate instruction and data"
[ 2022.646479] cache_regs: cpu=2 level=L1 kind=Data/Unified CSSELR_EL1=0x0000000000000000 CCSIDR_EL1=0x0000007f0000001a
[ 2022.646498] cache_regs: cpu=2 level=L1 kind=Instruction CSSELR_EL1=0x0000000000000001 CCSIDR_EL1=0x0000007f0000001a
[ 2022.646516] cache_regs: cpu=2 level=L2 CLIDR-type=4 description="Unified"
[ 2022.646531] cache_regs: cpu=2 level=L2 kind=Data/Unified CSSELR_EL1=0x0000000000000002 CCSIDR_EL1=0x00003fff0000005a
[ 2022.646549] cache_regs: cpu=2 level=L3 CLIDR-type=0 description="No cache"
[ 2022.646564] cache_regs: cpu=2 level=L4 CLIDR-type=0 description="No cache"
[ 2022.646578] cache_regs: cpu=2 level=L5 CLIDR-type=0 description="No cache"
[ 2022.646592] cache_regs: cpu=2 level=L6 CLIDR-type=0 description="No cache"
[ 2022.646606] cache_regs: cpu=2 level=L7 CLIDR-type=0 description="No cache"
[ 2022.646626] cache_regs: cpu=3 CLIDR_EL1=0x0000001482000023 CTR_EL0=0x000000049444c004 ID_AA64MMFR2_EL1=0x1221011110101011 CCIDX=1
[ 2022.646658] cache_regs: cpu=3 level=L1 CLIDR-type=3 description="Separate instruction and data"
[ 2022.646677] cache_regs: cpu=3 level=L1 kind=Data/Unified CSSELR_EL1=0x0000000000000000 CCSIDR_EL1=0x0000007f0000001a
[ 2022.646695] cache_regs: cpu=3 level=L1 kind=Instruction CSSELR_EL1=0x0000000000000001 CCSIDR_EL1=0x0000007f0000001a
[ 2022.646713] cache_regs: cpu=3 level=L2 CLIDR-type=4 description="Unified"
[ 2022.646728] cache_regs: cpu=3 level=L2 kind=Data/Unified CSSELR_EL1=0x0000000000000002 CCSIDR_EL1=0x00003fff0000005a
[ 2022.646745] cache_regs: cpu=3 level=L3 CLIDR-type=0 description="No cache"
[ 2022.646760] cache_regs: cpu=3 level=L4 CLIDR-type=0 description="No cache"
[ 2022.646775] cache_regs: cpu=3 level=L5 CLIDR-type=0 description="No cache"
[ 2022.646794] cache_regs: cpu=3 level=L6 CLIDR-type=0 description="No cache"
[ 2022.646809] cache_regs: cpu=3 level=L7 CLIDR-type=0 description="No cache"
[ 2022.646839] cache_regs: cpu=4 CLIDR_EL1=0x0000001482000023 CTR_EL0=0x000000049444c004 ID_AA64MMFR2_EL1=0x1221011110101011 CCIDX=1
[ 2022.646880] cache_regs: cpu=4 level=L1 CLIDR-type=3 description="Separate instruction and data"
[ 2022.646899] cache_regs: cpu=4 level=L1 kind=Data/Unified CSSELR_EL1=0x0000000000000000 CCSIDR_EL1=0x0000007f0000001a
[ 2022.646923] cache_regs: cpu=4 level=L1 kind=Instruction CSSELR_EL1=0x0000000000000001 CCSIDR_EL1=0x0000007f0000001a
[ 2022.646941] cache_regs: cpu=4 level=L2 CLIDR-type=4 description="Unified"
[ 2022.646956] cache_regs: cpu=4 level=L2 kind=Data/Unified CSSELR_EL1=0x0000000000000002 CCSIDR_EL1=0x00003fff0000005a
[ 2022.646979] cache_regs: cpu=4 level=L3 CLIDR-type=0 description="No cache"
[ 2022.646994] cache_regs: cpu=4 level=L4 CLIDR-type=0 description="No cache"
[ 2022.647008] cache_regs: cpu=4 level=L5 CLIDR-type=0 description="No cache"
[ 2022.647022] cache_regs: cpu=4 level=L6 CLIDR-type=0 description="No cache"
[ 2022.647037] cache_regs: cpu=4 level=L7 CLIDR-type=0 description="No cache"
[ 2022.647063] cache_regs: cpu=5 CLIDR_EL1=0x0000001482000023 CTR_EL0=0x000000049444c004 ID_AA64MMFR2_EL1=0x1221011110101011 CCIDX=1
[ 2022.647097] cache_regs: cpu=5 level=L1 CLIDR-type=3 description="Separate instruction and data"
[ 2022.647116] cache_regs: cpu=5 level=L1 kind=Data/Unified CSSELR_EL1=0x0000000000000000 CCSIDR_EL1=0x0000007f0000001a
[ 2022.647141] cache_regs: cpu=5 level=L1 kind=Instruction CSSELR_EL1=0x0000000000000001 CCSIDR_EL1=0x0000007f0000001a
[ 2022.647160] cache_regs: cpu=5 level=L2 CLIDR-type=4 description="Unified"
[ 2022.647176] cache_regs: cpu=5 level=L2 kind=Data/Unified CSSELR_EL1=0x0000000000000002 CCSIDR_EL1=0x00003fff0000005a
[ 2022.647195] cache_regs: cpu=5 level=L3 CLIDR-type=0 description="No cache"
[ 2022.647211] cache_regs: cpu=5 level=L4 CLIDR-type=0 description="No cache"
[ 2022.647227] cache_regs: cpu=5 level=L5 CLIDR-type=0 description="No cache"
[ 2022.647243] cache_regs: cpu=5 level=L6 CLIDR-type=0 description="No cache"
[ 2022.647259] cache_regs: cpu=5 level=L7 CLIDR-type=0 description="No cache"
[ 2022.647278] cache_regs: cpu=6 CLIDR_EL1=0x00000054c3000123 CTR_EL0=0x000000049444c004 ID_AA64MMFR2_EL1=0x1221011110101011 CCIDX=1
[ 2022.647291] cache_regs: cpu=6 level=L1 CLIDR-type=3 description="Separate instruction and data"
[ 2022.647296] cache_regs: cpu=6 level=L1 kind=Data/Unified CSSELR_EL1=0x0000000000000000 CCSIDR_EL1=0x000000ff0000001a
[ 2022.647302] cache_regs: cpu=6 level=L1 kind=Instruction CSSELR_EL1=0x0000000000000001 CCSIDR_EL1=0x000000ff0000001a
[ 2022.647307] cache_regs: cpu=6 level=L2 CLIDR-type=4 description="Unified"
[ 2022.647311] cache_regs: cpu=6 level=L2 kind=Data/Unified CSSELR_EL1=0x0000000000000002 CCSIDR_EL1=0x000003ff0000003a
[ 2022.647316] cache_regs: cpu=6 level=L3 CLIDR-type=4 description="Unified"
[ 2022.647320] cache_regs: cpu=6 level=L3 kind=Data/Unified CSSELR_EL1=0x0000000000000004 CCSIDR_EL1=0x00003fff0000005a
[ 2022.647325] cache_regs: cpu=6 level=L4 CLIDR-type=0 description="No cache"
[ 2022.647329] cache_regs: cpu=6 level=L5 CLIDR-type=0 description="No cache"
[ 2022.647332] cache_regs: cpu=6 level=L6 CLIDR-type=0 description="No cache"
[ 2022.647336] cache_regs: cpu=6 level=L7 CLIDR-type=0 description="No cache"
[ 2022.647340] cache_regs: cpu=7 CLIDR_EL1=0x00000054c3000123 CTR_EL0=0x000000049444c004 ID_AA64MMFR2_EL1=0x1221011110101011 CCIDX=1
[ 2022.647348] cache_regs: cpu=7 level=L1 CLIDR-type=3 description="Separate instruction and data"
[ 2022.647353] cache_regs: cpu=7 level=L1 kind=Data/Unified CSSELR_EL1=0x0000000000000000 CCSIDR_EL1=0x000000ff0000001a
[ 2022.647358] cache_regs: cpu=7 level=L1 kind=Instruction CSSELR_EL1=0x0000000000000001 CCSIDR_EL1=0x000000ff0000001a
[ 2022.647363] cache_regs: cpu=7 level=L2 CLIDR-type=4 description="Unified"
[ 2022.647367] cache_regs: cpu=7 level=L2 kind=Data/Unified CSSELR_EL1=0x0000000000000002 CCSIDR_EL1=0x000003ff0000003a
[ 2022.647372] cache_regs: cpu=7 level=L3 CLIDR-type=4 description="Unified"
[ 2022.647376] cache_regs: cpu=7 level=L3 kind=Data/Unified CSSELR_EL1=0x0000000000000004 CCSIDR_EL1=0x00003fff0000005a
[ 2022.647381] cache_regs: cpu=7 level=L4 CLIDR-type=0 description="No cache"
[ 2022.647385] cache_regs: cpu=7 level=L5 CLIDR-type=0 description="No cache"
[ 2022.647388] cache_regs: cpu=7 level=L6 CLIDR-type=0 description="No cache"
[ 2022.647392] cache_regs: cpu=7 level=L7 CLIDR-type=0 description="No cache"
[ 2022.647399] cache_regs: cpu=8 CLIDR_EL1=0x00000054c3000123 CTR_EL0=0x000000049444c004 ID_AA64MMFR2_EL1=0x1221011110101011 CCIDX=1
[ 2022.647413] cache_regs: cpu=8 level=L1 CLIDR-type=3 description="Separate instruction and data"
[ 2022.647418] cache_regs: cpu=8 level=L1 kind=Data/Unified CSSELR_EL1=0x0000000000000000 CCSIDR_EL1=0x000000ff0000001a
[ 2022.647424] cache_regs: cpu=8 level=L1 kind=Instruction CSSELR_EL1=0x0000000000000001 CCSIDR_EL1=0x000000ff0000001a
[ 2022.647429] cache_regs: cpu=8 level=L2 CLIDR-type=4 description="Unified"
[ 2022.647434] cache_regs: cpu=8 level=L2 kind=Data/Unified CSSELR_EL1=0x0000000000000002 CCSIDR_EL1=0x000003ff0000003a
[ 2022.647439] cache_regs: cpu=8 level=L3 CLIDR-type=4 description="Unified"
[ 2022.647443] cache_regs: cpu=8 level=L3 kind=Data/Unified CSSELR_EL1=0x0000000000000004 CCSIDR_EL1=0x00003fff0000005a
[ 2022.647448] cache_regs: cpu=8 level=L4 CLIDR-type=0 description="No cache"
[ 2022.647452] cache_regs: cpu=8 level=L5 CLIDR-type=0 description="No cache"
[ 2022.647455] cache_regs: cpu=8 level=L6 CLIDR-type=0 description="No cache"
[ 2022.647459] cache_regs: cpu=8 level=L7 CLIDR-type=0 description="No cache"
[ 2022.647467] cache_regs: cpu=9 CLIDR_EL1=0x00000054c3000123 CTR_EL0=0x000000049444c004 ID_AA64MMFR2_EL1=0x1221011110101011 CCIDX=1
[ 2022.647480] cache_regs: cpu=9 level=L1 CLIDR-type=3 description="Separate instruction and data"
[ 2022.647485] cache_regs: cpu=9 level=L1 kind=Data/Unified CSSELR_EL1=0x0000000000000000 CCSIDR_EL1=0x000000ff0000001a
[ 2022.647491] cache_regs: cpu=9 level=L1 kind=Instruction CSSELR_EL1=0x0000000000000001 CCSIDR_EL1=0x000000ff0000001a
[ 2022.647497] cache_regs: cpu=9 level=L2 CLIDR-type=4 description="Unified"
[ 2022.647501] cache_regs: cpu=9 level=L2 kind=Data/Unified CSSELR_EL1=0x0000000000000002 CCSIDR_EL1=0x000003ff0000003a
[ 2022.647506] cache_regs: cpu=9 level=L3 CLIDR-type=4 description="Unified"
[ 2022.647510] cache_regs: cpu=9 level=L3 kind=Data/Unified CSSELR_EL1=0x0000000000000004 CCSIDR_EL1=0x00003fff0000005a
[ 2022.647515] cache_regs: cpu=9 level=L4 CLIDR-type=0 description="No cache"
[ 2022.647519] cache_regs: cpu=9 level=L5 CLIDR-type=0 description="No cache"
[ 2022.647523] cache_regs: cpu=9 level=L6 CLIDR-type=0 description="No cache"
[ 2022.647527] cache_regs: cpu=9 level=L7 CLIDR-type=0 description="No cache"
[ 2022.647534] cache_regs: cpu=10 CLIDR_EL1=0x00000054c3000123 CTR_EL0=0x000000049444c004 ID_AA64MMFR2_EL1=0x1221011110101011 CCIDX=1
[ 2022.647547] cache_regs: cpu=10 level=L1 CLIDR-type=3 description="Separate instruction and data"
[ 2022.647552] cache_regs: cpu=10 level=L1 kind=Data/Unified CSSELR_EL1=0x0000000000000000 CCSIDR_EL1=0x000000ff0000001a
[ 2022.647557] cache_regs: cpu=10 level=L1 kind=Instruction CSSELR_EL1=0x0000000000000001 CCSIDR_EL1=0x000000ff0000001a
[ 2022.647562] cache_regs: cpu=10 level=L2 CLIDR-type=4 description="Unified"
[ 2022.647566] cache_regs: cpu=10 level=L2 kind=Data/Unified CSSELR_EL1=0x0000000000000002 CCSIDR_EL1=0x000003ff0000003a
[ 2022.647571] cache_regs: cpu=10 level=L3 CLIDR-type=4 description="Unified"
[ 2022.647574] cache_regs: cpu=10 level=L3 kind=Data/Unified CSSELR_EL1=0x0000000000000004 CCSIDR_EL1=0x00003fff0000005a
[ 2022.647579] cache_regs: cpu=10 level=L4 CLIDR-type=0 description="No cache"
[ 2022.647583] cache_regs: cpu=10 level=L5 CLIDR-type=0 description="No cache"
[ 2022.647586] cache_regs: cpu=10 level=L6 CLIDR-type=0 description="No cache"
[ 2022.647589] cache_regs: cpu=10 level=L7 CLIDR-type=0 description="No cache"
[ 2022.647597] cache_regs: cpu=11 CLIDR_EL1=0x00000054c3000123 CTR_EL0=0x000000049444c004 ID_AA64MMFR2_EL1=0x1221011110101011 CCIDX=1
[ 2022.647610] cache_regs: cpu=11 level=L1 CLIDR-type=3 description="Separate instruction and data"
[ 2022.647615] cache_regs: cpu=11 level=L1 kind=Data/Unified CSSELR_EL1=0x0000000000000000 CCSIDR_EL1=0x000000ff0000001a
[ 2022.647621] cache_regs: cpu=11 level=L1 kind=Instruction CSSELR_EL1=0x0000000000000001 CCSIDR_EL1=0x000000ff0000001a
[ 2022.647626] cache_regs: cpu=11 level=L2 CLIDR-type=4 description="Unified"
[ 2022.647629] cache_regs: cpu=11 level=L2 kind=Data/Unified CSSELR_EL1=0x0000000000000002 CCSIDR_EL1=0x000003ff0000003a
[ 2022.647634] cache_regs: cpu=11 level=L3 CLIDR-type=4 description="Unified"
[ 2022.647637] cache_regs: cpu=11 level=L3 kind=Data/Unified CSSELR_EL1=0x0000000000000004 CCSIDR_EL1=0x00003fff0000005a
[ 2022.647642] cache_regs: cpu=11 level=L4 CLIDR-type=0 description="No cache"
[ 2022.647646] cache_regs: cpu=11 level=L5 CLIDR-type=0 description="No cache"
[ 2022.647649] cache_regs: cpu=11 level=L6 CLIDR-type=0 description="No cache"
[ 2022.647653] cache_regs: cpu=11 level=L7 CLIDR-type=0 description="No cache"
[ 2022.647656] cache_regs: dump complete

After instrumenting the kernel with a custom module that reads the architectural cache registers directly from every online CPU, we now have a much clearer picture of the actual cache hierarchy implemented by the CIX CD8160/CD8180. The Cortex-A720 cores (CPUs 0, 1, and 6 through 11) report CLIDR_EL1 = 0x54c3000123, which exposes a conventional three level hierarchy consisting of separate L1 instruction and data caches, a private unified 512 KiB L2 cache (CCSIDR_EL1 = 0x000003ff0000003a), and a shared unified 12 MiB L3 cache (CCSIDR_EL1 = 0x00003fff0000005a). In contrast, the Cortex-A520 cores (CPUs 2 through 5) report a completely different CLIDR_EL1 = 0x1482000023. These cores expose separate L1 instruction and data caches, followed by a unified cache whose CCSIDR_EL1 value is identical to the 12 MiB cache reported as L3 by the A720 cores, while reporting no L3 cache at all (CLIDR-type = 0 for level 3). In other words, the hardware itself presents the shared 12 MiB DSU cache as the last visible cache level for the A520 cluster, whereas the A720 cores expose the exact same physical cache as L3 behind their private 512 KiB L2 caches. This means both core types ultimately share the same 12 MiB DSU cache, but they expose different architectural views through CLIDR/CCSIDR. This observation explains why several PPTT layouts produced inconsistent results in lscpu and hwloc: the kernel attempts to reconcile the ACPI PPTT hierarchy with the cache topology reported by the processors themselves. The architectural registers are the definitive source of truth, and the dump confirms that any PPTT describing this SoC must accurately reflect these differing cache hierarchies instead of assuming that all cores expose identical cache levels.

@coffnix

coffnix commented Jul 13, 2026

Copy link
Copy Markdown
Author

Unless the SoC on the Orange Pi 6 Plus has advanced from the CD8180 of the Orion O6, I don't think this is right: the A520 cores have no direct L2, and use the shared 12MB (A720) L3 as their L2.

The vendor block-diagram at #43 seems to support this conclusion, as does direct inspection of the per-core CLIDR_EL1/CCSIDR_EL1 cache registers.

now:

opi6plus ~ # lscpu -C
NAME ONE-SIZE ALL-SIZE WAYS TYPE        LEVEL SETS PHY-LINE COHERENCY-SIZE
L1d       64K     640K      Data            1                           64
L1i       64K     640K      Instruction     1                           64
L2       512K       6M      Unified         2                           64
L3        12M      12M      Unified         3                           64
opi6plus ~ # hwloc-ls
Machine (31GB total)
  Package L#0
    NUMANode L#0 (P#0 31GB)
    L3 L#0 (12MB)
      Group0(Cluster) L#0
        L2 L#0 (512KB) + L1d L#0 (64KB) + L1i L#0 (64KB) + Core L#0 + PU L#0 (P#0)
        L2 L#1 (512KB) + L1d L#1 (64KB) + L1i L#1 (64KB) + Core L#1 + PU L#1 (P#1)
      Group0(Cluster) L#1
        L2 L#2 (512KB) + L1d L#2 (32KB) + L1i L#2 (32KB) + Core L#2 + PU L#2 (P#2)
        L2 L#3 (512KB) + L1d L#3 (32KB) + L1i L#3 (32KB) + Core L#3 + PU L#3 (P#3)
        L2 L#4 (512KB) + L1d L#4 (32KB) + L1i L#4 (32KB) + Core L#4 + PU L#4 (P#4)
        L2 L#5 (512KB) + L1d L#5 (32KB) + L1i L#5 (32KB) + Core L#5 + PU L#5 (P#5)
      Group0(Cluster) L#2
        L2 L#6 (512KB) + L1d L#6 (64KB) + L1i L#6 (64KB) + Core L#6 + PU L#6 (P#6)
        L2 L#7 (512KB) + L1d L#7 (64KB) + L1i L#7 (64KB) + Core L#7 + PU L#7 (P#7)
      Group0(Cluster) L#3
        L2 L#8 (512KB) + L1d L#8 (64KB) + L1i L#8 (64KB) + Core L#8 + PU L#8 (P#8)
        L2 L#9 (512KB) + L1d L#9 (64KB) + L1i L#9 (64KB) + Core L#9 + PU L#9 (P#9)
      Group0(Cluster) L#4
        L2 L#10 (512KB) + L1d L#10 (64KB) + L1i L#10 (64KB) + Core L#10 + PU L#10 (P#10)
        L2 L#11 (512KB) + L1d L#11 (64KB) + L1i L#11 (64KB) + Core L#11 + PU L#11 (P#11)
  HostBridge
    PCIBridge
      PCI 31:00.0 (Ethernet)
        Net "eth1"
  HostBridge
    PCIBridge
      PCI 61:00.0 (Ethernet)
        Net "eth0"
  HostBridge
    PCIBridge
      PCI 91:00.0 (NVMExp)
        Block(Disk) "nvme0n1"

thanks again @srcshelton

@MartJohnson

MartJohnson commented Jul 14, 2026

Copy link
Copy Markdown

@coffnix on an MS-R1 from the Minisforum BIOS, this is what I get, its slightly different to what you posted above:

image

Any idea why?

L2 on mine shows as 4M in lscpu -C...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants