diff --git a/src/cpufreq.c b/src/cpufreq.c index 7dd33b2e2..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; @@ -39,19 +44,76 @@ struct feat_t { bool pcluster_only; }; +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: + 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 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: + 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) < + 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; @@ -88,11 +150,60 @@ int cpufreq_init_cluster(const struct cluster_t *cluster, const struct feat_t *f } } - /* Unknown */ - write64(cluster->base + 0x440f8, 1); + 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: + 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 +240,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: @@ -148,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}, @@ -203,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: @@ -225,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}, @@ -271,8 +462,24 @@ 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 ... T6002: + case T6000: + case T6001: + case T6002: return t8103_features; case T8112: return t8112_features; @@ -298,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); 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); 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 {