// SPDX-License-Identifier: GPL-2.0 /* * Allwinner CPUFreq nvmem based driver * * The sun50i-cpufreq-nvmem driver reads the efuse value from the SoC to * provide the OPP framework with required information. * * Copyright (C) 2019 Yangtao Li <tiny.windzz@gmail.com>
*/
/* * We treat unexpected efuse values as if the SoC was from * the slowest bin. Expected efuse values are 1-3, slowest * to fastest.
*/ if (efuse_value >= 1 && efuse_value <= 3) return efuse_value - 1; else return 0;
}
/* * Judging by the OPP tables in the vendor BSP, the quality order of the * returned speedbin index is 4 -> 0/2 -> 3 -> 1, from worst to best. * 0 and 2 seem identical from the OPP tables' point of view.
*/ static u32 sun50i_h616_efuse_xlate(u32 speedbin)
{ int ver_bits = get_soc_id_revision();
u32 value = 0;
switch (speedbin & 0xffff) { case 0x2000:
value = 0; break; case 0x2400: case 0x7400: case 0x2c00: case 0x7c00: if (ver_bits != SMCCC_RET_NOT_SUPPORTED && ver_bits <= 1) { /* ic version A/B */
value = 1;
} else { /* ic version C and later version */
value = 2;
} break; case 0x5000: case 0x5400: case 0x6000:
value = 3; break; case 0x5c00:
value = 4; break; case 0x5d00:
value = 0; break; case 0x6c00:
value = 5; break; default:
pr_warn("sun50i-cpufreq-nvmem: unknown speed bin 0x%x, using default bin 0\n",
speedbin & 0xffff);
value = 0; break;
}
/** * dt_has_supported_hw() - Check if any OPPs use opp-supported-hw * * If we ask the cpufreq framework to use the opp-supported-hw feature, it * will ignore every OPP node without that DT property. If none of the OPPs * have it, the driver will fail probing, due to the lack of OPPs. * * Returns true if we have at least one OPP with the opp-supported-hw property.
*/ staticbool dt_has_supported_hw(void)
{ bool has_opp_supported_hw = false; struct device *cpu_dev;
cpu_dev = get_cpu_device(0); if (!cpu_dev) returnfalse;
struct device_node *np __free(device_node) =
dev_pm_opp_of_get_opp_desc_node(cpu_dev); if (!np) returnfalse;
/** * sun50i_cpufreq_get_efuse() - Determine speed grade from efuse value * * Returns non-negative speed bin index on success, a negative error * value otherwise.
*/ staticint sun50i_cpufreq_get_efuse(void)
{ conststruct sunxi_cpufreq_data *opp_data; struct nvmem_cell *speedbin_nvmem; conststruct of_device_id *match; struct device *cpu_dev; void *speedbin_ptr;
u32 speedbin = 0;
size_t len; int ret;
cpu_dev = get_cpu_device(0); if (!cpu_dev) return -ENODEV;
struct device_node *np __free(device_node) =
dev_pm_opp_of_get_opp_desc_node(cpu_dev); if (!np) return -ENOENT;
match = of_match_node(cpu_opp_match_list, np); if (!match) return -ENOENT;
opp_data = match->data;
speedbin_nvmem = of_nvmem_cell_get(np, NULL); if (IS_ERR(speedbin_nvmem)) return dev_err_probe(cpu_dev, PTR_ERR(speedbin_nvmem), "Could not get nvmem cell\n");
speedbin_ptr = nvmem_cell_read(speedbin_nvmem, &len);
nvmem_cell_put(speedbin_nvmem); if (IS_ERR(speedbin_ptr)) return PTR_ERR(speedbin_ptr);
if (len <= 4)
memcpy(&speedbin, speedbin_ptr, len);
speedbin = le32_to_cpu(speedbin);
ret = opp_data->efuse_xlate(speedbin);
kfree(speedbin_ptr);
return ret;
};
staticint sun50i_cpufreq_nvmem_probe(struct platform_device *pdev)
{ int *opp_tokens; char name[] = "speedXXXXXXXXXXX"; /* Integers can take 11 chars max */ unsignedint cpu, supported_hw; struct dev_pm_opp_config config = {}; int speed; int ret;
opp_tokens = kcalloc(num_possible_cpus(), sizeof(*opp_tokens),
GFP_KERNEL); if (!opp_tokens) return -ENOMEM;
/* * We need at least one OPP with the "opp-supported-hw" property, * or else the upper layers will ignore every OPP and will bail out.
*/ if (dt_has_supported_hw()) {
supported_hw = 1U << speed;
config.supported_hw = &supported_hw;
config.supported_hw_count = 1;
}
/* * Since the driver depends on nvmem drivers, which may return EPROBE_DEFER, * all the real activity is done in the probe, which may be defered as well. * The init here is only registering the driver and the platform device.
*/ staticint __init sun50i_cpufreq_init(void)
{ conststruct of_device_id *match; int ret;
match = sun50i_cpufreq_match_node(); if (!match) return -ENODEV;
ret = platform_driver_register(&sun50i_cpufreq_driver); if (unlikely(ret < 0)) return ret;
sun50i_cpufreq_pdev =
platform_device_register_simple("sun50i-cpufreq-nvmem",
-1, NULL, 0);
ret = PTR_ERR_OR_ZERO(sun50i_cpufreq_pdev); if (ret == 0) return 0;
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.