From 1b6f8c0bb198a5498cc7e39362b004fd82191a0a Mon Sep 17 00:00:00 2001 From: Nick Chan Date: Sun, 9 Feb 2025 00:34:55 +0800 Subject: [PATCH 1/5] pmgr: Add pmgr_power_on() function to power on power domains by name Some power domains are not referenced by other nodes through clock-gates or power-gates, so this is needed. Signed-off-by: Nick Chan --- src/pmgr.c | 22 ++++++++++++++++++++++ src/pmgr.h | 1 + 2 files changed, 23 insertions(+) diff --git a/src/pmgr.c b/src/pmgr.c index d78bc8e6b..7613e0d4c 100644 --- a/src/pmgr.c +++ b/src/pmgr.c @@ -341,6 +341,28 @@ int pmgr_reset(int die, const char *name) return pmgr_reset_device(die, dev); } +int pmgr_power_on(int die, const char *name) +{ + const struct pmgr_device *dev = NULL; + + for (unsigned int i = 0; i < pmgr_devices_len; ++i) { + if (strncmp(pmgr_devices[i].name, name, 0x10) == 0) { + dev = &pmgr_devices[i]; + break; + } + } + + if (!dev) + return -1; + + uintptr_t addr = pmgr_device_get_addr(die, dev); + + if (!addr) + return -1; + + return pmgr_set_mode(addr, PMGR_PS_ACTIVE); +} + int pmgr_init(void) { int node = adt_path_offset(adt, "/arm-io"); diff --git a/src/pmgr.h b/src/pmgr.h index 3b61a57af..ba2ca9907 100644 --- a/src/pmgr.h +++ b/src/pmgr.h @@ -26,6 +26,7 @@ int pmgr_adt_power_disable_index(const char *path, u32 index); int pmgr_adt_reset(const char *path); int pmgr_reset(int die, const char *name); +int pmgr_power_on(int die, const char *name); int pmgr_set_mode(uintptr_t addr, u8 target_mode); From abcb681c8ee10c848adbae5d803ea446ed4876fa Mon Sep 17 00:00:00 2001 From: Nick Chan Date: Sun, 9 Feb 2025 00:36:47 +0800 Subject: [PATCH 2/5] utils: Add poll64() function This is the 64-bit variant of poll32(). Signed-off-by: Nick Chan --- src/utils.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/utils.h b/src/utils.h index 6789da7ec..ff75a676e 100644 --- a/src/utils.h +++ b/src/utils.h @@ -454,6 +454,18 @@ static inline int poll32(u64 addr, u32 mask, u32 target, u32 timeout) return -1; } +static inline int poll64(u64 addr, u64 mask, u64 target, u32 timeout) +{ + while (--timeout > 0) { + u32 value = read64(addr) & mask; + if (value == target) + return 0; + udelay(1); + } + + return -1; +} + typedef u64(generic_func)(u64, u64, u64, u64, u64); struct vector_args { From 02ead58cb7be9e9c5403cd7c6fff7d12d8dacbaa Mon Sep 17 00:00:00 2001 From: Nick Chan Date: Sun, 23 Feb 2025 21:28:16 +0800 Subject: [PATCH 3/5] cpufreq: refractor code to use hardcoded chip_id operations The existing code is already confusing as is and support for more chips with different requirements will be added, which would make the code even more confusing. Instead, switch to a hardcoded list of chip_id's for operations that are chip-specific or will be chip-speciifc. Signed-off-by: Nick Chan --- src/cpufreq.c | 81 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 68 insertions(+), 13 deletions(-) diff --git a/src/cpufreq.c b/src/cpufreq.c index 7dd33b2e2..848629310 100644 --- a/src/cpufreq.c +++ b/src/cpufreq.c @@ -39,16 +39,49 @@ struct feat_t { bool pcluster_only; }; +static u32 pstate_reg_to_pstate(u64 val) +{ + switch (chip_id) { + case T8103: + case T6000: + case T6001: + case T6002: + case T8112: + case T6020: + case T6021: + case T6022: + case T6031: + return FIELD_GET(CLUSTER_PSTATE_DESIRED1, val); + default: + printf("cpufreq: Chip 0x%x is unsupported\n", chip_id); + return 0; + } +} + static int set_pstate(const struct cluster_t *cluster, uint32_t pstate) { u64 val = read64(cluster->base + CLUSTER_PSTATE); - if (FIELD_GET(CLUSTER_PSTATE_DESIRED1, val) != pstate) { - val &= ~CLUSTER_PSTATE_DESIRED1; - val |= CLUSTER_PSTATE_SET | FIELD_PREP(CLUSTER_PSTATE_DESIRED1, pstate); - if (chip_id == T8103 || chip_id <= T6002) { - val &= ~CLUSTER_PSTATE_DESIRED2; - val |= CLUSTER_PSTATE_SET | FIELD_PREP(CLUSTER_PSTATE_DESIRED2, pstate); + if (pstate_reg_to_pstate(val) != pstate) { + switch (chip_id) { + case T8103: + case T6000: + case T6001: + case T6002: + val &= ~CLUSTER_PSTATE_DESIRED2; + val |= CLUSTER_PSTATE_SET | FIELD_PREP(CLUSTER_PSTATE_DESIRED2, pstate); + /* fallthrough */ + case T8112: + case T6020: + case T6021: + case T6022: + case T6031: + val &= ~CLUSTER_PSTATE_DESIRED1; + val |= CLUSTER_PSTATE_SET | FIELD_PREP(CLUSTER_PSTATE_DESIRED1, pstate); + break; + default: + printf("cpufreq: Chip 0x%x is unsupported\n", chip_id); + return -1; } write64(cluster->base + CLUSTER_PSTATE, val); if (poll32(cluster->base + CLUSTER_PSTATE, CLUSTER_PSTATE_BUSY, 0, CLUSTER_SWITCH_TIMEOUT) < @@ -88,11 +121,27 @@ int cpufreq_init_cluster(const struct cluster_t *cluster, const struct feat_t *f } } - /* Unknown */ - write64(cluster->base + 0x440f8, 1); + switch (chip_id) { + case T8103: + case T6000: + case T6001: + case T6002: + case T8112: + case T6020: + case T6021: + case T6022: + case T6031: + /* Unknown */ + write64(cluster->base + 0x440f8, 1); + + /* Initialize APSC */ + set64(cluster->base + 0x200f8, BIT(40)); + break; + default: + printf("cpufreq: Chip 0x%x is unsupported\n", chip_id); + break; + } - /* Initialize APSC */ - set64(cluster->base + 0x200f8, BIT(40)); switch (chip_id) { case T8103: { u64 lo = read64(cluster->base + 0x70000 + cluster->apsc_pstate * 0x20); @@ -129,11 +178,15 @@ void cpufreq_fixup_cluster(const struct cluster_t *cluster) u64 bits = 0; switch (chip_id) { case T8103: - case T6000 ... T6002: + case T6000: + case T6001: + case T6002: bits = CLUSTER_PSTATE_UNK_M1; break; case T8112: - case T6020 ... T6022: + case T6020: + case T6021: + case T6022: bits = CLUSTER_PSTATE_UNK_M2; break; default: @@ -272,7 +325,9 @@ const struct feat_t *cpufreq_get_features(void) { switch (chip_id) { case T8103: - case T6000 ... T6002: + case T6000: + case T6001: + case T6002: return t8103_features; case T8112: return t8112_features; From 7d6119d64fe41d6659b8378e9ea113245b53635d Mon Sep 17 00:00:00 2001 From: Nick Chan Date: Sun, 23 Feb 2025 21:30:28 +0800 Subject: [PATCH 4/5] cpufreq: Switch to poll64() for polling CLUSTER_PSTATE cpufreq support for Apple A9 will be added. It requires 64-bit access on the CLUSTER_PSTATE register. Signed-off-by: Nick Chan --- src/cpufreq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpufreq.c b/src/cpufreq.c index 848629310..c4a55eac1 100644 --- a/src/cpufreq.c +++ b/src/cpufreq.c @@ -84,7 +84,7 @@ static int set_pstate(const struct cluster_t *cluster, uint32_t pstate) return -1; } write64(cluster->base + CLUSTER_PSTATE, val); - if (poll32(cluster->base + CLUSTER_PSTATE, CLUSTER_PSTATE_BUSY, 0, CLUSTER_SWITCH_TIMEOUT) < + if (poll64(cluster->base + CLUSTER_PSTATE, CLUSTER_PSTATE_BUSY, 0, CLUSTER_SWITCH_TIMEOUT) < 0) { printf("cpufreq: Timed out waiting for cluster %s P-State switch\n", cluster->name); return -1; From 69d1feefd8a7763978f15d3e67cf39ebb2ddc3f3 Mon Sep 17 00:00:00 2001 From: Nick Chan Date: Sun, 23 Feb 2025 21:41:48 +0800 Subject: [PATCH 5/5] cpufreq: Add Apple A7-A11, T2 SoC support - A7-A8X uses 3 bits only for pstate, and PS1 mask is GENMASK(24, 22) - Add tunables, mainly the voltage control register in PMGR (iBoot boots us with voltage changes seemingly disabled, with obvious disastrous outcome trying to switch to high P-states) - Skip writes to offset 0x4xxxx from cluster->base for A7-A11, T2. The range starting from 0x40000 from cluster->base does not exist in ADT /arm-io/pmgr, so it probably does not exist for those older chips. This includes the 0x440f8 and ppt-thrtl writes. Signed-off-by: Nick Chan --- src/cpufreq.c | 158 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 157 insertions(+), 1 deletion(-) diff --git a/src/cpufreq.c b/src/cpufreq.c index c4a55eac1..b237a2da1 100644 --- a/src/cpufreq.c +++ b/src/cpufreq.c @@ -19,8 +19,13 @@ #define CLUSTER_PSTATE_DESIRED2 GENMASK(15, 12) #define CLUSTER_PSTATE_APSC_BUSY BIT(7) #define CLUSTER_PSTATE_DESIRED1 GENMASK(4, 0) +#define CLUSTER_PSTATE_DESIRED1_S5L8960X GENMASK(24, 22) -#define CLUSTER_SWITCH_TIMEOUT 100 +#define PMGR_VOLTAGE_CTL_OFF_S5L8960X 0x20c00 +#define PMGR_VOLTAGE_CTL_OFF_T7000 0x23000 +#define PMGR_VOLTAGE_CTL_OFF_S8000 0xa0000 + +#define CLUSTER_SWITCH_TIMEOUT 400 struct cluster_t { const char *name; @@ -42,6 +47,17 @@ struct feat_t { static u32 pstate_reg_to_pstate(u64 val) { switch (chip_id) { + case S5L8960X: + case T7000: + case T7001: + return FIELD_GET(CLUSTER_PSTATE_DESIRED1_S5L8960X, val); + case S8000: + case S8001: + case S8003: + case T8010: + case T8011: + case T8012: + case T8015: case T8103: case T6000: case T6001: @@ -64,6 +80,19 @@ static int set_pstate(const struct cluster_t *cluster, uint32_t pstate) if (pstate_reg_to_pstate(val) != pstate) { switch (chip_id) { + case S5L8960X: + case T7000: + case T7001: + val &= ~CLUSTER_PSTATE_DESIRED1_S5L8960X; + val |= CLUSTER_PSTATE_SET | FIELD_PREP(CLUSTER_PSTATE_DESIRED1_S5L8960X, pstate); + break; + case S8000: + case S8001: + case S8003: + case T8010: + case T8011: + case T8012: + case T8015: case T8103: case T6000: case T6001: @@ -121,7 +150,40 @@ int cpufreq_init_cluster(const struct cluster_t *cluster, const struct feat_t *f } } + int pmgr_path[8]; + u64 pmgr_reg; + + if (adt_path_offset_trace(adt, "/arm-io/pmgr", pmgr_path) < 0) { + printf("Error getting /arm-io/pmgr node\n"); + return -1; + } + + if (adt_get_reg(adt, pmgr_path, "reg", 0, &pmgr_reg, NULL) < 0) { + printf("Error getting /arm-io/pmgr regs\n"); + return -1; + } + switch (chip_id) { + case S5L8960X: + write32(pmgr_reg + PMGR_VOLTAGE_CTL_OFF_S5L8960X, 1); + break; + case T7000: + case T7001: + write32(pmgr_reg + PMGR_VOLTAGE_CTL_OFF_T7000, 1); + break; + case S8000: + case S8001: + case S8003: + case T8010: + case T8011: + case T8012: + case T8015: + /* + * On T8015 this will result in the register being written + * two times (for two clusters). However, this is fine. + */ + write32(pmgr_reg + PMGR_VOLTAGE_CTL_OFF_S8000, 1); + break; case T8103: case T6000: case T6001: @@ -201,6 +263,44 @@ void cpufreq_fixup_cluster(const struct cluster_t *cluster) } } +static const struct cluster_t s5l8960x_clusters[] = { + {"CPU", 0x202200000, false, 2, 6}, + {}, +}; + +static const struct cluster_t t7000_clusters[] = { + {"CPU", 0x202200000, false, 2, 5}, + {}, +}; + +static const struct cluster_t t7001_clusters[] = { + {"CPU", 0x202200000, false, 2, 7}, + {}, +}; + +static const struct cluster_t s8000_clusters[] = { + {"CPU", 0x202200000, false, 2, 7}, + {}, +}; + +static const struct cluster_t t8010_clusters[] = { + /* Fused cluster, kernel expects E-core entry */ + {"CPU", 0x202f00000, false, 2, 4}, + {}, +}; + +static const struct cluster_t t8012_clusters[] = { + /* Fused cluster, kernel expects P-core entry */ + {"CPU", 0x202f00000, false, 6, 10}, + {}, +}; + +static const struct cluster_t t8015_clusters[] = { + {"ECPU", 0x208e00000, false, 2, 6}, + {"PCPU", 0x208e80000, true, 2, 7}, + {}, +}; + static const struct cluster_t t8103_clusters[] = { {"ECPU", 0x210e00000, false, 1, 5}, {"PCPU", 0x211e00000, true, 1, 7}, @@ -256,6 +356,23 @@ static const struct cluster_t t6031_clusters[] = { const struct cluster_t *cpufreq_get_clusters(void) { switch (chip_id) { + case S5L8960X: + return s5l8960x_clusters; + case T7000: + return t7000_clusters; + case T7001: + return t7001_clusters; + case S8000: + case S8001: + case S8003: + return s8000_clusters; + case T8010: + case T8011: + return t8010_clusters; + case T8012: + return t8012_clusters; + case T8015: + return t8015_clusters; case T8103: return t8103_clusters; case T6000: @@ -278,6 +395,27 @@ const struct cluster_t *cpufreq_get_clusters(void) } } +static const struct feat_t s5l8960x_features[] = { + {}, +}; + +static const struct feat_t s8000_features[] = { + {"cpu-apsc", CLUSTER_PSTATE, CLUSTER_PSTATE_M1_APSC_DIS, 0, CLUSTER_PSTATE_APSC_BUSY, false}, + {}, +}; + +static const struct feat_t t8010_features[] = { + {"cpu-apsc", CLUSTER_PSTATE, CLUSTER_PSTATE_M1_APSC_DIS, 0, CLUSTER_PSTATE_APSC_BUSY, false}, + {}, +}; + +static const struct feat_t t8015_features[] = { + {"cpu-apsc", CLUSTER_PSTATE, CLUSTER_PSTATE_M1_APSC_DIS, 0, CLUSTER_PSTATE_APSC_BUSY, false}, + {"cpu-fixed-freq-pll-relock", CLUSTER_PSTATE, 0, CLUSTER_PSTATE_FIXED_FREQ_PLL_RECLOCK, 0, + false}, + {}, +}; + static const struct feat_t t8103_features[] = { {"cpu-apsc", CLUSTER_PSTATE, CLUSTER_PSTATE_M1_APSC_DIS, 0, CLUSTER_PSTATE_APSC_BUSY, false}, {"ppt-thrtl", 0x48400, 0, BIT(63), 0, false}, @@ -324,6 +462,20 @@ static const struct feat_t t6031_features[] = { const struct feat_t *cpufreq_get_features(void) { switch (chip_id) { + case S5L8960X: + case T7000: + case T7001: + return s5l8960x_features; + case S8000: + case S8001: + case S8003: + return s8000_features; + case T8010: + case T8011: + case T8012: + return t8010_features; + case T8015: + return t8015_features; case T8103: case T6000: case T6001: @@ -353,6 +505,10 @@ int cpufreq_init(void) if (!cluster || !features) return -1; + /* Without this, CLUSTER_PSTATE_BUSY gets stuck */ + if (chip_id == T8012 || chip_id == T8015) + pmgr_power_on(0, "SPMI"); + bool err = false; while (cluster->base) { err |= cpufreq_init_cluster(cluster++, features);