mirror of
https://github.com/AsahiLinux/m1n1
synced 2024-11-10 09:44:13 +00:00
9b73944684
GPU stuff is a wild guess. Signed-off-by: Hector Martin <marcan@marcan.st>
288 lines
8.5 KiB
C
288 lines
8.5 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},
|
|
{},
|
|
};
|
|
|
|
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:
|
|
case T6022:
|
|
return t6020_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++);
|
|
}
|
|
}
|