18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2018, The Linux Foundation. All rights reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci/* 78c2ecf20Sopenharmony_ci * In Certain QCOM SoCs like apq8096 and msm8996 that have KRYO processors, 88c2ecf20Sopenharmony_ci * the CPU frequency subset and voltage value of each OPP varies 98c2ecf20Sopenharmony_ci * based on the silicon variant in use. Qualcomm Process Voltage Scaling Tables 108c2ecf20Sopenharmony_ci * defines the voltage and frequency value based on the msm-id in SMEM 118c2ecf20Sopenharmony_ci * and speedbin blown in the efuse combination. 128c2ecf20Sopenharmony_ci * The qcom-cpufreq-nvmem driver reads the msm-id and efuse value from the SoC 138c2ecf20Sopenharmony_ci * to provide the OPP framework with required information. 148c2ecf20Sopenharmony_ci * This is used to determine the voltage and frequency value for each OPP of 158c2ecf20Sopenharmony_ci * operating-points-v2 table when it is parsed by the OPP framework. 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/cpu.h> 198c2ecf20Sopenharmony_ci#include <linux/err.h> 208c2ecf20Sopenharmony_ci#include <linux/init.h> 218c2ecf20Sopenharmony_ci#include <linux/kernel.h> 228c2ecf20Sopenharmony_ci#include <linux/module.h> 238c2ecf20Sopenharmony_ci#include <linux/nvmem-consumer.h> 248c2ecf20Sopenharmony_ci#include <linux/of.h> 258c2ecf20Sopenharmony_ci#include <linux/of_device.h> 268c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 278c2ecf20Sopenharmony_ci#include <linux/pm_domain.h> 288c2ecf20Sopenharmony_ci#include <linux/pm_opp.h> 298c2ecf20Sopenharmony_ci#include <linux/slab.h> 308c2ecf20Sopenharmony_ci#include <linux/soc/qcom/smem.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define MSM_ID_SMEM 137 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cienum _msm_id { 358c2ecf20Sopenharmony_ci MSM8996V3 = 0xF6ul, 368c2ecf20Sopenharmony_ci APQ8096V3 = 0x123ul, 378c2ecf20Sopenharmony_ci MSM8996SG = 0x131ul, 388c2ecf20Sopenharmony_ci APQ8096SG = 0x138ul, 398c2ecf20Sopenharmony_ci}; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cienum _msm8996_version { 428c2ecf20Sopenharmony_ci MSM8996_V3, 438c2ecf20Sopenharmony_ci MSM8996_SG, 448c2ecf20Sopenharmony_ci NUM_OF_MSM8996_VERSIONS, 458c2ecf20Sopenharmony_ci}; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistruct qcom_cpufreq_drv; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistruct qcom_cpufreq_match_data { 508c2ecf20Sopenharmony_ci int (*get_version)(struct device *cpu_dev, 518c2ecf20Sopenharmony_ci struct nvmem_cell *speedbin_nvmem, 528c2ecf20Sopenharmony_ci char **pvs_name, 538c2ecf20Sopenharmony_ci struct qcom_cpufreq_drv *drv); 548c2ecf20Sopenharmony_ci const char **genpd_names; 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistruct qcom_cpufreq_drv { 588c2ecf20Sopenharmony_ci struct opp_table **names_opp_tables; 598c2ecf20Sopenharmony_ci struct opp_table **hw_opp_tables; 608c2ecf20Sopenharmony_ci struct opp_table **genpd_opp_tables; 618c2ecf20Sopenharmony_ci u32 versions; 628c2ecf20Sopenharmony_ci const struct qcom_cpufreq_match_data *data; 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic struct platform_device *cpufreq_dt_pdev, *cpufreq_pdev; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic void get_krait_bin_format_a(struct device *cpu_dev, 688c2ecf20Sopenharmony_ci int *speed, int *pvs, int *pvs_ver, 698c2ecf20Sopenharmony_ci struct nvmem_cell *pvs_nvmem, u8 *buf) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci u32 pte_efuse; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci pte_efuse = *((u32 *)buf); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci *speed = pte_efuse & 0xf; 768c2ecf20Sopenharmony_ci if (*speed == 0xf) 778c2ecf20Sopenharmony_ci *speed = (pte_efuse >> 4) & 0xf; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (*speed == 0xf) { 808c2ecf20Sopenharmony_ci *speed = 0; 818c2ecf20Sopenharmony_ci dev_warn(cpu_dev, "Speed bin: Defaulting to %d\n", *speed); 828c2ecf20Sopenharmony_ci } else { 838c2ecf20Sopenharmony_ci dev_dbg(cpu_dev, "Speed bin: %d\n", *speed); 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci *pvs = (pte_efuse >> 10) & 0x7; 878c2ecf20Sopenharmony_ci if (*pvs == 0x7) 888c2ecf20Sopenharmony_ci *pvs = (pte_efuse >> 13) & 0x7; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (*pvs == 0x7) { 918c2ecf20Sopenharmony_ci *pvs = 0; 928c2ecf20Sopenharmony_ci dev_warn(cpu_dev, "PVS bin: Defaulting to %d\n", *pvs); 938c2ecf20Sopenharmony_ci } else { 948c2ecf20Sopenharmony_ci dev_dbg(cpu_dev, "PVS bin: %d\n", *pvs); 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic void get_krait_bin_format_b(struct device *cpu_dev, 998c2ecf20Sopenharmony_ci int *speed, int *pvs, int *pvs_ver, 1008c2ecf20Sopenharmony_ci struct nvmem_cell *pvs_nvmem, u8 *buf) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci u32 pte_efuse, redundant_sel; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci pte_efuse = *((u32 *)buf); 1058c2ecf20Sopenharmony_ci redundant_sel = (pte_efuse >> 24) & 0x7; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci *pvs_ver = (pte_efuse >> 4) & 0x3; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci switch (redundant_sel) { 1108c2ecf20Sopenharmony_ci case 1: 1118c2ecf20Sopenharmony_ci *pvs = ((pte_efuse >> 28) & 0x8) | ((pte_efuse >> 6) & 0x7); 1128c2ecf20Sopenharmony_ci *speed = (pte_efuse >> 27) & 0xf; 1138c2ecf20Sopenharmony_ci break; 1148c2ecf20Sopenharmony_ci case 2: 1158c2ecf20Sopenharmony_ci *pvs = (pte_efuse >> 27) & 0xf; 1168c2ecf20Sopenharmony_ci *speed = pte_efuse & 0x7; 1178c2ecf20Sopenharmony_ci break; 1188c2ecf20Sopenharmony_ci default: 1198c2ecf20Sopenharmony_ci /* 4 bits of PVS are in efuse register bits 31, 8-6. */ 1208c2ecf20Sopenharmony_ci *pvs = ((pte_efuse >> 28) & 0x8) | ((pte_efuse >> 6) & 0x7); 1218c2ecf20Sopenharmony_ci *speed = pte_efuse & 0x7; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* Check SPEED_BIN_BLOW_STATUS */ 1258c2ecf20Sopenharmony_ci if (pte_efuse & BIT(3)) { 1268c2ecf20Sopenharmony_ci dev_dbg(cpu_dev, "Speed bin: %d\n", *speed); 1278c2ecf20Sopenharmony_ci } else { 1288c2ecf20Sopenharmony_ci dev_warn(cpu_dev, "Speed bin not set. Defaulting to 0!\n"); 1298c2ecf20Sopenharmony_ci *speed = 0; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* Check PVS_BLOW_STATUS */ 1338c2ecf20Sopenharmony_ci pte_efuse = *(((u32 *)buf) + 1); 1348c2ecf20Sopenharmony_ci pte_efuse &= BIT(21); 1358c2ecf20Sopenharmony_ci if (pte_efuse) { 1368c2ecf20Sopenharmony_ci dev_dbg(cpu_dev, "PVS bin: %d\n", *pvs); 1378c2ecf20Sopenharmony_ci } else { 1388c2ecf20Sopenharmony_ci dev_warn(cpu_dev, "PVS bin not set. Defaulting to 0!\n"); 1398c2ecf20Sopenharmony_ci *pvs = 0; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci dev_dbg(cpu_dev, "PVS version: %d\n", *pvs_ver); 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic enum _msm8996_version qcom_cpufreq_get_msm_id(void) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci size_t len; 1488c2ecf20Sopenharmony_ci u32 *msm_id; 1498c2ecf20Sopenharmony_ci enum _msm8996_version version; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci msm_id = qcom_smem_get(QCOM_SMEM_HOST_ANY, MSM_ID_SMEM, &len); 1528c2ecf20Sopenharmony_ci if (IS_ERR(msm_id)) 1538c2ecf20Sopenharmony_ci return NUM_OF_MSM8996_VERSIONS; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci /* The first 4 bytes are format, next to them is the actual msm-id */ 1568c2ecf20Sopenharmony_ci msm_id++; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci switch ((enum _msm_id)*msm_id) { 1598c2ecf20Sopenharmony_ci case MSM8996V3: 1608c2ecf20Sopenharmony_ci case APQ8096V3: 1618c2ecf20Sopenharmony_ci version = MSM8996_V3; 1628c2ecf20Sopenharmony_ci break; 1638c2ecf20Sopenharmony_ci case MSM8996SG: 1648c2ecf20Sopenharmony_ci case APQ8096SG: 1658c2ecf20Sopenharmony_ci version = MSM8996_SG; 1668c2ecf20Sopenharmony_ci break; 1678c2ecf20Sopenharmony_ci default: 1688c2ecf20Sopenharmony_ci version = NUM_OF_MSM8996_VERSIONS; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci return version; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic int qcom_cpufreq_kryo_name_version(struct device *cpu_dev, 1758c2ecf20Sopenharmony_ci struct nvmem_cell *speedbin_nvmem, 1768c2ecf20Sopenharmony_ci char **pvs_name, 1778c2ecf20Sopenharmony_ci struct qcom_cpufreq_drv *drv) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci size_t len; 1808c2ecf20Sopenharmony_ci u8 *speedbin; 1818c2ecf20Sopenharmony_ci enum _msm8996_version msm8996_version; 1828c2ecf20Sopenharmony_ci *pvs_name = NULL; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci msm8996_version = qcom_cpufreq_get_msm_id(); 1858c2ecf20Sopenharmony_ci if (NUM_OF_MSM8996_VERSIONS == msm8996_version) { 1868c2ecf20Sopenharmony_ci dev_err(cpu_dev, "Not Snapdragon 820/821!"); 1878c2ecf20Sopenharmony_ci return -ENODEV; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci speedbin = nvmem_cell_read(speedbin_nvmem, &len); 1918c2ecf20Sopenharmony_ci if (IS_ERR(speedbin)) 1928c2ecf20Sopenharmony_ci return PTR_ERR(speedbin); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci switch (msm8996_version) { 1958c2ecf20Sopenharmony_ci case MSM8996_V3: 1968c2ecf20Sopenharmony_ci drv->versions = 1 << (unsigned int)(*speedbin); 1978c2ecf20Sopenharmony_ci break; 1988c2ecf20Sopenharmony_ci case MSM8996_SG: 1998c2ecf20Sopenharmony_ci drv->versions = 1 << ((unsigned int)(*speedbin) + 4); 2008c2ecf20Sopenharmony_ci break; 2018c2ecf20Sopenharmony_ci default: 2028c2ecf20Sopenharmony_ci BUG(); 2038c2ecf20Sopenharmony_ci break; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci kfree(speedbin); 2078c2ecf20Sopenharmony_ci return 0; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic int qcom_cpufreq_krait_name_version(struct device *cpu_dev, 2118c2ecf20Sopenharmony_ci struct nvmem_cell *speedbin_nvmem, 2128c2ecf20Sopenharmony_ci char **pvs_name, 2138c2ecf20Sopenharmony_ci struct qcom_cpufreq_drv *drv) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci int speed = 0, pvs = 0, pvs_ver = 0; 2168c2ecf20Sopenharmony_ci u8 *speedbin; 2178c2ecf20Sopenharmony_ci size_t len; 2188c2ecf20Sopenharmony_ci int ret = 0; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci speedbin = nvmem_cell_read(speedbin_nvmem, &len); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci if (IS_ERR(speedbin)) 2238c2ecf20Sopenharmony_ci return PTR_ERR(speedbin); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci switch (len) { 2268c2ecf20Sopenharmony_ci case 4: 2278c2ecf20Sopenharmony_ci get_krait_bin_format_a(cpu_dev, &speed, &pvs, &pvs_ver, 2288c2ecf20Sopenharmony_ci speedbin_nvmem, speedbin); 2298c2ecf20Sopenharmony_ci break; 2308c2ecf20Sopenharmony_ci case 8: 2318c2ecf20Sopenharmony_ci get_krait_bin_format_b(cpu_dev, &speed, &pvs, &pvs_ver, 2328c2ecf20Sopenharmony_ci speedbin_nvmem, speedbin); 2338c2ecf20Sopenharmony_ci break; 2348c2ecf20Sopenharmony_ci default: 2358c2ecf20Sopenharmony_ci dev_err(cpu_dev, "Unable to read nvmem data. Defaulting to 0!\n"); 2368c2ecf20Sopenharmony_ci ret = -ENODEV; 2378c2ecf20Sopenharmony_ci goto len_error; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci snprintf(*pvs_name, sizeof("speedXX-pvsXX-vXX"), "speed%d-pvs%d-v%d", 2418c2ecf20Sopenharmony_ci speed, pvs, pvs_ver); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci drv->versions = (1 << speed); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cilen_error: 2468c2ecf20Sopenharmony_ci kfree(speedbin); 2478c2ecf20Sopenharmony_ci return ret; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic const struct qcom_cpufreq_match_data match_data_kryo = { 2518c2ecf20Sopenharmony_ci .get_version = qcom_cpufreq_kryo_name_version, 2528c2ecf20Sopenharmony_ci}; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic const struct qcom_cpufreq_match_data match_data_krait = { 2558c2ecf20Sopenharmony_ci .get_version = qcom_cpufreq_krait_name_version, 2568c2ecf20Sopenharmony_ci}; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic const char *qcs404_genpd_names[] = { "cpr", NULL }; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic const struct qcom_cpufreq_match_data match_data_qcs404 = { 2618c2ecf20Sopenharmony_ci .genpd_names = qcs404_genpd_names, 2628c2ecf20Sopenharmony_ci}; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic int qcom_cpufreq_probe(struct platform_device *pdev) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci struct qcom_cpufreq_drv *drv; 2678c2ecf20Sopenharmony_ci struct nvmem_cell *speedbin_nvmem; 2688c2ecf20Sopenharmony_ci struct device_node *np; 2698c2ecf20Sopenharmony_ci struct device *cpu_dev; 2708c2ecf20Sopenharmony_ci char pvs_name_buffer[] = "speedXX-pvsXX-vXX"; 2718c2ecf20Sopenharmony_ci char *pvs_name = pvs_name_buffer; 2728c2ecf20Sopenharmony_ci unsigned cpu; 2738c2ecf20Sopenharmony_ci const struct of_device_id *match; 2748c2ecf20Sopenharmony_ci int ret; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci cpu_dev = get_cpu_device(0); 2778c2ecf20Sopenharmony_ci if (!cpu_dev) 2788c2ecf20Sopenharmony_ci return -ENODEV; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci np = dev_pm_opp_of_get_opp_desc_node(cpu_dev); 2818c2ecf20Sopenharmony_ci if (!np) 2828c2ecf20Sopenharmony_ci return -ENOENT; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci ret = of_device_is_compatible(np, "operating-points-v2-kryo-cpu"); 2858c2ecf20Sopenharmony_ci if (!ret) { 2868c2ecf20Sopenharmony_ci of_node_put(np); 2878c2ecf20Sopenharmony_ci return -ENOENT; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci drv = kzalloc(sizeof(*drv), GFP_KERNEL); 2918c2ecf20Sopenharmony_ci if (!drv) 2928c2ecf20Sopenharmony_ci return -ENOMEM; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci match = pdev->dev.platform_data; 2958c2ecf20Sopenharmony_ci drv->data = match->data; 2968c2ecf20Sopenharmony_ci if (!drv->data) { 2978c2ecf20Sopenharmony_ci ret = -ENODEV; 2988c2ecf20Sopenharmony_ci goto free_drv; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (drv->data->get_version) { 3028c2ecf20Sopenharmony_ci speedbin_nvmem = of_nvmem_cell_get(np, NULL); 3038c2ecf20Sopenharmony_ci if (IS_ERR(speedbin_nvmem)) { 3048c2ecf20Sopenharmony_ci if (PTR_ERR(speedbin_nvmem) != -EPROBE_DEFER) 3058c2ecf20Sopenharmony_ci dev_err(cpu_dev, 3068c2ecf20Sopenharmony_ci "Could not get nvmem cell: %ld\n", 3078c2ecf20Sopenharmony_ci PTR_ERR(speedbin_nvmem)); 3088c2ecf20Sopenharmony_ci ret = PTR_ERR(speedbin_nvmem); 3098c2ecf20Sopenharmony_ci goto free_drv; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci ret = drv->data->get_version(cpu_dev, 3138c2ecf20Sopenharmony_ci speedbin_nvmem, &pvs_name, drv); 3148c2ecf20Sopenharmony_ci if (ret) { 3158c2ecf20Sopenharmony_ci nvmem_cell_put(speedbin_nvmem); 3168c2ecf20Sopenharmony_ci goto free_drv; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci nvmem_cell_put(speedbin_nvmem); 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci of_node_put(np); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci drv->names_opp_tables = kcalloc(num_possible_cpus(), 3238c2ecf20Sopenharmony_ci sizeof(*drv->names_opp_tables), 3248c2ecf20Sopenharmony_ci GFP_KERNEL); 3258c2ecf20Sopenharmony_ci if (!drv->names_opp_tables) { 3268c2ecf20Sopenharmony_ci ret = -ENOMEM; 3278c2ecf20Sopenharmony_ci goto free_drv; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci drv->hw_opp_tables = kcalloc(num_possible_cpus(), 3308c2ecf20Sopenharmony_ci sizeof(*drv->hw_opp_tables), 3318c2ecf20Sopenharmony_ci GFP_KERNEL); 3328c2ecf20Sopenharmony_ci if (!drv->hw_opp_tables) { 3338c2ecf20Sopenharmony_ci ret = -ENOMEM; 3348c2ecf20Sopenharmony_ci goto free_opp_names; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci drv->genpd_opp_tables = kcalloc(num_possible_cpus(), 3388c2ecf20Sopenharmony_ci sizeof(*drv->genpd_opp_tables), 3398c2ecf20Sopenharmony_ci GFP_KERNEL); 3408c2ecf20Sopenharmony_ci if (!drv->genpd_opp_tables) { 3418c2ecf20Sopenharmony_ci ret = -ENOMEM; 3428c2ecf20Sopenharmony_ci goto free_opp; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) { 3468c2ecf20Sopenharmony_ci cpu_dev = get_cpu_device(cpu); 3478c2ecf20Sopenharmony_ci if (NULL == cpu_dev) { 3488c2ecf20Sopenharmony_ci ret = -ENODEV; 3498c2ecf20Sopenharmony_ci goto free_genpd_opp; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci if (drv->data->get_version) { 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci if (pvs_name) { 3558c2ecf20Sopenharmony_ci drv->names_opp_tables[cpu] = dev_pm_opp_set_prop_name( 3568c2ecf20Sopenharmony_ci cpu_dev, 3578c2ecf20Sopenharmony_ci pvs_name); 3588c2ecf20Sopenharmony_ci if (IS_ERR(drv->names_opp_tables[cpu])) { 3598c2ecf20Sopenharmony_ci ret = PTR_ERR(drv->names_opp_tables[cpu]); 3608c2ecf20Sopenharmony_ci dev_err(cpu_dev, "Failed to add OPP name %s\n", 3618c2ecf20Sopenharmony_ci pvs_name); 3628c2ecf20Sopenharmony_ci goto free_opp; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci drv->hw_opp_tables[cpu] = dev_pm_opp_set_supported_hw( 3678c2ecf20Sopenharmony_ci cpu_dev, &drv->versions, 1); 3688c2ecf20Sopenharmony_ci if (IS_ERR(drv->hw_opp_tables[cpu])) { 3698c2ecf20Sopenharmony_ci ret = PTR_ERR(drv->hw_opp_tables[cpu]); 3708c2ecf20Sopenharmony_ci dev_err(cpu_dev, 3718c2ecf20Sopenharmony_ci "Failed to set supported hardware\n"); 3728c2ecf20Sopenharmony_ci goto free_genpd_opp; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (drv->data->genpd_names) { 3778c2ecf20Sopenharmony_ci drv->genpd_opp_tables[cpu] = 3788c2ecf20Sopenharmony_ci dev_pm_opp_attach_genpd(cpu_dev, 3798c2ecf20Sopenharmony_ci drv->data->genpd_names, 3808c2ecf20Sopenharmony_ci NULL); 3818c2ecf20Sopenharmony_ci if (IS_ERR(drv->genpd_opp_tables[cpu])) { 3828c2ecf20Sopenharmony_ci ret = PTR_ERR(drv->genpd_opp_tables[cpu]); 3838c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 3848c2ecf20Sopenharmony_ci dev_err(cpu_dev, 3858c2ecf20Sopenharmony_ci "Could not attach to pm_domain: %d\n", 3868c2ecf20Sopenharmony_ci ret); 3878c2ecf20Sopenharmony_ci goto free_genpd_opp; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci cpufreq_dt_pdev = platform_device_register_simple("cpufreq-dt", -1, 3938c2ecf20Sopenharmony_ci NULL, 0); 3948c2ecf20Sopenharmony_ci if (!IS_ERR(cpufreq_dt_pdev)) { 3958c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, drv); 3968c2ecf20Sopenharmony_ci return 0; 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci ret = PTR_ERR(cpufreq_dt_pdev); 4008c2ecf20Sopenharmony_ci dev_err(cpu_dev, "Failed to register platform device\n"); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cifree_genpd_opp: 4038c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) { 4048c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(drv->genpd_opp_tables[cpu])) 4058c2ecf20Sopenharmony_ci break; 4068c2ecf20Sopenharmony_ci dev_pm_opp_detach_genpd(drv->genpd_opp_tables[cpu]); 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci kfree(drv->genpd_opp_tables); 4098c2ecf20Sopenharmony_cifree_opp: 4108c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) { 4118c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(drv->names_opp_tables[cpu])) 4128c2ecf20Sopenharmony_ci break; 4138c2ecf20Sopenharmony_ci dev_pm_opp_put_prop_name(drv->names_opp_tables[cpu]); 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) { 4168c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(drv->hw_opp_tables[cpu])) 4178c2ecf20Sopenharmony_ci break; 4188c2ecf20Sopenharmony_ci dev_pm_opp_put_supported_hw(drv->hw_opp_tables[cpu]); 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci kfree(drv->hw_opp_tables); 4218c2ecf20Sopenharmony_cifree_opp_names: 4228c2ecf20Sopenharmony_ci kfree(drv->names_opp_tables); 4238c2ecf20Sopenharmony_cifree_drv: 4248c2ecf20Sopenharmony_ci kfree(drv); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci return ret; 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_cistatic int qcom_cpufreq_remove(struct platform_device *pdev) 4308c2ecf20Sopenharmony_ci{ 4318c2ecf20Sopenharmony_ci struct qcom_cpufreq_drv *drv = platform_get_drvdata(pdev); 4328c2ecf20Sopenharmony_ci unsigned int cpu; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci platform_device_unregister(cpufreq_dt_pdev); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci for_each_possible_cpu(cpu) { 4378c2ecf20Sopenharmony_ci if (drv->names_opp_tables[cpu]) 4388c2ecf20Sopenharmony_ci dev_pm_opp_put_supported_hw(drv->names_opp_tables[cpu]); 4398c2ecf20Sopenharmony_ci if (drv->hw_opp_tables[cpu]) 4408c2ecf20Sopenharmony_ci dev_pm_opp_put_supported_hw(drv->hw_opp_tables[cpu]); 4418c2ecf20Sopenharmony_ci if (drv->genpd_opp_tables[cpu]) 4428c2ecf20Sopenharmony_ci dev_pm_opp_detach_genpd(drv->genpd_opp_tables[cpu]); 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci kfree(drv->names_opp_tables); 4468c2ecf20Sopenharmony_ci kfree(drv->hw_opp_tables); 4478c2ecf20Sopenharmony_ci kfree(drv->genpd_opp_tables); 4488c2ecf20Sopenharmony_ci kfree(drv); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci return 0; 4518c2ecf20Sopenharmony_ci} 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cistatic struct platform_driver qcom_cpufreq_driver = { 4548c2ecf20Sopenharmony_ci .probe = qcom_cpufreq_probe, 4558c2ecf20Sopenharmony_ci .remove = qcom_cpufreq_remove, 4568c2ecf20Sopenharmony_ci .driver = { 4578c2ecf20Sopenharmony_ci .name = "qcom-cpufreq-nvmem", 4588c2ecf20Sopenharmony_ci }, 4598c2ecf20Sopenharmony_ci}; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_cistatic const struct of_device_id qcom_cpufreq_match_list[] __initconst = { 4628c2ecf20Sopenharmony_ci { .compatible = "qcom,apq8096", .data = &match_data_kryo }, 4638c2ecf20Sopenharmony_ci { .compatible = "qcom,msm8996", .data = &match_data_kryo }, 4648c2ecf20Sopenharmony_ci { .compatible = "qcom,qcs404", .data = &match_data_qcs404 }, 4658c2ecf20Sopenharmony_ci { .compatible = "qcom,ipq8064", .data = &match_data_krait }, 4668c2ecf20Sopenharmony_ci { .compatible = "qcom,apq8064", .data = &match_data_krait }, 4678c2ecf20Sopenharmony_ci { .compatible = "qcom,msm8974", .data = &match_data_krait }, 4688c2ecf20Sopenharmony_ci { .compatible = "qcom,msm8960", .data = &match_data_krait }, 4698c2ecf20Sopenharmony_ci {}, 4708c2ecf20Sopenharmony_ci}; 4718c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, qcom_cpufreq_match_list); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci/* 4748c2ecf20Sopenharmony_ci * Since the driver depends on smem and nvmem drivers, which may 4758c2ecf20Sopenharmony_ci * return EPROBE_DEFER, all the real activity is done in the probe, 4768c2ecf20Sopenharmony_ci * which may be defered as well. The init here is only registering 4778c2ecf20Sopenharmony_ci * the driver and the platform device. 4788c2ecf20Sopenharmony_ci */ 4798c2ecf20Sopenharmony_cistatic int __init qcom_cpufreq_init(void) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci struct device_node *np = of_find_node_by_path("/"); 4828c2ecf20Sopenharmony_ci const struct of_device_id *match; 4838c2ecf20Sopenharmony_ci int ret; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci if (!np) 4868c2ecf20Sopenharmony_ci return -ENODEV; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci match = of_match_node(qcom_cpufreq_match_list, np); 4898c2ecf20Sopenharmony_ci of_node_put(np); 4908c2ecf20Sopenharmony_ci if (!match) 4918c2ecf20Sopenharmony_ci return -ENODEV; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci ret = platform_driver_register(&qcom_cpufreq_driver); 4948c2ecf20Sopenharmony_ci if (unlikely(ret < 0)) 4958c2ecf20Sopenharmony_ci return ret; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci cpufreq_pdev = platform_device_register_data(NULL, "qcom-cpufreq-nvmem", 4988c2ecf20Sopenharmony_ci -1, match, sizeof(*match)); 4998c2ecf20Sopenharmony_ci ret = PTR_ERR_OR_ZERO(cpufreq_pdev); 5008c2ecf20Sopenharmony_ci if (0 == ret) 5018c2ecf20Sopenharmony_ci return 0; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci platform_driver_unregister(&qcom_cpufreq_driver); 5048c2ecf20Sopenharmony_ci return ret; 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_cimodule_init(qcom_cpufreq_init); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_cistatic void __exit qcom_cpufreq_exit(void) 5098c2ecf20Sopenharmony_ci{ 5108c2ecf20Sopenharmony_ci platform_device_unregister(cpufreq_pdev); 5118c2ecf20Sopenharmony_ci platform_driver_unregister(&qcom_cpufreq_driver); 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_cimodule_exit(qcom_cpufreq_exit); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm Technologies, Inc. CPUfreq driver"); 5168c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 517