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