// SPDX-License-Identifier: GPL-2.0-only /* * Apple SoC CPU cluster performance state driver * * Copyright The Asahi Linux Contributors * * Based on scpi-cpufreq.c
*/
/* Same timebase as CPU counter (24MHz) */ #define APPLE_DVFS_LAST_CHG_TIME 0x38
/* * Apple ran out of bits and had to shift this in T8112...
*/ #define APPLE_DVFS_STATUS 0x50 #define APPLE_DVFS_STATUS_CUR_PS_S5L8960X GENMASK(5, 3) #define APPLE_DVFS_STATUS_CUR_PS_SHIFT_S5L8960X 3 #define APPLE_DVFS_STATUS_TGT_PS_S5L8960X GENMASK(2, 0) #define APPLE_DVFS_STATUS_CUR_PS_T8103 GENMASK(7, 4) #define APPLE_DVFS_STATUS_CUR_PS_SHIFT_T8103 4 #define APPLE_DVFS_STATUS_TGT_PS_T8103 GENMASK(3, 0) #define APPLE_DVFS_STATUS_CUR_PS_T8112 GENMASK(9, 5) #define APPLE_DVFS_STATUS_CUR_PS_SHIFT_T8112 5 #define APPLE_DVFS_STATUS_TGT_PS_T8112 GENMASK(4, 0)
/* * Div is +1, base clock is 12MHz on existing SoCs. * For documentation purposes. We use the OPP table to * get the frequency.
*/ #define APPLE_DVFS_PLL_STATUS 0xc0 #define APPLE_DVFS_PLL_FACTOR 0xc8 #define APPLE_DVFS_PLL_FACTOR_MULT GENMASK(31, 16) #define APPLE_DVFS_PLL_FACTOR_DIV GENMASK(15, 0)
policy = cpufreq_cpu_get_raw(cpu); if (unlikely(!policy)) return 0;
priv = policy->driver_data;
if (priv->info->cur_pstate_mask) {
u32 reg = readl_relaxed(priv->reg_base + APPLE_DVFS_STATUS);
pstate = (reg & priv->info->cur_pstate_mask) >> priv->info->cur_pstate_shift;
} else { /* * For the fallback case we might not know the layout of DVFS_STATUS, * so just use the command register value (which ignores boost limitations).
*/
u64 reg = readq_relaxed(priv->reg_base + APPLE_DVFS_CMD);
pstate = FIELD_GET(APPLE_DVFS_CMD_PS1, reg);
}
cpufreq_for_each_valid_entry(p, policy->freq_table) if (p->driver_data == pstate) return p->frequency;
dev_err(priv->cpu_dev, "could not find frequency for pstate %d\n",
pstate); return 0;
}
cpu_dev = get_cpu_device(policy->cpu); if (!cpu_dev) {
pr_err("failed to get cpu%d device\n", policy->cpu); return -ENODEV;
}
ret = dev_pm_opp_of_add_table(cpu_dev); if (ret < 0) {
dev_err(cpu_dev, "%s: failed to add OPP table: %d\n", __func__, ret); return ret;
}
ret = apple_soc_cpufreq_find_cluster(policy, ®_base, &info); if (ret) {
dev_err(cpu_dev, "%s: failed to get cluster info: %d\n", __func__, ret); return ret;
}
ret = dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus); if (ret) {
dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n", __func__, ret); goto out_iounmap;
}
ret = dev_pm_opp_get_opp_count(cpu_dev); if (ret <= 0) {
dev_dbg(cpu_dev, "OPP table is not ready, deferring probe\n");
ret = -EPROBE_DEFER; goto out_free_opp;
}
priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) {
ret = -ENOMEM; goto out_free_opp;
}
ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); if (ret) {
dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret); goto out_free_priv;
}
/* Get OPP levels (p-state indexes) and stash them in driver_data */ for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { unsignedlong rate = freq_table[i].frequency * 1000 + 999; struct dev_pm_opp *opp = dev_pm_opp_find_freq_floor(cpu_dev, &rate);
if (IS_ERR(opp)) {
ret = PTR_ERR(opp); goto out_free_cpufreq_table;
}
freq_table[i].driver_data = dev_pm_opp_get_level(opp);
dev_pm_opp_put(opp);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.