1*7d127095SSricharan R // SPDX-License-Identifier: GPL-2.0 2*7d127095SSricharan R /* 3*7d127095SSricharan R * Copyright (c) 2018, The Linux Foundation. All rights reserved. 4*7d127095SSricharan R */ 5*7d127095SSricharan R 6*7d127095SSricharan R /* 7*7d127095SSricharan R * In Certain QCOM SoCs like apq8096 and msm8996 that have KRYO processors, 8*7d127095SSricharan R * the CPU frequency subset and voltage value of each OPP varies 9*7d127095SSricharan R * based on the silicon variant in use. Qualcomm Process Voltage Scaling Tables 10*7d127095SSricharan R * defines the voltage and frequency value based on the msm-id in SMEM 11*7d127095SSricharan R * and speedbin blown in the efuse combination. 12*7d127095SSricharan R * The qcom-cpufreq-nvmem driver reads the msm-id and efuse value from the SoC 13*7d127095SSricharan R * to provide the OPP framework with required information. 14*7d127095SSricharan R * This is used to determine the voltage and frequency value for each OPP of 15*7d127095SSricharan R * operating-points-v2 table when it is parsed by the OPP framework. 16*7d127095SSricharan R */ 17*7d127095SSricharan R 18*7d127095SSricharan R #include <linux/cpu.h> 19*7d127095SSricharan R #include <linux/err.h> 20*7d127095SSricharan R #include <linux/init.h> 21*7d127095SSricharan R #include <linux/kernel.h> 22*7d127095SSricharan R #include <linux/module.h> 23*7d127095SSricharan R #include <linux/nvmem-consumer.h> 24*7d127095SSricharan R #include <linux/of.h> 25*7d127095SSricharan R #include <linux/of_device.h> 26*7d127095SSricharan R #include <linux/platform_device.h> 27*7d127095SSricharan R #include <linux/pm_opp.h> 28*7d127095SSricharan R #include <linux/slab.h> 29*7d127095SSricharan R #include <linux/soc/qcom/smem.h> 30*7d127095SSricharan R 31*7d127095SSricharan R #define MSM_ID_SMEM 137 32*7d127095SSricharan R 33*7d127095SSricharan R enum _msm_id { 34*7d127095SSricharan R MSM8996V3 = 0xF6ul, 35*7d127095SSricharan R APQ8096V3 = 0x123ul, 36*7d127095SSricharan R MSM8996SG = 0x131ul, 37*7d127095SSricharan R APQ8096SG = 0x138ul, 38*7d127095SSricharan R }; 39*7d127095SSricharan R 40*7d127095SSricharan R enum _msm8996_version { 41*7d127095SSricharan R MSM8996_V3, 42*7d127095SSricharan R MSM8996_SG, 43*7d127095SSricharan R NUM_OF_MSM8996_VERSIONS, 44*7d127095SSricharan R }; 45*7d127095SSricharan R 46*7d127095SSricharan R static struct platform_device *cpufreq_dt_pdev, *cpufreq_pdev; 47*7d127095SSricharan R 48*7d127095SSricharan R static enum _msm8996_version qcom_cpufreq_get_msm_id(void) 49*7d127095SSricharan R { 50*7d127095SSricharan R size_t len; 51*7d127095SSricharan R u32 *msm_id; 52*7d127095SSricharan R enum _msm8996_version version; 53*7d127095SSricharan R 54*7d127095SSricharan R msm_id = qcom_smem_get(QCOM_SMEM_HOST_ANY, MSM_ID_SMEM, &len); 55*7d127095SSricharan R if (IS_ERR(msm_id)) 56*7d127095SSricharan R return NUM_OF_MSM8996_VERSIONS; 57*7d127095SSricharan R 58*7d127095SSricharan R /* The first 4 bytes are format, next to them is the actual msm-id */ 59*7d127095SSricharan R msm_id++; 60*7d127095SSricharan R 61*7d127095SSricharan R switch ((enum _msm_id)*msm_id) { 62*7d127095SSricharan R case MSM8996V3: 63*7d127095SSricharan R case APQ8096V3: 64*7d127095SSricharan R version = MSM8996_V3; 65*7d127095SSricharan R break; 66*7d127095SSricharan R case MSM8996SG: 67*7d127095SSricharan R case APQ8096SG: 68*7d127095SSricharan R version = MSM8996_SG; 69*7d127095SSricharan R break; 70*7d127095SSricharan R default: 71*7d127095SSricharan R version = NUM_OF_MSM8996_VERSIONS; 72*7d127095SSricharan R } 73*7d127095SSricharan R 74*7d127095SSricharan R return version; 75*7d127095SSricharan R } 76*7d127095SSricharan R 77*7d127095SSricharan R static int qcom_cpufreq_kryo_name_version(struct device *cpu_dev, 78*7d127095SSricharan R struct nvmem_cell *speedbin_nvmem, 79*7d127095SSricharan R u32 *versions) 80*7d127095SSricharan R { 81*7d127095SSricharan R size_t len; 82*7d127095SSricharan R u8 *speedbin; 83*7d127095SSricharan R enum _msm8996_version msm8996_version; 84*7d127095SSricharan R 85*7d127095SSricharan R msm8996_version = qcom_cpufreq_get_msm_id(); 86*7d127095SSricharan R if (NUM_OF_MSM8996_VERSIONS == msm8996_version) { 87*7d127095SSricharan R dev_err(cpu_dev, "Not Snapdragon 820/821!"); 88*7d127095SSricharan R return -ENODEV; 89*7d127095SSricharan R } 90*7d127095SSricharan R 91*7d127095SSricharan R speedbin = nvmem_cell_read(speedbin_nvmem, &len); 92*7d127095SSricharan R if (IS_ERR(speedbin)) 93*7d127095SSricharan R return PTR_ERR(speedbin); 94*7d127095SSricharan R 95*7d127095SSricharan R switch (msm8996_version) { 96*7d127095SSricharan R case MSM8996_V3: 97*7d127095SSricharan R *versions = 1 << (unsigned int)(*speedbin); 98*7d127095SSricharan R break; 99*7d127095SSricharan R case MSM8996_SG: 100*7d127095SSricharan R *versions = 1 << ((unsigned int)(*speedbin) + 4); 101*7d127095SSricharan R break; 102*7d127095SSricharan R default: 103*7d127095SSricharan R BUG(); 104*7d127095SSricharan R break; 105*7d127095SSricharan R } 106*7d127095SSricharan R 107*7d127095SSricharan R kfree(speedbin); 108*7d127095SSricharan R return 0; 109*7d127095SSricharan R } 110*7d127095SSricharan R 111*7d127095SSricharan R static int qcom_cpufreq_probe(struct platform_device *pdev) 112*7d127095SSricharan R { 113*7d127095SSricharan R struct opp_table **opp_tables; 114*7d127095SSricharan R int (*get_version)(struct device *cpu_dev, 115*7d127095SSricharan R struct nvmem_cell *speedbin_nvmem, 116*7d127095SSricharan R u32 *versions); 117*7d127095SSricharan R struct nvmem_cell *speedbin_nvmem; 118*7d127095SSricharan R struct device_node *np; 119*7d127095SSricharan R struct device *cpu_dev; 120*7d127095SSricharan R unsigned cpu; 121*7d127095SSricharan R u32 versions; 122*7d127095SSricharan R const struct of_device_id *match; 123*7d127095SSricharan R int ret; 124*7d127095SSricharan R 125*7d127095SSricharan R cpu_dev = get_cpu_device(0); 126*7d127095SSricharan R if (!cpu_dev) 127*7d127095SSricharan R return -ENODEV; 128*7d127095SSricharan R 129*7d127095SSricharan R match = pdev->dev.platform_data; 130*7d127095SSricharan R get_version = match->data; 131*7d127095SSricharan R if (!get_version) 132*7d127095SSricharan R return -ENODEV; 133*7d127095SSricharan R 134*7d127095SSricharan R np = dev_pm_opp_of_get_opp_desc_node(cpu_dev); 135*7d127095SSricharan R if (!np) 136*7d127095SSricharan R return -ENOENT; 137*7d127095SSricharan R 138*7d127095SSricharan R ret = of_device_is_compatible(np, "operating-points-v2-kryo-cpu"); 139*7d127095SSricharan R if (!ret) { 140*7d127095SSricharan R of_node_put(np); 141*7d127095SSricharan R return -ENOENT; 142*7d127095SSricharan R } 143*7d127095SSricharan R 144*7d127095SSricharan R speedbin_nvmem = of_nvmem_cell_get(np, NULL); 145*7d127095SSricharan R of_node_put(np); 146*7d127095SSricharan R if (IS_ERR(speedbin_nvmem)) { 147*7d127095SSricharan R if (PTR_ERR(speedbin_nvmem) != -EPROBE_DEFER) 148*7d127095SSricharan R dev_err(cpu_dev, "Could not get nvmem cell: %ld\n", 149*7d127095SSricharan R PTR_ERR(speedbin_nvmem)); 150*7d127095SSricharan R return PTR_ERR(speedbin_nvmem); 151*7d127095SSricharan R } 152*7d127095SSricharan R 153*7d127095SSricharan R ret = get_version(cpu_dev, speedbin_nvmem, &versions); 154*7d127095SSricharan R nvmem_cell_put(speedbin_nvmem); 155*7d127095SSricharan R if (ret) 156*7d127095SSricharan R return ret; 157*7d127095SSricharan R 158*7d127095SSricharan R opp_tables = kcalloc(num_possible_cpus(), sizeof(*opp_tables), GFP_KERNEL); 159*7d127095SSricharan R if (!opp_tables) 160*7d127095SSricharan R return -ENOMEM; 161*7d127095SSricharan R 162*7d127095SSricharan R for_each_possible_cpu(cpu) { 163*7d127095SSricharan R cpu_dev = get_cpu_device(cpu); 164*7d127095SSricharan R if (NULL == cpu_dev) { 165*7d127095SSricharan R ret = -ENODEV; 166*7d127095SSricharan R goto free_opp; 167*7d127095SSricharan R } 168*7d127095SSricharan R 169*7d127095SSricharan R opp_tables[cpu] = dev_pm_opp_set_supported_hw(cpu_dev, 170*7d127095SSricharan R &versions, 1); 171*7d127095SSricharan R if (IS_ERR(opp_tables[cpu])) { 172*7d127095SSricharan R ret = PTR_ERR(opp_tables[cpu]); 173*7d127095SSricharan R dev_err(cpu_dev, "Failed to set supported hardware\n"); 174*7d127095SSricharan R goto free_opp; 175*7d127095SSricharan R } 176*7d127095SSricharan R } 177*7d127095SSricharan R 178*7d127095SSricharan R cpufreq_dt_pdev = platform_device_register_simple("cpufreq-dt", -1, 179*7d127095SSricharan R NULL, 0); 180*7d127095SSricharan R if (!IS_ERR(cpufreq_dt_pdev)) { 181*7d127095SSricharan R platform_set_drvdata(pdev, opp_tables); 182*7d127095SSricharan R return 0; 183*7d127095SSricharan R } 184*7d127095SSricharan R 185*7d127095SSricharan R ret = PTR_ERR(cpufreq_dt_pdev); 186*7d127095SSricharan R dev_err(cpu_dev, "Failed to register platform device\n"); 187*7d127095SSricharan R 188*7d127095SSricharan R free_opp: 189*7d127095SSricharan R for_each_possible_cpu(cpu) { 190*7d127095SSricharan R if (IS_ERR_OR_NULL(opp_tables[cpu])) 191*7d127095SSricharan R break; 192*7d127095SSricharan R dev_pm_opp_put_supported_hw(opp_tables[cpu]); 193*7d127095SSricharan R } 194*7d127095SSricharan R kfree(opp_tables); 195*7d127095SSricharan R 196*7d127095SSricharan R return ret; 197*7d127095SSricharan R } 198*7d127095SSricharan R 199*7d127095SSricharan R static int qcom_cpufreq_remove(struct platform_device *pdev) 200*7d127095SSricharan R { 201*7d127095SSricharan R struct opp_table **opp_tables = platform_get_drvdata(pdev); 202*7d127095SSricharan R unsigned int cpu; 203*7d127095SSricharan R 204*7d127095SSricharan R platform_device_unregister(cpufreq_dt_pdev); 205*7d127095SSricharan R 206*7d127095SSricharan R for_each_possible_cpu(cpu) 207*7d127095SSricharan R dev_pm_opp_put_supported_hw(opp_tables[cpu]); 208*7d127095SSricharan R 209*7d127095SSricharan R kfree(opp_tables); 210*7d127095SSricharan R 211*7d127095SSricharan R return 0; 212*7d127095SSricharan R } 213*7d127095SSricharan R 214*7d127095SSricharan R static struct platform_driver qcom_cpufreq_driver = { 215*7d127095SSricharan R .probe = qcom_cpufreq_probe, 216*7d127095SSricharan R .remove = qcom_cpufreq_remove, 217*7d127095SSricharan R .driver = { 218*7d127095SSricharan R .name = "qcom-cpufreq-nvmem", 219*7d127095SSricharan R }, 220*7d127095SSricharan R }; 221*7d127095SSricharan R 222*7d127095SSricharan R static const struct of_device_id qcom_cpufreq_match_list[] __initconst = { 223*7d127095SSricharan R { .compatible = "qcom,apq8096", 224*7d127095SSricharan R .data = qcom_cpufreq_kryo_name_version }, 225*7d127095SSricharan R { .compatible = "qcom,msm8996", 226*7d127095SSricharan R .data = qcom_cpufreq_kryo_name_version }, 227*7d127095SSricharan R {}, 228*7d127095SSricharan R }; 229*7d127095SSricharan R 230*7d127095SSricharan R /* 231*7d127095SSricharan R * Since the driver depends on smem and nvmem drivers, which may 232*7d127095SSricharan R * return EPROBE_DEFER, all the real activity is done in the probe, 233*7d127095SSricharan R * which may be defered as well. The init here is only registering 234*7d127095SSricharan R * the driver and the platform device. 235*7d127095SSricharan R */ 236*7d127095SSricharan R static int __init qcom_cpufreq_init(void) 237*7d127095SSricharan R { 238*7d127095SSricharan R struct device_node *np = of_find_node_by_path("/"); 239*7d127095SSricharan R const struct of_device_id *match; 240*7d127095SSricharan R int ret; 241*7d127095SSricharan R 242*7d127095SSricharan R if (!np) 243*7d127095SSricharan R return -ENODEV; 244*7d127095SSricharan R 245*7d127095SSricharan R match = of_match_node(qcom_cpufreq_match_list, np); 246*7d127095SSricharan R of_node_put(np); 247*7d127095SSricharan R if (!match) 248*7d127095SSricharan R return -ENODEV; 249*7d127095SSricharan R 250*7d127095SSricharan R ret = platform_driver_register(&qcom_cpufreq_driver); 251*7d127095SSricharan R if (unlikely(ret < 0)) 252*7d127095SSricharan R return ret; 253*7d127095SSricharan R 254*7d127095SSricharan R cpufreq_pdev = platform_device_register_data(NULL, "qcom-cpufreq-nvmem", 255*7d127095SSricharan R -1, match, sizeof(*match)); 256*7d127095SSricharan R ret = PTR_ERR_OR_ZERO(cpufreq_pdev); 257*7d127095SSricharan R if (0 == ret) 258*7d127095SSricharan R return 0; 259*7d127095SSricharan R 260*7d127095SSricharan R platform_driver_unregister(&qcom_cpufreq_driver); 261*7d127095SSricharan R return ret; 262*7d127095SSricharan R } 263*7d127095SSricharan R module_init(qcom_cpufreq_init); 264*7d127095SSricharan R 265*7d127095SSricharan R static void __exit qcom_cpufreq_exit(void) 266*7d127095SSricharan R { 267*7d127095SSricharan R platform_device_unregister(cpufreq_pdev); 268*7d127095SSricharan R platform_driver_unregister(&qcom_cpufreq_driver); 269*7d127095SSricharan R } 270*7d127095SSricharan R module_exit(qcom_cpufreq_exit); 271*7d127095SSricharan R 272*7d127095SSricharan R MODULE_DESCRIPTION("Qualcomm Technologies, Inc. CPUfreq driver"); 273*7d127095SSricharan R MODULE_LICENSE("GPL v2"); 274