// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2018, The Linux Foundation. All rights reserved.
*/
/* * In Certain QCOM SoCs like apq8096 and msm8996 that have KRYO processors, * the CPU frequency subset and voltage value of each OPP varies * based on the silicon variant in use. Qualcomm Process Voltage Scaling Tables * defines the voltage and frequency value based on the msm-id in SMEM * and speedbin blown in the efuse combination. * The qcom-cpufreq-nvmem driver reads the msm-id and efuse value from the SoC * to provide the OPP framework with required information. * This is used to determine the voltage and frequency value for each OPP of * operating-points-v2 table when it is parsed by the OPP framework.
*/
ret = qcom_smem_get_soc_id(&msm_id); if (ret) return ret;
speedbin = nvmem_cell_read(speedbin_nvmem, &len); if (IS_ERR(speedbin)) return PTR_ERR(speedbin);
switch (msm_id) { case QCOM_ID_MSM8996: case QCOM_ID_APQ8096: case QCOM_ID_IPQ5332: case QCOM_ID_IPQ5322: case QCOM_ID_IPQ5312: case QCOM_ID_IPQ5302: case QCOM_ID_IPQ5300: case QCOM_ID_IPQ5321: case QCOM_ID_IPQ9514: case QCOM_ID_IPQ9550: case QCOM_ID_IPQ9554: case QCOM_ID_IPQ9570: case QCOM_ID_IPQ9574:
drv->versions = 1 << (unsignedint)(*speedbin); break; case QCOM_ID_MSM8996SG: case QCOM_ID_APQ8096SG:
drv->versions = 1 << ((unsignedint)(*speedbin) + 4); break; default:
BUG(); break;
}
ret = qcom_smem_get_soc_id(&msm_id); if (ret) gotoexit;
switch (msm_id) { case QCOM_ID_IPQ8062:
drv->versions = BIT(IPQ8062_VERSION); break; case QCOM_ID_IPQ8064: case QCOM_ID_IPQ8066: case QCOM_ID_IPQ8068:
drv->versions = BIT(IPQ8064_VERSION); break; case QCOM_ID_IPQ8065: case QCOM_ID_IPQ8069:
drv->versions = BIT(IPQ8065_VERSION); break; default:
dev_err(cpu_dev, "SoC ID %u is not part of IPQ8064 family, limiting to 1.0GHz!\n",
msm_id);
drv->versions = BIT(IPQ8062_VERSION); break;
}
/* IPQ8064 speed is never fused. Only pvs values are fused. */
snprintf(*pvs_name, sizeof("speed0-pvsXX"), "speed0-pvs%d", pvs);
ret = qcom_smem_get_soc_id(&msm_id); if (ret) return ret;
speedbin = nvmem_cell_read(speedbin_nvmem, NULL); if (IS_ERR(speedbin)) return PTR_ERR(speedbin);
switch (msm_id) { case QCOM_ID_IPQ6005: case QCOM_ID_IPQ6010: case QCOM_ID_IPQ6018: case QCOM_ID_IPQ6028: /* Fuse Value Freq BIT to set * --------------------------------- * 2’b0 No Limit BIT(0) * 2’b1 1.5 GHz BIT(1)
*/
drv->versions = 1 << (unsignedint)(*speedbin); break; case QCOM_ID_IPQ6000: /* * IPQ6018 family only has one bit to advertise the CPU * speed-bin, but that is not enough for IPQ6000 which * is only rated up to 1.2GHz. * So for IPQ6000 manually set BIT(2) based on SMEM ID.
*/
drv->versions = IPQ6000_VERSION; break; default:
dev_err(cpu_dev, "SoC ID %u is not part of IPQ6018 family, limiting to 1.2GHz!\n",
msm_id);
drv->versions = IPQ6000_VERSION; break;
}
ret = qcom_smem_get_soc_id(&msm_id); if (ret) return ret;
switch (msm_id) { case QCOM_ID_IPQ8070A: case QCOM_ID_IPQ8071A: case QCOM_ID_IPQ8172: case QCOM_ID_IPQ8173: case QCOM_ID_IPQ8174:
drv->versions = BIT(IPQ8074_ACORN_VERSION); break; case QCOM_ID_IPQ8072A: case QCOM_ID_IPQ8074A: case QCOM_ID_IPQ8076A: case QCOM_ID_IPQ8078A:
drv->versions = BIT(IPQ8074_HAWKEYE_VERSION); break; default:
dev_err(cpu_dev, "SoC ID %u is not part of IPQ8074 family, limiting to 1.4GHz!\n",
msm_id);
drv->versions = BIT(IPQ8074_ACORN_VERSION); break;
}
match = pdev->dev.platform_data;
drv->data = match->data; if (!drv->data) return -ENODEV;
if (drv->data->get_version) {
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");
ret = drv->data->get_version(cpu_dev,
speedbin_nvmem, &pvs_name, drv); if (ret) {
nvmem_cell_put(speedbin_nvmem); return ret;
}
nvmem_cell_put(speedbin_nvmem);
}
cpu_dev = get_cpu_device(cpu); if (NULL == cpu_dev) {
ret = -ENODEV; goto free_opp;
}
if (drv->data->get_version) {
config.supported_hw = &drv->versions;
config.supported_hw_count = 1;
if (pvs_name)
config.prop_name = pvs_name;
}
if (config.supported_hw) {
drv->cpus[cpu].opp_token = dev_pm_opp_set_config(cpu_dev, &config); if (drv->cpus[cpu].opp_token < 0) {
ret = drv->cpus[cpu].opp_token;
dev_err(cpu_dev, "Failed to set OPP config\n"); goto free_opp;
}
}
/* * Since the driver depends on smem and 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 qcom_cpufreq_init(void)
{ struct device_node *np __free(device_node) = of_find_node_by_path("/"); conststruct of_device_id *match; int ret;
if (!np) return -ENODEV;
match = of_match_node(qcom_cpufreq_match_list, np); if (!match) return -ENODEV;
ret = platform_driver_register(&qcom_cpufreq_driver); if (unlikely(ret < 0)) return ret;
cpufreq_pdev = platform_device_register_data(NULL, "qcom-cpufreq-nvmem",
-1, match, sizeof(*match));
ret = PTR_ERR_OR_ZERO(cpufreq_pdev); if (0 == ret) 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.