m1n1/src/cpufreq.c
Hector Martin 6de69564bd cpufreq: Fix t6022 clusters
We were only initializing the first die...

Signed-off-by: Hector Martin <marcan@marcan.st>
2023-10-08 19:14:33 +09:00

299 lines
8.9 KiB
C

/* SPDX-License-Identifier: MIT */
#include "cpufreq.h"
#include "adt.h"
#include "firmware.h"
#include "pmgr.h"
#include "soc.h"
#include "utils.h"
#define CLUSTER_PSTATE 0x20020
#define CLUSTER_PSTATE_FIXED_FREQ_PLL_RECLOCK BIT(42)
#define CLUSTER_PSTATE_BUSY BIT(31)
#define CLUSTER_PSTATE_SET BIT(25)
#define CLUSTER_PSTATE_M2_APSC_DIS BIT(23)
#define CLUSTER_PSTATE_M1_APSC_DIS BIT(22)
#define CLUSTER_PSTATE_UNK_M2 BIT(22)
#define CLUSTER_PSTATE_UNK_M1 BIT(20)
#define CLUSTER_PSTATE_DESIRED2 GENMASK(15, 12)
#define CLUSTER_PSTATE_APSC_BUSY BIT(7)
#define CLUSTER_PSTATE_DESIRED1 GENMASK(4, 0)
#define CLUSTER_SWITCH_TIMEOUT 100
struct cluster_t {
const char *name;
u64 base;
bool pcluster;
uint32_t apsc_pstate;
uint32_t default_pstate;
};
struct feat_t {
const char *name;
u64 offset;
u64 clear;
u64 set;
u64 wait;
bool pcluster_only;
};
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);
}
write64(cluster->base + CLUSTER_PSTATE, val);
if (poll32(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;
}
}
return 0;
}
int cpufreq_init_cluster(const struct cluster_t *cluster, const struct feat_t *features)
{
/* Reset P-State to the APSC p-state */
if (cluster->apsc_pstate && set_pstate(cluster, cluster->apsc_pstate))
return -1;
/* CPU complex features */
for (; features->name; features++) {
if (features->pcluster_only && !cluster->pcluster)
continue;
u64 reg = cluster->base + features->offset;
if (pmgr_get_feature(features->name))
mask64(reg, features->clear, features->set);
else
mask64(reg, features->set, features->clear);
if (features->wait && poll32(reg, features->wait, 0, CLUSTER_SWITCH_TIMEOUT) < 0) {
printf("cpufreq: Timed out waiting for feature %s on cluster %s\n", features->name,
cluster->name);
return -1;
}
}
/* Unknown */
write64(cluster->base + 0x440f8, 1);
/* Initialize APSC */
set64(cluster->base + 0x200f8, BIT(40));
switch (chip_id) {
case T8103: {
u64 lo = read64(cluster->base + 0x70000 + cluster->apsc_pstate * 0x20);
u64 hi = read64(cluster->base + 0x70008 + cluster->apsc_pstate * 0x20);
write64(cluster->base + 0x70210, lo);
write64(cluster->base + 0x70218, hi);
break;
}
case T8112: {
u64 lo = read64(cluster->base + 0x78000 + cluster->apsc_pstate * 0x40);
u64 hi = read64(cluster->base + 0x78008 + cluster->apsc_pstate * 0x40);
write64(cluster->base + 0x7ffe8, lo);
write64(cluster->base + 0x7fff0, hi);
break;
}
}
/* Default P-State */
if (cluster->default_pstate && set_pstate(cluster, cluster->default_pstate))
return -1;
return 0;
}
void cpufreq_fixup_cluster(const struct cluster_t *cluster)
{
u64 val = read64(cluster->base + CLUSTER_PSTATE);
// Older versions of m1n1 stage 1 erroneously cleared CLUSTER_PSTATE_UNK_Mx, so put it back for
// firmwares it supported (don't touch anything newer, which includes newer devices).
// Also clear the CLUSTER_PSTATE_DESIRED2 field since it doesn't seem to do anything, and isn't
// used on newer chips.
if (os_firmware.version != V_UNKNOWN && os_firmware.version <= V13_3) {
u64 bits = 0;
switch (chip_id) {
case T8103:
case T6000 ... T6002:
bits = CLUSTER_PSTATE_UNK_M1;
break;
case T8112:
case T6020 ... T6022:
bits = CLUSTER_PSTATE_UNK_M2;
break;
default:
return;
}
if (!(val & bits) || (val & CLUSTER_PSTATE_DESIRED2)) {
val |= bits;
val &= ~CLUSTER_PSTATE_DESIRED2;
printf("cpufreq: Correcting setting for cluster %s\n", cluster->name);
write64(cluster->base + CLUSTER_PSTATE, val);
}
}
}
static const struct cluster_t t8103_clusters[] = {
{"ECPU", 0x210e00000, false, 1, 5},
{"PCPU", 0x211e00000, true, 1, 7},
{},
};
static const struct cluster_t t6000_clusters[] = {
{"ECPU0", 0x210e00000, false, 1, 5},
{"PCPU0", 0x211e00000, true, 1, 7},
{"PCPU1", 0x212e00000, true, 1, 7},
{},
};
static const struct cluster_t t6002_clusters[] = {
{"ECPU0", 0x0210e00000, false, 1, 5},
{"PCPU0", 0x0211e00000, true, 1, 7},
{"PCPU1", 0x0212e00000, true, 1, 7},
{"ECPU1", 0x2210e00000, false, 1, 5},
{"PCPU2", 0x2211e00000, true, 1, 7},
{"PCPU3", 0x2212e00000, true, 1, 7},
{},
};
static const struct cluster_t t8112_clusters[] = {
{"ECPU", 0x210e00000, false, 1, 7},
{"PCPU", 0x211e00000, true, 1, 6},
{},
};
static const struct cluster_t t6020_clusters[] = {
{"ECPU0", 0x210e00000, false, 1, 5},
{"PCPU0", 0x211e00000, true, 1, 6},
{"PCPU1", 0x212e00000, true, 1, 6},
{},
};
static const struct cluster_t t6022_clusters[] = {
{"ECPU0", 0x0210e00000, false, 1, 5},
{"PCPU0", 0x0211e00000, true, 1, 6},
{"PCPU1", 0x0212e00000, true, 1, 6},
{"ECPU1", 0x2210e00000, false, 1, 5},
{"PCPU2", 0x2211e00000, true, 1, 6},
{"PCPU3", 0x2212e00000, true, 1, 6},
{},
};
const struct cluster_t *cpufreq_get_clusters(void)
{
switch (chip_id) {
case T8103:
return t8103_clusters;
case T6000:
case T6001:
return t6000_clusters;
case T6002:
return t6002_clusters;
case T8112:
return t8112_clusters;
case T6020:
case T6021:
return t6020_clusters;
case T6022:
return t6022_clusters;
default:
printf("cpufreq: Chip 0x%x is unsupported\n", chip_id);
return NULL;
}
}
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},
{"llc-thrtl", 0x40240, 0, BIT(63), 0, false},
{"amx-thrtl", 0x40250, 0, BIT(63), 0, false},
{"cpu-fixed-freq-pll-relock", CLUSTER_PSTATE, 0, CLUSTER_PSTATE_FIXED_FREQ_PLL_RECLOCK, 0,
false},
{},
};
static const struct feat_t t8112_features[] = {
{"cpu-apsc", CLUSTER_PSTATE, CLUSTER_PSTATE_M2_APSC_DIS, 0, CLUSTER_PSTATE_APSC_BUSY, false},
{"ppt-thrtl", 0x40270, 0, BIT(63), 0, false},
{"ppt-thrtl", 0x48408, 0, BIT(63), 0, false},
{"ppt-thrtl", 0x48b30, 0, BIT(0), 0, true},
{"ppt-thrtl", 0x20078, 0, BIT(0), 0, true},
{"ppt-thrtl", 0x48400, 0, BIT(63), 0, false},
{"amx-thrtl", 0x40250, 0, BIT(63), 0, false},
{"cpu-fixed-freq-pll-relock", CLUSTER_PSTATE, 0, CLUSTER_PSTATE_FIXED_FREQ_PLL_RECLOCK, 0,
false},
{},
};
static const struct feat_t t6020_features[] = {
{"cpu-apsc", CLUSTER_PSTATE, CLUSTER_PSTATE_M2_APSC_DIS, 0, CLUSTER_PSTATE_APSC_BUSY, false},
{"ppt-thrtl", 0x48400, 0, BIT(63), 0, false},
{"llc-thrtl", 0x40270, 0, BIT(63), 0, false},
{"amx-thrtl", 0x40250, 0, BIT(63), 0, false},
{"cpu-fixed-freq-pll-relock", CLUSTER_PSTATE, 0, CLUSTER_PSTATE_FIXED_FREQ_PLL_RECLOCK, 0,
false},
{},
};
const struct feat_t *cpufreq_get_features(void)
{
switch (chip_id) {
case T8103:
case T6000 ... T6002:
return t8103_features;
case T8112:
return t8112_features;
case T6020:
case T6021:
case T6022:
return t6020_features;
default:
printf("cpufreq: Chip 0x%x is unsupported\n", chip_id);
return NULL;
}
}
int cpufreq_init(void)
{
printf("cpufreq: Initializing clusters\n");
const struct cluster_t *cluster = cpufreq_get_clusters();
const struct feat_t *features = cpufreq_get_features();
if (!cluster || !features)
return -1;
bool err = false;
while (cluster->base) {
err |= cpufreq_init_cluster(cluster++, features);
}
return err ? -1 : 0;
}
void cpufreq_fixup(void)
{
const struct cluster_t *cluster = cpufreq_get_clusters();
if (!cluster)
return;
while (cluster->base) {
cpufreq_fixup_cluster(cluster++);
}
}